From 7789874e6ef3ef3515a806c743ec88c503c75cd0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 9 Apr 2024 10:36:58 -0400 Subject: [PATCH] Deny gen keyword in edition_2024_compat lints --- compiler/rustc_lint/src/builtin.rs | 102 ++++++++++++------ compiler/rustc_lint/src/lib.rs | 4 +- src/tools/lint-docs/src/groups.rs | 4 + src/tools/lint-docs/src/lib.rs | 1 + ...015-edition-error-various-positions.stderr | 1 + .../await-keyword/2015-edition-warning.stderr | 1 + ...dyn-2015-edition-keyword-ident-lint.stderr | 1 + .../lint-pre-expansion-extern-module.stderr | 4 +- tests/ui/rust-2018/async-ident-allowed.stderr | 2 +- tests/ui/rust-2018/async-ident.stderr | 1 + tests/ui/rust-2018/dyn-keyword.stderr | 1 + tests/ui/rust-2018/try-ident.stderr | 2 +- tests/ui/rust-2018/try-macro.stderr | 2 +- tests/ui/rust-2024/gen-kw.e2015.stderr | 26 +++++ tests/ui/rust-2024/gen-kw.e2018.stderr | 26 +++++ tests/ui/rust-2024/gen-kw.rs | 16 +++ 16 files changed, 158 insertions(+), 36 deletions(-) create mode 100644 tests/ui/rust-2024/gen-kw.e2015.stderr create mode 100644 tests/ui/rust-2024/gen-kw.e2018.stderr create mode 100644 tests/ui/rust-2024/gen-kw.rs diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8b974a461d4da..0c89e186c47e3 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1769,13 +1769,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } declare_lint! { - /// The `keyword_idents` lint detects edition keywords being used as an + /// The `keyword_idents_2018` lint detects edition keywords being used as an /// identifier. /// /// ### Example /// /// ```rust,edition2015,compile_fail - /// #![deny(keyword_idents)] + /// #![deny(keyword_idents_2018)] /// // edition 2015 /// fn dyn() {} /// ``` @@ -1804,7 +1804,7 @@ declare_lint! { /// [editions]: https://doc.rust-lang.org/edition-guide/ /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html - pub KEYWORD_IDENTS, + pub KEYWORD_IDENTS_2018, Allow, "detects edition keywords being used as an identifier", @future_incompatible = FutureIncompatibleInfo { @@ -1813,9 +1813,54 @@ declare_lint! { }; } +declare_lint! { + /// The `keyword_idents_2024` lint detects edition keywords being used as an + /// identifier. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(keyword_idents_2024)] + /// // edition 2015 + /// fn gen() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses new keywords + /// that are added to the language that are used as identifiers (such as a + /// variable name, function name, etc.). If you switch the compiler to a + /// new edition without updating the code, then it will fail to compile if + /// you are using a new keyword as an identifier. + /// + /// You can manually change the identifiers to a non-keyword, or use a + /// [raw identifier], for example `r#gen`, to transition to a new edition. + /// + /// This lint solves the problem automatically. 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 (which is + /// to use a raw identifier). This provides a completely automated way to + /// update old code for a new edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html + pub KEYWORD_IDENTS_2024, + Allow, + "detects edition keywords being used as an identifier", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #49716 ", + }; +} + declare_lint_pass!( /// Check for uses of edition keywords used as an identifier. - KeywordIdents => [KEYWORD_IDENTS] + KeywordIdents => [KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024] ); struct UnderMacro(bool); @@ -1841,42 +1886,39 @@ impl KeywordIdents { UnderMacro(under_macro): UnderMacro, ident: Ident, ) { - let next_edition = match cx.sess().edition() { - Edition::Edition2015 => { - match ident.name { - kw::Async | kw::Await | kw::Try => Edition::Edition2018, - - // rust-lang/rust#56327: Conservatively do not - // attempt to report occurrences of `dyn` within - // macro definitions or invocations, because `dyn` - // can legitimately occur as a contextual keyword - // in 2015 code denoting its 2018 meaning, and we - // do not want rustfix to inject bugs into working - // code by rewriting such occurrences. - // - // But if we see `dyn` outside of a macro, we know - // its precise role in the parsed AST and thus are - // assured this is truly an attempt to use it as - // an identifier. - kw::Dyn if !under_macro => Edition::Edition2018, - - _ => return, - } - } + let (lint, edition) = match ident.name { + kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018), + + // rust-lang/rust#56327: Conservatively do not + // attempt to report occurrences of `dyn` within + // macro definitions or invocations, because `dyn` + // can legitimately occur as a contextual keyword + // in 2015 code denoting its 2018 meaning, and we + // do not want rustfix to inject bugs into working + // code by rewriting such occurrences. + // + // But if we see `dyn` outside of a macro, we know + // its precise role in the parsed AST and thus are + // assured this is truly an attempt to use it as + // an identifier. + kw::Dyn if !under_macro => (KEYWORD_IDENTS_2018, Edition::Edition2018), + + kw::Gen => (KEYWORD_IDENTS_2024, Edition::Edition2024), - // There are no new keywords yet for the 2018 edition and beyond. _ => return, }; // Don't lint `r#foo`. - if cx.sess().psess.raw_identifier_spans.contains(ident.span) { + if ident.span.edition() >= edition + || cx.sess().psess.raw_identifier_spans.contains(ident.span) + { return; } cx.emit_span_lint( - KEYWORD_IDENTS, + lint, ident.span, - BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span }, + BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span }, ); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 31c80c4d75bb1..00bc6b24b2a16 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -313,6 +313,8 @@ fn register_builtins(store: &mut LintStore) { // MACRO_USE_EXTERN_CRATE ); + add_lint_group!("keyword_idents", KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024); + add_lint_group!( "refining_impl_trait", REFINING_IMPL_TRAIT_REACHABLE, @@ -325,7 +327,7 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("bare_trait_object", "bare_trait_objects"); store.register_renamed("unstable_name_collision", "unstable_name_collisions"); store.register_renamed("unused_doc_comment", "unused_doc_comments"); - store.register_renamed("async_idents", "keyword_idents"); + store.register_renamed("async_idents", "keyword_idents_2018"); store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); store.register_renamed("redundant_semicolon", "redundant_semicolons"); store.register_renamed("overlapping_patterns", "overlapping_range_endpoints"); diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index 0c39f2fa6010f..73f5738469ed1 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -20,6 +20,10 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ "refining-impl-trait", "Detects refinement of `impl Trait` return types by trait implementations", ), + ( + "keyword-idents", + "Lints that detect identifiers which will be come keywords in later editions", + ), ]; type LintGroups = BTreeMap>; diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index aca780e33f0ea..22ab576b07762 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -25,6 +25,7 @@ static RENAMES: &[(Level, &[(&str, &str)])] = &[ ("elided-lifetime-in-path", "elided-lifetimes-in-paths"), ("async-idents", "keyword-idents"), ("disjoint-capture-migration", "rust-2021-incompatible-closure-captures"), + ("keyword-idents", "keyword-idents-2018"), ("or-patterns-back-compat", "rust-2021-incompatible-or-patterns"), ], ), diff --git a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr index d99967eb23ca6..8cea73f865148 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr +++ b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: `await` is a keyword in the 2018 edition --> $DIR/2015-edition-error-various-positions.rs:7:20 diff --git a/tests/ui/async-await/await-keyword/2015-edition-warning.stderr b/tests/ui/async-await/await-keyword/2015-edition-warning.stderr index bf5c4d8d6aab3..70b7fa52a19c7 100644 --- a/tests/ui/async-await/await-keyword/2015-edition-warning.stderr +++ b/tests/ui/async-await/await-keyword/2015-edition-warning.stderr @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: `await` is a keyword in the 2018 edition --> $DIR/2015-edition-warning.rs:10:20 diff --git a/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr b/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr index 89aded9134f9f..0d53fb024acab 100644 --- a/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr +++ b/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: `dyn` is a keyword in the 2018 edition --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:17:20 diff --git a/tests/ui/lint/lint-pre-expansion-extern-module.stderr b/tests/ui/lint/lint-pre-expansion-extern-module.stderr index 8a6e1531d5fdb..32c76da98b525 100644 --- a/tests/ui/lint/lint-pre-expansion-extern-module.stderr +++ b/tests/ui/lint/lint-pre-expansion-extern-module.stderr @@ -6,8 +6,8 @@ LL | pub fn try() {} | = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! = note: for more information, see issue #49716 - = note: `-W keyword-idents` implied by `-W rust-2018-compatibility` - = help: to override `-W rust-2018-compatibility` add `#[allow(keyword_idents)]` + = note: `-W keyword-idents-2018` implied by `-W rust-2018-compatibility` + = help: to override `-W rust-2018-compatibility` add `#[allow(keyword_idents_2018)]` warning: 1 warning emitted diff --git a/tests/ui/rust-2018/async-ident-allowed.stderr b/tests/ui/rust-2018/async-ident-allowed.stderr index b413c0fd9ba8f..378c81d3c770b 100644 --- a/tests/ui/rust-2018/async-ident-allowed.stderr +++ b/tests/ui/rust-2018/async-ident-allowed.stderr @@ -11,7 +11,7 @@ note: the lint level is defined here | LL | #![deny(rust_2018_compatibility)] | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[deny(keyword_idents)]` implied by `#[deny(rust_2018_compatibility)]` + = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(rust_2018_compatibility)]` error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/async-ident.stderr b/tests/ui/rust-2018/async-ident.stderr index d15250c54ec92..5b8d8184f4f3d 100644 --- a/tests/ui/rust-2018/async-ident.stderr +++ b/tests/ui/rust-2018/async-ident.stderr @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: `async` is a keyword in the 2018 edition --> $DIR/async-ident.rs:12:7 diff --git a/tests/ui/rust-2018/dyn-keyword.stderr b/tests/ui/rust-2018/dyn-keyword.stderr index 6f9a5ddb14fe1..f8245bc88f573 100644 --- a/tests/ui/rust-2018/dyn-keyword.stderr +++ b/tests/ui/rust-2018/dyn-keyword.stderr @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(keyword_idents)] | ^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]` error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/try-ident.stderr b/tests/ui/rust-2018/try-ident.stderr index 74015ac9da426..eaf4c235697c7 100644 --- a/tests/ui/rust-2018/try-ident.stderr +++ b/tests/ui/rust-2018/try-ident.stderr @@ -11,7 +11,7 @@ note: the lint level is defined here | LL | #![warn(rust_2018_compatibility)] | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[warn(keyword_idents)]` implied by `#[warn(rust_2018_compatibility)]` + = note: `#[warn(keyword_idents_2018)]` implied by `#[warn(rust_2018_compatibility)]` warning: `try` is a keyword in the 2018 edition --> $DIR/try-ident.rs:12:4 diff --git a/tests/ui/rust-2018/try-macro.stderr b/tests/ui/rust-2018/try-macro.stderr index 760378f0955c7..095c755539db4 100644 --- a/tests/ui/rust-2018/try-macro.stderr +++ b/tests/ui/rust-2018/try-macro.stderr @@ -11,7 +11,7 @@ note: the lint level is defined here | LL | #![warn(rust_2018_compatibility)] | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[warn(keyword_idents)]` implied by `#[warn(rust_2018_compatibility)]` + = note: `#[warn(keyword_idents_2018)]` implied by `#[warn(rust_2018_compatibility)]` warning: 1 warning emitted diff --git a/tests/ui/rust-2024/gen-kw.e2015.stderr b/tests/ui/rust-2024/gen-kw.e2015.stderr new file mode 100644 index 0000000000000..b12363184b713 --- /dev/null +++ b/tests/ui/rust-2024/gen-kw.e2015.stderr @@ -0,0 +1,26 @@ +error: `gen` is a keyword in the 2024 edition + --> $DIR/gen-kw.rs:6:4 + | +LL | fn gen() {} + | ^^^ help: you can use a raw identifier to stay compatible: `r#gen` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #49716 +note: the lint level is defined here + --> $DIR/gen-kw.rs:4:9 + | +LL | #![deny(rust_2024_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2024)]` implied by `#[deny(rust_2024_compatibility)]` + +error: `gen` is a keyword in the 2024 edition + --> $DIR/gen-kw.rs:12:9 + | +LL | let gen = r#gen; + | ^^^ help: you can use a raw identifier to stay compatible: `r#gen` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #49716 + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/gen-kw.e2018.stderr b/tests/ui/rust-2024/gen-kw.e2018.stderr new file mode 100644 index 0000000000000..e10fc4c45120c --- /dev/null +++ b/tests/ui/rust-2024/gen-kw.e2018.stderr @@ -0,0 +1,26 @@ +error: `gen` is a keyword in the 2024 edition + --> $DIR/gen-kw.rs:6:4 + | +LL | fn gen() {} + | ^^^ help: you can use a raw identifier to stay compatible: `r#gen` + | + = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024! + = note: for more information, see issue #49716 +note: the lint level is defined here + --> $DIR/gen-kw.rs:4:9 + | +LL | #![deny(rust_2024_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(keyword_idents_2024)]` implied by `#[deny(rust_2024_compatibility)]` + +error: `gen` is a keyword in the 2024 edition + --> $DIR/gen-kw.rs:12:9 + | +LL | let gen = r#gen; + | ^^^ help: you can use a raw identifier to stay compatible: `r#gen` + | + = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024! + = note: for more information, see issue #49716 + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/gen-kw.rs b/tests/ui/rust-2024/gen-kw.rs new file mode 100644 index 0000000000000..3d2a3f9516558 --- /dev/null +++ b/tests/ui/rust-2024/gen-kw.rs @@ -0,0 +1,16 @@ +//@ revisions: e2015 e2018 +//@[e2018] edition: 2018 + +#![deny(rust_2024_compatibility)] + +fn gen() {} +//~^ ERROR `gen` is a keyword in the 2024 edition +//[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! +//[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024! + +fn main() { + let gen = r#gen; + //~^ ERROR `gen` is a keyword in the 2024 edition + //[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + //[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024! +}