Rollup merge of #67052 - Centril:config-1, r=petrochenkov
Ditch `parse_in_attr` Fixes #66940 r? @petrochenkov
This commit is contained in:
commit
941c4cd56b
18 changed files with 328 additions and 165 deletions
|
@ -8,18 +8,19 @@
|
||||||
//!
|
//!
|
||||||
//! [#64197]: https://github.com/rust-lang/rust/issues/64197
|
//! [#64197]: https://github.com/rust-lang/rust/issues/64197
|
||||||
|
|
||||||
use crate::validate_attr;
|
use crate::{parse_in, validate_attr};
|
||||||
use rustc_feature::Features;
|
use rustc_feature::Features;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use syntax::attr::HasAttrs;
|
use syntax::attr::HasAttrs;
|
||||||
use syntax::feature_gate::{feature_err, get_features};
|
use syntax::feature_gate::{feature_err, get_features};
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::ast;
|
use syntax::ast::{self, Attribute, AttrItem, MetaItem};
|
||||||
use syntax::edition::Edition;
|
use syntax::edition::Edition;
|
||||||
use syntax::mut_visit::*;
|
use syntax::mut_visit::*;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
use syntax::sess::ParseSess;
|
use syntax::sess::ParseSess;
|
||||||
use syntax::util::map_in_place::MapInPlace;
|
use syntax::util::map_in_place::MapInPlace;
|
||||||
|
use syntax_pos::Span;
|
||||||
use syntax_pos::symbol::sym;
|
use syntax_pos::symbol::sym;
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -72,6 +73,11 @@ macro_rules! configure {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
|
||||||
|
const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
|
||||||
|
<https://doc.rust-lang.org/reference/conditional-compilation.html\
|
||||||
|
#the-cfg_attr-attribute>";
|
||||||
|
|
||||||
impl<'a> StripUnconfigured<'a> {
|
impl<'a> StripUnconfigured<'a> {
|
||||||
pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
|
pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
|
||||||
self.process_cfg_attrs(&mut node);
|
self.process_cfg_attrs(&mut node);
|
||||||
|
@ -97,34 +103,14 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
|
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
|
||||||
/// is in the original source file. Gives a compiler error if the syntax of
|
/// is in the original source file. Gives a compiler error if the syntax of
|
||||||
/// the attribute is incorrect.
|
/// the attribute is incorrect.
|
||||||
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
|
fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
|
||||||
if !attr.has_name(sym::cfg_attr) {
|
if !attr.has_name(sym::cfg_attr) {
|
||||||
return vec![attr];
|
return vec![attr];
|
||||||
}
|
}
|
||||||
if let ast::MacArgs::Empty = attr.get_normal_item().args {
|
|
||||||
self.sess.span_diagnostic
|
|
||||||
.struct_span_err(
|
|
||||||
attr.span,
|
|
||||||
"malformed `cfg_attr` attribute input",
|
|
||||||
).span_suggestion(
|
|
||||||
attr.span,
|
|
||||||
"missing condition and attribute",
|
|
||||||
"#[cfg_attr(condition, attribute, other_attribute, ...)]".to_owned(),
|
|
||||||
Applicability::HasPlaceholders,
|
|
||||||
).note("for more information, visit \
|
|
||||||
<https://doc.rust-lang.org/reference/conditional-compilation.html\
|
|
||||||
#the-cfg_attr-attribute>")
|
|
||||||
.emit();
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = crate::parse_in_attr(self.sess, &attr, |p| p.parse_cfg_attr());
|
let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
|
||||||
let (cfg_predicate, expanded_attrs) = match res {
|
None => return vec![],
|
||||||
Ok(result) => result,
|
Some(r) => r,
|
||||||
Err(mut e) => {
|
|
||||||
e.emit();
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lint on zero attributes in source.
|
// Lint on zero attributes in source.
|
||||||
|
@ -135,24 +121,56 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
// At this point we know the attribute is considered used.
|
// At this point we know the attribute is considered used.
|
||||||
attr::mark_used(&attr);
|
attr::mark_used(&attr);
|
||||||
|
|
||||||
if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
|
if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
|
||||||
// We call `process_cfg_attr` recursively in case there's a
|
return vec![];
|
||||||
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
|
||||||
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
|
||||||
expanded_attrs.into_iter()
|
|
||||||
.flat_map(|(item, span)| self.process_cfg_attr(attr::mk_attr_from_item(
|
|
||||||
attr.style,
|
|
||||||
item,
|
|
||||||
span,
|
|
||||||
)))
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We call `process_cfg_attr` recursively in case there's a
|
||||||
|
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
||||||
|
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
||||||
|
expanded_attrs
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|(item, span)| {
|
||||||
|
let attr = attr::mk_attr_from_item(attr.style, item, span);
|
||||||
|
self.process_cfg_attr(attr)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
|
||||||
|
match attr.get_normal_item().args {
|
||||||
|
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
|
||||||
|
let msg = "wrong `cfg_attr` delimiters";
|
||||||
|
validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
|
||||||
|
match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
|
||||||
|
Ok(r) => return Some(r),
|
||||||
|
Err(mut e) => e
|
||||||
|
.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
|
||||||
|
.note(CFG_ATTR_NOTE_REF)
|
||||||
|
.emit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => self.error_malformed_cfg_attr_missing(attr.span),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_malformed_cfg_attr_missing(&self, span: Span) {
|
||||||
|
self.sess
|
||||||
|
.span_diagnostic
|
||||||
|
.struct_span_err(span, "malformed `cfg_attr` attribute input")
|
||||||
|
.span_suggestion(
|
||||||
|
span,
|
||||||
|
"missing condition and attribute",
|
||||||
|
CFG_ATTR_GRAMMAR_HELP.to_string(),
|
||||||
|
Applicability::HasPlaceholders,
|
||||||
|
)
|
||||||
|
.note(CFG_ATTR_NOTE_REF)
|
||||||
|
.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if a node with the given attributes should be included in this configuration.
|
/// Determines if a node with the given attributes should be included in this configuration.
|
||||||
pub fn in_cfg(&self, attrs: &[ast::Attribute]) -> bool {
|
pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
|
||||||
attrs.iter().all(|attr| {
|
attrs.iter().all(|attr| {
|
||||||
if !is_cfg(attr) {
|
if !is_cfg(attr) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -199,7 +217,7 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit attributes on expression and statements (but not attributes on items in blocks).
|
/// Visit attributes on expression and statements (but not attributes on items in blocks).
|
||||||
fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
|
fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
|
||||||
// flag the offending attributes
|
// flag the offending attributes
|
||||||
for attr in attrs.iter() {
|
for attr in attrs.iter() {
|
||||||
self.maybe_emit_expr_attr_err(attr);
|
self.maybe_emit_expr_attr_err(attr);
|
||||||
|
@ -207,7 +225,7 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If attributes are not allowed on expressions, emit an error for `attr`
|
/// If attributes are not allowed on expressions, emit an error for `attr`
|
||||||
pub fn maybe_emit_expr_attr_err(&self, attr: &ast::Attribute) {
|
pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
|
||||||
if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
|
if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
|
||||||
let mut err = feature_err(self.sess,
|
let mut err = feature_err(self.sess,
|
||||||
sym::stmt_expr_attributes,
|
sym::stmt_expr_attributes,
|
||||||
|
@ -350,7 +368,7 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_cfg(attr: &ast::Attribute) -> bool {
|
fn is_cfg(attr: &Attribute) -> bool {
|
||||||
attr.check_name(sym::cfg)
|
attr.check_name(sym::cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,8 +377,8 @@ fn is_cfg(attr: &ast::Attribute) -> bool {
|
||||||
pub fn process_configure_mod(
|
pub fn process_configure_mod(
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
cfg_mods: bool,
|
cfg_mods: bool,
|
||||||
attrs: &[ast::Attribute],
|
attrs: &[Attribute],
|
||||||
) -> (bool, Vec<ast::Attribute>) {
|
) -> (bool, Vec<Attribute>) {
|
||||||
// Don't perform gated feature checking.
|
// Don't perform gated feature checking.
|
||||||
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
|
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
|
||||||
let mut attrs = attrs.to_owned();
|
let mut attrs = attrs.to_owned();
|
||||||
|
|
|
@ -271,21 +271,13 @@ pub fn stream_to_parser_with_base_dir<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
|
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
|
||||||
pub fn parse_in_attr<'a, T>(
|
pub fn parse_in<'a, T>(
|
||||||
sess: &'a ParseSess,
|
sess: &'a ParseSess,
|
||||||
attr: &ast::Attribute,
|
tts: TokenStream,
|
||||||
|
name: &'static str,
|
||||||
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
) -> PResult<'a, T> {
|
) -> PResult<'a, T> {
|
||||||
let mut parser = Parser::new(
|
let mut parser = Parser::new(sess, tts, None, false, false, Some(name));
|
||||||
sess,
|
|
||||||
// FIXME(#66940, Centril | petrochenkov): refactor this function so it doesn't
|
|
||||||
// require reconstructing and immediately re-parsing delimiters.
|
|
||||||
attr.get_normal_item().args.outer_tokens(),
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
Some("attribute"),
|
|
||||||
);
|
|
||||||
let result = f(&mut parser)?;
|
let result = f(&mut parser)?;
|
||||||
if parser.token != token::Eof {
|
if parser.token != token::Eof {
|
||||||
parser.unexpected()?;
|
parser.unexpected()?;
|
||||||
|
|
|
@ -220,7 +220,7 @@ impl<'a> Parser<'a> {
|
||||||
Ok(attrs)
|
Ok(attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
|
crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
|
||||||
let lit = self.parse_lit()?;
|
let lit = self.parse_lit()?;
|
||||||
debug!("checking if {:?} is unusuffixed", lit);
|
debug!("checking if {:?} is unusuffixed", lit);
|
||||||
|
|
||||||
|
@ -238,25 +238,36 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
|
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
|
||||||
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
|
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
|
||||||
self.expect(&token::OpenDelim(token::Paren))?;
|
|
||||||
|
|
||||||
let cfg_predicate = self.parse_meta_item()?;
|
let cfg_predicate = self.parse_meta_item()?;
|
||||||
self.expect(&token::Comma)?;
|
self.expect(&token::Comma)?;
|
||||||
|
|
||||||
// Presumably, the majority of the time there will only be one attr.
|
// Presumably, the majority of the time there will only be one attr.
|
||||||
let mut expanded_attrs = Vec::with_capacity(1);
|
let mut expanded_attrs = Vec::with_capacity(1);
|
||||||
|
while self.token.kind != token::Eof {
|
||||||
while !self.check(&token::CloseDelim(token::Paren)) {
|
let lo = self.token.span;
|
||||||
let lo = self.token.span.lo();
|
|
||||||
let item = self.parse_attr_item()?;
|
let item = self.parse_attr_item()?;
|
||||||
expanded_attrs.push((item, self.prev_span.with_lo(lo)));
|
expanded_attrs.push((item, lo.to(self.prev_span)));
|
||||||
self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
|
if !self.eat(&token::Comma) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(&token::CloseDelim(token::Paren))?;
|
|
||||||
Ok((cfg_predicate, expanded_attrs))
|
Ok((cfg_predicate, expanded_attrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Matches `COMMASEP(meta_item_inner)`.
|
||||||
|
crate fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
|
||||||
|
// Presumably, the majority of the time there will only be one attr.
|
||||||
|
let mut nmis = Vec::with_capacity(1);
|
||||||
|
while self.token.kind != token::Eof {
|
||||||
|
nmis.push(self.parse_meta_item_inner()?);
|
||||||
|
if !self.eat(&token::Comma) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(nmis)
|
||||||
|
}
|
||||||
|
|
||||||
/// Matches the following grammar (per RFC 1559).
|
/// Matches the following grammar (per RFC 1559).
|
||||||
///
|
///
|
||||||
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
|
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::maybe_whole;
|
||||||
use rustc_errors::{PResult, Applicability, pluralize};
|
use rustc_errors::{PResult, Applicability, pluralize};
|
||||||
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
|
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
|
||||||
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
|
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
|
||||||
use syntax::ast::MacArgs;
|
|
||||||
use syntax::ThinVec;
|
use syntax::ThinVec;
|
||||||
use syntax::token::{self, Token};
|
use syntax::token::{self, Token};
|
||||||
use syntax_pos::source_map::{Span, BytePos};
|
use syntax_pos::source_map::{Span, BytePos};
|
||||||
|
@ -109,42 +108,6 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Path { segments, span: lo.to(self.prev_span) })
|
Ok(Path { segments, span: lo.to(self.prev_span) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `parse_path`, but also supports parsing `Word` meta items into paths for
|
|
||||||
/// backwards-compatibility. This is used when parsing derive macro paths in `#[derive]`
|
|
||||||
/// attributes.
|
|
||||||
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
|
|
||||||
let meta_ident = match self.token.kind {
|
|
||||||
token::Interpolated(ref nt) => match **nt {
|
|
||||||
token::NtMeta(ref item) => match item.args {
|
|
||||||
MacArgs::Empty => Some(item.path.clone()),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
if let Some(path) = meta_ident {
|
|
||||||
self.bump();
|
|
||||||
return Ok(path);
|
|
||||||
}
|
|
||||||
self.parse_path(style)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a list of paths inside `#[derive(path_0, ..., path_n)]`.
|
|
||||||
pub fn parse_derive_paths(&mut self) -> PResult<'a, Vec<Path>> {
|
|
||||||
self.expect(&token::OpenDelim(token::Paren))?;
|
|
||||||
let mut list = Vec::new();
|
|
||||||
while !self.eat(&token::CloseDelim(token::Paren)) {
|
|
||||||
let path = self.parse_path_allowing_meta(PathStyle::Mod)?;
|
|
||||||
list.push(path);
|
|
||||||
if !self.eat(&token::Comma) {
|
|
||||||
self.expect(&token::CloseDelim(token::Paren))?;
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn parse_path_segments(
|
pub(super) fn parse_path_segments(
|
||||||
&mut self,
|
&mut self,
|
||||||
segments: &mut Vec<PathSegment>,
|
segments: &mut Vec<PathSegment>,
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
//! Meta-syntax validation logic of attributes for post-expansion.
|
//! Meta-syntax validation logic of attributes for post-expansion.
|
||||||
|
|
||||||
|
use crate::parse_in;
|
||||||
|
|
||||||
use rustc_errors::{PResult, Applicability};
|
use rustc_errors::{PResult, Applicability};
|
||||||
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
|
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
|
||||||
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
|
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
|
||||||
use syntax::attr::mk_name_value_item_str;
|
use syntax::attr::mk_name_value_item_str;
|
||||||
use syntax::early_buffered_lints::ILL_FORMED_ATTRIBUTE_INPUT;
|
use syntax::early_buffered_lints::ILL_FORMED_ATTRIBUTE_INPUT;
|
||||||
|
use syntax::tokenstream::DelimSpan;
|
||||||
use syntax::sess::ParseSess;
|
use syntax::sess::ParseSess;
|
||||||
use syntax_pos::{Symbol, sym};
|
use syntax_pos::{Symbol, sym};
|
||||||
|
|
||||||
|
@ -27,9 +30,20 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
|
||||||
pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
|
pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
|
||||||
Ok(match attr.kind {
|
Ok(match attr.kind {
|
||||||
AttrKind::Normal(ref item) => MetaItem {
|
AttrKind::Normal(ref item) => MetaItem {
|
||||||
path: item.path.clone(),
|
|
||||||
kind: super::parse_in_attr(sess, attr, |p| p.parse_meta_item_kind())?,
|
|
||||||
span: attr.span,
|
span: attr.span,
|
||||||
|
path: item.path.clone(),
|
||||||
|
kind: match &attr.get_normal_item().args {
|
||||||
|
MacArgs::Empty => MetaItemKind::Word,
|
||||||
|
MacArgs::Eq(_, t) => {
|
||||||
|
let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?;
|
||||||
|
MetaItemKind::NameValue(v)
|
||||||
|
}
|
||||||
|
MacArgs::Delimited(dspan, delim, t) => {
|
||||||
|
check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
|
||||||
|
let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
|
||||||
|
MetaItemKind::List(nmis)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
AttrKind::DocComment(comment) => {
|
AttrKind::DocComment(comment) => {
|
||||||
mk_name_value_item_str(Ident::new(sym::doc, attr.span), comment, attr.span)
|
mk_name_value_item_str(Ident::new(sym::doc, attr.span), comment, attr.span)
|
||||||
|
@ -37,6 +51,24 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
|
||||||
|
if let ast::MacDelimiter::Parenthesis = delim {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess.span_diagnostic
|
||||||
|
.struct_span_err(span.entire(), msg)
|
||||||
|
.multipart_suggestion(
|
||||||
|
"the delimiters should be `(` and `)`",
|
||||||
|
vec![
|
||||||
|
(span.open, "(".to_string()),
|
||||||
|
(span.close, ")".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
|
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
|
||||||
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
|
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
|
||||||
match meta {
|
match meta {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::base::{self, *};
|
use crate::base::{self, *};
|
||||||
use crate::proc_macro_server;
|
use crate::proc_macro_server;
|
||||||
|
|
||||||
use syntax::ast::{self, ItemKind, MacArgs};
|
use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
|
||||||
use syntax::errors::{Applicability, FatalError};
|
use syntax::errors::{Applicability, FatalError};
|
||||||
use syntax::symbol::sym;
|
use syntax::symbol::sym;
|
||||||
use syntax::token;
|
use syntax::token;
|
||||||
|
@ -171,34 +171,71 @@ crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>)
|
||||||
if !attr.has_name(sym::derive) {
|
if !attr.has_name(sym::derive) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if !attr.is_meta_item_list() {
|
|
||||||
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
|
|
||||||
.span_suggestion(
|
|
||||||
attr.span,
|
|
||||||
"missing traits to be derived",
|
|
||||||
"#[derive(Trait1, Trait2, ...)]".to_owned(),
|
|
||||||
Applicability::HasPlaceholders,
|
|
||||||
).emit();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let parse_derive_paths = |attr: &ast::Attribute| {
|
// 1) First let's ensure that it's a meta item.
|
||||||
if let MacArgs::Empty = attr.get_normal_item().args {
|
let nmis = match attr.meta_item_list() {
|
||||||
return Ok(Vec::new());
|
None => {
|
||||||
|
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
|
||||||
|
.span_suggestion(
|
||||||
|
attr.span,
|
||||||
|
"missing traits to be derived",
|
||||||
|
"#[derive(Trait1, Trait2, ...)]".to_owned(),
|
||||||
|
Applicability::HasPlaceholders,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
|
Some(x) => x,
|
||||||
};
|
};
|
||||||
|
|
||||||
match parse_derive_paths(attr) {
|
let mut error_reported_filter_map = false;
|
||||||
Ok(traits) => {
|
let mut error_reported_map = false;
|
||||||
result.extend(traits);
|
let traits = nmis
|
||||||
true
|
.into_iter()
|
||||||
}
|
// 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`.
|
||||||
Err(mut e) => {
|
.filter_map(|nmi| match nmi {
|
||||||
e.emit();
|
NestedMetaItem::Literal(lit) => {
|
||||||
false
|
error_reported_filter_map = true;
|
||||||
}
|
cx.struct_span_err(lit.span, "expected path to a trait, found literal")
|
||||||
}
|
.help("for example, write `#[derive(Debug)]` for `Debug`")
|
||||||
|
.emit();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
NestedMetaItem::MetaItem(mi) => Some(mi),
|
||||||
|
})
|
||||||
|
// 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]`
|
||||||
|
// but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`.
|
||||||
|
// In this case we can still at least determine that the user
|
||||||
|
// wanted this trait to be derived, so let's keep it.
|
||||||
|
.map(|mi| {
|
||||||
|
let mut traits_dont_accept = |title, action| {
|
||||||
|
error_reported_map = true;
|
||||||
|
let sp = mi.span.with_lo(mi.path.span.hi());
|
||||||
|
cx.struct_span_err(sp, title)
|
||||||
|
.span_suggestion(
|
||||||
|
sp,
|
||||||
|
action,
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
};
|
||||||
|
match &mi.kind {
|
||||||
|
MetaItemKind::List(..) => traits_dont_accept(
|
||||||
|
"traits in `#[derive(...)]` don't accept arguments",
|
||||||
|
"remove the arguments",
|
||||||
|
),
|
||||||
|
MetaItemKind::NameValue(..) => traits_dont_accept(
|
||||||
|
"traits in `#[derive(...)]` don't accept values",
|
||||||
|
"remove the value",
|
||||||
|
),
|
||||||
|
MetaItemKind::Word => {}
|
||||||
|
}
|
||||||
|
mi.path
|
||||||
|
});
|
||||||
|
|
||||||
|
result.extend(traits);
|
||||||
|
!error_reported_filter_map && !error_reported_map
|
||||||
});
|
});
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// Parse `cfg_attr` with varying numbers of attributes and trailing commas
|
// Parse `cfg_attr` with varying numbers of attributes and trailing commas
|
||||||
|
|
||||||
// Completely empty `cfg_attr` input
|
// Completely empty `cfg_attr` input
|
||||||
#[cfg_attr()] //~ error: expected identifier, found `)`
|
#[cfg_attr()] //~ error: malformed `cfg_attr` attribute input
|
||||||
struct NoConfigurationPredicate;
|
struct NoConfigurationPredicate;
|
||||||
|
|
||||||
// Zero attributes, zero trailing comma (comma manatory here)
|
// Zero attributes, zero trailing comma (comma manatory here)
|
||||||
#[cfg_attr(all())] //~ error: expected `,`, found `)`
|
#[cfg_attr(all())] //~ error: expected `,`, found end of `cfg_attr`
|
||||||
struct A0C0;
|
struct A0C0;
|
||||||
|
|
||||||
// Zero attributes, one trailing comma
|
// Zero attributes, one trailing comma
|
||||||
|
@ -40,4 +40,16 @@ struct A2C1;
|
||||||
#[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
|
#[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
|
||||||
struct A2C2;
|
struct A2C2;
|
||||||
|
|
||||||
|
// Wrong delimiter `[`
|
||||||
|
#[cfg_attr[all(),,]]
|
||||||
|
//~^ ERROR wrong `cfg_attr` delimiters
|
||||||
|
//~| ERROR expected identifier, found `,`
|
||||||
|
struct BracketZero;
|
||||||
|
|
||||||
|
// Wrong delimiter `{`
|
||||||
|
#[cfg_attr{all(),,}]
|
||||||
|
//~^ ERROR wrong `cfg_attr` delimiters
|
||||||
|
//~| ERROR expected identifier, found `,`
|
||||||
|
struct BraceZero;
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,32 +1,86 @@
|
||||||
error: expected identifier, found `)`
|
error: malformed `cfg_attr` attribute input
|
||||||
--> $DIR/cfg-attr-parse.rs:4:12
|
--> $DIR/cfg-attr-parse.rs:4:1
|
||||||
|
|
|
|
||||||
LL | #[cfg_attr()]
|
LL | #[cfg_attr()]
|
||||||
| ^ expected identifier
|
| ^^^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
|
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: expected `,`, found `)`
|
error: expected `,`, found end of `cfg_attr` input
|
||||||
--> $DIR/cfg-attr-parse.rs:8:17
|
--> $DIR/cfg-attr-parse.rs:8:17
|
||||||
|
|
|
|
||||||
LL | #[cfg_attr(all())]
|
LL | #[cfg_attr(all())]
|
||||||
| ^ expected `,`
|
| ^ expected `,`
|
||||||
|
|
|
||||||
|
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: expected identifier, found `,`
|
error: expected identifier, found `,`
|
||||||
--> $DIR/cfg-attr-parse.rs:16:18
|
--> $DIR/cfg-attr-parse.rs:16:18
|
||||||
|
|
|
|
||||||
LL | #[cfg_attr(all(),,)]
|
LL | #[cfg_attr(all(),,)]
|
||||||
| ^ expected identifier
|
| ^ expected identifier
|
||||||
|
|
|
||||||
|
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: expected identifier, found `,`
|
error: expected identifier, found `,`
|
||||||
--> $DIR/cfg-attr-parse.rs:28:28
|
--> $DIR/cfg-attr-parse.rs:28:28
|
||||||
|
|
|
|
||||||
LL | #[cfg_attr(all(), must_use,,)]
|
LL | #[cfg_attr(all(), must_use,,)]
|
||||||
| ^ expected identifier
|
| ^ expected identifier
|
||||||
|
|
|
||||||
|
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: expected identifier, found `,`
|
error: expected identifier, found `,`
|
||||||
--> $DIR/cfg-attr-parse.rs:40:40
|
--> $DIR/cfg-attr-parse.rs:40:40
|
||||||
|
|
|
|
||||||
LL | #[cfg_attr(all(), must_use, deprecated,,)]
|
LL | #[cfg_attr(all(), must_use, deprecated,,)]
|
||||||
| ^ expected identifier
|
| ^ expected identifier
|
||||||
|
|
|
||||||
|
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error: wrong `cfg_attr` delimiters
|
||||||
|
--> $DIR/cfg-attr-parse.rs:44:11
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr[all(),,]]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: the delimiters should be `(` and `)`
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr(all(),,)]
|
||||||
|
| ^ ^
|
||||||
|
|
||||||
|
error: expected identifier, found `,`
|
||||||
|
--> $DIR/cfg-attr-parse.rs:44:18
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr[all(),,]]
|
||||||
|
| ^ expected identifier
|
||||||
|
|
|
||||||
|
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
|
error: wrong `cfg_attr` delimiters
|
||||||
|
--> $DIR/cfg-attr-parse.rs:50:11
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr{all(),,}]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: the delimiters should be `(` and `)`
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr(all(),,)]
|
||||||
|
| ^ ^
|
||||||
|
|
||||||
|
error: expected identifier, found `,`
|
||||||
|
--> $DIR/cfg-attr-parse.rs:50:18
|
||||||
|
|
|
||||||
|
LL | #[cfg_attr{all(),,}]
|
||||||
|
| ^ expected identifier
|
||||||
|
|
|
||||||
|
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#[derive(Copy(Bad))] //~ ERROR expected one of `)`, `,`, or `::`, found `(`
|
#[derive(Copy(Bad))]
|
||||||
|
//~^ ERROR traits in `#[derive(...)]` don't accept arguments
|
||||||
|
//~| ERROR the trait bound
|
||||||
struct Test1;
|
struct Test1;
|
||||||
|
|
||||||
#[derive(Copy="bad")] //~ ERROR expected one of `)`, `,`, or `::`, found `=`
|
#[derive(Copy="bad")]
|
||||||
|
//~^ ERROR traits in `#[derive(...)]` don't accept values
|
||||||
|
//~| ERROR the trait bound
|
||||||
struct Test2;
|
struct Test2;
|
||||||
|
|
||||||
#[derive] //~ ERROR malformed `derive` attribute input
|
#[derive] //~ ERROR malformed `derive` attribute input
|
||||||
|
|
|
@ -1,20 +1,33 @@
|
||||||
error: expected one of `)`, `,`, or `::`, found `(`
|
error: traits in `#[derive(...)]` don't accept arguments
|
||||||
--> $DIR/malformed-derive-entry.rs:1:14
|
--> $DIR/malformed-derive-entry.rs:1:14
|
||||||
|
|
|
|
||||||
LL | #[derive(Copy(Bad))]
|
LL | #[derive(Copy(Bad))]
|
||||||
| ^ expected one of `)`, `,`, or `::`
|
| ^^^^^ help: remove the arguments
|
||||||
|
|
||||||
error: expected one of `)`, `,`, or `::`, found `=`
|
error: traits in `#[derive(...)]` don't accept values
|
||||||
--> $DIR/malformed-derive-entry.rs:4:14
|
--> $DIR/malformed-derive-entry.rs:6:14
|
||||||
|
|
|
|
||||||
LL | #[derive(Copy="bad")]
|
LL | #[derive(Copy="bad")]
|
||||||
| ^ expected one of `)`, `,`, or `::`
|
| ^^^^^^ help: remove the value
|
||||||
|
|
||||||
error: malformed `derive` attribute input
|
error: malformed `derive` attribute input
|
||||||
--> $DIR/malformed-derive-entry.rs:7:1
|
--> $DIR/malformed-derive-entry.rs:11:1
|
||||||
|
|
|
|
||||||
LL | #[derive]
|
LL | #[derive]
|
||||||
| ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
|
| ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error[E0277]: the trait bound `Test1: std::clone::Clone` is not satisfied
|
||||||
|
--> $DIR/malformed-derive-entry.rs:1:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Copy(Bad))]
|
||||||
|
| ^^^^ the trait `std::clone::Clone` is not implemented for `Test1`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `Test2: std::clone::Clone` is not satisfied
|
||||||
|
--> $DIR/malformed-derive-entry.rs:6:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Copy="bad")]
|
||||||
|
| ^^^^ the trait `std::clone::Clone` is not implemented for `Test2`
|
||||||
|
|
||||||
|
error: aborting due to 5 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
|
|
11
src/test/ui/malformed/malformed-meta-delim.rs
Normal file
11
src/test/ui/malformed/malformed-meta-delim.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
#[allow { foo_lint } ]
|
||||||
|
//~^ ERROR wrong meta list delimiters
|
||||||
|
//~| HELP the delimiters should be `(` and `)`
|
||||||
|
fn delim_brace() {}
|
||||||
|
|
||||||
|
#[allow [ foo_lint ] ]
|
||||||
|
//~^ ERROR wrong meta list delimiters
|
||||||
|
//~| HELP the delimiters should be `(` and `)`
|
||||||
|
fn delim_bracket() {}
|
24
src/test/ui/malformed/malformed-meta-delim.stderr
Normal file
24
src/test/ui/malformed/malformed-meta-delim.stderr
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
error: wrong meta list delimiters
|
||||||
|
--> $DIR/malformed-meta-delim.rs:3:9
|
||||||
|
|
|
||||||
|
LL | #[allow { foo_lint } ]
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: the delimiters should be `(` and `)`
|
||||||
|
|
|
||||||
|
LL | #[allow ( foo_lint ) ]
|
||||||
|
| ^ ^
|
||||||
|
|
||||||
|
error: wrong meta list delimiters
|
||||||
|
--> $DIR/malformed-meta-delim.rs:8:9
|
||||||
|
|
|
||||||
|
LL | #[allow [ foo_lint ] ]
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: the delimiters should be `(` and `)`
|
||||||
|
|
|
||||||
|
LL | #[allow ( foo_lint ) ]
|
||||||
|
| ^ ^
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[cfg_attr] //~ ERROR malformed `cfg_attr` attribute
|
#[cfg_attr] //~ ERROR malformed `cfg_attr` attribute
|
||||||
struct S1;
|
struct S1;
|
||||||
|
|
||||||
#[cfg_attr = ""] //~ ERROR expected `(`, found `=`
|
#[cfg_attr = ""] //~ ERROR malformed `cfg_attr` attribute
|
||||||
struct S2;
|
struct S2;
|
||||||
|
|
||||||
#[derive] //~ ERROR malformed `derive` attribute
|
#[derive] //~ ERROR malformed `derive` attribute
|
||||||
|
|
|
@ -6,11 +6,13 @@ LL | #[cfg_attr]
|
||||||
|
|
|
|
||||||
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: expected `(`, found `=`
|
error: malformed `cfg_attr` attribute input
|
||||||
--> $DIR/malformed-special-attrs.rs:4:12
|
--> $DIR/malformed-special-attrs.rs:4:1
|
||||||
|
|
|
|
||||||
LL | #[cfg_attr = ""]
|
LL | #[cfg_attr = ""]
|
||||||
| ^ expected `(`
|
| ^^^^^^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
|
||||||
|
|
|
||||||
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
|
||||||
|
|
||||||
error: malformed `derive` attribute input
|
error: malformed `derive` attribute input
|
||||||
--> $DIR/malformed-special-attrs.rs:7:1
|
--> $DIR/malformed-special-attrs.rs:7:1
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#[rustc_on_unimplemented(
|
#[rustc_on_unimplemented(
|
||||||
message="the message"
|
message="the message"
|
||||||
label="the label" //~ ERROR expected one of `)` or `,`, found `label`
|
label="the label" //~ ERROR expected `,`, found `label`
|
||||||
)]
|
)]
|
||||||
trait T {}
|
trait T {}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
error: expected one of `)` or `,`, found `label`
|
error: expected `,`, found `label`
|
||||||
--> $DIR/expected-comma-found-token.rs:9:5
|
--> $DIR/expected-comma-found-token.rs:9:5
|
||||||
|
|
|
|
||||||
LL | message="the message"
|
LL | message="the message"
|
||||||
| -
|
| - expected `,`
|
||||||
| |
|
|
||||||
| expected one of `)` or `,`
|
|
||||||
| help: missing `,`
|
|
||||||
LL | label="the label"
|
LL | label="the label"
|
||||||
| ^^^^^ unexpected token
|
| ^^^^^ unexpected token
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,4 @@ fn main() {
|
||||||
foo::<T>!(); //~ ERROR generic arguments in macro path
|
foo::<T>!(); //~ ERROR generic arguments in macro path
|
||||||
foo::<>!(); //~ ERROR generic arguments in macro path
|
foo::<>!(); //~ ERROR generic arguments in macro path
|
||||||
m!(Default<>); //~ ERROR generic arguments in macro path
|
m!(Default<>); //~ ERROR generic arguments in macro path
|
||||||
//~^ ERROR unexpected generic arguments in path
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,11 @@ error: generic arguments in macro path
|
||||||
LL | foo::<>!();
|
LL | foo::<>!();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: unexpected generic arguments in path
|
|
||||||
--> $DIR/macro-ty-params.rs:12:8
|
|
||||||
|
|
|
||||||
LL | m!(Default<>);
|
|
||||||
| ^^^^^^^^^
|
|
||||||
|
|
||||||
error: generic arguments in macro path
|
error: generic arguments in macro path
|
||||||
--> $DIR/macro-ty-params.rs:12:15
|
--> $DIR/macro-ty-params.rs:12:15
|
||||||
|
|
|
|
||||||
LL | m!(Default<>);
|
LL | m!(Default<>);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue