diff --git a/Cargo.toml b/Cargo.toml index 95de98e8ee5..e1308fb9cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ plugin = true [dev-dependencies] compiletest_rs = "*" +regex = "*" +regex_macros = "*" +lazy_static = "*" diff --git a/src/lib.rs b/src/lib.rs index 00d28deeed9..27d097118fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![feature(plugin_registrar, box_syntax)] #![feature(rustc_private, collections)] - #![allow(unused_imports)] #[macro_use] diff --git a/src/mut_mut.rs b/src/mut_mut.rs index b1e21def574..a4c2d3932a3 100644 --- a/src/mut_mut.rs +++ b/src/mut_mut.rs @@ -2,7 +2,7 @@ use syntax::ptr::P; use syntax::ast::*; use rustc::lint::{Context, LintPass, LintArray, Lint}; use rustc::middle::ty::{expr_ty, sty, ty_ptr, ty_rptr, mt}; -use syntax::codemap::ExpnInfo; +use syntax::codemap::{BytePos, ExpnInfo, MacroFormat, Span}; declare_lint!(pub MUT_MUT, Warn, "Warn on usage of double-mut refs, e.g. '&mut &mut ...'"); @@ -27,7 +27,7 @@ impl LintPass for MutMut { } fn check_expr_expd(cx: &Context, expr: &Expr, info: Option<&ExpnInfo>) { - if in_external_macro(info) { return; } + if in_macro(cx, info) { return; } fn unwrap_addr(expr : &Expr) -> Option<&Expr> { match expr.node { @@ -51,8 +51,14 @@ fn check_expr_expd(cx: &Context, expr: &Expr, info: Option<&ExpnInfo>) { }) } -fn in_external_macro(info: Option<&ExpnInfo>) -> bool { - info.map_or(false, |i| i.callee.span.is_some()) +fn in_macro(cx: &Context, opt_info: Option<&ExpnInfo>) -> bool { + opt_info.map_or(false, |info| { + info.callee.span.map_or(true, |span| { + cx.sess().codemap().span_to_snippet(span).ok().map_or(true, |code| + !code.starts_with("macro_rules") + ) + }) + }) } fn unwrap_mut(ty : &Ty) -> Option<&Ty> { diff --git a/tests/compile-fail/mut_mut.rs b/tests/compile-fail/mut_mut.rs index 65e3762e2c4..d7adc067740 100644 --- a/tests/compile-fail/mut_mut.rs +++ b/tests/compile-fail/mut_mut.rs @@ -1,11 +1,18 @@ #![feature(plugin)] #![plugin(clippy)] +//#![plugin(regex_macros)] +//extern crate regex; + #[deny(mut_mut)] fn fun(x : &mut &mut u32) -> bool { //~ERROR **x > 0 } +macro_rules! mut_ptr { + ($p:expr) => { &mut $p } +} + #[deny(mut_mut)] #[allow(unused_mut, unused_variables)] fn main() { @@ -22,4 +29,6 @@ fn main() { //~^^^^ ERROR ***y + **x; } + + let mut z = mut_ptr!(&mut 3u32); //~ERROR } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 04f3fc16b1b..6fcf71d38ad 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; fn run_mode(mode: &'static str) { let mut config = compiletest::default_config(); let cfg_mode = mode.parse().ok().expect("Invalid mode"); - config.target_rustcflags = Some("-L target/debug/".to_string()); + config.target_rustcflags = Some("-l regex_macros -L target/debug/".to_string()); config.mode = cfg_mode; config.src_base = PathBuf::from(format!("tests/{}", mode)); diff --git a/tests/run-pass.rs b/tests/run-pass.rs new file mode 100644 index 00000000000..32fdea3a340 --- /dev/null +++ b/tests/run-pass.rs @@ -0,0 +1,31 @@ +#![feature(plugin)] +#![plugin(clippy, regex_macros)] + +#[macro_use] +extern crate lazy_static; +extern crate regex; + +use std::collections::HashMap; + +#[test] +#[deny(mut_mut)] +fn test_regex() { + let pattern = regex!(r"^(?P[#]+)\s(?P.+)$"); + assert!(pattern.is_match("# headline")); +} + +#[test] +#[deny(mut_mut)] +#[allow(unused_variables, unused_mut)] +fn test_lazy_static() { + lazy_static! { + static ref MUT_MAP : HashMap<usize, &'static str> = { + let mut m = HashMap::new(); + let mut zero = &mut &mut "zero"; + m.insert(0, "zero"); + m + }; + static ref MUT_COUNT : usize = MUT_MAP.len(); + } + assert!(*MUT_COUNT == 1); +}