Skip to content
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

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

Merged
merged 2 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
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
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#![feature(log_syntax)]
#![feature(mem_take)]
#![feature(associated_type_bounds)]
#![feature(rustc_attrs)]

#![recursion_limit="512"]

Expand Down Expand Up @@ -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;
Expand Down
16 changes: 5 additions & 11 deletions src/librustc/lint/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())));
}
}
Expand Down
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
Expand Up @@ -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;

Expand Down
19 changes: 18 additions & 1 deletion src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
}
Expand Down Expand Up @@ -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" }
}
Expand Down
21 changes: 21 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
};
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
1 change: 1 addition & 0 deletions src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
18 changes: 18 additions & 0 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>)
Expand Down
Loading