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

#14902 Write a MIR lint for rooting analysis #20264

Closed
wants to merge 15 commits into from
Closed
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Skip checking subexpressions (temporary variables).

  • Loading branch information
mrowqa committed Mar 11, 2018
commit ea0c1d47cbaa451645a69d9c4c497ddf32f46990
@@ -8,7 +8,7 @@ use rustc::hir::map as ast_map;
use rustc::lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc::ty;
use syntax::{ast, codemap};
use utils::match_def_path;
use utils::{match_def_path, in_derive_expn};

declare_lint!(UNROOTED_MUST_ROOT, Deny,
"Warn and report usage of unrooted jsmanaged objects");
@@ -130,7 +130,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnrootedPass {
kind: visit::FnKind,
_decl: &'tcx hir::FnDecl,
_body: &'tcx hir::Body,
_span: codemap::Span,
span: codemap::Span,
id: ast::NodeId) {
let in_new_function = match kind {
visit::FnKind::ItemFn(n, _, _, _, _, _, _) |
@@ -142,17 +142,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnrootedPass {

let def_id = cx.tcx.hir.local_def_id(id);
let mir = cx.tcx.optimized_mir(def_id);
for (i, decl) in mir.local_decls.iter().enumerate() {
match i {
0 => if !in_new_function && is_unrooted_ty(cx, decl.ty, false) {
cx.span_lint(UNROOTED_MUST_ROOT, decl.source_info.span, "Function return type must be rooted.")
}
_ if i <= mir.arg_count => if is_unrooted_ty(cx, decl.ty, false) {

if !in_derive_expn(span) {
let ret_decl = mir.local_decls.iter().next().unwrap();
if !in_new_function && is_unrooted_ty(cx, ret_decl.ty, false) {
cx.span_lint(UNROOTED_MUST_ROOT, ret_decl.source_info.span, "Function return type must be rooted.")
}

for decl_ind in mir.args_iter() {
let decl = &mir.local_decls[decl_ind];
if is_unrooted_ty(cx, decl.ty, false) {
cx.span_lint(UNROOTED_MUST_ROOT, decl.source_info.span, "Function argument type must be rooted.")
}
_ => if is_unrooted_ty(cx, decl.ty, in_new_function) {
cx.span_lint(UNROOTED_MUST_ROOT, decl.source_info.span, "Type of binding/expression must be rooted.")
}
}
}

for decl_ind in mir.vars_iter() {
let decl = &mir.local_decls[decl_ind];
if is_unrooted_ty(cx, decl.ty, in_new_function) {
cx.span_lint(UNROOTED_MUST_ROOT, decl.source_info.span, "Type of binding/expression must be rooted.")
}
}
}
@@ -4,6 +4,7 @@

use rustc::hir::def_id::DefId;
use rustc::lint::LateContext;
use syntax::codemap::{ExpnFormat, Span};

/// check if a DefId's path matches the given absolute type path
/// usage e.g. with
@@ -26,3 +27,15 @@ pub fn match_def_path(cx: &LateContext, def_id: DefId, path: &[&str]) -> bool {
.zip(path)
.all(|(nm, p)| &*nm.as_interned_str() == *p)
}

pub fn in_derive_expn(span: Span) -> bool {
if let Some(i) = span.ctxt().outer().expn_info() {
if let ExpnFormat::MacroAttribute(n) = i.callee.format {
n.as_str().contains("derive")
} else {
false
}
} else {
false
}
}
@@ -8,32 +8,24 @@ pub mod unrooted_must_root {
#![feature(plugin)]
#![plugin(script_plugins)]
#[derive(Clone, Debug)] // derive should not be checked
#[must_root] struct Foo(i32);
#[must_root] struct Bar(Foo);
impl Foo {
fn new() -> Box<Foo> {
Box::new(Foo(0))
let ret = Box::new(Foo(0)); // Box should be allowed as a binding type within constructors
ret
}
fn new_with(x: i32) -> Foo {
Foo(x)
}
}
// MIR check gives this errors:
// ---- lib.rs - unrooted_must_root::ok (line 6) stdout ----
// error: Type of binding/expression must be rooted.
// --> lib.rs:15:18
// |
// 10 | Box::new(Foo(0))
// | ^^^^^^
// |
// = note: #[deny(unrooted_must_root)] on by default
fn foo1(_: &Foo) {}
fn foo2(_: &()) -> &Foo { unimplemented!() }
fn new_hack() -> Foo { Foo::new_with(0) }
fn new_foo() -> Foo { Foo::new_with(0) }
fn main() {}
```
@@ -81,6 +73,23 @@ pub mod unrooted_must_root {
*/
pub fn return_type() {}

/**
```
#![feature(plugin)]
#![plugin(script_plugins)]
#[must_root] struct Foo([i32; 42]);
fn foo(x: &Foo) -> i32 {
let y = Foo(x.0).0;
y[4]
}
fn main() {}
```
*/
pub fn allow_subexpression() {}

/**
```compile_fail
#![feature(plugin)]
@@ -96,7 +105,41 @@ pub mod unrooted_must_root {
fn main() {}
```
*/
pub fn expression() {}
pub fn local_var() {}

/**
```compile_fail
#![feature(plugin)]
#![plugin(script_plugins)]
#[must_root] struct Foo(i32);
fn foo(x: &Foo) -> i32 {
let y = Box::new(Foo(x.0 + 3));
y.0
}
fn main() {}
```
*/
pub fn ban_box() {}

/**
```
#![feature(plugin)]
#![plugin(script_plugins)]
#[must_root] struct Foo(i32);
fn foo(x: &Foo) -> i32 {
let y = &Box::new(Foo(x.0 + 3));
y.0
}
fn main() {}
```
*/
pub fn allow_box_ref() {}

/**
```compile_fail
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.