Skip to content

Commit c7d4df0

Browse files
committed
Auto merge of #60966 - oli-obk:diagnostic_items, r=eddyb
Add a "diagnostic item" scheme for lints referring to libstd items fixes #39131 r? @Manishearth @rust-lang/wg-diagnostics
2 parents 72b2abf + 6978b94 commit c7d4df0

File tree

22 files changed

+260
-18
lines changed

22 files changed

+260
-18
lines changed

src/liballoc/vec.rs

+1
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ use crate::raw_vec::RawVec;
291291
/// [`reserve`]: ../../std/vec/struct.Vec.html#method.reserve
292292
/// [owned slice]: ../../std/boxed/struct.Box.html
293293
#[stable(feature = "rust1", since = "1.0.0")]
294+
#[cfg_attr(all(not(bootstrap), not(test)), rustc_diagnostic_item = "vec_type")]
294295
pub struct Vec<T> {
295296
buf: RawVec<T>,
296297
len: usize,

src/libcore/fmt/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,8 @@ impl Display for Arguments<'_> {
518518
label="`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`",
519519
)]
520520
#[doc(alias = "{:?}")]
521-
#[lang = "debug_trait"]
521+
#[cfg_attr(boostrap_stdarch_ignore_this, lang = "debug_trait")]
522+
#[cfg_attr(not(boostrap_stdarch_ignore_this), rustc_diagnostic_item = "debug_trait")]
522523
pub trait Debug {
523524
/// Formats the value using the given formatter.
524525
///

src/librustc/arena.rs

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ macro_rules! arena_types {
9494
rustc::hir::def_id::CrateNum
9595
>
9696
>,
97+
[few] diagnostic_items: rustc_data_structures::fx::FxHashMap<
98+
syntax::symbol::Symbol,
99+
rustc::hir::def_id::DefId,
100+
>,
97101
[few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes,
98102
[decode] generic_predicates: rustc::ty::GenericPredicates<'tcx>,
99103
[few] lint_levels: rustc::lint::LintLevelMap,

src/librustc/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#![feature(log_syntax)]
6363
#![feature(mem_take)]
6464
#![feature(associated_type_bounds)]
65+
#![feature(rustc_attrs)]
6566

6667
#![recursion_limit="512"]
6768

@@ -109,6 +110,7 @@ pub mod middle {
109110
pub mod cstore;
110111
pub mod dead;
111112
pub mod dependency_format;
113+
pub mod diagnostic_items;
112114
pub mod entry;
113115
pub mod exported_symbols;
114116
pub mod free_region;

src/librustc/lint/internal.rs

+5-11
Original file line numberDiff line numberDiff line change
@@ -159,29 +159,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
159159
}
160160

161161
fn lint_ty_kind_usage(cx: &LateContext<'_, '_>, segment: &PathSegment) -> bool {
162-
if segment.ident.name == sym::TyKind {
163-
if let Some(res) = segment.res {
164-
if let Some(did) = res.opt_def_id() {
165-
return cx.match_def_path(did, TYKIND_PATH);
166-
}
162+
if let Some(res) = segment.res {
163+
if let Some(did) = res.opt_def_id() {
164+
return cx.tcx.is_diagnostic_item(sym::TyKind, did);
167165
}
168166
}
169167

170168
false
171169
}
172170

173-
const TYKIND_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::sty, sym::TyKind];
174-
const TY_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::Ty];
175-
const TYCTXT_PATH: &[Symbol] = &[sym::rustc, sym::ty, sym::context, sym::TyCtxt];
176-
177171
fn is_ty_or_ty_ctxt(cx: &LateContext<'_, '_>, ty: &Ty) -> Option<String> {
178172
match &ty.node {
179173
TyKind::Path(qpath) => {
180174
if let QPath::Resolved(_, path) = qpath {
181175
let did = path.res.opt_def_id()?;
182-
if cx.match_def_path(did, TY_PATH) {
176+
if cx.tcx.is_diagnostic_item(sym::Ty, did) {
183177
return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
184-
} else if cx.match_def_path(did, TYCTXT_PATH) {
178+
} else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
185179
return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
186180
}
187181
}
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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+
}

src/librustc/middle/lang_items.rs

-2
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,6 @@ language_item_table! {
367367

368368
MaybeUninitLangItem, "maybe_uninit", maybe_uninit, Target::Union;
369369

370-
DebugTraitLangItem, "debug_trait", debug_trait, Target::Trait;
371-
372370
// Align offset for stride != 1, must not panic.
373371
AlignOffsetLangItem, "align_offset", align_offset_fn, Target::Fn;
374372

src/librustc/query/mod.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ rustc_queries! {
804804
}
805805

806806
BorrowChecking {
807-
// Lifetime resolution. See `middle::resolve_lifetimes`.
807+
/// Lifetime resolution. See `middle::resolve_lifetimes`.
808808
query resolve_lifetimes(_: CrateNum) -> &'tcx ResolveLifetimes {
809809
desc { "resolving lifetimes" }
810810
}
@@ -846,13 +846,30 @@ rustc_queries! {
846846
-> &'tcx [(Symbol, Option<Symbol>)] {
847847
desc { "calculating the lib features defined in a crate" }
848848
}
849+
/// Returns the lang items defined in another crate by loading it from metadata.
850+
// FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid
851+
// of that argument?
849852
query get_lang_items(_: CrateNum) -> &'tcx LanguageItems {
850853
eval_always
851854
desc { "calculating the lang items map" }
852855
}
856+
857+
/// Returns all diagnostic items defined in all crates
858+
query all_diagnostic_items(_: CrateNum) -> &'tcx FxHashMap<Symbol, DefId> {
859+
eval_always
860+
desc { "calculating the diagnostic items map" }
861+
}
862+
863+
/// Returns the lang items defined in another crate by loading it from metadata.
853864
query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] {
854865
desc { "calculating the lang items defined in a crate" }
855866
}
867+
868+
/// Returns the diagnostic items defined in a crate
869+
query diagnostic_items(_: CrateNum) -> &'tcx FxHashMap<Symbol, DefId> {
870+
desc { "calculating the diagnostic items map in a crate" }
871+
}
872+
856873
query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] {
857874
desc { "calculating the missing lang items in a crate" }
858875
}

src/librustc/ty/context.rs

+21
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,7 @@ pub struct FreeRegionInfo {
978978
///
979979
/// [rustc guide]: https://rust-lang.github.io/rustc-guide/ty.html
980980
#[derive(Copy, Clone)]
981+
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyCtxt")]
981982
pub struct TyCtxt<'tcx> {
982983
gcx: &'tcx GlobalCtxt<'tcx>,
983984
}
@@ -1308,10 +1309,22 @@ impl<'tcx> TyCtxt<'tcx> {
13081309
self.get_lib_features(LOCAL_CRATE)
13091310
}
13101311

1312+
/// Obtain all lang items of this crate and all dependencies (recursively)
13111313
pub fn lang_items(self) -> &'tcx middle::lang_items::LanguageItems {
13121314
self.get_lang_items(LOCAL_CRATE)
13131315
}
13141316

1317+
/// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to
1318+
/// compare against another `DefId`, since `is_diagnostic_item` is cheaper.
1319+
pub fn get_diagnostic_item(self, name: Symbol) -> Option<DefId> {
1320+
self.all_diagnostic_items(LOCAL_CRATE).get(&name).copied()
1321+
}
1322+
1323+
/// Check whether the diagnostic item with the given `name` has the given `DefId`.
1324+
pub fn is_diagnostic_item(self, name: Symbol, did: DefId) -> bool {
1325+
self.diagnostic_items(did.krate).get(&name) == Some(&did)
1326+
}
1327+
13151328
pub fn stability(self) -> &'tcx stability::Index<'tcx> {
13161329
self.stability_index(LOCAL_CRATE)
13171330
}
@@ -2896,6 +2909,14 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
28962909
assert_eq!(id, LOCAL_CRATE);
28972910
tcx.arena.alloc(middle::lang_items::collect(tcx))
28982911
};
2912+
providers.diagnostic_items = |tcx, id| {
2913+
assert_eq!(id, LOCAL_CRATE);
2914+
middle::diagnostic_items::collect(tcx)
2915+
};
2916+
providers.all_diagnostic_items = |tcx, id| {
2917+
assert_eq!(id, LOCAL_CRATE);
2918+
middle::diagnostic_items::collect_all(tcx)
2919+
};
28992920
providers.maybe_unused_trait_import = |tcx, id| {
29002921
tcx.maybe_unused_trait_imports.contains(&id)
29012922
};

src/librustc/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::TyS<'tcx> {
581581
}
582582
}
583583

584+
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "Ty")]
584585
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
585586

586587
impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {}

src/librustc/ty/sty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl BoundRegion {
8686
/// AST structure in `libsyntax/ast.rs` as well.
8787
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
8888
RustcEncodable, RustcDecodable, HashStable, Debug)]
89+
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "TyKind")]
8990
pub enum TyKind<'tcx> {
9091
/// The primitive boolean type. Written as `bool`.
9192
Bool,

src/librustc_lint/builtin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
570570
_ => return,
571571
}
572572

573-
let debug = match cx.tcx.lang_items().debug_trait() {
573+
let debug = match cx.tcx.get_diagnostic_item(sym::debug_trait) {
574574
Some(debug) => debug,
575575
None => return,
576576
};

src/librustc_metadata/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
226226
}
227227
defined_lib_features => { cdata.get_lib_features(tcx) }
228228
defined_lang_items => { cdata.get_lang_items(tcx) }
229+
diagnostic_items => { cdata.get_diagnostic_items(tcx) }
229230
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
230231

231232
missing_extern_crate_item => {

src/librustc_metadata/decoder.rs

+18
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
1212
use rustc::hir::def::{self, Res, DefKind, CtorOf, CtorKind};
1313
use rustc::hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
1414
use rustc_data_structures::fingerprint::Fingerprint;
15+
use rustc_data_structures::fx::FxHashMap;
1516
use rustc::middle::lang_items;
1617
use rustc::mir::{self, interpret};
1718
use rustc::mir::interpret::AllocDecodingSession;
@@ -757,6 +758,23 @@ impl<'a, 'tcx> CrateMetadata {
757758
}
758759
}
759760

761+
/// Iterates over the diagnostic items in the given crate.
762+
pub fn get_diagnostic_items(
763+
&self,
764+
tcx: TyCtxt<'tcx>,
765+
) -> &'tcx FxHashMap<Symbol, DefId> {
766+
tcx.arena.alloc(if self.is_proc_macro_crate() {
767+
// Proc macro crates do not export any diagnostic-items to the target.
768+
Default::default()
769+
} else {
770+
self.root
771+
.diagnostic_items
772+
.decode(self)
773+
.map(|(name, def_index)| (name, self.local_def_id(def_index)))
774+
.collect()
775+
})
776+
}
777+
760778
/// Iterates over each child of the given item.
761779
pub fn each_child_of_item<F>(&self, id: DefIndex, mut callback: F, sess: &Session)
762780
where F: FnMut(def::Export<hir::HirId>)

0 commit comments

Comments
 (0)