Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ pub fn parse_cfg_attr(
}) {
Ok(r) => return Some(r),
Err(e) => {
let suggestions = CFG_ATTR_TEMPLATE.suggestions(cfg_attr.style, sym::cfg_attr);
let suggestions =
CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr);
e.with_span_suggestions(
cfg_attr.span,
"must be of the form",
Expand Down Expand Up @@ -356,7 +357,7 @@ pub fn parse_cfg_attr(
template: CFG_ATTR_TEMPLATE,
attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
reason,
attr_style: cfg_attr.style,
suggestions: CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr),
});
}
}
Expand Down Expand Up @@ -388,6 +389,7 @@ fn parse_cfg_attr_internal<'a>(
let cfg_predicate = AttributeParser::parse_single_args(
sess,
attribute.span,
attribute.get_normal_item().span(),
attribute.style,
AttrPath {
segments: attribute
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_attr_parsing/src/attributes/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "inline");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "link");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
Expand Down
19 changes: 6 additions & 13 deletions compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use rustc_ast::AttrStyle;
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::MacroUseArgs;

Expand Down Expand Up @@ -102,7 +101,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
Expand Down Expand Up @@ -149,19 +148,14 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
]);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let suggestions = || {
<Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(AttrStyle::Inner, "macro_export")
};
let local_inner_macros = match args {
ArgParser::NoArgs => false,
ArgParser::List(list) => {
let Some(l) = list.single() else {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments {
suggestions: suggestions(),
},
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
return None;
Expand All @@ -170,10 +164,9 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
Some(sym::local_inner_macros) => true,
_ => {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments {
suggestions: suggestions(),
},
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
return None;
Expand All @@ -182,7 +175,7 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
}
ArgParser::NameValue(_) => {
let span = cx.attr_span;
let suggestions = suggestions();
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_attr_parsing/src/attributes/must_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
Some(value_str)
}
ArgParser::List(_) => {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "must_use");
let suggestions = cx.suggestions();
cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NoArgs => None,
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
Expand All @@ -32,8 +31,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
.suggestions(cx.attr_style, "ignore");
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
Expand Down
43 changes: 29 additions & 14 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,16 @@ pub struct Late;
/// Gives [`AttributeParser`]s enough information to create errors, for example.
pub struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) shared: SharedContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed

/// The outer span of the attribute currently being parsed
/// #[attribute(...)]
/// ^^^^^^^^^^^^^^^^^ outer span
/// For attributes in `cfg_attr`, the outer span and inner spans are equal.
pub(crate) attr_span: Span,
/// The inner span of the attribute currently being parsed
/// #[attribute(...)]
/// ^^^^^^^^^^^^^^ inner span
pub(crate) inner_span: Span,

/// Whether it is an inner or outer attribute
pub(crate) attr_style: AttrStyle,
Expand Down Expand Up @@ -427,7 +435,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -438,7 +446,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -449,7 +457,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -460,7 +468,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -472,7 +480,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -485,7 +493,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -497,7 +505,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -510,7 +518,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -521,7 +529,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -532,7 +540,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,
suggestions: self.suggestions(),
})
}

Expand All @@ -552,7 +560,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: false,
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}

Expand All @@ -573,7 +581,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: true,
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}

Expand All @@ -593,7 +601,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: true,
list: false,
},
attr_style: self.attr_style,
suggestions: self.suggestions(),
})
}

Expand All @@ -605,6 +613,13 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span,
);
}

pub(crate) fn suggestions(&self) -> Vec<String> {
// If the outer and inner spans are equal, we are parsing an attribute from `cfg_attr`,
// So don't display an attribute style in the suggestions
let style = (self.attr_span != self.inner_span).then_some(self.attr_style);
self.template.suggestions(style, &self.attr_path)
}
}

impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ impl<'sess> AttributeParser<'sess, Early> {
Self::parse_single_args(
sess,
attr.span,
normal_attr.item.span(),
attr.style,
path.get_attribute_path(),
target_span,
Expand All @@ -159,6 +160,7 @@ impl<'sess> AttributeParser<'sess, Early> {
pub fn parse_single_args<T, I>(
sess: &'sess Session,
attr_span: Span,
inner_span: Span,
attr_style: AttrStyle,
attr_path: AttrPath,
target_span: Span,
Expand Down Expand Up @@ -186,6 +188,7 @@ impl<'sess> AttributeParser<'sess, Early> {
},
},
attr_span,
inner_span,
attr_style,
template,
attr_path,
Expand Down Expand Up @@ -305,6 +308,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
inner_span: lower_span(attr.get_normal_item().span()),
attr_style: attr.style,
template: &accept.template,
attr_path: path.get_attribute_path(),
Expand Down
9 changes: 4 additions & 5 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::num::IntErrorKind;

use rustc_ast::{self as ast, AttrStyle, Path};
use rustc_ast::{self as ast, Path};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
Expand Down Expand Up @@ -613,10 +613,10 @@ pub(crate) enum AttributeParseErrorReason<'a> {
pub(crate) struct AttributeParseError<'a> {
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<'a>,
pub(crate) suggestions: Vec<String>,
}

impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
Expand Down Expand Up @@ -752,16 +752,15 @@ 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(self.attr_style, &name);

diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {
if self.suggestions.len() == 1 {
"must be of the form"
} else {
"try changing it to one of the following valid forms of the attribute"
},
suggestions,
self.suggestions,
Applicability::HasPlaceholders,
);

Expand Down
21 changes: 13 additions & 8 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,29 @@ pub struct AttributeTemplate {
}

impl AttributeTemplate {
pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec<String> {
pub fn suggestions(
&self,
style: Option<AttrStyle>,
name: impl std::fmt::Display,
) -> Vec<String> {
let mut suggestions = vec![];
let inner = match style {
AttrStyle::Outer => "",
AttrStyle::Inner => "!",
let (start, end) = match style {
Some(AttrStyle::Outer) => ("#[", "]"),
Some(AttrStyle::Inner) => ("#![", "]"),
None => ("", ""),
};
if self.word {
suggestions.push(format!("#{inner}[{name}]"));
suggestions.push(format!("{start}{name}{end}"));
}
if let Some(descr) = self.list {
for descr in descr {
suggestions.push(format!("#{inner}[{name}({descr})]"));
suggestions.push(format!("{start}{name}({descr}){end}"));
}
}
suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
suggestions.extend(self.one_of.iter().map(|&word| format!("{start}{name}({word}){end}")));
if let Some(descr) = self.name_value_str {
for descr in descr {
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
suggestions.push(format!("{start}{name} = \"{descr}\"{end}"));
}
}
suggestions.sort();
Expand Down
Loading
Loading