Skip to content

Commit 77ecbdd

Browse files
authored
Unrolled build for rust-lang#125627
Rollup merge of rust-lang#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 rust-lang#123742 I created also a repository to test out the migration https://github.com/vincenzopalazzo/expr2024-cargo-fix-migration Co-Developed-by: ``@eholk``
2 parents 7caf672 + 568e78f commit 77ecbdd

10 files changed

+259
-3
lines changed

compiler/rustc_lint/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ lint_lintpass_by_hand = implementing `LintPass` by hand
439439
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
440440
.note = the macro is defined here
441441
442+
lint_macro_expr_fragment_specifier_2024_migration =
443+
the `expr` fragment specifier will accept more expressions in the 2024 edition
444+
.suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier
442445
lint_macro_is_private = macro `{$ident}` is private
443446
444447
lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ mod late;
6060
mod let_underscore;
6161
mod levels;
6262
mod lints;
63+
mod macro_expr_fragment_specifier_2024_migration;
6364
mod map_unit_fn;
6465
mod methods;
6566
mod multiple_supertrait_upcastable;
@@ -97,6 +98,7 @@ use impl_trait_overcaptures::ImplTraitOvercaptures;
9798
use internal::*;
9899
use invalid_from_utf8::*;
99100
use let_underscore::*;
101+
use macro_expr_fragment_specifier_2024_migration::*;
100102
use map_unit_fn::*;
101103
use methods::*;
102104
use multiple_supertrait_upcastable::*;
@@ -170,6 +172,7 @@ early_lint_methods!(
170172
IncompleteInternalFeatures: IncompleteInternalFeatures,
171173
RedundantSemicolons: RedundantSemicolons,
172174
UnusedDocComment: UnusedDocComment,
175+
Expr2024: Expr2024,
173176
]
174177
]
175178
);

compiler/rustc_lint/src/lints.rs

+7
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,13 @@ pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
317317
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
318318
}
319319

320+
#[derive(LintDiagnostic)]
321+
#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
322+
pub struct MacroExprFragment2024 {
323+
#[suggestion(code = "expr_2021", applicability = "machine-applicable")]
324+
pub suggestion: Span,
325+
}
326+
320327
pub struct BuiltinTypeAliasGenericBoundsSuggestion {
321328
pub suggestions: Vec<(Span, String)>,
322329
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//! Migration code for the `expr_fragment_specifier_2024`
2+
//! rule.
3+
use tracing::debug;
4+
5+
use rustc_ast::token::Token;
6+
use rustc_ast::token::TokenKind;
7+
use rustc_ast::tokenstream::TokenStream;
8+
use rustc_ast::tokenstream::TokenTree;
9+
use rustc_session::declare_lint;
10+
use rustc_session::declare_lint_pass;
11+
use rustc_session::lint::FutureIncompatibilityReason;
12+
use rustc_span::edition::Edition;
13+
use rustc_span::sym;
14+
15+
use crate::lints::MacroExprFragment2024;
16+
use crate::EarlyLintPass;
17+
18+
declare_lint! {
19+
/// The `edition_2024_expr_fragment_specifier` lint detects the use of
20+
/// `expr` fragments in macros during migration to the 2024 edition.
21+
///
22+
/// The `expr` fragment specifier will accept more expressions in the 2024
23+
/// edition. To maintain the behavior from the 2021 edition and earlier, use
24+
/// the `expr_2021` fragment specifier.
25+
///
26+
/// ### Example
27+
///
28+
/// ```rust,edition2021,compile_fail
29+
/// #![deny(edition_2024_expr_fragment_specifier)]
30+
/// macro_rules! m {
31+
/// ($e:expr) => {
32+
/// $e
33+
/// }
34+
/// }
35+
///
36+
/// fn main() {
37+
/// m!(1);
38+
/// }
39+
/// ```
40+
///
41+
/// {{produces}}
42+
///
43+
/// ### Explanation
44+
///
45+
/// Rust [editions] allow the language to evolve without breaking backwards
46+
/// compatibility. This lint catches code that uses [macro matcher fragment
47+
/// specifiers] that have changed meaning in the 2024 edition. If you switch
48+
/// to the new edition without updating the code, your macros may behave
49+
/// differently.
50+
///
51+
/// In the 2024 edition, the `expr` fragment specifier `expr` will also
52+
/// match `const { ... }` blocks. This means if a macro had a pattern that
53+
/// matched `$e:expr` and another that matches `const { $e: expr }`, for
54+
/// example, that under the 2024 edition the first pattern would match while
55+
/// in the 2021 and earlier editions the second pattern would match. To keep
56+
/// the old behavior, use the `expr_2021` fragment specifier.
57+
///
58+
/// This lint detects macros whose behavior might change due to the changing
59+
/// meaning of the `expr` fragment specifier. It is "allow" by default
60+
/// because the code is perfectly valid in older editions. The [`cargo fix`]
61+
/// tool with the `--edition` flag will switch this lint to "warn" and
62+
/// automatically apply the suggested fix from the compiler. This provides a
63+
/// completely automated way to update old code for a new edition.
64+
///
65+
/// Using `cargo fix --edition` with this lint will ensure that your code
66+
/// retains the same behavior. This may not be the desired, as macro authors
67+
/// often will want their macros to use the latest grammar for matching
68+
/// expressions. Be sure to carefully review changes introduced by this lint
69+
/// to ensure the macros implement the desired behavior.
70+
///
71+
/// [editions]: https://doc.rust-lang.org/edition-guide/
72+
/// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
73+
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
74+
pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
75+
Allow,
76+
"The `expr` fragment specifier will accept more expressions in the 2024 edition. \
77+
To keep the existing behavior, use the `expr_2021` fragment specifier.",
78+
@future_incompatible = FutureIncompatibleInfo {
79+
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
80+
reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
81+
};
82+
}
83+
84+
declare_lint_pass!(Expr2024 => [EDITION_2024_EXPR_FRAGMENT_SPECIFIER,]);
85+
86+
impl Expr2024 {
87+
fn check_tokens(&mut self, cx: &crate::EarlyContext<'_>, tokens: &TokenStream) {
88+
let mut prev_colon = false;
89+
let mut prev_identifier = false;
90+
let mut prev_dollar = false;
91+
for tt in tokens.trees() {
92+
debug!(
93+
"check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
94+
tt
95+
);
96+
match tt {
97+
TokenTree::Token(token, _) => match token.kind {
98+
TokenKind::Dollar => {
99+
prev_dollar = true;
100+
continue;
101+
}
102+
TokenKind::Ident(..) | TokenKind::NtIdent(..) => {
103+
if prev_colon && prev_identifier && prev_dollar {
104+
self.check_ident_token(cx, token);
105+
} else if prev_dollar {
106+
prev_identifier = true;
107+
continue;
108+
}
109+
}
110+
TokenKind::Colon => {
111+
if prev_dollar && prev_identifier {
112+
prev_colon = true;
113+
continue;
114+
}
115+
}
116+
_ => {}
117+
},
118+
TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts),
119+
}
120+
prev_colon = false;
121+
prev_identifier = false;
122+
prev_dollar = false;
123+
}
124+
}
125+
126+
fn check_ident_token(&mut self, cx: &crate::EarlyContext<'_>, token: &Token) {
127+
debug!("check_ident_token: {:?}", token);
128+
let (sym, edition) = match token.kind {
129+
TokenKind::Ident(sym, _) => (sym, Edition::Edition2024),
130+
_ => return,
131+
};
132+
133+
debug!("token.span.edition(): {:?}", token.span.edition());
134+
if token.span.edition() >= edition {
135+
return;
136+
}
137+
138+
if sym != sym::expr {
139+
return;
140+
}
141+
142+
debug!("emitting lint");
143+
cx.builder.emit_span_lint(
144+
&EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
145+
token.span.into(),
146+
MacroExprFragment2024 { suggestion: token.span },
147+
);
148+
}
149+
}
150+
151+
impl EarlyLintPass for Expr2024 {
152+
fn check_mac_def(&mut self, cx: &crate::EarlyContext<'_>, mc: &rustc_ast::MacroDef) {
153+
self.check_tokens(cx, &mc.body.tokens);
154+
}
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ run-rustfix
2+
//@ check-pass
3+
//@ compile-flags: --edition=2021
4+
#![allow(incomplete_features)]
5+
#![feature(expr_fragment_specifier_2024)]
6+
#![warn(edition_2024_expr_fragment_specifier)]
7+
8+
macro_rules! m {
9+
($e:expr_2021) => { //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
10+
//~^ WARN: this changes meaning in Rust 2024
11+
$e
12+
};
13+
($($i:expr_2021)*) => { }; //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
14+
//~^ WARN: this changes meaning in Rust 2024
15+
}
16+
17+
macro_rules! test {
18+
(expr) => {}
19+
}
20+
21+
fn main() {
22+
m!(());
23+
test!(expr);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ run-rustfix
2+
//@ check-pass
3+
//@ compile-flags: --edition=2021
4+
#![allow(incomplete_features)]
5+
#![feature(expr_fragment_specifier_2024)]
6+
#![warn(edition_2024_expr_fragment_specifier)]
7+
8+
macro_rules! m {
9+
($e:expr) => { //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
10+
//~^ WARN: this changes meaning in Rust 2024
11+
$e
12+
};
13+
($($i:expr)*) => { }; //~ WARN: the `expr` fragment specifier will accept more expressions in the 2024 edition
14+
//~^ WARN: this changes meaning in Rust 2024
15+
}
16+
17+
macro_rules! test {
18+
(expr) => {}
19+
}
20+
21+
fn main() {
22+
m!(());
23+
test!(expr);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
warning: the `expr` fragment specifier will accept more expressions in the 2024 edition
2+
--> $DIR/expr_2021_cargo_fix_edition.rs:9:9
3+
|
4+
LL | ($e:expr) => {
5+
| ^^^^
6+
|
7+
= warning: this changes meaning in Rust 2024
8+
= note: for more information, see Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>
9+
note: the lint level is defined here
10+
--> $DIR/expr_2021_cargo_fix_edition.rs:6:9
11+
|
12+
LL | #![warn(edition_2024_expr_fragment_specifier)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
help: to keep the existing behavior, use the `expr_2021` fragment specifier
15+
|
16+
LL | ($e:expr_2021) => {
17+
| ~~~~~~~~~
18+
19+
warning: the `expr` fragment specifier will accept more expressions in the 2024 edition
20+
--> $DIR/expr_2021_cargo_fix_edition.rs:13:11
21+
|
22+
LL | ($($i:expr)*) => { };
23+
| ^^^^
24+
|
25+
= warning: this changes meaning in Rust 2024
26+
= note: for more information, see Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>
27+
help: to keep the existing behavior, use the `expr_2021` fragment specifier
28+
|
29+
LL | ($($i:expr_2021)*) => { };
30+
| ~~~~~~~~~
31+
32+
warning: 2 warnings emitted
33+

tests/ui/macros/expr_2021_inline_const.edi2021.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: no rules expected the token `const`
2-
--> $DIR/expr_2021_inline_const.rs:21:12
2+
--> $DIR/expr_2021_inline_const.rs:26:12
33
|
44
LL | macro_rules! m2021 {
55
| ------------------ when calling this macro
@@ -14,7 +14,7 @@ LL | ($e:expr_2021) => {
1414
| ^^^^^^^^^^^^
1515

1616
error: no rules expected the token `const`
17-
--> $DIR/expr_2021_inline_const.rs:22:12
17+
--> $DIR/expr_2021_inline_const.rs:27:12
1818
|
1919
LL | macro_rules! m2024 {
2020
| ------------------ when calling this macro

tests/ui/macros/expr_2021_inline_const.edi2024.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: no rules expected the token `const`
2-
--> $DIR/expr_2021_inline_const.rs:21:12
2+
--> $DIR/expr_2021_inline_const.rs:26:12
33
|
44
LL | macro_rules! m2021 {
55
| ------------------ when calling this macro

tests/ui/macros/expr_2021_inline_const.rs

+7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ macro_rules! m2024 {
1717
$e
1818
};
1919
}
20+
21+
macro_rules! test {
22+
(expr) => {}
23+
}
24+
2025
fn main() {
2126
m2021!(const { 1 }); //~ ERROR: no rules expected the token `const`
2227
m2024!(const { 1 }); //[edi2021]~ ERROR: no rules expected the token `const`
28+
29+
test!(expr);
2330
}

0 commit comments

Comments
 (0)