internal/lsp: fix completion for nested *ast.BadStmt

Now when trying to fix *ast.BadStmt, we parse the manually extracted
expression using parser.ParseFile instead of parser.ParseExpr.
ParseFile will yield *ast.BadStmt nodes for any bad statements nested
in our first bad statement, allowing us to fix them recursively.

To turn our expression into a "valid" file we can pass to
parser.ParseFile, I wrapped it thusly:

package fake

func _() {
  <our expression>
}

Change-Id: I0d4fd4ebce6450021da8e03caa11d0ae5152ea8d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/194342
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index 5413062..643b1d8 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -171,12 +171,12 @@
 
 // fix inspects the AST and potentially modifies any *ast.BadStmts so that it can be
 // type-checked more effectively.
-func fix(ctx context.Context, file *ast.File, tok *token.File, src []byte) error {
+func fix(ctx context.Context, n ast.Node, tok *token.File, src []byte) error {
 	var (
 		ancestors []ast.Node
 		err       error
 	)
-	ast.Inspect(file, func(n ast.Node) bool {
+	ast.Inspect(n, func(n ast.Node) bool {
 		if n == nil {
 			if len(ancestors) > 0 {
 				ancestors = ancestors[:len(ancestors)-1]
@@ -190,7 +190,10 @@
 				parent = ancestors[len(ancestors)-1]
 			}
 			err = parseDeferOrGoStmt(n, parent, tok, src) // don't shadow err
-			if err != nil {
+			if err == nil {
+				// Recursively fix any BadStmts 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
@@ -329,17 +332,40 @@
 		exprBytes = append(exprBytes, '_')
 	}
 
-	exprStr := string(exprBytes)
-	expr, err := parser.ParseExpr(exprStr)
-	if expr == nil {
-		return errors.Errorf("no expr in %s: %v", exprStr, err)
+	// 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)
 	}
 
+	// 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)
+	offsetPositions(expr, from-1-(expr.Pos()-1))
 
-	// Package the expression into a fake *ast.CallExpr and re-insert into the function.
+	// Package the expression into a fake *ast.CallExpr and re-insert
+	// into the function.
 	call := &ast.CallExpr{
 		Fun:    expr,
 		Lparen: to,
diff --git a/internal/lsp/testdata/badstmt/badstmt_4.go.in b/internal/lsp/testdata/badstmt/badstmt_4.go.in
new file mode 100644
index 0000000..8767306
--- /dev/null
+++ b/internal/lsp/testdata/badstmt/badstmt_4.go.in
@@ -0,0 +1,11 @@
+package badstmt
+
+import (
+	"golang.org/x/tools/internal/lsp/foo"
+)
+
+func _() {
+	go func() {
+		defer foo. //@complete(" //", Foo, IntFoo, StructFoo)
+	}
+}
diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go
index 92a7260..67f155a 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -29,7 +29,7 @@
 // We hardcode the expected number of test cases to ensure that all tests
 // are being executed. If a test is added, this number must be changed.
 const (
-	ExpectedCompletionsCount       = 159
+	ExpectedCompletionsCount       = 160
 	ExpectedCompletionSnippetCount = 16
 	ExpectedDiagnosticsCount       = 21
 	ExpectedFormatCount            = 6