From bf94da4667789c94296c32cb038e5238d314157b Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Sun, 1 May 2022 20:07:12 +0300 Subject: [PATCH 1/2] Fix gocognit output when a function has generic receiver type recvString func in gocognit returns BADRECV when the function's receiver is a generic type. Implement recvString in two variants in order to keep the code compatible with go versions less than 1.18 --- gocognit.go | 12 ------------ recv.go | 30 ++++++++++++++++++++++++++++++ recv_pre118.go | 20 ++++++++++++++++++++ 3 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 recv.go create mode 100644 recv_pre118.go diff --git a/gocognit.go b/gocognit.go index 418c163..1d539ee 100644 --- a/gocognit.go +++ b/gocognit.go @@ -49,18 +49,6 @@ func funcName(fn *ast.FuncDecl) string { return fn.Name.Name } -// recvString returns a string representation of recv of the -// form "T", "*T", or "BADRECV" (if not a proper receiver type). -func recvString(recv ast.Expr) string { - switch t := recv.(type) { - case *ast.Ident: - return t.Name - case *ast.StarExpr: - return "*" + recvString(t.X) - } - return "BADRECV" -} - // Complexity calculates the cognitive complexity of a function. func Complexity(fn *ast.FuncDecl) int { v := complexityVisitor{ diff --git a/recv.go b/recv.go new file mode 100644 index 0000000..b2e8b6a --- /dev/null +++ b/recv.go @@ -0,0 +1,30 @@ +//go:build go1.18 +// +build go1.18 + +package gocognit + +import ( + "go/ast" + "strings" +) + +// recvString returns a string representation of recv of the +// form "T", "*T", Type[T], Type[T, V], or "BADRECV" (if not a proper receiver type). +func recvString(recv ast.Expr) string { + switch t := recv.(type) { + case *ast.Ident: + return t.Name + case *ast.StarExpr: + return "*" + recvString(t.X) + case *ast.IndexExpr: + return recvString(t.X) + "[" + recvString(t.Index) + "]" + case *ast.IndexListExpr: + targs := make([]string, len(t.Indices)) + for i, exp := range t.Indices { + targs[i] = recvString(exp) + } + + return recvString(t.X) + "[" + strings.Join(targs, ", ") + "]" + } + return "BADRECV" +} diff --git a/recv_pre118.go b/recv_pre118.go new file mode 100644 index 0000000..9e0ebfd --- /dev/null +++ b/recv_pre118.go @@ -0,0 +1,20 @@ +//go:build !go1.18 +// +build !go1.18 + +package gocognit + +import ( + "go/ast" +) + +// recvString returns a string representation of recv of the +// form "T", "*T", or "BADRECV" (if not a proper receiver type). +func recvString(recv ast.Expr) string { + switch t := recv.(type) { + case *ast.Ident: + return t.Name + case *ast.StarExpr: + return "*" + recvString(t.X) + } + return "BADRECV" +} From c660021124c82f8c675246d4c3cf32f180556193 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 1 Jul 2022 15:28:59 +0300 Subject: [PATCH 2/2] Add unit test for methods with generic receivers --- gocognit118_test.go | 17 +++++++++++++++++ testdata/src/c/c.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 gocognit118_test.go create mode 100644 testdata/src/c/c.go diff --git a/gocognit118_test.go b/gocognit118_test.go new file mode 100644 index 0000000..51d396d --- /dev/null +++ b/gocognit118_test.go @@ -0,0 +1,17 @@ +//go:build go1.18 +// +build go1.18 + +package gocognit_test + +import ( + "testing" + + "github.com/uudashr/gocognit" + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestAnalyzer_Generics(t *testing.T) { + testdata := analysistest.TestData() + gocognit.Analyzer.Flags.Set("over", "0") + analysistest.Run(t, testdata, gocognit.Analyzer, "c") +} diff --git a/testdata/src/c/c.go b/testdata/src/c/c.go new file mode 100644 index 0000000..5a75c99 --- /dev/null +++ b/testdata/src/c/c.go @@ -0,0 +1,36 @@ +package testdata + +type Node[T any] struct { +} + +func (n *Node[T]) String() string { // want "cognitive complexity 1 of func \\(\\*Node\\[T\\]\\)\\.String is high \\(> 0\\)" + if n != nil { // +1 + return "Node" + } + + return "" +} // total complexity = 1 + +type Pair[K any, V any] struct { + Key K + Value V +} + +func (p *Pair[K, V]) String() string { // want "cognitive complexity 1 of func \\(\\*Pair\\[K, V\\]\\)\\.String is high \\(> 0\\)" + if p != nil { // +1 + return "Pair" + } + + return "" +} // total complexity = 1 + +type Triple[K any, V any, T any] struct { +} + +func (t *Triple[K, V, T]) String() string { // want "cognitive complexity 1 of func \\(\\*Triple\\[K, V, T\\]\\)\\.String is high \\(> 0\\)" + if t != nil { // +1 ` + return "Triple" + } + + return "" +} // total complexity = 1