use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{AstLike, Block, Inline, ItemKind, Local, MacArgs};
+use rustc_ast::{AstLike, Block, Inline, ItemKind, Local, MacArgs, MacCall};
use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
use rustc_ast_pretty::pprust;
AttemptLocalParseRecovery, ForceCollect, Parser, RecoverColon, RecoverComma,
};
use rustc_parse::validate_attr;
-use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
+use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::parse::{feature_err, ParseSess};
use rustc_session::Limit;
}
}
SyntaxExtensionKind::NonMacroAttr { mark_used } => {
- self.cx.sess.mark_attr_known(&attr);
+ self.cx.expanded_inert_attrs.mark(&attr);
if *mark_used {
self.cx.sess.mark_attr_used(&attr);
}
item.visit_attrs(|attrs| {
attr = attrs
.iter()
- .position(|a| !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a))
+ .position(|a| !self.cx.expanded_inert_attrs.is_marked(a) && !is_builtin_attr(a))
.map(|attr_pos| {
let attr = attrs.remove(attr_pos);
let following_derives = attrs[attr_pos..]
// Detect use of feature-gated or invalid attributes on macro invocations
// since they will not be detected after macro expansion.
- fn check_attributes(&mut self, attrs: &[ast::Attribute]) {
+ fn check_attributes(&mut self, attrs: &[ast::Attribute], call: &MacCall) {
let features = self.cx.ecfg.features.unwrap();
let mut attrs = attrs.iter().peekable();
let mut span: Option<Span> = None;
continue;
}
- if attr.doc_str().is_some() {
+ if attr.is_doc_comment() {
self.cx.sess.parse_sess.buffer_lint_with_diagnostic(
&UNUSED_DOC_COMMENTS,
current_span,
- ast::CRATE_NODE_ID,
+ self.cx.current_expansion.lint_node_id,
"unused doc comment",
BuiltinLintDiagnostics::UnusedDocComment(attr.span),
);
+ } else if rustc_attr::is_builtin_attr(attr) {
+ let attr_name = attr.ident().unwrap().name;
+ // `#[cfg]` and `#[cfg_attr]` are special - they are
+ // eagerly evaluated.
+ if attr_name != sym::cfg && attr_name != sym::cfg_attr {
+ self.cx.sess.parse_sess.buffer_lint_with_diagnostic(
+ &UNUSED_ATTRIBUTES,
+ attr.span,
+ self.cx.current_expansion.lint_node_id,
+ &format!("unused attribute `{}`", attr_name),
+ BuiltinLintDiagnostics::UnusedBuiltinAttribute {
+ attr_name,
+ macro_name: pprust::path_to_string(&call.path),
+ invoc_span: call.path.span,
+ },
+ );
+ }
}
}
}
}
if let ast::ExprKind::MacCall(mac) = expr.kind {
- self.check_attributes(&expr.attrs);
+ self.check_attributes(&expr.attrs, &mac);
self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr().into_inner()
} else {
assign_id!(self, &mut expr.id, || {
}
if let ast::ExprKind::MacCall(mac) = expr.kind {
- self.check_attributes(&expr.attrs);
+ self.check_attributes(&expr.attrs, &mac);
self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr)
.make_opt_expr()
.map(|expr| expr.into_inner())
if let StmtKind::MacCall(mac) = stmt.kind {
let MacCallStmt { mac, style, attrs, tokens: _ } = mac.into_inner();
- self.check_attributes(&attrs);
+ self.check_attributes(&attrs, &mac);
let mut placeholder =
self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts();
return placeholder;
}
+ // The only way that we can end up with a `MacCall` expression statement,
+ // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the
+ // traiing expression in a block (e.g. `fn foo() { my_macro!() }`).
+ // Record this information, so that we can report a more specific
+ // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
+ // See #78991 for an investigation of treating macros in this position
+ // as statements, rather than expressions, during parsing.
+ if let StmtKind::Expr(expr) = &stmt.kind {
+ if matches!(**expr, ast::Expr { kind: ast::ExprKind::MacCall(..), .. }) {
+ self.cx.current_expansion.is_trailing_mac = true;
+ }
+ }
+
// The placeholder expander gives ids to statements, so we avoid folding the id here.
// We don't use `assign_id!` - it will be called when we visit statement's contents
// (e.g. an expression, item, or local)
let ast::Stmt { id, kind, span } = stmt;
- noop_flat_map_stmt_kind(kind, self)
+ let res = noop_flat_map_stmt_kind(kind, self)
.into_iter()
.map(|kind| ast::Stmt { id, kind, span })
- .collect()
+ .collect();
+
+ self.cx.current_expansion.is_trailing_mac = false;
+ res
}
fn visit_block(&mut self, block: &mut P<Block>) {
let span = item.span;
match item.kind {
- ast::ItemKind::MacCall(..) => {
+ ast::ItemKind::MacCall(ref mac) => {
+ self.check_attributes(&attrs, &mac);
item.attrs = attrs;
- self.check_attributes(&item.attrs);
item.and_then(|item| match item.kind {
ItemKind::MacCall(mac) => {
self.collect_bang(mac, span, AstFragmentKind::Items).make_items()
}
match item.kind {
- ast::AssocItemKind::MacCall(..) => {
- self.check_attributes(&item.attrs);
+ ast::AssocItemKind::MacCall(ref mac) => {
+ self.check_attributes(&item.attrs, &mac);
item.and_then(|item| match item.kind {
ast::AssocItemKind::MacCall(mac) => self
.collect_bang(mac, item.span, AstFragmentKind::TraitItems)
}
match item.kind {
- ast::AssocItemKind::MacCall(..) => {
- self.check_attributes(&item.attrs);
+ ast::AssocItemKind::MacCall(ref mac) => {
+ self.check_attributes(&item.attrs, &mac);
item.and_then(|item| match item.kind {
ast::AssocItemKind::MacCall(mac) => self
.collect_bang(mac, item.span, AstFragmentKind::ImplItems)
}
match foreign_item.kind {
- ast::ForeignItemKind::MacCall(..) => {
- self.check_attributes(&foreign_item.attrs);
+ ast::ForeignItemKind::MacCall(ref mac) => {
+ self.check_attributes(&foreign_item.attrs, &mac);
foreign_item.and_then(|item| match item.kind {
ast::ForeignItemKind::MacCall(mac) => self
.collect_bang(mac, item.span, AstFragmentKind::ForeignItems)