]>
Commit | Line | Data |
---|---|---|
e1599b0c XL |
1 | //! Detecting diagnostic items. |
2 | //! | |
3 | //! Diagnostic items are items that are not language-inherent, but can reasonably be expected to | |
4 | //! exist for diagnostic purposes. This allows diagnostic authors to refer to specific items | |
5 | //! directly, without having to guess module paths and crates. | |
6 | //! Examples are: | |
7 | //! | |
8 | //! * Traits like `Debug`, that have no bearing on language semantics | |
9 | //! | |
10 | //! * Compiler internal types like `Ty` and `TyCtxt` | |
11 | ||
12 | use crate::hir::def_id::{DefId, LOCAL_CRATE}; | |
13 | use crate::ty::TyCtxt; | |
14 | use crate::util::nodemap::FxHashMap; | |
15 | ||
16 | use syntax::ast; | |
17 | use syntax::symbol::{Symbol, sym}; | |
18 | use crate::hir::itemlikevisit::ItemLikeVisitor; | |
19 | use crate::hir; | |
20 | ||
21 | struct DiagnosticItemCollector<'tcx> { | |
22 | // items from this crate | |
23 | items: FxHashMap<Symbol, DefId>, | |
24 | tcx: TyCtxt<'tcx>, | |
25 | } | |
26 | ||
27 | impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> { | |
28 | fn visit_item(&mut self, item: &hir::Item) { | |
29 | self.observe_item(&item.attrs, item.hir_id); | |
30 | } | |
31 | ||
32 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { | |
33 | self.observe_item(&trait_item.attrs, trait_item.hir_id); | |
34 | } | |
35 | ||
36 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) { | |
37 | self.observe_item(&impl_item.attrs, impl_item.hir_id); | |
38 | } | |
39 | } | |
40 | ||
41 | impl<'tcx> DiagnosticItemCollector<'tcx> { | |
42 | fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> { | |
43 | DiagnosticItemCollector { | |
44 | tcx, | |
45 | items: Default::default(), | |
46 | } | |
47 | } | |
48 | ||
49 | fn observe_item(&mut self, attrs: &[ast::Attribute], hir_id: hir::HirId) { | |
50 | if let Some(name) = extract(attrs) { | |
51 | let def_id = self.tcx.hir().local_def_id(hir_id); | |
52 | // insert into our table | |
53 | collect_item(self.tcx, &mut self.items, name, def_id); | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
58 | fn collect_item( | |
59 | tcx: TyCtxt<'_>, | |
60 | items: &mut FxHashMap<Symbol, DefId>, | |
61 | name: Symbol, | |
62 | item_def_id: DefId, | |
63 | ) { | |
64 | // Check for duplicates. | |
65 | if let Some(original_def_id) = items.insert(name, item_def_id) { | |
66 | if original_def_id != item_def_id { | |
67 | let mut err = match tcx.hir().span_if_local(item_def_id) { | |
68 | Some(span) => tcx.sess.struct_span_err( | |
69 | span, | |
70 | &format!("duplicate diagnostic item found: `{}`.", name)), | |
71 | None => tcx.sess.struct_err(&format!( | |
72 | "duplicate diagnostic item in crate `{}`: `{}`.", | |
73 | tcx.crate_name(item_def_id.krate), | |
74 | name)), | |
75 | }; | |
76 | if let Some(span) = tcx.hir().span_if_local(original_def_id) { | |
77 | span_note!(&mut err, span, "first defined here."); | |
78 | } else { | |
79 | err.note(&format!("first defined in crate `{}`.", | |
80 | tcx.crate_name(original_def_id.krate))); | |
81 | } | |
82 | err.emit(); | |
83 | } | |
84 | } | |
85 | } | |
86 | ||
87 | /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. | |
88 | fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> { | |
89 | attrs.iter().find_map(|attr| { | |
90 | if attr.check_name(sym::rustc_diagnostic_item) { | |
91 | attr.value_str() | |
92 | } else { | |
93 | None | |
94 | } | |
95 | }) | |
96 | } | |
97 | ||
98 | /// Traverse and collect the diagnostic items in the current | |
99 | pub fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap<Symbol, DefId> { | |
100 | // Initialize the collector. | |
101 | let mut collector = DiagnosticItemCollector::new(tcx); | |
102 | ||
103 | // Collect diagnostic items in this crate. | |
104 | tcx.hir().krate().visit_all_item_likes(&mut collector); | |
105 | ||
106 | tcx.arena.alloc(collector.items) | |
107 | } | |
108 | ||
109 | ||
110 | /// Traverse and collect all the diagnostic items in all crates. | |
111 | pub fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap<Symbol, DefId> { | |
112 | // Initialize the collector. | |
113 | let mut collector = FxHashMap::default(); | |
114 | ||
115 | // Collect diagnostic items in other crates. | |
116 | for &cnum in tcx.crates().iter().chain(std::iter::once(&LOCAL_CRATE)) { | |
117 | for (&name, &def_id) in tcx.diagnostic_items(cnum).iter() { | |
118 | collect_item(tcx, &mut collector, name, def_id); | |
119 | } | |
120 | } | |
121 | ||
122 | tcx.arena.alloc(collector) | |
123 | } |