diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 930f87e697c04..bc8b4a71697e1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,12 +1,14 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_feature::AttributeStability; use rustc_hir::attrs::{CrateType, WindowsSubsystemKind}; -use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES; +use rustc_session::lint::builtin::{DUPLICATE_TOOLS, UNKNOWN_CRATE_TYPES}; use rustc_span::Symbol; use rustc_span::edit_distance::find_best_match_for_name_with_substrings; use super::prelude::*; -use crate::diagnostics::{ToolReserved, UnknownCrateTypes, UnknownCrateTypesSuggestion}; +use crate::diagnostics::{ + DuplicateTool, ToolReserved, UnknownCrateTypes, UnknownCrateTypesSuggestion, +}; pub(crate) struct CrateNameParser; @@ -352,6 +354,10 @@ fn parse_register_tool( cx.adcx().expected_identifier(path.span()); continue; }; + if !ident.name.can_be_raw() { + cx.adcx().expected_identifier(path.span()); + continue; + } if ident.name == sym::rustc { cx.should_emit @@ -359,8 +365,18 @@ fn parse_register_tool( continue; } + let mut lint_emitted = false; for tools in tools.iter_mut() { - tools.insert(ident); + if let Some(old_ident) = tools.replace(ident) + && !lint_emitted + { + lint_emitted = true; + cx.emit_lint( + DUPLICATE_TOOLS, + DuplicateTool { span: ident.span, tool: ident, old_ident_span: old_ident.span }, + ident.span, + ); + } } } } diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index bd99e370c70e5..6d430f5f66d1e 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -829,6 +829,16 @@ pub(crate) struct UnknownExternLangItem { pub lang_item: Symbol, } +#[derive(Diagnostic)] +#[diag("duplicate tool `{$tool}` registered")] +pub(crate) struct DuplicateTool { + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Ident, + #[label("already registered here")] + pub(crate) old_ident_span: Span, +} + #[derive(Diagnostic)] #[diag("tool `{$tool}` is reserved and cannot be registered")] pub(crate) struct ToolReserved { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b7d9323c6dac7..cf386fada9361 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -44,6 +44,7 @@ pub mod hardwired { DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_FEATURES, DUPLICATE_MACRO_ATTRIBUTES, + DUPLICATE_TOOLS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, @@ -5712,3 +5713,29 @@ declare_lint! { report_in_deps: false, }; } + +declare_lint! { + /// The `duplicate_tools` lint detects duplicate tools found in crate-level + /// [`register_tool` attributes] (including `register_attribute_tool` or `register_lint_tool`). + /// + /// [`register_tool` attributes]: https://doc.rust-lang.org/nightly/unstable-book/language-features/register-tool.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(register_tool)] + /// #![register_tool(foo)] + /// #![register_tool(foo)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Enabling a tool more than once is a no-op. + /// To avoid this warning, remove the second `register_tool()` attribute. + pub DUPLICATE_TOOLS, + Deny, + "duplicate tools found in crate-level `#[register_tools]` directives", + @feature_gate = register_tool; +} diff --git a/tests/ui/feature-gates/feature-gate-register_tool.rs b/tests/ui/feature-gates/feature-gate-register_tool.rs index 4034a95e8fb65..e884e1d4c070d 100644 --- a/tests/ui/feature-gates/feature-gate-register_tool.rs +++ b/tests/ui/feature-gates/feature-gate-register_tool.rs @@ -1,3 +1,4 @@ +#![allow(duplicate_tools)] //~ WARN [unknown_lints] #![register_tool(tool)] //~ ERROR the `register_tool` attribute is an experimental feature #![register_attribute_tool(attr_tool)] //~ ERROR the `register_attribute_tool` attribute is an experimental feature #![register_lint_tool(lint_tool)] //~ ERROR the `register_lint_tool` attribute is an experimental feature diff --git a/tests/ui/feature-gates/feature-gate-register_tool.stderr b/tests/ui/feature-gates/feature-gate-register_tool.stderr index 678ae48a00143..5f7a83a7c3a6c 100644 --- a/tests/ui/feature-gates/feature-gate-register_tool.stderr +++ b/tests/ui/feature-gates/feature-gate-register_tool.stderr @@ -1,5 +1,17 @@ +warning: unknown lint: `duplicate_tools` + --> $DIR/feature-gate-register_tool.rs:1:10 + | +LL | #![allow(duplicate_tools)] + | ^^^^^^^^^^^^^^^ + | + = note: the `duplicate_tools` lint is unstable + = note: see issue #66079 for more information + = help: add `#![feature(register_tool)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(unknown_lints)]` on by default + error[E0658]: the `register_tool` attribute is an experimental feature - --> $DIR/feature-gate-register_tool.rs:1:4 + --> $DIR/feature-gate-register_tool.rs:2:4 | LL | #![register_tool(tool)] | ^^^^^^^^^^^^^ @@ -9,7 +21,7 @@ LL | #![register_tool(tool)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the `register_attribute_tool` attribute is an experimental feature - --> $DIR/feature-gate-register_tool.rs:2:4 + --> $DIR/feature-gate-register_tool.rs:3:4 | LL | #![register_attribute_tool(attr_tool)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +31,7 @@ LL | #![register_attribute_tool(attr_tool)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the `register_lint_tool` attribute is an experimental feature - --> $DIR/feature-gate-register_tool.rs:3:4 + --> $DIR/feature-gate-register_tool.rs:4:4 | LL | #![register_lint_tool(lint_tool)] | ^^^^^^^^^^^^^^^^^^ @@ -28,6 +40,6 @@ LL | #![register_lint_tool(lint_tool)] = help: add `#![feature(register_tool)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/tool-attributes/auxiliary/use_tool.rs b/tests/ui/tool-attributes/auxiliary/use_tool.rs new file mode 100644 index 0000000000000..3faf2424c3b9b --- /dev/null +++ b/tests/ui/tool-attributes/auxiliary/use_tool.rs @@ -0,0 +1,2 @@ +#![feature(register_tool)] +#![register_tool(foo)] diff --git a/tests/ui/tool-attributes/crate-attr-dup-tool.rs b/tests/ui/tool-attributes/crate-attr-dup-tool.rs new file mode 100644 index 0000000000000..72bd2f013697d --- /dev/null +++ b/tests/ui/tool-attributes/crate-attr-dup-tool.rs @@ -0,0 +1,13 @@ +//@ check-pass +//@ compile-flags: -Z crate-attr=feature(register_tool) -Z crate-attr=register_tool(foo) +//@ compile-flags: -Z crate-attr=register_attribute_tool(bar) -Z crate-attr=register_lint_tool(baz) +//@ compile-flags: -A duplicate_features -A duplicate_tools +#![feature(register_tool)] +#![register_tool(foo)] +#![register_attribute_tool(bar)] +#![register_lint_tool(baz)] + +#[foo::foo] +#[bar::bar] +#[allow(foo::baz, baz::baz)] +fn main() {} diff --git a/tests/ui/tool-attributes/cross-crate.rs b/tests/ui/tool-attributes/cross-crate.rs new file mode 100644 index 0000000000000..1992174aca3e1 --- /dev/null +++ b/tests/ui/tool-attributes/cross-crate.rs @@ -0,0 +1,10 @@ +//@ aux-build: use_tool.rs + +// `use_tool` references tool "foo", and we want to check that it has no impact on this crate. +extern crate use_tool; + +#[foo::bar] //~ ERROR cannot find module or crate `foo` in this scope +#[allow(foo::baz)] //~ ERROR unknown tool name `foo` + //~| ERROR unknown tool name `foo` + //~| ERROR unknown tool name `foo` +fn main() {} diff --git a/tests/ui/tool-attributes/cross-crate.stderr b/tests/ui/tool-attributes/cross-crate.stderr new file mode 100644 index 0000000000000..051a1b05405e9 --- /dev/null +++ b/tests/ui/tool-attributes/cross-crate.stderr @@ -0,0 +1,36 @@ +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::baz` + --> $DIR/cross-crate.rs:7:9 + | +LL | #[allow(foo::baz)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + +error[E0433]: cannot find module or crate `foo` in this scope + --> $DIR/cross-crate.rs:6:3 + | +LL | #[foo::bar] + | ^^^ use of unresolved module or unlinked crate `foo` + +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::baz` + --> $DIR/cross-crate.rs:7:9 + | +LL | #[allow(foo::baz)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::baz` + --> $DIR/cross-crate.rs:7:9 + | +LL | #[allow(foo::baz)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0433, E0710. +For more information about an error, try `rustc --explain E0433`. diff --git a/tests/ui/tool-attributes/duplicate-tool.rs b/tests/ui/tool-attributes/duplicate-tool.rs index 603292d1eb3cd..3c5ede7698202 100644 --- a/tests/ui/tool-attributes/duplicate-tool.rs +++ b/tests/ui/tool-attributes/duplicate-tool.rs @@ -1,15 +1,16 @@ //@ check-pass #![feature(register_tool)] +#![warn(duplicate_tools)] // Register a tool multiple times is okay. #![register_tool(foo)] -#![register_tool(foo)] +#![register_tool(foo)] //~ WARN [duplicate_tools] #![register_tool(bar)] -#![register_attribute_tool(bar)] +#![register_attribute_tool(bar)] //~ WARN [duplicate_tools] #![register_tool(baz)] -#![register_lint_tool(baz)] -#![register_attribute_tool(qux)] +#![register_lint_tool(baz)] //~ WARN [duplicate_tools] #![register_attribute_tool(qux)] +#![register_attribute_tool(qux)] //~ WARN [duplicate_tools] #![register_lint_tool(quux)] -#![register_lint_tool(quux)] +#![register_lint_tool(quux)] //~ WARN [duplicate_tools] fn main() {} diff --git a/tests/ui/tool-attributes/duplicate-tool.stderr b/tests/ui/tool-attributes/duplicate-tool.stderr new file mode 100644 index 0000000000000..b0298f6414d57 --- /dev/null +++ b/tests/ui/tool-attributes/duplicate-tool.stderr @@ -0,0 +1,48 @@ +warning: duplicate tool `foo` registered + --> $DIR/duplicate-tool.rs:6:18 + | +LL | #![register_tool(foo)] + | --- already registered here +LL | #![register_tool(foo)] + | ^^^ + | +note: the lint level is defined here + --> $DIR/duplicate-tool.rs:3:9 + | +LL | #![warn(duplicate_tools)] + | ^^^^^^^^^^^^^^^ + +warning: duplicate tool `bar` registered + --> $DIR/duplicate-tool.rs:8:28 + | +LL | #![register_tool(bar)] + | --- already registered here +LL | #![register_attribute_tool(bar)] + | ^^^ + +warning: duplicate tool `baz` registered + --> $DIR/duplicate-tool.rs:10:23 + | +LL | #![register_tool(baz)] + | --- already registered here +LL | #![register_lint_tool(baz)] + | ^^^ + +warning: duplicate tool `qux` registered + --> $DIR/duplicate-tool.rs:12:28 + | +LL | #![register_attribute_tool(qux)] + | --- already registered here +LL | #![register_attribute_tool(qux)] + | ^^^ + +warning: duplicate tool `quux` registered + --> $DIR/duplicate-tool.rs:14:23 + | +LL | #![register_lint_tool(quux)] + | ---- already registered here +LL | #![register_lint_tool(quux)] + | ^^^^ + +warning: 5 warnings emitted + diff --git a/tests/ui/tool-attributes/invalid-tool.rs b/tests/ui/tool-attributes/invalid-tool.rs index aec31cc7f667c..51bf9bdf4b043 100644 --- a/tests/ui/tool-attributes/invalid-tool.rs +++ b/tests/ui/tool-attributes/invalid-tool.rs @@ -2,5 +2,24 @@ #![register_tool(1)] //~^ ERROR malformed `register_tool` attribute input +#![register_tool(_)] +//~^ ERROR expected identifier, found reserved identifier `_` +//~| ERROR expected identifier, found reserved identifier `_` +//~| ERROR expected identifier, found reserved identifier `_` +//~| ERROR malformed `register_tool` attribute input + +// Special path keywords cannot be used. +#![register_tool(crate)] +//~^ ERROR malformed `register_tool` attribute input +#![register_tool(self)] +//~^ ERROR malformed `register_tool` attribute input +#![register_tool(Self)] +//~^ ERROR malformed `register_tool` attribute input +#![register_tool(super)] +//~^ ERROR malformed `register_tool` attribute input + +// These are okay +#![register_tool(r#type)] +#![register_tool(铁锈)] fn main() {} diff --git a/tests/ui/tool-attributes/invalid-tool.stderr b/tests/ui/tool-attributes/invalid-tool.stderr index b4e2c3e7eaccb..5d7742c45d248 100644 --- a/tests/ui/tool-attributes/invalid-tool.stderr +++ b/tests/ui/tool-attributes/invalid-tool.stderr @@ -1,3 +1,17 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/invalid-tool.rs:5:18 + | +LL | #![register_tool(_)] + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/invalid-tool.rs:5:18 + | +LL | #![register_tool(_)] + | ^ expected identifier, found reserved identifier + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0565]: malformed `register_tool` attribute input --> $DIR/invalid-tool.rs:3:4 | @@ -12,6 +26,84 @@ LL - #![register_tool(1)] LL + #![register_tool(tool1, tool2, ...)] | -error: aborting due to 1 previous error +error: expected identifier, found reserved identifier `_` + --> $DIR/invalid-tool.rs:5:18 + | +LL | #![register_tool(_)] + | ^ expected identifier, found reserved identifier + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0565]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:5:4 + | +LL | #![register_tool(_)] + | ^^^^^^^^^^^^^^-^ + | | + | expected a valid identifier here + | +help: must be of the form + | +LL - #![register_tool(_)] +LL + #![register_tool(tool1, tool2, ...)] + | + +error[E0565]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:12:4 + | +LL | #![register_tool(crate)] + | ^^^^^^^^^^^^^^-----^ + | | + | expected a valid identifier here + | +help: must be of the form + | +LL - #![register_tool(crate)] +LL + #![register_tool(tool1, tool2, ...)] + | + +error[E0565]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:14:4 + | +LL | #![register_tool(self)] + | ^^^^^^^^^^^^^^----^ + | | + | expected a valid identifier here + | +help: must be of the form + | +LL - #![register_tool(self)] +LL + #![register_tool(tool1, tool2, ...)] + | + +error[E0565]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:16:4 + | +LL | #![register_tool(Self)] + | ^^^^^^^^^^^^^^----^ + | | + | expected a valid identifier here + | +help: must be of the form + | +LL - #![register_tool(Self)] +LL + #![register_tool(tool1, tool2, ...)] + | + +error[E0565]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:18:4 + | +LL | #![register_tool(super)] + | ^^^^^^^^^^^^^^-----^ + | | + | expected a valid identifier here + | +help: must be of the form + | +LL - #![register_tool(super)] +LL + #![register_tool(tool1, tool2, ...)] + | + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0565`.