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

Complexity rule prefers and && over or || #8535

Closed
edi9999 opened this issue May 2, 2017 · 8 comments · Fixed by mono-js/mono-notifications#5, mono-js/mono-push#5 or terrajs/lib-starter#5 · May be fixed by ali8889/emerald-wallet#4 or DmytroSkrypnyk/test_bootstrap#6
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion bug ESLint is working incorrectly rule Relates to ESLint's core rules

Comments

@edi9999
Copy link

edi9999 commented May 2, 2017

  • ESLint Version: 3.19.0
  • Node Version: 7.7.3
  • npm Version: 4.5.0

What parser (default, Babel-ESLint, etc.) are you using? : babel-eslint

Please show your full configuration:

{
  "parser": "babel-eslint",
  "rules": {
    "complexity": [2, 1]
  }
}

What did you do? Please include the actual source code causing the issue.

let foo, bar, baz, bang;

function check(foo, bar, baz, bang) {
	if (!bar || !bang) {
		baz = foo;
	}
}
function check2(foo, bar, baz, bang) {
	if (!(bar && bang)) {
		baz = foo;
	}
}
function check3(foo, bar, baz, bang) {
	if (bar && bang) {
		baz = foo;
	}
}
function check4(foo, bar, baz, bang) {
	if (!bar && !bang) {
		baz = foo;
	}
}
function check5(foo, bar, baz, bang) {
	if (bar || bang) {
		baz = foo;
	}
}

check(foo, bar, baz, bang);
check2(foo, bar, baz, bang);
check3(foo, bar, baz, bang);
check4(foo, bar, baz, bang);
check5(foo, bar, baz, bang);

What did you expect to happen?

All functions check to check5 should have the same complexity

What actually happened? Please include the actual, raw output from ESLint.

   3:1  error  Function 'check' has a complexity of 3   complexity
   8:1  error  Function 'check2' has a complexity of 2  complexity
  13:1  error  Function 'check3' has a complexity of 2  complexity
  18:1  error  Function 'check4' has a complexity of 2  complexity
  23:1  error  Function 'check5' has a complexity of 3  complexity

It seems that || has more complexity than && in the current implementation, but I don't think that is true. All the check have a complexity of 3. (That is what http://jsmeter.info/axtsgb/1#results reports)

@eslintbot eslintbot added the triage An ESLint team member will look at this issue soon label May 2, 2017
@soda0289 soda0289 added bug ESLint is working incorrectly rule Relates to ESLint's core rules and removed triage An ESLint team member will look at this issue soon labels May 2, 2017
@soda0289
Copy link
Member

soda0289 commented May 2, 2017

There seems to be hardcoded logic in the rule that well only increase complexity for ||:
https://github.com/eslint/eslint/blob/master/lib/rules/complexity.js#L138-L140

So it is working as intended.

I think the logic is saying that since an || could always be rewritten as a nested if statement it is more complex than and && statement. This is not true in the worse case scenario, where each condition must be checked, and I believe they should have the same complexity. Or at the very least update the documentation.

@soda0289 soda0289 added the evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion label May 2, 2017
@not-an-aardvark
Copy link
Member

Closing because this seems to be working as intended.

@edi9999
Copy link
Author

edi9999 commented Jun 3, 2017

No it is not :

See @soda0289 statement :

This is not true in the worse case scenario, where each condition must be checked, and I believe they should have the same complexity

Also according to Wikipedia :

McCabe showed that the cyclomatic complexity of any structured program with only one entrance point and one exit point is equal to the number of decision points (i.e., "if" statements or conditional loops) contained in that program plus one. However, this is true only for decision points counted at the lowest, machine-level instructions. Decisions involving compound predicates like those found in high-level languages like IF cond1 AND cond2 THEN ... should be counted in terms of predicate variables involved, i.e. in this example one should count two decision points, because at machine level it is equivalent to IF cond1 THEN IF cond2 THEN ....[2][4]

So this means that to count the complexity, we need to change our program to remove the "or" and "and", that would be :

function check(foo, bar, baz, bang) {
	if (!bar || !bang) {
		baz = foo;
	}
}

=>

function check(foo, bar, baz, bang) {
	if (!bar) {
           baz = foo
        } 
        if (!bang) {
             baz = foo;
	}
}

Complexitiy = 1 + number of if statements = 1+2 = 3

For check3 :

function check3(foo, bar, baz, bang) {
	if (bar && bang) {
		baz = foo;
	}
}
function check3(foo, bar, baz, bang) {
	if (bar) {
                if (bang) {
         		baz = foo;
                }
	}
}

Complexity = 1 + number of if statements = 3.

So please, reopen this issue :-)

@platinumazure
Copy link
Member

I think I agree. Assuming a negation does not count towards cyclomatic complexity, by DeMorgan's law it is always the case that !(a && b) is equal to !a || !b, so those expressions (again, assuming we can ignore negation) should be equal in cyclomatic complexity.

I'm convinced this is a bug. @soda0289 @not-an-aardvark Okay if I reopen?

@not-an-aardvark
Copy link
Member

Ok, sounds good.

@soda0289
Copy link
Member

soda0289 commented Jun 7, 2017

Do you think we should add an option to count or statements the same as and? That way if people preferred the old behavior they could always go back or fix it in the future.

@not-an-aardvark
Copy link
Member

I just found this: jshint/jshint#840

If I had to guess, this bug exists because we were originally mimicing/copying the logic from JSHint, but JSHint also had a bug in the logic related to &&.

@not-an-aardvark not-an-aardvark added accepted There is consensus among the team that this change meets the criteria for inclusion and removed evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Nov 3, 2017
@not-an-aardvark
Copy link
Member

Marking this accepted because I'm also now convinced that this is a bug.

@eslint-deprecated eslint-deprecated bot locked and limited conversation to collaborators Jun 22, 2018
@eslint-deprecated eslint-deprecated bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Jun 22, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.