|
| 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 | +} |
0 commit comments