gopls: add test cases for embedded fields in struct literals
Go1.27 supports direct reference to embedded fields in struct literals.
Gopls needs to handle Completion, Hover, Fill Struct,
Go To Definition, Diagnostics.
For golang/go#78553
diff --git a/gopls/internal/analysis/fillstruct/fillstruct_test.go b/gopls/internal/analysis/fillstruct/fillstruct_test.go
index e0ad83d..41b456c 100644
--- a/gopls/internal/analysis/fillstruct/fillstruct_test.go
+++ b/gopls/internal/analysis/fillstruct/fillstruct_test.go
@@ -11,6 +11,7 @@
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/gopls/internal/analysis/fillstruct"
+ "golang.org/x/tools/internal/testenv"
)
// analyzer allows us to test the fillstruct code action using the analysistest
@@ -34,3 +35,10 @@
testdata := analysistest.TestData()
analysistest.Run(t, testdata, analyzer, "a", "typeparams")
}
+
+func TestIssue9859(t *testing.T) {
+ testenv.NeedsGo1Point(t, 27)
+ t.Skip("Skipping as this feature is not yet implemented. Ref: go.dev/issues/78553")
+ testdata := analysistest.TestData()
+ analysistest.Run(t, testdata, analyzer, "issue9859")
+}
diff --git a/gopls/internal/analysis/fillstruct/testdata/src/issue9859/issue9859.go b/gopls/internal/analysis/fillstruct/testdata/src/issue9859/issue9859.go
new file mode 100644
index 0000000..b01bba5
--- /dev/null
+++ b/gopls/internal/analysis/fillstruct/testdata/src/issue9859/issue9859.go
@@ -0,0 +1,18 @@
+// Copyright 2026 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 issue9859
+
+type E struct {
+ A int
+}
+
+type T struct {
+ E
+}
+
+// Current behavior: fillstruct thinks E is missing because it only looks at top-level fields.
+// Expected behavior for issue 9859: T{A: 1} should be considered fully populated (or at least A should be recognized).
+// For now, we expect it to report missing fields because it doesn't support the new syntax.
+var _ = T{A: 1} // want `T literal has missing fields`
diff --git a/gopls/internal/test/integration/completion/issue9859_test.go b/gopls/internal/test/integration/completion/issue9859_test.go
new file mode 100644
index 0000000..0db5d39
--- /dev/null
+++ b/gopls/internal/test/integration/completion/issue9859_test.go
@@ -0,0 +1,64 @@
+// Copyright 2026 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 completion
+
+import (
+ "testing"
+
+ . "golang.org/x/tools/gopls/internal/test/integration"
+ "golang.org/x/tools/internal/testenv"
+)
+
+// TestIssue9859Completion tests completions for direct reference to embedded fields in struct literals.
+// Ref: go.dev/issues/9859
+func TestIssue9859Completion(t *testing.T) {
+ testenv.NeedsGo1Point(t, 27) // requires at least go1.22 for some features, but gotip is preferred.
+
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.22
+-- main.go --
+package main
+
+type E1 struct {
+ A int
+}
+
+type E2 struct {
+ E1
+ B int
+}
+
+type T struct {
+ E2
+ C int
+}
+
+func main() {
+ _ = T{
+
+ }
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ t.Skip("Skipping as this feature is not yet implemented. Ref: go.dev/issues/78553")
+
+ env.OpenFile("main.go")
+ env.Await(env.DoneWithOpen())
+
+ // Trigger completion inside T completion.
+ loc := env.RegexpSearch("main.go", `T\{\n\t\t()`)
+ completions := env.Completion(loc)
+
+ // We expect to see fields from embedded structs: A, B, and direct fields: C, E2.
+ want := []string{"A", "B", "C", "E2"}
+ diff := compareCompletionLabels(want, completions.Items)
+ if diff != "" {
+ t.Fatal(diff)
+ }
+ })
+}
diff --git a/gopls/internal/test/integration/diagnostics/issue9859_test.go b/gopls/internal/test/integration/diagnostics/issue9859_test.go
new file mode 100644
index 0000000..a4d19b4
--- /dev/null
+++ b/gopls/internal/test/integration/diagnostics/issue9859_test.go
@@ -0,0 +1,54 @@
+// Copyright 2026 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 diagnostics
+
+import (
+ "testing"
+
+ . "golang.org/x/tools/gopls/internal/test/integration"
+ "golang.org/x/tools/internal/testenv"
+)
+
+// TestIssue9859Diagnostics tests that direct reference to embedded fields in struct literals does not produce error diagnostics.
+// Ref: go.dev/issues/9859
+func TestIssue9859Diagnostics(t *testing.T) {
+ testenv.NeedsGo1Point(t, 27)
+
+ const src = `
+-- go.mod --
+module mod.com
+
+go 1.22
+-- main.go --
+package main
+
+type E1 struct {
+ A int
+}
+
+type T struct {
+ E1
+}
+
+func main() {
+ _ = T{
+ A: 1,
+ }
+}
+`
+ Run(t, src, func(t *testing.T, env *Env) {
+ t.Skip("Skipping as this feature is not yet implemented. Ref: go.dev/issues/78553")
+
+ env.OpenFile("main.go")
+ env.Await(
+ env.DoneWithOpen(),
+ )
+
+ // Expect no diagnostics (errors) for this file.
+ env.Await(
+ NoDiagnostics(ForFile("main.go")),
+ )
+ })
+}
diff --git a/gopls/internal/test/integration/hover/issue9859_test.go b/gopls/internal/test/integration/hover/issue9859_test.go
new file mode 100644
index 0000000..23137d7
--- /dev/null
+++ b/gopls/internal/test/integration/hover/issue9859_test.go
@@ -0,0 +1,64 @@
+// Copyright 2026 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 hover
+
+import (
+ "strings"
+ "testing"
+
+ . "golang.org/x/tools/gopls/internal/test/integration"
+ "golang.org/x/tools/internal/testenv"
+)
+
+// TestIssue9859Hover tests hover for direct reference to embedded fields in struct literals.
+// Ref: go.dev/issues/9859
+func TestIssue9859Hover(t *testing.T) {
+ testenv.NeedsGo1Point(t, 27)
+
+ const src = `
+-- go.mod --
+module mod.com
+
+go 1.22
+-- main.go --
+package main
+
+type E1 struct {
+ A int
+}
+
+type T struct {
+ E1
+}
+
+func main() {
+ _ = T{
+ A: 1,
+ }
+}
+`
+ Run(t, src, func(t *testing.T, env *Env) {
+ t.Skip("Skipping as this feature is not yet implemented. Ref: go.dev/issues/78553")
+
+ env.OpenFile("main.go")
+ env.Await(env.DoneWithOpen())
+
+ // Search for "A" in the struct literal.
+ // We need to be specific to match the "A" in "A: 1".
+ loc := env.RegexpSearch("main.go", `\bA\b:`)
+ // The search returns the range for "A:". We want to hover over "A".
+ loc.Range.End.Character = loc.Range.Start.Character + 1
+
+ content, _ := env.Hover(loc)
+
+ if content == nil {
+ t.Fatal("hover returned nil")
+ }
+
+ if !strings.Contains(content.Value, "A") {
+ t.Errorf("expected hover content to contain 'A', got: %v", content.Value)
+ }
+ })
+}
diff --git a/gopls/internal/test/integration/misc/issue9859_def_test.go b/gopls/internal/test/integration/misc/issue9859_def_test.go
new file mode 100644
index 0000000..0f31a51
--- /dev/null
+++ b/gopls/internal/test/integration/misc/issue9859_def_test.go
@@ -0,0 +1,61 @@
+// Copyright 2026 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 misc
+
+import (
+ "testing"
+
+ . "golang.org/x/tools/gopls/internal/test/integration"
+ "golang.org/x/tools/internal/testenv"
+)
+
+// TestIssue9859Definition tests "Go to Definition" for direct reference to embedded fields in struct literals.
+// Ref: go.dev/issues/9859
+func TestIssue9859Definition(t *testing.T) {
+ testenv.NeedsGo1Point(t, 27)
+
+ const src = `
+-- go.mod --
+module mod.com
+
+go 1.22
+-- main.go --
+package main
+
+type E1 struct {
+ A int
+}
+
+type T struct {
+ E1
+}
+
+func main() {
+ _ = T{
+ A: 1,
+ }
+}
+`
+ Run(t, src, func(t *testing.T, env *Env) {
+ t.Skip("Skipping as this feature is not yet implemented. Ref: go.dev/issues/78553")
+
+ env.OpenFile("main.go")
+ env.Await(env.DoneWithOpen())
+
+ // Search for "A" in the struct literal "A: 1".
+ loc := env.RegexpSearch("main.go", `\bA\b:`)
+ loc.Range.End.Character = loc.Range.Start.Character + 1
+
+ defLoc := env.FirstDefinition(loc)
+
+ // Expected definition is the field A in E1.
+ expectedLoc := env.RegexpSearch("main.go", `\bA\b int`)
+ expectedLoc.Range.End.Character = expectedLoc.Range.Start.Character + 1
+
+ if defLoc != expectedLoc {
+ t.Errorf("Definition: got %+v, want %+v", defLoc, expectedLoc)
+ }
+ })
+}
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Thanks for getting this work started!
Run(t, files, func(t *testing.T, env *Env) {
t.Skip("Skipping as this feature is not yet implemented. Ref: go.dev/issues/78553")
env.OpenFile("main.go")
env.Await(env.DoneWithOpen())
// Trigger completion inside T completion.
loc := env.RegexpSearch("main.go", `T\{\n\t\t()`)
completions := env.Completion(loc)
// We expect to see fields from embedded structs: A, B, and direct fields: C, E2.
want := []string{"A", "B", "C", "E2"}
diff := compareCompletionLabels(want, completions.Items)
if diff != "" {
t.Fatal(diff)
}
})This test could be expressed as one-liner in the marker test framework; see gopls/internal/test/marker/testdata/completion/*.txt for examples. (You can express NeedsGo1Point using `-min_go=go1.27` in `-- flags --` archive section.)
Run(t, src, func(t *testing.T, env *Env) {ditto (`@diag`, not that you'll need it!)
Run(t, src, func(t *testing.T, env *Env) {ditto (`@hover`)
Run(t, src, func(t *testing.T, env *Env) {ditto (`@def`)
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |