Skip to content

Commit

Permalink
all: merge master (abc106c) into gopls-release-branch.0.8
Browse files Browse the repository at this point in the history
Also add a replace directive to gopls/go.mod.

Updates golang/go#51074

Conflicts:

- gopls/go.mod (due to gofumpt update)

Merge List:

+ 2022-02-28 abc106c gopls/integration/govim: build gopls using go1.18rc1
+ 2022-02-28 c2ddf3d internal/lsp: add quick fix for unsupported feature
+ 2022-02-28 0e44f7a gopls/doc/advanced.md: correct commands for unstable version build
+ 2022-02-25 acdddf6 go/ssa: allows right operand of a shift to be signed.
+ 2022-02-25 9ffa3ad internal/lsp: Provide completions for test function definitions
+ 2022-02-24 b7525f4 internal/lsp: hash go version into package key
+ 2022-02-24 5210e0c gopls: wire in LangVersion and ModulePath for gofumpt formatting
+ 2022-02-24 e6ef770 go/types/typeutil: don't recurse into constraints when hashing tparams
+ 2022-02-23 258e473 internal/lsp/source: disable the useany analyzer by default
+ 2022-02-23 b7d2949 internal/lsp: don't store diagnostics for builtin.go
+ 2022-02-23 4f21f7a gopls: update gofumpt to v0.3.0
+ 2022-02-22 3e31058 internal/imports: update to permit multiple main modules
+ 2022-02-22 43f084e internal/typesinternal: update typesinternal for 1.18
+ 2022-02-18 897bd77 internal/gocommand: remove support for -workfile
+ 2022-02-17 e6a7e13 go/analysis/tools/internal/checker: add support for RunDespiteError

Change-Id: I4ca5a581cb276b904f4a9d73d686aaa7cb0c6093
  • Loading branch information
findleyr committed Feb 28, 2022
2 parents f1bc086 + abc106c commit 1868799
Show file tree
Hide file tree
Showing 37 changed files with 1,177 additions and 85 deletions.
6 changes: 3 additions & 3 deletions cmd/godoc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,21 +207,21 @@ func main() {
fmt.Printf("using module mode; GOMOD=%s\n", goModFile)

// Detect whether to use vendor mode or not.
mainMod, vendorEnabled, err := gocommand.VendorEnabled(context.Background(), gocommand.Invocation{}, &gocommand.Runner{})
vendorEnabled, mainModVendor, err := gocommand.VendorEnabled(context.Background(), gocommand.Invocation{}, &gocommand.Runner{})
if err != nil {
fmt.Fprintf(os.Stderr, "failed to determine if vendoring is enabled: %v", err)
os.Exit(1)
}
if vendorEnabled {
// Bind the root directory of the main module.
fs.Bind(path.Join("/src", mainMod.Path), gatefs.New(vfs.OS(mainMod.Dir), fsGate), "/", vfs.BindAfter)
fs.Bind(path.Join("/src", mainModVendor.Path), gatefs.New(vfs.OS(mainModVendor.Dir), fsGate), "/", vfs.BindAfter)

// Bind the vendor directory.
//
// Note that in module mode, vendor directories in locations
// other than the main module's root directory are ignored.
// See https://golang.org/ref/mod#vendoring.
vendorDir := filepath.Join(mainMod.Dir, "vendor")
vendorDir := filepath.Join(mainModVendor.Dir, "vendor")
fs.Bind("/src", gatefs.New(vfs.OS(vendorDir), fsGate), "/", vfs.BindAfter)

} else {
Expand Down
57 changes: 47 additions & 10 deletions go/analysis/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package checker
import (
"bytes"
"encoding/gob"
"errors"
"flag"
"fmt"
"go/format"
Expand Down Expand Up @@ -129,8 +130,13 @@ func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
allSyntax := needFacts(analyzers)
initial, err := load(args, allSyntax)
if err != nil {
log.Print(err)
return 1 // load errors
if _, ok := err.(typeParseError); !ok {
// Fail when some of the errors are not
// related to parsing nor typing.
log.Print(err)
return 1
}
// TODO: filter analyzers based on RunDespiteError?
}

// Print the results.
Expand All @@ -139,11 +145,17 @@ func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
if Fix {
applyFixes(roots)
}

return printDiagnostics(roots)
}

// load loads the initial packages.
// typeParseError represents a package load error
// that is related to typing and parsing.
type typeParseError struct {
error
}

// load loads the initial packages. If all loading issues are related to
// typing and parsing, the returned error is of type typeParseError.
func load(patterns []string, allSyntax bool) ([]*packages.Package, error) {
mode := packages.LoadSyntax
if allSyntax {
Expand All @@ -155,18 +167,43 @@ func load(patterns []string, allSyntax bool) ([]*packages.Package, error) {
}
initial, err := packages.Load(&conf, patterns...)
if err == nil {
if n := packages.PrintErrors(initial); n > 1 {
err = fmt.Errorf("%d errors during loading", n)
} else if n == 1 {
err = fmt.Errorf("error during loading")
} else if len(initial) == 0 {
if len(initial) == 0 {
err = fmt.Errorf("%s matched no packages", strings.Join(patterns, " "))
} else {
err = loadingError(initial)
}
}

return initial, err
}

// loadingError checks for issues during the loading of initial
// packages. Returns nil if there are no issues. Returns error
// of type typeParseError if all errors, including those in
// dependencies, are related to typing or parsing. Otherwise,
// a plain error is returned with an appropriate message.
func loadingError(initial []*packages.Package) error {
var err error
if n := packages.PrintErrors(initial); n > 1 {
err = fmt.Errorf("%d errors during loading", n)
} else if n == 1 {
err = errors.New("error during loading")
} else {
// no errors
return nil
}
all := true
packages.Visit(initial, nil, func(pkg *packages.Package) {
for _, err := range pkg.Errors {
typeOrParse := err.Kind == packages.TypeError || err.Kind == packages.ParseError
all = all && typeOrParse
}
})
if all {
return typeParseError{err}
}
return err
}

// TestAnalyzer applies an analysis to a set of packages (and their
// dependencies if necessary) and returns the results.
//
Expand Down
59 changes: 59 additions & 0 deletions go/analysis/internal/checker/checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,62 @@ func run(pass *analysis.Pass) (interface{}, error) {

return nil, nil
}

func TestRunDespiteErrors(t *testing.T) {
testenv.NeedsGoPackages(t)

files := map[string]string{
"rderr/test.go": `package rderr
// Foo deliberately has a type error
func Foo(s string) int {
return s + 1
}
`}

testdata, cleanup, err := analysistest.WriteFiles(files)
if err != nil {
t.Fatal(err)
}
path := filepath.Join(testdata, "src/rderr/test.go")

// A no-op analyzer that should finish regardless of
// parse or type errors in the code.
noop := &analysis.Analyzer{
Name: "noop",
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: func(pass *analysis.Pass) (interface{}, error) {
return nil, nil
},
RunDespiteErrors: true,
}

for _, test := range []struct {
name string
pattern []string
analyzers []*analysis.Analyzer
code int
}{
// parse/type errors
{name: "skip-error", pattern: []string{"file=" + path}, analyzers: []*analysis.Analyzer{analyzer}, code: 1},
{name: "despite-error", pattern: []string{"file=" + path}, analyzers: []*analysis.Analyzer{noop}, code: 0},
// combination of parse/type errors and no errors
{name: "despite-error-and-no-error", pattern: []string{"file=" + path, "sort"}, analyzers: []*analysis.Analyzer{analyzer, noop}, code: 1},
// non-existing package error
{name: "no-package", pattern: []string{"xyz"}, analyzers: []*analysis.Analyzer{analyzer}, code: 1},
{name: "no-package-despite-error", pattern: []string{"abc"}, analyzers: []*analysis.Analyzer{noop}, code: 1},
{name: "no-multi-package-despite-error", pattern: []string{"xyz", "abc"}, analyzers: []*analysis.Analyzer{noop}, code: 1},
// combination of type/parsing and different errors
{name: "different-errors", pattern: []string{"file=" + path, "xyz"}, analyzers: []*analysis.Analyzer{analyzer, noop}, code: 1},
// non existing dir error
{name: "no-match-dir", pattern: []string{"file=non/existing/dir"}, analyzers: []*analysis.Analyzer{analyzer, noop}, code: 1},
// no errors
{name: "no-errors", pattern: []string{"sort"}, analyzers: []*analysis.Analyzer{analyzer, noop}, code: 0},
} {
if got := checker.Run(test.pattern, test.analyzers); got != test.code {
t.Errorf("got incorrect exit code %d for test %s; want %d", got, test.name, test.code)
}
}

defer cleanup()
}
13 changes: 10 additions & 3 deletions go/ssa/emit.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,16 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.
case token.SHL, token.SHR:
x = emitConv(f, x, t)
// y may be signed or an 'untyped' constant.
// TODO(adonovan): whence signed values?
if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
y = emitConv(f, y, types.Typ[types.Uint64])

// There is a runtime panic if y is signed and <0. Instead of inserting a check for y<0
// and converting to an unsigned value (like the compiler) leave y as is.

if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
// Untyped conversion:
// Spec https://go.dev/ref/spec#Operators:
// The right operand in a shift expression must have integer type or be an untyped constant
// representable by a value of type uint.
y = emitConv(f, y, types.Typ[types.Uint])
}

case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
Expand Down
1 change: 1 addition & 0 deletions go/ssa/interp/interp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ var gorootTestTests = []string{
var testdataTests = []string{
"boundmeth.go",
"complit.go",
"convert.go",
"coverage.go",
"defer.go",
"fieldprom.go",
Expand Down
32 changes: 30 additions & 2 deletions go/ssa/interp/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ func asUint64(x value) uint64 {
panic(fmt.Sprintf("cannot convert %T to uint64", x))
}

// asUnsigned returns the value of x, which must be an integer type, as its equivalent unsigned type,
// and returns true if x is non-negative.
func asUnsigned(x value) (value, bool) {
switch x := x.(type) {
case int:
return uint(x), x >= 0
case int8:
return uint8(x), x >= 0
case int16:
return uint16(x), x >= 0
case int32:
return uint32(x), x >= 0
case int64:
return uint64(x), x >= 0
case uint, uint8, uint32, uint64, uintptr:
return x, true
}
panic(fmt.Sprintf("cannot convert %T to unsigned", x))
}

// zero returns a new "zero" value of the specified type.
func zero(t types.Type) value {
switch t := t.(type) {
Expand Down Expand Up @@ -576,7 +596,11 @@ func binop(op token.Token, t types.Type, x, y value) value {
}

case token.SHL:
y := asUint64(y)
u, ok := asUnsigned(y)
if !ok {
panic("negative shift amount")
}
y := asUint64(u)
switch x.(type) {
case int:
return x.(int) << y
Expand All @@ -603,7 +627,11 @@ func binop(op token.Token, t types.Type, x, y value) value {
}

case token.SHR:
y := asUint64(y)
u, ok := asUnsigned(y)
if !ok {
panic("negative shift amount")
}
y := asUint64(u)
switch x.(type) {
case int:
return x.(int) >> y
Expand Down
38 changes: 38 additions & 0 deletions go/ssa/interp/testdata/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test conversion operations.

package main

func left(x int) { _ = 1 << x }
func right(x int) { _ = 1 >> x }

func main() {
wantPanic(
func() {
left(-1)
},
"runtime error: negative shift amount",
)
wantPanic(
func() {
right(-1)
},
"runtime error: negative shift amount",
)
}

func wantPanic(fn func(), s string) {
defer func() {
err := recover()
if err == nil {
panic("expected panic")
}
if got := err.(error).Error(); got != s {
panic("expected panic " + s + " got " + got)
}
}()
fn()
}
10 changes: 6 additions & 4 deletions go/types/typeutil/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func (h Hasher) hashFor(t types.Type) uint32 {
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
// See go/types.identicalTypes for rationale.
n := tuple.Len()
var hash uint32 = 9137 + 2*uint32(n)
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 3 * h.Hash(tuple.At(i).Type())
}
Expand All @@ -398,7 +398,7 @@ func (h Hasher) hashUnion(t *typeparams.Union) uint32 {
}

func (h Hasher) hashTermSet(terms []*typeparams.Term) uint32 {
var hash uint32 = 9157 + 2*uint32(len(terms))
hash := 9157 + 2*uint32(len(terms))
for _, term := range terms {
// term order is not significant.
termHash := h.Hash(term.Type())
Expand All @@ -416,14 +416,16 @@ func (h Hasher) hashTermSet(terms []*typeparams.Term) uint32 {
// If h.sigTParams is set and contains t, then we are in the process of hashing
// a signature, and the hash value of t must depend only on t's index and
// constraint: signatures are considered identical modulo type parameter
// renaming.
// renaming. To avoid infinite recursion, we only hash the type parameter
// index, and rely on types.Identical to handle signatures where constraints
// are not identical.
//
// Otherwise the hash of t depends only on t's pointer identity.
func (h Hasher) hashTypeParam(t *typeparams.TypeParam) uint32 {
if h.sigTParams != nil {
i := t.Index()
if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
return 9173 + 2*h.Hash(t.Constraint()) + 3*uint32(i)
return 9173 + 3*uint32(i)
}
}
return h.hashPtr(t.Obj())
Expand Down
26 changes: 26 additions & 0 deletions go/types/typeutil/map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,17 @@ var ME2 = G2[int].M
// ME1Type should have identical type as ME1.
var ME1Type func(G1[int], G1[int], G2[int])
// Examples from issue #51314
type Constraint[T any] interface{}
func Foo[T Constraint[T]]() {}
func Fn[T1 ~*T2, T2 ~*T1](t1 T1, t2 T2) {}
// Bar and Baz are identical to Foo.
func Bar[P Constraint[P]]() {}
func Baz[Q any]() {} // The underlying type of Constraint[P] is any.
// But Quux is not.
func Quux[Q interface{ quux() }]() {}
`

fset := token.NewFileSet()
Expand Down Expand Up @@ -284,6 +295,13 @@ var ME1Type func(G1[int], G1[int], G2[int])
ME1 = scope.Lookup("ME1").Type()
ME1Type = scope.Lookup("ME1Type").Type()
ME2 = scope.Lookup("ME2").Type()

Constraint = scope.Lookup("Constraint").Type()
Foo = scope.Lookup("Foo").Type()
Fn = scope.Lookup("Fn").Type()
Bar = scope.Lookup("Foo").Type()
Baz = scope.Lookup("Foo").Type()
Quux = scope.Lookup("Quux").Type()
)

tmap := new(typeutil.Map)
Expand Down Expand Up @@ -345,6 +363,14 @@ var ME1Type func(G1[int], G1[int], G2[int])
{ME1, "ME1", true},
{ME1Type, "ME1Type", false},
{ME2, "ME2", true},

// See golang/go#51314: avoid infinite recursion on cyclic type constraints.
{Constraint, "Constraint", true},
{Foo, "Foo", true},
{Fn, "Fn", true},
{Bar, "Bar", false},
{Baz, "Baz", false},
{Quux, "Quux", true},
}

for _, step := range steps {
Expand Down
Loading

0 comments on commit 1868799

Please sign in to comment.