aboutsummaryrefslogtreecommitdiff
path: root/internal/analysisinternal/analysis.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/analysisinternal/analysis.go')
-rw-r--r--internal/analysisinternal/analysis.go126
1 files changed, 44 insertions, 82 deletions
diff --git a/internal/analysisinternal/analysis.go b/internal/analysisinternal/analysis.go
index 78ee2c06b..d15f0eb7a 100644
--- a/internal/analysisinternal/analysis.go
+++ b/internal/analysisinternal/analysis.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package analysisinternal exposes internal-only fields from go/analysis.
+// Package analysisinternal provides gopls' internal analyses with a
+// number of helper functions that operate on typed syntax trees.
package analysisinternal
import (
@@ -11,20 +12,13 @@ import (
"go/ast"
"go/token"
"go/types"
- "strings"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/internal/lsp/fuzzy"
+ "strconv"
)
-// Flag to gate diagnostics for fuzz tests in 1.18.
+// DiagnoseFuzzTests controls whether the 'tests' analyzer diagnoses fuzz tests
+// in Go 1.18+.
var DiagnoseFuzzTests bool = false
-var (
- GetTypeErrors func(p interface{}) []types.Error
- SetTypeErrors func(p interface{}, errors []types.Error)
-)
-
func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos {
// Get the end position for the type error.
offset, end := fset.PositionFor(start, false).Offset, start
@@ -37,7 +31,7 @@ func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos
return end
}
-func ZeroValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
+func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
under := typ
if n, ok := typ.(*types.Named); ok {
under = n.Underlying()
@@ -57,7 +51,7 @@ func ZeroValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.T
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
return ast.NewIdent("nil")
case *types.Struct:
- texpr := TypeExpr(fset, f, pkg, typ) // typ because we want the name here.
+ texpr := TypeExpr(f, pkg, typ) // typ because we want the name here.
if texpr == nil {
return nil
}
@@ -81,7 +75,10 @@ func IsZeroValue(expr ast.Expr) bool {
}
}
-func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
+// TypeExpr returns syntax for the specified type. References to
+// named types from packages other than pkg are qualified by an appropriate
+// package name, as defined by the import environment of file.
+func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
switch t := typ.(type) {
case *types.Basic:
switch t.Kind() {
@@ -91,7 +88,7 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
return ast.NewIdent(t.Name())
}
case *types.Pointer:
- x := TypeExpr(fset, f, pkg, t.Elem())
+ x := TypeExpr(f, pkg, t.Elem())
if x == nil {
return nil
}
@@ -100,7 +97,7 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
X: x,
}
case *types.Array:
- elt := TypeExpr(fset, f, pkg, t.Elem())
+ elt := TypeExpr(f, pkg, t.Elem())
if elt == nil {
return nil
}
@@ -112,7 +109,7 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
Elt: elt,
}
case *types.Slice:
- elt := TypeExpr(fset, f, pkg, t.Elem())
+ elt := TypeExpr(f, pkg, t.Elem())
if elt == nil {
return nil
}
@@ -120,8 +117,8 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
Elt: elt,
}
case *types.Map:
- key := TypeExpr(fset, f, pkg, t.Key())
- value := TypeExpr(fset, f, pkg, t.Elem())
+ key := TypeExpr(f, pkg, t.Key())
+ value := TypeExpr(f, pkg, t.Elem())
if key == nil || value == nil {
return nil
}
@@ -134,7 +131,7 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
if t.Dir() == types.SendRecv {
dir = ast.SEND | ast.RECV
}
- value := TypeExpr(fset, f, pkg, t.Elem())
+ value := TypeExpr(f, pkg, t.Elem())
if value == nil {
return nil
}
@@ -145,7 +142,7 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
case *types.Signature:
var params []*ast.Field
for i := 0; i < t.Params().Len(); i++ {
- p := TypeExpr(fset, f, pkg, t.Params().At(i).Type())
+ p := TypeExpr(f, pkg, t.Params().At(i).Type())
if p == nil {
return nil
}
@@ -160,7 +157,7 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
}
var returns []*ast.Field
for i := 0; i < t.Results().Len(); i++ {
- r := TypeExpr(fset, f, pkg, t.Results().At(i).Type())
+ r := TypeExpr(f, pkg, t.Results().At(i).Type())
if r == nil {
return nil
}
@@ -184,13 +181,12 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
return ast.NewIdent(t.Obj().Name())
}
pkgName := t.Obj().Pkg().Name()
+
// If the file already imports the package under another name, use that.
- for _, group := range astutil.Imports(fset, f) {
- for _, cand := range group {
- if strings.Trim(cand.Path.Value, `"`) == t.Obj().Pkg().Path() {
- if cand.Name != nil && cand.Name.Name != "" {
- pkgName = cand.Name.Name
- }
+ for _, cand := range f.Imports {
+ if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() {
+ if cand.Name != nil && cand.Name.Name != "" {
+ pkgName = cand.Name.Name
}
}
}
@@ -210,14 +206,6 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
}
}
-type TypeErrorPass string
-
-const (
- NoNewVars TypeErrorPass = "nonewvars"
- NoResultValues TypeErrorPass = "noresultvalues"
- UndeclaredName TypeErrorPass = "undeclaredname"
-)
-
// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable.
// Some examples:
//
@@ -311,19 +299,21 @@ func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) {
})
}
-// FindMatchingIdents finds all identifiers in 'node' that match any of the given types.
+// MatchingIdents finds the names of all identifiers in 'node' that match any of the given types.
// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within
// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that
// is unrecognized.
-func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]*ast.Ident {
- matches := map[types.Type][]*ast.Ident{}
+func MatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]string {
+
// Initialize matches to contain the variable types we are searching for.
+ matches := make(map[types.Type][]string)
for _, typ := range typs {
if typ == nil {
- continue
+ continue // TODO(adonovan): is this reachable?
}
- matches[typ] = []*ast.Ident{}
+ matches[typ] = nil // create entry
}
+
seen := map[types.Object]struct{}{}
ast.Inspect(node, func(n ast.Node) bool {
if n == nil {
@@ -335,8 +325,7 @@ func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *t
//
// x := fakeStruct{f0: x}
//
- assignment, ok := n.(*ast.AssignStmt)
- if ok && pos > assignment.Pos() && pos <= assignment.End() {
+ if assign, ok := n.(*ast.AssignStmt); ok && pos > assign.Pos() && pos <= assign.End() {
return false
}
if n.End() > pos {
@@ -369,17 +358,17 @@ func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *t
return true
}
// The object must match one of the types that we are searching for.
- if idents, ok := matches[obj.Type()]; ok {
- matches[obj.Type()] = append(idents, ast.NewIdent(ident.Name))
- }
- // If the object type does not exactly match any of the target types, greedily
- // find the first target type that the object type can satisfy.
- for typ := range matches {
- if obj.Type() == typ {
- continue
- }
- if equivalentTypes(obj.Type(), typ) {
- matches[typ] = append(matches[typ], ast.NewIdent(ident.Name))
+ // TODO(adonovan): opt: use typeutil.Map?
+ if names, ok := matches[obj.Type()]; ok {
+ matches[obj.Type()] = append(names, ident.Name)
+ } else {
+ // If the object type does not exactly match
+ // any of the target types, greedily find the first
+ // target type that the object type can satisfy.
+ for typ := range matches {
+ if equivalentTypes(obj.Type(), typ) {
+ matches[typ] = append(matches[typ], ident.Name)
+ }
}
}
return true
@@ -388,7 +377,7 @@ func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *t
}
func equivalentTypes(want, got types.Type) bool {
- if want == got || types.Identical(want, got) {
+ if types.Identical(want, got) {
return true
}
// Code segment to help check for untyped equality from (golang/go#32146).
@@ -399,30 +388,3 @@ func equivalentTypes(want, got types.Type) bool {
}
return types.AssignableTo(want, got)
}
-
-// FindBestMatch employs fuzzy matching to evaluate the similarity of each given identifier to the
-// given pattern. We return the identifier whose name is most similar to the pattern.
-func FindBestMatch(pattern string, idents []*ast.Ident) ast.Expr {
- fuzz := fuzzy.NewMatcher(pattern)
- var bestFuzz ast.Expr
- highScore := float32(0) // minimum score is 0 (no match)
- for _, ident := range idents {
- // TODO: Improve scoring algorithm.
- score := fuzz.Score(ident.Name)
- if score > highScore {
- highScore = score
- bestFuzz = ident
- } else if score == 0 {
- // Order matters in the fuzzy matching algorithm. If we find no match
- // when matching the target to the identifier, try matching the identifier
- // to the target.
- revFuzz := fuzzy.NewMatcher(ident.Name)
- revScore := revFuzz.Score(pattern)
- if revScore > highScore {
- highScore = revScore
- bestFuzz = ident
- }
- }
- }
- return bestFuzz
-}