Skip to content

Rewrite inline attribute parser to use new infrastructure and improve diagnostics for all parsed attributes #138165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use thin_vec::ThinVec;

use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
pub enum InlineAttr {
None,
Hint,
Expand Down Expand Up @@ -188,12 +188,14 @@ pub enum AttributeKind {
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },

/// Represents `#[inline]`.
Inline(InlineAttr, Span),

/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),

/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr(ThinVec<(ReprAttr, Span)>),

/// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
Stability {
stability: Stability,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_data_structures/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ pub struct AttributeLint<Id> {
#[derive(Clone, Debug, HashStable_Generic)]
pub enum AttributeLintKind {
UnusedDuplicate { this: Span, other: Span, warning: bool },
IllFormedAttributeInput { suggestions: Vec<String> },
}
14 changes: 5 additions & 9 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ attr_parsing_expects_feature_list =
attr_parsing_expects_features =
`{$name}` expects feature names

attr_parsing_incorrect_meta_item = expected a quoted string literal
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
[1] attribute must be of the form {$suggestions}
*[other] valid forms for the attribute are {$suggestions}
}

attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
Expand Down Expand Up @@ -81,9 +83,6 @@ attr_parsing_missing_note =
attr_parsing_missing_since =
missing 'since'

attr_parsing_multiple_item =
multiple '{$item}' items

attr_parsing_multiple_stability_levels =
multiple stability levels

Expand Down Expand Up @@ -122,10 +121,6 @@ attr_parsing_unsupported_literal_cfg_boolean =
literal in `cfg` predicate value must be a boolean
attr_parsing_unsupported_literal_cfg_string =
literal in `cfg` predicate value must be a string
attr_parsing_unsupported_literal_deprecated_kv_pair =
item in `deprecated` must be a key/value pair
attr_parsing_unsupported_literal_deprecated_string =
literal in `deprecated` value must be a string
attr_parsing_unsupported_literal_generic =
unsupported literal
attr_parsing_unsupported_literal_suggestion =
Expand All @@ -136,6 +131,7 @@ attr_parsing_unused_duplicate =
.suggestion = remove this attribute
.note = attribute also specified here
.warn = {-passes_previously_accepted}

attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::iter;

use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};

use super::{CombineAttributeParser, ConvertFn};
Expand All @@ -13,6 +14,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");

fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
Expand All @@ -29,6 +31,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");

fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
Expand Down
53 changes: 25 additions & 28 deletions compiler/rustc_attr_parsing/src/attributes/confusables.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::template;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;

Expand All @@ -13,37 +14,33 @@ pub(crate) struct ConfusablesParser {
}

impl<S: Stage> AttributeParser<S> for ConfusablesParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
let Some(list) = args.list() else {
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
// NOTE: currently subsequent attributes are silently ignored using
// tcx.get_attr().
return;
};

if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}

for param in list.mixed() {
let span = param.span();

let Some(lit) = param.lit() else {
cx.emit_err(session_diagnostics::IncorrectMetaItem {
span,
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
lo: span.shrink_to_lo(),
hi: span.shrink_to_hi(),
}),
});
continue;
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::rustc_confusables],
template!(List: r#""name1", "name2", ..."#),
|this, cx, args| {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return;
};

this.confusables.push(lit.symbol);
}
if list.is_empty() {
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
}

for param in list.mixed() {
let span = param.span();

let Some(lit) = param.lit().and_then(|i| i.value_str()) else {
cx.expected_string_literal(span, param.lit());
continue;
};

this.confusables.push(lit);
}

this.first_span.get_or_insert(cx.attr_span);
})];
this.first_span.get_or_insert(cx.attr_span);
},
)];

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.confusables.is_empty() {
Expand Down
111 changes: 56 additions & 55 deletions compiler/rustc_attr_parsing/src/attributes/deprecation.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};

use super::util::parse_version;
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics;
use crate::session_diagnostics::UnsupportedLiteralReason;

pub(crate) struct DeprecationParser;

Expand All @@ -18,25 +18,18 @@ fn get<S: Stage>(
item: &Option<Symbol>,
) -> Option<Symbol> {
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() });
cx.duplicate_key(param_span, name);
return None;
}
if let Some(v) = arg.name_value() {
if let Some(value_str) = v.value_as_str() {
Some(value_str)
} else {
let lit = v.value_as_lit();
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: v.value_span,
reason: UnsupportedLiteralReason::DeprecatedString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: cx.sess().source_map().start_point(lit.span),
});
cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
None
}
} else {
// FIXME(jdonszelmann): suggestion?
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
cx.expected_name_value(param_span, Some(name));
None
}
}
Expand All @@ -45,6 +38,11 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
const PATH: &[Symbol] = &[sym::deprecated];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(
Word,
List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
NameValueStr: "reason"
);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let features = cx.features();
Expand All @@ -55,57 +53,60 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {

let is_rustc = features.staged_api();

if let Some(value) = args.name_value()
&& let Some(value_str) = value.value_as_str()
{
note = Some(value_str)
} else if let Some(list) = args.list() {
for param in list.mixed() {
let param_span = param.span();
let Some(param) = param.meta_item() else {
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param_span,
reason: UnsupportedLiteralReason::DeprecatedKvPair,
is_bytestr: false,
start_point_span: cx.sess().source_map().start_point(param_span),
});
return None;
};
match args {
ArgParser::NoArgs => {
// ok
}
ArgParser::List(list) => {
for param in list.mixed() {
let Some(param) = param.meta_item() else {
cx.unexpected_literal(param.span());
return None;
};

let ident_name = param.path().word_sym();
let ident_name = param.path().word_sym();

match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param_span, param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param_span, param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param_span,
is_nightly: cx.sess().is_nightly_build(),
details: (),
});
match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param.span(), param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(cx, name, param.span(), param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
span: param.span(),
is_nightly: cx.sess().is_nightly_build(),
details: (),
});
}

suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?);
}
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path().to_string(),
expected: if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
});
return None;
suggestion =
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
}
_ => {
cx.unknown_key(
param.span(),
param.path().to_string(),
if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {
&["since", "note"]
},
);
return None;
}
}
}
}
ArgParser::NameValue(v) => {
let Some(value) = v.value_as_str() else {
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};
note = Some(value);
}
}

let since = if let Some(since) = since {
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_attr_parsing/src/attributes/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
return None;
};

match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) {
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::always) => {
Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
}
Expand Down Expand Up @@ -63,7 +63,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");

fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let reason = match args {
ArgParser::NoArgs => None,
ArgParser::List(list) => {
Expand All @@ -73,15 +73,15 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
};

let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
cx.expected_string_literal(l.span());
cx.expected_string_literal(l.span(), l.lit());
return None;
};

Some(reason)
}
ArgParser::NameValue(v) => {
let Some(reason) = v.value_as_str() else {
cx.expected_string_literal(v.value_span);
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};

Expand Down
Loading
Loading