mirror of https://github.com/golang/go.git
go/analysis/passes/ctrlflow: add typeparams test
This CL adds a test for the assign pass that involves use of generics. Updates golang/go#48704 Change-Id: I19d27932f0d326e2456b6714f76de01d46f1b1e3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/357777 Trust: Zvonimir Pavlinovic <zpavlinovic@google.com> Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
8de2a7fd17
commit
c8ad2e1325
|
|
@ -16,9 +16,11 @@ import (
|
|||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/cfg"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
|
|
@ -187,8 +189,12 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
|
|||
return false // panic never returns
|
||||
}
|
||||
|
||||
// Is this a static call?
|
||||
fn := typeutil.StaticCallee(c.pass.TypesInfo, call)
|
||||
// Is this a static call? Also includes static functions
|
||||
// parameterized by a type. Such functions may or may not
|
||||
// return depending on the parameter type, but in some
|
||||
// cases the answer is definite. We let ctrlflow figure
|
||||
// that out.
|
||||
fn := staticCallee(c.pass.TypesInfo, call)
|
||||
if fn == nil {
|
||||
return true // callee not statically known; be conservative
|
||||
}
|
||||
|
|
@ -204,6 +210,50 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
|
|||
return !c.pass.ImportObjectFact(fn, new(noReturn))
|
||||
}
|
||||
|
||||
// staticCallee returns static function, if any, called by call.
|
||||
// Effectivelly reduces to typeutil.StaticCallee. In addition,
|
||||
// returns static function parameterized by a type, if any.
|
||||
//
|
||||
// TODO(zpavlinovic): can this be replaced by typeutil.StaticCallee
|
||||
// in the future?
|
||||
func staticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
|
||||
if fn := typeutil.StaticCallee(info, call); fn != nil {
|
||||
return fn
|
||||
}
|
||||
return staticTypeParamCallee(info, call)
|
||||
}
|
||||
|
||||
// staticTypeParamCallee returns the static function in call, if any,
|
||||
// expected to be parameterized by a type.
|
||||
func staticTypeParamCallee(info *types.Info, call *ast.CallExpr) *types.Func {
|
||||
ix := typeparams.GetIndexExprData(astutil.Unparen(call.Fun))
|
||||
if ix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var obj types.Object
|
||||
switch fun := ix.X.(type) {
|
||||
case *ast.Ident:
|
||||
obj = info.Uses[fun] // type, var, builtin, or declared func
|
||||
case *ast.SelectorExpr:
|
||||
if sel, ok := info.Selections[fun]; ok {
|
||||
obj = sel.Obj() // method or field
|
||||
} else {
|
||||
obj = info.Uses[fun.Sel] // qualified identifier?
|
||||
}
|
||||
}
|
||||
|
||||
if f, ok := obj.(*types.Func); ok && !interfaceMethod(f) {
|
||||
return f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func interfaceMethod(f *types.Func) bool {
|
||||
recv := f.Type().(*types.Signature).Recv()
|
||||
return recv != nil && types.IsInterface(recv.Type())
|
||||
}
|
||||
|
||||
var panicBuiltin = types.Universe.Lookup("panic").(*types.Builtin)
|
||||
|
||||
func hasReachableReturn(g *cfg.CFG) bool {
|
||||
|
|
|
|||
|
|
@ -10,13 +10,19 @@ import (
|
|||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/ctrlflow"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
|
||||
// load testdata/src/a/a.go
|
||||
results := analysistest.Run(t, testdata, ctrlflow.Analyzer, "a")
|
||||
tests := []string{"a"}
|
||||
if typeparams.Enabled {
|
||||
// and testdata/src/typeparams/typeparams.go when possible
|
||||
tests = append(tests, "typeparams")
|
||||
}
|
||||
results := analysistest.Run(t, testdata, ctrlflow.Analyzer, tests...)
|
||||
|
||||
// Perform a minimal smoke test on
|
||||
// the result (CFG) computed by ctrlflow.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
package a
|
||||
|
||||
// This file tests facts produced by ctrlflow.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
package lib
|
||||
|
||||
func CanReturn() {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
package a
|
||||
|
||||
// This file tests facts produced by ctrlflow.
|
||||
|
||||
var cond bool
|
||||
|
||||
var funcs = []func(){func() {}}
|
||||
|
||||
func a[A any]() { // want a:"noReturn"
|
||||
if cond {
|
||||
funcs[0]()
|
||||
b[A]()
|
||||
} else {
|
||||
for {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func b[B any]() { // want b:"noReturn"
|
||||
select {}
|
||||
}
|
||||
|
||||
func c[A, B any]() { // want c:"noReturn"
|
||||
if cond {
|
||||
a[A]()
|
||||
} else {
|
||||
d[A, B]()
|
||||
}
|
||||
}
|
||||
|
||||
func d[A, B any]() { // want d:"noReturn"
|
||||
b[B]()
|
||||
}
|
||||
|
||||
type I[T any] interface {
|
||||
Id(T) T
|
||||
}
|
||||
|
||||
func e[T any](i I[T], t T) T {
|
||||
return i.Id(t)
|
||||
}
|
||||
|
||||
func k[T any](i I[T], t T) T { // want k:"noReturn"
|
||||
b[T]()
|
||||
return i.Id(t)
|
||||
}
|
||||
|
||||
type T[X any] int
|
||||
|
||||
func (T[X]) method1() { // want method1:"noReturn"
|
||||
a[X]()
|
||||
}
|
||||
|
||||
func (T[X]) method2() { // (may return)
|
||||
if cond {
|
||||
a[X]()
|
||||
} else {
|
||||
funcs[0]()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue