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

Introduce proc_macro_back_compat lint, and emit for time-macros-impl #83127

Merged
merged 1 commit into from
Mar 15, 2021
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
50 changes: 1 addition & 49 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ use crate::tokenstream::TokenTree;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lrc;
use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::ExpnKind;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP};
use rustc_span::{self, edition::Edition, Span, DUMMY_SP};
use std::borrow::Cow;
use std::{fmt, mem};

Expand Down Expand Up @@ -813,52 +811,6 @@ impl Nonterminal {
}
false
}

// See issue #74616 for details
pub fn ident_name_compatibility_hack(
&self,
orig_span: Span,
source_map: &SourceMap,
) -> Option<(Ident, bool)> {
if let NtIdent(ident, is_raw) = self {
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::Named(path)) = filename {
let matches_prefix = |prefix, filename| {
// Check for a path that ends with 'prefix*/src/<filename>'
let mut iter = path.components().rev();
iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
&& iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
&& iter
.next()
.and_then(|p| p.as_os_str().to_str())
.map_or(false, |p| p.starts_with(prefix))
};

if (macro_name == sym::impl_macros
&& matches_prefix("time-macros-impl", "lib.rs"))
|| (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$name") {
return Some((*ident, *is_raw));
}
}

if macro_name == sym::tuple_from_req
&& (matches_prefix("actix-web", "extract.rs")
|| matches_prefix("actori-web", "extract.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$T") {
return Some((*ident, *is_raw));
}
}
}
}
}
None
}
}

impl PartialEq for Nonterminal {
Expand Down
70 changes: 66 additions & 4 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ use crate::base::ExtCtxt;

use rustc_ast as ast;
use rustc_ast::token;
use rustc_ast::token::Nonterminal;
use rustc_ast::token::NtIdent;
use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens};
use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
use rustc_errors::Diagnostic;
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::lexer::nfc_normalize;
use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str};
use rustc_session::parse::ParseSess;
use rustc_span::hygiene::ExpnKind;
use rustc_span::symbol::{self, kw, sym, Symbol};
use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span};
use rustc_span::{BytePos, FileName, MultiSpan, Pos, RealFileName, SourceFile, Span};

use pm::bridge::{server, TokenTree};
use pm::{Delimiter, Level, LineColumn, Spacing};
Expand Down Expand Up @@ -174,9 +179,7 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)>
}

Interpolated(nt) => {
if let Some((name, is_raw)) =
nt.ident_name_compatibility_hack(span, sess.source_map())
{
if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, sess) {
TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span))
} else {
let stream = nt_to_tokenstream(&nt, sess, CanSynthesizeMissingTokens::No);
Expand Down Expand Up @@ -711,3 +714,62 @@ impl server::Span for Rustc<'_> {
self.sess.source_map().span_to_snippet(span).ok()
}
}

// See issue #74616 for details
fn ident_name_compatibility_hack(
nt: &Nonterminal,
orig_span: Span,
sess: &ParseSess,
) -> Option<(rustc_span::symbol::Ident, bool)> {
if let NtIdent(ident, is_raw) = nt {
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
let source_map = sess.source_map();
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::Named(path)) = filename {
let matches_prefix = |prefix, filename| {
// Check for a path that ends with 'prefix*/src/<filename>'
let mut iter = path.components().rev();
iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
&& iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
&& iter
.next()
.and_then(|p| p.as_os_str().to_str())
.map_or(false, |p| p.starts_with(prefix))
};

let time_macros_impl =
macro_name == sym::impl_macros && matches_prefix("time-macros-impl", "lib.rs");
if time_macros_impl
|| (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$name") {
if time_macros_impl {
sess.buffer_lint_with_diagnostic(
&PROC_MACRO_BACK_COMPAT,
orig_span,
ast::CRATE_NODE_ID,
"using an old version of `time-macros-impl`",
BuiltinLintDiagnostics::ProcMacroBackCompat(
"the `time-macros-impl` crate will stop compiling in futures version of Rust. \
Please update to the latest version of the `time` crate to avoid breakage".to_string())
);
}
return Some((*ident, *is_raw));
}
}

if macro_name == sym::tuple_from_req
&& (matches_prefix("actix-web", "extract.rs")
|| matches_prefix("actori-web", "extract.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$T") {
return Some((*ident, *is_raw));
}
}
}
}
}
None
}
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,9 @@ pub trait LintContext: Sized {
json
);
}
BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
db.note(&note);
}
}
// Rewrap `db`, and pass control to the user.
decorate(LintDiagnosticBuilder::new(db));
Expand Down
53 changes: 52 additions & 1 deletion compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! compiler code, rather than using their own custom pass. Those
//! lints are all available in `rustc_lint::builtin`.

use crate::{declare_lint, declare_lint_pass};
use crate::{declare_lint, declare_lint_pass, FutureBreakage};
use rustc_span::edition::Edition;

declare_lint! {
Expand Down Expand Up @@ -2955,6 +2955,7 @@ declare_lint_pass! {
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
DISJOINT_CAPTURE_DROP_REORDER,
LEGACY_DERIVE_HELPERS,
PROC_MACRO_BACK_COMPAT,
]
}

Expand Down Expand Up @@ -3082,3 +3083,53 @@ declare_lint! {
edition: None,
};
}

declare_lint! {
/// The `proc_macro_back_compat` lint detects uses of old versions of certain
/// proc-macro crates, which have hardcoded workarounds in the compiler.
///
/// ### Example
///
/// ```rust,ignore (needs-dependency)
///
/// use time_macros_impl::impl_macros;
/// struct Foo;
/// impl_macros!(Foo);
/// ```
///
/// This will produce:
///
/// ```text
/// warning: using an old version of `time-macros-impl`
/// ::: $DIR/group-compat-hack.rs:27:5
/// |
/// LL | impl_macros!(Foo);
/// | ------------------ in this macro invocation
/// |
/// = note: `#[warn(proc_macro_back_compat)]` on by default
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
/// = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
/// = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
/// = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
/// ```
///
/// ### Explanation
///
/// Eventually, the backwards-compatibility hacks present in the compiler will be removed,
/// causing older versions of certain crates to stop compiling.
/// This is a [future-incompatible] lint to ease the transition to an error.
/// See [issue #83125] for more details.
///
/// [issue #83125]: https://github.com/rust-lang/rust/issues/83125
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub PROC_MACRO_BACK_COMPAT,
Warn,
"detects usage of old versions of certain proc-macro crates",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #83125 <https://github.com/rust-lang/rust/issues/83125>",
edition: None,
future_breakage: Some(FutureBreakage {
date: None
})
};
}
1 change: 1 addition & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ pub enum BuiltinLintDiagnostics {
PatternsInFnsWithoutBody(Span, Ident),
LegacyDeriveHelpers(Span),
ExternDepSpec(String, ExternDepSpec),
ProcMacroBackCompat(String),
}

/// Lints that are buffered up early on in the `Session` before the
Expand Down
6 changes: 4 additions & 2 deletions src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ mod no_version {
}

struct Foo;
impl_macros!(Foo);
impl_macros!(Foo); //~ WARN using an old version
//~| WARN this was previously
arrays!(Foo);
other!(Foo);
}
Expand All @@ -40,7 +41,8 @@ mod with_version {
}

struct Foo;
impl_macros!(Foo);
impl_macros!(Foo); //~ WARN using an old version
//~| WARN this was previously
arrays!(Foo);
other!(Foo);
}
Expand Down
70 changes: 70 additions & 0 deletions src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
warning: using an old version of `time-macros-impl`
--> $DIR/time-macros-impl/src/lib.rs:5:32
|
LL | #[my_macro] struct One($name);
| ^^^^^
|
::: $DIR/group-compat-hack.rs:27:5
|
LL | impl_macros!(Foo);
| ------------------ in this macro invocation
|
= note: `#[warn(proc_macro_back_compat)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
= note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

warning: using an old version of `time-macros-impl`
--> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32
|
LL | #[my_macro] struct One($name);
| ^^^^^
|
::: $DIR/group-compat-hack.rs:44:5
|
LL | impl_macros!(Foo);
| ------------------ in this macro invocation
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
= note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

warning: 2 warnings emitted

Future incompatibility report: Future breakage date: None, diagnostic:
warning: using an old version of `time-macros-impl`
--> $DIR/time-macros-impl/src/lib.rs:5:32
|
LL | #[my_macro] struct One($name);
| ^^^^^
|
::: $DIR/group-compat-hack.rs:27:5
|
LL | impl_macros!(Foo);
| ------------------ in this macro invocation
|
= note: `#[warn(proc_macro_back_compat)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
= note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Future breakage date: None, diagnostic:
warning: using an old version of `time-macros-impl`
--> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32
|
LL | #[my_macro] struct One($name);
| ^^^^^
|
::: $DIR/group-compat-hack.rs:44:5
|
LL | impl_macros!(Foo);
| ------------------ in this macro invocation
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
= note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Loading