internal/lsp: adjust extract function range if block statement

If the selected region is a block statement, gopls
does not return a valid function extraction. This
change adjusts the range to be the statements inside
of the selected block statement.

Fixes golang/go#48963

Change-Id: I9b1fb5005f961f30c1fa0333cd1f2050ed5eedef
Reviewed-on: https://go-review.googlesource.com/c/tools/+/357615
Trust: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/extract.go b/internal/lsp/source/extract.go
index 54170fd..43b414a 100644
--- a/internal/lsp/source/extract.go
+++ b/internal/lsp/source/extract.go
@@ -995,6 +995,16 @@
 	if start == nil || end == nil {
 		return nil, false, false, fmt.Errorf("range does not map to AST nodes")
 	}
+	// If the region is a blockStmt, use the first and last nodes in the block
+	// statement.
+	// <rng.start>{ ... }<rng.end> => { <rng.start>...<rng.end> }
+	if blockStmt, ok := start.(*ast.BlockStmt); ok {
+		if len(blockStmt.List) == 0 {
+			return nil, false, false, fmt.Errorf("range maps to empty block statement")
+		}
+		start, end = blockStmt.List[0], blockStmt.List[len(blockStmt.List)-1]
+		rng.Start, rng.End = start.Pos(), end.End()
+	}
 	return &fnExtractParams{
 		tok:   tok,
 		path:  path,
diff --git a/internal/lsp/testdata/extract/extract_function/extract_basic.go b/internal/lsp/testdata/extract/extract_function/extract_basic.go
index b5b9efd..5e44de2 100644
--- a/internal/lsp/testdata/extract/extract_function/extract_basic.go
+++ b/internal/lsp/testdata/extract/extract_function/extract_basic.go
@@ -1,7 +1,8 @@
 package extract
 
-func _() {
+func _() { //@mark(exSt25, "{")
 	a := 1    //@mark(exSt1, "a")
 	_ = 3 + 4 //@mark(exEn1, "4")
 	//@extractfunc(exSt1, exEn1)
-}
+	//@extractfunc(exSt25, exEn25)
+} //@mark(exEn25, "}")
diff --git a/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden b/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden
index ba40ff2..18adc4d 100644
--- a/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden
+++ b/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden
@@ -1,14 +1,30 @@
--- functionextraction_extract_basic_4_2 --
+-- functionextraction_extract_basic_3_10 --
 package extract
 
-func _() {
+func _() { //@mark(exSt25, "{")
 	//@mark(exSt1, "a")
 	newFunction() //@mark(exEn1, "4")
 	//@extractfunc(exSt1, exEn1)
+	//@extractfunc(exSt25, exEn25)
 }
 
 func newFunction() {
 	a := 1
 	_ = 3 + 4
+} //@mark(exEn25, "}")
+
+-- functionextraction_extract_basic_4_2 --
+package extract
+
+func _() { //@mark(exSt25, "{")
+	//@mark(exSt1, "a")
+	newFunction() //@mark(exEn1, "4")
+	//@extractfunc(exSt1, exEn1)
+	//@extractfunc(exSt25, exEn25)
 }
 
+func newFunction() {
+	a := 1
+	_ = 3 + 4
+} //@mark(exEn25, "}")
+
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 9e6334a..fa1bddf 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -14,7 +14,7 @@
 ImportCount = 8
 SemanticTokenCount = 3
 SuggestedFixCount = 49
-FunctionExtractionCount = 24
+FunctionExtractionCount = 25
 MethodExtractionCount = 6
 DefinitionsCount = 95
 TypeDefinitionsCount = 18
diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden
index fefd3d2..61ca3b1 100644
--- a/internal/lsp/testdata/summary_go1.18.txt.golden
+++ b/internal/lsp/testdata/summary_go1.18.txt.golden
@@ -14,7 +14,7 @@
 ImportCount = 8
 SemanticTokenCount = 3
 SuggestedFixCount = 49
-FunctionExtractionCount = 24
+FunctionExtractionCount = 25
 MethodExtractionCount = 6
 DefinitionsCount = 99
 TypeDefinitionsCount = 18