rust-lang/rust · #164486

improve diagnostic for literals in lint attributes

annieversary · merged Oct 18, 20267 files · 165 + / 66 −
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+