]>
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 | ||
3dfed10e | 12 | use rustc_ast as ast; |
dfeec247 XL |
13 | use rustc_data_structures::fx::FxHashMap; |
14 | use rustc_hir as hir; | |
dfeec247 | 15 | use rustc_hir::itemlikevisit::ItemLikeVisitor; |
ba9703b0 XL |
16 | use rustc_middle::ty::query::Providers; |
17 | use rustc_middle::ty::TyCtxt; | |
3dfed10e | 18 | use rustc_session::Session; |
6a06907d | 19 | use rustc_span::def_id::{DefId, LocalDefId, LOCAL_CRATE}; |
dfeec247 | 20 | use rustc_span::symbol::{sym, Symbol}; |
e1599b0c XL |
21 | |
22 | struct DiagnosticItemCollector<'tcx> { | |
23 | // items from this crate | |
24 | items: FxHashMap<Symbol, DefId>, | |
25 | tcx: TyCtxt<'tcx>, | |
26 | } | |
27 | ||
28 | impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> { | |
dfeec247 | 29 | fn visit_item(&mut self, item: &hir::Item<'_>) { |
6a06907d | 30 | self.observe_item(item.def_id); |
e1599b0c XL |
31 | } |
32 | ||
dfeec247 | 33 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { |
6a06907d | 34 | self.observe_item(trait_item.def_id); |
e1599b0c XL |
35 | } |
36 | ||
dfeec247 | 37 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { |
6a06907d | 38 | self.observe_item(impl_item.def_id); |
e1599b0c | 39 | } |
fc512014 XL |
40 | |
41 | fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { | |
6a06907d | 42 | self.observe_item(foreign_item.def_id); |
fc512014 | 43 | } |
e1599b0c XL |
44 | } |
45 | ||
46 | impl<'tcx> DiagnosticItemCollector<'tcx> { | |
47 | fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> { | |
dfeec247 | 48 | DiagnosticItemCollector { tcx, items: Default::default() } |
e1599b0c XL |
49 | } |
50 | ||
6a06907d XL |
51 | fn observe_item(&mut self, def_id: LocalDefId) { |
52 | let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); | |
53 | let attrs = self.tcx.hir().attrs(hir_id); | |
3dfed10e | 54 | if let Some(name) = extract(&self.tcx.sess, attrs) { |
e1599b0c | 55 | // insert into our table |
f9f354fc | 56 | collect_item(self.tcx, &mut self.items, name, def_id.to_def_id()); |
e1599b0c XL |
57 | } |
58 | } | |
59 | } | |
60 | ||
61 | fn collect_item( | |
62 | tcx: TyCtxt<'_>, | |
63 | items: &mut FxHashMap<Symbol, DefId>, | |
64 | name: Symbol, | |
65 | item_def_id: DefId, | |
66 | ) { | |
67 | // Check for duplicates. | |
68 | if let Some(original_def_id) = items.insert(name, item_def_id) { | |
69 | if original_def_id != item_def_id { | |
70 | let mut err = match tcx.hir().span_if_local(item_def_id) { | |
71 | Some(span) => tcx.sess.struct_span_err( | |
72 | span, | |
dfeec247 XL |
73 | &format!("duplicate diagnostic item found: `{}`.", name), |
74 | ), | |
e1599b0c | 75 | None => tcx.sess.struct_err(&format!( |
dfeec247 XL |
76 | "duplicate diagnostic item in crate `{}`: `{}`.", |
77 | tcx.crate_name(item_def_id.krate), | |
78 | name | |
79 | )), | |
e1599b0c XL |
80 | }; |
81 | if let Some(span) = tcx.hir().span_if_local(original_def_id) { | |
74b04a01 | 82 | err.span_note(span, "the diagnostic item is first defined here"); |
e1599b0c | 83 | } else { |
dfeec247 | 84 | err.note(&format!( |
74b04a01 | 85 | "the diagnostic item is first defined in crate `{}`.", |
dfeec247 XL |
86 | tcx.crate_name(original_def_id.krate) |
87 | )); | |
e1599b0c XL |
88 | } |
89 | err.emit(); | |
90 | } | |
91 | } | |
92 | } | |
93 | ||
94 | /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. | |
3dfed10e | 95 | fn extract(sess: &Session, attrs: &[ast::Attribute]) -> Option<Symbol> { |
e1599b0c | 96 | attrs.iter().find_map(|attr| { |
3dfed10e | 97 | if sess.check_name(attr, sym::rustc_diagnostic_item) { attr.value_str() } else { None } |
e1599b0c XL |
98 | }) |
99 | } | |
100 | ||
101 | /// Traverse and collect the diagnostic items in the current | |
f9f354fc | 102 | fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap<Symbol, DefId> { |
e1599b0c XL |
103 | // Initialize the collector. |
104 | let mut collector = DiagnosticItemCollector::new(tcx); | |
105 | ||
106 | // Collect diagnostic items in this crate. | |
107 | tcx.hir().krate().visit_all_item_likes(&mut collector); | |
fc512014 XL |
108 | |
109 | for m in tcx.hir().krate().exported_macros { | |
6a06907d | 110 | collector.observe_item(m.def_id); |
1b1a35ee | 111 | } |
e1599b0c | 112 | |
f9f354fc | 113 | collector.items |
e1599b0c XL |
114 | } |
115 | ||
e1599b0c | 116 | /// Traverse and collect all the diagnostic items in all crates. |
f9f354fc | 117 | fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap<Symbol, DefId> { |
e1599b0c XL |
118 | // Initialize the collector. |
119 | let mut collector = FxHashMap::default(); | |
120 | ||
121 | // Collect diagnostic items in other crates. | |
122 | for &cnum in tcx.crates().iter().chain(std::iter::once(&LOCAL_CRATE)) { | |
123 | for (&name, &def_id) in tcx.diagnostic_items(cnum).iter() { | |
124 | collect_item(tcx, &mut collector, name, def_id); | |
125 | } | |
126 | } | |
127 | ||
f9f354fc | 128 | collector |
e1599b0c | 129 | } |
dfeec247 | 130 | |
f035d41b | 131 | pub fn provide(providers: &mut Providers) { |
dfeec247 XL |
132 | providers.diagnostic_items = |tcx, id| { |
133 | assert_eq!(id, LOCAL_CRATE); | |
134 | collect(tcx) | |
135 | }; | |
136 | providers.all_diagnostic_items = |tcx, id| { | |
137 | assert_eq!(id, LOCAL_CRATE); | |
138 | collect_all(tcx) | |
139 | }; | |
140 | } |