1 //! Detecting diagnostic items.
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.
8 //! * Traits like `Debug`, that have no bearing on language semantics
10 //! * Compiler internal types like `Ty` and `TyCtxt`
13 use rustc_data_structures
::fx
::FxHashMap
;
15 use rustc_hir
::def_id
::{DefId, LOCAL_CRATE}
;
16 use rustc_hir
::itemlikevisit
::ItemLikeVisitor
;
17 use rustc_middle
::ty
::query
::Providers
;
18 use rustc_middle
::ty
::TyCtxt
;
19 use rustc_session
::Session
;
20 use rustc_span
::symbol
::{sym, Symbol}
;
22 struct DiagnosticItemCollector
<'tcx
> {
23 // items from this crate
24 items
: FxHashMap
<Symbol
, DefId
>,
28 impl<'v
, 'tcx
> ItemLikeVisitor
<'v
> for DiagnosticItemCollector
<'tcx
> {
29 fn visit_item(&mut self, item
: &hir
::Item
<'_
>) {
30 self.observe_item(&item
.attrs
, item
.hir_id
);
33 fn visit_trait_item(&mut self, trait_item
: &hir
::TraitItem
<'_
>) {
34 self.observe_item(&trait_item
.attrs
, trait_item
.hir_id
);
37 fn visit_impl_item(&mut self, impl_item
: &hir
::ImplItem
<'_
>) {
38 self.observe_item(&impl_item
.attrs
, impl_item
.hir_id
);
42 impl<'tcx
> DiagnosticItemCollector
<'tcx
> {
43 fn new(tcx
: TyCtxt
<'tcx
>) -> DiagnosticItemCollector
<'tcx
> {
44 DiagnosticItemCollector { tcx, items: Default::default() }
47 fn observe_item(&mut self, attrs
: &[ast
::Attribute
], hir_id
: hir
::HirId
) {
48 if let Some(name
) = extract(&self.tcx
.sess
, attrs
) {
49 let def_id
= self.tcx
.hir().local_def_id(hir_id
);
50 // insert into our table
51 collect_item(self.tcx
, &mut self.items
, name
, def_id
.to_def_id());
58 items
: &mut FxHashMap
<Symbol
, DefId
>,
62 // Check for duplicates.
63 if let Some(original_def_id
) = items
.insert(name
, item_def_id
) {
64 if original_def_id
!= item_def_id
{
65 let mut err
= match tcx
.hir().span_if_local(item_def_id
) {
66 Some(span
) => tcx
.sess
.struct_span_err(
68 &format
!("duplicate diagnostic item found: `{}`.", name
),
70 None
=> tcx
.sess
.struct_err(&format
!(
71 "duplicate diagnostic item in crate `{}`: `{}`.",
72 tcx
.crate_name(item_def_id
.krate
),
76 if let Some(span
) = tcx
.hir().span_if_local(original_def_id
) {
77 err
.span_note(span
, "the diagnostic item is first defined here");
80 "the diagnostic item is first defined in crate `{}`.",
81 tcx
.crate_name(original_def_id
.krate
)
89 /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
90 fn extract(sess
: &Session
, attrs
: &[ast
::Attribute
]) -> Option
<Symbol
> {
91 attrs
.iter().find_map(|attr
| {
92 if sess
.check_name(attr
, sym
::rustc_diagnostic_item
) { attr.value_str() }
else { None }
96 /// Traverse and collect the diagnostic items in the current
97 fn collect
<'tcx
>(tcx
: TyCtxt
<'tcx
>) -> FxHashMap
<Symbol
, DefId
> {
98 // Initialize the collector.
99 let mut collector
= DiagnosticItemCollector
::new(tcx
);
101 // Collect diagnostic items in this crate.
102 tcx
.hir().krate().visit_all_item_likes(&mut collector
);
107 /// Traverse and collect all the diagnostic items in all crates.
108 fn collect_all
<'tcx
>(tcx
: TyCtxt
<'tcx
>) -> FxHashMap
<Symbol
, DefId
> {
109 // Initialize the collector.
110 let mut collector
= FxHashMap
::default();
112 // Collect diagnostic items in other crates.
113 for &cnum
in tcx
.crates().iter().chain(std
::iter
::once(&LOCAL_CRATE
)) {
114 for (&name
, &def_id
) in tcx
.diagnostic_items(cnum
).iter() {
115 collect_item(tcx
, &mut collector
, name
, def_id
);
122 pub fn provide(providers
: &mut Providers
) {
123 providers
.diagnostic_items
= |tcx
, id
| {
124 assert_eq
!(id
, LOCAL_CRATE
);
127 providers
.all_diagnostic_items
= |tcx
, id
| {
128 assert_eq
!(id
, LOCAL_CRATE
);