use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{AstLike, Block, Inline, ItemKind, 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,
+ },
+ );
+ }
}
}
}
}
+/// Wraps a call to `noop_visit_*` / `noop_flat_map_*`
+/// for an AST node that supports attributes
+/// (see the `Annotatable` enum)
+/// This method assigns a `NodeId`, and sets that `NodeId`
+/// as our current 'lint node id'. If a macro call is found
+/// inside this AST node, we will use this AST node's `NodeId`
+/// to emit lints associated with that macro (allowing
+/// `#[allow]` / `#[deny]` to be applied close to
+/// the macro invocation).
+///
+/// Do *not* call this for a macro AST node
+/// (e.g. `ExprKind::MacCall`) - we cannot emit lints
+/// at these AST nodes, since they are removed and
+/// replaced with the result of macro expansion.
+///
+/// All other `NodeId`s are assigned by `visit_id`.
+/// * `self` is the 'self' parameter for the current method,
+/// * `id` is a mutable reference to the `NodeId` field
+/// of the current AST node.
+/// * `closure` is a closure that executes the
+/// `noop_visit_*` / `noop_flat_map_*` method
+/// for the current AST node.
+macro_rules! assign_id {
+ ($self:ident, $id:expr, $closure:expr) => {{
+ let old_id = $self.cx.current_expansion.lint_node_id;
+ if $self.monotonic {
+ debug_assert_eq!(*$id, ast::DUMMY_NODE_ID);
+ let new_id = $self.cx.resolver.next_node_id();
+ *$id = new_id;
+ $self.cx.current_expansion.lint_node_id = new_id;
+ }
+ let ret = ($closure)();
+ $self.cx.current_expansion.lint_node_id = old_id;
+ ret
+ }};
+}
+
impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
self.cfg.configure_expr(expr);
}
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 {
- ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self));
+ assign_id!(self, &mut expr.id, || {
+ ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self));
+ });
expr
}
});
}
+ // This is needed in order to set `lint_node_id` for `let` statements
+ fn visit_local(&mut self, local: &mut P<Local>) {
+ assign_id!(self, &mut local.id, || noop_visit_local(local, self));
+ }
+
fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
let mut arm = configure!(self, arm);
.make_arms();
}
- noop_flat_map_arm(arm, self)
+ assign_id!(self, &mut arm.id, || noop_flat_map_arm(arm, self))
}
fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
.make_expr_fields();
}
- noop_flat_map_expr_field(field, self)
+ assign_id!(self, &mut field.id, || noop_flat_map_expr_field(field, self))
}
fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
.make_pat_fields();
}
- noop_flat_map_pat_field(fp, self)
+ assign_id!(self, &mut fp.id, || noop_flat_map_pat_field(fp, self))
}
fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
.make_params();
}
- noop_flat_map_param(p, self)
+ assign_id!(self, &mut p.id, || noop_flat_map_param(p, self))
}
fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
.make_field_defs();
}
- noop_flat_map_field_def(sf, self)
+ assign_id!(self, &mut sf.id, || noop_flat_map_field_def(sf, self))
}
fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
.make_variants();
}
- noop_flat_map_variant(variant, self)
+ assign_id!(self, &mut variant.id, || noop_flat_map_variant(variant, self))
}
fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
}
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())
} else {
- Some({
- noop_visit_expr(&mut expr, self);
- expr
+ assign_id!(self, &mut expr.id, || {
+ Some({
+ noop_visit_expr(&mut expr, self);
+ expr
+ })
})
}
})
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()
let orig_dir_ownership =
mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership);
- let result = noop_flat_map_item(item, self);
+ let result = assign_id!(self, &mut item.id, || noop_flat_map_item(item, self));
// Restore the module info.
self.cx.current_expansion.dir_ownership = orig_dir_ownership;
}
_ => {
item.attrs = attrs;
- noop_flat_map_item(item, self)
+ // The crate root is special - don't assign an ID to it.
+ if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::invalid()) {
+ assign_id!(self, &mut item.id, || noop_flat_map_item(item, self))
+ } else {
+ noop_flat_map_item(item, self)
+ }
}
}
}
}
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)
_ => unreachable!(),
})
}
- _ => noop_flat_map_assoc_item(item, self),
+ _ => {
+ assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self))
+ }
}
}
}
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)
_ => unreachable!(),
})
}
- _ => noop_flat_map_assoc_item(item, self),
+ _ => {
+ assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self))
+ }
}
}
}
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)
_ => unreachable!(),
})
}
- _ => noop_flat_map_foreign_item(foreign_item, self),
+ _ => {
+ assign_id!(self, &mut foreign_item.id, || noop_flat_map_foreign_item(
+ foreign_item,
+ self
+ ))
+ }
}
}
.make_generic_params();
}
- noop_flat_map_generic_param(param, self)
+ assign_id!(self, &mut param.id, || noop_flat_map_generic_param(param, self))
}
fn visit_id(&mut self, id: &mut ast::NodeId) {
- if self.monotonic {
- debug_assert_eq!(*id, ast::DUMMY_NODE_ID);
- *id = self.cx.resolver.next_node_id()
+ // We may have already assigned a `NodeId`
+ // by calling `assign_id`
+ if self.monotonic && *id == ast::DUMMY_NODE_ID {
+ *id = self.cx.resolver.next_node_id();
}
}
}