diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 304e6eb712e3c..dba8981c8a680 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1618,6 +1618,8 @@ impl LintPass for KeywordIdents { } } +struct UnderMacro(bool); + impl KeywordIdents { fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) { for tt in tokens.into_trees() { @@ -1625,7 +1627,7 @@ impl KeywordIdents { TokenTree::Token(span, tok) => match tok.ident() { // only report non-raw idents Some((ident, false)) => { - self.check_ident(cx, ast::Ident { + self.check_ident_token(cx, UnderMacro(true), ast::Ident { span: span.substitute_dummy(ident.span), ..ident }); @@ -1638,16 +1640,12 @@ impl KeywordIdents { } } } -} -impl EarlyLintPass for KeywordIdents { - fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) { - self.check_tokens(cx, mac_def.stream()); - } - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) { - self.check_tokens(cx, mac.node.tts.clone().into()); - } - fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) { + fn check_ident_token(&mut self, + cx: &EarlyContext<'_>, + UnderMacro(under_macro): UnderMacro, + ident: ast::Ident) + { let ident_str = &ident.as_str()[..]; let cur_edition = cx.sess.edition(); let is_raw_ident = |ident: ast::Ident| { @@ -1656,7 +1654,22 @@ impl EarlyLintPass for KeywordIdents { let next_edition = match cur_edition { Edition::Edition2015 => { match ident_str { - "async" | "try" | "dyn" => Edition::Edition2018, + "async" | "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. + "dyn" if !under_macro => Edition::Edition2018, + // Only issue warnings for `await` if the `async_await` // feature isn't being used. Otherwise, users need // to keep using `await` for the macro exposed by std. @@ -1714,6 +1727,18 @@ impl EarlyLintPass for KeywordIdents { } } +impl EarlyLintPass for KeywordIdents { + fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) { + self.check_tokens(cx, mac_def.stream()); + } + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) { + self.check_tokens(cx, mac.node.tts.clone().into()); + } + fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) { + self.check_ident_token(cx, UnderMacro(false), ident); + } +} + pub struct ExplicitOutlivesRequirements; diff --git a/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.fixed b/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.fixed new file mode 100644 index 0000000000000..003736208ed38 --- /dev/null +++ b/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.fixed @@ -0,0 +1,81 @@ +// Under the 2015 edition with the keyword_idents lint, `dyn` is not +// entirely acceptable as an identifier. We currently do not attempt +// to detect or fix uses of `dyn` under a macro. Since we are testing +// this file via `rustfix`, we want the rustfix output to be +// compilable; so the macros here carefully use `dyn` "correctly." + +// run-rustfix + +#![allow(non_camel_case_types)] +#![deny(keyword_idents)] + +mod outer_mod { + pub mod r#dyn { +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + pub struct r#dyn; +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + } +} +use outer_mod::r#dyn::r#dyn; +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + +fn main() { + match r#dyn { r#dyn => {} } +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + macro_defn::r#dyn(); +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + + macro_defn::boxed(); +} + +mod macro_defn { + use super::Trait; + + macro_rules! r#dyn { +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + + // Note that we do not lint nor fix occurrences under macros + ($dyn:tt) => { (Box, Box<$dyn Trait>) } + } + + pub fn r#dyn() -> ::outer_mod::r#dyn::r#dyn { +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + ::outer_mod::r#dyn::r#dyn +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + } + + + + pub fn boxed() -> r#dyn!( + //~^ ERROR `dyn` is a keyword + //~| WARN was previously accepted + + // Note that we do not lint nor fix occurrences under macros + dyn + ) + { + (Box::new(1), Box::new(2)) + } +} + +pub trait Trait { } + +impl Trait for u32 { } diff --git a/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.rs b/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.rs new file mode 100644 index 0000000000000..0e5c39fc501be --- /dev/null +++ b/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.rs @@ -0,0 +1,81 @@ +// Under the 2015 edition with the keyword_idents lint, `dyn` is not +// entirely acceptable as an identifier. We currently do not attempt +// to detect or fix uses of `dyn` under a macro. Since we are testing +// this file via `rustfix`, we want the rustfix output to be +// compilable; so the macros here carefully use `dyn` "correctly." + +// run-rustfix + +#![allow(non_camel_case_types)] +#![deny(keyword_idents)] + +mod outer_mod { + pub mod dyn { +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + pub struct dyn; +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + } +} +use outer_mod::dyn::dyn; +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + +fn main() { + match dyn { dyn => {} } +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + macro_defn::dyn(); +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + + macro_defn::boxed(); +} + +mod macro_defn { + use super::Trait; + + macro_rules! dyn { +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted + + // Note that we do not lint nor fix occurrences under macros + ($dyn:tt) => { (Box, Box<$dyn Trait>) } + } + + pub fn dyn() -> ::outer_mod::dyn::dyn { +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + ::outer_mod::dyn::dyn +//~^ ERROR `dyn` is a keyword +//~| WARN was previously accepted +//~| ERROR `dyn` is a keyword +//~| WARN was previously accepted + } + + + + pub fn boxed() -> dyn!( + //~^ ERROR `dyn` is a keyword + //~| WARN was previously accepted + + // Note that we do not lint nor fix occurrences under macros + dyn + ) + { + (Box::new(1), Box::new(2)) + } +} + +pub trait Trait { } + +impl Trait for u32 { } diff --git a/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr b/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr new file mode 100644 index 0000000000000..361727733bc57 --- /dev/null +++ b/src/test/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr @@ -0,0 +1,133 @@ +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:13:13 + | +LL | pub mod dyn { + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | +note: lint level defined here + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:10:9 + | +LL | #![deny(keyword_idents)] + | ^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:16:20 + | +LL | pub struct dyn; + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:21:16 + | +LL | use outer_mod::dyn::dyn; + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:21:21 + | +LL | use outer_mod::dyn::dyn; + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:28:11 + | +LL | match dyn { dyn => {} } + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:28:17 + | +LL | match dyn { dyn => {} } + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:33:17 + | +LL | macro_defn::dyn(); + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:43:18 + | +LL | macro_rules! dyn { + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:51:12 + | +LL | pub fn dyn() -> ::outer_mod::dyn::dyn { + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:51:34 + | +LL | pub fn dyn() -> ::outer_mod::dyn::dyn { + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:51:39 + | +LL | pub fn dyn() -> ::outer_mod::dyn::dyn { + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:58:22 + | +LL | ::outer_mod::dyn::dyn + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:58:27 + | +LL | ::outer_mod::dyn::dyn + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:67:23 + | +LL | pub fn boxed() -> dyn!( + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: aborting due to 14 previous errors + diff --git a/src/test/ui/dyn-keyword/dyn-2015-idents-in-decl-macros-unlinted.rs b/src/test/ui/dyn-keyword/dyn-2015-idents-in-decl-macros-unlinted.rs new file mode 100644 index 0000000000000..f535791d7fbe4 --- /dev/null +++ b/src/test/ui/dyn-keyword/dyn-2015-idents-in-decl-macros-unlinted.rs @@ -0,0 +1,51 @@ +// compile-pass + +// Under the 2015 edition with the keyword_idents lint, `dyn` is +// not entirely acceptable as an identifier. +// +// We currently do not attempt to detect or fix uses of `dyn` as an +// identifier under a macro, including under the declarative `macro` +// forms from macros 1.2 and macros 2.0. + +#![feature(decl_macro)] +#![allow(non_camel_case_types)] +#![deny(keyword_idents)] + +mod outer_mod { + pub mod r#dyn { + pub struct r#dyn; + } +} + +// Here we are illustrating that the current lint does not flag the +// occurrences of `dyn` in this macro definition; however, it +// certainly *could* (and it would be nice if it did), since these +// occurrences are not compatible with the 2018 edition's +// interpretation of `dyn` as a keyword. +macro defn_has_dyn_idents() { ::outer_mod::dyn::dyn } + +struct X; +trait Trait { fn hello(&self) { }} +impl Trait for X { } + +macro tt_trait($arg:tt) { & $arg Trait } +macro id_trait($id:ident) { & $id Trait } + +fn main() { + defn_has_dyn_idents!(); + + // Here we are illustrating that the current lint does not flag + // the occurrences of `dyn` in these macro invocations. It + // definitely should *not* flag the one in `tt_trait`, since that + // is expanding in a valid fashion to `&dyn Trait`. + // + // It is arguable whether it would be valid to flag the occurrence + // in `id_trait`, since that macro specifies that it takes an + // `ident` as its input. + fn f_tt(x: &X) -> tt_trait!(dyn) { x } + fn f_id(x: &X) -> id_trait!(dyn) { x } + + let x = X; + f_tt(&x).hello(); + f_id(&x).hello(); +} diff --git a/src/test/ui/dyn-keyword/dyn-2015-idents-in-macros-unlinted.rs b/src/test/ui/dyn-keyword/dyn-2015-idents-in-macros-unlinted.rs new file mode 100644 index 0000000000000..27e490558689f --- /dev/null +++ b/src/test/ui/dyn-keyword/dyn-2015-idents-in-macros-unlinted.rs @@ -0,0 +1,56 @@ +// compile-pass + +// Under the 2015 edition with the keyword_idents lint, `dyn` is +// not entirely acceptable as an identifier. +// +// We currently do not attempt to detect or fix uses of `dyn` as an +// identifier under a macro. + +#![allow(non_camel_case_types)] +#![deny(keyword_idents)] + +mod outer_mod { + pub mod r#dyn { + pub struct r#dyn; + } +} + +// Here we are illustrating that the current lint does not flag the +// occurrences of `dyn` in this macro definition; however, it +// certainly *could* (and it would be nice if it did), since these +// occurrences are not compatible with the 2018 edition's +// interpretation of `dyn` as a keyword. +macro_rules! defn_has_dyn_idents { + () => { ::outer_mod::dyn::dyn } +} + +struct X; +trait Trait { fn hello(&self) { }} +impl Trait for X { } + +macro_rules! tt_trait { + ($arg:tt) => { & $arg Trait } +} + +macro_rules! id_trait { + ($id:ident) => { & $id Trait } +} + +fn main() { + defn_has_dyn_idents!(); + + // Here we are illustrating that the current lint does not flag + // the occurrences of `dyn` in these macro invocations. It + // definitely should *not* flag the one in `tt_trait`, since that + // is expanding in a valid fashion to `&dyn Trait`. + // + // It is arguable whether it would be valid to flag the occurrence + // in `id_trait`, since that macro specifies that it takes an + // `ident` as its input. + fn f_tt(x: &X) -> tt_trait!(dyn) { x } + fn f_id(x: &X) -> id_trait!(dyn) { x } + + let x = X; + f_tt(&x).hello(); + f_id(&x).hello(); +} diff --git a/src/test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs b/src/test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs new file mode 100644 index 0000000000000..8cef5c2b34947 --- /dev/null +++ b/src/test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs @@ -0,0 +1,27 @@ +// Under the 2015 edition without the keyword_idents lint, `dyn` is +// entirely acceptable as an identifier. + +// compile-pass + +#![allow(non_camel_case_types)] + +mod outer_mod { + pub mod dyn { + pub struct dyn; + } +} +use outer_mod::dyn::dyn; + +fn main() { + match dyn { dyn => {} } + macro_defn::dyn(); +} +mod macro_defn { + macro_rules! dyn { + () => { ::outer_mod::dyn::dyn } + } + + pub fn dyn() -> ::outer_mod::dyn::dyn { + dyn!() + } +} diff --git a/src/test/ui/dyn-keyword/issue-56327-dyn-trait-in-macro-is-okay.rs b/src/test/ui/dyn-keyword/issue-56327-dyn-trait-in-macro-is-okay.rs new file mode 100644 index 0000000000000..ff3830d61755a --- /dev/null +++ b/src/test/ui/dyn-keyword/issue-56327-dyn-trait-in-macro-is-okay.rs @@ -0,0 +1,25 @@ +// compile-pass + +// rust-lang/rust#56327: Some occurrences of `dyn` within a macro are +// not instances of identifiers, and thus should *not* be caught by the +// keyword_ident lint. +// +// Otherwise, rustfix replaces the type `Box` with +// `Box`, which is injecting a bug rather than fixing +// anything. + +#![deny(rust_2018_compatibility)] + +macro_rules! foo { + () => { + fn generated_foo() { + let _x: Box; + } + } +} + +foo!(); + +fn main() { + generated_foo(); +}