Skip to content

Commit

Permalink
gopls/internal/analysis/stdversion: suppress before go1.21
Browse files Browse the repository at this point in the history
This change causes the stdversion checker not to report
any diagnostics in modules with go versons before go1.21,
because the semantics of the go declaration were not
clearly defined to correspond to toolchain requirements
at that point. Also, those Go versions are now unsupported,
so reporting diagnostics just creates unnecessary churn.

Updates golang/go#46136

Change-Id: I323f704c4d4f1f0fe5fc8b5680824bc07d3c4112
Reviewed-on: https://go-review.googlesource.com/c/tools/+/570140
Reviewed-by: Robert Findley <[email protected]>
Auto-Submit: Alan Donovan <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
  • Loading branch information
adonovan committed Mar 8, 2024
1 parent c1eaf76 commit 176e895
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 136 deletions.
13 changes: 10 additions & 3 deletions gopls/internal/analysis/stdversion/stdversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ func run(pass *analysis.Pass) (any, error) {
return nil, nil
}

// Don't report diagnostics for modules marked before go1.21,
// since at that time the go directive wasn't clearly
// specified as a toolchain requirement.
//
// TODO(adonovan): after go1.21, call GoVersion directly.
pkgVersion := any(pass.Pkg).(interface{ GoVersion() string }).GoVersion()
if !versions.AtLeast(pkgVersion, "go1.21") {
return nil, nil
}

// disallowedSymbols returns the set of standard library symbols
// in a given package that are disallowed at the specified Go version.
type key struct {
Expand All @@ -67,9 +77,6 @@ func run(pass *analysis.Pass) (any, error) {
return disallowed
}

// TODO(adonovan): after go1.21, call GoVersion directly.
pkgVersion := any(pass.Pkg).(interface{ GoVersion() string }).GoVersion()

// Scan the syntax looking for references to symbols
// that are disallowed by the version of the file.
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
Expand Down
5 changes: 4 additions & 1 deletion gopls/internal/analysis/stdversion/stdversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ func Test(t *testing.T) {
testenv.NeedsGo1Point(t, 22)

testfile := filepath.Join(analysistest.TestData(), "test.txtar")
runTxtarFile(t, testfile, stdversion.Analyzer, "example.com/a", "example.com/sub")
runTxtarFile(t, testfile, stdversion.Analyzer,
"example.com/a",
"example.com/sub",
"example.com/old")
}

// runTxtarFile unpacks a txtar archive to a directory, and runs
Expand Down
103 changes: 50 additions & 53 deletions gopls/internal/analysis/stdversion/testdata/test.txtar
Original file line number Diff line number Diff line change
@@ -1,104 +1,101 @@
Test of "too new" diagnostics from the stdversion analyzer.

This test references go1.21 symbols from std.
This test references go1.21 and go1.22 symbols from std.

It uses a txtar file due to golang/go#37054.

See also gopls/internal/test/marker/testdata/diagnostics/stdversion.txt
which runs the same test within the gopls analysis driver, to ensure
coverage of per-file Go version support.

-- go.work --
use .
use ./sub

-- go.mod --
module example.com

go 1.19
go 1.21

-- a/a.go --
package a

import (
"context"
"go/types"
"time"
)
import "go/types"

func _() {
time.Now() // ok: defined by go1.0

context.Cause(nil) // want `context.Cause requires go1.20 or later \(module is go1.19\)`
// old package-level type
var _ types.Info // ok: defined by go1.0

context.WithDeadlineCause(nil, time.Time{}, nil) // want `context.WithDeadlineCause requires go1.21 or later \(module is go1.19\)`
// new field of older type
_ = new(types.Info).FileVersions // want `types.FileVersions requires go1.22 or later \(module is go1.21\)`

// GoVersion is a new method of an older type.
new(types.Package).GoVersion() // want `types.GoVersion requires go1.21 or later \(module is go1.19\)`
}
// new method of older type
new(types.Info).PkgNameOf // want `types.PkgNameOf requires go1.22 or later \(module is go1.21\)`

-- a/selections.go --
package a

import (
"go/ast" // for File.FileEnd field (go1.20)
"time" // for Time.Compare method (go1.20)
"log/slog" // for slog.Logger and its Info method (both go1.21)
)

func _() {
_ = new(ast.File).FileEnd // want `ast.FileEnd requires go1.20 or later \(module is go1.19\)`
time.Now().Compare(time.Now()) // want `time.Compare requires go1.20 or later \(module is go1.19\)`
// new package-level type
var a types.Alias // want `types.Alias requires go1.22 or later \(module is go1.21\)`

var log slog.Logger // want `slog.Logger requires go1.21 or later \(module is go1.19\)`
log.Info("") // no diagnostic
// new method of new type
a.Underlying() // no diagnostic
}

-- sub/go.mod --
module example.com/sub

go 1.20
go 1.21

-- sub/sub.go --
package sub

import (
"context"
"go/types"
"time"
)
import "go/types"

func _() {
time.Now() // ok: defined by go1.0
// old package-level type
var _ types.Info // ok: defined by go1.0

context.Cause(nil) // ok: go.mod requires 1.20
// new field of older type
_ = new(types.Info).FileVersions // want `types.FileVersions requires go1.22 or later \(module is go1.21\)`

context.WithDeadlineCause(nil, time.Time{}, nil) // want `context.WithDeadlineCause requires go1.21 or later \(module is go1.20\)`
// new method of older type
new(types.Info).PkgNameOf // want `types.PkgNameOf requires go1.22 or later \(module is go1.21\)`

// GoVersion is a new (go1.21) method of an old (go1.0) type.
new(types.Package).GoVersion() // want `types.GoVersion requires go1.21 or later \(module is go1.20\)`
// new package-level type
var a types.Alias // want `types.Alias requires go1.22 or later \(module is go1.21\)`

// new method of new type
a.Underlying() // no diagnostic
}

invalid syntax // exercise RunDespiteErrors

-- sub/tagged.go --
//go:build go1.21
//go:build go1.22

package sub

import (
"context"
"go/types"
"time"
)
import "go/types"

func _() {
time.Now() // ok: defined by go1.0
// old package-level type
var _ types.Info

// new field of older type
_ = new(types.Info).FileVersions

context.Cause(nil) // ok: go.mod requires 1.20
// new method of older type
new(types.Info).PkgNameOf

context.WithDeadlineCause(nil, time.Time{}, nil) // ok: file requires go1.21
// new package-level type
var a types.Alias

new(types.Package).GoVersion() // ok: file requires go1.21
// new method of new type
a.Underlying()
}

-- old/go.mod --
module example.com/old

go 1.5

-- old/old.go --
package old

import "go/types"

var _ types.Alias // no diagnostic: go.mod is too old for us to care
44 changes: 20 additions & 24 deletions gopls/internal/test/marker/testdata/completion/imported-std.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ Test of imported completions respecting the effective Go version of the file.

(See "un-" prefixed file for same test of unimported completions.)

These symbols below were introduced in go1.20:
These symbols below were introduced to go/types in go1.22:

types.Satisfied
ast.File.FileStart
(*token.FileSet).RemoveFile
Alias
Info.FileVersions
(Checker).PkgNameOf

The underlying logic depends on versions.FileVersion, which only
behaves correctly in go1.22. (When go1.22 is assured, we can remove
Expand All @@ -18,7 +18,7 @@ the min_go flag but leave the test inputs unchanged.)
-- go.mod --
module example.com

go 1.19
go 1.21

-- a/a.go --
package a
Expand All @@ -27,39 +27,35 @@ import "go/ast"
import "go/token"
import "go/types"

// package-level func
var _ = types.Imple //@rankl("Imple", "Implements")
var _ = types.Satis //@rankl("Satis", "!Satisfies")

// (Apparently we don't even offer completions of methods
// of types from unimported packages, so the fact that
// we don't implement std version filtering isn't evident.)
// package-level decl
var _ = types.Sat //@rankl("Sat", "Satisfies")
var _ = types.Ali //@rankl("Ali", "!Alias")

// field
var _ = new(ast.File).Packa //@rankl("Packa", "Package")
var _ = new(ast.File).FileS //@rankl("FileS", "!FileStart")
var _ = new(types.Info).Use //@rankl("Use", "Uses")
var _ = new(types.Info).Fil //@rankl("Fil", "!FileVersions")

// method
var _ = new(token.FileSet).Add //@rankl("Add", "AddFile")
var _ = new(token.FileSet).Remove //@rankl("Remove", "!RemoveFile")
var _ = new(types.Checker).Obje //@rankl("Obje", "ObjectOf")
var _ = new(types.Checker).PkgN //@rankl("PkgN", "!PkgNameOf")

-- b/b.go --
//go:build go1.20
//go:build go1.22

package a

import "go/ast"
import "go/token"
import "go/types"

// package-level func
var _ = types.Imple //@rankl("Imple", "Implements")
var _ = types.Satis //@rankl("Satis", "Satisfies")
// package-level decl
var _ = types.Sat //@rankl("Sat", "Satisfies")
var _ = types.Ali //@rankl("Ali", "Alias")

// field
var _ = new(ast.File).Packa //@rankl("Packa", "Package")
var _ = new(ast.File).FileS //@rankl("FileS", "FileStart")
var _ = new(types.Info).Use //@rankl("Use", "Uses")
var _ = new(types.Info).Fil //@rankl("Fil", "FileVersions")

// method
var _ = new(token.FileSet).Add //@rankl("Add", "AddFile")
var _ = new(token.FileSet).Remove //@rankl("Remove", "RemoveFile")
var _ = new(types.Checker).Obje //@rankl("Obje", "ObjectOf")
var _ = new(types.Checker).PkgN //@rankl("PkgN", "PkgNameOf")
32 changes: 16 additions & 16 deletions gopls/internal/test/marker/testdata/completion/unimported-std.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ Test of unimported completions respecting the effective Go version of the file.

(See unprefixed file for same test of imported completions.)

These symbols below were introduced in go1.20:
These symbols below were introduced to go/types in go1.22:

types.Satisfied
ast.File.FileStart
(*token.FileSet).RemoveFile
Alias
Info.FileVersions
(Checker).PkgNameOf

The underlying logic depends on versions.FileVersion, which only
behaves correctly in go1.22. (When go1.22 is assured, we can remove
Expand All @@ -18,32 +18,32 @@ the min_go flag but leave the test inputs unchanged.)
-- go.mod --
module example.com

go 1.19
go 1.21

-- a/a.go --
package a

// package-level func
var _ = types.Imple //@rankl("Imple", "Implements")
var _ = types.Satis //@rankl("Satis", "!Satisfies")
var _ = types.Sat //@rankl("Sat", "Satisfies")
var _ = types.Ali //@rankl("Ali", "!Alias")

// (Apparently we don't even offer completions of methods
// (We don't offer completions of methods
// of types from unimported packages, so the fact that
// we don't implement std version filtering isn't evident.)

// field
var _ = new(ast.File).Packa //@rankl("Packa", "!Package")
var _ = new(ast.File).FileS //@rankl("FileS", "!FileStart")
var _ = new(types.Info).Use //@rankl("Use", "!Uses")
var _ = new(types.Info).Fil //@rankl("Fil", "!FileVersions")

// method
var _ = new(token.FileSet).Ad //@rankl("Ad", "!Add")
var _ = new(token.FileSet).Remove //@rankl("Remove", "!RemoveFile")
var _ = new(types.Checker).Obje //@rankl("Obje", "!ObjectOf")
var _ = new(types.Checker).PkgN //@rankl("PkgN", "!PkgNameOf")

-- b/b.go --
//go:build go1.20
//go:build go1.22

package a

// package-level func
var _ = types.Imple //@rankl("Imple", "Implements")
var _ = types.Satis //@rankl("Satis", "Satisfies")
// package-level decl
var _ = types.Sat //@rankl("Sat", "Satisfies")
var _ = types.Ali //@rankl("Ali", "Alias")
Loading

0 comments on commit 176e895

Please sign in to comment.