From d755295dd4f3c2a9a429d2372e4d3fb0762d8956 Mon Sep 17 00:00:00 2001 From: Nuruddin Ashr Date: Thu, 5 Dec 2024 21:47:11 +0700 Subject: [PATCH] Fix nesting inside else/else if --- gocognit.go | 11 +++------ gocognit_test.go | 6 +++++ testdata/src/a/a.go | 47 +++++++++++++++++++++++++++++++---- testdata/src/b/b.go | 30 ++++++++++++++++++++--- testdata/src/d/d.go | 60 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 testdata/src/d/d.go diff --git a/gocognit.go b/gocognit.go index dd8dfa2..126452e 100644 --- a/gocognit.go +++ b/gocognit.go @@ -179,14 +179,9 @@ func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor { ast.Walk(v, n.Cond) - pure := !v.markedAsElseNode(n) // pure `if` statement, not an `else if` - if pure { - v.incNesting() - ast.Walk(v, n.Body) - v.decNesting() - } else { - ast.Walk(v, n.Body) - } + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() if _, ok := n.Else.(*ast.BlockStmt); ok { v.incComplexity() diff --git a/gocognit_test.go b/gocognit_test.go index 6b35954..f9708d3 100644 --- a/gocognit_test.go +++ b/gocognit_test.go @@ -18,3 +18,9 @@ func TestAnalyzerOver3(t *testing.T) { gocognit.Analyzer.Flags.Set("over", "3") analysistest.Run(t, testdata, gocognit.Analyzer, "b") } + +func TestAnalyzerComplex(t *testing.T) { + testdata := analysistest.TestData() + gocognit.Analyzer.Flags.Set("over", "0") + analysistest.Run(t, testdata, gocognit.Analyzer, "d") +} diff --git a/testdata/src/a/a.go b/testdata/src/a/a.go index 30a551d..327c7d4 100644 --- a/testdata/src/a/a.go +++ b/testdata/src/a/a.go @@ -23,7 +23,7 @@ func IfElseNested(n int) string { // want "cognitive complexity 3 of func IfElse if n == 100 { // +1 return "a hundred" } else { // + 1 - if n == 200 { // + 1 + if n == 200 { // +1 return "two hundred" } } @@ -31,17 +31,39 @@ func IfElseNested(n int) string { // want "cognitive complexity 3 of func IfElse return "others" } // total complexity = 3 -func IfElseIfNested(n int) string { // want "cognitive complexity 3 of func IfElseIfNested is high \\(> 0\\)" +func IfElseIfNested(n int) string { // want "cognitive complexity 4 of func IfElseIfNested is high \\(> 0\\)" if n == 100 { // +1 return "a hundred" - } else if n < 300 { // + 1 - if n == 200 { // + 1 + } else if n < 300 { // +1 + if n == 200 { // +2 (nesting=1) return "two hundred" } } return "others" -} // total complexity = 3 +} // total complexity = 4 + +func NestingIfElseIfNested(n int) string { // want "cognitive complexity 7 of func NestingIfElseIfNested is high \\(> 0\\)" + if n > 0 { // +1 + if n == 100 { // +2 (nesting=1) + return "a hundred" + } else if n < 300 { // +1 + if n == 200 { // +3 (nesting=2) + return "two hundred" + } + } + } + + return "others" +} // total complexity = 7 + +func SimpleLogicalSeq0(a, b bool) string { // want "cognitive complexity 2 of func SimpleLogicalSeq0 is high \\(> 0\\)" + if a && b { // +1 "if", +1 "&&" + return "ok" + } + + return "not ok" +} // total complexity = 2 func SimpleLogicalSeq1(a, b, c, d bool) string { // want "cognitive complexity 2 of func SimpleLogicalSeq1 is high \\(> 0\\)" if a && b && c && d { // +1 for `if`, +1 for `&&` sequence @@ -247,3 +269,18 @@ func IgnoreMe(name string) bool { return false } // total complexity = 1 + +func SwitchInNest(w io.Writer, i interface{}) error { // want "cognitive complexity 3 of func SwitchInNest is high \\(> 0\\)" + if i != nil { // +1 + switch v := i.(type) { // +2 (nesting = 1) + case int: + fmt.Fprint(w, "int ", v) + case string: + fmt.Fprint(w, "string", v) + default: + return errors.New("unrecognized type") + } + } + + return errors.New("nil interface") +} // total complexity = 3 diff --git a/testdata/src/b/b.go b/testdata/src/b/b.go index 817de48..f8efa14 100644 --- a/testdata/src/b/b.go +++ b/testdata/src/b/b.go @@ -31,17 +31,39 @@ func IfElseNested(n int) string { return "others" } // total complexity = 3 -func IfElseIfNested(n int) string { +func IfElseIfNested(n int) string { // want "cognitive complexity 4 of func IfElseIfNested is high \\(> 3\\)" if n == 100 { // +1 return "a hundred" - } else if n < 300 { // + 1 - if n == 200 { // + 1 + } else if n < 300 { // +1 + if n == 200 { // +2 (nesting=1) return "two hundred" } } return "others" -} // total complexity = 3 +} // total complexity = 4 + +func NestingIfElseIfNested(n int) string { // want "cognitive complexity 7 of func NestingIfElseIfNested is high \\(> 3\\)" + if n > 0 { // +1 + if n == 100 { // +2 (nesting = 1) + return "a hundred" + } else if n < 300 { // +1 + if n == 200 { // +3 (nesting = 2) + return "two hundred" + } + } + } + + return "others" +} // total complexity = 7 + +func SimpleLogicalSeq0(a, b bool) string { + if a && b { // +1 for `if`, +1 for `&&` sequence + return "ok" + } + + return "not ok" +} // total complexity = 2 func SimpleLogicalSeq1(a, b, c, d bool) string { if a && b && c && d { // +1 for `if`, +1 for `&&` sequence diff --git a/testdata/src/d/d.go b/testdata/src/d/d.go new file mode 100644 index 0000000..4c237f8 --- /dev/null +++ b/testdata/src/d/d.go @@ -0,0 +1,60 @@ +package testdata + +import ( + "strings" +) + +// Adopt from org.sonar.api.utils.WildcardPattern.java in SonarQube + +func ToRegexp(antPattern string, directorySeparator string) string { // want "cognitive complexity 20 of func ToRegexp is high \\(> 0\\)" + escapedDirectorySeparator := "\\" + directorySeparator + + var sb strings.Builder + sb.WriteString("^") + + i := 0 + if strings.HasPrefix(antPattern, "/") || // +1 "if", +1 "||" + strings.HasPrefix(antPattern, "\\") { + i = 1 + } + + for i < len(antPattern) { // +1 + ch := antPattern[i] + + if strings.ContainsRune(SPECIAL_CHARS, rune(ch)) { // +2 (nesting=1) + sb.WriteString("\\") + sb.WriteByte(ch) + } else if ch == '*' { // +1 + if i+1 < len(antPattern) && antPattern[i+1] == '*' { // +3 "if" (nesting=2), +1 "&&" + if i+2 < len(antPattern) && isSlash(antPattern[i+2]) { // +4 "if" (nesting=3), +1 "&&" + sb.WriteString("(?:.*") + sb.WriteString(escapedDirectorySeparator) + sb.WriteString("|)") + i += 2 + } else { // +1 + sb.WriteString(".*") + i += 1 + } + } else { // +1 + sb.WriteString("[^" + escapedDirectorySeparator + "]*?") + } + } else if ch == '?' { // +1 + sb.WriteString("[^" + escapedDirectorySeparator + "]") + } else if isSlash(ch) { // +1 + sb.WriteString(escapedDirectorySeparator) + } else { // +1 + sb.WriteByte(ch) + } + + i++ + } + + sb.WriteString("$") + return sb.String() +} // total complelxity = 20 + +func isSlash(ch byte) bool { // want "cognitive complexity 1 of func isSlash is high \\(> 0\\)" + return ch == '/' || ch == '\\' // +1 +} + +var SPECIAL_CHARS = "()[]^$.{}+|"