From 2c66e154681087f8c1da6809e41e23c1e0962d30 Mon Sep 17 00:00:00 2001 From: hi-rustin Date: Thu, 25 Mar 2021 21:42:21 +0800 Subject: [PATCH 1/2] add OR_PATTERNS_BACK_COMPAT lint test: add more cases test: add comments refine msg --- compiler/rustc_expand/src/mbe/macro_rules.rs | 42 +++++++++++++++++-- compiler/rustc_lint/src/context.rs | 3 ++ compiler/rustc_lint_defs/src/builtin.rs | 35 ++++++++++++++++ compiler/rustc_lint_defs/src/lib.rs | 1 + .../macro-or-patterns-back-compat.fixed | 26 ++++++++++++ .../macros/macro-or-patterns-back-compat.rs | 26 ++++++++++++ .../macro-or-patterns-back-compat.stderr | 32 ++++++++++++++ 7 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/macros/macro-or-patterns-back-compat.fixed create mode 100644 src/test/ui/macros/macro-or-patterns-back-compat.rs create mode 100644 src/test/ui/macros/macro-or-patterns-back-compat.stderr diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 9ed478e6fccce..a85ed18055b77 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -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; @@ -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 specific 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) => { @@ -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 { @@ -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`", diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 14bd823ea2266..b3a19bfbf7532 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -709,6 +709,9 @@ pub trait LintContext: Sized { BuiltinLintDiagnostics::ProcMacroBackCompat(note) => { db.note(¬e); } + 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)); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4eaee7bf6e854..b602490905c29 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2959,6 +2959,7 @@ declare_lint_pass! { DISJOINT_CAPTURE_DROP_REORDER, LEGACY_DERIVE_HELPERS, PROC_MACRO_BACK_COMPAT, + OR_PATTERNS_BACK_COMPAT, ] } @@ -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 = 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", +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 400b367095ec3..70475563a4abe 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -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 diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.fixed b/src/test/ui/macros/macro-or-patterns-back-compat.fixed new file mode 100644 index 0000000000000..60ec0f7430de3 --- /dev/null +++ b/src/test/ui/macros/macro-or-patterns-back-compat.fixed @@ -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 specific is changing in Rust 2021, which may affect this macro +macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the pat fragment specific 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 specific 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 specific is changing in Rust 2021, which may affect this macro + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.rs b/src/test/ui/macros/macro-or-patterns-back-compat.rs new file mode 100644 index 0000000000000..dc9e8595ca291 --- /dev/null +++ b/src/test/ui/macros/macro-or-patterns-back-compat.rs @@ -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 specific is changing in Rust 2021, which may affect this macro +macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the pat fragment specific 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 specific 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 specific is changing in Rust 2021, which may affect this macro + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.stderr b/src/test/ui/macros/macro-or-patterns-back-compat.stderr new file mode 100644 index 0000000000000..630a11c17345d --- /dev/null +++ b/src/test/ui/macros/macro-or-patterns-back-compat.stderr @@ -0,0 +1,32 @@ +error: the meaning of the pat fragment specific 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 specific 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 specific 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 specific 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 + From aa987c2f57d5604d5dd3a7db5031f021dc8418a6 Mon Sep 17 00:00:00 2001 From: hi-rustin Date: Fri, 2 Apr 2021 08:08:02 +0800 Subject: [PATCH 2/2] address comments --- compiler/rustc_expand/src/mbe/macro_rules.rs | 2 +- src/test/ui/macros/macro-or-patterns-back-compat.fixed | 8 ++++---- src/test/ui/macros/macro-or-patterns-back-compat.rs | 8 ++++---- src/test/ui/macros/macro-or-patterns-back-compat.stderr | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index a85ed18055b77..bc45c57596e15 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -969,7 +969,7 @@ fn check_matcher_core( &OR_PATTERNS_BACK_COMPAT, span, ast::CRATE_NODE_ID, - &*format!("the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro",), + &*format!("the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",), BuiltinLintDiagnostics::OrPatternsBackCompat( span, suggestion, ), diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.fixed b/src/test/ui/macros/macro-or-patterns-back-compat.fixed index 60ec0f7430de3..4e8b057457c35 100644 --- a/src/test/ui/macros/macro-or-patterns-back-compat.fixed +++ b/src/test/ui/macros/macro-or-patterns-back-compat.fixed @@ -4,13 +4,13 @@ #![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 specific is changing in Rust 2021, which may affect this macro -macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro +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 specific is changing in Rust 2021, which may affect this macro +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 specific is changing in Rust 2021, which may affect this macro + ( $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, )+ diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.rs b/src/test/ui/macros/macro-or-patterns-back-compat.rs index dc9e8595ca291..023abae36d06b 100644 --- a/src/test/ui/macros/macro-or-patterns-back-compat.rs +++ b/src/test/ui/macros/macro-or-patterns-back-compat.rs @@ -4,13 +4,13 @@ #![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 specific is changing in Rust 2021, which may affect this macro -macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro +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 specific is changing in Rust 2021, which may affect this macro +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 specific is changing in Rust 2021, which may affect this macro + ( $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, )+ diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.stderr b/src/test/ui/macros/macro-or-patterns-back-compat.stderr index 630a11c17345d..29bbd696033c2 100644 --- a/src/test/ui/macros/macro-or-patterns-back-compat.stderr +++ b/src/test/ui/macros/macro-or-patterns-back-compat.stderr @@ -1,4 +1,4 @@ -error: the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro +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) => {} } @@ -10,19 +10,19 @@ note: the lint level is defined here LL | #![deny(or_patterns_back_compat)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: the meaning of the pat fragment specific is changing in Rust 2021, which may affect this macro +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 specific is changing in Rust 2021, which may affect this macro +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 specific is changing in Rust 2021, which may affect this macro +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 ),+ ) => {