diff --git a/.golangci.next.reference.yml b/.golangci.next.reference.yml index fb0895eb3700..808b28cd6bc2 100644 --- a/.golangci.next.reference.yml +++ b/.golangci.next.reference.yml @@ -1988,6 +1988,7 @@ linters-settings: - identical # Identifies interfaces in the same package that have identical method sets. - unused # Identifies interfaces that are not used anywhere in the same package where the interface is defined. - opaque # Identifies functions that return interfaces, but the actual returned value is always a single concrete implementation. + - unexported # Identifies interfaces that are not exported but are used in exported functions or methods as parameters or return values. settings: unused: # List of packages path to exclude from the check. diff --git a/go.mod b/go.mod index 4755f528fab1..4e61211e9903 100644 --- a/go.mod +++ b/go.mod @@ -115,7 +115,7 @@ require ( github.com/ultraware/funlen v0.2.0 github.com/ultraware/whitespace v0.2.0 github.com/uudashr/gocognit v1.2.0 - github.com/uudashr/iface v1.3.1 + github.com/uudashr/iface v1.4.0 github.com/valyala/quicktemplate v1.8.0 github.com/xen0n/gosmopolitan v1.2.2 github.com/yagipy/maintidx v1.0.0 @@ -126,8 +126,8 @@ require ( go-simpler.org/sloglint v0.9.0 go.uber.org/automaxprocs v1.6.0 golang.org/x/mod v0.24.0 - golang.org/x/sys v0.31.0 - golang.org/x/tools v0.31.0 + golang.org/x/sys v0.33.0 + golang.org/x/tools v0.33.0 gopkg.in/yaml.v3 v3.0.1 honnef.co/go/tools v0.6.1 mvdan.cc/gofumpt v0.7.0 @@ -196,7 +196,7 @@ require ( go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/sync v0.12.0 // indirect + golang.org/x/sync v0.14.0 // indirect golang.org/x/text v0.22.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index eb6fe0ef722f..ccdd2b942795 100644 --- a/go.sum +++ b/go.sum @@ -571,8 +571,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA= github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= -github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= -github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= +github.com/uudashr/iface v1.4.0 h1:ImZ+1oEJPXvjap7nK0md7gA9RRH7PMp4vliaLkJ2+cg= +github.com/uudashr/iface v1.4.0/go.mod h1:i/H4cfRMPe0izticV8Yz0g6/zcsh5xXlvthrdh1kqcY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/quicktemplate v1.8.0 h1:zU0tjbIqTRgKQzFY1L42zq0qR3eh4WoQQdIdqCysW5k= @@ -713,8 +713,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -736,8 +736,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -793,8 +793,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -876,8 +876,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/golinters/iface/iface.go b/pkg/golinters/iface/iface.go index 31f88160eafd..4b8684d6d8ed 100644 --- a/pkg/golinters/iface/iface.go +++ b/pkg/golinters/iface/iface.go @@ -5,6 +5,7 @@ import ( "github.com/uudashr/iface/identical" "github.com/uudashr/iface/opaque" + "github.com/uudashr/iface/unexported" "github.com/uudashr/iface/unused" "golang.org/x/tools/go/analysis" @@ -28,9 +29,10 @@ func New(settings *config.IfaceSettings) *goanalysis.Linter { func analyzersFromSettings(settings *config.IfaceSettings) []*analysis.Analyzer { allAnalyzers := map[string]*analysis.Analyzer{ - "identical": identical.Analyzer, - "unused": unused.Analyzer, - "opaque": opaque.Analyzer, + "identical": identical.Analyzer, + "unused": unused.Analyzer, + "opaque": opaque.Analyzer, + "unexported": unexported.Analyzer, } if settings == nil || len(settings.Enable) == 0 { diff --git a/pkg/golinters/iface/testdata/iface_all.go b/pkg/golinters/iface/testdata/iface_all.go index 3a6f912a1d2a..a344aad0e688 100644 --- a/pkg/golinters/iface/testdata/iface_all.go +++ b/pkg/golinters/iface/testdata/iface_all.go @@ -6,11 +6,11 @@ import "fmt" // identical -type Pinger interface { // want "identical: interface Pinger contains identical methods or type constraints from another interface, causing redundancy" +type Pinger interface { // want "identical: interface 'Pinger' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } -type Healthcheck interface { // want "identical: interface Healthcheck contains identical methods or type constraints from another interface, causing redundancy" +type Healthcheck interface { // want "identical: interface 'Healthcheck' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } @@ -28,7 +28,7 @@ func (s server) Serve() error { return nil } -func NewServer(addr string) Server { // want "opaque: NewServer function return Server interface at the 1st result, abstract a single concrete implementation of \\*server" +func NewServer(addr string) Server { // want "opaque: 'NewServer' function return 'Server' interface at the 1st result, abstract a single concrete implementation of '\\*server'" return &server{addr: addr} } @@ -39,7 +39,7 @@ type User struct { Name string } -type UserRepository interface { // want "unused: interface UserRepository is declared but not used within the package" +type UserRepository interface { // want "unused: interface 'UserRepository' is declared but not used within the package" UserOf(id string) (*User, error) } @@ -66,3 +66,19 @@ func Allow(x any) { _ = x.(Allower) fmt.Println("allow") } + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { // want "unexported interface 'unexportedReader' used as parameter in exported function 'ReadAll'" + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { // want "unexported interface 'unexportedReader' used as return value in exported function 'NewUnexportedReader'" + return nil // stub +} diff --git a/pkg/golinters/iface/testdata/iface_all.yml b/pkg/golinters/iface/testdata/iface_all.yml index e00cb8970659..c88698d1b167 100644 --- a/pkg/golinters/iface/testdata/iface_all.yml +++ b/pkg/golinters/iface/testdata/iface_all.yml @@ -4,3 +4,4 @@ linters-settings: - unused - identical - opaque + - unexported diff --git a/pkg/golinters/iface/testdata/iface_cgo.go b/pkg/golinters/iface/testdata/iface_cgo.go index 43ef0216a036..7caad083c76a 100644 --- a/pkg/golinters/iface/testdata/iface_cgo.go +++ b/pkg/golinters/iface/testdata/iface_cgo.go @@ -24,11 +24,11 @@ func _() { // identical -type Pinger interface { // want "identical: interface Pinger contains identical methods or type constraints from another interface, causing redundancy" +type Pinger interface { // want "identical: interface 'Pinger' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } -type Healthcheck interface { // want "identical: interface Healthcheck contains identical methods or type constraints from another interface, causing redundancy" +type Healthcheck interface { // want "identical: interface 'Healthcheck' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } @@ -84,3 +84,19 @@ func Allow(x any) { _ = x.(Allower) fmt.Println("allow") } + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { + return nil // stub +} diff --git a/pkg/golinters/iface/testdata/iface_default.go b/pkg/golinters/iface/testdata/iface_default.go index 34117389738b..34d64e392bad 100644 --- a/pkg/golinters/iface/testdata/iface_default.go +++ b/pkg/golinters/iface/testdata/iface_default.go @@ -5,11 +5,11 @@ import "fmt" // identical -type Pinger interface { // want "identical: interface Pinger contains identical methods or type constraints from another interface, causing redundancy" +type Pinger interface { // want "identical: interface 'Pinger' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } -type Healthcheck interface { // want "identical: interface Healthcheck contains identical methods or type constraints from another interface, causing redundancy" +type Healthcheck interface { // want "identical: interface 'Healthcheck' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } @@ -65,3 +65,19 @@ func Allow(x any) { _ = x.(Allower) fmt.Println("allow") } + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { + return nil // stub +} diff --git a/pkg/golinters/iface/testdata/iface_identical.go b/pkg/golinters/iface/testdata/iface_identical.go index 821aff7bd41e..1eb3a34d177c 100644 --- a/pkg/golinters/iface/testdata/iface_identical.go +++ b/pkg/golinters/iface/testdata/iface_identical.go @@ -6,11 +6,11 @@ import "fmt" // identical -type Pinger interface { // want "identical: interface Pinger contains identical methods or type constraints from another interface, causing redundancy" +type Pinger interface { // want "identical: interface 'Pinger' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } -type Healthcheck interface { // want "identical: interface Healthcheck contains identical methods or type constraints from another interface, causing redundancy" +type Healthcheck interface { // want "identical: interface 'Healthcheck' contains identical methods or type constraints with another interface, causing redundancy" Ping() error } @@ -66,3 +66,19 @@ func Allow(x any) { _ = x.(Allower) fmt.Println("allow") } + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { + return nil // stub +} diff --git a/pkg/golinters/iface/testdata/iface_opaque.go b/pkg/golinters/iface/testdata/iface_opaque.go index b7ebeddc600f..3465a8b7f9d6 100644 --- a/pkg/golinters/iface/testdata/iface_opaque.go +++ b/pkg/golinters/iface/testdata/iface_opaque.go @@ -28,7 +28,7 @@ func (s server) Serve() error { return nil } -func NewServer(addr string) Server { // want "opaque: NewServer function return Server interface at the 1st result, abstract a single concrete implementation of \\*server" +func NewServer(addr string) Server { // want "opaque: 'NewServer' function return 'Server' interface at the 1st result, abstract a single concrete implementation of '\\*server'" return &server{addr: addr} } @@ -66,3 +66,19 @@ func Allow(x any) { _ = x.(Allower) fmt.Println("allow") } + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { + return nil // stub +} diff --git a/pkg/golinters/iface/testdata/iface_unexported.go b/pkg/golinters/iface/testdata/iface_unexported.go new file mode 100644 index 000000000000..01a4f210f9f7 --- /dev/null +++ b/pkg/golinters/iface/testdata/iface_unexported.go @@ -0,0 +1,84 @@ +//golangcitest:args -Eiface +//golangcitest:config_path testdata/iface_unexported.yml +package testdata + +import "fmt" + +// identical + +type Pinger interface { + Ping() error +} + +type Healthcheck interface { + Ping() error +} + +// opaque + +type Server interface { + Serve() error +} + +type server struct { + addr string +} + +func (s server) Serve() error { + return nil +} + +func NewServer(addr string) Server { + return &server{addr: addr} +} + +// unused + +type User struct { + ID string + Name string +} + +type UserRepository interface { + UserOf(id string) (*User, error) +} + +type UserRepositorySQL struct { +} + +func (r *UserRepositorySQL) UserOf(id string) (*User, error) { + return nil, nil +} + +type Granter interface { + Grant(permission string) error +} + +func AllowAll(g Granter) error { + return g.Grant("all") +} + +type Allower interface { + Allow(permission string) error +} + +func Allow(x any) { + _ = x.(Allower) + fmt.Println("allow") +} + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { // want "unexported interface 'unexportedReader' used as parameter in exported function 'ReadAll'" + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { // want "unexported interface 'unexportedReader' used as return value in exported function 'NewUnexportedReader'" + return nil // stub +} diff --git a/pkg/golinters/iface/testdata/iface_unexported.yml b/pkg/golinters/iface/testdata/iface_unexported.yml new file mode 100644 index 000000000000..2ae6b6c6663e --- /dev/null +++ b/pkg/golinters/iface/testdata/iface_unexported.yml @@ -0,0 +1,4 @@ +linters-settings: + iface: + enable: + - unexported diff --git a/pkg/golinters/iface/testdata/iface_unused.go b/pkg/golinters/iface/testdata/iface_unused.go index b75fdc8964c6..65d9fa391f74 100644 --- a/pkg/golinters/iface/testdata/iface_unused.go +++ b/pkg/golinters/iface/testdata/iface_unused.go @@ -6,11 +6,11 @@ import "fmt" // identical -type Pinger interface { // want "unused: interface Pinger is declared but not used within the package" +type Pinger interface { // want "unused: interface 'Pinger' is declared but not used within the package" Ping() error } -type Healthcheck interface { // want "unused: interface Healthcheck is declared but not used within the package" +type Healthcheck interface { // want "unused: interface 'Healthcheck' is declared but not used within the package" Ping() error } @@ -39,7 +39,7 @@ type User struct { Name string } -type UserRepository interface { // want "unused: interface UserRepository is declared but not used within the package" +type UserRepository interface { // want "unused: interface 'UserRepository' is declared but not used within the package" UserOf(id string) (*User, error) } @@ -66,3 +66,19 @@ func Allow(x any) { _ = x.(Allower) fmt.Println("allow") } + +// unexported + +type unexportedReader interface { + Read([]byte) (int, error) +} + +func ReadAll(r unexportedReader) ([]byte, error) { + buf := make([]byte, 1024) + _, err := r.Read(buf) + return buf, err +} + +func NewUnexportedReader() unexportedReader { + return nil // stub +}