diff --git a/check/check.go b/check/check.go index b738c27..97c5a59 100644 --- a/check/check.go +++ b/check/check.go @@ -21,6 +21,7 @@ import ( "sort" "strings" + "golang.org/x/exp/typeparams" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" @@ -44,7 +45,7 @@ func UnusedParams(tests, exported, debug bool, args ...string) ([]string, error) return c.lines(args...) } -// Checker finds unused parameterss in a program. You probably want to use +// Checker finds unused parameters in a program. You probably want to use // UnusedParams instead, unless you want to use a *loader.Program and // *ssa.Program directly. type Checker struct { @@ -877,8 +878,23 @@ func recvPrefix(recv *ast.FieldList) string { } expr = star.X } - id := expr.(*ast.Ident) - return id.Name + "." + switch expr := expr.(type) { + case *ast.Ident: + return expr.Name + "." + default: + x, _, _, _ := typeparams.UnpackIndexExpr(expr) + if x == nil { + panic(fmt.Sprintf("unexepected receiver AST node: %T", expr)) + } + return x.(*ast.Ident).Name + "." + // TODO: remove the use of x/exp/typeparams once we drop Go 1.17 + // case *ast.IndexExpr: + // return expr.X.(*ast.Ident).Name + "." + // case *ast.IndexListExpr: + // return expr.X.(*ast.Ident).Name + "." + // default: + // panic(fmt.Sprintf("unexepected receiver AST node: %T", expr)) + } } // multipleImpls reports whether a function has multiple implementations in the diff --git a/go.mod b/go.mod index 1052dc6..773c861 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,14 @@ go 1.17 require ( github.com/rogpeppe/go-internal v1.8.1 - golang.org/x/tools v0.1.10 + golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d + golang.org/x/tools v0.1.12-0.20220628192153-7743d1d949f1 ) require ( github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect - golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect - golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/errgo.v2 v2.1.0 // indirect ) diff --git a/go.sum b/go.sum index cf5642c..8eb5003 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,10 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d h1:+W8Qf4iJtMGKkyAygcKohjxTk4JPsL9DpzApJ22m5Ic= +golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -22,8 +24,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -31,12 +33,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12-0.20220628192153-7743d1d949f1 h1:NHLFZ56qCjD+0hYY3kE5Wl40Z7q4Gn9Ln/7YU0lsGko= +golang.org/x/tools v0.1.12-0.20220628192153-7743d1d949f1/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/testdata/scripts/build_tags.txt b/testdata/scripts/build_tags.txt index ae84ff6..9dc521b 100644 --- a/testdata/scripts/build_tags.txt +++ b/testdata/scripts/build_tags.txt @@ -3,6 +3,8 @@ cmp stdout stdout.golden -- go.mod -- module testdata.tld/foo + +go 1.18 -- stdout.golden -- foo_main.go:5:23: oneUnusedMain - b is unused foo_main.go:24:22: multImplsMethod - f is unused @@ -12,6 +14,7 @@ package foo var DoWork func() type FooType int + -- foo_main.go -- //+build !other @@ -40,6 +43,7 @@ func multImplsMethod(f int) uint64 { DoWork() return 3 } + -- foo_other.go -- //+build other diff --git a/testdata/scripts/typeparams.txt b/testdata/scripts/typeparams.txt new file mode 100644 index 0000000..fc22924 --- /dev/null +++ b/testdata/scripts/typeparams.txt @@ -0,0 +1,91 @@ +[!go1.18] skip + +# TODO: catch unusedGeneric. +unparam . + +-- stdout.golden -- +-- go.mod -- +module testdata.tld/foo + +go 1.18 +-- foo.go -- +package foo + +var DoWork func() + +func GenericFunc[GenericParamA, B any](x GenericParamA, y B) {} + +type GenericVector[GenericParamT any] []GenericParamT + +type GenericGraph[T any] struct { + Content T + Edges []GenericGraph[T] +} + +type PredeclaredSignedInteger interface { + int | int8 | int16 | int32 | int64 +} + +type StringableSignedInteger interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 + + String() string +} + +type CombineEmbeds interface { + string | int + + interface { EmbeddedMethod() } + RegularMethod() +} + +type Tuple[T1 any, T2 any] struct { + left T1 + right T2 +} + +func (t Tuple[T1, T2]) Left() T1 { return t.left } +func (t Tuple[T1, T2]) Right() T2 { return t.right } + +func (t Tuple[T1, T2]) unusedGeneric(t1 T1, t2 T2) T2 { + DoWork() + return t2 +} + +// otherwise Tuple isn't reachable. +var Sink interface{} = new(Tuple[int, string]) + +// The different number of type parameters result in different receiver AST +// nodes on methods: IndexExpr and IndexListExpr. +type GenericType1[T1 any] []T1 +type GenericType2[T1 any, T2 any] struct { + t1 T1 + t2 T2 +} + +-- foo_main.go -- +//+build !other + +package foo + +func (g *GenericType1[T1]) multImplsMethodGeneric(f1 T1) { + DoWork() +} + +func (g GenericType2[T1, T2]) multImplsMethodGeneric(f1 T1, f2 T2) T2 { + DoWork() + return f2 +} +-- foo_other.go -- +//+build other + +package foo + +func (g *GenericType1[T1]) multImplsMethodGeneric(f1 T1) { + DoWork() +} + +func (g GenericType2[T1, T2]) multImplsMethodGeneric(f1 T1, f2 T2) T2 { + DoWork() + return f2 +}