Skip to content

Rewrite the new attribute argument parser #144689

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 5 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
3 changes: 1 addition & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3458,7 +3458,6 @@ dependencies = [
"rustc_feature",
"rustc_fluent_macro",
"rustc_macros",
"rustc_parse",
"rustc_session",
"rustc_span",
"rustc_target",
Expand Down Expand Up @@ -3489,6 +3488,7 @@ dependencies = [
"rustc_hir",
"rustc_lexer",
"rustc_macros",
"rustc_parse",
"rustc_session",
"rustc_span",
"thin-vec",
Expand Down Expand Up @@ -4344,7 +4344,6 @@ dependencies = [
"rustc-literal-escaper",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_ast_passes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_macros = { path = "../rustc_macros" }
rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ use rustc_ast::ptr::P;
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
use rustc_ast::*;
use rustc_ast_pretty::pprust::{self, State};
use rustc_attr_parsing::validate_attr;
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::DiagCtxtHandle;
use rustc_feature::Features;
use rustc_parse::validate_attr;
use rustc_session::Session;
use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,22 @@ attr_parsing_unused_multiple =

-attr_parsing_previously_accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

attr_parsing_meta_bad_delim = wrong meta list delimiters
attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`

attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`

attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
.label = this is not an unsafe attribute
.suggestion = remove the `unsafe(...)`
.note = extraneous unsafe is not allowed in attributes

attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal

attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
79 changes: 56 additions & 23 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
Expand Down Expand Up @@ -213,7 +214,6 @@ mod private {
#[allow(private_interfaces)]
pub trait Stage: Sized + 'static + Sealed {
type Id: Copy;
const SHOULD_EMIT_LINTS: bool;

fn parsers() -> &'static group_type!(Self);

Expand All @@ -222,13 +222,14 @@ pub trait Stage: Sized + 'static + Sealed {
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed;

fn should_emit(&self) -> ShouldEmit;
}

// allow because it's a sealed trait
#[allow(private_interfaces)]
impl Stage for Early {
type Id = NodeId;
const SHOULD_EMIT_LINTS: bool = false;

fn parsers() -> &'static group_type!(Self) {
&early::ATTRIBUTE_PARSERS
Expand All @@ -244,13 +245,16 @@ impl Stage for Early {
sess.dcx().create_err(diag).delay_as_bug()
}
}

fn should_emit(&self) -> ShouldEmit {
self.emit_errors
}
}

// allow because it's a sealed trait
#[allow(private_interfaces)]
impl Stage for Late {
type Id = HirId;
const SHOULD_EMIT_LINTS: bool = true;

fn parsers() -> &'static group_type!(Self) {
&late::ATTRIBUTE_PARSERS
Expand All @@ -262,6 +266,10 @@ impl Stage for Late {
) -> ErrorGuaranteed {
tcx.dcx().emit_err(diag)
}

fn should_emit(&self) -> ShouldEmit {
ShouldEmit::ErrorsAndLints
}
}

/// used when parsing attributes for miscellaneous things *before* ast lowering
Expand Down Expand Up @@ -300,7 +308,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
if !S::SHOULD_EMIT_LINTS {
if !self.stage.should_emit().should_emit() {
return;
}
let id = self.target_id;
Expand Down Expand Up @@ -657,14 +665,31 @@ impl<'sess> AttributeParser<'sess, Early> {
target_node_id: NodeId,
features: Option<&'sess Features>,
) -> Option<Attribute> {
let mut p = Self {
features,
tools: Vec::new(),
parse_only: Some(sym),
let mut parsed = Self::parse_limited_all(
sess,
stage: Early { emit_errors: ShouldEmit::Nothing },
};
let mut parsed = p.parse_attribute_list(
attrs,
Some(sym),
target_span,
target_node_id,
features,
ShouldEmit::Nothing,
);
assert!(parsed.len() <= 1);
parsed.pop()
}

pub fn parse_limited_all(
sess: &'sess Session,
attrs: &[ast::Attribute],
parse_only: Option<Symbol>,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
) -> Vec<Attribute> {
let mut p =
Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
p.parse_attribute_list(
attrs,
target_span,
target_node_id,
Expand All @@ -673,10 +698,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|_lint| {
panic!("can't emit lints here for now (nothing uses this atm)");
},
);
assert!(parsed.len() <= 1);

parsed.pop()
)
}

pub fn parse_single<T>(
Expand All @@ -686,9 +708,9 @@ impl<'sess> AttributeParser<'sess, Early> {
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
template: &AttributeTemplate,
) -> T {
) -> Option<T> {
let mut parser = Self {
features,
tools: Vec::new(),
Expand All @@ -699,7 +721,9 @@ impl<'sess> AttributeParser<'sess, Early> {
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
panic!("parse_single called on a doc attr")
};
let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
let parts =
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
let path = meta_parser.path();
let args = meta_parser.args();
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
Expand Down Expand Up @@ -804,14 +828,23 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// }))
// }
ast::AttrKind::Normal(n) => {
attr_paths.push(PathParser::Ast(&n.item.path));
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));

let parser = MetaItemParser::from_attr(n, self.dcx());
let path = parser.path();
let args = parser.args();
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
let parts =
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();

if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
let Some(parser) = MetaItemParser::from_attr(
n,
&parts,
&self.sess.psess,
self.stage.should_emit(),
) else {
continue;
};
let path = parser.path();
let args = parser.args();

for (template, accept) in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub(crate) mod context;
mod lints;
pub mod parser;
mod session_diagnostics;
pub mod validate_attr;

pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg_old::*;
Expand Down
Loading
Loading