Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check_attr doesn't check every HIR node type that can carry attributes. #80048

Closed
eddyb opened this issue Dec 15, 2020 · 3 comments
Closed

check_attr doesn't check every HIR node type that can carry attributes. #80048

eddyb opened this issue Dec 15, 2020 · 3 comments
Assignees
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) C-bug Category: This is a bug.

Comments

@eddyb
Copy link
Member

eddyb commented Dec 15, 2020

The check_attr visitor here is missing several methods:

impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::OnlyBodies(self.tcx.hir())
}
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
let target = Target::from_item(item);
self.check_attributes(
item.hir_id,
item.attrs,
&item.span,
target,
Some(ItemLike::Item(item)),
);
intravisit::walk_item(self, item)
}
fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
let target = Target::from_trait_item(trait_item);
self.check_attributes(trait_item.hir_id, &trait_item.attrs, &trait_item.span, target, None);
intravisit::walk_trait_item(self, trait_item)
}
fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
let target = Target::from_foreign_item(f_item);
self.check_attributes(
f_item.hir_id,
&f_item.attrs,
&f_item.span,
target,
Some(ItemLike::ForeignItem(f_item)),
);
intravisit::walk_foreign_item(self, f_item)
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
let target = target_from_impl_item(self.tcx, impl_item);
self.check_attributes(impl_item.hir_id, &impl_item.attrs, &impl_item.span, target, None);
intravisit::walk_impl_item(self, impl_item)
}
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
// When checking statements ignore expressions, they will be checked later.
if let hir::StmtKind::Local(ref l) = stmt.kind {
self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
}
intravisit::walk_stmt(self, stmt)
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
let target = match expr.kind {
hir::ExprKind::Closure(..) => Target::Closure,
_ => Target::Expression,
};
self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
intravisit::walk_expr(self, expr)
}
fn visit_variant(
&mut self,
variant: &'tcx hir::Variant<'tcx>,
generics: &'tcx hir::Generics<'tcx>,
item_id: HirId,
) {
self.check_attributes(variant.id, variant.attrs, &variant.span, Target::Variant, None);
intravisit::walk_variant(self, variant, generics, item_id)
}
}

A quick search (for attrs:) in the HIR definition finds these additional types that can contain attributes (they're also missing from the hir::Target enum, which should probably be renamed to AttrTarget but that's besides the point):

(click to see definition code snippets)

(summary: GenericParam, MacroDef, Arm, Param, StructField)

This isn't necessary a bug for some of them, as there is special validation elsewhere, e.g.:

"allow, cfg, cfg_attr, deny, \
forbid, and warn are the only allowed built-in attributes in function parameters",

But it's not as uniform and future-proof as it could be. It might even be worth having a contextual attribute Target, and handling all attributes on visit_attr, or a hybrid solution that ICEs when visit_attr comes across an attribute not already handled.

And I did end up finding at least one bug, this compiles without any warnings (try on playground):

fn main() {
    match 5 {
        #[inline] _ => {}
    }
}

cc @varkor @davidtwco @petrochenkov

@eddyb eddyb added A-attributes Area: Attributes (`#[…]`, `#![…]`) C-bug Category: This is a bug. labels Dec 15, 2020
@rylev
Copy link
Member

rylev commented Jan 7, 2021

@rustbot claim

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 14, 2021
Visit more targets when validating attributes

This begins to address rust-lang#80048, allowing for additional validation of attributes.

There are more refactorings that can be done, though I think they should be tackled in additional PRs:
* ICE when a builtin attribute is encountered that is not checked
* Move some of the attr checking done `ast_validation` into `rustc_passes`
  * note that this requires a bit of additional refactoring, especially of extern items which currently parse attributes (and thus are a part of the AST) but do not possess attributes in their HIR representation.
* Rename `Target` to `AttributeTarget`
* Refactor attribute validation completely to go through `Visitor::visit_attribute`.
  * This would require at a minimum passing `Target` into this method which might be too big of a refactoring to be worth it.
  * It's also likely not possible to do all the validation this way as some validation requires knowing what other attributes a target has.

r? `@davidtwco`
@eddyb
Copy link
Member Author

eddyb commented Mar 2, 2021

After #80641 and #80920, what's left? cc @oli-obk

@oli-obk
Copy link
Contributor

oli-obk commented Mar 2, 2021

Looked over those PRs, yea, this is fixed now.

@oli-obk oli-obk closed this as completed Mar 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

3 participants