From 5cab48a214c7a2738c46674cc6d1376800594ee2 Mon Sep 17 00:00:00 2001 From: xd009642 Date: Sat, 15 Aug 2020 17:09:58 +0100 Subject: [PATCH] Move to source analysis object --- src/source_analysis/attributes.rs | 54 +-- src/source_analysis/expressions.rs | 529 ++++++++++++++++------------- src/source_analysis/items.rs | 370 +++++++++++--------- src/source_analysis/macros.rs | 80 ++--- src/source_analysis/mod.rs | 134 ++++---- src/source_analysis/statements.rs | 101 +++--- src/source_analysis/tests.rs | 280 +++++++++------ 7 files changed, 846 insertions(+), 702 deletions(-) diff --git a/src/source_analysis/attributes.rs b/src/source_analysis/attributes.rs index cc021c21b5..282ddc3301 100644 --- a/src/source_analysis/attributes.rs +++ b/src/source_analysis/attributes.rs @@ -1,39 +1,41 @@ use crate::source_analysis::prelude::*; use syn::*; -pub(crate) fn check_attr_list( - attrs: &[Attribute], - ctx: &Context, - analysis: &mut LineAnalysis, -) -> bool { - let mut check_cover = true; - for attr in attrs { - analysis.ignore_tokens(attr); - if let Ok(x) = attr.parse_meta() { - if check_cfg_attr(&x) { - check_cover = false; - } else if x.path().is_ident("cfg") { - match x { - Meta::List(ref ml) => { - let mut skip = false; - for c in &ml.nested { - if let NestedMeta::Meta(Meta::Path(ref i)) = c { - skip |= i.is_ident("test") && ctx.config.ignore_tests; +impl SourceAnalysis { + pub(crate) fn check_attr_list(&mut self, attrs: &[Attribute], ctx: &Context) -> bool { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + let mut check_cover = true; + for attr in attrs { + analysis.ignore_tokens(attr); + if let Ok(x) = attr.parse_meta() { + if check_cfg_attr(&x) { + check_cover = false; + } else if x.path().is_ident("cfg") { + match x { + Meta::List(ref ml) => { + let mut skip = false; + for c in &ml.nested { + if let NestedMeta::Meta(Meta::Path(ref i)) = c { + skip |= i.is_ident("test") && ctx.config.ignore_tests; + } + } + if skip { + check_cover = false; } } - if skip { - check_cover = false; - } + _ => {} } - _ => {} } } + if !check_cover { + break; + } } - if !check_cover { - break; - } + check_cover } - check_cover } pub(crate) fn check_cfg_attr(attr: &Meta) -> bool { diff --git a/src/source_analysis/expressions.rs b/src/source_analysis/expressions.rs index 9637c7ae84..1bccdc8000 100644 --- a/src/source_analysis/expressions.rs +++ b/src/source_analysis/expressions.rs @@ -2,310 +2,357 @@ use crate::source_analysis::prelude::*; use std::collections::HashSet; use syn::{punctuated::Pair, punctuated::Punctuated, spanned::Spanned, token::Comma, *}; -pub(crate) fn process_expr(expr: &Expr, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - let res = match *expr { - Expr::Macro(ref m) => visit_macro_call(&m.mac, ctx, analysis), - Expr::Struct(ref s) => visit_struct_expr(&s, analysis), - Expr::Unsafe(ref u) => visit_unsafe_block(&u, ctx, analysis), - Expr::Call(ref c) => visit_callable(&c, ctx, analysis), - Expr::MethodCall(ref m) => visit_methodcall(&m, ctx, analysis), - Expr::Match(ref m) => visit_match(&m, ctx, analysis), - Expr::Block(ref b) => visit_expr_block(&b, ctx, analysis), - Expr::If(ref i) => visit_if(&i, ctx, analysis), - Expr::While(ref w) => visit_while(&w, ctx, analysis), - Expr::ForLoop(ref f) => visit_for(&f, ctx, analysis), - Expr::Loop(ref l) => visit_loop(&l, ctx, analysis), - Expr::Return(ref r) => visit_return(&r, ctx, analysis), - Expr::Closure(ref c) => visit_closure(&c, ctx, analysis), - Expr::Path(ref p) => visit_path(&p, analysis), - Expr::Let(ref l) => visit_let(&l, ctx, analysis), - // don't try to compute unreachability on other things - _ => SubResult::Ok, - }; - if let SubResult::Unreachable = res { - analysis.ignore_tokens(expr); +impl SourceAnalysis { + pub(crate) fn process_expr(&mut self, expr: &Expr, ctx: &Context) -> SubResult { + let res = match *expr { + Expr::Macro(ref m) => self.visit_macro_call(&m.mac, ctx), + Expr::Struct(ref s) => self.visit_struct_expr(&s, ctx), + Expr::Unsafe(ref u) => self.visit_unsafe_block(&u, ctx), + Expr::Call(ref c) => self.visit_callable(&c, ctx), + Expr::MethodCall(ref m) => self.visit_methodcall(&m, ctx), + Expr::Match(ref m) => self.visit_match(&m, ctx), + Expr::Block(ref b) => self.visit_expr_block(&b, ctx), + Expr::If(ref i) => self.visit_if(&i, ctx), + Expr::While(ref w) => self.visit_while(&w, ctx), + Expr::ForLoop(ref f) => self.visit_for(&f, ctx), + Expr::Loop(ref l) => self.visit_loop(&l, ctx), + Expr::Return(ref r) => self.visit_return(&r, ctx), + Expr::Closure(ref c) => self.visit_closure(&c, ctx), + Expr::Path(ref p) => self.visit_path(&p, ctx), + Expr::Let(ref l) => self.visit_let(&l, ctx), + // don't try to compute unreachability on other things + _ => SubResult::Ok, + }; + if let SubResult::Unreachable = res { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(expr); + } + res } - res -} -fn visit_let(let_expr: &ExprLet, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - let check_cover = check_attr_list(&let_expr.attrs, ctx, analysis); - if check_cover { - for a in &let_expr.attrs { - analysis.ignore_tokens(a); - } - let spn = let_expr.span(); - let base_line = let_expr.let_token.span().start().line; - if base_line != spn.end().line { - // Now check the other lines - let lhs = let_expr.pat.span(); - if lhs.start().line != base_line { - analysis.logical_lines.insert(lhs.start().line, base_line); + fn visit_let(&mut self, let_expr: &ExprLet, ctx: &Context) -> SubResult { + let check_cover = self.check_attr_list(&let_expr.attrs, ctx); + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + if check_cover { + for a in &let_expr.attrs { + analysis.ignore_tokens(a); } - let eq = let_expr.eq_token.span(); - if eq.start().line != base_line { - analysis.logical_lines.insert(eq.start().line, base_line); + let spn = let_expr.span(); + let base_line = let_expr.let_token.span().start().line; + if base_line != spn.end().line { + // Now check the other lines + let lhs = let_expr.pat.span(); + if lhs.start().line != base_line { + analysis.logical_lines.insert(lhs.start().line, base_line); + } + let eq = let_expr.eq_token.span(); + if eq.start().line != base_line { + analysis.logical_lines.insert(eq.start().line, base_line); + } + if let_expr.expr.span().start().line != base_line { + analysis + .logical_lines + .insert(let_expr.expr.span().start().line, base_line); + } + self.process_expr(&let_expr.expr, ctx); } - if let_expr.expr.span().start().line != base_line { - analysis - .logical_lines - .insert(let_expr.expr.span().start().line, base_line); + } else { + analysis.ignore_tokens(let_expr); + } + SubResult::Ok + } + + fn visit_path(&mut self, path: &ExprPath, ctx: &Context) -> SubResult { + if let Some(PathSegment { + ref ident, + arguments: _, + }) = path.path.segments.last() + { + if ident == "unreachable_unchecked" { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(path); + return SubResult::Unreachable; } - process_expr(&let_expr.expr, ctx, analysis); } - } else { - analysis.ignore_tokens(let_expr); + SubResult::Ok } - SubResult::Ok -} -fn visit_path(path: &ExprPath, analysis: &mut LineAnalysis) -> SubResult { - if let Some(PathSegment { - ref ident, - arguments: _, - }) = path.path.segments.last() - { - if ident == "unreachable_unchecked" { - analysis.ignore_tokens(path); - return SubResult::Unreachable; + fn visit_return(&mut self, ret: &ExprReturn, ctx: &Context) -> SubResult { + let check_cover = self.check_attr_list(&ret.attrs, ctx); + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + if check_cover { + for a in &ret.attrs { + analysis.ignore_tokens(a); + } + } else { + analysis.ignore_tokens(ret); } + SubResult::Ok } - SubResult::Ok -} -fn visit_return(ret: &ExprReturn, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - let check_cover = check_attr_list(&ret.attrs, ctx, analysis); - if check_cover { - for a in &ret.attrs { - analysis.ignore_tokens(a); + fn visit_expr_block(&mut self, block: &ExprBlock, ctx: &Context) -> SubResult { + if self.check_attr_list(&block.attrs, ctx) { + self.visit_block(&block.block, ctx) + } else { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(block); + SubResult::Ok } - } else { - analysis.ignore_tokens(ret); } - SubResult::Ok -} -fn visit_expr_block(block: &ExprBlock, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if check_attr_list(&block.attrs, ctx, analysis) { - visit_block(&block.block, ctx, analysis) - } else { - analysis.ignore_tokens(block); - SubResult::Ok + fn visit_block(&mut self, block: &Block, ctx: &Context) -> SubResult { + if let SubResult::Unreachable = self.process_statements(&block.stmts, ctx) { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(block); + SubResult::Unreachable + } else { + SubResult::Ok + } } -} -fn visit_block(block: &Block, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if let SubResult::Unreachable = process_statements(&block.stmts, ctx, analysis) { - analysis.ignore_tokens(block); - SubResult::Unreachable - } else { + fn visit_closure(&mut self, closure: &ExprClosure, ctx: &Context) -> SubResult { + self.process_expr(&closure.body, ctx); + // Even if a closure is "unreachable" it might be part of a chained method + // call and I don't want that propagating up. SubResult::Ok } -} -fn visit_closure(closure: &ExprClosure, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - process_expr(&closure.body, ctx, analysis); - // Even if a closure is "unreachable" it might be part of a chained method - // call and I don't want that propagating up. - SubResult::Ok -} - -fn visit_match(mat: &ExprMatch, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - // a match with some arms is unreachable iff all its arms are unreachable - let mut reachable_arm = false; - for arm in &mat.arms { - if check_attr_list(&arm.attrs, ctx, analysis) { - if let SubResult::Ok = process_expr(&arm.body, ctx, analysis) { - reachable_arm = true + fn visit_match(&mut self, mat: &ExprMatch, ctx: &Context) -> SubResult { + // a match with some arms is unreachable iff all its arms are unreachable + let mut reachable_arm = false; + for arm in &mat.arms { + if self.check_attr_list(&arm.attrs, ctx) { + if let SubResult::Ok = self.process_expr(&arm.body, ctx) { + reachable_arm = true + } + } else { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(arm); } + } + if !reachable_arm { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(mat); + SubResult::Unreachable } else { - analysis.ignore_tokens(arm); + SubResult::Ok } } - if !reachable_arm { - analysis.ignore_tokens(mat); - SubResult::Unreachable - } else { - SubResult::Ok - } -} -fn visit_if(if_block: &ExprIf, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - // an if expression is unreachable iff both its branches are unreachable - let mut reachable_arm = false; + fn visit_if(&mut self, if_block: &ExprIf, ctx: &Context) -> SubResult { + // an if expression is unreachable iff both its branches are unreachable + let mut reachable_arm = false; - process_expr(&if_block.cond, ctx, analysis); + self.process_expr(&if_block.cond, ctx); - if let SubResult::Ok = visit_block(&if_block.then_branch, ctx, analysis) { - reachable_arm = true; - } - if let Some((_, ref else_block)) = if_block.else_branch { - if let SubResult::Ok = process_expr(&else_block, ctx, analysis) { + if let SubResult::Ok = self.visit_block(&if_block.then_branch, ctx) { reachable_arm = true; } - } else { - // an empty else branch is reachable - reachable_arm = true; - } - if !reachable_arm { - analysis.ignore_tokens(if_block); - SubResult::Unreachable - } else { - SubResult::Ok + if let Some((_, ref else_block)) = if_block.else_branch { + if let SubResult::Ok = self.process_expr(&else_block, ctx) { + reachable_arm = true; + } + } else { + // an empty else branch is reachable + reachable_arm = true; + } + if !reachable_arm { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(if_block); + SubResult::Unreachable + } else { + SubResult::Ok + } } -} -fn visit_while(whl: &ExprWhile, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if check_attr_list(&whl.attrs, ctx, analysis) { - // a while block is unreachable iff its body is - if let SubResult::Unreachable = visit_block(&whl.body, ctx, analysis) { - analysis.ignore_tokens(whl); - SubResult::Unreachable + fn visit_while(&mut self, whl: &ExprWhile, ctx: &Context) -> SubResult { + if self.check_attr_list(&whl.attrs, ctx) { + // a while block is unreachable iff its body is + if let SubResult::Unreachable = self.visit_block(&whl.body, ctx) { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(whl); + SubResult::Unreachable + } else { + SubResult::Ok + } } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(whl); SubResult::Ok } - } else { - analysis.ignore_tokens(whl); - SubResult::Ok } -} -fn visit_for(for_loop: &ExprForLoop, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if check_attr_list(&for_loop.attrs, ctx, analysis) { - // a for block is unreachable iff its body is - if let SubResult::Unreachable = visit_block(&for_loop.body, ctx, analysis) { - analysis.ignore_tokens(for_loop); - SubResult::Unreachable + fn visit_for(&mut self, for_loop: &ExprForLoop, ctx: &Context) -> SubResult { + if self.check_attr_list(&for_loop.attrs, ctx) { + // a for block is unreachable iff its body is + if let SubResult::Unreachable = self.visit_block(&for_loop.body, ctx) { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(for_loop); + SubResult::Unreachable + } else { + SubResult::Ok + } } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(for_loop); SubResult::Ok } - } else { - analysis.ignore_tokens(for_loop); - SubResult::Ok } -} -fn visit_loop(loopex: &ExprLoop, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if check_attr_list(&loopex.attrs, ctx, analysis) { - // a loop block is unreachable iff its body is - if let SubResult::Unreachable = visit_block(&loopex.body, ctx, analysis) { - analysis.ignore_tokens(loopex); - SubResult::Unreachable + fn visit_loop(&mut self, loopex: &ExprLoop, ctx: &Context) -> SubResult { + if self.check_attr_list(&loopex.attrs, ctx) { + // a loop block is unreachable iff its body is + if let SubResult::Unreachable = self.visit_block(&loopex.body, ctx) { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(loopex); + SubResult::Unreachable + } else { + SubResult::Ok + } } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(loopex); SubResult::Ok } - } else { - analysis.ignore_tokens(loopex); - SubResult::Ok } -} -fn get_coverable_args(args: &Punctuated) -> HashSet { - let mut lines: HashSet = HashSet::new(); - for a in args.iter() { - match *a { - Expr::Lit(_) => {} - _ => { - for i in get_line_range(a) { - lines.insert(i); - } + fn visit_callable(&mut self, call: &ExprCall, ctx: &Context) -> SubResult { + if self.check_attr_list(&call.attrs, ctx) { + if !call.args.is_empty() { + let lines = get_coverable_args(&call.args); + let lines = get_line_range(call) + .filter(|x| !lines.contains(&x)) + .collect::>(); + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.add_to_ignore(&lines); } + self.process_expr(&call.func, ctx); + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(call); } + // We can't guess if a callable would actually be unreachable + SubResult::Ok } - lines -} -fn visit_callable(call: &ExprCall, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if check_attr_list(&call.attrs, ctx, analysis) { - if !call.args.is_empty() { - let lines = get_coverable_args(&call.args); - let lines = get_line_range(call) + fn visit_methodcall(&mut self, meth: &ExprMethodCall, ctx: &Context) -> SubResult { + if self.check_attr_list(&meth.attrs, ctx) { + self.process_expr(&meth.receiver, ctx); + let start = meth.receiver.span().end().line + 1; + let range = get_line_range(meth); + let lines = get_coverable_args(&meth.args); + let lines = (start..range.end) .filter(|x| !lines.contains(&x)) .collect::>(); + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); analysis.add_to_ignore(&lines); + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(meth); } - process_expr(&call.func, ctx, analysis); - } else { - analysis.ignore_tokens(call); - } - // We can't guess if a callable would actually be unreachable - SubResult::Ok -} - -fn visit_methodcall( - meth: &ExprMethodCall, - ctx: &Context, - analysis: &mut LineAnalysis, -) -> SubResult { - if check_attr_list(&meth.attrs, ctx, analysis) { - process_expr(&meth.receiver, ctx, analysis); - let start = meth.receiver.span().end().line + 1; - let range = get_line_range(meth); - let lines = get_coverable_args(&meth.args); - let lines = (start..range.end) - .filter(|x| !lines.contains(&x)) - .collect::>(); - analysis.add_to_ignore(&lines); - } else { - analysis.ignore_tokens(meth); + // We can't guess if a method would actually be unreachable + SubResult::Ok } - // We can't guess if a method would actually be unreachable - SubResult::Ok -} -fn visit_unsafe_block( - unsafe_expr: &ExprUnsafe, - ctx: &Context, - analysis: &mut LineAnalysis, -) -> SubResult { - let u_line = unsafe_expr.unsafe_token.span().start().line; + fn visit_unsafe_block(&mut self, unsafe_expr: &ExprUnsafe, ctx: &Context) -> SubResult { + let u_line = unsafe_expr.unsafe_token.span().start().line; - let blk = &unsafe_expr.block; - if u_line != blk.brace_token.span.start().line || blk.stmts.is_empty() { - analysis.ignore_tokens(unsafe_expr.unsafe_token); - } else if let Some(ref first_stmt) = blk.stmts.get(0) { - let s = match **first_stmt { - Stmt::Local(ref l) => l.span(), - Stmt::Item(ref i) => i.span(), - Stmt::Expr(ref e) => e.span(), - Stmt::Semi(ref e, _) => e.span(), - }; - if u_line != s.start().line { + let blk = &unsafe_expr.block; + if u_line != blk.brace_token.span.start().line || blk.stmts.is_empty() { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(unsafe_expr.unsafe_token); + } else if let Some(ref first_stmt) = blk.stmts.get(0) { + let s = match **first_stmt { + Stmt::Local(ref l) => l.span(), + Stmt::Item(ref i) => i.span(), + Stmt::Expr(ref e) => e.span(), + Stmt::Semi(ref e, _) => e.span(), + }; + if u_line != s.start().line { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(unsafe_expr.unsafe_token); + } + if let SubResult::Unreachable = self.process_statements(&blk.stmts, ctx) { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(unsafe_expr); + return SubResult::Unreachable; + } + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); analysis.ignore_tokens(unsafe_expr.unsafe_token); + analysis.ignore_span(blk.brace_token.span); } - if let SubResult::Unreachable = process_statements(&blk.stmts, ctx, analysis) { - analysis.ignore_tokens(unsafe_expr); - return SubResult::Unreachable; + SubResult::Ok + } + + fn visit_struct_expr(&mut self, structure: &ExprStruct, ctx: &Context) -> SubResult { + let mut cover: HashSet = HashSet::new(); + for field in structure.fields.pairs() { + let first = match field { + Pair::Punctuated(t, _) => t, + Pair::End(t) => t, + }; + let span = match first.member { + Member::Named(ref i) => i.span(), + Member::Unnamed(ref i) => i.span, + }; + match first.expr { + Expr::Lit(_) | Expr::Path(_) => {} + _ => { + cover.insert(span.start().line); + } + } } - } else { - analysis.ignore_tokens(unsafe_expr.unsafe_token); - analysis.ignore_span(blk.brace_token.span); + let x = get_line_range(structure) + .filter(|x| !cover.contains(&x)) + .collect::>(); + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.add_to_ignore(&x); + // struct expressions are never unreachable by themselves + SubResult::Ok } - SubResult::Ok } - -fn visit_struct_expr(structure: &ExprStruct, analysis: &mut LineAnalysis) -> SubResult { - let mut cover: HashSet = HashSet::new(); - for field in structure.fields.pairs() { - let first = match field { - Pair::Punctuated(t, _) => t, - Pair::End(t) => t, - }; - let span = match first.member { - Member::Named(ref i) => i.span(), - Member::Unnamed(ref i) => i.span, - }; - match first.expr { - Expr::Lit(_) | Expr::Path(_) => {} +fn get_coverable_args(args: &Punctuated) -> HashSet { + let mut lines: HashSet = HashSet::new(); + for a in args.iter() { + match *a { + Expr::Lit(_) => {} _ => { - cover.insert(span.start().line); + for i in get_line_range(a) { + lines.insert(i); + } } } } - let x = get_line_range(structure) - .filter(|x| !cover.contains(&x)) - .collect::>(); - analysis.add_to_ignore(&x); - // struct expressions are never unreachable by themselves - SubResult::Ok + lines } diff --git a/src/source_analysis/items.rs b/src/source_analysis/items.rs index 954e0820dc..e2fca91690 100644 --- a/src/source_analysis/items.rs +++ b/src/source_analysis/items.rs @@ -3,64 +3,76 @@ use quote::ToTokens; use std::path::PathBuf; use syn::{spanned::Spanned, *}; -pub(crate) fn process_items( - items: &[Item], - ctx: &Context, - analysis: &mut LineAnalysis, -) -> SubResult { - let mut res = SubResult::Ok; - for item in items.iter() { - match *item { - Item::ExternCrate(ref i) => analysis.ignore_tokens(i), - Item::Use(ref i) => analysis.ignore_tokens(i), - Item::Mod(ref i) => visit_mod(&i, analysis, ctx), - Item::Fn(ref i) => visit_fn(&i, analysis, ctx), - Item::Struct(ref i) => { - analysis.ignore_tokens(i); - } - Item::Enum(ref i) => { - analysis.ignore_tokens(i); - } - Item::Union(ref i) => { - analysis.ignore_tokens(i); - } - Item::Trait(ref i) => visit_trait(&i, analysis, ctx), - Item::Impl(ref i) => visit_impl(&i, analysis, ctx), - Item::Macro(ref i) => { - if let SubResult::Unreachable = visit_macro_call(&i.mac, ctx, analysis) { - res = SubResult::Unreachable; +impl SourceAnalysis { + pub(crate) fn process_items(&mut self, items: &[Item], ctx: &Context) -> SubResult { + let mut res = SubResult::Ok; + for item in items.iter() { + match *item { + Item::ExternCrate(ref i) => { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(i); } + Item::Use(ref i) => { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(i); + } + Item::Mod(ref i) => self.visit_mod(&i, ctx), + Item::Fn(ref i) => self.visit_fn(&i, ctx), + Item::Struct(ref i) => { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(i); + } + Item::Enum(ref i) => { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(i); + } + Item::Union(ref i) => { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(i); + } + Item::Trait(ref i) => self.visit_trait(&i, ctx), + Item::Impl(ref i) => self.visit_impl(&i, ctx), + Item::Macro(ref i) => { + if let SubResult::Unreachable = self.visit_macro_call(&i.mac, ctx) { + res = SubResult::Unreachable; + } + } + Item::Const(ref c) => { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(c); + } + _ => {} } - Item::Const(ref c) => { - analysis.ignore_tokens(c); - } - _ => {} } + res } - res -} -fn visit_mod(module: &ItemMod, analysis: &mut LineAnalysis, ctx: &Context) { - analysis.ignore_tokens(module.mod_token); - let mut check_insides = true; - for attr in &module.attrs { - if let Ok(x) = attr.parse_meta() { - if check_cfg_attr(&x) { - analysis.ignore_tokens(module); - if let Some((ref braces, _)) = module.content { - analysis.ignore_span(braces.span); - } - check_insides = false; - break; - } else if ctx.config.ignore_tests && x.path().is_ident("cfg") { - if let Meta::List(ref ml) = x { - for nested in &ml.nested { - if let NestedMeta::Meta(Meta::Path(ref i)) = *nested { - if i.is_ident("test") { - check_insides = false; - analysis.ignore_tokens(module.mod_token); - if let Some((ref braces, _)) = module.content { - analysis.ignore_span(braces.span); + fn visit_mod(&mut self, module: &ItemMod, ctx: &Context) { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + analysis.ignore_tokens(module.mod_token); + let mut check_insides = true; + for attr in &module.attrs { + if let Ok(x) = attr.parse_meta() { + if check_cfg_attr(&x) { + analysis.ignore_tokens(module); + if let Some((ref braces, _)) = module.content { + analysis.ignore_span(braces.span); + } + check_insides = false; + break; + } else if ctx.config.ignore_tests && x.path().is_ident("cfg") { + if let Meta::List(ref ml) = x { + for nested in &ml.nested { + if let NestedMeta::Meta(Meta::Path(ref i)) = *nested { + if i.is_ident("test") { + check_insides = false; + analysis.ignore_tokens(module.mod_token); + if let Some((ref braces, _)) = module.content { + analysis.ignore_span(braces.span); + } } } } @@ -68,142 +80,164 @@ fn visit_mod(module: &ItemMod, analysis: &mut LineAnalysis, ctx: &Context) { } } } - } - if check_insides { - if let Some((_, ref items)) = module.content { - process_items(items, ctx, analysis); - } - } else { - // Get the file or directory name of the module - let mut p = if let Some(parent) = ctx.file.parent() { - parent.join(module.ident.to_string()) + if check_insides { + if let Some((_, ref items)) = module.content { + self.process_items(items, ctx); + } } else { - PathBuf::from(module.ident.to_string()) - }; - if !p.exists() { - p.set_extension("rs"); + // Get the file or directory name of the module + let mut p = if let Some(parent) = ctx.file.parent() { + parent.join(module.ident.to_string()) + } else { + PathBuf::from(module.ident.to_string()) + }; + if !p.exists() { + p.set_extension("rs"); + } + ctx.ignore_mods.borrow_mut().insert(p); } - ctx.ignore_mods.borrow_mut().insert(p); } -} -fn visit_fn(func: &ItemFn, analysis: &mut LineAnalysis, ctx: &Context) { - let mut test_func = false; - let mut ignored_attr = false; - let mut is_inline = false; - let mut ignore_span = false; - for attr in &func.attrs { - if let Ok(x) = attr.parse_meta() { - let id = x.path(); - if id.is_ident("test") { - test_func = true; - } else if id.is_ident("derive") { - analysis.ignore_span(attr.bracket_token.span); - } else if id.is_ident("inline") { - is_inline = true; - } else if id.is_ident("ignore") { - ignored_attr = true; - } else if check_cfg_attr(&x) { - ignore_span = true; - break; + fn visit_fn(&mut self, func: &ItemFn, ctx: &Context) { + let mut test_func = false; + let mut ignored_attr = false; + let mut is_inline = false; + let mut ignore_span = false; + for attr in &func.attrs { + if let Ok(x) = attr.parse_meta() { + let id = x.path(); + if id.is_ident("test") { + test_func = true; + } else if id.is_ident("derive") { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_span(attr.bracket_token.span); + } else if id.is_ident("inline") { + is_inline = true; + } else if id.is_ident("ignore") { + ignored_attr = true; + } else if check_cfg_attr(&x) { + ignore_span = true; + break; + } } } - } - if ignore_span - || (test_func && ctx.config.ignore_tests) - || (ignored_attr && !ctx.config.run_ignored) - { - analysis.ignore_tokens(func); - } else { - if is_inline { - // We need to force cover! - analysis.cover_span(func.block.brace_token.span, Some(ctx.file_contents)); - } - if let SubResult::Unreachable = process_statements(&func.block.stmts, ctx, analysis) { - // if the whole body of the function is unreachable, that means the function itself - // cannot be called, so is unreachable as a whole + if ignore_span + || (test_func && ctx.config.ignore_tests) + || (ignored_attr && !ctx.config.run_ignored) + { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); analysis.ignore_tokens(func); - return; + } else { + if is_inline { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + // We need to force cover! + analysis.cover_span(func.block.brace_token.span, Some(ctx.file_contents)); + } + if let SubResult::Unreachable = self.process_statements(&func.block.stmts, ctx) { + // if the whole body of the function is unreachable, that means the function itself + // cannot be called, so is unreachable as a whole + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(func); + return; + } + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + visit_generics(&func.sig.generics, analysis); + let line_number = func.sig.fn_token.span().start().line; + analysis.ignore.remove(&Lines::Line(line_number)); + // Ignore multiple lines of fn decl + let decl_start = func.sig.fn_token.span().start().line + 1; + let stmts_start = func.block.span().start().line; + let lines = (decl_start..(stmts_start + 1)).collect::>(); + analysis.add_to_ignore(&lines); } - visit_generics(&func.sig.generics, analysis); - let line_number = func.sig.fn_token.span().start().line; - analysis.ignore.remove(&Lines::Line(line_number)); - // Ignore multiple lines of fn decl - let decl_start = func.sig.fn_token.span().start().line + 1; - let stmts_start = func.block.span().start().line; - let lines = (decl_start..(stmts_start + 1)).collect::>(); - analysis.add_to_ignore(&lines); } -} -fn visit_trait(trait_item: &ItemTrait, analysis: &mut LineAnalysis, ctx: &Context) { - let check_cover = check_attr_list(&trait_item.attrs, ctx, analysis); - if check_cover { - for item in &trait_item.items { - if let TraitItem::Method(ref i) = *item { - if check_attr_list(&i.attrs, ctx, analysis) { - if let Some(ref block) = i.default { - analysis - .cover_token_stream(item.into_token_stream(), Some(ctx.file_contents)); - visit_generics(&i.sig.generics, analysis); - analysis - .ignore - .remove(&Lines::Line(i.sig.span().start().line)); + fn visit_trait(&mut self, trait_item: &ItemTrait, ctx: &Context) { + let check_cover = self.check_attr_list(&trait_item.attrs, ctx); + if check_cover { + for item in &trait_item.items { + if let TraitItem::Method(ref i) = *item { + if self.check_attr_list(&i.attrs, ctx) { + if let Some(ref block) = i.default { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.cover_token_stream( + item.into_token_stream(), + Some(ctx.file_contents), + ); + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + visit_generics(&i.sig.generics, analysis); + analysis + .ignore + .remove(&Lines::Line(i.sig.span().start().line)); - // Ignore multiple lines of fn decl - let decl_start = i.sig.fn_token.span().start().line + 1; - let stmts_start = block.span().start().line; - let lines = (decl_start..(stmts_start + 1)).collect::>(); - analysis.add_to_ignore(&lines); + // Ignore multiple lines of fn decl + let decl_start = i.sig.fn_token.span().start().line + 1; + let stmts_start = block.span().start().line; + let lines = (decl_start..(stmts_start + 1)).collect::>(); + analysis.add_to_ignore(&lines); + } + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(i); + } + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + for a in &i.attrs { + analysis.ignore_tokens(a); } - } else { - analysis.ignore_tokens(i); - } - for a in &i.attrs { - analysis.ignore_tokens(a); } } + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + visit_generics(&trait_item.generics, analysis); + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(trait_item); } - visit_generics(&trait_item.generics, analysis); - } else { - analysis.ignore_tokens(trait_item); } -} -fn visit_impl(impl_blk: &ItemImpl, analysis: &mut LineAnalysis, ctx: &Context) { - let check_cover = check_attr_list(&impl_blk.attrs, ctx, analysis); - if check_cover { - for item in &impl_blk.items { - if let ImplItem::Method(ref i) = *item { - if check_attr_list(&i.attrs, ctx, analysis) { - analysis.cover_token_stream(i.into_token_stream(), Some(ctx.file_contents)); - if let SubResult::Unreachable = - process_statements(&i.block.stmts, ctx, analysis) - { - // if the body of this method is unreachable, this means that the method - // cannot be called, and is unreachable - analysis.ignore_tokens(i); - return; - } + fn visit_impl(&mut self, impl_blk: &ItemImpl, ctx: &Context) { + let check_cover = self.check_attr_list(&impl_blk.attrs, ctx); + if check_cover { + for item in &impl_blk.items { + if let ImplItem::Method(ref i) = *item { + if self.check_attr_list(&i.attrs, ctx) { + { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis + .cover_token_stream(i.into_token_stream(), Some(ctx.file_contents)); + } + if let SubResult::Unreachable = self.process_statements(&i.block.stmts, ctx) + { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + // if the body of this method is unreachable, this means that the method + // cannot be called, and is unreachable + analysis.ignore_tokens(i); + return; + } - visit_generics(&i.sig.generics, analysis); - analysis.ignore.remove(&Lines::Line(i.span().start().line)); + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + visit_generics(&i.sig.generics, analysis); + analysis.ignore.remove(&Lines::Line(i.span().start().line)); - // Ignore multiple lines of fn decl - let decl_start = i.sig.fn_token.span().start().line + 1; - let stmts_start = i.block.span().start().line; - let lines = (decl_start..(stmts_start + 1)).collect::>(); - analysis.add_to_ignore(&lines); - } else { - analysis.ignore_tokens(item); - } - for a in &i.attrs { - analysis.ignore_tokens(a); + // Ignore multiple lines of fn decl + let decl_start = i.sig.fn_token.span().start().line + 1; + let stmts_start = i.block.span().start().line; + let lines = (decl_start..(stmts_start + 1)).collect::>(); + analysis.add_to_ignore(&lines); + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(item); + } + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + for a in &i.attrs { + analysis.ignore_tokens(a); + } } } + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + visit_generics(&impl_blk.generics, analysis); + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(impl_blk); } - visit_generics(&impl_blk.generics, analysis); - } else { - analysis.ignore_tokens(impl_blk); } } diff --git a/src/source_analysis/macros.rs b/src/source_analysis/macros.rs index 839fd0244f..1ff61ae15f 100644 --- a/src/source_analysis/macros.rs +++ b/src/source_analysis/macros.rs @@ -6,6 +6,47 @@ use std::collections::HashSet; use std::ops::Range; use syn::{spanned::Spanned, *}; +impl SourceAnalysis { + pub(crate) fn visit_macro_call(&mut self, mac: &Macro, ctx: &Context) -> SubResult { + let analysis = self + .lines + .entry(ctx.file.to_path_buf()) + .or_insert_with(line_analysis_missing); + let mut skip = false; + if let Some(PathSegment { + ref ident, + arguments: _, + }) = mac.path.segments.last() + { + let ident_s = ident.to_string(); + let unreachable = ident == "unreachable"; + let standard_ignores = + ident == "unimplemented" || ident == "include" || ident == "cfg" || ident == "todo"; + let ignore_panic = ctx.config.ignore_panics + && (ident == "panic" + || ident_s.starts_with("assert") + || ident_s.starts_with("debug_assert")); + if standard_ignores || ignore_panic || unreachable { + analysis.ignore_tokens(mac); + skip = true; + } + if unreachable { + return SubResult::Unreachable; + } + } + if !skip { + let start = mac.span().start().line + 1; + let range = get_line_range(mac); + let lines = process_mac_args(&mac.tokens); + let lines = (start..range.end) + .filter(|x| !lines.contains(&x)) + .collect::>(); + analysis.add_to_ignore(&lines); + } + SubResult::Ok + } +} + pub(crate) fn get_line_range(tokens: T) -> Range where T: ToTokens, @@ -30,45 +71,6 @@ where } } -pub(crate) fn visit_macro_call( - mac: &Macro, - ctx: &Context, - analysis: &mut LineAnalysis, -) -> SubResult { - let mut skip = false; - if let Some(PathSegment { - ref ident, - arguments: _, - }) = mac.path.segments.last() - { - let ident_s = ident.to_string(); - let unreachable = ident == "unreachable"; - let standard_ignores = - ident == "unimplemented" || ident == "include" || ident == "cfg" || ident == "todo"; - let ignore_panic = ctx.config.ignore_panics - && (ident == "panic" - || ident_s.starts_with("assert") - || ident_s.starts_with("debug_assert")); - if standard_ignores || ignore_panic || unreachable { - analysis.ignore_tokens(mac); - skip = true; - } - if unreachable { - return SubResult::Unreachable; - } - } - if !skip { - let start = mac.span().start().line + 1; - let range = get_line_range(mac); - let lines = process_mac_args(&mac.tokens); - let lines = (start..range.end) - .filter(|x| !lines.contains(&x)) - .collect::>(); - analysis.add_to_ignore(&lines); - } - SubResult::Ok -} - fn process_mac_args(tokens: &TokenStream) -> HashSet { let mut cover: HashSet = HashSet::new(); // IntoIter not implemented for &TokenStream. diff --git a/src/source_analysis/mod.rs b/src/source_analysis/mod.rs index 8632f33eac..849539de86 100644 --- a/src/source_analysis/mod.rs +++ b/src/source_analysis/mod.rs @@ -1,9 +1,8 @@ use crate::branching::BranchAnalysis; use crate::config::{Config, RunType}; use crate::path_utils::{get_source_walker, is_source_file}; -use items::process_items; use lazy_static::lazy_static; -use log::trace; +use log::{error, trace}; use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use regex::Regex; @@ -26,9 +25,7 @@ mod tests; pub(crate) mod prelude { pub(crate) use super::*; pub(crate) use attributes::*; - pub(crate) use expressions::*; pub(crate) use macros::*; - pub(crate) use statements::*; } /// Enumeration representing which lines to ignore @@ -195,6 +192,11 @@ impl LineAnalysis { } } +pub(crate) fn line_analysis_missing() -> LineAnalysis { + error!("LineAnalysis entry not found for file. Creating default"); + LineAnalysis::new() +} + pub struct SourceAnalysis { pub lines: HashMap, pub branches: Option, @@ -213,21 +215,18 @@ impl SourceAnalysis { } } + pub fn get_line_analysis(&mut self, path: PathBuf) -> &mut LineAnalysis { + self.lines.entry(path).or_insert_with(LineAnalysis::new) + } + pub fn get_analysis(config: &Config) -> Self { let mut result = Self::new(config.branch_coverage); - let mut ignored_files: HashSet = HashSet::new(); let root = config.root(); for e in get_source_walker(config) { if !ignored_files.contains(e.path()) { - analyse_package( - e.path(), - &root, - &config, - &mut result.lines, - &mut ignored_files, - ); + result.analyse_package(e.path(), &root, &config, &mut ignored_files); } else { let mut analysis = LineAnalysis::new(); analysis.ignore_all(); @@ -246,6 +245,63 @@ impl SourceAnalysis { result } + /// Analyses a package of the target crate. + fn analyse_package( + &mut self, + path: &Path, + root: &Path, + config: &Config, + filtered_files: &mut HashSet, + ) { + if let Some(file) = path.to_str() { + let skip_cause_test = config.ignore_tests && path.starts_with(root.join("tests")); + let skip_cause_example = path.starts_with(root.join("examples")) + && !config.run_types.contains(&RunType::Examples); + if !(skip_cause_test || skip_cause_example) { + let file = File::open(file); + if let Ok(mut file) = file { + let mut content = String::new(); + let _ = file.read_to_string(&mut content); + let file = parse_file(&content); + if let Ok(file) = file { + let mut analysis = LineAnalysis::new(); + let ctx = Context { + config, + file_contents: &content, + file: path, + ignore_mods: RefCell::new(HashSet::new()), + }; + + find_ignorable_lines(&content, &mut analysis); + self.lines.insert(path.to_path_buf(), analysis); + self.process_items(&file.items, &ctx); + //process_items(&file.items, &ctx, &mut analysis); + // Check there's no conflict! + //self.lines.insert(path.to_path_buf(), analysis); + + let mut ignored_files = ctx.ignore_mods.into_inner(); + for f in ignored_files.drain() { + if f.is_file() { + filtered_files.insert(f); + } else { + let walker = WalkDir::new(f).into_iter(); + for e in walker.filter_map(|e| e.ok()).filter(|e| is_source_file(e)) + { + filtered_files.insert(e.path().to_path_buf()); + } + } + } + // This could probably be done with the DWARF if I could find a discriminating factor + // to why lib.rs:1 shows up as a real line! + if path.ends_with("src/lib.rs") { + analyse_lib_rs(path, &mut self.lines); + } + } + } + } + } + } + /// Printout a debug summary of the results of source analysis if debug logging /// is enabled pub fn debug_printout(&self, config: &Config) { @@ -320,60 +376,6 @@ pub(crate) struct Context<'a> { ignore_mods: RefCell>, } -/// Analyses a package of the target crate. -fn analyse_package( - path: &Path, - root: &Path, - config: &Config, - result: &mut HashMap, - filtered_files: &mut HashSet, -) { - if let Some(file) = path.to_str() { - let skip_cause_test = config.ignore_tests && path.starts_with(root.join("tests")); - let skip_cause_example = path.starts_with(root.join("examples")) - && !config.run_types.contains(&RunType::Examples); - if !(skip_cause_test || skip_cause_example) { - let file = File::open(file); - if let Ok(mut file) = file { - let mut content = String::new(); - let _ = file.read_to_string(&mut content); - let file = parse_file(&content); - if let Ok(file) = file { - let mut analysis = LineAnalysis::new(); - let ctx = Context { - config, - file_contents: &content, - file: path, - ignore_mods: RefCell::new(HashSet::new()), - }; - - find_ignorable_lines(&content, &mut analysis); - process_items(&file.items, &ctx, &mut analysis); - // Check there's no conflict! - result.insert(path.to_path_buf(), analysis); - - let mut ignored_files = ctx.ignore_mods.into_inner(); - for f in ignored_files.drain() { - if f.is_file() { - filtered_files.insert(f); - } else { - let walker = WalkDir::new(f).into_iter(); - for e in walker.filter_map(|e| e.ok()).filter(|e| is_source_file(e)) { - filtered_files.insert(e.path().to_path_buf()); - } - } - } - // This could probably be done with the DWARF if I could find a discriminating factor - // to why lib.rs:1 shows up as a real line! - if path.ends_with("src/lib.rs") { - analyse_lib_rs(path, result); - } - } - } - } - } -} - /// Finds lines from the raw string which are ignorable. /// These are often things like close braces, semi colons that may regiser as /// false positives. diff --git a/src/source_analysis/statements.rs b/src/source_analysis/statements.rs index af2e5f3385..0277dd6193 100644 --- a/src/source_analysis/statements.rs +++ b/src/source_analysis/statements.rs @@ -1,62 +1,63 @@ use crate::source_analysis::prelude::*; use syn::{spanned::Spanned, *}; -pub(crate) fn process_statements( - stmts: &[Stmt], - ctx: &Context, - analysis: &mut LineAnalysis, -) -> SubResult { - // in a list of statements, if any of them is unreachable, the whole list is - // unreachable - let mut unreachable = false; - for stmt in stmts.iter() { - let res = match *stmt { - Stmt::Item(ref i) => process_items(&[i.clone()], ctx, analysis), - Stmt::Expr(ref i) | Stmt::Semi(ref i, _) => process_expr(&i, ctx, analysis), - Stmt::Local(ref i) => process_local(&i, ctx, analysis), - }; - if let SubResult::Unreachable = res { - unreachable = true; +impl SourceAnalysis { + pub(crate) fn process_statements(&mut self, stmts: &[Stmt], ctx: &Context) -> SubResult { + // in a list of statements, if any of them is unreachable, the whole list is + // unreachable + let mut unreachable = false; + for stmt in stmts.iter() { + let res = match *stmt { + Stmt::Item(ref i) => self.process_items(&[i.clone()], ctx), + Stmt::Expr(ref i) | Stmt::Semi(ref i, _) => self.process_expr(&i, ctx), + Stmt::Local(ref i) => self.process_local(&i, ctx), + }; + if let SubResult::Unreachable = res { + unreachable = true; + } + } + // We must be in a block, the parent will handle marking the span as unreachable + if unreachable { + SubResult::Unreachable + } else { + SubResult::Ok } } - // We must be in a block, the parent will handle marking the span as unreachable - if unreachable { - SubResult::Unreachable - } else { - SubResult::Ok - } -} -fn process_local(local: &Local, ctx: &Context, analysis: &mut LineAnalysis) -> SubResult { - if let Some((eq, expr)) = &local.init { - let check_cover = check_attr_list(&local.attrs, ctx, analysis); - if check_cover { - for a in &local.attrs { - analysis.ignore_tokens(a); - } - let spn = local.span(); - let base_line = local.let_token.span().start().line; - if base_line != spn.end().line { - // Now check the other lines - let lhs = local.pat.span(); - if lhs.start().line != base_line { - analysis.logical_lines.insert(lhs.start().line, base_line); - } - let eq = eq.span(); - if eq.start().line != base_line { - analysis.logical_lines.insert(eq.start().line, base_line); + fn process_local(&mut self, local: &Local, ctx: &Context) -> SubResult { + if let Some((eq, expr)) = &local.init { + let check_cover = self.check_attr_list(&local.attrs, ctx); + if check_cover { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + for a in &local.attrs { + analysis.ignore_tokens(a); } - if expr.span().start().line != base_line { - analysis - .logical_lines - .insert(expr.span().start().line, base_line); + let spn = local.span(); + let base_line = local.let_token.span().start().line; + if base_line != spn.end().line { + // Now check the other lines + let lhs = local.pat.span(); + if lhs.start().line != base_line { + analysis.logical_lines.insert(lhs.start().line, base_line); + } + let eq = eq.span(); + if eq.start().line != base_line { + analysis.logical_lines.insert(eq.start().line, base_line); + } + if expr.span().start().line != base_line { + analysis + .logical_lines + .insert(expr.span().start().line, base_line); + } + std::mem::drop(analysis); + self.process_expr(&expr, ctx); } - process_expr(&expr, ctx, analysis); + } else { + let analysis = self.get_line_analysis(ctx.file.to_path_buf()); + analysis.ignore_tokens(local); } - } else { - analysis.ignore_tokens(local); } - } - SubResult::Ok + SubResult::Ok + } } diff --git a/src/source_analysis/tests.rs b/src/source_analysis/tests.rs index 895bfa04a1..eb35f36021 100644 --- a/src/source_analysis/tests.rs +++ b/src/source_analysis/tests.rs @@ -4,7 +4,7 @@ use syn::parse_file; #[test] fn logical_lines_let_bindings() { let config = Config::default(); - let mut lines = LineAnalysis::new(); + let mut analysis = SourceAnalysis::new(false); let ctx = Context { config: &config, file_contents: "fn foo() { @@ -16,7 +16,8 @@ fn logical_lines_let_bindings() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert_eq!(lines.logical_lines.get(&3).copied(), Some(2)); assert_eq!(lines.logical_lines.get(&4).copied(), Some(2)); @@ -38,10 +39,10 @@ fn logical_lines_let_bindings() { ignore_mods: RefCell::new(HashSet::new()), }; - let mut lines = LineAnalysis::new(); let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); - println!("Lines {:?}", lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.logical_lines.contains_key(&4)); assert!(!lines.logical_lines.contains_key(&5)); assert!(!lines.logical_lines.contains_key(&6)); @@ -67,7 +68,6 @@ fn line_analysis_works() { #[test] fn filter_str_literals() { - let mut lines = LineAnalysis::new(); let config = Config::default(); let ctx = Context { config: &config, @@ -80,7 +80,9 @@ fn filter_str_literals() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.len() > 1); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); @@ -97,13 +99,13 @@ fn filter_str_literals() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - let mut lines = LineAnalysis::new(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.len() > 1); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: " @@ -118,14 +120,15 @@ fn filter_str_literals() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(5))); } #[test] fn filter_struct_members() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[derive(Debug)]\npub struct Struct {\npub i: i32,\nj:String,\n}", @@ -133,7 +136,9 @@ fn filter_struct_members() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.len() > 3); assert!(lines.ignore.contains(&Lines::Line(1))); @@ -147,7 +152,9 @@ fn filter_struct_members() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.is_empty()); assert!(lines.ignore.contains(&Lines::Line(3))); @@ -156,7 +163,6 @@ fn filter_struct_members() { #[test] fn filter_enum_members() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[derive(Debug)]\npub enum E {\nI1,\nI2(u32),\nI3{\nx:u32,\n},\n}", @@ -164,7 +170,9 @@ fn filter_enum_members() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.len() > 3); assert!(lines.ignore.contains(&Lines::Line(3))); @@ -177,7 +185,6 @@ fn filter_enum_members() { #[test] fn filter_struct_consts() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct T{x:String, y:i32} @@ -191,7 +198,9 @@ fn filter_struct_consts() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(5))); } @@ -199,7 +208,6 @@ fn filter_struct_consts() { #[test] fn filter_unreachable_unchecked() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn test() { @@ -209,14 +217,15 @@ fn filter_unreachable_unchecked() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); } #[test] fn filter_loop_attr() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn test() { @@ -234,7 +243,9 @@ fn filter_loop_attr() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(5))); assert!(lines.ignore.contains(&Lines::Line(8))); @@ -250,11 +261,11 @@ fn filter_mods() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - let mut lines = LineAnalysis::new(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(3))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "mod foo;", @@ -262,10 +273,11 @@ fn filter_mods() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(1))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "mod foo{}", @@ -273,14 +285,15 @@ fn filter_mods() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(1))); } #[test] fn filter_macros() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "\n\nfn unused() {\nunimplemented!();\n}", @@ -288,12 +301,13 @@ fn filter_macros() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); // Braces should be ignored so number could be higher assert!(!lines.ignore.is_empty()); assert!(lines.ignore.contains(&Lines::Line(4))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "\n\nfn unused() {\nunreachable!();\n}", @@ -301,11 +315,12 @@ fn filter_macros() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.is_empty()); assert!(lines.ignore.contains(&Lines::Line(4))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unreachable_match(x: u32) -> u32 { @@ -319,10 +334,11 @@ fn filter_macros() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(5))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unused() {\nprintln!(\"text\");\n}", @@ -330,7 +346,9 @@ fn filter_macros() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(2))); } @@ -340,7 +358,6 @@ fn filter_tests() { let mut igconfig = Config::default(); igconfig.ignore_tests = true; - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[cfg(test)] @@ -352,7 +369,9 @@ fn filter_tests() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(4))); let ctx = Context { @@ -367,8 +386,9 @@ fn filter_tests() { ignore_mods: RefCell::new(HashSet::new()), }; - let mut lines = LineAnalysis::new(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(4))); let ctx = Context { @@ -378,8 +398,9 @@ fn filter_tests() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - let mut lines = LineAnalysis::new(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(2))); assert!(!lines.ignore.contains(&Lines::Line(3))); @@ -389,8 +410,9 @@ fn filter_tests() { file: Path::new(""), ignore_mods: RefCell::new(HashSet::new()), }; - let mut lines = LineAnalysis::new(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3))); } @@ -400,7 +422,6 @@ fn filter_test_utilities() { let mut config = Config::default(); config.ignore_tests = true; - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "trait Thing { @@ -413,14 +434,15 @@ fn filter_test_utilities() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "trait Thing { @@ -433,7 +455,9 @@ fn filter_test_utilities() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(3))); assert!(!lines.ignore.contains(&Lines::Line(4))); } @@ -441,7 +465,6 @@ fn filter_test_utilities() { #[test] fn filter_where() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn boop() -> T where T:Default { @@ -451,10 +474,11 @@ fn filter_where() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(1))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn boop() -> T @@ -465,10 +489,11 @@ fn filter_where() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "trait foof { @@ -481,14 +506,15 @@ fn filter_where() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(3))); } #[test] fn filter_derives() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[derive(Debug)]\nstruct T;", @@ -496,10 +522,11 @@ fn filter_derives() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(1))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "\n#[derive(Copy, Eq)]\nunion x { x:i32, y:f32}", @@ -507,14 +534,15 @@ fn filter_derives() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); } #[test] fn filter_unsafe() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unsafe_fn() {\n let x=1;\nunsafe {\nprintln!(\"{}\", x);\n}\n}", @@ -522,11 +550,12 @@ fn filter_unsafe() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(!lines.ignore.contains(&Lines::Line(4))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unsafe_fn() {\n let x=1;\nunsafe {println!(\"{}\", x);}\n}", @@ -534,14 +563,15 @@ fn filter_unsafe() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(3))); } #[test] fn cover_generic_impl_methods() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct GenericStruct(T); @@ -554,11 +584,12 @@ fn cover_generic_impl_methods() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.cover.contains(&3)); assert!(lines.cover.contains(&4)); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct GenericStruct{v:Vec} @@ -573,14 +604,15 @@ fn cover_generic_impl_methods() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.cover.contains(&5)); } #[test] fn cover_default_trait_methods() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "trait Thing { @@ -592,7 +624,9 @@ fn cover_default_trait_methods() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.cover.contains(&2)); assert!(lines.cover.contains(&3)); } @@ -600,7 +634,6 @@ fn cover_default_trait_methods() { #[test] fn filter_method_args() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct Thing; @@ -628,7 +661,9 @@ fn filter_method_args() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(15))); assert!(!lines.ignore.contains(&Lines::Line(19))); } @@ -636,7 +671,6 @@ fn filter_method_args() { #[test] fn filter_use_statements() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "use std::collections::HashMap; @@ -645,7 +679,9 @@ fn filter_use_statements() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(1))); assert!(lines.ignore.contains(&Lines::Line(2))); } @@ -653,7 +689,6 @@ fn filter_use_statements() { #[test] fn include_inline_fns() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[inline] @@ -669,7 +704,9 @@ fn include_inline_fns() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.cover.contains(&3)); assert!(lines.cover.contains(&4)); assert!(!lines.cover.contains(&5)); @@ -681,7 +718,6 @@ fn include_inline_fns() { #[test] fn cover_callable_noargs() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn foo() { @@ -691,14 +727,15 @@ fn cover_callable_noargs() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(2))); } #[test] fn filter_closure_contents() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn inline_func() { @@ -710,14 +747,15 @@ fn filter_closure_contents() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(3))); } #[test] fn tarpaulin_skip_attr() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[cfg(not(tarpaulin_include))] @@ -744,7 +782,9 @@ fn tarpaulin_skip_attr() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(!lines.ignore.contains(&Lines::Line(7))); @@ -754,7 +794,6 @@ fn tarpaulin_skip_attr() { assert!(lines.ignore.contains(&Lines::Line(17))); assert!(lines.ignore.contains(&Lines::Line(18))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[cfg(not(tarpaulin_include))] @@ -773,7 +812,9 @@ fn tarpaulin_skip_attr() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(8))); @@ -783,7 +824,6 @@ fn tarpaulin_skip_attr() { #[test] fn tarpaulin_skip_trait_attrs() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "#[cfg(not(tarpaulin_include))] @@ -802,13 +842,14 @@ fn tarpaulin_skip_trait_attrs() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(8))); assert!(lines.ignore.contains(&Lines::Line(9))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "trait Foo { @@ -826,7 +867,9 @@ fn tarpaulin_skip_trait_attrs() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(2))); assert!(!lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(7))); @@ -836,7 +879,6 @@ fn tarpaulin_skip_trait_attrs() { #[test] fn tarpaulin_skip_impl_attrs() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct Foo; @@ -856,13 +898,14 @@ fn tarpaulin_skip_impl_attrs() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(5))); assert!(lines.ignore.contains(&Lines::Line(9))); assert!(lines.ignore.contains(&Lines::Line(10))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct Foo; @@ -882,7 +925,9 @@ fn tarpaulin_skip_impl_attrs() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(3))); assert!(!lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(9))); @@ -892,7 +937,6 @@ fn tarpaulin_skip_impl_attrs() { #[test] fn filter_block_contents() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unreachable_match(x: u32) -> u32 { @@ -909,7 +953,9 @@ fn filter_block_contents() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(5))); assert!(lines.ignore.contains(&Lines::Line(7))); } @@ -917,7 +963,6 @@ fn filter_block_contents() { #[test] fn filter_consts() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn boo() { @@ -927,14 +972,15 @@ fn filter_consts() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); } #[test] fn optional_panic_ignore() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unreachable_match(x: u32) -> u32 { @@ -950,14 +996,15 @@ fn optional_panic_ignore() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(!lines.ignore.contains(&Lines::Line(2))); assert!(!lines.ignore.contains(&Lines::Line(3))); assert!(!lines.ignore.contains(&Lines::Line(7))); let mut config = Config::default(); config.ignore_panics = true; - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn unreachable_match(x: u32) -> u32 { @@ -974,7 +1021,9 @@ fn optional_panic_ignore() { }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(7))); @@ -983,7 +1032,6 @@ fn optional_panic_ignore() { #[test] fn filter_nested_blocks() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn block() { @@ -1007,14 +1055,15 @@ fn filter_nested_blocks() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(9))); } #[test] fn filter_multi_line_decls() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn print_it(x:u32, @@ -1026,11 +1075,12 @@ fn filter_multi_line_decls() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "struct Boo; @@ -1045,11 +1095,12 @@ fn filter_multi_line_decls() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(5))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "trait Boo { @@ -1063,7 +1114,9 @@ fn filter_multi_line_decls() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); } @@ -1071,7 +1124,6 @@ fn filter_multi_line_decls() { #[test] fn unreachable_propagate() { let config = Config::default(); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "enum Void {} @@ -1083,13 +1135,14 @@ fn unreachable_propagate() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(5))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn foo() { @@ -1108,7 +1161,9 @@ fn unreachable_propagate() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(3))); assert!(lines.ignore.contains(&Lines::Line(4))); assert!(lines.ignore.contains(&Lines::Line(5))); @@ -1116,7 +1171,6 @@ fn unreachable_propagate() { assert!(lines.ignore.contains(&Lines::Line(7))); assert!(lines.ignore.contains(&Lines::Line(8))); - let mut lines = LineAnalysis::new(); let ctx = Context { config: &config, file_contents: "fn test_unreachable() { @@ -1130,7 +1184,9 @@ fn unreachable_propagate() { ignore_mods: RefCell::new(HashSet::new()), }; let parser = parse_file(ctx.file_contents).unwrap(); - process_items(&parser.items, &ctx, &mut lines); + let mut analysis = SourceAnalysis::new(false); + analysis.process_items(&parser.items, &ctx); + let lines = analysis.get_line_analysis(ctx.file.to_path_buf()); assert!(lines.ignore.contains(&Lines::Line(1))); assert!(lines.ignore.contains(&Lines::Line(2))); assert!(lines.ignore.contains(&Lines::Line(3)));