Support deprecation checking for macros

This commit is contained in:
Vadim Petrochenkov 2019-06-22 02:44:45 +03:00
parent d9ee97e896
commit 0817fc6c6c
11 changed files with 203 additions and 79 deletions

View file

@ -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,
@ -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(
@ -586,6 +589,8 @@ impl BuiltinLintDiagnostics {
);
}
}
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) =>
stability::deprecation_suggestion(db, suggestion, span),
}
}
}

View file

@ -4,6 +4,7 @@
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};
@ -11,12 +12,13 @@ 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};
@ -531,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>,
}
@ -593,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) {
@ -634,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);
}
};
}
@ -661,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
);
}
}
}

View file

@ -231,21 +231,14 @@ impl<'a> base::Resolver for Resolver<'a> {
};
let span = invoc.span();
let path = fast_print_path(path);
let format = match kind {
MacroKind::Derive => format!("derive({})", fast_print_path(path)),
_ => fast_print_path(path),
MacroKind::Derive => format!("derive({})", path),
_ => path.clone(),
};
invoc.expansion_data.mark.set_expn_info(ext.expn_info(span, &format));
if let Some(stability) = ext.stability {
if let StabilityLevel::Unstable { reason, issue } = stability.level {
let (feature, features) = (stability.feature, self.session.features_untracked());
if !span.allows_unstable(feature) &&
features.declared_lib_features.iter().all(|(feat, _)| *feat != feature) {
stability::report_unstable(self.session, feature, reason, issue, span);
}
}
}
self.check_stability_and_deprecation(&ext, &path, span);
if let Res::Def(_, def_id) = res {
if after_derive {
@ -1017,6 +1010,28 @@ impl<'a> Resolver<'a> {
}
}
fn check_stability_and_deprecation(&self, ext: &SyntaxExtension, path: &str, span: Span) {
if let Some(stability) = &ext.stability {
if let StabilityLevel::Unstable { reason, issue } = stability.level {
let (feature, features) = (stability.feature, self.session.features_untracked());
if !span.allows_unstable(feature) &&
features.declared_lib_features.iter().all(|(feat, _)| *feat != feature) {
stability::report_unstable(self.session, feature, reason, issue, span);
}
}
if let Some(depr) = &stability.rustc_depr {
let (message, lint) = stability::rustc_deprecation_message(depr, path);
stability::early_report_deprecation(
self.session, &message, depr.suggestion, lint, span
);
}
}
if let Some(depr) = &ext.deprecation {
let (message, lint) = stability::deprecation_message(depr, path);
stability::early_report_deprecation(self.session, &message, None, lint, span);
}
}
fn prohibit_imported_non_macro_attrs(&self, binding: Option<&'a NameBinding<'a>>,
res: Option<Res>, span: Span) {
if let Some(Res::NonMacroAttr(kind)) = res {

View file

@ -1,5 +1,5 @@
use crate::ast::{self, Attribute, Name, PatKind};
use crate::attr::{HasAttrs, Stability};
use crate::attr::{HasAttrs, Stability, Deprecation};
use crate::source_map::{SourceMap, Spanned, respan};
use crate::edition::Edition;
use crate::ext::expand::{self, AstFragment, Invocation};
@ -616,8 +616,10 @@ pub struct SyntaxExtension {
pub allow_internal_unsafe: bool,
/// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro.
pub local_inner_macros: bool,
/// The macro's stability and deprecation info.
/// The macro's stability info.
pub stability: Option<Stability>,
/// The macro's deprecation info.
pub deprecation: Option<Deprecation>,
/// Names of helper attributes registered by this macro.
pub helper_attrs: Vec<Symbol>,
/// Edition of the crate in which this macro is defined.
@ -663,6 +665,7 @@ impl SyntaxExtension {
allow_internal_unsafe: false,
local_inner_macros: false,
stability: None,
deprecation: None,
helper_attrs: Vec::new(),
edition,
kind,

View file

@ -437,6 +437,7 @@ pub fn compile(
allow_internal_unsafe: attr::contains_name(&def.attrs, sym::allow_internal_unsafe),
local_inner_macros,
stability: attr::find_stability(&sess, &def.attrs, def.span),
deprecation: attr::find_deprecation(&sess, &def.attrs, def.span),
helper_attrs: Vec::new(),
edition,
}

View file

@ -0,0 +1,3 @@
#[deprecated(since = "1.0.0", note = "deprecation note")]
#[macro_export]
macro_rules! deprecated_macro{ () => () }

View file

@ -1,6 +1,16 @@
#![feature(decl_macro)]
#![feature(staged_api)]
#![stable(feature = "unit_test", since = "1.0.0")]
#[unstable(feature = "unstable_macros", issue = "0")]
#[macro_export]
macro_rules! unstable_macro{ () => () }
#[stable(feature = "deprecated_macros", since = "1.0.0")]
#[rustc_deprecated(since = "1.0.0", reason = "deprecation reason")]
#[macro_export]
macro_rules! deprecated_macro{ () => () }
// FIXME: Cannot use a `pub` macro 2.0 in a staged API crate due to reachability issues.
// #[unstable(feature = "unstable_macros", issue = "0")]
// pub macro unstable_macro_modern() {}

View file

@ -0,0 +1,13 @@
// compile-pass
// aux-build:deprecated-macros.rs
#[macro_use] extern crate deprecated_macros;
#[deprecated(since = "1.0.0", note = "local deprecation note")]
#[macro_export]
macro_rules! local_deprecated{ () => () }
fn main() {
local_deprecated!(); //~ WARN use of deprecated item 'local_deprecated': local deprecation note
deprecated_macro!(); //~ WARN use of deprecated item 'deprecated_macro': deprecation note
}

View file

@ -0,0 +1,14 @@
warning: use of deprecated item 'local_deprecated': local deprecation note
--> $DIR/macro-deprecation.rs:11:5
|
LL | local_deprecated!();
| ^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(deprecated)] on by default
warning: use of deprecated item 'deprecated_macro': deprecation note
--> $DIR/macro-deprecation.rs:12:5
|
LL | deprecated_macro!();
| ^^^^^^^^^^^^^^^^^^^^

View file

@ -1,12 +1,28 @@
// aux-build:unstable-macros.rs
#![feature(decl_macro)]
#![feature(staged_api)]
#[macro_use] extern crate unstable_macros;
#[unstable(feature = "local_unstable", issue = "0")]
macro_rules! local_unstable { () => () }
#[unstable(feature = "local_unstable", issue = "0")]
macro local_unstable_modern() {}
#[stable(feature = "deprecated_macros", since = "1.0.0")]
#[rustc_deprecated(since = "1.0.0", reason = "local deprecation reason")]
#[macro_export]
macro_rules! local_deprecated{ () => () }
fn main() {
local_unstable!(); //~ ERROR use of unstable library feature 'local_unstable'
local_unstable_modern!(); //~ ERROR use of unstable library feature 'local_unstable'
unstable_macro!(); //~ ERROR use of unstable library feature 'unstable_macros'
// unstable_macro_modern!(); // ERROR use of unstable library feature 'unstable_macros'
deprecated_macro!();
//~^ WARN use of deprecated item 'deprecated_macro': deprecation reason
local_deprecated!();
//~^ WARN use of deprecated item 'local_deprecated': local deprecation reason
}

View file

@ -1,19 +1,41 @@
error[E0658]: use of unstable library feature 'local_unstable'
--> $DIR/macro-stability.rs:10:5
--> $DIR/macro-stability.rs:19:5
|
LL | local_unstable!();
| ^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(local_unstable)] to the crate attributes to enable
error[E0658]: use of unstable library feature 'local_unstable'
--> $DIR/macro-stability.rs:20:5
|
LL | local_unstable_modern!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(local_unstable)] to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_macros'
--> $DIR/macro-stability.rs:11:5
--> $DIR/macro-stability.rs:21:5
|
LL | unstable_macro!();
| ^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(unstable_macros)] to the crate attributes to enable
error: aborting due to 2 previous errors
warning: use of deprecated item 'deprecated_macro': deprecation reason
--> $DIR/macro-stability.rs:24:5
|
LL | deprecated_macro!();
| ^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(deprecated)] on by default
warning: use of deprecated item 'local_deprecated': local deprecation reason
--> $DIR/macro-stability.rs:26:5
|
LL | local_deprecated!();
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0658`.