Skip to content

Commit 6360c0b

Browse files
vikblombsiegert
authored andcommitted
gopls/internal/lsp/source: find linkname directives without parsing
Avoid full source code parsing when looking for linkname directives to hover over or go-to-defintion from. Change-Id: I348a907fea3dcb15430095f5207bc3eaed28f6b6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/535516 Reviewed-by: Benny Siegert <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 71f6a46 commit 6360c0b

File tree

3 files changed

+25
-38
lines changed

3 files changed

+25
-38
lines changed

gopls/internal/lsp/source/definition.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func Definition(ctx context.Context, snapshot Snapshot, fh FileHandle, position
6060
}
6161

6262
// Handle the case where the cursor is in a linkname directive.
63-
locations, err := LinknameDefinition(ctx, snapshot, fh, position)
63+
locations, err := LinknameDefinition(ctx, snapshot, pgf.Mapper, position)
6464
if !errors.Is(err, ErrNoLinkname) {
6565
return locations, err
6666
}

gopls/internal/lsp/source/hover.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Po
131131

132132
// Handle linkname directive by overriding what to look for.
133133
var linkedRange *protocol.Range // range referenced by linkname directive, or nil
134-
if pkgPath, name, offset := parseLinkname(ctx, snapshot, fh, pp); pkgPath != "" && name != "" {
134+
if pkgPath, name, offset := parseLinkname(pgf.Mapper, pp); pkgPath != "" && name != "" {
135135
// rng covering 2nd linkname argument: pkgPath.name.
136136
rng, err := pgf.PosRange(pgf.Tok.Pos(offset), pgf.Tok.Pos(offset+len(pkgPath)+len(".")+len(name)))
137137
if err != nil {

gopls/internal/lsp/source/linkname.go

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import (
2121
// As such it indicates that other definitions could be worth checking.
2222
var ErrNoLinkname = errors.New("no linkname directive found")
2323

24-
// LinknameDefinition finds the definition of the linkname directive in fh at pos.
24+
// LinknameDefinition finds the definition of the linkname directive in m at pos.
2525
// If there is no linkname directive at pos, returns ErrNoLinkname.
26-
func LinknameDefinition(ctx context.Context, snapshot Snapshot, fh FileHandle, from protocol.Position) ([]protocol.Location, error) {
27-
pkgPath, name, _ := parseLinkname(ctx, snapshot, fh, from)
26+
func LinknameDefinition(ctx context.Context, snapshot Snapshot, m *protocol.Mapper, from protocol.Position) ([]protocol.Location, error) {
27+
pkgPath, name, _ := parseLinkname(m, from)
2828
if pkgPath == "" {
2929
return nil, ErrNoLinkname
3030
}
@@ -44,34 +44,46 @@ func LinknameDefinition(ctx context.Context, snapshot Snapshot, fh FileHandle, f
4444
// If successful, it returns
4545
// - package path referenced
4646
// - object name referenced
47-
// - byte offset in fh of the start of the link target
47+
// - byte offset in mapped file of the start of the link target
4848
// of the linkname directives 2nd argument.
4949
//
5050
// If the position is not in the second argument of a go:linkname directive,
5151
// or parsing fails, it returns "", "", 0.
52-
func parseLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (pkgPath, name string, targetOffset int) {
53-
// TODO(adonovan): opt: parsing isn't necessary here.
54-
// We're only looking for a line comment.
55-
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
52+
func parseLinkname(m *protocol.Mapper, pos protocol.Position) (pkgPath, name string, targetOffset int) {
53+
lineStart, err := m.PositionOffset(protocol.Position{Line: pos.Line, Character: 0})
5654
if err != nil {
5755
return "", "", 0
5856
}
59-
60-
offset, err := pgf.Mapper.PositionOffset(pos)
57+
lineEnd, err := m.PositionOffset(protocol.Position{Line: pos.Line + 1, Character: 0})
6158
if err != nil {
6259
return "", "", 0
6360
}
6461

62+
directive := string(m.Content[lineStart:lineEnd])
63+
// (Assumes no leading spaces.)
64+
if !strings.HasPrefix(directive, "//go:linkname") {
65+
return "", "", 0
66+
}
67+
// Sometimes source code (typically tests) has another
68+
// comment after the directive, trim that away.
69+
if i := strings.LastIndex(directive, "//"); i != 0 {
70+
directive = strings.TrimSpace(directive[:i])
71+
}
72+
6573
// Looking for pkgpath in '//go:linkname f pkgpath.g'.
6674
// (We ignore 1-arg linkname directives.)
67-
directive, end := findLinknameAtOffset(pgf, offset)
6875
parts := strings.Fields(directive)
6976
if len(parts) != 3 {
7077
return "", "", 0
7178
}
7279

7380
// Inside 2nd arg [start, end]?
7481
// (Assumes no trailing spaces.)
82+
offset, err := m.PositionOffset(pos)
83+
if err != nil {
84+
return "", "", 0
85+
}
86+
end := lineStart + len(directive)
7587
start := end - len(parts[2])
7688
if !(start <= offset && offset <= end) {
7789
return "", "", 0
@@ -87,31 +99,6 @@ func parseLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos pr
8799
return linkname[:dot], linkname[dot+1:], start
88100
}
89101

90-
// findLinknameAtOffset returns the first linkname directive on line and its end offset.
91-
// Returns "", 0 if the offset is not in a linkname directive.
92-
func findLinknameAtOffset(pgf *ParsedGoFile, offset int) (string, int) {
93-
for _, grp := range pgf.File.Comments {
94-
for _, com := range grp.List {
95-
if strings.HasPrefix(com.Text, "//go:linkname") {
96-
p := safetoken.Position(pgf.Tok, com.Pos())
97-
98-
// Sometimes source code (typically tests) has another
99-
// comment after the directive, trim that away.
100-
text := com.Text
101-
if i := strings.LastIndex(text, "//"); i != 0 {
102-
text = strings.TrimSpace(text[:i])
103-
}
104-
105-
end := p.Offset + len(text)
106-
if p.Offset <= offset && offset < end {
107-
return text, end
108-
}
109-
}
110-
}
111-
}
112-
return "", 0
113-
}
114-
115102
// findLinkname searches dependencies of packages containing fh for an object
116103
// with linker name matching the given package path and name.
117104
func findLinkname(ctx context.Context, snapshot Snapshot, pkgPath PackagePath, name string) (Package, *ParsedGoFile, token.Pos, error) {

0 commit comments

Comments
 (0)