Auto merge of #144689 - JonathanBrouwer:share_parse_path, r=jdonszelmann

Rewrite the new attribute argument parser

Fixes https://github.com/rust-lang/rust/issues/143940

This rewrites the parser, should improve performance and maintainability.
This can be reviewed commit by commit
This commit is contained in:
bors 2025-08-22 10:27:12 +00:00
commit f5703d5dd3
51 changed files with 670 additions and 524 deletions

View file

@ -3459,7 +3459,6 @@ dependencies = [
"rustc_feature",
"rustc_fluent_macro",
"rustc_macros",
"rustc_parse",
"rustc_session",
"rustc_span",
"rustc_target",
@ -3490,6 +3489,7 @@ dependencies = [
"rustc_hir",
"rustc_lexer",
"rustc_macros",
"rustc_parse",
"rustc_session",
"rustc_span",
"thin-vec",
@ -4355,7 +4355,6 @@ dependencies = [
"rustc-literal-escaper",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",

View file

@ -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" }

View file

@ -25,10 +25,10 @@ use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
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,

View file

@ -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"

View file

@ -170,3 +170,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.)

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_errors::DiagCtxtHandle;
@ -49,27 +51,44 @@ 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::Crate, // Does not matter, we're not going to emit errors anyways
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: Target,
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,
Target::Crate, // Does not matter, we're not going to emit errors anyways
target,
OmitDoc::Skip,
std::convert::identity,
|_lint| {
panic!("can't emit lints here for now (nothing uses this atm)");
// FIXME: Can't emit lints here for now
// This branch can be hit when an attribute produces a warning during early parsing (such as attributes on macro calls)
},
);
assert!(parsed.len() <= 1);
parsed.pop()
)
}
pub fn parse_single<T>(
@ -79,9 +98,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(),
@ -92,7 +111,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 {
@ -199,14 +220,22 @@ 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 path_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().accepters.get(path_parts.as_slice()) {
if let Some(accepts) = S::parsers().accepters.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 accept in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {

View file

@ -102,6 +102,7 @@ pub mod parser;
mod lints;
mod session_diagnostics;
mod target_checking;
pub mod validate_attr;
pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg_old::*;

View file

@ -3,45 +3,30 @@
//!
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
use std::borrow::Cow;
use std::fmt::{Debug, Display};
use std::iter::Peekable;
use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
use rustc_ast::token::{self, Delimiter, MetaVarKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast_pretty::pprust;
use rustc_errors::DiagCtxtHandle;
use rustc_errors::PResult;
use rustc_hir::{self as hir, AttrPath};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use rustc_parse::exp;
use rustc_parse::parser::{Parser, PathStyle, token_descr};
use rustc_session::errors::report_lit_error;
use rustc_session::parse::ParseSess;
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
use thin_vec::ThinVec;
pub struct SegmentIterator<'a> {
offset: usize,
path: &'a PathParser<'a>,
}
impl<'a> Iterator for SegmentIterator<'a> {
type Item = &'a Ident;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.path.len() {
return None;
}
let res = match self.path {
PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
};
self.offset += 1;
Some(res)
}
}
use crate::ShouldEmit;
use crate::session_diagnostics::{
InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim,
MetaBadDelimSugg, SuffixedLiteralInAttribute,
};
#[derive(Clone, Debug)]
pub enum PathParser<'a> {
Ast(&'a Path),
Attr(AttrPath),
}
pub struct PathParser<'a>(pub Cow<'a, Path>);
impl<'a> PathParser<'a> {
pub fn get_attribute_path(&self) -> hir::AttrPath {
@ -52,21 +37,15 @@ impl<'a> PathParser<'a> {
}
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
SegmentIterator { offset: 0, path: self }
self.0.segments.iter().map(|seg| &seg.ident)
}
pub fn span(&self) -> Span {
match self {
PathParser::Ast(path) => path.span,
PathParser::Attr(attr_path) => attr_path.span,
}
self.0.span
}
pub fn len(&self) -> usize {
match self {
PathParser::Ast(path) => path.segments.len(),
PathParser::Attr(attr_path) => attr_path.segments.len(),
}
self.0.segments.len()
}
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
@ -99,10 +78,7 @@ impl<'a> PathParser<'a> {
impl Display for PathParser<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
}
write!(f, "{}", pprust::path_to_string(&self.0))
}
}
@ -123,21 +99,39 @@ impl<'a> ArgParser<'a> {
}
}
pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
match value {
pub fn from_attr_args<'sess>(
value: &'a AttrArgs,
parts: &[Symbol],
psess: &'sess ParseSess,
should_emit: ShouldEmit,
) -> Option<Self> {
Some(match value {
AttrArgs::Empty => Self::NoArgs,
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
Self::List(MetaItemListParser::new(args, dcx))
}
AttrArgs::Delimited(args) => {
Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
// The arguments of rustc_dummy are not validated if the arguments are delimited
if parts == &[sym::rustc_dummy] {
return Some(ArgParser::List(MetaItemListParser {
sub_parsers: ThinVec::new(),
span: args.dspan.entire(),
}));
}
if args.delim != Delimiter::Parenthesis {
psess.dcx().emit_err(MetaBadDelim {
span: args.dspan.entire(),
sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
});
return None;
}
Self::List(MetaItemListParser::new(args, psess, should_emit)?)
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
value: expr_to_lit(dcx, &expr, *eq_span),
value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
value_span: expr.span,
}),
}
})
}
/// Asserts that this MetaItem is a list
@ -249,11 +243,16 @@ impl<'a> Debug for MetaItemParser<'a> {
impl<'a> MetaItemParser<'a> {
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
/// [`ast::Attribute`](rustc_ast::Attribute)
pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self {
Self {
path: PathParser::Ast(&attr.item.path),
args: ArgParser::from_attr_args(&attr.item.args, dcx),
}
pub fn from_attr<'sess>(
attr: &'a NormalAttr,
parts: &[Symbol],
psess: &'sess ParseSess,
should_emit: ShouldEmit,
) -> Option<Self> {
Some(Self {
path: PathParser(Cow::Borrowed(&attr.item.path)),
args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
})
}
}
@ -318,215 +317,232 @@ impl NameValueParser {
}
}
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
// In valid code the value always ends up as a single literal. Otherwise, a dummy
// literal suffices because the error is handled elsewhere.
if let ExprKind::Lit(token_lit) = expr.kind
&& let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
{
lit
fn expr_to_lit(
psess: &ParseSess,
expr: &Expr,
span: Span,
should_emit: ShouldEmit,
) -> Option<MetaItemLit> {
if let ExprKind::Lit(token_lit) = expr.kind {
let res = MetaItemLit::from_token_lit(token_lit, expr.span);
match res {
Ok(lit) => {
if token_lit.suffix.is_some() {
psess
.dcx()
.create_err(SuffixedLiteralInAttribute { span: lit.span })
.emit_unless_delay(!should_emit.should_emit());
None
} else {
if should_emit.should_emit() && !lit.kind.is_unsuffixed() {
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
psess.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
}
Some(lit)
}
}
Err(err) => {
let guar = report_lit_error(psess, err, token_lit, expr.span);
let lit = MetaItemLit {
symbol: token_lit.symbol,
suffix: token_lit.suffix,
kind: LitKind::Err(guar),
span: expr.span,
};
Some(lit)
}
}
} else {
let guar = dcx.span_delayed_bug(
span,
"expr in place where literal is expected (builtin attr parsing)",
);
MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
// Example cases:
// - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
// - `#[foo = include_str!("nonexistent-file.rs")]`:
// results in `ast::ExprKind::Err`. In that case we delay
// the error because an earlier error will have already
// been reported.
let msg = "attribute value must be a literal";
let mut err = psess.dcx().struct_span_err(span, msg);
if let ExprKind::Err(_) = expr.kind {
err.downgrade_to_delayed_bug();
}
err.emit_unless_delay(!should_emit.should_emit());
None
}
}
struct MetaItemListParserContext<'a, 'sess> {
// the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
inside_delimiters: Peekable<TokenStreamIter<'a>>,
dcx: DiagCtxtHandle<'sess>,
parser: &'a mut Parser<'sess>,
should_emit: ShouldEmit,
}
impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
fn done(&mut self) -> bool {
self.inside_delimiters.peek().is_none()
}
fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
let uninterpolated_span = self.parser.token_uninterpolated_span();
let Some(token_lit) = self.parser.eat_token_lit() else {
return self.parser.handle_missing_lit(Parser::mk_meta_item_lit_char);
};
fn next_path(&mut self) -> Option<AttrPath> {
// FIXME: Share code with `parse_path`.
let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
match tt.as_deref()? {
&TokenTree::Token(
Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
_,
) => {
// here we have either an ident or pathsep `::`.
let mut segments = if let &token::Ident(name, _) = kind {
// when we lookahead another pathsep, more path's coming
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
self.inside_delimiters.peek()
{
self.inside_delimiters.next();
vec![Ident::new(name, span)]
} else {
// else we have a single identifier path, that's all
return Some(AttrPath {
segments: vec![Ident::new(name, span)].into_boxed_slice(),
span,
});
}
} else {
// if `::` is all we get, we just got a path root
vec![Ident::new(kw::PathRoot, span)]
};
// one segment accepted. accept n more
loop {
// another ident?
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
self.inside_delimiters
.next()
.map(|tt| TokenTree::uninterpolate(tt))
.as_deref()
{
segments.push(Ident::new(name, span));
} else {
return None;
}
// stop unless we see another `::`
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
self.inside_delimiters.peek()
{
self.inside_delimiters.next();
} else {
break;
}
}
let span = span.with_hi(segments.last().unwrap().span.hi());
Some(AttrPath { segments: segments.into_boxed_slice(), span })
let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
Ok(lit) => lit,
Err(err) => {
let guar =
report_lit_error(&self.parser.psess, err, token_lit, uninterpolated_span);
// Pack possible quotes and prefixes from the original literal into
// the error literal's symbol so they can be pretty-printed faithfully.
let suffixless_lit = token::Lit::new(token_lit.kind, token_lit.symbol, None);
let symbol = Symbol::intern(&suffixless_lit.to_string());
let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix);
MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap()
}
TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None,
_ => {
// malformed attributes can get here. We can't crash, but somewhere else should've
// already warned for this.
None
}
}
}
};
fn value(&mut self) -> Option<MetaItemLit> {
match self.inside_delimiters.next() {
Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
MetaItemListParserContext {
inside_delimiters: inner_tokens.iter().peekable(),
dcx: self.dcx,
}
.value()
}
Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
_ => None,
}
}
/// parses one element on the inside of a list attribute like `#[my_attr( <insides> )]`
///
/// parses a path followed be either:
/// 1. nothing (a word attr)
/// 2. a parenthesized list
/// 3. an equals sign and a literal (name-value)
///
/// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
/// where no path is given before the literal
///
/// Some exceptions too for interpolated attributes which are already pre-processed
fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
// a list element is either a literal
if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
&& let Some(lit) = MetaItemLit::from_token(token)
{
self.inside_delimiters.next();
return Some(MetaItemOrLitParser::Lit(lit));
} else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
self.inside_delimiters.peek()
{
self.inside_delimiters.next();
return MetaItemListParserContext {
inside_delimiters: inner_tokens.iter().peekable(),
dcx: self.dcx,
}
.next();
if self.should_emit.should_emit() && !lit.kind.is_unsuffixed() {
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
self.parser.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
}
// or a path.
let path = self.next_path()?;
// Paths can be followed by:
// - `(more meta items)` (another list)
// - `= lit` (a name-value)
// - nothing
Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
self.inside_delimiters.next();
MetaItemParser {
path: PathParser::Attr(path),
args: ArgParser::List(MetaItemListParser::new_tts(
inner_tokens.iter(),
dspan.entire(),
self.dcx,
)),
}
}
Some(TokenTree::Delimited(_, ..)) => {
self.inside_delimiters.next();
// self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
return None;
}
Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
self.inside_delimiters.next();
let value = self.value()?;
MetaItemParser {
path: PathParser::Attr(path),
args: ArgParser::NameValue(NameValueParser {
eq_span: *span,
value_span: value.span,
value,
}),
}
}
_ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
}))
Ok(lit)
}
fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
let mut sub_parsers = Vec::new();
while !self.done() {
let Some(n) = self.next() else {
continue;
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
return if has_meta_form {
let attr_item = self
.parser
.eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
MetaItemListParserContext { parser: this, should_emit: self.should_emit }
.parse_attr_item()
})
.unwrap();
Ok(attr_item)
} else {
self.parser.unexpected_any()
};
sub_parsers.push(n);
}
match self.inside_delimiters.peek() {
None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
self.inside_delimiters.next();
}
Some(_) => {}
let path = self.parser.parse_path(PathStyle::Mod)?;
// Check style of arguments that this meta item has
let args = if self.parser.check(exp!(OpenParen)) {
let start = self.parser.token.span;
let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
MetaItemListParserContext { parser, should_emit: self.should_emit }
.parse_meta_item_inner()
})?;
let end = self.parser.prev_token.span;
ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
} else if self.parser.eat(exp!(Eq)) {
let eq_span = self.parser.prev_token.span;
let value = self.parse_unsuffixed_meta_item_lit()?;
ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
} else {
ArgParser::NoArgs
};
Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
}
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
match self.parse_unsuffixed_meta_item_lit() {
Ok(lit) => return Ok(MetaItemOrLitParser::Lit(lit)),
Err(err) => err.cancel(), // we provide a better error below
}
match self.parse_attr_item() {
Ok(mi) => return Ok(MetaItemOrLitParser::MetaItemParser(mi)),
Err(err) => err.cancel(), // we provide a better error below
}
let mut err = InvalidMetaItem {
span: self.parser.token.span,
descr: token_descr(&self.parser.token),
quote_ident_sugg: None,
remove_neg_sugg: None,
};
// Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
// when macro metavariables are involved.
if self.parser.prev_token == token::Eq
&& let token::Ident(..) = self.parser.token.kind
{
let before = self.parser.token.span.shrink_to_lo();
while let token::Ident(..) = self.parser.token.kind {
self.parser.bump();
}
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
before,
after: self.parser.prev_token.span.shrink_to_hi(),
});
}
if self.parser.token == token::Minus
&& self
.parser
.look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. }))
{
err.remove_neg_sugg =
Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
self.parser.bump();
self.parser.bump();
}
Err(self.parser.dcx().create_err(err))
}
fn parse(
tokens: TokenStream,
psess: &'sess ParseSess,
span: Span,
should_emit: ShouldEmit,
) -> PResult<'sess, MetaItemListParser<'static>> {
let mut parser = Parser::new(psess, tokens, None);
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
// Presumably, the majority of the time there will only be one attr.
let mut sub_parsers = ThinVec::with_capacity(1);
while this.parser.token != token::Eof {
sub_parsers.push(this.parse_meta_item_inner()?);
if !this.parser.eat(exp!(Comma)) {
break;
}
}
MetaItemListParser { sub_parsers, span }
if parser.token != token::Eof {
parser.unexpected()?;
}
Ok(MetaItemListParser { sub_parsers, span })
}
}
#[derive(Debug, Clone)]
pub struct MetaItemListParser<'a> {
sub_parsers: Vec<MetaItemOrLitParser<'a>>,
sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
pub span: Span,
}
impl<'a> MetaItemListParser<'a> {
fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
}
fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self {
MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
fn new<'sess>(
delim: &'a DelimArgs,
psess: &'sess ParseSess,
should_emit: ShouldEmit,
) -> Option<Self> {
match MetaItemListParserContext::parse(
delim.tokens.clone(),
psess,
delim.dspan.entire(),
should_emit,
) {
Ok(s) => Some(s),
Err(e) => {
e.emit_unless_delay(!should_emit.should_emit());
None
}
}
}
/// Lets you pick and choose as what you want to parse each element in the list

View file

@ -1,6 +1,6 @@
use std::num::IntErrorKind;
use rustc_ast::{self as ast, AttrStyle};
use rustc_ast::{self as ast, AttrStyle, Path};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@ -737,3 +737,92 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
diag
}
}
#[derive(Diagnostic)]
#[diag(attr_parsing_invalid_attr_unsafe)]
#[note]
pub(crate) struct InvalidAttrUnsafe {
#[primary_span]
#[label]
pub span: Span,
pub name: Path,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafe {
#[primary_span]
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
attr_parsing_unsafe_attr_outside_unsafe_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
#[suggestion_part(code = "unsafe(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_meta_bad_delim)]
pub(crate) struct MetaBadDelim {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: MetaBadDelimSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
attr_parsing_meta_bad_delim_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct MetaBadDelimSugg {
#[suggestion_part(code = "(")]
pub open: Span,
#[suggestion_part(code = ")")]
pub close: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_invalid_meta_item)]
pub(crate) struct InvalidMetaItem {
#[primary_span]
pub span: Span,
pub descr: String,
#[subdiagnostic]
pub quote_ident_sugg: Option<InvalidMetaItemQuoteIdentSugg>,
#[subdiagnostic]
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")]
pub(crate) struct InvalidMetaItemQuoteIdentSugg {
#[suggestion_part(code = "\"")]
pub before: Span,
#[suggestion_part(code = "\"")]
pub after: Span,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")]
pub(crate) struct InvalidMetaItemRemoveNegSugg {
#[suggestion_part(code = "")]
pub negative_sign: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_suffixed_literal_in_attribute)]
#[help]
pub(crate) struct SuffixedLiteralInAttribute {
#[primary_span]
pub span: Span,
}

View file

@ -8,16 +8,16 @@ use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
Path, Safety,
};
use rustc_attr_parsing::{AttributeParser, Late};
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
use rustc_parse::parse_in;
use rustc_session::errors::report_lit_error;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE};
use rustc_session::parse::ParseSess;
use rustc_span::{Span, Symbol, sym};
use crate::{errors, parse_in};
use crate::{AttributeParser, Late, session_diagnostics as errors};
pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
@ -33,7 +33,10 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
// Check input tokens for built-in and key-value attributes.
match builtin_attr_info {
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
Some(BuiltinAttribute { name, template, .. }) => {
if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
return;
}
match parse_meta(psess, attr) {
// Don't check safety again, we just did that
Ok(meta) => {
@ -133,16 +136,6 @@ fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
});
}
pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
if let Delimiter::Parenthesis = delim {
return;
}
psess.dcx().emit_err(errors::CfgAttrBadDelim {
span: span.entire(),
sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
});
}
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
let is_one_allowed_subword = |items: &[MetaItemInner]| match items {
@ -269,9 +262,6 @@ pub fn check_builtin_meta_item(
) {
if !is_attr_template_compatible(&template, &meta.kind) {
// attrs with new parsers are locally validated so excluded here
if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
return;
}
emit_malformed_attribute(psess, style, meta.span, name, template);
}

View file

@ -1,9 +1,9 @@
//! Implementation of the `#[cfg_accessible(path)]` attribute macro.
use rustc_ast as ast;
use rustc_attr_parsing::validate_attr;
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
use rustc_feature::AttributeTemplate;
use rustc_parse::validate_attr;
use rustc_span::{Span, sym};
use crate::errors;

View file

@ -1,10 +1,10 @@
use rustc_ast as ast;
use rustc_ast::{GenericParamKind, ItemKind, MetaItemInner, MetaItemKind, StmtKind};
use rustc_attr_parsing::validate_attr;
use rustc_expand::base::{
Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
};
use rustc_feature::AttributeTemplate;
use rustc_parse::validate_attr;
use rustc_session::Session;
use rustc_span::{ErrorGuaranteed, Ident, Span, sym};

View file

@ -1,12 +1,13 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{self as ast, AttrStyle, Attribute, MetaItem, attr, token};
use rustc_attr_parsing::validate_attr;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt};
use rustc_expand::expand::AstFragment;
use rustc_feature::AttributeTemplate;
use rustc_lint_defs::BuiltinLintDiag;
use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES;
use rustc_parse::{exp, parser, validate_attr};
use rustc_parse::{exp, parser};
use rustc_session::errors::report_lit_error;
use rustc_span::{BytePos, Span, Symbol};

View file

@ -11,8 +11,10 @@ use rustc_ast::{
NodeId, NormalAttr,
};
use rustc_attr_parsing as attr;
use rustc_attr_parsing::validate_attr::deny_builtin_meta_unsafety;
use rustc_attr_parsing::{
AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr,
validate_attr,
};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_feature::{
@ -20,8 +22,6 @@ use rustc_feature::{
REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES,
};
use rustc_lint_defs::BuiltinLintDiag;
use rustc_parse::validate_attr;
use rustc_parse::validate_attr::deny_builtin_meta_unsafety;
use rustc_session::Session;
use rustc_session::parse::feature_err;
use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym};
@ -415,16 +415,6 @@ impl<'a> StripUnconfigured<'a> {
node: NodeId,
emit_errors: ShouldEmit,
) -> EvalConfigResult {
// We need to run this to do basic validation of the attribute, such as that lits are valid, etc
// FIXME(jdonszelmann) this should not be necessary in the future
match validate_attr::parse_meta(&self.sess.psess, attr) {
Ok(_) => {}
Err(err) => {
err.emit();
return EvalConfigResult::True;
}
}
// Unsafety check needs to be done explicitly here because this attribute will be removed before the normal check
deny_builtin_meta_unsafety(
self.sess.dcx(),

View file

@ -1,28 +1,28 @@
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::{iter, mem};
use std::{iter, mem, slice};
use rustc_ast::mut_visit::*;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list};
use rustc_ast::{
self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, DUMMY_NODE_ID,
ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner,
MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token,
self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, CRATE_NODE_ID,
DUMMY_NODE_ID, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle,
MetaItemInner, MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token,
};
use rustc_ast_pretty::pprust;
use rustc_attr_parsing::{EvalConfigResult, ShouldEmit};
use rustc_attr_parsing::{AttributeParser, EvalConfigResult, ShouldEmit, validate_attr};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::PResult;
use rustc_feature::Features;
use rustc_hir::Target;
use rustc_hir::def::MacroKinds;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
token_descr,
};
use rustc_parse::validate_attr;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
use rustc_session::parse::feature_err;
@ -2159,6 +2159,16 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
attr,
self.cx.current_expansion.lint_node_id,
);
AttributeParser::parse_limited_all(
self.cx.sess,
slice::from_ref(attr),
None,
Target::MacroCall,
call.span(),
CRATE_NODE_ID,
Some(self.cx.ecfg.features),
ShouldEmit::ErrorsAndLints,
);
let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
span = Some(current_span);

View file

@ -2,8 +2,9 @@ use std::iter::once;
use std::path::{self, Path, PathBuf};
use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans};
use rustc_attr_parsing::validate_attr;
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal, validate_attr};
use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal};
use rustc_session::Session;
use rustc_session::parse::ParseSess;
use rustc_span::{Ident, Span, sym};

View file

@ -6,6 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock};
use std::{env, fs, iter};
use rustc_ast as ast;
use rustc_attr_parsing::validate_attr;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::jobserver::Proxy;
use rustc_data_structures::steal::Steal;
@ -25,9 +26,7 @@ use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepsType;
use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt};
use rustc_middle::util::Providers;
use rustc_parse::{
new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal, validate_attr,
};
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_passes::{abi_test, input_stats, layout_test};
use rustc_resolve::{Resolver, ResolverOutputs};
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};

View file

@ -5,12 +5,12 @@ use std::sync::{Arc, OnceLock};
use std::{env, thread};
use rustc_ast as ast;
use rustc_attr_parsing::validate_attr;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::jobserver::Proxy;
use rustc_data_structures::sync;
use rustc_metadata::{DylibError, load_symbol_from_dylib};
use rustc_middle::ty::CurrentGcx;
use rustc_parse::validate_attr;
use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
use rustc_session::output::{CRATE_TYPES, categorize_crate_type};

View file

@ -9,7 +9,6 @@ bitflags = "2.4.1"
rustc-literal-escaper = "0.0.5"
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }

View file

@ -436,11 +436,6 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment
.label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item}
.sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style
parse_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
parse_invalid_block_macro_segment = cannot use a `block` macro fragment here
.label = the `block` fragment is within this context
.suggestion = wrap this in another block
@ -601,7 +596,6 @@ parse_maybe_report_ambiguous_plus =
ambiguous `+` in a type
.suggestion = use parentheses to disambiguate
parse_meta_bad_delim = wrong meta list delimiters
parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}`
@ -990,10 +984,6 @@ parse_unmatched_angle_brackets = {$num_extra_brackets ->
*[other] remove extra angle brackets
}
parse_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
parse_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped
.label = {parse_unskipped_whitespace}

View file

@ -3345,15 +3345,6 @@ pub(crate) struct KwBadCase<'a> {
pub kw: &'a str,
}
#[derive(Diagnostic)]
#[diag(parse_meta_bad_delim)]
pub(crate) struct MetaBadDelim {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: MetaBadDelimSugg,
}
#[derive(Diagnostic)]
#[diag(parse_cfg_attr_bad_delim)]
pub(crate) struct CfgAttrBadDelim {
@ -3493,38 +3484,6 @@ pub(crate) struct DotDotRangeAttribute {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_invalid_attr_unsafe)]
#[note]
pub(crate) struct InvalidAttrUnsafe {
#[primary_span]
#[label]
pub span: Span,
pub name: Path,
}
#[derive(Diagnostic)]
#[diag(parse_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafe {
#[primary_span]
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
parse_unsafe_attr_outside_unsafe_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
#[suggestion_part(code = "unsafe(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag(parse_binder_before_modifiers)]
pub(crate) struct BinderBeforeModifiers {

View file

@ -16,7 +16,7 @@ use std::str::Utf8Error;
use std::sync::Arc;
use rustc_ast as ast;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{AttrItem, Attribute, MetaItemInner, token};
use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize};
@ -31,8 +31,9 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
#[macro_use]
pub mod parser;
use parser::Parser;
use rustc_ast::token::Delimiter;
pub mod lexer;
pub mod validate_attr;
mod errors;
@ -235,7 +236,7 @@ pub fn parse_cfg_attr(
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
crate::validate_attr::check_cfg_attr_bad_delim(psess, dspan, delim);
check_cfg_attr_bad_delim(psess, dspan, delim);
match parse_in(psess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
Ok(r) => return Some(r),
Err(e) => {
@ -254,3 +255,13 @@ pub fn parse_cfg_attr(
}
None
}
fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
if let Delimiter::Parenthesis = delim {
return;
}
psess.dcx().emit_err(errors::CfgAttrBadDelim {
span: span.entire(),
sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
});
}

View file

@ -399,7 +399,7 @@ impl<'a> Parser<'a> {
}
/// Matches `COMMASEP(meta_item_inner)`.
pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::MetaItemInner>> {
pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::MetaItemInner>> {
// Presumably, the majority of the time there will only be one attr.
let mut nmis = ThinVec::with_capacity(1);
while self.token != token::Eof {

View file

@ -2077,7 +2077,7 @@ impl<'a> Parser<'a> {
(token::Lit { symbol: name, suffix: None, kind: token::Char }, span)
}
fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit {
pub fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit {
ast::MetaItemLit {
symbol: name,
suffix: None,
@ -2086,7 +2086,7 @@ impl<'a> Parser<'a> {
}
}
fn handle_missing_lit<L>(
pub fn handle_missing_lit<L>(
&mut self,
mk_lit_char: impl FnOnce(Symbol, Span) -> L,
) -> PResult<'a, L> {
@ -2156,7 +2156,7 @@ impl<'a> Parser<'a> {
/// Keep this in sync with `Token::can_begin_literal_maybe_minus` and
/// `Lit::from_token` (excluding unary negation).
fn eat_token_lit(&mut self) -> Option<token::Lit> {
pub fn eat_token_lit(&mut self) -> Option<token::Lit> {
let check_expr = |expr: Box<Expr>| {
if let ast::ExprKind::Lit(token_lit) = expr.kind {
Some(token_lit)

View file

@ -24,7 +24,7 @@ pub use diagnostics::AttemptLocalParseRecovery;
pub(crate) use expr::ForbiddenLetReason;
pub(crate) use item::{FnContext, FnParseMode};
pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
use path::PathStyle;
pub use path::PathStyle;
use rustc_ast::token::{
self, IdentIsRaw, InvisibleOrigin, MetaVarKind, NtExprKind, NtPatKind, Token, TokenKind,
};
@ -285,7 +285,7 @@ pub enum FollowedByType {
}
#[derive(Copy, Clone, Debug)]
enum Trailing {
pub enum Trailing {
No,
Yes,
}
@ -494,7 +494,7 @@ impl<'a> Parser<'a> {
/// This method will automatically add `tok` to `expected_token_types` if `tok` is not
/// encountered.
#[inline]
fn check(&mut self, exp: ExpTokenPair<'_>) -> bool {
pub fn check(&mut self, exp: ExpTokenPair<'_>) -> bool {
let is_present = self.token == *exp.tok;
if !is_present {
self.expected_token_types.insert(exp.token_type);
@ -633,7 +633,7 @@ impl<'a> Parser<'a> {
}
/// Consume a sequence produced by a metavar expansion, if present.
fn eat_metavar_seq<T>(
pub fn eat_metavar_seq<T>(
&mut self,
mv_kind: MetaVarKind,
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> {
/// Parses a comma-separated sequence delimited by parentheses (e.g. `(x, y)`).
/// The function `f` must consume tokens until reaching the next separator or
/// closing bracket.
fn parse_paren_comma_seq<T>(
pub fn parse_paren_comma_seq<T>(
&mut self,
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, (ThinVec<T>, Trailing)> {
@ -1355,7 +1355,8 @@ impl<'a> Parser<'a> {
AttrArgs::Delimited(args)
} else if self.eat(exp!(Eq)) {
let eq_span = self.prev_token.span;
AttrArgs::Eq { eq_span, expr: self.parse_expr_force_collect()? }
let expr = self.parse_expr_force_collect()?;
AttrArgs::Eq { eq_span, expr }
} else {
AttrArgs::Empty
})

View file

@ -26,7 +26,7 @@ use crate::parser::{
/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
pub(super) enum PathStyle {
pub enum PathStyle {
/// In some contexts, notably in expressions, paths with generic arguments are ambiguous
/// with something else. For example, in expressions `segment < ....` can be interpreted
/// as a comparison and `segment ( ....` can be interpreted as a function call.
@ -150,7 +150,7 @@ impl<'a> Parser<'a> {
true
}
pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> {
pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> {
self.parse_path_inner(style, None)
}

View file

@ -94,7 +94,6 @@ check!(reg_f32, f32, freg, "mov.s");
// CHECK: #APP
// CHECK: mov.s $f0, $f0
// CHECK: #NO_APP
#[no_mangle]
check_reg!(f0_f32, f32, "$f0", "mov.s");
// CHECK-LABEL: reg_f32_64:
@ -107,21 +106,18 @@ check!(reg_f32_64, f32, freg, "mov.d");
// CHECK: #APP
// CHECK: mov.d $f0, $f0
// CHECK: #NO_APP
#[no_mangle]
check_reg!(f0_f32_64, f32, "$f0", "mov.d");
// CHECK-LABEL: reg_f64:
// CHECK: #APP
// CHECK: mov.d $f{{[0-9]+}}, $f{{[0-9]+}}
// CHECK: #NO_APP
#[no_mangle]
check!(reg_f64, f64, freg, "mov.d");
// CHECK-LABEL: f0_f64:
// CHECK: #APP
// CHECK: mov.d $f0, $f0
// CHECK: #NO_APP
#[no_mangle]
check_reg!(f0_f64, f64, "$f0", "mov.d");
// CHECK-LABEL: reg_ptr:

View file

@ -1,9 +1,3 @@
error: attribute value must be a literal
--> $DIR/key-value-expansion.rs:21:6
|
LL | bug!((column!()));
| ^^^^^^^^^^^
error: attribute value must be a literal
--> $DIR/key-value-expansion.rs:27:14
|
@ -26,5 +20,11 @@ LL | some_macro!(u8);
|
= note: this error originates in the macro `some_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: attribute value must be a literal
--> $DIR/key-value-expansion.rs:21:6
|
LL | bug!((column!()));
| ^^^^^^^^^^^
error: aborting due to 3 previous errors

View file

@ -26,7 +26,7 @@ fn f3() {}
#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on functions
fn f4() {}
#[rustc_align(-1)] //~ ERROR expected unsuffixed literal, found `-`
#[rustc_align(-1)] //~ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
fn f5() {}
#[rustc_align(3)] //~ ERROR invalid alignment value: not a power of two

View file

@ -1,17 +1,3 @@
error: expected unsuffixed literal, found `-`
--> $DIR/malformed-fn-align.rs:29:15
|
LL | #[rustc_align(-1)]
| ^
error: suffixed literals are not allowed in attributes
--> $DIR/malformed-fn-align.rs:35:15
|
LL | #[rustc_align(4usize)]
| ^^^^^^
|
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
error[E0539]: malformed `rustc_align` attribute input
--> $DIR/malformed-fn-align.rs:10:5
|
@ -51,12 +37,32 @@ error[E0589]: invalid alignment value: not a power of two
LL | #[rustc_align(0)]
| ^
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
--> $DIR/malformed-fn-align.rs:29:15
|
LL | #[rustc_align(-1)]
| ^
|
help: negative numbers are not literals, try removing the `-` sign
|
LL - #[rustc_align(-1)]
LL + #[rustc_align(1)]
|
error[E0589]: invalid alignment value: not a power of two
--> $DIR/malformed-fn-align.rs:32:15
|
LL | #[rustc_align(3)]
| ^
error: suffixed literals are not allowed in attributes
--> $DIR/malformed-fn-align.rs:35:15
|
LL | #[rustc_align(4usize)]
| ^^^^^^
|
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
error[E0589]: invalid alignment value: not an unsuffixed integer
--> $DIR/malformed-fn-align.rs:35:15
|

View file

@ -5,7 +5,7 @@
macro_rules! pass_nonterminal {
($n:expr) => {
#[repr(align($n))]
//~^ ERROR expected unsuffixed literal, found `expr` metavariable
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable
struct S;
};
}
@ -15,6 +15,5 @@ macro_rules! n {
}
pass_nonterminal!(n!());
//~^ ERROR incorrect `repr(align)` attribute format: `align` expects a literal integer as argument [E0693]
fn main() {}

View file

@ -1,4 +1,4 @@
error: expected unsuffixed literal, found `expr` metavariable
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable
--> $DIR/nonterminal-expansion.rs:7:22
|
LL | #[repr(align($n))]
@ -9,12 +9,5 @@ LL | pass_nonterminal!(n!());
|
= note: this error originates in the macro `pass_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument
--> $DIR/nonterminal-expansion.rs:17:19
|
LL | pass_nonterminal!(n!());
| ^
error: aborting due to 1 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0693`.

View file

@ -28,17 +28,6 @@ LL | #[unsafe(proc_macro_derive(Foo))]
|
= note: extraneous unsafe is not allowed in attributes
error: expected identifier, found keyword `unsafe`
--> $DIR/proc-unsafe-attributes.rs:12:21
|
LL | #[proc_macro_derive(unsafe(Foo))]
| ^^^^^^ expected identifier, found keyword
|
help: escape `unsafe` to use it as an identifier
|
LL | #[proc_macro_derive(r#unsafe(Foo))]
| ++
error: `proc_macro_attribute` is not an unsafe attribute
--> $DIR/proc-unsafe-attributes.rs:18:3
|
@ -114,6 +103,17 @@ LL | #[unsafe(allow(unsafe(dead_code)))]
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: expected identifier, found keyword `unsafe`
--> $DIR/proc-unsafe-attributes.rs:12:21
|
LL | #[proc_macro_derive(unsafe(Foo))]
| ^^^^^^ expected identifier, found keyword
|
help: escape `unsafe` to use it as an identifier
|
LL | #[proc_macro_derive(r#unsafe(Foo))]
| ++
error[E0565]: malformed `proc_macro_derive` attribute input
--> $DIR/proc-unsafe-attributes.rs:12:1
|

View file

@ -43,7 +43,7 @@ struct S9;
macro_rules! generate_s10 {
($expr: expr) => {
#[cfg(feature = $expr)]
//~^ ERROR expected unsuffixed literal, found `expr` metavariable
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable
struct S10;
}
}

View file

@ -81,7 +81,7 @@ LL | #[cfg(a = b"hi")]
|
= note: expected a normal string literal, not a byte string literal
error: expected unsuffixed literal, found `expr` metavariable
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable
--> $DIR/cfg-attr-syntax-validation.rs:45:25
|
LL | #[cfg(feature = $expr)]

View file

@ -1,15 +1,3 @@
error: expected identifier, found `,`
--> $DIR/bad-syntax.rs:44:12
|
LL | #[coverage(,off)]
| ^ expected identifier
|
help: remove this comma
|
LL - #[coverage(,off)]
LL + #[coverage(off)]
|
error: multiple `coverage` attributes
--> $DIR/bad-syntax.rs:9:1
|
@ -162,6 +150,18 @@ LL - #[coverage(off, bogus)]
LL + #[coverage(on)]
|
error: expected identifier, found `,`
--> $DIR/bad-syntax.rs:44:12
|
LL | #[coverage(,off)]
| ^ expected identifier
|
help: remove this comma
|
LL - #[coverage(,off)]
LL + #[coverage(off)]
|
error: aborting due to 11 previous errors
Some errors have detailed explanations: E0539, E0805.

View file

@ -3,9 +3,9 @@
// was a well-formed `MetaItem`.
fn main() {
foo() //~ WARNING use of deprecated function `foo`
foo()
}
#[deprecated(note = test)]
//~^ ERROR expected unsuffixed literal, found `test`
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `test`
fn foo() {}

View file

@ -1,4 +1,4 @@
error: expected unsuffixed literal, found `test`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `test`
--> $DIR/issue-66340-deprecated-attr-non-meta-grammar.rs:9:21
|
LL | #[deprecated(note = test)]
@ -9,13 +9,5 @@ help: surround the identifier with quotation marks to make it into a string lite
LL | #[deprecated(note = "test")]
| + +
warning: use of deprecated function `foo`
--> $DIR/issue-66340-deprecated-attr-non-meta-grammar.rs:6:5
|
LL | foo()
| ^^^
|
= note: `#[warn(deprecated)]` on by default
error: aborting due to 1 previous error; 1 warning emitted
error: aborting due to 1 previous error

View file

@ -4,7 +4,9 @@ macro_rules! bar (
macro_rules! foo (
() => (
#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps
#[allow_internal_unstable()]
//~^ ERROR allow_internal_unstable side-steps
//~| ERROR `#[allow_internal_unstable]` attribute cannot be used on macro calls
bar!();
);
);

View file

@ -1,8 +1,8 @@
error[E0658]: allow_internal_unstable side-steps feature gating and stability checks
--> $DIR/issue-32782.rs:7:9
|
LL | #[allow_internal_unstable]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[allow_internal_unstable()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | foo!();
| ------ in this macro invocation
@ -11,6 +11,18 @@ LL | foo!();
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error
error: `#[allow_internal_unstable]` attribute cannot be used on macro calls
--> $DIR/issue-32782.rs:7:9
|
LL | #[allow_internal_unstable()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | foo!();
| ------ in this macro invocation
|
= help: `#[allow_internal_unstable]` can be applied to macro defs and functions
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -1,5 +1,6 @@
//@ check-pass
#![feature(rustc_attrs)]
#![warn(unused)]
macro_rules! foo {
@ -7,16 +8,16 @@ macro_rules! foo {
}
fn main() {
#[inline] foo!(); //~ WARN unused attribute `inline`
#[rustc_dummy] foo!(); //~ WARN unused attribute `rustc_dummy`
// This does nothing, since `#[allow(warnings)]` is itself
// an inert attribute on a macro call
#[allow(warnings)] #[inline] foo!(); //~ WARN unused attribute `allow`
//~^ WARN unused attribute `inline`
#[allow(warnings)] #[rustc_dummy] foo!(); //~ WARN unused attribute `allow`
//~^ WARN unused attribute `rustc_dummy`
// This does work, since the attribute is on a parent
// of the macro invocation.
#[allow(warnings)] { #[inline] foo!(); }
#[allow(warnings)] { #[rustc_dummy] foo!(); }
// Ok, `cfg` and `cfg_attr` are expanded eagerly and do not warn.
#[cfg(true)] foo!();

View file

@ -1,44 +1,44 @@
warning: unused attribute `inline`
--> $DIR/inert-attr-macro.rs:10:5
warning: unused attribute `rustc_dummy`
--> $DIR/inert-attr-macro.rs:11:5
|
LL | #[inline] foo!();
| ^^^^^^^^^
LL | #[rustc_dummy] foo!();
| ^^^^^^^^^^^^^^
|
note: the built-in attribute `inline` will be ignored, since it's applied to the macro invocation `foo`
--> $DIR/inert-attr-macro.rs:10:15
note: the built-in attribute `rustc_dummy` will be ignored, since it's applied to the macro invocation `foo`
--> $DIR/inert-attr-macro.rs:11:20
|
LL | #[inline] foo!();
| ^^^
LL | #[rustc_dummy] foo!();
| ^^^
note: the lint level is defined here
--> $DIR/inert-attr-macro.rs:3:9
--> $DIR/inert-attr-macro.rs:4:9
|
LL | #![warn(unused)]
| ^^^^^^
= note: `#[warn(unused_attributes)]` implied by `#[warn(unused)]`
warning: unused attribute `allow`
--> $DIR/inert-attr-macro.rs:14:5
--> $DIR/inert-attr-macro.rs:15:5
|
LL | #[allow(warnings)] #[inline] foo!();
LL | #[allow(warnings)] #[rustc_dummy] foo!();
| ^^^^^^^^^^^^^^^^^^
|
note: the built-in attribute `allow` will be ignored, since it's applied to the macro invocation `foo`
--> $DIR/inert-attr-macro.rs:14:34
--> $DIR/inert-attr-macro.rs:15:39
|
LL | #[allow(warnings)] #[inline] foo!();
| ^^^
LL | #[allow(warnings)] #[rustc_dummy] foo!();
| ^^^
warning: unused attribute `inline`
--> $DIR/inert-attr-macro.rs:14:24
warning: unused attribute `rustc_dummy`
--> $DIR/inert-attr-macro.rs:15:24
|
LL | #[allow(warnings)] #[inline] foo!();
| ^^^^^^^^^
LL | #[allow(warnings)] #[rustc_dummy] foo!();
| ^^^^^^^^^^^^^^
|
note: the built-in attribute `inline` will be ignored, since it's applied to the macro invocation `foo`
--> $DIR/inert-attr-macro.rs:14:34
note: the built-in attribute `rustc_dummy` will be ignored, since it's applied to the macro invocation `foo`
--> $DIR/inert-attr-macro.rs:15:39
|
LL | #[allow(warnings)] #[inline] foo!();
| ^^^
LL | #[allow(warnings)] #[rustc_dummy] foo!();
| ^^^
warning: 3 warnings emitted

View file

@ -1,7 +1,7 @@
macro_rules! mac {
($attr_item: meta) => {
#[cfg($attr_item)]
//~^ ERROR expected unsuffixed literal, found `meta` metavariable
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `meta` metavariable
struct S;
}
}
@ -9,7 +9,7 @@ macro_rules! mac {
mac!(an(arbitrary token stream));
#[cfg(feature = -1)]
//~^ ERROR expected unsuffixed literal, found `-`
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
fn handler() {}
fn main() {}

View file

@ -1,10 +1,16 @@
error: expected unsuffixed literal, found `-`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
--> $DIR/attr-bad-meta-4.rs:11:17
|
LL | #[cfg(feature = -1)]
| ^
|
help: negative numbers are not literals, try removing the `-` sign
|
LL - #[cfg(feature = -1)]
LL + #[cfg(feature = 1)]
|
error: expected unsuffixed literal, found `meta` metavariable
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `meta` metavariable
--> $DIR/attr-bad-meta-4.rs:3:15
|
LL | #[cfg($attr_item)]

View file

@ -0,0 +1,17 @@
#[cfg(target-os = "windows")]
//~^ ERROR expected one of `(`, `,`, `::`, or `=`, found `-`
pub fn test1() { }
#[cfg(target_os = %)]
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `%`
pub fn test2() { }
#[cfg(target_os?)]
//~^ ERROR expected one of `(`, `,`, `::`, or `=`, found `?`
pub fn test3() { }
#[cfg[target_os]]
//~^ ERROR wrong meta list delimiters
pub fn test4() { }
pub fn main() {}

View file

@ -0,0 +1,32 @@
error: expected one of `(`, `,`, `::`, or `=`, found `-`
--> $DIR/attr-incomplete.rs:1:13
|
LL | #[cfg(target-os = "windows")]
| ^ expected one of `(`, `,`, `::`, or `=`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `%`
--> $DIR/attr-incomplete.rs:5:19
|
LL | #[cfg(target_os = %)]
| ^
error: expected one of `(`, `,`, `::`, or `=`, found `?`
--> $DIR/attr-incomplete.rs:9:16
|
LL | #[cfg(target_os?)]
| ^ expected one of `(`, `,`, `::`, or `=`
error: wrong meta list delimiters
--> $DIR/attr-incomplete.rs:13:6
|
LL | #[cfg[target_os]]
| ^^^^^^^^^^^
|
help: the delimiters should be `(` and `)`
|
LL - #[cfg[target_os]]
LL + #[cfg(target_os)]
|
error: aborting due to 4 previous errors

View file

@ -4,13 +4,13 @@
fn main() {
#[cfg(key=foo)]
//~^ ERROR expected unsuffixed literal, found `foo`
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
//~| HELP surround the identifier with quotation marks to make it into a string literal
println!();
#[cfg(key="bar")]
println!();
#[cfg(key=foo bar baz)]
//~^ ERROR expected unsuffixed literal, found `foo`
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
//~| HELP surround the identifier with quotation marks to make it into a string literal
println!();
}

View file

@ -1,4 +1,4 @@
error: expected unsuffixed literal, found `foo`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
--> $DIR/attr-unquoted-ident.rs:6:15
|
LL | #[cfg(key=foo)]
@ -9,7 +9,7 @@ help: surround the identifier with quotation marks to make it into a string lite
LL | #[cfg(key="foo")]
| + +
error: expected unsuffixed literal, found `foo`
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
--> $DIR/attr-unquoted-ident.rs:12:15
|
LL | #[cfg(key=foo bar baz)]

View file

@ -10,32 +10,12 @@ error: suffixes on string literals are invalid
LL | "C"suffix
| ^^^^^^^^^ invalid suffix `suffix`
error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:30:17
|
LL | #[rustc_dummy = "string"suffix]
| ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:34:14
|
LL | #[must_use = "string"suffix]
| ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:39:15
|
LL | #[link(name = "string"suffix)]
| ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: invalid suffix `suffix` for number literal
--> $DIR/bad-lit-suffixes.rs:43:41
|
LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
| ^^^^^^^ invalid suffix `suffix`
|
= help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/bad-lit-suffixes.rs:3:1
|
@ -150,6 +130,18 @@ LL | 1.0e10suffix;
|
= help: valid suffixes are `f32` and `f64`
error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:30:17
|
LL | #[rustc_dummy = "string"suffix]
| ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:34:14
|
LL | #[must_use = "string"suffix]
| ^^^^^^^^^^^^^^ invalid suffix `suffix`
error[E0539]: malformed `must_use` attribute input
--> $DIR/bad-lit-suffixes.rs:34:1
|
@ -168,16 +160,23 @@ LL - #[must_use = "string"suffix]
LL + #[must_use]
|
error[E0805]: malformed `rustc_layout_scalar_valid_range_start` attribute input
error: invalid suffix `suffix` for number literal
--> $DIR/bad-lit-suffixes.rs:43:41
|
LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
| ^^^^^^^ invalid suffix `suffix`
|
= help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input
--> $DIR/bad-lit-suffixes.rs:43:1
|
LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^
| | |
| | expected a single argument here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^
| | |
| | expected an integer literal here
| help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]`
error: aborting due to 22 previous errors; 2 warnings emitted
Some errors have detailed explanations: E0539, E0805.
For more information about an error, try `rustc --explain E0539`.
For more information about this error, try `rustc --explain E0539`.

View file

@ -5,6 +5,5 @@ fn main() {
const {
#![path = foo!()]
//~^ ERROR: cannot find macro `foo` in this scope
//~| ERROR malformed `path` attribute input
}
}

View file

@ -4,17 +4,5 @@ error: cannot find macro `foo` in this scope
LL | #![path = foo!()]
| ^^^
error[E0539]: malformed `path` attribute input
--> $DIR/path-attr-in-const-block.rs:6:9
|
LL | #![path = foo!()]
| ^^^^^^^^^^------^
| | |
| | expected a string literal here
| 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>
error: aborting due to 1 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0539`.