diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d69641c..8c2bf6f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,14 +16,14 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v6 with: - go-version: 1.24 + go-version: '1.26' - name: Unit tests run: go test -coverprofile=cover.out ./... diff --git a/_testdata/26/bar.go b/_testdata/26/bar.go new file mode 100644 index 0000000..53a05f8 --- /dev/null +++ b/_testdata/26/bar.go @@ -0,0 +1,3 @@ +type twentysixB[T twentysixB[T]] interface { + xyz(T) T +} diff --git a/_testdata/26/foo.go b/_testdata/26/foo.go new file mode 100644 index 0000000..d753308 --- /dev/null +++ b/_testdata/26/foo.go @@ -0,0 +1,3 @@ +func twentysixA() *int { + return new(17) +} diff --git a/decl.go b/decl.go index 1ef9b36..abef27b 100644 --- a/decl.go +++ b/decl.go @@ -91,8 +91,32 @@ func (p *pkgScanner) valueSpec(spec *ast.ValueSpec) (bool, error) { } func (p *pkgScanner) typeSpec(spec *ast.TypeSpec) (bool, error) { + var generic bool + + if spec.TypeParams != nil && len(spec.TypeParams.List) > 0 { + generic = true + p.result(posResult{ + version: 18, + pos: p.fset.Position(spec.Pos()), + desc: "generic type decl", + }) + + v := recursiveTypeParamVisitor{name: spec.Name} + ast.Walk(&v, spec.TypeParams) + if v.found { + isMax := p.result(posResult{ + version: 26, + pos: p.fset.Position(spec.Pos()), + desc: "recursive type parameter", + }) + if isMax { + return true, nil + } + } + } + if spec.Assign.IsValid() { - if spec.TypeParams != nil && len(spec.TypeParams.List) > 0 { + if generic { p.result(posResult{ version: 24, pos: p.fset.Position(spec.Pos()), @@ -117,3 +141,19 @@ func (p *pkgScanner) typeSpec(spec *ast.TypeSpec) (bool, error) { } return p.expr(spec.Type) } + +type recursiveTypeParamVisitor struct { + name *ast.Ident + found bool +} + +func (v *recursiveTypeParamVisitor) Visit(node ast.Node) ast.Visitor { + if v.found { + return nil + } + if ident, ok := node.(*ast.Ident); ok && ident.Name == v.name.Name { + v.found = true + return nil + } + return v +} diff --git a/expr.go b/expr.go index cbf1ab5..5c539af 100644 --- a/expr.go +++ b/expr.go @@ -450,6 +450,21 @@ func (p *pkgScanner) builtinCall(expr *ast.CallExpr) (bool, error) { if p.result(result) { return true, nil } + + case "new": + if len(expr.Args) == 1 { + arg := expr.Args[0] + if typ, ok := p.info.Types[arg]; ok && !typ.IsType() { + result := posResult{ + version: 26, + pos: p.fset.Position(expr.Pos()), + desc: "use of new builtin with non-type argument", + } + if p.result(result) { + return true, nil + } + } + } } case *ast.SelectorExpr: diff --git a/go.mod b/go.mod index 3b2a832..21afa3f 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,12 @@ module github.com/bobg/mingo -go 1.23.0 - -toolchain go1.24.1 +go 1.26 require ( - github.com/bobg/errors v1.1.0 + github.com/bobg/errors v1.3.0 github.com/bobg/go-generics/v4 v4.2.0 - golang.org/x/mod v0.27.0 - golang.org/x/tools v0.36.0 + golang.org/x/mod v0.33.0 + golang.org/x/tools v0.42.0 ) -require golang.org/x/sync v0.16.0 // indirect +require golang.org/x/sync v0.19.0 // indirect diff --git a/go.sum b/go.sum index aa5d1a4..09e047d 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,14 @@ github.com/bobg/errors v1.1.0 h1:gsVanPzJMpZQpwY+27/GQYElZez5CuMYwiIpk2A3RGw= github.com/bobg/errors v1.1.0/go.mod h1:Q4775qBZpnte7EGFJqmvnlB1U4pkI1XmU3qxqdp7Zcc= +github.com/bobg/errors v1.3.0 h1:LJlNiQniZ+wm+kEwRblYYAsY3+DkW4ekPdjZF0raYfk= +github.com/bobg/errors v1.3.0/go.mod h1:HExZHNKjrSozaLs3/X7HpryCMRBKV6SQXngfQJ9wYb8= github.com/bobg/go-generics/v4 v4.2.0 h1:c3eX8rlFCRrxFnUepwQIA174JK7WuckbdRHf5ARCl7w= github.com/bobg/go-generics/v4 v4.2.0/go.mod h1:KVwpxEYErjvcqjJSJqVNZd/JEq3SsQzb9t01+82pZGw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= diff --git a/hist.go b/hist.go index 5c4e0f9..9e91451 100644 --- a/hist.go +++ b/hist.go @@ -130,6 +130,8 @@ func goroot() string { if g := os.Getenv("GOROOT"); g != "" { return g } + + //lint:ignore SA1019 We actually do want the behavior that is the reason runtime.GOROOT is deprecated. return runtime.GOROOT() } diff --git a/mingo_test.go b/mingo_test.go index 29515b3..5a7ced9 100644 --- a/mingo_test.go +++ b/mingo_test.go @@ -163,16 +163,11 @@ func TestLangChecks(t *testing.T) { t.Run("CheckFail", func(t *testing.T) { _, err := s.ScanDir(tmpdir) - var ( - verr VersionError - lerr LoadError - ) - switch { - case errors.As(err, &lerr): + if _, ok := errors.AsType[VersionError](err); ok { // Do nothing - case errors.As(err, &verr): + } else if _, ok := errors.AsType[LoadError](err); ok { // Do nothing - default: + } else { t.Errorf("got error %v, want a LoadError or VersionError", err) } })