]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/inline.rs
Merge commit '61eb38aeda6cb54b93b872bf503d70084c4d621c' into clippyup
[rust.git] / src / librustdoc / clean / inline.rs
1 //! Support for inlining external documentation into the current AST.
2
3 use std::iter::once;
4 use std::sync::Arc;
5
6 use rustc_ast as ast;
7 use rustc_data_structures::fx::FxHashSet;
8 use rustc_hir as hir;
9 use rustc_hir::def::{DefKind, Res};
10 use rustc_hir::def_id::DefId;
11 use rustc_hir::Mutability;
12 use rustc_metadata::creader::LoadedMacro;
13 use rustc_middle::ty::{self, TyCtxt};
14 use rustc_span::hygiene::MacroKind;
15 use rustc_span::symbol::{kw, sym, Symbol};
16 use rustc_span::Span;
17
18 use crate::clean::{
19     self, Attributes, AttributesExt, FakeDefId, GetDefId, NestedAttributesExt, ToSource, Type,
20 };
21 use crate::core::DocContext;
22 use crate::formats::item_type::ItemType;
23
24 use super::{Clean, Visibility};
25
26 type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>;
27
28 /// Attempt to inline a definition into this AST.
29 ///
30 /// This function will fetch the definition specified, and if it is
31 /// from another crate it will attempt to inline the documentation
32 /// from the other crate into this crate.
33 ///
34 /// This is primarily used for `pub use` statements which are, in general,
35 /// implementation details. Inlining the documentation should help provide a
36 /// better experience when reading the documentation in this use case.
37 ///
38 /// The returned value is `None` if the definition could not be inlined,
39 /// and `Some` of a vector of items if it was successfully expanded.
40 ///
41 /// `parent_module` refers to the parent of the *re-export*, not the original item.
42 crate fn try_inline(
43     cx: &mut DocContext<'_>,
44     parent_module: DefId,
45     res: Res,
46     name: Symbol,
47     attrs: Option<Attrs<'_>>,
48     visited: &mut FxHashSet<DefId>,
49 ) -> Option<Vec<clean::Item>> {
50     let did = res.opt_def_id()?;
51     if did.is_local() {
52         return None;
53     }
54     let mut ret = Vec::new();
55
56     debug!("attrs={:?}", attrs);
57     let attrs_clone = attrs;
58
59     let kind = match res {
60         Res::Def(DefKind::Trait, did) => {
61             record_extern_fqn(cx, did, ItemType::Trait);
62             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
63             clean::TraitItem(build_external_trait(cx, did))
64         }
65         Res::Def(DefKind::Fn, did) => {
66             record_extern_fqn(cx, did, ItemType::Function);
67             clean::FunctionItem(build_external_function(cx, did))
68         }
69         Res::Def(DefKind::Struct, did) => {
70             record_extern_fqn(cx, did, ItemType::Struct);
71             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
72             clean::StructItem(build_struct(cx, did))
73         }
74         Res::Def(DefKind::Union, did) => {
75             record_extern_fqn(cx, did, ItemType::Union);
76             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
77             clean::UnionItem(build_union(cx, did))
78         }
79         Res::Def(DefKind::TyAlias, did) => {
80             record_extern_fqn(cx, did, ItemType::Typedef);
81             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
82             clean::TypedefItem(build_type_alias(cx, did), false)
83         }
84         Res::Def(DefKind::Enum, did) => {
85             record_extern_fqn(cx, did, ItemType::Enum);
86             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
87             clean::EnumItem(build_enum(cx, did))
88         }
89         Res::Def(DefKind::ForeignTy, did) => {
90             record_extern_fqn(cx, did, ItemType::ForeignType);
91             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
92             clean::ForeignTypeItem
93         }
94         // Never inline enum variants but leave them shown as re-exports.
95         Res::Def(DefKind::Variant, _) => return None,
96         // Assume that enum variants and struct types are re-exported next to
97         // their constructors.
98         Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()),
99         Res::Def(DefKind::Mod, did) => {
100             record_extern_fqn(cx, did, ItemType::Module);
101             clean::ModuleItem(build_module(cx, did, visited))
102         }
103         Res::Def(DefKind::Static, did) => {
104             record_extern_fqn(cx, did, ItemType::Static);
105             clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))
106         }
107         Res::Def(DefKind::Const, did) => {
108             record_extern_fqn(cx, did, ItemType::Constant);
109             clean::ConstantItem(build_const(cx, did))
110         }
111         Res::Def(DefKind::Macro(kind), did) => {
112             let mac = build_macro(cx, did, name);
113
114             let type_kind = match kind {
115                 MacroKind::Bang => ItemType::Macro,
116                 MacroKind::Attr => ItemType::ProcAttribute,
117                 MacroKind::Derive => ItemType::ProcDerive,
118             };
119             record_extern_fqn(cx, did, type_kind);
120             mac
121         }
122         _ => return None,
123     };
124
125     let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs_clone);
126     cx.inlined.insert(did.into());
127     ret.push(clean::Item::from_def_id_and_attrs_and_parts(
128         did,
129         Some(name),
130         kind,
131         box attrs,
132         cx,
133         cfg,
134     ));
135     Some(ret)
136 }
137
138 crate fn try_inline_glob(
139     cx: &mut DocContext<'_>,
140     res: Res,
141     visited: &mut FxHashSet<DefId>,
142 ) -> Option<Vec<clean::Item>> {
143     let did = res.opt_def_id()?;
144     if did.is_local() {
145         return None;
146     }
147
148     match res {
149         Res::Def(DefKind::Mod, did) => {
150             let m = build_module(cx, did, visited);
151             Some(m.items)
152         }
153         // glob imports on things like enums aren't inlined even for local exports, so just bail
154         _ => None,
155     }
156 }
157
158 crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
159     cx.tcx.get_attrs(did)
160 }
161
162 /// Record an external fully qualified name in the external_paths cache.
163 ///
164 /// These names are used later on by HTML rendering to generate things like
165 /// source links back to the original item.
166 crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
167     let crate_name = cx.tcx.crate_name(did.krate).to_string();
168
169     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
170         // extern blocks have an empty name
171         let s = elem.data.to_string();
172         if !s.is_empty() { Some(s) } else { None }
173     });
174     let fqn = if let ItemType::Macro = kind {
175         // Check to see if it is a macro 2.0 or built-in macro
176         if matches!(
177             cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())),
178             LoadedMacro::MacroDef(def, _)
179                 if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def)
180                     if !ast_def.macro_rules)
181         ) {
182             once(crate_name).chain(relative).collect()
183         } else {
184             vec![crate_name, relative.last().expect("relative was empty")]
185         }
186     } else {
187         once(crate_name).chain(relative).collect()
188     };
189
190     if did.is_local() {
191         cx.cache.exact_paths.insert(did, fqn);
192     } else {
193         cx.cache.external_paths.insert(did, (fqn, kind));
194     }
195 }
196
197 crate fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait {
198     let trait_items = cx
199         .tcx
200         .associated_items(did)
201         .in_definition_order()
202         .map(|item| {
203             // When building an external trait, the cleaned trait will have all items public,
204             // which causes methods to have a `pub` prefix, which is invalid since items in traits
205             // can not have a visibility prefix. Thus we override the visibility here manually.
206             // See https://github.com/rust-lang/rust/issues/81274
207             clean::Item { visibility: Visibility::Inherited, ..item.clean(cx) }
208         })
209         .collect();
210
211     let predicates = cx.tcx.predicates_of(did);
212     let generics = (cx.tcx.generics_of(did), predicates).clean(cx);
213     let generics = filter_non_trait_generics(did, generics);
214     let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
215     let is_auto = cx.tcx.trait_is_auto(did);
216     clean::Trait {
217         unsafety: cx.tcx.trait_def(did).unsafety,
218         generics,
219         items: trait_items,
220         bounds: supertrait_bounds,
221         is_auto,
222     }
223 }
224
225 fn build_external_function(cx: &mut DocContext<'_>, did: DefId) -> clean::Function {
226     let sig = cx.tcx.fn_sig(did);
227
228     let constness =
229         if cx.tcx.is_const_fn_raw(did) { hir::Constness::Const } else { hir::Constness::NotConst };
230     let asyncness = cx.tcx.asyncness(did);
231     let predicates = cx.tcx.predicates_of(did);
232     let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
233         ((cx.tcx.generics_of(did), predicates).clean(cx), (did, sig).clean(cx))
234     });
235     clean::Function {
236         decl,
237         generics,
238         header: hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness },
239     }
240 }
241
242 fn build_enum(cx: &mut DocContext<'_>, did: DefId) -> clean::Enum {
243     let predicates = cx.tcx.explicit_predicates_of(did);
244
245     clean::Enum {
246         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
247         variants_stripped: false,
248         variants: cx.tcx.adt_def(did).variants.clean(cx),
249     }
250 }
251
252 fn build_struct(cx: &mut DocContext<'_>, did: DefId) -> clean::Struct {
253     let predicates = cx.tcx.explicit_predicates_of(did);
254     let variant = cx.tcx.adt_def(did).non_enum_variant();
255
256     clean::Struct {
257         struct_type: variant.ctor_kind,
258         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
259         fields: variant.fields.clean(cx),
260         fields_stripped: false,
261     }
262 }
263
264 fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
265     let predicates = cx.tcx.explicit_predicates_of(did);
266     let variant = cx.tcx.adt_def(did).non_enum_variant();
267
268     clean::Union {
269         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
270         fields: variant.fields.clean(cx),
271         fields_stripped: false,
272     }
273 }
274
275 fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::Typedef {
276     let predicates = cx.tcx.explicit_predicates_of(did);
277     let type_ = cx.tcx.type_of(did).clean(cx);
278
279     clean::Typedef {
280         type_,
281         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
282         item_type: None,
283     }
284 }
285
286 /// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport.
287 crate fn build_impls(
288     cx: &mut DocContext<'_>,
289     parent_module: Option<DefId>,
290     did: DefId,
291     attrs: Option<Attrs<'_>>,
292     ret: &mut Vec<clean::Item>,
293 ) {
294     let tcx = cx.tcx;
295
296     // for each implementation of an item represented by `did`, build the clean::Item for that impl
297     for &did in tcx.inherent_impls(did).iter() {
298         build_impl(cx, parent_module, did, attrs, ret);
299     }
300 }
301
302 /// `parent_module` refers to the parent of the re-export, not the original item
303 fn merge_attrs(
304     cx: &mut DocContext<'_>,
305     parent_module: Option<DefId>,
306     old_attrs: Attrs<'_>,
307     new_attrs: Option<Attrs<'_>>,
308 ) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
309     // NOTE: If we have additional attributes (from a re-export),
310     // always insert them first. This ensure that re-export
311     // doc comments show up before the original doc comments
312     // when we render them.
313     if let Some(inner) = new_attrs {
314         let mut both = inner.to_vec();
315         both.extend_from_slice(old_attrs);
316         (
317             if let Some(new_id) = parent_module {
318                 Attributes::from_ast(old_attrs, Some((inner, new_id)))
319             } else {
320                 Attributes::from_ast(&both, None)
321             },
322             both.cfg(cx.sess()),
323         )
324     } else {
325         (old_attrs.clean(cx), old_attrs.cfg(cx.sess()))
326     }
327 }
328
329 /// Builds a specific implementation of a type. The `did` could be a type method or trait method.
330 crate fn build_impl(
331     cx: &mut DocContext<'_>,
332     parent_module: impl Into<Option<DefId>>,
333     did: DefId,
334     attrs: Option<Attrs<'_>>,
335     ret: &mut Vec<clean::Item>,
336 ) {
337     if !cx.inlined.insert(did.into()) {
338         return;
339     }
340
341     let tcx = cx.tcx;
342     let associated_trait = tcx.impl_trait_ref(did);
343
344     // Only inline impl if the implemented trait is
345     // reachable in rustdoc generated documentation
346     if !did.is_local() {
347         if let Some(traitref) = associated_trait {
348             let did = traitref.def_id;
349             if !cx.cache.access_levels.is_public(did) {
350                 return;
351             }
352
353             if let Some(stab) = tcx.lookup_stability(did) {
354                 if stab.level.is_unstable() && stab.feature == sym::rustc_private {
355                     return;
356                 }
357             }
358         }
359     }
360
361     let impl_item = match did.as_local() {
362         Some(did) => {
363             let hir_id = tcx.hir().local_def_id_to_hir_id(did);
364             match &tcx.hir().expect_item(hir_id).kind {
365                 hir::ItemKind::Impl(impl_) => Some(impl_),
366                 _ => panic!("`DefID` passed to `build_impl` is not an `impl"),
367             }
368         }
369         None => None,
370     };
371
372     let for_ = match &impl_item {
373         Some(impl_) => impl_.self_ty.clean(cx),
374         None => tcx.type_of(did).clean(cx),
375     };
376
377     // Only inline impl if the implementing type is
378     // reachable in rustdoc generated documentation
379     if !did.is_local() {
380         if let Some(did) = for_.def_id() {
381             if !cx.cache.access_levels.is_public(did) {
382                 return;
383             }
384
385             if let Some(stab) = tcx.lookup_stability(did) {
386                 if stab.level.is_unstable() && stab.feature == sym::rustc_private {
387                     return;
388                 }
389             }
390         }
391     }
392
393     let predicates = tcx.explicit_predicates_of(did);
394     let (trait_items, generics) = match impl_item {
395         Some(impl_) => (
396             impl_
397                 .items
398                 .iter()
399                 .map(|item| tcx.hir().impl_item(item.id).clean(cx))
400                 .collect::<Vec<_>>(),
401             impl_.generics.clean(cx),
402         ),
403         None => (
404             tcx.associated_items(did)
405                 .in_definition_order()
406                 .filter_map(|item| {
407                     if associated_trait.is_some() || item.vis == ty::Visibility::Public {
408                         Some(item.clean(cx))
409                     } else {
410                         None
411                     }
412                 })
413                 .collect::<Vec<_>>(),
414             clean::enter_impl_trait(cx, |cx| (tcx.generics_of(did), predicates).clean(cx)),
415         ),
416     };
417     let polarity = tcx.impl_polarity(did);
418     let trait_ = associated_trait.clean(cx).map(|bound| match bound {
419         clean::GenericBound::TraitBound(polyt, _) => polyt.trait_,
420         clean::GenericBound::Outlives(..) => unreachable!(),
421     });
422     if trait_.def_id() == tcx.lang_items().deref_trait() {
423         super::build_deref_target_impls(cx, &trait_items, ret);
424     }
425
426     // Return if the trait itself or any types of the generic parameters are doc(hidden).
427     let mut stack: Vec<&Type> = trait_.iter().collect();
428     stack.push(&for_);
429     while let Some(ty) = stack.pop() {
430         if let Some(did) = ty.def_id() {
431             if cx.tcx.get_attrs(did).lists(sym::doc).has_word(sym::hidden) {
432                 return;
433             }
434         }
435         if let Some(generics) = ty.generics() {
436             stack.extend(generics);
437         }
438     }
439
440     if let Some(trait_did) = trait_.def_id() {
441         record_extern_trait(cx, trait_did);
442     }
443
444     let (merged_attrs, cfg) = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
445     debug!("merged_attrs={:?}", merged_attrs);
446
447     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
448     ret.push(clean::Item::from_def_id_and_attrs_and_parts(
449         did,
450         None,
451         clean::ImplItem(clean::Impl {
452             span: clean::types::rustc_span(did, cx.tcx),
453             unsafety: hir::Unsafety::Normal,
454             generics,
455             trait_,
456             for_,
457             items: trait_items,
458             negative_polarity: polarity.clean(cx),
459             synthetic: false,
460             blanket_impl: None,
461         }),
462         box merged_attrs,
463         cx,
464         cfg,
465     ));
466 }
467
468 fn build_module(
469     cx: &mut DocContext<'_>,
470     did: DefId,
471     visited: &mut FxHashSet<DefId>,
472 ) -> clean::Module {
473     let mut items = Vec::new();
474
475     // If we're re-exporting a re-export it may actually re-export something in
476     // two namespaces, so the target may be listed twice. Make sure we only
477     // visit each node at most once.
478     for &item in cx.tcx.item_children(did).iter() {
479         if item.vis == ty::Visibility::Public {
480             if let Some(def_id) = item.res.mod_def_id() {
481                 if did == def_id || !visited.insert(def_id) {
482                     continue;
483                 }
484             }
485             if let Res::PrimTy(p) = item.res {
486                 // Primitive types can't be inlined so generate an import instead.
487                 items.push(clean::Item {
488                     name: None,
489                     attrs: box clean::Attributes::default(),
490                     def_id: FakeDefId::new_fake(did.krate),
491                     visibility: clean::Public,
492                     kind: box clean::ImportItem(clean::Import::new_simple(
493                         item.ident.name,
494                         clean::ImportSource {
495                             path: clean::Path {
496                                 global: false,
497                                 res: item.res,
498                                 segments: vec![clean::PathSegment {
499                                     name: clean::PrimitiveType::from(p).as_sym(),
500                                     args: clean::GenericArgs::AngleBracketed {
501                                         args: Vec::new(),
502                                         bindings: Vec::new(),
503                                     },
504                                 }],
505                             },
506                             did: None,
507                         },
508                         true,
509                     )),
510                     cfg: None,
511                 });
512             } else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) {
513                 items.extend(i)
514             }
515         }
516     }
517
518     let span = clean::Span::from_rustc_span(cx.tcx.def_span(did));
519     clean::Module { items, span }
520 }
521
522 crate fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
523     if let Some(did) = did.as_local() {
524         let hir_id = tcx.hir().local_def_id_to_hir_id(did);
525         rustc_hir_pretty::id_to_string(&tcx.hir(), hir_id)
526     } else {
527         tcx.rendered_const(did)
528     }
529 }
530
531 fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
532     clean::Constant {
533         type_: cx.tcx.type_of(def_id).clean(cx),
534         kind: clean::ConstantKind::Extern { def_id },
535     }
536 }
537
538 fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static {
539     clean::Static {
540         type_: cx.tcx.type_of(did).clean(cx),
541         mutability: if mutable { Mutability::Mut } else { Mutability::Not },
542         expr: None,
543     }
544 }
545
546 fn build_macro(cx: &mut DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind {
547     let imported_from = cx.tcx.crate_name(did.krate);
548     match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
549         LoadedMacro::MacroDef(def, _) => {
550             let matchers: Vec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.kind {
551                 let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
552                 tts.chunks(4).map(|arm| arm[0].span()).collect()
553             } else {
554                 unreachable!()
555             };
556
557             let source = format!(
558                 "macro_rules! {} {{\n{}}}",
559                 name.clean(cx),
560                 matchers
561                     .iter()
562                     .map(|span| { format!("    {} => {{ ... }};\n", span.to_src(cx)) })
563                     .collect::<String>()
564             );
565
566             clean::MacroItem(clean::Macro { source, imported_from: Some(imported_from) })
567         }
568         LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro {
569             kind: ext.macro_kind(),
570             helpers: ext.helper_attrs,
571         }),
572     }
573 }
574
575 /// A trait's generics clause actually contains all of the predicates for all of
576 /// its associated types as well. We specifically move these clauses to the
577 /// associated types instead when displaying, so when we're generating the
578 /// generics for the trait itself we need to be sure to remove them.
579 /// We also need to remove the implied "recursive" Self: Trait bound.
580 ///
581 /// The inverse of this filtering logic can be found in the `Clean`
582 /// implementation for `AssociatedType`
583 fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
584     for pred in &mut g.where_predicates {
585         match *pred {
586             clean::WherePredicate::BoundPredicate {
587                 ty: clean::Generic(ref s),
588                 ref mut bounds,
589                 ..
590             } if *s == kw::SelfUpper => {
591                 bounds.retain(|bound| match *bound {
592                     clean::GenericBound::TraitBound(
593                         clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. },
594                         _,
595                     ) => did != trait_did,
596                     _ => true,
597                 });
598             }
599             _ => {}
600         }
601     }
602
603     g.where_predicates.retain(|pred| match *pred {
604         clean::WherePredicate::BoundPredicate {
605             ty:
606                 clean::QPath {
607                     self_type: box clean::Generic(ref s),
608                     trait_: box clean::ResolvedPath { did, .. },
609                     name: ref _name,
610                     ..
611                 },
612             ref bounds,
613             ..
614         } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did),
615         _ => true,
616     });
617     g
618 }
619
620 /// Supertrait bounds for a trait are also listed in the generics coming from
621 /// the metadata for a crate, so we want to separate those out and create a new
622 /// list of explicit supertrait bounds to render nicely.
623 fn separate_supertrait_bounds(
624     mut g: clean::Generics,
625 ) -> (clean::Generics, Vec<clean::GenericBound>) {
626     let mut ty_bounds = Vec::new();
627     g.where_predicates.retain(|pred| match *pred {
628         clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds, .. }
629             if *s == kw::SelfUpper =>
630         {
631             ty_bounds.extend(bounds.iter().cloned());
632             false
633         }
634         _ => true,
635     });
636     (g, ty_bounds)
637 }
638
639 crate fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) {
640     if did.is_local() {
641         return;
642     }
643
644     {
645         if cx.external_traits.borrow().contains_key(&did) || cx.active_extern_traits.contains(&did)
646         {
647             return;
648         }
649     }
650
651     {
652         cx.active_extern_traits.insert(did);
653     }
654
655     debug!("record_extern_trait: {:?}", did);
656     let trait_ = build_external_trait(cx, did);
657
658     let trait_ = clean::TraitWithExtraInfo {
659         trait_,
660         is_notable: clean::utils::has_doc_flag(cx.tcx.get_attrs(did), sym::notable_trait),
661     };
662     cx.external_traits.borrow_mut().insert(did, trait_);
663     cx.active_extern_traits.remove(&did);
664 }