Skip to content

Add a "diagnostic item" scheme for lints referring to libstd items #60966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 30, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/liballoc/vec.rs
Original file line number Diff line number Diff line change
@@ -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,
3 changes: 2 additions & 1 deletion src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
///
4 changes: 4 additions & 0 deletions src/librustc/arena.rs
Original file line number Diff line number Diff line change
@@ -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,
2 changes: 2 additions & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
16 changes: 5 additions & 11 deletions src/librustc/lint/internal.rs
Original file line number Diff line number Diff line change
@@ -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())));
}
}
123 changes: 123 additions & 0 deletions src/librustc/middle/diagnostic_items.rs
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 0 additions & 2 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
@@ -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;

19 changes: 18 additions & 1 deletion src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
@@ -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" }
}
21 changes: 21 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
@@ -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)
};
1 change: 1 addition & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -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> {}
1 change: 1 addition & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
@@ -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,
2 changes: 1 addition & 1 deletion src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
@@ -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,
};
1 change: 1 addition & 0 deletions src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
@@ -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 => {
18 changes: 18 additions & 0 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
@@ -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>)
15 changes: 14 additions & 1 deletion src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
@@ -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();
1 change: 1 addition & 0 deletions src/librustc_metadata/schema.rs
Original file line number Diff line number Diff line change
@@ -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]>,
11 changes: 11 additions & 0 deletions src/libsyntax/feature_gate/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -461,6 +461,17 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
lang, Normal, template!(NameValueStr: "name"), lang_items,
"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(
5 changes: 4 additions & 1 deletion src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
@@ -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,
2 changes: 2 additions & 0 deletions src/test/ui/tool-attributes/diagnostic_item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[rustc_diagnostic_item = "foomp"] //~ ERROR compiler internal support for linting
struct Foomp;
17 changes: 17 additions & 0 deletions src/test/ui/tool-attributes/diagnostic_item.stderr
Original file line number Diff line number Diff line change
@@ -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`.
6 changes: 6 additions & 0 deletions src/test/ui/tool-attributes/diagnostic_item2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// check-pass

#[clippy::diagnostic_item = "mep"]
struct Mep;

fn main() {}
7 changes: 7 additions & 0 deletions src/test/ui/tool-attributes/diagnostic_item3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// check-pass
#![feature(rustc_attrs)]

#[rustc_diagnostic_item = "foomp"]
struct Foomp;

fn main() {}