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_hir
::diagnostic_items
::DiagnosticItems
;
14 use rustc_middle
::ty
::query
::Providers
;
15 use rustc_middle
::ty
::TyCtxt
;
16 use rustc_span
::def_id
::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}
;
17 use rustc_span
::symbol
::{sym, Symbol}
;
19 fn observe_item
<'tcx
>(
21 diagnostic_items
: &mut DiagnosticItems
,
24 let hir_id
= tcx
.hir().local_def_id_to_hir_id(def_id
);
25 let attrs
= tcx
.hir().attrs(hir_id
);
26 if let Some(name
) = extract(attrs
) {
27 // insert into our table
28 collect_item(tcx
, diagnostic_items
, name
, def_id
.to_def_id());
32 fn collect_item(tcx
: TyCtxt
<'_
>, items
: &mut DiagnosticItems
, name
: Symbol
, item_def_id
: DefId
) {
33 items
.id_to_name
.insert(item_def_id
, name
);
34 if let Some(original_def_id
) = items
.name_to_id
.insert(name
, item_def_id
) {
35 if original_def_id
!= item_def_id
{
36 let mut err
= match tcx
.hir().span_if_local(item_def_id
) {
39 .struct_span_err(span
, &format
!("duplicate diagnostic item found: `{name}`.")),
40 None
=> tcx
.sess
.struct_err(&format
!(
41 "duplicate diagnostic item in crate `{}`: `{}`.",
42 tcx
.crate_name(item_def_id
.krate
),
46 if let Some(span
) = tcx
.hir().span_if_local(original_def_id
) {
47 err
.span_note(span
, "the diagnostic item is first defined here");
50 "the diagnostic item is first defined in crate `{}`.",
51 tcx
.crate_name(original_def_id
.krate
)
59 /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
60 fn extract(attrs
: &[ast
::Attribute
]) -> Option
<Symbol
> {
61 attrs
.iter().find_map(|attr
| {
62 if attr
.has_name(sym
::rustc_diagnostic_item
) { attr.value_str() }
else { None }
66 /// Traverse and collect the diagnostic items in the current
67 fn diagnostic_items
<'tcx
>(tcx
: TyCtxt
<'tcx
>, cnum
: CrateNum
) -> DiagnosticItems
{
68 assert_eq
!(cnum
, LOCAL_CRATE
);
70 // Initialize the collector.
71 let mut diagnostic_items
= DiagnosticItems
::default();
73 // Collect diagnostic items in this crate.
74 let crate_items
= tcx
.hir_crate_items(());
76 for id
in crate_items
.items() {
77 observe_item(tcx
, &mut diagnostic_items
, id
.def_id
);
80 for id
in crate_items
.trait_items() {
81 observe_item(tcx
, &mut diagnostic_items
, id
.def_id
);
84 for id
in crate_items
.impl_items() {
85 observe_item(tcx
, &mut diagnostic_items
, id
.def_id
);
88 for id
in crate_items
.foreign_items() {
89 observe_item(tcx
, &mut diagnostic_items
, id
.def_id
);
95 /// Traverse and collect all the diagnostic items in all crates.
96 fn all_diagnostic_items
<'tcx
>(tcx
: TyCtxt
<'tcx
>, (): ()) -> DiagnosticItems
{
97 // Initialize the collector.
98 let mut items
= DiagnosticItems
::default();
100 // Collect diagnostic items in other crates.
101 for &cnum
in tcx
.crates(()).iter().chain(std
::iter
::once(&LOCAL_CRATE
)) {
102 for (&name
, &def_id
) in &tcx
.diagnostic_items(cnum
).name_to_id
{
103 collect_item(tcx
, &mut items
, name
, def_id
);
110 pub fn provide(providers
: &mut Providers
) {
111 providers
.diagnostic_items
= diagnostic_items
;
112 providers
.all_diagnostic_items
= all_diagnostic_items
;