Rollup merge of #125627 - vincenzopalazzo:macros/cargo-fix-expr2024, r=compiler-errors,eholk
migration lint for `expr2024` for the edition 2024 This is adding a migration lint for the current (in the 2021 edition and previous) to move expr to expr_2021 from expr Issue https://github.com/rust-lang/rust/issues/123742 I created also a repository to test out the migration https://github.com/vincenzopalazzo/expr2024-cargo-fix-migration Co-Developed-by: ``@eholk``
This commit is contained in:
commit
21a0e86234
@ -439,6 +439,9 @@ lint_lintpass_by_hand = implementing `LintPass` by hand
|
|||||||
lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
|
lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
|
||||||
.note = the macro is defined here
|
.note = the macro is defined here
|
||||||
|
|
||||||
|
lint_macro_expr_fragment_specifier_2024_migration =
|
||||||
|
the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
.suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier
|
||||||
lint_macro_is_private = macro `{$ident}` is private
|
lint_macro_is_private = macro `{$ident}` is private
|
||||||
|
|
||||||
lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used
|
lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
mod let_underscore;
|
mod let_underscore;
|
||||||
mod levels;
|
mod levels;
|
||||||
mod lints;
|
mod lints;
|
||||||
|
mod macro_expr_fragment_specifier_2024_migration;
|
||||||
mod map_unit_fn;
|
mod map_unit_fn;
|
||||||
mod methods;
|
mod methods;
|
||||||
mod multiple_supertrait_upcastable;
|
mod multiple_supertrait_upcastable;
|
||||||
@ -97,6 +98,7 @@
|
|||||||
use internal::*;
|
use internal::*;
|
||||||
use invalid_from_utf8::*;
|
use invalid_from_utf8::*;
|
||||||
use let_underscore::*;
|
use let_underscore::*;
|
||||||
|
use macro_expr_fragment_specifier_2024_migration::*;
|
||||||
use map_unit_fn::*;
|
use map_unit_fn::*;
|
||||||
use methods::*;
|
use methods::*;
|
||||||
use multiple_supertrait_upcastable::*;
|
use multiple_supertrait_upcastable::*;
|
||||||
@ -170,6 +172,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
|
|||||||
IncompleteInternalFeatures: IncompleteInternalFeatures,
|
IncompleteInternalFeatures: IncompleteInternalFeatures,
|
||||||
RedundantSemicolons: RedundantSemicolons,
|
RedundantSemicolons: RedundantSemicolons,
|
||||||
UnusedDocComment: UnusedDocComment,
|
UnusedDocComment: UnusedDocComment,
|
||||||
|
Expr2024: Expr2024,
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -317,6 +317,13 @@ pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
|
|||||||
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
|
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
|
||||||
|
pub struct MacroExprFragment2024 {
|
||||||
|
#[suggestion(code = "expr_2021", applicability = "machine-applicable")]
|
||||||
|
pub suggestion: Span,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct BuiltinTypeAliasGenericBoundsSuggestion {
|
pub struct BuiltinTypeAliasGenericBoundsSuggestion {
|
||||||
pub suggestions: Vec<(Span, String)>,
|
pub suggestions: Vec<(Span, String)>,
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,155 @@
|
|||||||
|
//! Migration code for the `expr_fragment_specifier_2024`
|
||||||
|
//! rule.
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use rustc_ast::token::Token;
|
||||||
|
use rustc_ast::token::TokenKind;
|
||||||
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
|
use rustc_ast::tokenstream::TokenTree;
|
||||||
|
use rustc_session::declare_lint;
|
||||||
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_session::lint::FutureIncompatibilityReason;
|
||||||
|
use rustc_span::edition::Edition;
|
||||||
|
use rustc_span::sym;
|
||||||
|
|
||||||
|
use crate::lints::MacroExprFragment2024;
|
||||||
|
use crate::EarlyLintPass;
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `edition_2024_expr_fragment_specifier` lint detects the use of
|
||||||
|
/// `expr` fragments in macros during migration to the 2024 edition.
|
||||||
|
///
|
||||||
|
/// The `expr` fragment specifier will accept more expressions in the 2024
|
||||||
|
/// edition. To maintain the behavior from the 2021 edition and earlier, use
|
||||||
|
/// the `expr_2021` fragment specifier.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,edition2021,compile_fail
|
||||||
|
/// #![deny(edition_2024_expr_fragment_specifier)]
|
||||||
|
/// macro_rules! m {
|
||||||
|
/// ($e:expr) => {
|
||||||
|
/// $e
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// m!(1);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// Rust [editions] allow the language to evolve without breaking backwards
|
||||||
|
/// compatibility. This lint catches code that uses [macro matcher fragment
|
||||||
|
/// specifiers] that have changed meaning in the 2024 edition. If you switch
|
||||||
|
/// to the new edition without updating the code, your macros may behave
|
||||||
|
/// differently.
|
||||||
|
///
|
||||||
|
/// In the 2024 edition, the `expr` fragment specifier `expr` will also
|
||||||
|
/// match `const { ... }` blocks. This means if a macro had a pattern that
|
||||||
|
/// matched `$e:expr` and another that matches `const { $e: expr }`, for
|
||||||
|
/// example, that under the 2024 edition the first pattern would match while
|
||||||
|
/// in the 2021 and earlier editions the second pattern would match. To keep
|
||||||
|
/// the old behavior, use the `expr_2021` fragment specifier.
|
||||||
|
///
|
||||||
|
/// This lint detects macros whose behavior might change due to the changing
|
||||||
|
/// meaning of the `expr` fragment specifier. It is "allow" by default
|
||||||
|
/// because the code is perfectly valid in older editions. The [`cargo fix`]
|
||||||
|
/// tool with the `--edition` flag will switch this lint to "warn" and
|
||||||
|
/// automatically apply the suggested fix from the compiler. This provides a
|
||||||
|
/// completely automated way to update old code for a new edition.
|
||||||
|
///
|
||||||
|
/// Using `cargo fix --edition` with this lint will ensure that your code
|
||||||
|
/// retains the same behavior. This may not be the desired, as macro authors
|
||||||
|
/// often will want their macros to use the latest grammar for matching
|
||||||
|
/// expressions. Be sure to carefully review changes introduced by this lint
|
||||||
|
/// to ensure the macros implement the desired behavior.
|
||||||
|
///
|
||||||
|
/// [editions]: https://doc.rust-lang.org/edition-guide/
|
||||||
|
/// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
|
||||||
|
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
|
||||||
|
pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
|
||||||
|
Allow,
|
||||||
|
"The `expr` fragment specifier will accept more expressions in the 2024 edition. \
|
||||||
|
To keep the existing behavior, use the `expr_2021` fragment specifier.",
|
||||||
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
|
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||||
|
reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(Expr2024 => [EDITION_2024_EXPR_FRAGMENT_SPECIFIER,]);
|
||||||
|
|
||||||
|
impl Expr2024 {
|
||||||
|
fn check_tokens(&mut self, cx: &crate::EarlyContext<'_>, tokens: &TokenStream) {
|
||||||
|
let mut prev_colon = false;
|
||||||
|
let mut prev_identifier = false;
|
||||||
|
let mut prev_dollar = false;
|
||||||
|
for tt in tokens.trees() {
|
||||||
|
debug!(
|
||||||
|
"check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
|
||||||
|
tt
|
||||||
|
);
|
||||||
|
match tt {
|
||||||
|
TokenTree::Token(token, _) => match token.kind {
|
||||||
|
TokenKind::Dollar => {
|
||||||
|
prev_dollar = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TokenKind::Ident(..) | TokenKind::NtIdent(..) => {
|
||||||
|
if prev_colon && prev_identifier && prev_dollar {
|
||||||
|
self.check_ident_token(cx, token);
|
||||||
|
} else if prev_dollar {
|
||||||
|
prev_identifier = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenKind::Colon => {
|
||||||
|
if prev_dollar && prev_identifier {
|
||||||
|
prev_colon = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts),
|
||||||
|
}
|
||||||
|
prev_colon = false;
|
||||||
|
prev_identifier = false;
|
||||||
|
prev_dollar = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_ident_token(&mut self, cx: &crate::EarlyContext<'_>, token: &Token) {
|
||||||
|
debug!("check_ident_token: {:?}", token);
|
||||||
|
let (sym, edition) = match token.kind {
|
||||||
|
TokenKind::Ident(sym, _) => (sym, Edition::Edition2024),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("token.span.edition(): {:?}", token.span.edition());
|
||||||
|
if token.span.edition() >= edition {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if sym != sym::expr {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("emitting lint");
|
||||||
|
cx.builder.emit_span_lint(
|
||||||
|
&EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
|
||||||
|
token.span.into(),
|
||||||
|
MacroExprFragment2024 { suggestion: token.span },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EarlyLintPass for Expr2024 {
|
||||||
|
fn check_mac_def(&mut self, cx: &crate::EarlyContext<'_>, mc: &rustc_ast::MacroDef) {
|
||||||
|
self.check_tokens(cx, &mc.body.tokens);
|
||||||
|
}
|
||||||
|
}
|
24
tests/ui/macros/expr_2021_cargo_fix_edition.fixed
Normal file
24
tests/ui/macros/expr_2021_cargo_fix_edition.fixed
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//@ run-rustfix
|
||||||
|
//@ check-pass
|
||||||
|
//@ compile-flags: --edition=2021
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(expr_fragment_specifier_2024)]
|
||||||
|
#![warn(edition_2024_expr_fragment_specifier)]
|
||||||
|
|
||||||
|
macro_rules! m {
|
||||||
|
($e:expr_2021) => { //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
//~^ WARN: this changes meaning in Rust 2024
|
||||||
|
$e
|
||||||
|
};
|
||||||
|
($($i:expr_2021)*) => { }; //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
//~^ WARN: this changes meaning in Rust 2024
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! test {
|
||||||
|
(expr) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
m!(());
|
||||||
|
test!(expr);
|
||||||
|
}
|
24
tests/ui/macros/expr_2021_cargo_fix_edition.rs
Normal file
24
tests/ui/macros/expr_2021_cargo_fix_edition.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//@ run-rustfix
|
||||||
|
//@ check-pass
|
||||||
|
//@ compile-flags: --edition=2021
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(expr_fragment_specifier_2024)]
|
||||||
|
#![warn(edition_2024_expr_fragment_specifier)]
|
||||||
|
|
||||||
|
macro_rules! m {
|
||||||
|
($e:expr) => { //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
//~^ WARN: this changes meaning in Rust 2024
|
||||||
|
$e
|
||||||
|
};
|
||||||
|
($($i:expr)*) => { }; //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
//~^ WARN: this changes meaning in Rust 2024
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! test {
|
||||||
|
(expr) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
m!(());
|
||||||
|
test!(expr);
|
||||||
|
}
|
33
tests/ui/macros/expr_2021_cargo_fix_edition.stderr
Normal file
33
tests/ui/macros/expr_2021_cargo_fix_edition.stderr
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
warning: the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
--> $DIR/expr_2021_cargo_fix_edition.rs:9:9
|
||||||
|
|
|
||||||
|
LL | ($e:expr) => {
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/expr_2021_cargo_fix_edition.rs:6:9
|
||||||
|
|
|
||||||
|
LL | #![warn(edition_2024_expr_fragment_specifier)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: to keep the existing behavior, use the `expr_2021` fragment specifier
|
||||||
|
|
|
||||||
|
LL | ($e:expr_2021) => {
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
warning: the `expr` fragment specifier will accept more expressions in the 2024 edition
|
||||||
|
--> $DIR/expr_2021_cargo_fix_edition.rs:13:11
|
||||||
|
|
|
||||||
|
LL | ($($i:expr)*) => { };
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>
|
||||||
|
help: to keep the existing behavior, use the `expr_2021` fragment specifier
|
||||||
|
|
|
||||||
|
LL | ($($i:expr_2021)*) => { };
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
warning: 2 warnings emitted
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
error: no rules expected the token `const`
|
error: no rules expected the token `const`
|
||||||
--> $DIR/expr_2021_inline_const.rs:21:12
|
--> $DIR/expr_2021_inline_const.rs:26:12
|
||||||
|
|
|
|
||||||
LL | macro_rules! m2021 {
|
LL | macro_rules! m2021 {
|
||||||
| ------------------ when calling this macro
|
| ------------------ when calling this macro
|
||||||
@ -14,7 +14,7 @@ LL | ($e:expr_2021) => {
|
|||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error: no rules expected the token `const`
|
error: no rules expected the token `const`
|
||||||
--> $DIR/expr_2021_inline_const.rs:22:12
|
--> $DIR/expr_2021_inline_const.rs:27:12
|
||||||
|
|
|
|
||||||
LL | macro_rules! m2024 {
|
LL | macro_rules! m2024 {
|
||||||
| ------------------ when calling this macro
|
| ------------------ when calling this macro
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: no rules expected the token `const`
|
error: no rules expected the token `const`
|
||||||
--> $DIR/expr_2021_inline_const.rs:21:12
|
--> $DIR/expr_2021_inline_const.rs:26:12
|
||||||
|
|
|
|
||||||
LL | macro_rules! m2021 {
|
LL | macro_rules! m2021 {
|
||||||
| ------------------ when calling this macro
|
| ------------------ when calling this macro
|
||||||
|
@ -17,7 +17,14 @@ macro_rules! m2024 {
|
|||||||
$e
|
$e
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! test {
|
||||||
|
(expr) => {}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
m2021!(const { 1 }); //~ ERROR: no rules expected the token `const`
|
m2021!(const { 1 }); //~ ERROR: no rules expected the token `const`
|
||||||
m2024!(const { 1 }); //[edi2021]~ ERROR: no rules expected the token `const`
|
m2024!(const { 1 }); //[edi2021]~ ERROR: no rules expected the token `const`
|
||||||
|
|
||||||
|
test!(expr);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user