about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-04-09 10:36:58 -0400
committerMichael Goulet <michael@errs.io>2024-04-22 11:51:50 -0400
commit7789874e6ef3ef3515a806c743ec88c503c75cd0 (patch)
treed8ac37674b42bec87d69932803ce8e2050ce3c11
parent7f2fc33da6633f5a764ddc263c769b6b2873d167 (diff)
downloadrust-7789874e6ef3ef3515a806c743ec88c503c75cd0.tar.gz
rust-7789874e6ef3ef3515a806c743ec88c503c75cd0.zip
Deny gen keyword in edition_2024_compat lints
-rw-r--r--compiler/rustc_lint/src/builtin.rs102
-rw-r--r--compiler/rustc_lint/src/lib.rs4
-rw-r--r--src/tools/lint-docs/src/groups.rs4
-rw-r--r--src/tools/lint-docs/src/lib.rs1
-rw-r--r--tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr1
-rw-r--r--tests/ui/async-await/await-keyword/2015-edition-warning.stderr1
-rw-r--r--tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr1
-rw-r--r--tests/ui/lint/lint-pre-expansion-extern-module.stderr4
-rw-r--r--tests/ui/rust-2018/async-ident-allowed.stderr2
-rw-r--r--tests/ui/rust-2018/async-ident.stderr1
-rw-r--r--tests/ui/rust-2018/dyn-keyword.stderr1
-rw-r--r--tests/ui/rust-2018/try-ident.stderr2
-rw-r--r--tests/ui/rust-2018/try-macro.stderr2
-rw-r--r--tests/ui/rust-2024/gen-kw.e2015.stderr26
-rw-r--r--tests/ui/rust-2024/gen-kw.e2018.stderr26
-rw-r--r--tests/ui/rust-2024/gen-kw.rs16
16 files changed, 158 insertions, 36 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8b974a461d4..0c89e186c47 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 <https://github.com/rust-lang/rust/issues/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 31c80c4d75b..00bc6b24b2a 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 0c39f2fa601..73f5738469e 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<String, BTreeSet<String>>;
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index aca780e33f0..22ab576b077 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 d99967eb23c..8cea73f8651 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 bf5c4d8d6aa..70b7fa52a19 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 89aded9134f..0d53fb024ac 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 8a6e1531d5f..32c76da98b5 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 <https://github.com/rust-lang/rust/issues/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 b413c0fd9ba..378c81d3c77 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 d15250c54ec..5b8d8184f4f 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 6f9a5ddb14f..f8245bc88f5 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 74015ac9da4..eaf4c235697 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 760378f0955..095c755539d 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 00000000000..b12363184b7
--- /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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 00000000000..e10fc4c4512
--- /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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 00000000000..3d2a3f95165
--- /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!
+}