Skip to content

Commit 80a4166

Browse files
committed
gopls/internal/lsp/source: fix panic in definition(error.Error)
Fixes golang/go#64086 Change-Id: I9e469c81006fec02b39019c1a14601005f5f6083 Reviewed-on: https://go-review.googlesource.com/c/tools/+/543136 Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 00a4006 commit 80a4166

File tree

2 files changed

+95
-35
lines changed

2 files changed

+95
-35
lines changed

gopls/internal/lsp/source/definition.go

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -78,55 +78,87 @@ func Definition(ctx context.Context, snapshot Snapshot, fh FileHandle, position
7878

7979
// Handle objects with no position: builtin, unsafe.
8080
if !obj.Pos().IsValid() {
81-
var pgf *ParsedGoFile
82-
if obj.Parent() == types.Universe {
83-
// pseudo-package "builtin"
84-
builtinPGF, err := snapshot.BuiltinFile(ctx)
85-
if err != nil {
86-
return nil, err
87-
}
88-
pgf = builtinPGF
89-
90-
} else if obj.Pkg() == types.Unsafe {
91-
// package "unsafe"
92-
unsafe := snapshot.Metadata("unsafe")
93-
if unsafe == nil {
94-
return nil, fmt.Errorf("no metadata for package 'unsafe'")
95-
}
96-
uri := unsafe.GoFiles[0]
97-
fh, err := snapshot.ReadFile(ctx, uri)
98-
if err != nil {
99-
return nil, err
100-
}
101-
pgf, err = snapshot.ParseGo(ctx, fh, ParseFull&^SkipObjectResolution)
102-
if err != nil {
103-
return nil, err
104-
}
81+
return builtinDefinition(ctx, snapshot, obj)
82+
}
10583

106-
} else {
107-
return nil, bug.Errorf("internal error: no position for %v", obj.Name())
108-
}
109-
// Inv: pgf ∈ {builtin,unsafe}.go
84+
// Finally, map the object position.
85+
loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, obj.Pos(), adjustedObjEnd(obj))
86+
if err != nil {
87+
return nil, err
88+
}
89+
return []protocol.Location{loc}, nil
90+
}
11091

111-
// Use legacy (go/ast) object resolution.
112-
astObj := pgf.File.Scope.Lookup(obj.Name())
92+
// builtinDefinition returns the location of the fake source
93+
// declaration of a built-in in {builtin,unsafe}.go.
94+
func builtinDefinition(ctx context.Context, snapshot Snapshot, obj types.Object) ([]protocol.Location, error) {
95+
// getDecl returns the file-level declaration of name
96+
// using legacy (go/ast) object resolution.
97+
getDecl := func(file *ast.File, name string) (ast.Node, error) {
98+
astObj := file.Scope.Lookup(name)
11399
if astObj == nil {
114100
// Every built-in should have documentation syntax.
115-
return nil, bug.Errorf("internal error: no object for %s", obj.Name())
101+
return nil, bug.Errorf("internal error: no object for %s", name)
116102
}
117103
decl, ok := astObj.Decl.(ast.Node)
118104
if !ok {
119105
return nil, bug.Errorf("internal error: no declaration for %s", obj.Name())
120106
}
121-
loc, err := pgf.PosLocation(decl.Pos(), decl.Pos()+token.Pos(len(obj.Name())))
107+
return decl, nil
108+
}
109+
110+
var (
111+
pgf *ParsedGoFile
112+
decl ast.Node
113+
err error
114+
)
115+
if obj.Pkg() == types.Unsafe {
116+
// package "unsafe":
117+
// parse $GOROOT/src/unsafe/unsafe.go
118+
unsafe := snapshot.Metadata("unsafe")
119+
if unsafe == nil {
120+
return nil, fmt.Errorf("no metadata for package 'unsafe'")
121+
}
122+
uri := unsafe.GoFiles[0]
123+
fh, err := snapshot.ReadFile(ctx, uri)
122124
if err != nil {
123125
return nil, err
124126
}
125-
return []protocol.Location{loc}, nil
127+
pgf, err = snapshot.ParseGo(ctx, fh, ParseFull&^SkipObjectResolution)
128+
if err != nil {
129+
return nil, err
130+
}
131+
132+
decl, err = getDecl(pgf.File, obj.Name())
133+
} else {
134+
// pseudo-package "builtin":
135+
// use parsed $GOROOT/src/builtin/builtin.go
136+
pgf, err = snapshot.BuiltinFile(ctx)
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
if obj.Parent() == types.Universe {
142+
// built-in function or type
143+
decl, err = getDecl(pgf.File, obj.Name())
144+
145+
} else if obj.Name() == "Error" {
146+
// error.Error method
147+
decl, err = getDecl(pgf.File, "error")
148+
if err != nil {
149+
return nil, err
150+
}
151+
decl = decl.(*ast.TypeSpec).Type.(*ast.InterfaceType).Methods.List[0]
152+
153+
} else {
154+
return nil, bug.Errorf("unknown built-in %v", obj)
155+
}
156+
}
157+
if err != nil {
158+
return nil, err
126159
}
127160

128-
// Finally, map the object position.
129-
loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, obj.Pos(), adjustedObjEnd(obj))
161+
loc, err := pgf.PosLocation(decl.Pos(), decl.Pos()+token.Pos(len(obj.Name())))
130162
if err != nil {
131163
return nil, err
132164
}

gopls/internal/regtest/misc/definition_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,31 @@ func TestGoToEmbedDefinition(t *testing.T) {
569569
}
570570
})
571571
}
572+
573+
func TestDefinitionOfErrorErrorMethod(t *testing.T) {
574+
const src = `Regression test for a panic in definition of error.Error (of course).
575+
golang/go#64086
576+
577+
-- go.mod --
578+
module mod.com
579+
go 1.18
580+
581+
-- a.go --
582+
package a
583+
584+
func _(err error) {
585+
_ = err.Error()
586+
}
587+
588+
`
589+
Run(t, src, func(t *testing.T, env *Env) {
590+
env.OpenFile("a.go")
591+
592+
start := env.RegexpSearch("a.go", `Error`)
593+
loc := env.GoToDefinition(start)
594+
595+
if !strings.HasSuffix(string(loc.URI), "builtin.go") {
596+
t.Errorf("GoToDefinition(err.Error) = %#v, want builtin.go", loc)
597+
}
598+
})
599+
}

0 commit comments

Comments
 (0)