take attr style into account in attr diagnostics

This commit is contained in:
Jana Dönszelmann 2025-08-11 11:46:30 +02:00
parent 1ae7c49072
commit 70e26c1b7b
No known key found for this signature in database
13 changed files with 53 additions and 37 deletions

View File

@ -62,8 +62,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
ArgParser::NameValue(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "inline");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View File

@ -107,7 +107,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(

View File

@ -35,8 +35,8 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
Some(value_str)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "must_use");
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(

View File

@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(false, "ignore");
.suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
@ -40,8 +40,8 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
let suggestions =
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;

View File

@ -5,7 +5,7 @@ use std::sync::LazyLock;
use itertools::Itertools;
use private::Sealed;
use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
use rustc_ast::{self as ast, AttrStyle, LitKind, MetaItemLit, NodeId};
use rustc_errors::{DiagCtxtHandle, Diagnostic};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
@ -313,6 +313,7 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
pub(crate) attr_style: AttrStyle,
/// The expected structure of the attribute.
///
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
@ -394,6 +395,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
attr_style: self.attr_style,
})
}
@ -404,6 +406,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
attr_style: self.attr_style,
})
}
@ -414,6 +417,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
attr_style: self.attr_style,
})
}
@ -424,6 +428,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNoArgs,
attr_style: self.attr_style,
})
}
@ -435,6 +440,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
attr_style: self.attr_style,
})
}
@ -447,6 +453,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
attr_style: self.attr_style,
})
}
@ -458,6 +465,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
attr_style: self.attr_style,
})
}
@ -470,6 +478,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
attr_style: self.attr_style,
})
}
@ -480,6 +489,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
attr_style: self.attr_style,
})
}
@ -490,6 +500,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
attr_style: self.attr_style,
})
}
@ -508,6 +519,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: false,
},
attr_style: self.attr_style,
})
}
@ -526,6 +538,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: true,
},
attr_style: self.attr_style,
})
}
@ -544,6 +557,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: true,
list: false,
},
attr_style: self.attr_style,
})
}
@ -802,6 +816,7 @@ impl<'sess> AttributeParser<'sess, Early> {
},
},
attr_span: attr.span,
attr_style: attr.style,
template,
attr_path: path.get_attribute_path(),
};
@ -912,6 +927,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
attr_style: attr.style,
template: &accept.template,
attr_path: path.get_attribute_path(),
};

View File

@ -1,6 +1,6 @@
use std::num::IntErrorKind;
use rustc_ast as ast;
use rustc_ast::{self as ast, AttrStyle};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@ -579,6 +579,7 @@ pub(crate) enum AttributeParseErrorReason {
pub(crate) struct AttributeParseError {
pub(crate) span: Span,
pub(crate) attr_span: Span,
pub(crate) attr_style: AttrStyle,
pub(crate) template: AttributeTemplate,
pub(crate) attribute: AttrPath,
pub(crate) reason: AttributeParseErrorReason,
@ -717,7 +718,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
if let Some(link) = self.template.docs {
diag.note(format!("for more information, visit <{link}>"));
}
let suggestions = self.template.suggestions(false, &name);
let suggestions = self.template.suggestions(self.attr_style, &name);
diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {

View File

@ -6,6 +6,7 @@ use AttributeDuplicates::*;
use AttributeGate::*;
use AttributeType::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::AttrStyle;
use rustc_hir::attrs::EncodeCrossCrate;
use rustc_span::edition::Edition;
use rustc_span::{Symbol, sym};
@ -132,9 +133,12 @@ pub struct AttributeTemplate {
}
impl AttributeTemplate {
pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> {
pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec<String> {
let mut suggestions = vec![];
let inner = if inner { "!" } else { "" };
let inner = match style {
AttrStyle::Outer => "",
AttrStyle::Inner => "!",
};
if self.word {
suggestions.push(format!("#{inner}[{name}]"));
}

View File

@ -1,7 +1,7 @@
// NOTE: this used to panic in debug builds (by a sanity assertion)
// and not emit any lint on release builds. See https://github.com/rust-lang/rust/issues/142891.
#![inline = ""]
//~^ ERROR: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` [ill_formed_attribute_input]
//~^ ERROR: valid forms for the attribute are `#![inline(always)]`, `#![inline(never)]`, and `#![inline]` [ill_formed_attribute_input]
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
//~| ERROR attribute cannot be used on

View File

@ -6,7 +6,7 @@ LL | #![inline = ""]
|
= help: `#[inline]` can only be applied to functions
error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
error: valid forms for the attribute are `#![inline(always)]`, `#![inline(never)]`, and `#![inline]`
--> $DIR/lint_on_root.rs:3:1
|
LL | #![inline = ""]
@ -19,7 +19,7 @@ LL | #![inline = ""]
error: aborting due to 2 previous errors
Future incompatibility report: Future breakage diagnostic:
error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
error: valid forms for the attribute are `#![inline(always)]`, `#![inline(never)]`, and `#![inline]`
--> $DIR/lint_on_root.rs:3:1
|
LL | #![inline = ""]

View File

@ -7,18 +7,14 @@ LL | #![repr]
= note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
help: try changing it to one of the following valid forms of the attribute
|
LL - #![repr]
LL + #[repr(<integer type>)]
|
LL - #![repr]
LL + #[repr(C)]
|
LL - #![repr]
LL + #[repr(Rust)]
|
LL - #![repr]
LL + #[repr(align(...))]
|
LL | #![repr(<integer type>)]
| ++++++++++++++++
LL | #![repr(C)]
| +++
LL | #![repr(Rust)]
| ++++++
LL | #![repr(align(...))]
| ++++++++++++
= and 2 other candidates
error[E0589]: invalid `repr(align)` attribute: not a power of two

View File

@ -22,10 +22,10 @@ LL | #![coverage = "off"]
help: try changing it to one of the following valid forms of the attribute
|
LL - #![coverage = "off"]
LL + #[coverage(off)]
LL + #![coverage(off)]
|
LL - #![coverage = "off"]
LL + #[coverage(on)]
LL + #![coverage(on)]
|
error[E0539]: malformed `coverage` attribute input

View File

@ -19,12 +19,10 @@ LL | #![coverage]
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #![coverage]
LL + #[coverage(off)]
|
LL - #![coverage]
LL + #[coverage(on)]
|
LL | #![coverage(off)]
| +++++
LL | #![coverage(on)]
| ++++
error[E0539]: malformed `coverage` attribute input
--> $DIR/word-only.rs:21:1

View File

@ -11,7 +11,7 @@ LL | #![path = foo!()]
| ^^^^^^^^^^------^
| | |
| | expected a string literal here
| help: must be of the form: `#[path = "file"]`
| help: must be of the form: `#![path = "file"]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute>