internal/lsp: fix link anchors for struct fields

This change fixes the link anchors for fields within a struct or
composite literal by getting the enclosing types.Type.

Fixes golang/go#36138

Change-Id: I534a900fad6fa6fa1b1acaa5a63ca264c5d34c39
Reviewed-on: https://go-review.googlesource.com/c/tools/+/211582
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go
index f16620f..868c265 100644
--- a/internal/lsp/source/hover.go
+++ b/internal/lsp/source/hover.go
@@ -93,13 +93,11 @@
 	switch obj := obj.(type) {
 	case *types.Var:
 		if obj.IsField() {
-			// If the object is a field, and we have an associated selector,
-			// we can determine the struct.
-			if selection, ok := i.pkg.GetTypesInfo().Selections[i.selector]; ok {
-				switch rtyp := deref(selection.Recv()).(type) {
-				case *types.Named:
-					rTypeName = rtyp.Obj().Name()
-				}
+			// If the object is a field, and we have an associated selector
+			// composite literal, or struct, we can determine the link.
+			switch typ := i.enclosing.(type) {
+			case *types.Named:
+				rTypeName = typ.Obj().Name()
 			}
 		}
 	case *types.Func:
diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go
index 2cfdf72..0cf7955 100644
--- a/internal/lsp/source/identifier.go
+++ b/internal/lsp/source/identifier.go
@@ -33,8 +33,10 @@
 
 	Declaration Declaration
 
-	ident    *ast.Ident
-	selector *ast.SelectorExpr
+	ident *ast.Ident
+
+	// enclosing is an expression used to determine the link anchor for an identifier.
+	enclosing types.Type
 
 	pkg Package
 	qf  types.Qualifier
@@ -120,12 +122,12 @@
 		}
 	}
 	result := &IdentifierInfo{
-		File:     ph,
-		Snapshot: s,
-		qf:       qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
-		pkg:      pkg,
-		ident:    searchForIdent(path[0]),
-		selector: searchForSelector(path),
+		File:      ph,
+		Snapshot:  s,
+		qf:        qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
+		pkg:       pkg,
+		ident:     searchForIdent(path[0]),
+		enclosing: searchForEnclosing(pkg, path),
 	}
 
 	// No identifier at the given position.
@@ -232,11 +234,23 @@
 	return nil
 }
 
-func searchForSelector(path []ast.Node) *ast.SelectorExpr {
+func searchForEnclosing(pkg Package, path []ast.Node) types.Type {
 	for _, n := range path {
-		switch node := n.(type) {
+		switch n := n.(type) {
 		case *ast.SelectorExpr:
-			return node
+			if selection, ok := pkg.GetTypesInfo().Selections[n]; ok {
+				return deref(selection.Recv())
+			}
+		case *ast.CompositeLit:
+			if t, ok := pkg.GetTypesInfo().Types[n]; ok {
+				return t.Type
+			}
+		case *ast.TypeSpec:
+			if _, ok := n.Type.(*ast.StructType); ok {
+				if t, ok := pkg.GetTypesInfo().Defs[n.Name]; ok {
+					return t.Type()
+				}
+			}
 		}
 	}
 	return nil
diff --git a/internal/lsp/testdata/godef/a/d.go b/internal/lsp/testdata/godef/a/d.go
index 4025788..3b76e82 100644
--- a/internal/lsp/testdata/godef/a/d.go
+++ b/internal/lsp/testdata/godef/a/d.go
@@ -17,7 +17,9 @@
 }
 
 func useThings() {
-	t := Thing{}        //@mark(aStructType, "ing")
+	t := Thing{ //@mark(aStructType, "ing")
+		Member: "string", //@mark(fMember, "ember")
+	}
 	fmt.Print(t.Member) //@mark(aMember, "ember")
 	fmt.Print(Other)    //@mark(aVar, "ther")
 	Things()            //@mark(aFunc, "ings")
@@ -30,6 +32,8 @@
 godef(aVar, Other)
 godef(aFunc, Things)
 godef(aMethod, Method)
+godef(fMember, Member)
+godef(Member, Member)
 
 //param
 //package name
diff --git a/internal/lsp/testdata/godef/b/c.go.golden b/internal/lsp/testdata/godef/b/c.go.golden
index 30fb109..cf7e753 100644
--- a/internal/lsp/testdata/godef/b/c.go.golden
+++ b/internal/lsp/testdata/godef/b/c.go.golden
@@ -39,7 +39,7 @@
 -- S1F1-definition --
 godef/b/b.go:9:2-4: defined here as \@mark\(S1F1, \"F1\"\)
 
-[`b.F1` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/b#F1)
+[`(b.S1).F1` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/b#S1.F1)
 
 ```go
 field F1 int
@@ -59,13 +59,13 @@
 			"offset": 214
 		}
 	},
-	"description": "\\@mark\\(S1F1, \\\"F1\\\"\\)\n\n[`b.F1` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/b#F1)\n\n```go\nfield F1 int\n```"
+	"description": "\\@mark\\(S1F1, \\\"F1\\\"\\)\n\n[`(b.S1).F1` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/b#S1.F1)\n\n```go\nfield F1 int\n```"
 }
 
 -- S1F1-hover --
 \@mark\(S1F1, \"F1\"\)
 
-[`b.F1` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/b#F1)
+[`(b.S1).F1` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/b#S1.F1)
 
 ```go
 field F1 int
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 1067353..b96f1d6 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -11,7 +11,7 @@
 FormatCount = 6
 ImportCount = 7
 SuggestedFixCount = 1
-DefinitionsCount = 39
+DefinitionsCount = 41
 TypeDefinitionsCount = 2
 HighlightsCount = 44
 ReferencesCount = 7