rust-lang/rust · #164486
improve diagnostic for literals in lint attributes
compiler/rustc_attr_parsing/src/attributes/lint.rs63 + / 51 −
@@ -7,7 +7,7 @@ use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; use crate::context::{AcceptContext, ExpectStringLiteral, FinalizeContext};-use crate::parser::ArgParser;+use crate::parser::{ArgParser, MetaItemOrLitParser}; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::{Allow, Warn}; use crate::{AttributeTemplate, diagnostics, template};@@ -37,7 +37,7 @@ impl LintParser { if !p.path().word_is(sym::reason) { cx.emit_err(diagnostics::MalformedAttribute { span: p.span(),- sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(p.span()),+ kind: diagnostics::MalformedAttributeKind::BadAttributeArgument(p.span()), }); } else { parsers = &parsers[..(parsers.len() - 1)];@@ -48,58 +48,70 @@ impl LintParser { }; for item in parsers {- if let Some(p) = item.meta_item() {- match p.args() {- ArgParser::NoArgs => {- let (tool, lint_name, rest) = match &*p.path().0.segments {- [] => unreachable!(),- [lint_name] => (None, lint_name.ident.name, None),- [tool, lint_name] => (Some(tool.ident), lint_name.ident.name, None),- [tool, lint_name, rest @ ..] => {- let rest = rest- .iter()- .map(|s| s.ident.name)- .collect::<Vec<_>>()- .into();- (Some(tool.ident), lint_name.ident.name, Some(rest))- }- };+ match item {+ MetaItemOrLitParser::MetaItemParser(p) => {+ match p.args() {+ ArgParser::NoArgs => {+ let (tool, lint_name, rest) = match &*p.path().0.segments {+ [] => unreachable!(),+ [lint_name] => (None, lint_name.ident.name, None),+ [tool, lint_name] => {+ (Some(tool.ident), lint_name.ident.name, None)+ }+ [tool, lint_name, rest @ ..] => {+ let rest = rest+ .iter()+ .map(|s| s.ident.name)+ .collect::<Vec<_>>()+ .into();+ (Some(tool.ident), lint_name.ident.name, Some(rest))+ }+ }; - self.lints.push(LintCheck {- tool,- lint_name,- lint_span: p.span(),- kind,- attr_id,- reason,- attr_span,- rest,- })- }- // We're found a `reason = "reason"` but we're not the last element.- ArgParser::NameValue(nv) if p.path().word_is(sym::reason) => {- cx.emit_err(diagnostics::MalformedAttribute {- span: p.span(),- sub: diagnostics::MalformedAttributeSub::ReasonMustComeLast(- item.span(),- ),- });- nv.expect_string_literal(cx);- }- ArgParser::NameValue(_) | ArgParser::List(_) => {- cx.emit_err(diagnostics::MalformedAttribute {- span: p.span(),- sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(- item.span(),- ),- });+ self.lints.push(LintCheck {+ tool,+ lint_name,+ lint_span: p.span(),+ kind,+ attr_id,+ reason,+ attr_span,+ rest,+ })+ }+ // We've found a `reason = "reason"` but we're not the last element.+ ArgParser::NameValue(nv) if p.path().word_is(sym::reason) => {+ cx.emit_err(diagnostics::MalformedAttribute {+ span: p.span(),+ kind: diagnostics::MalformedAttributeKind::ReasonMustComeLast(+ item.span(),+ ),+ });+ nv.expect_string_literal(cx);+ }+ ArgParser::NameValue(_) | ArgParser::List(_) => {+ cx.emit_err(diagnostics::MalformedAttribute {+ span: p.span(),+ kind: diagnostics::MalformedAttributeKind::BadAttributeArgument(+ item.span(),+ ),+ });+ } } }- } else {- cx.emit_err(diagnostics::MalformedAttribute {- span: item.span(),- sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(item.span()),- });+ MetaItemOrLitParser::Lit(lit) => {+ cx.emit_err(diagnostics::BadAttributeLit {+ span: item.span(),+ help: if lit.kind.is_str() {+ diagnostics::BadAttributeLitHelp::StrLit {+ sym: lit.symbol,+ kind: kind.sym(),+ }+ } else {+ diagnostics::BadAttributeLitHelp::Other { kind: kind.sym() }+ },+ });+ } } } if parsers.is_empty() {compiler/rustc_attr_parsing/src/diagnostics.rs20 + / 2 −
@@ -2139,17 +2139,35 @@ pub(crate) struct MalformedAttribute { #[primary_span] pub span: Span, #[subdiagnostic]- pub sub: MalformedAttributeSub,+ pub kind: MalformedAttributeKind, } #[derive(Subdiagnostic)]-pub(crate) enum MalformedAttributeSub {+pub(crate) enum MalformedAttributeKind { #[label("bad attribute argument")] BadAttributeArgument(#[primary_span] Span), #[label("reason in lint attribute must come last")] ReasonMustComeLast(#[primary_span] Span), } +#[derive(Diagnostic)]+#[diag("expected a lint, found literal")]+pub(crate) struct BadAttributeLit {+ #[primary_span]+ #[label("not the name of a lint")]+ pub span: Span,+ #[subdiagnostic]+ pub help: BadAttributeLitHelp,+}++#[derive(Subdiagnostic)]+pub(crate) enum BadAttributeLitHelp {+ #[help("try using `#[{$kind}({$sym})]`")]+ StrLit { sym: Symbol, kind: Symbol },+ #[help("for example, write `#[{$kind}(dead_code)]` for `dead_code`")]+ Other { kind: Symbol },+}+ #[derive(Subdiagnostic)] pub(crate) enum UnusedNote { #[note("attribute `{$name}` with an empty list has no effect")]tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr4 + / 3 −
@@ -1,8 +1,10 @@-error[E0452]: malformed lint attribute input+error: expected a lint, found literal --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")]- | ^^^^^^^^^ bad attribute argument+ | ^^^^^^^^^ not the name of a lint+ |+ = help: try using `#[deny(literal)]` error: cannot find derive macro `Unresolved` in this scope --> $DIR/deduplicate-diagnostics.rs:6:10@@ -12,4 +14,3 @@ LL | #[derive(Unresolved)] error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0452`.tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr10 + / 7 −
@@ -1,8 +1,10 @@-error[E0452]: malformed lint attribute input+error: expected a lint, found literal --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")]- | ^^^^^^^^^ bad attribute argument+ | ^^^^^^^^^ not the name of a lint+ |+ = help: try using `#[deny(literal)]` error: cannot find derive macro `Unresolved` in this scope --> $DIR/deduplicate-diagnostics.rs:6:10@@ -18,22 +20,23 @@ LL | #[derive(Unresolved)] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input+error: expected a lint, found literal --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")]- | ^^^^^^^^^ bad attribute argument+ | ^^^^^^^^^ not the name of a lint |+ = help: try using `#[deny(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input+error: expected a lint, found literal --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")]- | ^^^^^^^^^ bad attribute argument+ | ^^^^^^^^^ not the name of a lint |+ = help: try using `#[deny(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0452`.tests/ui/diagnostic-flags/deduplicate-diagnostics.rs3 + / 3 −
@@ -7,7 +7,7 @@ //[duplicate]~| ERROR cannot find derive macro `Unresolved` in this scope struct S; -#[deny("literal")] //~ ERROR malformed lint attribute input- //[duplicate]~| ERROR malformed lint attribute input- //[duplicate]~| ERROR malformed lint attribute input+#[deny("literal")] //~ ERROR expected a lint, found literal+ //[duplicate]~| ERROR expected a lint, found literal+ //[duplicate]~| ERROR expected a lint, found literal fn main() {}tests/ui/lint/lint-attribute-literals.rsadded11 + / 0 −
@@ -0,0 +1,11 @@+// Checks that lint attributes with literals have an explanatory error++#[allow("literal")]+//~^ ERROR expected a lint, found literal+//~| ERROR expected a lint, found literal+//~| ERROR expected a lint, found literal+#[deny(1.0)]+//~^ ERROR expected a lint, found literal+//~| ERROR expected a lint, found literal+//~| ERROR expected a lint, found literal+fn main() {}tests/ui/lint/lint-attribute-literals.stderradded54 + / 0 −
@@ -0,0 +1,54 @@+error: expected a lint, found literal+ --> $DIR/lint-attribute-literals.rs:3:9+ |+LL | #[allow("literal")]+ | ^^^^^^^^^ not the name of a lint+ |+ = help: try using `#[allow(literal)]`++error: expected a lint, found literal+ --> $DIR/lint-attribute-literals.rs:7:8+ |+LL | #[deny(1.0)]+ | ^^^ not the name of a lint+ |+ = help: for example, write `#[deny(dead_code)]` for `dead_code`++error: expected a lint, found literal+ --> $DIR/lint-attribute-literals.rs:3:9+ |+LL | #[allow("literal")]+ | ^^^^^^^^^ not the name of a lint+ |+ = help: try using `#[allow(literal)]`+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`++error: expected a lint, found literal+ --> $DIR/lint-attribute-literals.rs:7:8+ |+LL | #[deny(1.0)]+ | ^^^ not the name of a lint+ |+ = help: for example, write `#[deny(dead_code)]` for `dead_code`+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`++error: expected a lint, found literal+ --> $DIR/lint-attribute-literals.rs:3:9+ |+LL | #[allow("literal")]+ | ^^^^^^^^^ not the name of a lint+ |+ = help: try using `#[allow(literal)]`+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`++error: expected a lint, found literal+ --> $DIR/lint-attribute-literals.rs:7:8+ |+LL | #[deny(1.0)]+ | ^^^ not the name of a lint+ |+ = help: for example, write `#[deny(dead_code)]` for `dead_code`+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`++error: aborting due to 6 previous errors+