Skip to content

Commit

Permalink
Auto merge of #83468 - hi-rustin:rustin-patch-lint, r=nikomatsakis
Browse files Browse the repository at this point in the history
add OR_PATTERNS_BACK_COMPAT lint

close #83318
  • Loading branch information
bors committed Apr 2, 2021
2 parents a207871 + aa987c2 commit 36bcf40
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 3 deletions.
42 changes: 39 additions & 3 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_feature::Features;
use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS;
use rustc_lint_defs::builtin::{OR_PATTERNS_BACK_COMPAT, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS};
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::parser::Parser;
use rustc_session::parse::ParseSess;
use rustc_session::Session;
Expand Down Expand Up @@ -951,8 +952,32 @@ fn check_matcher_core(
// Now `last` holds the complete set of NT tokens that could
// end the sequence before SUFFIX. Check that every one works with `suffix`.
for token in &last.tokens {
if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token {
if let TokenTree::MetaVarDecl(span, name, Some(kind)) = *token {
for next_token in &suffix_first.tokens {
// Check if the old pat is used and the next token is `|`.
if let NonterminalKind::Pat2015 { inferred: true } = kind {
if let TokenTree::Token(token) = next_token {
if let BinOp(token) = token.kind {
if let token::BinOpToken::Or = token {
// It is suggestion to use pat2015, for example: $x:pat -> $x:pat2015.
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::Pat2015 { inferred: false }),
));
sess.buffer_lint_with_diagnostic(
&OR_PATTERNS_BACK_COMPAT,
span,
ast::CRATE_NODE_ID,
&*format!("the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",),
BuiltinLintDiagnostics::OrPatternsBackCompat(
span, suggestion,
),
);
}
}
}
}
match is_in_follow(next_token, kind) {
IsInFollow::Yes => {}
IsInFollow::No(possible) => {
Expand Down Expand Up @@ -1080,7 +1105,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::Pat2015 { .. } | NonterminalKind::Pat2021 { .. } => {
NonterminalKind::Pat2015 { .. } => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
Expand All @@ -1091,6 +1116,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::Pat2021 { .. } => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Eq => IsInFollow::Yes,
Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
},
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::Path | NonterminalKind::Ty => {
const TOKENS: &[&str] = &[
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,9 @@ pub trait LintContext: Sized {
BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
db.note(&note);
}
BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
db.span_suggestion(span, "use pat2015 to preserve semantics", suggestion, Applicability::MachineApplicable);
}
}
// Rewrap `db`, and pass control to the user.
decorate(LintDiagnosticBuilder::new(db));
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2959,6 +2959,7 @@ declare_lint_pass! {
DISJOINT_CAPTURE_DROP_REORDER,
LEGACY_DERIVE_HELPERS,
PROC_MACRO_BACK_COMPAT,
OR_PATTERNS_BACK_COMPAT,
]
}

Expand Down Expand Up @@ -3136,3 +3137,37 @@ declare_lint! {
})
};
}

declare_lint! {
/// The `or_patterns_back_compat` lint detects usage of old versions of or-patterns.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(or_patterns_back_compat)]
/// macro_rules! match_any {
/// ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
/// match $expr {
/// $(
/// $( $pat => $expr_arm, )+
/// )+
/// }
/// };
/// }
///
/// fn main() {
/// let result: Result<i64, i32> = Err(42);
/// let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
/// assert_eq!(int, 42);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// In Rust 2021, the pat matcher will match new patterns, which include the | character.
pub OR_PATTERNS_BACK_COMPAT,
Allow,
"detects usage of old versions of or-patterns",
}
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 @@ -267,6 +267,7 @@ pub enum BuiltinLintDiagnostics {
LegacyDeriveHelpers(Span),
ExternDepSpec(String, ExternDepSpec),
ProcMacroBackCompat(String),
OrPatternsBackCompat(Span, String),
}

/// Lints that are buffered up early on in the `Session` before the
Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/macros/macro-or-patterns-back-compat.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// ignore-tidy-linelength
// run-rustfix

#![feature(edition_macro_pats)]
#![deny(or_patterns_back_compat)]
#![allow(unused_macros)]
macro_rules! foo { ($x:pat2015 | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
macro_rules! ogg { ($x:pat2015 | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
macro_rules! match_any {
( $expr:expr , $( $( $pat:pat2015 )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
match $expr {
$(
$( $pat => $expr_arm, )+
)+
}
};
}

fn main() {
let result: Result<i64, i32> = Err(42);
let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
assert_eq!(int, 42);
}
26 changes: 26 additions & 0 deletions src/test/ui/macros/macro-or-patterns-back-compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// ignore-tidy-linelength
// run-rustfix

#![feature(edition_macro_pats)]
#![deny(or_patterns_back_compat)]
#![allow(unused_macros)]
macro_rules! foo { ($x:pat | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok
macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok
macro_rules! ogg { ($x:pat | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
macro_rules! match_any {
( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
match $expr {
$(
$( $pat => $expr_arm, )+
)+
}
};
}

fn main() {
let result: Result<i64, i32> = Err(42);
let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into());
assert_eq!(int, 42);
}
32 changes: 32 additions & 0 deletions src/test/ui/macros/macro-or-patterns-back-compat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:7:21
|
LL | macro_rules! foo { ($x:pat | $y:pat) => {} }
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`
|
note: the lint level is defined here
--> $DIR/macro-or-patterns-back-compat.rs:5:9
|
LL | #![deny(or_patterns_back_compat)]
| ^^^^^^^^^^^^^^^^^^^^^^^

error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:8:23
|
LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} }
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`

error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:11:21
|
LL | macro_rules! ogg { ($x:pat | $y:pat2015) => {} }
| ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015`

error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro
--> $DIR/macro-or-patterns-back-compat.rs:13:26
|
LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => {
| ^^^^^^^^ help: use pat2015 to preserve semantics: `$pat:pat2015`

error: aborting due to 4 previous errors

0 comments on commit 36bcf40

Please sign in to comment.