about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/liballoc/vec.rs1
-rw-r--r--src/libcore/fmt/mod.rs3
-rw-r--r--src/librustc/arena.rs4
-rw-r--r--src/librustc/lib.rs2
-rw-r--r--src/librustc/lint/internal.rs16
-rw-r--r--src/librustc/middle/diagnostic_items.rs123
-rw-r--r--src/librustc/middle/lang_items.rs2
-rw-r--r--src/librustc/query/mod.rs19
-rw-r--r--src/librustc/ty/context.rs21
-rw-r--r--src/librustc/ty/mod.rs1
-rw-r--r--src/librustc/ty/sty.rs1
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_metadata/cstore_impl.rs1
-rw-r--r--src/librustc_metadata/decoder.rs18
-rw-r--r--src/librustc_metadata/encoder.rs15
-rw-r--r--src/librustc_metadata/schema.rs1
-rw-r--r--src/libsyntax/feature_gate/builtin_attrs.rs11
-rw-r--r--src/libsyntax_pos/symbol.rs5
-rw-r--r--src/test/ui/tool-attributes/diagnostic_item.rs2
-rw-r--r--src/test/ui/tool-attributes/diagnostic_item.stderr17
-rw-r--r--src/test/ui/tool-attributes/diagnostic_item2.rs6
-rw-r--r--src/test/ui/tool-attributes/diagnostic_item3.rs7
22 files changed, 260 insertions, 18 deletions
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index d2798955c46..d5dc2d4b868 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -291,6 +291,7 @@ use crate::raw_vec::RawVec;
 /// [`reserve`]: ../../std/vec/struct.Vec.html#method.reserve
 /// [owned slice]: ../../std/boxed/struct.Box.html
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(all(not(bootstrap), not(test)), rustc_diagnostic_item = "vec_type")]
 pub struct Vec<T> {
     buf: RawVec<T>,
     len: usize,
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index bd31d25dd03..7e35188bc10 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -518,7 +518,8 @@ impl Display for Arguments<'_> {
     label="`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`",
 )]
 #[doc(alias = "{:?}")]
-#[lang = "debug_trait"]
+#[cfg_attr(boostrap_stdarch_ignore_this, lang = "debug_trait")]
+#[cfg_attr(not(boostrap_stdarch_ignore_this), rustc_diagnostic_item = "debug_trait")]
 pub trait Debug {
     /// Formats the value using the given formatter.
     ///
diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs
index a38dbbdd50c..b3a561ef74b 100644
--- a/src/librustc/arena.rs
+++ b/src/librustc/arena.rs
@@ -94,6 +94,10 @@ macro_rules! arena_types {
                         rustc::hir::def_id::CrateNum
                     >
                 >,
+            [few] diagnostic_items: rustc_data_structures::fx::FxHashMap<
+                syntax::symbol::Symbol,
+                rustc::hir::def_id::DefId,
+            >,
             [few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes,
             [decode] generic_predicates: rustc::ty::GenericPredicates<'tcx>,
             [few] lint_levels: rustc::lint::LintLevelMap,
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 8d4cd51e460..368f5bb64fe 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -62,6 +62,7 @@
 #![feature(log_syntax)]
 #![feature(mem_take)]
 #![feature(associated_type_bounds)]
+#![feature(rustc_attrs)]
 
 #![recursion_limit="512"]
 
@@ -109,6 +110,7 @@ pub mod middle {
     pub mod cstore;
     pub mod dead;
     pub mod dependency_format;
+    pub mod diagnostic_items;
     pub mod entry;
     pub mod exported_symbols;
     pub mod free_region;
diff --git a/src/librustc/lint/internal.rs b/src/librustc/lint/internal.rs
index be73b305e2c..13834eaf40f 100644
--- a/src/librustc/lint/internal.rs
+++ b/src/librustc/lint/internal.rs
@@ -159,29 +159,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
 }
 
 fn lint_ty_kind_usage(cx: &LateContext<'_, '_>, segment: &PathSegment) -> bool {
-    if segment.ident.name == sym::TyKind {
-        if let Some(res) = segment.res {
-            if let Some(did) = res.opt_def_id() {
-                return cx.match_def_path(did, TYKIND_PATH);
-            }
+    if let Some(res) = segment.res {
+        if let Some(did) = res.opt_def_id() {
+            return cx.tcx.is_diagnostic_item(sym::TyKind, did);
         }
     }
 
     false
 }
 
-const TYKIND_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::sty, sym::TyKind];
-const TY_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::Ty];
-const TYCTXT_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::context, sym::TyCtxt];
-
 fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty) -> Option<String> {
     match &ty.node {
         TyKind::Path(qpath) => {
             if let QPath::Resolved(_, path) = qpath {
                 let did = path.res.opt_def_id()?;
-                if cx.match_def_path(did, TY_PATH) {
+                if cx.tcx.is_diagnostic_item(sym::Ty, did) {
                     return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
-                } else if cx.match_def_path(did, TYCTXT_PATH) {
+                } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
                     return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
                 }
             }
diff --git a/src/librustc/middle/diagnostic_items.rs b/src/librustc/middle/diagnostic_items.rs
new file mode 100644
index 00000000000..dfae169b278
--- /dev/null
+++ b/src/librustc/middle/diagnostic_items.rs
@@ -0,0 +1,123 @@
+//! Detecting diagnostic items.
+//!
+//! Diagnostic items are items that are not language-inherent, but can reasonably be expected to
+//! exist for diagnostic purposes. This allows diagnostic authors to refer to specific items
+//! directly, without having to guess module paths and crates.
+//! Examples are:
+//!
+//! * Traits like `Debug`, that have no bearing on language semantics
+//!
+//! * Compiler internal types like `Ty` and `TyCtxt`
+
+use crate::hir::def_id::{DefId, LOCAL_CRATE};
+use crate::ty::TyCtxt;
+use crate::util::nodemap::FxHashMap;
+
+use syntax::ast;
+use syntax::symbol::{Symbol, sym};
+use crate::hir::itemlikevisit::ItemLikeVisitor;
+use crate::hir;
+
+struct DiagnosticItemCollector<'tcx> {
+    // items from this crate
+    items: FxHashMap<Symbol, DefId>,
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> {
+    fn visit_item(&mut self, item: &hir::Item) {
+        self.observe_item(&item.attrs, item.hir_id);
+    }
+
+    fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) {
+        self.observe_item(&trait_item.attrs, trait_item.hir_id);
+    }
+
+    fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) {
+        self.observe_item(&impl_item.attrs, impl_item.hir_id);
+    }
+}
+
+impl<'tcx> DiagnosticItemCollector<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>) -> DiagnosticItemCollector<'tcx> {
+        DiagnosticItemCollector {
+            tcx,
+            items: Default::default(),
+        }
+    }
+
+    fn observe_item(&mut self, attrs: &[ast::Attribute], hir_id: hir::HirId) {
+        if let Some(name) = extract(attrs) {
+            let def_id = self.tcx.hir().local_def_id(hir_id);
+            // insert into our table
+            collect_item(self.tcx, &mut self.items, name, def_id);
+        }
+    }
+}
+
+fn collect_item(
+    tcx: TyCtxt<'_>,
+    items: &mut FxHashMap<Symbol, DefId>,
+    name: Symbol,
+    item_def_id: DefId,
+) {
+    // Check for duplicates.
+    if let Some(original_def_id) = items.insert(name, item_def_id) {
+        if original_def_id != item_def_id {
+            let mut err = match tcx.hir().span_if_local(item_def_id) {
+                Some(span) => tcx.sess.struct_span_err(
+                    span,
+                    &format!("duplicate diagnostic item found: `{}`.", name)),
+                None => tcx.sess.struct_err(&format!(
+                        "duplicate diagnostic item in crate `{}`: `{}`.",
+                        tcx.crate_name(item_def_id.krate),
+                        name)),
+            };
+            if let Some(span) = tcx.hir().span_if_local(original_def_id) {
+                span_note!(&mut err, span, "first defined here.");
+            } else {
+                err.note(&format!("first defined in crate `{}`.",
+                                    tcx.crate_name(original_def_id.krate)));
+            }
+            err.emit();
+        }
+    }
+}
+
+/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
+fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
+    attrs.iter().find_map(|attr| {
+        if attr.check_name(sym::rustc_diagnostic_item) {
+            attr.value_str()
+        } else {
+            None
+        }
+    })
+}
+
+/// Traverse and collect the diagnostic items in the current
+pub fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap<Symbol, DefId> {
+    // Initialize the collector.
+    let mut collector = DiagnosticItemCollector::new(tcx);
+
+    // Collect diagnostic items in this crate.
+    tcx.hir().krate().visit_all_item_likes(&mut collector);
+
+    tcx.arena.alloc(collector.items)
+}
+
+
+/// Traverse and collect all the diagnostic items in all crates.
+pub fn collect_all<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx FxHashMap<Symbol, DefId> {
+    // Initialize the collector.
+    let mut collector = FxHashMap::default();
+
+    // Collect diagnostic items in other crates.
+    for &cnum in tcx.crates().iter().chain(std::iter::once(&LOCAL_CRATE)) {
+        for (&name, &def_id) in tcx.diagnostic_items(cnum).iter() {
+            collect_item(tcx, &mut collector, name, def_id);
+        }
+    }
+
+    tcx.arena.alloc(collector)
+}
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index 334c06618bb..6b04600eb75 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -367,8 +367,6 @@ language_item_table! {
 
     MaybeUninitLangItem,         "maybe_uninit",       maybe_uninit,            Target::Union;
 
-    DebugTraitLangItem,          "debug_trait",        debug_trait,             Target::Trait;
-
     // Align offset for stride != 1, must not panic.
     AlignOffsetLangItem,         "align_offset",       align_offset_fn,         Target::Fn;
 
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index c4f7ca51f4a..ef838114f6c 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -804,7 +804,7 @@ rustc_queries! {
     }
 
     BorrowChecking {
-        // Lifetime resolution. See `middle::resolve_lifetimes`.
+        /// Lifetime resolution. See `middle::resolve_lifetimes`.
         query resolve_lifetimes(_: CrateNum) -> &'tcx ResolveLifetimes {
             desc { "resolving lifetimes" }
         }
@@ -846,13 +846,30 @@ rustc_queries! {
             -> &'tcx [(Symbol, Option<Symbol>)] {
             desc { "calculating the lib features defined in a crate" }
         }
+        /// Returns the lang items defined in another crate by loading it from metadata.
+        // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid
+        // of that argument?
         query get_lang_items(_: CrateNum) -> &'tcx LanguageItems {
             eval_always
             desc { "calculating the lang items map" }
         }
+
+        /// Returns all diagnostic items defined in all crates
+        query all_diagnostic_items(_: CrateNum) -> &'tcx FxHashMap<Symbol, DefId> {
+            eval_always
+            desc { "calculating the diagnostic items map" }
+        }
+
+        /// Returns the lang items defined in another crate by loading it from metadata.
         query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] {
             desc { "calculating the lang items defined in a crate" }
         }
+
+        /// Returns the diagnostic items defined in a crate
+        query diagnostic_items(_: CrateNum) -> &'tcx FxHashMap<Symbol, DefId> {
+            desc { "calculating the diagnostic items map in a crate" }
+        }
+
         query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] {
             desc { "calculating the missing lang items in a crate" }
         }
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index c0d86a79882..e240e0df8b9 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -978,6 +978,7 @@ pub struct FreeRegionInfo {
 ///
 /// [rustc guide]: https://rust-lang.github.io/rustc-guide/ty.html
 #[derive(Copy, Clone)]
+#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyCtxt")]
 pub struct TyCtxt<'tcx> {
     gcx: &'tcx GlobalCtxt<'tcx>,
 }
@@ -1308,10 +1309,22 @@ impl<'tcx> TyCtxt<'tcx> {
         self.get_lib_features(LOCAL_CRATE)
     }
 
+    /// Obtain all lang items of this crate and all dependencies (recursively)
     pub fn lang_items(self) -> &'tcx middle::lang_items::LanguageItems {
         self.get_lang_items(LOCAL_CRATE)
     }
 
+    /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to
+    /// compare against another `DefId`, since `is_diagnostic_item` is cheaper.
+    pub fn get_diagnostic_item(self, name: Symbol) -> Option<DefId> {
+        self.all_diagnostic_items(LOCAL_CRATE).get(&name).copied()
+    }
+
+    /// Check whether the diagnostic item with the given `name` has the given `DefId`.
+    pub fn is_diagnostic_item(self, name: Symbol, did: DefId) -> bool {
+        self.diagnostic_items(did.krate).get(&name) == Some(&did)
+    }
+
     pub fn stability(self) -> &'tcx stability::Index<'tcx> {
         self.stability_index(LOCAL_CRATE)
     }
@@ -2896,6 +2909,14 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
         assert_eq!(id, LOCAL_CRATE);
         tcx.arena.alloc(middle::lang_items::collect(tcx))
     };
+    providers.diagnostic_items = |tcx, id| {
+        assert_eq!(id, LOCAL_CRATE);
+        middle::diagnostic_items::collect(tcx)
+    };
+    providers.all_diagnostic_items = |tcx, id| {
+        assert_eq!(id, LOCAL_CRATE);
+        middle::diagnostic_items::collect_all(tcx)
+    };
     providers.maybe_unused_trait_import = |tcx, id| {
         tcx.maybe_unused_trait_imports.contains(&id)
     };
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index eaaaf75f75d..56505c04f0f 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -581,6 +581,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::TyS<'tcx> {
     }
 }
 
+#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "Ty")]
 pub type Ty<'tcx> = &'tcx TyS<'tcx>;
 
 impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {}
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index f41fffe507d..d2edf6fb1ee 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -86,6 +86,7 @@ impl BoundRegion {
 /// AST structure in `libsyntax/ast.rs` as well.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
          RustcEncodable, RustcDecodable, HashStable, Debug)]
+#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyKind")]
 pub enum TyKind<'tcx> {
     /// The primitive boolean type. Written as `bool`.
     Bool,
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index d3c94060e27..26e7b789f8f 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -570,7 +570,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
             _ => return,
         }
 
-        let debug = match cx.tcx.lang_items().debug_trait() {
+        let debug = match cx.tcx.get_diagnostic_item(sym::debug_trait) {
             Some(debug) => debug,
             None => return,
         };
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index ca0cf0a5a66..d6450f00c8b 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -226,6 +226,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
     }
     defined_lib_features => { cdata.get_lib_features(tcx) }
     defined_lang_items => { cdata.get_lang_items(tcx) }
+    diagnostic_items => { cdata.get_diagnostic_items(tcx) }
     missing_lang_items => { cdata.get_missing_lang_items(tcx) }
 
     missing_extern_crate_item => {
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index cdee14d07fb..75d72617047 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -12,6 +12,7 @@ use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
 use rustc::hir::def::{self, Res, DefKind, CtorOf, CtorKind};
 use rustc::hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_data_structures::fingerprint::Fingerprint;
+use rustc_data_structures::fx::FxHashMap;
 use rustc::middle::lang_items;
 use rustc::mir::{self, interpret};
 use rustc::mir::interpret::AllocDecodingSession;
@@ -757,6 +758,23 @@ impl<'a, 'tcx> CrateMetadata {
         }
     }
 
+    /// Iterates over the diagnostic items in the given crate.
+    pub fn get_diagnostic_items(
+        &self,
+        tcx: TyCtxt<'tcx>,
+    ) -> &'tcx FxHashMap<Symbol, DefId> {
+        tcx.arena.alloc(if self.is_proc_macro_crate() {
+            // Proc macro crates do not export any diagnostic-items to the target.
+            Default::default()
+        } else {
+            self.root
+                .diagnostic_items
+                .decode(self)
+                .map(|(name, def_index)| (name, self.local_def_id(def_index)))
+                .collect()
+        })
+    }
+
     /// Iterates over each child of the given item.
     pub fn each_child_of_item<F>(&self, id: DefIndex, mut callback: F, sess: &Session)
         where F: FnMut(def::Export<hir::HirId>)
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 0eafcebefd7..db212408d8e 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -33,7 +33,7 @@ use syntax::ast;
 use syntax::attr;
 use syntax::ext::proc_macro::is_proc_macro_attr;
 use syntax::source_map::Spanned;
-use syntax::symbol::{kw, sym, Ident};
+use syntax::symbol::{kw, sym, Ident, Symbol};
 use syntax_pos::{self, FileName, SourceFile, Span};
 use log::{debug, trace};
 
@@ -404,6 +404,11 @@ impl<'tcx> EncodeContext<'tcx> {
         let lang_items_missing = self.encode_lang_items_missing();
         let lang_item_bytes = self.position() - i;
 
+        // Encode the diagnostic items.
+        i = self.position();
+        let diagnostic_items = self.encode_diagnostic_items();
+        let diagnostic_item_bytes = self.position() - i;
+
         // Encode the native libraries used
         i = self.position();
         let native_libraries = self.encode_native_libraries();
@@ -520,6 +525,7 @@ impl<'tcx> EncodeContext<'tcx> {
             dylib_dependency_formats,
             lib_features,
             lang_items,
+            diagnostic_items,
             lang_items_missing,
             native_libraries,
             foreign_modules,
@@ -545,6 +551,7 @@ impl<'tcx> EncodeContext<'tcx> {
             println!("             dep bytes: {}", dep_bytes);
             println!("     lib feature bytes: {}", lib_feature_bytes);
             println!("       lang item bytes: {}", lang_item_bytes);
+            println!(" diagnostic item bytes: {}", diagnostic_item_bytes);
             println!("          native bytes: {}", native_lib_bytes);
             println!("         source_map bytes: {}", source_map_bytes);
             println!("            impl bytes: {}", impl_bytes);
@@ -1555,6 +1562,12 @@ impl EncodeContext<'tcx> {
         self.lazy(lib_features.to_vec())
     }
 
+    fn encode_diagnostic_items(&mut self) -> Lazy<[(Symbol, DefIndex)]> {
+        let tcx = self.tcx;
+        let diagnostic_items = tcx.diagnostic_items(LOCAL_CRATE);
+        self.lazy(diagnostic_items.iter().map(|(&name, def_id)| (name, def_id.index)))
+    }
+
     fn encode_lang_items(&mut self) -> Lazy<[(DefIndex, usize)]> {
         let tcx = self.tcx;
         let lang_items = tcx.lang_items();
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index 1a5887bbf4e..1a5f0e17ba7 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -176,6 +176,7 @@ pub struct CrateRoot<'tcx> {
     pub lib_features: Lazy<[(Symbol, Option<Symbol>)]>,
     pub lang_items: Lazy<[(DefIndex, usize)]>,
     pub lang_items_missing: Lazy<[lang_items::LangItem]>,
+    pub diagnostic_items: Lazy<[(Symbol, DefIndex)]>,
     pub native_libraries: Lazy<[NativeLibrary]>,
     pub foreign_modules: Lazy<[ForeignModule]>,
     pub source_map: Lazy<[syntax_pos::SourceFile]>,
diff --git a/src/libsyntax/feature_gate/builtin_attrs.rs b/src/libsyntax/feature_gate/builtin_attrs.rs
index b934f2e7f64..ee7ac3b15d9 100644
--- a/src/libsyntax/feature_gate/builtin_attrs.rs
+++ b/src/libsyntax/feature_gate/builtin_attrs.rs
@@ -462,6 +462,17 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "language items are subject to change",
     ),
     (
+        sym::rustc_diagnostic_item,
+        Normal,
+        template!(NameValueStr: "name"),
+        Gated(
+            Stability::Unstable,
+            sym::rustc_attrs,
+            "diagnostic items compiler internal support for linting",
+            cfg_fn!(rustc_attrs),
+        ),
+    ),
+    (
         sym::no_debug, Whitelisted, template!(Word),
         Gated(
             Stability::Deprecated("https://github.com/rust-lang/rust/issues/29721", None),
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 8833e03c72b..f44716e013e 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -225,9 +225,10 @@ symbols! {
         custom_inner_attributes,
         custom_test_frameworks,
         c_variadic,
-        Debug,
+        debug_trait,
         declare_lint_pass,
         decl_macro,
+        Debug,
         Decodable,
         Default,
         default_lib_allocator,
@@ -238,6 +239,7 @@ symbols! {
         deref,
         deref_mut,
         derive,
+        diagnostic,
         direct,
         doc,
         doc_alias,
@@ -569,6 +571,7 @@ symbols! {
         rustc_conversion_suggestion,
         rustc_def_path,
         rustc_deprecated,
+        rustc_diagnostic_item,
         rustc_diagnostic_macros,
         rustc_dirty,
         rustc_dummy,
diff --git a/src/test/ui/tool-attributes/diagnostic_item.rs b/src/test/ui/tool-attributes/diagnostic_item.rs
new file mode 100644
index 00000000000..1d35422ed62
--- /dev/null
+++ b/src/test/ui/tool-attributes/diagnostic_item.rs
@@ -0,0 +1,2 @@
+#[rustc_diagnostic_item = "foomp"] //~ ERROR compiler internal support for linting
+struct Foomp;
diff --git a/src/test/ui/tool-attributes/diagnostic_item.stderr b/src/test/ui/tool-attributes/diagnostic_item.stderr
new file mode 100644
index 00000000000..deff4da6b80
--- /dev/null
+++ b/src/test/ui/tool-attributes/diagnostic_item.stderr
@@ -0,0 +1,17 @@
+error[E0658]: diagnostic items compiler internal support for linting
+  --> $DIR/diagnostic_item.rs:1:1
+   |
+LL | #[rustc_diagnostic_item = "foomp"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
+
+error[E0601]: `main` function not found in crate `diagnostic_item`
+   |
+   = note: consider adding a `main` function to `$DIR/diagnostic_item.rs`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0601, E0658.
+For more information about an error, try `rustc --explain E0601`.
diff --git a/src/test/ui/tool-attributes/diagnostic_item2.rs b/src/test/ui/tool-attributes/diagnostic_item2.rs
new file mode 100644
index 00000000000..b32a66b16be
--- /dev/null
+++ b/src/test/ui/tool-attributes/diagnostic_item2.rs
@@ -0,0 +1,6 @@
+// check-pass
+
+#[clippy::diagnostic_item = "mep"]
+struct Mep;
+
+fn main() {}
diff --git a/src/test/ui/tool-attributes/diagnostic_item3.rs b/src/test/ui/tool-attributes/diagnostic_item3.rs
new file mode 100644
index 00000000000..c1a236ed1cc
--- /dev/null
+++ b/src/test/ui/tool-attributes/diagnostic_item3.rs
@@ -0,0 +1,7 @@
+// check-pass
+#![feature(rustc_attrs)]
+
+#[rustc_diagnostic_item = "foomp"]
+struct Foomp;
+
+fn main() {}