Skip to content
Permalink
Browse files

Add a "diagnostic item" scheme

This allows lints and other diagnostics to refer to items
by a unique ID instead of relying on whacky path
resolution schemes that may break when items are
relocated.
  • Loading branch information...
oli-obk committed May 19, 2019
1 parent fbdf1d2 commit 26e9990198d3c7262d58468f18a722920c897e35
@@ -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,
@@ -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.
///
@@ -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,
@@ -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;
@@ -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())));
}
}
@@ -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)
}
@@ -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;

@@ -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" }
}
@@ -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)
};
@@ -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> {}
@@ -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,
@@ -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,
};
@@ -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 => {
@@ -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>)

0 comments on commit 26e9990

Please sign in to comment.
You can’t perform that action at this time.