Skip to content

Commit 1febce3

Browse files
Adds expr_2024 migration lit
This is adding a migration lint for the current (in the 2021 edition and previous) to move expr to expr_2021 from expr Co-Developed-by: Eric Holk Signed-off-by: Vincenzo Palazzo <[email protected]>
1 parent 9ffe52e commit 1febce3

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
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,141 @@
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 `expr` fragments
20+
/// during migration to the 2024 edition.
21+
///
22+
/// The `expr` fragment specifier will accept more expressions in the 2024 edition.
23+
/// To maintain the current behavior, use the `expr_2021` fragment specifier.
24+
///
25+
/// ### Example
26+
///
27+
/// ```rust,edition2021,compile_fail
28+
/// #![deny(edition_2024_expr_fragment_specifier)]
29+
/// macro_rules! m {
30+
/// ($e:expr) => {
31+
/// $e
32+
/// }
33+
/// }
34+
///
35+
/// fn main() {
36+
/// m!(1);
37+
/// }
38+
/// ```
39+
///
40+
/// {{produces}}
41+
///
42+
/// ### Explanation
43+
///
44+
/// Rust [editions] allow the language to evolve without breaking
45+
/// backwards compatibility. This lint catches code that uses new keywords
46+
/// that are added to the language that are used as identifiers (such as a
47+
/// variable name, function name, etc.). If you switch the compiler to a
48+
/// new edition without updating the code, then it will fail to compile if
49+
/// you are using a new keyword as an identifier.
50+
///
51+
/// This lint solves the problem automatically. It is "allow" by default
52+
/// because the code is perfectly valid in older editions. The [`cargo
53+
/// fix`] tool with the `--edition` flag will switch this lint to "warn"
54+
/// and automatically apply the suggested fix from the compiler.
55+
/// This provides a completely automated way to update old code for
56+
/// a new edition.
57+
///
58+
/// [editions]: https://doc.rust-lang.org/edition-guide/
59+
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
60+
pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
61+
Allow,
62+
"The `expr` fragment specifier will accept more expressions in the 2024 edition. \
63+
To keep the existing behavior, use the `expr_2021` fragment specifier.",
64+
@future_incompatible = FutureIncompatibleInfo {
65+
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
66+
reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
67+
};
68+
}
69+
70+
declare_lint_pass!(Expr2024 => [EDITION_2024_EXPR_FRAGMENT_SPECIFIER,]);
71+
72+
impl Expr2024 {
73+
fn check_tokens(&mut self, cx: &crate::EarlyContext<'_>, tokens: &TokenStream) {
74+
let mut prev_colon = false;
75+
let mut prev_identifier = false;
76+
let mut prev_dollar = false;
77+
for tt in tokens.trees() {
78+
debug!(
79+
"check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
80+
tt
81+
);
82+
match tt {
83+
TokenTree::Token(token, _) => match token.kind {
84+
TokenKind::Dollar => {
85+
prev_dollar = true;
86+
continue;
87+
}
88+
TokenKind::Ident(..) | TokenKind::NtIdent(..) => {
89+
if prev_colon && prev_identifier && prev_dollar {
90+
self.check_ident_token(cx, token);
91+
} else if prev_dollar {
92+
prev_identifier = true;
93+
continue;
94+
}
95+
}
96+
TokenKind::Colon => {
97+
if prev_dollar && prev_identifier {
98+
prev_colon = true;
99+
continue;
100+
}
101+
}
102+
_ => {}
103+
},
104+
TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts),
105+
}
106+
prev_colon = false;
107+
prev_identifier = false;
108+
prev_dollar = false;
109+
}
110+
}
111+
112+
fn check_ident_token(&mut self, cx: &crate::EarlyContext<'_>, token: &Token) {
113+
debug!("check_ident_token: {:?}", token);
114+
let (sym, edition) = match token.kind {
115+
TokenKind::Ident(sym, _) => (sym, Edition::Edition2024),
116+
_ => return,
117+
};
118+
119+
debug!("token.span.edition(): {:?}", token.span.edition());
120+
if token.span.edition() >= edition {
121+
return;
122+
}
123+
124+
if sym != sym::expr {
125+
return;
126+
}
127+
128+
debug!("emitting lint");
129+
cx.builder.emit_span_lint(
130+
&EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
131+
token.span.into(),
132+
MacroExprFragment2024 { suggestion: token.span },
133+
);
134+
}
135+
}
136+
137+
impl EarlyLintPass for Expr2024 {
138+
fn check_mac_def(&mut self, cx: &crate::EarlyContext<'_>, mc: &rustc_ast::MacroDef) {
139+
self.check_tokens(cx, &mc.body.tokens);
140+
}
141+
}

0 commit comments

Comments
 (0)