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

Support stability and deprecation checking for all macros #62042

Merged
merged 10 commits into from
Jul 7, 2019
5 changes: 5 additions & 0 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub struct LoweringContext<'a> {
impl_items: BTreeMap<hir::ImplItemId, hir::ImplItem>,
bodies: BTreeMap<hir::BodyId, hir::Body>,
exported_macros: Vec<hir::MacroDef>,
non_exported_macro_attrs: Vec<ast::Attribute>,

trait_impls: BTreeMap<DefId, Vec<hir::HirId>>,

Expand Down Expand Up @@ -252,6 +253,7 @@ pub fn lower_crate(
trait_impls: BTreeMap::new(),
modules: BTreeMap::new(),
exported_macros: Vec::new(),
non_exported_macro_attrs: Vec::new(),
catch_scopes: Vec::new(),
loop_scopes: Vec::new(),
is_in_loop_condition: false,
Expand Down Expand Up @@ -662,6 +664,7 @@ impl<'a> LoweringContext<'a> {
attrs,
span: c.span,
exported_macros: hir::HirVec::from(self.exported_macros),
non_exported_macro_attrs: hir::HirVec::from(self.non_exported_macro_attrs),
items: self.items,
trait_items: self.trait_items,
impl_items: self.impl_items,
Expand Down Expand Up @@ -4022,6 +4025,8 @@ impl<'a> LoweringContext<'a> {
body,
legacy: def.legacy,
});
} else {
self.non_exported_macro_attrs.extend(attrs.into_iter());
}
return None;
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/hir/map/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
span,
// These fields are handled separately:
exported_macros: _,
non_exported_macro_attrs: _,
items: _,
trait_items: _,
impl_items: _,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,8 @@ pub struct Crate {
pub attrs: HirVec<Attribute>,
pub span: Span,
pub exported_macros: HirVec<MacroDef>,
// Attributes from non-exported macros, kept only for collecting the library feature list.
pub non_exported_macro_attrs: HirVec<Attribute>,

// N.B., we use a BTreeMap here so that `visit_all_items` iterates
// over the ids in increasing order. In principle it should not
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
//! lints are all available in `rustc_lint::builtin`.

use crate::lint::{LintPass, LateLintPass, LintArray};
use crate::middle::stability;
use crate::session::Session;
use errors::{Applicability, DiagnosticBuilder};
use syntax::ast;
use syntax::source_map::Span;
use syntax::symbol::Symbol;

declare_lint! {
pub EXCEEDING_BITSHIFTS,
Expand Down Expand Up @@ -461,6 +463,7 @@ pub enum BuiltinLintDiagnostics {
UnusedImports(String, Vec<(Span, String)>),
NestedImplTrait { outer_impl_trait_span: Span, inner_impl_trait_span: Span },
RedundantImport(Vec<(Span, bool)>, ast::Ident),
DeprecatedMacro(Option<Symbol>, Span),
}

pub(crate) fn add_elided_lifetime_in_path_suggestion(
Expand Down Expand Up @@ -586,6 +589,8 @@ impl BuiltinLintDiagnostics {
);
}
}
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) =>
stability::deprecation_suggestion(db, suggestion, span),
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustc/middle/lib_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ impl Visitor<'tcx> for LibFeatureCollector<'tcx> {

pub fn collect(tcx: TyCtxt<'_>) -> LibFeatures {
let mut collector = LibFeatureCollector::new(tcx);
intravisit::walk_crate(&mut collector, tcx.hir().krate());
let krate = tcx.hir().krate();
for attr in &krate.non_exported_macro_attrs {
collector.visit_attribute(attr);
}
intravisit::walk_crate(&mut collector, krate);
collector.lib_features
}
208 changes: 117 additions & 91 deletions src/librustc/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@
pub use self::StabilityLevel::*;

use crate::lint::{self, Lint, in_derive_expansion};
use crate::lint::builtin::BuiltinLintDiagnostics;
use crate::hir::{self, Item, Generics, StructField, Variant, HirId};
use crate::hir::def::{Res, DefKind};
use crate::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::ty::query::Providers;
use crate::middle::privacy::AccessLevels;
use crate::session::{DiagnosticMessageId, Session};
use errors::DiagnosticBuilder;
use syntax::symbol::{Symbol, sym};
use syntax_pos::{Span, MultiSpan};
use syntax::ast::Attribute;
use syntax::ast::{Attribute, CRATE_NODE_ID};
use syntax::errors::Applicability;
use syntax::feature_gate::{GateIssue, emit_feature_err};
use syntax::attr::{self, Stability, Deprecation};
use syntax::attr::{self, Stability, Deprecation, RustcDeprecation};
use crate::ty::{self, TyCtxt};
use crate::util::nodemap::{FxHashSet, FxHashMap};

Expand Down Expand Up @@ -477,6 +479,36 @@ pub fn provide(providers: &mut Providers<'_>) {
};
}

pub fn report_unstable(
sess: &Session, feature: Symbol, reason: Option<Symbol>, issue: u32, span: Span
) {
let msg = match reason {
Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
None => format!("use of unstable library feature '{}'", &feature)
};

let msp: MultiSpan = span.into();
let cm = &sess.parse_sess.source_map();
let span_key = msp.primary_span().and_then(|sp: Span|
if !sp.is_dummy() {
let file = cm.lookup_char_pos(sp.lo()).file;
if file.name.is_macros() {
None
} else {
Some(span)
}
} else {
None
}
);

let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id);
if fresh {
emit_feature_err(&sess.parse_sess, feature, span, GateIssue::Library(Some(issue)), &msg);
}
}

/// Checks whether an item marked with `deprecated(since="X")` is currently
/// deprecated (i.e., whether X is not greater than the current rustc version).
pub fn deprecation_in_effect(since: &str) -> bool {
Expand All @@ -501,6 +533,79 @@ pub fn deprecation_in_effect(since: &str) -> bool {
}
}

pub fn deprecation_suggestion(
diag: &mut DiagnosticBuilder<'_>, suggestion: Option<Symbol>, span: Span
) {
if let Some(suggestion) = suggestion {
diag.span_suggestion(
span,
"replace the use of the deprecated item",
suggestion.to_string(),
Applicability::MachineApplicable,
);
}
}

fn deprecation_message_common(message: String, reason: Option<Symbol>) -> String {
match reason {
Some(reason) => format!("{}: {}", message, reason),
None => message,
}
}

pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) {
let message = format!("use of deprecated item '{}'", path);
(deprecation_message_common(message, depr.note), lint::builtin::DEPRECATED)
}

pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) {
let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) {
(format!("use of deprecated item '{}'", path), lint::builtin::DEPRECATED)
} else {
(format!("use of item '{}' that will be deprecated in future version {}", path, depr.since),
lint::builtin::DEPRECATED_IN_FUTURE)
};
(deprecation_message_common(message, Some(depr.reason)), lint)
}

pub fn early_report_deprecation(
sess: &Session,
message: &str,
suggestion: Option<Symbol>,
lint: &'static Lint,
span: Span,
) {
if in_derive_expansion(span) {
return;
}

let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
sess.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag);
}

fn late_report_deprecation(
tcx: TyCtxt<'_>,
message: &str,
suggestion: Option<Symbol>,
lint: &'static Lint,
span: Span,
def_id: DefId,
hir_id: HirId,
) {
if in_derive_expansion(span) {
return;
}

let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
deprecation_suggestion(&mut diag, suggestion, span);
}
diag.emit();
if hir_id == hir::DUMMY_HIR_ID {
span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
}
}

struct Checker<'tcx> {
tcx: TyCtxt<'tcx>,
}
Expand Down Expand Up @@ -563,38 +668,6 @@ impl<'tcx> TyCtxt<'tcx> {
/// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
/// `id`.
pub fn eval_stability(self, def_id: DefId, id: Option<HirId>, span: Span) -> EvalResult {
let lint_deprecated = |def_id: DefId,
id: HirId,
note: Option<Symbol>,
suggestion: Option<Symbol>,
message: &str,
lint: &'static Lint| {
if in_derive_expansion(span) {
return;
}
let msg = if let Some(note) = note {
format!("{}: {}", message, note)
} else {
format!("{}", message)
};

let mut diag = self.struct_span_lint_hir(lint, id, span, &msg);
if let Some(suggestion) = suggestion {
if let hir::Node::Expr(_) = self.hir().get(id) {
diag.span_suggestion(
span,
"replace the use of the deprecated item",
suggestion.to_string(),
Applicability::MachineApplicable,
);
}
}
diag.emit();
if id == hir::DUMMY_HIR_ID {
span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
}
};

// Deprecated attributes apply in-crate and cross-crate.
if let Some(id) = id {
if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
Expand All @@ -604,14 +677,9 @@ impl<'tcx> TyCtxt<'tcx> {
.map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));

if !skip {
let path = self.def_path_str(def_id);
let message = format!("use of deprecated item '{}'", path);
lint_deprecated(def_id,
id,
depr_entry.attr.note,
None,
&message,
lint::builtin::DEPRECATED);
let (message, lint) =
deprecation_message(&depr_entry.attr, &self.def_path_str(def_id));
late_report_deprecation(self, &message, None, lint, span, def_id, id);
}
};
}
Expand All @@ -631,27 +699,11 @@ impl<'tcx> TyCtxt<'tcx> {
if let Some(id) = id {
if let Some(stability) = stability {
if let Some(depr) = &stability.rustc_depr {
let path = self.def_path_str(def_id);
if deprecation_in_effect(&depr.since.as_str()) {
let message = format!("use of deprecated item '{}'", path);
lint_deprecated(def_id,
id,
Some(depr.reason),
depr.suggestion,
&message,
lint::builtin::DEPRECATED);
} else {
let message = format!("use of item '{}' \
that will be deprecated in future version {}",
path,
depr.since);
lint_deprecated(def_id,
id,
Some(depr.reason),
depr.suggestion,
&message,
lint::builtin::DEPRECATED_IN_FUTURE);
}
let (message, lint) =
rustc_deprecation_message(depr, &self.def_path_str(def_id));
late_report_deprecation(
self, &message, depr.suggestion, lint, span, def_id, id
);
}
}
}
Expand Down Expand Up @@ -715,34 +767,8 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
match self.eval_stability(def_id, id, span) {
EvalResult::Allow => {}
EvalResult::Deny { feature, reason, issue } => {
let msg = match reason {
Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
None => format!("use of unstable library feature '{}'", &feature)
};

let msp: MultiSpan = span.into();
let cm = &self.sess.parse_sess.source_map();
let span_key = msp.primary_span().and_then(|sp: Span|
if !sp.is_dummy() {
let file = cm.lookup_char_pos(sp.lo()).file;
if file.name.is_macros() {
None
} else {
Some(span)
}
} else {
None
}
);

let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
let fresh = self.sess.one_time_diagnostics.borrow_mut().insert(error_id);
if fresh {
emit_feature_err(&self.sess.parse_sess, feature, span,
GateIssue::Library(Some(issue)), &msg);
}
}
EvalResult::Deny { feature, reason, issue } =>
report_unstable(self.sess, feature, reason, issue, span),
EvalResult::Unmarked => {
// The API could be uncallable for other reasons, for example when a private module
// was referenced.
Expand Down
5 changes: 1 addition & 4 deletions src/librustc_plugin/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@ impl<'a> Registry<'a> {
/// Register a syntax extension of any kind.
///
/// This is the most general hook into `libsyntax`'s expansion behavior.
pub fn register_syntax_extension(&mut self, name: ast::Name, mut extension: SyntaxExtension) {
if extension.def_info.is_none() {
extension.def_info = Some((ast::CRATE_NODE_ID, self.krate_span));
}
pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) {
self.syntax_exts.push((name, extension));
}

Expand Down
Loading