@@ -3,15 +3,19 @@ package cache
3
3
import (
4
4
"bytes"
5
5
"context"
6
+ "go/ast"
6
7
"go/scanner"
8
+ "go/token"
7
9
"go/types"
8
10
"strings"
9
11
10
12
"golang.org/x/tools/go/analysis"
13
+ "golang.org/x/tools/go/ast/astutil"
11
14
"golang.org/x/tools/go/packages"
12
15
"golang.org/x/tools/internal/lsp/protocol"
13
16
"golang.org/x/tools/internal/lsp/source"
14
17
"golang.org/x/tools/internal/span"
18
+ errors "golang.org/x/xerrors"
15
19
)
16
20
17
21
func sourceError (ctx context.Context , pkg * pkg , e interface {}) (* source.Error , error ) {
@@ -23,6 +27,7 @@ func sourceError(ctx context.Context, pkg *pkg, e interface{}) (*source.Error, e
23
27
fixes []source.SuggestedFix
24
28
related []source.RelatedInformation
25
29
)
30
+ fset := pkg .snapshot .view .session .cache .fset
26
31
switch e := e .(type ) {
27
32
case packages.Error :
28
33
if e .Pos == "" {
@@ -32,21 +37,32 @@ func sourceError(ctx context.Context, pkg *pkg, e interface{}) (*source.Error, e
32
37
}
33
38
msg = e .Msg
34
39
kind = toSourceErrorKind (e .Kind )
40
+
35
41
case * scanner.Error :
36
42
msg = e .Msg
37
43
kind = source .ParseError
38
- spn = span .Parse (e .Pos .String ())
44
+ spn , err = scannerErrorRange (ctx , fset , pkg , e .Pos )
45
+ if err != nil {
46
+ return nil , err
47
+ }
48
+
39
49
case scanner.ErrorList :
40
50
// The first parser error is likely the root cause of the problem.
41
- if e .Len () > 0 {
42
- spn = span .Parse (e [0 ].Pos .String ())
43
- msg = e [0 ].Msg
44
- kind = source .ParseError
51
+ if e .Len () <= 0 {
52
+ return nil , errors .Errorf ("no errors in %v" , e )
45
53
}
54
+ msg = e [0 ].Msg
55
+ kind = source .ParseError
56
+ spn , err = scannerErrorRange (ctx , fset , pkg , e [0 ].Pos )
57
+ if err != nil {
58
+ return nil , err
59
+ }
60
+
46
61
case types.Error :
47
- spn = span .Parse (pkg .snapshot .view .session .cache .fset .Position (e .Pos ).String ())
48
62
msg = e .Msg
49
63
kind = source .TypeError
64
+ spn , err = typeErrorRange (ctx , fset , pkg , e .Pos )
65
+
50
66
case * analysis.Diagnostic :
51
67
spn , err = span .NewRange (pkg .snapshot .view .session .cache .fset , e .Pos , e .End ).Span ()
52
68
if err != nil {
@@ -64,7 +80,7 @@ func sourceError(ctx context.Context, pkg *pkg, e interface{}) (*source.Error, e
64
80
return nil , err
65
81
}
66
82
}
67
- rng , err := spanToRange (ctx , pkg , spn , kind == source . TypeError )
83
+ rng , err := spanToRange (ctx , pkg , spn )
68
84
if err != nil {
69
85
return nil , err
70
86
}
@@ -88,7 +104,7 @@ func suggestedFixes(ctx context.Context, pkg *pkg, diag *analysis.Diagnostic) ([
88
104
if err != nil {
89
105
return nil , err
90
106
}
91
- rng , err := spanToRange (ctx , pkg , spn , false )
107
+ rng , err := spanToRange (ctx , pkg , spn )
92
108
if err != nil {
93
109
return nil , err
94
110
}
@@ -112,7 +128,7 @@ func relatedInformation(ctx context.Context, pkg *pkg, diag *analysis.Diagnostic
112
128
if err != nil {
113
129
return nil , err
114
130
}
115
- rng , err := spanToRange (ctx , pkg , spn , false )
131
+ rng , err := spanToRange (ctx , pkg , spn )
116
132
if err != nil {
117
133
return nil , err
118
134
}
@@ -138,32 +154,81 @@ func toSourceErrorKind(kind packages.ErrorKind) source.ErrorKind {
138
154
}
139
155
}
140
156
157
+ func typeErrorRange (ctx context.Context , fset * token.FileSet , pkg * pkg , pos token.Pos ) (span.Span , error ) {
158
+ spn , err := span .NewRange (fset , pos , pos ).Span ()
159
+ if err != nil {
160
+ return span.Span {}, err
161
+ }
162
+ posn := fset .Position (pos )
163
+ ph , _ , err := pkg .FindFile (ctx , span .FileURI (posn .Filename ))
164
+ if err != nil {
165
+ return span.Span {}, err
166
+ }
167
+ file , m , _ , err := ph .Cached (ctx )
168
+ if err != nil {
169
+ return span.Span {}, err
170
+ }
171
+ path , _ := astutil .PathEnclosingInterval (file , pos , pos )
172
+ if len (path ) > 0 {
173
+ if s , err := span .NewRange (fset , path [0 ].Pos (), path [0 ].End ()).Span (); err == nil {
174
+ return s , nil
175
+ }
176
+ }
177
+ s , err := spn .WithOffset (m .Converter )
178
+ if err != nil {
179
+ return span.Span {}, err
180
+ }
181
+ data , _ , err := ph .File ().Read (ctx )
182
+ if err != nil {
183
+ return span.Span {}, err
184
+ }
185
+ start := s .Start ()
186
+ offset := start .Offset ()
187
+ if offset < len (data ) {
188
+ if width := bytes .IndexAny (data [offset :], " \n ,():;[]" ); width > 0 {
189
+ return span .New (spn .URI (), start , span .NewPoint (start .Line (), start .Column ()+ width , offset + width )), nil
190
+ }
191
+ }
192
+ return spn , nil
193
+ }
194
+
195
+ func scannerErrorRange (ctx context.Context , fset * token.FileSet , pkg * pkg , posn token.Position ) (span.Span , error ) {
196
+ ph , _ , err := pkg .FindFile (ctx , span .FileURI (posn .Filename ))
197
+ if err != nil {
198
+ return span.Span {}, err
199
+ }
200
+ file , _ , _ , err := ph .Cached (ctx )
201
+ if err != nil {
202
+ return span.Span {}, err
203
+ }
204
+ tok := fset .File (file .Pos ())
205
+ if tok == nil {
206
+ return span.Span {}, errors .Errorf ("no token.File for %s" , ph .File ().Identity ().URI )
207
+ }
208
+ pos := tok .Pos (posn .Offset )
209
+ path , _ := astutil .PathEnclosingInterval (file , pos , pos )
210
+ if len (path ) > 0 {
211
+ switch n := path [0 ].(type ) {
212
+ case * ast.BadDecl , * ast.BadExpr , * ast.BadStmt :
213
+ if s , err := span .NewRange (fset , n .Pos (), n .End ()).Span (); err == nil {
214
+ return s , nil
215
+ }
216
+ }
217
+ }
218
+ return span .NewRange (fset , pos , pos ).Span ()
219
+ }
220
+
141
221
// spanToRange converts a span.Span to a protocol.Range,
142
222
// assuming that the span belongs to the package whose diagnostics are being computed.
143
- func spanToRange (ctx context.Context , pkg * pkg , spn span.Span , isTypeError bool ) (protocol.Range , error ) {
144
- ph , err := pkg .File ( spn .URI ())
223
+ func spanToRange (ctx context.Context , pkg * pkg , spn span.Span ) (protocol.Range , error ) {
224
+ ph , _ , err := pkg .FindFile ( ctx , spn .URI ())
145
225
if err != nil {
146
226
return protocol.Range {}, err
147
227
}
148
228
_ , m , _ , err := ph .Cached (ctx )
149
229
if err != nil {
150
230
return protocol.Range {}, err
151
231
}
152
- if spn .IsPoint () && isTypeError {
153
- data , _ , err := ph .File ().Read (ctx )
154
- if err != nil {
155
- return protocol.Range {}, err
156
- }
157
- if s , err := spn .WithOffset (m .Converter ); err == nil {
158
- start := s .Start ()
159
- offset := start .Offset ()
160
- if offset < len (data ) {
161
- if width := bytes .IndexAny (data [offset :], " \n ,():;[]" ); width > 0 {
162
- spn = span .New (spn .URI (), start , span .NewPoint (start .Line (), start .Column ()+ width , offset + width ))
163
- }
164
- }
165
- }
166
- }
167
232
return m .Range (spn )
168
233
}
169
234
0 commit comments