cc: early returns are special

This commit is contained in:
Oliver 'ker' Schneider 2016-04-23 14:30:05 +02:00
parent 30f7651271
commit bf4221c51a
2 changed files with 84 additions and 12 deletions

View file

@ -9,7 +9,7 @@ use syntax::ast::Attribute;
use syntax::attr; use syntax::attr;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::{in_macro, LimitStack, span_help_and_lint}; use utils::{in_macro, LimitStack, span_help_and_lint, paths, match_type};
/// **What it does:** This lint checks for methods with high cyclomatic complexity /// **What it does:** This lint checks for methods with high cyclomatic complexity
/// ///
@ -57,15 +57,26 @@ impl CyclomaticComplexity {
match_arms: 0, match_arms: 0,
divergence: 0, divergence: 0,
short_circuits: 0, short_circuits: 0,
returns: 0,
tcx: &cx.tcx, tcx: &cx.tcx,
}; };
helper.visit_block(block); helper.visit_block(block);
let CCHelper { match_arms, divergence, short_circuits, .. } = helper; let CCHelper { match_arms, divergence, short_circuits, returns, .. } = helper;
let ret_ty = cx.tcx.node_id_to_type(block.id);
if cc + divergence < match_arms + short_circuits { let ret_adjust = if match_type(cx, ret_ty, &paths::RESULT) {
report_cc_bug(cx, cc, match_arms, divergence, short_circuits, span); returns
} else { } else {
let rust_cc = cc + divergence - match_arms - short_circuits; returns / 2
};
if cc + divergence < match_arms + short_circuits {
report_cc_bug(cx, cc, match_arms, divergence, short_circuits, ret_adjust, span);
} else {
let mut rust_cc = cc + divergence - match_arms - short_circuits;
// prevent degenerate cases where unreachable code contains `return` statements
if rust_cc >= ret_adjust {
rust_cc -= ret_adjust;
}
if rust_cc > self.limit.limit() { if rust_cc > self.limit.limit() {
span_help_and_lint(cx, span_help_and_lint(cx,
CYCLOMATIC_COMPLEXITY, CYCLOMATIC_COMPLEXITY,
@ -109,6 +120,7 @@ impl LateLintPass for CyclomaticComplexity {
struct CCHelper<'a, 'tcx: 'a> { struct CCHelper<'a, 'tcx: 'a> {
match_arms: u64, match_arms: u64,
divergence: u64, divergence: u64,
returns: u64,
short_circuits: u64, // && and || short_circuits: u64, // && and ||
tcx: &'a ty::TyCtxt<'tcx>, tcx: &'a ty::TyCtxt<'tcx>,
} }
@ -142,31 +154,34 @@ impl<'a, 'b, 'tcx> Visitor<'a> for CCHelper<'b, 'tcx> {
_ => (), _ => (),
} }
} }
ExprRet(_) => self.returns += 1,
_ => walk_expr(self, e), _ => walk_expr(self, e),
} }
} }
} }
#[cfg(feature="debugging")] #[cfg(feature="debugging")]
fn report_cc_bug(_: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, span: Span) { fn report_cc_bug(_: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, returns: u64, span: Span) {
span_bug!(span, span_bug!(span,
"Clippy encountered a bug calculating cyclomatic complexity: cc = {}, arms = {}, \ "Clippy encountered a bug calculating cyclomatic complexity: cc = {}, arms = {}, \
div = {}, shorts = {}. Please file a bug report.", div = {}, shorts = {}, returns = {}. Please file a bug report.",
cc, cc,
narms, narms,
div, div,
shorts); shorts,
returns);
} }
#[cfg(not(feature="debugging"))] #[cfg(not(feature="debugging"))]
fn report_cc_bug(cx: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, span: Span) { fn report_cc_bug(cx: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, returns: u64, span: Span) {
if cx.current_level(CYCLOMATIC_COMPLEXITY) != Level::Allow { if cx.current_level(CYCLOMATIC_COMPLEXITY) != Level::Allow {
cx.sess().span_note_without_error(span, cx.sess().span_note_without_error(span,
&format!("Clippy encountered a bug calculating cyclomatic complexity \ &format!("Clippy encountered a bug calculating cyclomatic complexity \
(hide this message with `#[allow(cyclomatic_complexity)]`): cc \ (hide this message with `#[allow(cyclomatic_complexity)]`): cc \
= {}, arms = {}, div = {}, shorts = {}. Please file a bug report.", = {}, arms = {}, div = {}, shorts = {}, returns = {}. Please file a bug report.",
cc, cc,
narms, narms,
div, div,
shorts)); shorts,
returns));
} }
} }

View file

@ -315,3 +315,60 @@ fn mcarton_sees_all() {
panic!("meh"); panic!("meh");
panic!("möh"); panic!("möh");
} }
#[cyclomatic_complexity = "0"]
fn try() -> Result<i32, &'static str> { //~ ERROR: cyclomatic complexity of 1
match 5 {
5 => Ok(5),
_ => return Err("bla"),
}
}
#[cyclomatic_complexity = "0"]
fn try_again() -> Result<i32, &'static str> { //~ ERROR: cyclomatic complexity of 1
let _ = try!(Ok(42));
let _ = try!(Ok(43));
let _ = try!(Ok(44));
let _ = try!(Ok(45));
let _ = try!(Ok(46));
let _ = try!(Ok(47));
let _ = try!(Ok(48));
let _ = try!(Ok(49));
match 5 {
5 => Ok(5),
_ => return Err("bla"),
}
}
#[cyclomatic_complexity = "0"]
fn early() -> Result<i32, &'static str> { //~ ERROR: cyclomatic complexity of 1
return Ok(5);
return Ok(5);
return Ok(5);
return Ok(5);
return Ok(5);
return Ok(5);
return Ok(5);
return Ok(5);
return Ok(5);
}
#[cyclomatic_complexity = "0"]
fn early_ret() -> i32 { //~ ERROR: cyclomatic complexity of 8
let a = if true { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
let a = if a < 99 { 42 } else { return 0; };
match 5 {
5 => 5,
_ => return 6,
}
}