diff options
author | Eli Friedman <eli.friedman@gmail.com> | 2015-10-18 14:52:18 -0700 |
---|---|---|
committer | Eli Friedman <eli.friedman@gmail.com> | 2015-10-23 14:43:44 -0700 |
commit | 81ecf7824cae9629be020332c836205fedaae1a2 (patch) | |
tree | e4bf83a26f9850ab16905ca1556eb882fd6b658a /components/plugins/lints/unrooted_must_root.rs | |
parent | e3bcf7bab7d0340fc7ebd0e58a2cde34e534c1cf (diff) | |
download | servo-81ecf7824cae9629be020332c836205fedaae1a2.tar.gz servo-81ecf7824cae9629be020332c836205fedaae1a2.zip |
Make unrooted_must_root a bit more aggressive.
Basically, instead of trying to check for specific kinds of statements,
just check the types of all local variables.
Also included are some commented-out proposals for some slightly more
aggressive lints which might be useful (but trigger a little too
frequently at the moment).
Diffstat (limited to 'components/plugins/lints/unrooted_must_root.rs')
-rw-r--r-- | components/plugins/lints/unrooted_must_root.rs | 175 |
1 files changed, 85 insertions, 90 deletions
diff --git a/components/plugins/lints/unrooted_must_root.rs b/components/plugins/lints/unrooted_must_root.rs index bb599e845de..d6ecacc25dd 100644 --- a/components/plugins/lints/unrooted_must_root.rs +++ b/components/plugins/lints/unrooted_must_root.rs @@ -8,7 +8,7 @@ use rustc::middle::ty; use rustc_front::{hir, visit}; use syntax::attr::AttrMetaMethods; use syntax::{ast, codemap}; -use utils::{match_def_path, unsafe_context, in_derive_expn}; +use utils::{match_def_path, in_derive_expn}; declare_lint!(UNROOTED_MUST_ROOT, Deny, "Warn and report usage of unrooted jsmanaged objects"); @@ -30,15 +30,11 @@ declare_lint!(UNROOTED_MUST_ROOT, Deny, /// Structs which have their own mechanism of rooting their unrooted contents (e.g. `ScriptTask`) /// can be marked as `#[allow(unrooted_must_root)]`. Smart pointers which root their interior type /// can be marked as `#[allow_unrooted_interior]` -pub struct UnrootedPass { - in_new_function: bool -} +pub struct UnrootedPass; impl UnrootedPass { pub fn new() -> UnrootedPass { - UnrootedPass { - in_new_function: true - } + UnrootedPass } } @@ -55,9 +51,11 @@ fn is_unrooted_ty(cx: &LateContext, ty: &ty::TyS, in_new_function: bool) -> bool } else if cx.tcx.has_attr(did.did, "allow_unrooted_interior") { false } else if match_def_path(cx, did.did, &["core", "cell", "Ref"]) - || match_def_path(cx, did.did, &["core", "cell", "RefMut"]) { - // Ref and RefMut are borrowed pointers, okay to hold unrooted stuff - // since it will be rooted elsewhere + || match_def_path(cx, did.did, &["core", "cell", "RefMut"]) + || match_def_path(cx, did.did, &["core", "slice", "Iter"]) + || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "OccupiedEntry"]) + || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "VacantEntry"]) { + // Structures which are semantically similar to an &ptr. false } else { true @@ -99,6 +97,7 @@ impl LateLintPass for UnrootedPass { } } } + /// All enums containing #[must_root] types must be #[must_root] themselves fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _gen: &hir::Generics) { let ref map = cx.tcx.map; @@ -121,55 +120,55 @@ impl LateLintPass for UnrootedPass { } /// Function arguments that are #[must_root] types are not allowed fn check_fn(&mut self, cx: &LateContext, kind: visit::FnKind, decl: &hir::FnDecl, - block: &hir::Block, span: codemap::Span, id: ast::NodeId) { - match kind { + block: &hir::Block, span: codemap::Span, _id: ast::NodeId) { + let in_new_function = match kind { visit::FnKind::ItemFn(n, _, _, _, _, _) | - visit::FnKind::Method(n, _, _) if n.as_str() == "new" - || n.as_str().starts_with("new_") => { - self.in_new_function = true; - return; - }, - visit::FnKind::ItemFn(_, _, style, _, _, _) => match style { - hir::Unsafety::Unsafe => return, - _ => () - }, - _ => () - } - self.in_new_function = false; - - if unsafe_context(&cx.tcx.map, id) { - return; - } + visit::FnKind::Method(n, _, _) => { + n.as_str() == "new" || n.as_str().starts_with("new_") + } + visit::FnKind::Closure => return, + }; - match block.rules { - hir::DefaultBlock => { - for arg in &decl.inputs { - cx.tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id).map(|t| { - if is_unrooted_ty(cx, t, false) { - if in_derive_expn(cx, span) { - return; - } - cx.span_lint(UNROOTED_MUST_ROOT, arg.ty.span, "Type must be rooted") + if !in_new_function { + for arg in &decl.inputs { + cx.tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id).map(|t| { + if is_unrooted_ty(cx, t, false) { + if in_derive_expn(cx, span) { + return; } - }); - } - if let hir::Return(ref ty) = decl.output { - cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.id).map(|t| { - if is_unrooted_ty(cx, t, false) { - if in_derive_expn(cx, span) { - return; - } - cx.span_lint(UNROOTED_MUST_ROOT, ty.span, "Type must be rooted") + cx.span_lint(UNROOTED_MUST_ROOT, arg.ty.span, "Type must be rooted") + } + }); + } + if let hir::Return(ref ty) = decl.output { + cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.id).map(|t| { + if is_unrooted_ty(cx, t, false) { + if in_derive_expn(cx, span) { + return; } - }); - } + cx.span_lint(UNROOTED_MUST_ROOT, ty.span, "Type must be rooted") + } + }); } - _ => () // fn is `unsafe` } + + let mut visitor = FnDefVisitor { + cx: cx, + in_new_function: in_new_function, + }; + visit::walk_block(&mut visitor, block); } +} + +struct FnDefVisitor<'a, 'b: 'a, 'tcx: 'a+'b> { + cx: &'a LateContext<'b, 'tcx>, + in_new_function: bool, +} + +impl<'a, 'b: 'a, 'tcx: 'a+'b> visit::Visitor<'a> for FnDefVisitor<'a, 'b, 'tcx> { + fn visit_expr(&mut self, expr: &'a hir::Expr) { + let cx = self.cx; - /// Trait casts from #[must_root] types are not allowed - fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) { fn require_rooted(cx: &LateContext, in_new_function: bool, subexpr: &hir::Expr) { let ty = cx.tcx.expr_ty(&*subexpr); if is_unrooted_ty(cx, ty, in_new_function) { @@ -177,56 +176,52 @@ impl LateLintPass for UnrootedPass { subexpr.span, &format!("Expression of type {:?} must be rooted", ty)) } - }; + } match expr.node { + /// Trait casts from #[must_root] types are not allowed hir::ExprCast(ref subexpr, _) => require_rooted(cx, self.in_new_function, &*subexpr), + // This catches assignments... the main point of this would be to catch mutable + // references to `JS<T>`. + // FIXME: Enable this? Triggers on certain kinds of uses of DOMRefCell. + // hir::ExprAssign(_, ref rhs) => require_rooted(cx, self.in_new_function, &*rhs), + // This catches calls; basically, this enforces the constraint that only constructors + // can call other constructors. + // FIXME: Enable this? Currently triggers with constructs involving DOMRefCell, and + // constructs like Vec<JS<T>> and RootedVec<JS<T>>. + // hir::ExprCall(..) if !self.in_new_function => { + // require_rooted(cx, self.in_new_function, expr); + // } _ => { // TODO(pcwalton): Check generics with a whitelist of allowed generics. } } + + visit::walk_expr(self, expr); } - // Partially copied from rustc::middle::lint::builtin - // Catches `let` statements and assignments which store a #[must_root] value - // Expressions which return out of blocks eventually end up in a `let` or assignment - // statement or a function return (which will be caught when it is used elsewhere) - fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { - match s.node { - hir::StmtDecl(_, id) | - hir::StmtExpr(_, id) | - hir::StmtSemi(_, id) if unsafe_context(&cx.tcx.map, id) => { - return - }, - _ => () - }; + fn visit_pat(&mut self, pat: &'a hir::Pat) { + let cx = self.cx; - let expr = match s.node { - // Catch a `let` binding - hir::StmtDecl(ref decl, _) => match decl.node { - hir::DeclLocal(ref loc) => match loc.init { - Some(ref e) => &**e, - _ => return - }, - _ => return - }, - hir::StmtExpr(ref expr, _) => match expr.node { - // This catches deferred `let` statements - hir::ExprAssign(_, ref e) | - // Match statements allow you to bind onto the variable later in an arm - // We need not check arms individually since enum/struct fields are already - // linted in `check_struct_def` and `check_variant` - // (so there is no way of destructuring out a `#[must_root]` field) - hir::ExprMatch(ref e, _, _) => &**e, - _ => return - }, - _ => return - }; + if let hir::PatIdent(hir::BindByValue(_), _, _) = pat.node { + let ty = cx.tcx.pat_ty(pat); + if is_unrooted_ty(cx, ty, self.in_new_function) { + cx.span_lint(UNROOTED_MUST_ROOT, + pat.span, + &format!("Expression of type {:?} must be rooted", ty)) + } + } + + visit::walk_pat(self, pat); + } - let ty = cx.tcx.expr_ty(&*expr); - if is_unrooted_ty(cx, ty, self.in_new_function) { - cx.span_lint(UNROOTED_MUST_ROOT, expr.span, - &format!("Expression of type {:?} must be rooted", ty)) + fn visit_fn(&mut self, kind: visit::FnKind<'a>, decl: &'a hir::FnDecl, + block: &'a hir::Block, span: codemap::Span, _id: ast::NodeId) { + if kind == visit::FnKind::Closure { + visit::walk_fn(self, kind, decl, block, span); } } + + fn visit_foreign_item(&mut self, _: &'a hir::ForeignItem) {} + fn visit_ty(&mut self, _: &'a hir::Ty) { } } |