internal/lsp: fix bad *ast.ArrayTypes for completion

Currently array and slice literals don't work very well for
completion. When go/parser is not expecting a type, it often turns
array types (e.g. "[]int") into *ast.BadExpr, which messes up
completion because we can't figure out the prefix from *ast.BadExpr,
and *ast.BadExprs don't get type checked.

This change addresses the first problem of not being able to figure
out the prefix. If we see an *ast.BadExpr, we now blindly try to
reparse it as a composite literal by adding on "{}". If we end up with
an *ast.CompositeLit with an *ast.ArrayType "Type", we swap
the *ast.BadExpr for the *ast.ArrayType. This approach is dumb but
simple, and fixes lexical completions in array types.

Change-Id: Ifa42e646bcbf2a30170d73e6dd11982384d40b43
Reviewed-on: https://go-review.googlesource.com/c/tools/+/197437
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index 37b7818..83eb48a 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -199,45 +199,101 @@
 func fix(ctx context.Context, n ast.Node, tok *token.File, src []byte) error {
 	var (
 		ancestors []ast.Node
+		parent    ast.Node
 		err       error
 	)
 	ast.Inspect(n, func(n ast.Node) bool {
 		if n == nil {
 			if len(ancestors) > 0 {
 				ancestors = ancestors[:len(ancestors)-1]
+				if len(ancestors) > 0 {
+					parent = ancestors[len(ancestors)-1]
+				}
 			}
 			return false
 		}
+
 		switch n := n.(type) {
 		case *ast.BadStmt:
-			var parent ast.Node
-			if len(ancestors) > 0 {
-				parent = ancestors[len(ancestors)-1]
-			}
-			err = parseDeferOrGoStmt(n, parent, tok, src) // don't shadow err
+			err = fixDeferOrGoStmt(n, parent, tok, src) // don't shadow err
 			if err == nil {
-				// Recursively fix any BadStmts in our fixed node.
+				// Recursively fix in our fixed node.
 				err = fix(ctx, parent, tok, src)
 			} else {
 				err = errors.Errorf("unable to parse defer or go from *ast.BadStmt: %v", err)
 			}
 			return false
+		case *ast.BadExpr:
+			// Don't propagate this error since *ast.BadExpr is very common
+			// and it is only sometimes due to array types. Errors from here
+			// are expected and not actionable in general.
+			fixArrayErr := fixArrayType(n, parent, tok, src)
+			if fixArrayErr == nil {
+				// Recursively fix in our fixed node.
+				err = fix(ctx, parent, tok, src)
+			}
+			return false
 		default:
 			ancestors = append(ancestors, n)
+			parent = n
 			return true
 		}
 	})
 	return err
 }
 
-// parseDeferOrGoStmt tries to parse an *ast.BadStmt into a defer or a go statement.
+// fixArrayType tries to parse an *ast.BadExpr into an *ast.ArrayType.
+// go/parser often turns lone array types like "[]int" into BadExprs
+// if it isn't expecting a type.
+func fixArrayType(bad *ast.BadExpr, parent ast.Node, tok *token.File, src []byte) error {
+	// Our expected input is a bad expression that looks like "[]someExpr".
+
+	from := bad.Pos()
+	to := bad.End()
+
+	if !from.IsValid() || !to.IsValid() {
+		return errors.Errorf("invalid BadExpr from/to: %d/%d", from, to)
+	}
+
+	exprBytes := make([]byte, 0, int(to-from)+2)
+	// Avoid doing tok.Offset(to) since that panics if badExpr ends at EOF.
+	exprBytes = append(exprBytes, src[tok.Offset(from):tok.Offset(to-1)+1]...)
+
+	// Add "{}" to turn our ArrayType into a CompositeLit. This is to
+	// handle the case of "[...]int" where we must make it a composite
+	// literal to be parseable.
+	exprBytes = append(exprBytes, '{', '}')
+
+	expr, err := parseExpr(from, exprBytes)
+	if err != nil {
+		return err
+	}
+
+	cl, _ := expr.(*ast.CompositeLit)
+	if cl == nil {
+		return errors.Errorf("expr not compLit (%T)", expr)
+	}
+
+	at, _ := cl.Type.(*ast.ArrayType)
+	if at == nil {
+		return errors.Errorf("compLit type not array (%T)", cl.Type)
+	}
+
+	if !replaceNode(parent, bad, at) {
+		return errors.Errorf("couldn't replace array type")
+	}
+
+	return nil
+}
+
+// fixDeferOrGoStmt tries to parse an *ast.BadStmt into a defer or a go statement.
 //
 // go/parser packages a statement of the form "defer x." as an *ast.BadStmt because
 // it does not include a call expression. This means that go/types skips type-checking
 // this statement entirely, and we can't use the type information when completing.
 // Here, we try to generate a fake *ast.DeferStmt or *ast.GoStmt to put into the AST,
 // instead of the *ast.BadStmt.
-func parseDeferOrGoStmt(bad *ast.BadStmt, parent ast.Node, tok *token.File, src []byte) error {
+func fixDeferOrGoStmt(bad *ast.BadStmt, parent ast.Node, tok *token.File, src []byte) error {
 	// Check if we have a bad statement containing either a "go" or "defer".
 	s := &scanner.Scanner{}
 	s.Init(tok, src, nil, 0)
@@ -357,38 +413,11 @@
 		exprBytes = append(exprBytes, '_')
 	}
 
-	// Wrap our expression to make it a valid Go file we can pass to ParseFile.
-	fileSrc := bytes.Join([][]byte{
-		[]byte("package fake;func _(){"),
-		exprBytes,
-		[]byte("}"),
-	}, nil)
-
-	// Use ParseFile instead of ParseExpr because ParseFile has
-	// best-effort behavior, whereas ParseExpr fails hard on any error.
-	fakeFile, err := parser.ParseFile(token.NewFileSet(), "", fileSrc, 0)
-	if fakeFile == nil {
-		return errors.Errorf("error reading fake file source: %v", err)
+	expr, err := parseExpr(from, exprBytes)
+	if err != nil {
+		return err
 	}
 
-	// Extract our expression node from inside the fake file.
-	if len(fakeFile.Decls) == 0 {
-		return errors.Errorf("error parsing fake file: %v", err)
-	}
-	fakeDecl, _ := fakeFile.Decls[0].(*ast.FuncDecl)
-	if fakeDecl == nil || len(fakeDecl.Body.List) == 0 {
-		return errors.Errorf("no statement in %s: %v", exprBytes, err)
-	}
-	exprStmt, ok := fakeDecl.Body.List[0].(*ast.ExprStmt)
-	if !ok {
-		return errors.Errorf("no expr in %s: %v", exprBytes, err)
-	}
-	expr := exprStmt.X
-
-	// parser.ParseExpr returns undefined positions.
-	// Adjust them for the current file.
-	offsetPositions(expr, from-1-(expr.Pos()-1))
-
 	// Package the expression into a fake *ast.CallExpr and re-insert
 	// into the function.
 	call := &ast.CallExpr{
@@ -396,6 +425,7 @@
 		Lparen: to,
 		Rparen: to,
 	}
+
 	switch stmt := stmt.(type) {
 	case *ast.DeferStmt:
 		stmt.Call = call
@@ -404,12 +434,53 @@
 	}
 
 	if !replaceNode(parent, bad, stmt) {
-		return errors.Errorf("couldn't replace %T in %T", stmt, parent)
+		return errors.Errorf("couldn't replace CallExpr")
 	}
 
 	return nil
 }
 
+// parseExpr parses the expression in src and updates its position to
+// start at pos.
+func parseExpr(pos token.Pos, src []byte) (ast.Expr, error) {
+	// Wrap our expression to make it a valid Go file we can pass to ParseFile.
+	fileSrc := bytes.Join([][]byte{
+		[]byte("package fake;func _(){"),
+		src,
+		[]byte("}"),
+	}, nil)
+
+	// Use ParseFile instead of ParseExpr because ParseFile has
+	// best-effort behavior, whereas ParseExpr fails hard on any error.
+	fakeFile, err := parser.ParseFile(token.NewFileSet(), "", fileSrc, 0)
+	if fakeFile == nil {
+		return nil, errors.Errorf("error reading fake file source: %v", err)
+	}
+
+	// Extract our expression node from inside the fake file.
+	if len(fakeFile.Decls) == 0 {
+		return nil, errors.Errorf("error parsing fake file: %v", err)
+	}
+
+	fakeDecl, _ := fakeFile.Decls[0].(*ast.FuncDecl)
+	if fakeDecl == nil || len(fakeDecl.Body.List) == 0 {
+		return nil, errors.Errorf("no statement in %s: %v", src, err)
+	}
+
+	exprStmt, ok := fakeDecl.Body.List[0].(*ast.ExprStmt)
+	if !ok {
+		return nil, errors.Errorf("no expr in %s: %v", src, err)
+	}
+
+	expr := exprStmt.X
+
+	// parser.ParseExpr returns undefined positions.
+	// Adjust them for the current file.
+	offsetPositions(expr, pos-1-(expr.Pos()-1))
+
+	return expr, nil
+}
+
 var tokenPosType = reflect.TypeOf(token.NoPos)
 
 // offsetPositions applies an offset to the positions in an ast.Node.
@@ -433,7 +504,7 @@
 					continue
 				}
 
-				f.SetInt(int64(f.Interface().(token.Pos) + offset))
+				f.SetInt(f.Int() + int64(offset))
 			}
 		}
 
diff --git a/internal/lsp/testdata/arraytype/array_type.go.in b/internal/lsp/testdata/arraytype/array_type.go.in
new file mode 100644
index 0000000..2f1dbaf
--- /dev/null
+++ b/internal/lsp/testdata/arraytype/array_type.go.in
@@ -0,0 +1,25 @@
+package arraytype
+
+import (
+	"golang.org/x/tools/internal/lsp/foo"
+)
+
+func _() {
+	var (
+		val string //@item(atVal, "val", "string", "var")
+	)
+
+	[] //@complete(" //", atVal, PackageFoo)
+
+	[]val //@complete(" //", atVal)
+
+	[]foo.StructFoo //@complete(" //", StructFoo)
+
+	[]*foo.StructFoo //@complete(" //", StructFoo)
+
+	[...]foo.StructFoo //@complete(" //", StructFoo)
+
+	[2][][4]foo.StructFoo //@complete(" //", StructFoo)
+
+	[]struct { f []foo.StructFoo } //@complete(" }", StructFoo)
+}
diff --git a/internal/lsp/testdata/foo/foo.go b/internal/lsp/testdata/foo/foo.go
index 444f07f..277ac53 100644
--- a/internal/lsp/testdata/foo/foo.go
+++ b/internal/lsp/testdata/foo/foo.go
@@ -1,4 +1,4 @@
-package foo //@mark(PackageFoo, "foo")
+package foo //@mark(PackageFoo, "foo"),item(PackageFoo, "foo", "\"golang.org/x/tools/internal/lsp/foo\"", "package")
 
 type StructFoo struct { //@item(StructFoo, "StructFoo", "struct{...}", "struct")
 	Value int //@item(Value, "Value", "int", "field")
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 042115d..75f2e36 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -1,5 +1,5 @@
 -- summary --
-CompletionsCount = 178
+CompletionsCount = 185
 CompletionSnippetCount = 39
 UnimportedCompletionsCount = 1
 DeepCompletionsCount = 5