Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn parse_unstable<S: Stage>(

for param in list.mixed() {
let param_span = param.span();
if let Some(ident) = param.meta_item().and_then(|i| i.path().word()) {
if let Some(ident) = param.meta_item_no_args().and_then(|i| i.path().word()) {
res.push(ident.name);
} else {
cx.emit_err(session_diagnostics::ExpectsFeatures {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;

let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
let res = match single.meta_item_no_args().and_then(|i| i.path().word().map(|i| i.name)) {
Some(sym::size) => OptimizeAttr::Size,
Some(sym::speed) => OptimizeAttr::Speed,
Some(sym::none) => OptimizeAttr::DoNotOptimize,
Expand Down Expand Up @@ -80,7 +80,7 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
let mut fail_incorrect_argument =
|span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);

let Some(arg) = arg.meta_item() else {
let Some(arg) = arg.meta_item_no_args() else {
fail_incorrect_argument(arg.span());
return None;
};
Expand Down Expand Up @@ -375,7 +375,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
return;
};

match l.meta_item().and_then(|i| i.path().word_sym()) {
match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
Some(sym::compiler) => {
if !cx.features().used_with_arg() {
feature_err(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl<S: Stage> AttributeParser<S> for OnConstParser {
|this, cx, args| {
if !cx.features().diagnostic_on_const() {
// `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs
args.ignore_args();
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl OnMoveParser {
) {
if !cx.features().diagnostic_on_move() {
// `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs
args.ignore_args();
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl OnUnknownParser {
) {
if !cx.features().diagnostic_on_unknown() {
// `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs
args.ignore_args();
return;
}
let span = cx.attr_span;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ impl DocParser {

// FIXME: convert list into a Vec of `AttributeKind` because current code is awful.
for attr in list.mixed() {
// Arguments of `attr` are checked via the span, so can be safely ignored
attr.ignore_args();
self.attribute.test_attrs.push(attr.span());
}
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ impl<S: Stage> SingleAttributeParser<S> for RustcDummyParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really

fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option<AttributeKind> {
fn convert(_: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
args.ignore_args();
Some(AttributeKind::RustcDummy)
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
return None;
};

match l.meta_item().and_then(|i| i.path().word_sym()) {
match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
Some(sym::always) => {
Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl<S: Stage> SingleAttributeParser<S> for InstructionSetParser {
const POSSIBLE_ARM_SYMBOLS: &[Symbol] = &[sym::a32, sym::t32];
let maybe_meta_item = cx.single_element_list(args, cx.attr_span)?;

let Some(meta_item) = maybe_meta_item.meta_item() else {
let Some(meta_item) = maybe_meta_item.meta_item_no_args() else {
cx.adcx().expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS);
return None;
};
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
};
match l.meta_item().and_then(|i| i.path().word_sym()) {
match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
Some(sym::local_inner_macros) => true,
_ => {
cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl<S: Stage> CombineAttributeParser<S> for RustcDumpLayoutParser {

let mut result = Vec::new();
for item in items.mixed() {
let Some(arg) = item.meta_item() else {
let Some(arg) = item.meta_item_no_args() else {
cx.adcx().expected_not_literal(item.span());
continue;
};
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcMustImplementOneOfParser {

let mut errored = false;
for argument in inputs {
let Some(meta) = argument.meta_item() else {
let Some(meta) = argument.meta_item_no_args() else {
cx.adcx().expected_identifier(argument.span());
return None;
};
Expand Down Expand Up @@ -945,7 +945,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcIfThisChangedParser {
cx.adcx().expected_single_argument(attr_span, list.len());
return None;
};
let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
let Some(ident) = item.meta_item_no_args().and_then(|item| item.ident()) else {
cx.adcx().expected_identifier(item.span());
return None;
};
Expand Down Expand Up @@ -1003,7 +1003,7 @@ impl<S: Stage> CombineAttributeParser<S> for RustcThenThisWouldNeedParser {
cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
}
let item = cx.single_element_list(args, cx.attr_span)?;
let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
let Some(ident) = item.meta_item_no_args().and_then(|item| item.ident()) else {
cx.adcx().expected_identifier(item.span());
return None;
};
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
let mut fail_incorrect_argument =
|span| cx.adcx().expected_specific_argument(span, &[sym::assert_eq, sym::debug]);

let Some(arg) = arg.meta_item() else {
let Some(arg) = arg.meta_item_no_args() else {
fail_incorrect_argument(args.span);
return None;
};
Expand Down Expand Up @@ -210,7 +210,7 @@ impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;

let Some(meta) = single.meta_item() else {
let Some(meta) = single.meta_item_no_args() else {
cx.adcx().expected_not_literal(single.span());
return None;
};
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
kind: AttributeLintKind,
span: M,
) {
#[cfg(debug_assertions)]
self.has_lint_been_emitted.store(true, std::sync::atomic::Ordering::Relaxed);
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true }
Expand Down Expand Up @@ -568,6 +570,11 @@ pub struct SharedContext<'p, 'sess, S: Stage> {
/// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S`
/// is `Late` and is the ID of the syntactical component this attribute was applied to.
pub(crate) emit_lint: &'p mut dyn FnMut(LintId, MultiSpan, AttributeLintKind),

/// This atomic bool keeps track of whether any lint has been emitted.
/// This is used for the arguments-used check.
#[cfg(debug_assertions)]
pub(crate) has_lint_been_emitted: std::sync::atomic::AtomicBool,
}

/// Context given to every attribute parser during finalization.
Expand Down
41 changes: 40 additions & 1 deletion compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ impl<'sess> AttributeParser<'sess, Early> {
target_span,
target,
emit_lint: &mut emit_lint,
#[cfg(debug_assertions)]
has_lint_been_emitted: std::sync::atomic::AtomicBool::new(false),
},
attr_span,
inner_span,
Expand Down Expand Up @@ -372,6 +374,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
target_span,
target,
emit_lint: &mut emit_lint,
#[cfg(debug_assertions)]
has_lint_been_emitted: std::sync::atomic::AtomicBool::new(false),
},
attr_span,
inner_span: lower_span(n.item.span()),
Expand All @@ -384,6 +388,14 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);

#[cfg(debug_assertions)]
if !cx
.shared
.has_lint_been_emitted
.load(std::sync::atomic::Ordering::Relaxed)
{
cx.shared.cx.check_args_used(&attr, &args)
}
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
Expand Down Expand Up @@ -423,7 +435,14 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
for f in &finalizers {
if let Some(attr) = f(&mut FinalizeContext {
shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
shared: SharedContext {
cx: self,
target_span,
target,
emit_lint: &mut emit_lint,
#[cfg(debug_assertions)]
has_lint_been_emitted: std::sync::atomic::AtomicBool::new(false),
},
all_attrs: &attr_paths,
}) {
attributes.push(Attribute::Parsed(attr));
Expand All @@ -439,6 +458,26 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
attributes
}

#[cfg(debug_assertions)]
/// Checks whether all `ArgParser`s were observed by an attribute parser at least once
/// This check exists because otherwise it is too easy to accidentally ignore the arguments of an attribute
fn check_args_used(&self, attr: &ast::Attribute, args: &ArgParser) {
if let ArgParser::List(items) = args {
for item in items.mixed() {
if let crate::parser::MetaItemOrLitParser::MetaItemParser(item) = item {
if !item.are_args_checked() {
self.dcx().span_delayed_bug(
item.span(),
"attribute args were not properly checked",
);
return;
}
self.check_args_used(attr, item.args());
}
}
}
}

/// Returns whether there is a parser for an attribute with this name
pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
/// The list of attributes that are parsed attributes,
Expand Down
65 changes: 60 additions & 5 deletions compiler/rustc_attr_parsing/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<P: Borrow<Path>> Display for PathParser<P> {
}
}

#[derive(Clone, Debug)]
#[derive(Debug)]
#[must_use]
pub enum ArgParser {
NoArgs,
Expand Down Expand Up @@ -209,13 +209,26 @@ impl ArgParser {
Self::NameValue(args) => Err(args.args_span()),
}
}

/// Explicitly ignore the arguments, disarming the arguments-used check
pub fn ignore_args(&self) {
#[cfg(debug_assertions)]
match self {
ArgParser::List(list) => {
for item in list.mixed() {
item.ignore_args();
}
}
_ => {}
}
}
}

/// Inside lists, values could be either literals, or more deeply nested meta items.
/// This enum represents that.
///
/// Choose which one you want using the provided methods.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum MetaItemOrLitParser {
MetaItemParser(MetaItemParser),
Lit(MetaItemLit),
Expand Down Expand Up @@ -253,6 +266,26 @@ impl MetaItemOrLitParser {
MetaItemOrLitParser::Lit(_) => None,
}
}

/// Returns some if this `MetaItemOrLitParser` is a `MetaItem` with no arguments
pub fn meta_item_no_args(&self) -> Option<&MetaItemParser> {
let meta_item = self.meta_item()?;
match meta_item.args().no_args() {
Ok(_) => Some(meta_item),
Err(_) => None,
}
}

/// Explicitly ignore the arguments, disarming the arguments-used check
pub fn ignore_args(&self) {
#[cfg(debug_assertions)]
match self {
MetaItemOrLitParser::MetaItemParser(meta_item) => {
meta_item.ignore_args();
}
MetaItemOrLitParser::Lit(_) => {}
}
}
}

/// Utility that deconstructs a MetaItem into usable parts.
Expand All @@ -268,10 +301,14 @@ impl MetaItemOrLitParser {
/// `= value` part
///
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
#[derive(Clone)]
pub struct MetaItemParser {
path: OwnedPathParser,
args: ArgParser,

/// Whether the `args` of this meta item have been looked at.
/// This is tracked because if the arguments of a `MetaItemParser` are ignored, this is probably a mistake
#[cfg(debug_assertions)]
args_checked: std::sync::atomic::AtomicBool,
}

impl Debug for MetaItemParser {
Expand Down Expand Up @@ -308,6 +345,8 @@ impl MetaItemParser {

/// Gets just the args parser, without caring about the path.
pub fn args(&self) -> &ArgParser {
#[cfg(debug_assertions)]
self.args_checked.store(true, std::sync::atomic::Ordering::Relaxed);
&self.args
}

Expand All @@ -320,6 +359,17 @@ impl MetaItemParser {
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
self.path().word_is(sym).then(|| self.args())
}

/// Explicitly ignore the arguments, disarming the arguments-used check
pub fn ignore_args(&self) {
#[cfg(debug_assertions)]
self.args().ignore_args();
}

#[cfg(debug_assertions)]
pub fn are_args_checked(&self) -> bool {
self.args_checked.load(std::sync::atomic::Ordering::Relaxed)
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -530,7 +580,12 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
ArgParser::NoArgs
};

Ok(MetaItemParser { path: PathParser(path), args })
Ok(MetaItemParser {
path: PathParser(path),
args,
#[cfg(debug_assertions)]
args_checked: std::sync::atomic::AtomicBool::new(false),
})
}

fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
Expand Down Expand Up @@ -655,7 +710,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
}
}

#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct MetaItemListParser {
sub_parsers: ThinVec<MetaItemOrLitParser>,
pub span: Span,
Expand Down
Loading
Loading