S3776: Cognitive Complexity of functions with lambda functions

  • versions used (SonarQube, Scanner, language analyzer)
    sonar maven plugin 3.7.0.1746
    sonarqube 8.4.2.36762
  • minimal code sample to reproduce (with analysis parameter, and potential instructions to compile).
import java.util.Map;
import java.util.function.Function;
import java.util.function.UnaryOperator;

public class Complexity {

	private static final Function<String, String> NO_OP = UnaryOperator.identity();

	private Map<String, Function<String, String>> functions;

	public Complexity() {
		setFunctions();
	}

	public Function<String, String> getFunction(String key) {
		return functions.getOrDefault(key, NO_OP);
	}

	private void setFunctions() {
		functions = Map.ofEntries(
				Map.entry("key1", v -> {
					if ("test".equals(v)) {
						return "the test";
					} else {
						return v + "test";
					}
				}),
				Map.entry("key2", v -> {
					if ("test2".equals(v)) {
						return "the 2nd test";
					} else {
						return v + " 2nd test";
					}
				}),
				Map.entry("key3", v -> {
					if ("test3".equals(v)) {
						return "the 3rd test";
					} else {
						return v + " 3rd test";
					}
				}),
				Map.entry("key4", v -> {
					if ("test4".equals(v)) {
						return "the 4th test";
					} else {
						return v + " 4th test";
					}
				}),
				Map.entry("key5", v -> {
					if ("test4".equals(v)) {
						return "the 5th test";
					} else {
						return v + " 5th test";
					}
				}),
				Map.entry("key6", v -> {
					if ("test6".equals(v)) {
						return "the 6th test";
					} else {
						return v + " 6th test";
					}
				}),
				Map.entry("key7", v -> {
					if ("test5".equals(v)) {
						return "the 7th test";
					} else {
						return v + " 7th test";
					}
				}),
				Map.entry("key8", v -> {
					if ("test8".equals(v)) {
						return "the 8th test";
					} else {
						return v + " 8th test";
					}
				}),
				Map.entry("key9", v -> {
					if ("test9".equals(v)) {
						return "the 9th test";
					} else {
						return v + " 9th test";
					}
				}),
				Map.entry("key10", v -> {
					if ("test10".equals(v)) {
						return "the 10th test";
					} else {
						return v + " 10th test";
					}
				})
								 );
	}
}

In the above example I am getting a Cognitive complexity of 30 since it is adding up all if/else statements, even though the context of each if/else is within the lambda expression and not part of the setFunctions method flow. Is this a false positive, or am I missing some part of how Cognitive complexity works?

Thank you very much for your time.

Hi,

From Appendix B of the whitepaper, “nested methods and method-like structures such as lambdas” increment the nesting level. You should be seeing via secondary locations how the total of 30 is reached. Off hand, it looks like +2 for each if and of course only +1 for each else.

 
HTH,
Ann

Hello @ganncamp,

Thank you for the quick reply.
I can understand that lambda functions used inside methods can increase their complexity, if e.g. they are used as part of streams or optionals.
But in the scenario displayed the lambda functions are not interfering with the flow of the setFunctions method, and are not executed, thus the setFunctions has no branching, it just puts 10 map entries in a map.

Hi,

As a maintainer, don’t you still have to parse & understand those 10 entries?

 
Ann

Hello,

Yes, but each one individually, not in the context of the setFunctions method.
e.g. for key2 I don’t have to understand the function in key1 to determine the outcome.
And setFunctions is a branch-less method.

The setFunctions context does not pollute the context of the lambda expressions, and the lambda expressions don’t alter the outcome of the setFunctions method.

Hello,

After a more thorough study of the whitepaper I get the concept behind the demo code having complexity issues.

Thank you for your time

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.