@@ -44,6 +44,7 @@ import (
44
44
"golang.org/x/tools/gopls/internal/util/frob"
45
45
"golang.org/x/tools/gopls/internal/util/moremaps"
46
46
"golang.org/x/tools/gopls/internal/util/persistent"
47
+ "golang.org/x/tools/gopls/internal/util/safetoken"
47
48
"golang.org/x/tools/internal/analysisinternal"
48
49
"golang.org/x/tools/internal/event"
49
50
"golang.org/x/tools/internal/facts"
@@ -1019,93 +1020,6 @@ func (act *action) exec(ctx context.Context) (any, *actionSummary, error) {
1019
1020
factFilter [reflect .TypeOf (f )] = true
1020
1021
}
1021
1022
1022
- // posToLocation converts from token.Pos to protocol form.
1023
- posToLocation := func (start , end token.Pos ) (protocol.Location , error ) {
1024
- tokFile := apkg .pkg .FileSet ().File (start )
1025
-
1026
- // Find existing mapper by file name.
1027
- // (Don't require an exact token.File match
1028
- // as the analyzer may have re-parsed the file.)
1029
- var (
1030
- mapper * protocol.Mapper
1031
- fixed bool
1032
- )
1033
- for _ , p := range apkg .pkg .CompiledGoFiles () {
1034
- if p .Tok .Name () == tokFile .Name () {
1035
- mapper = p .Mapper
1036
- fixed = p .Fixed () // suppress some assertions after parser recovery
1037
- break
1038
- }
1039
- }
1040
- if mapper == nil {
1041
- // The start position was not among the package's parsed
1042
- // Go files, indicating that the analyzer added new files
1043
- // to the FileSet.
1044
- //
1045
- // For example, the cgocall analyzer re-parses and
1046
- // type-checks some of the files in a special environment;
1047
- // and asmdecl and other low-level runtime analyzers call
1048
- // ReadFile to parse non-Go files.
1049
- // (This is a supported feature, documented at go/analysis.)
1050
- //
1051
- // In principle these files could be:
1052
- //
1053
- // - OtherFiles (non-Go files such as asm).
1054
- // However, we set Pass.OtherFiles=[] because
1055
- // gopls won't service "diagnose" requests
1056
- // for non-Go files, so there's no point
1057
- // reporting diagnostics in them.
1058
- //
1059
- // - IgnoredFiles (files tagged for other configs).
1060
- // However, we set Pass.IgnoredFiles=[] because,
1061
- // in most cases, zero-config gopls should create
1062
- // another view that covers these files.
1063
- //
1064
- // - Referents of //line directives, as in cgo packages.
1065
- // The file names in this case are not known a priori.
1066
- // gopls generally tries to avoid honoring line directives,
1067
- // but analyzers such as cgocall may honor them.
1068
- //
1069
- // In short, it's unclear how this can be reached
1070
- // other than due to an analyzer bug.
1071
- return protocol.Location {}, bug .Errorf ("diagnostic location is not among files of package: %s" , tokFile .Name ())
1072
- }
1073
- // Inv: mapper != nil
1074
-
1075
- if end == token .NoPos {
1076
- end = start
1077
- }
1078
-
1079
- // debugging #64547
1080
- fileStart := token .Pos (tokFile .Base ())
1081
- fileEnd := fileStart + token .Pos (tokFile .Size ())
1082
- if start < fileStart {
1083
- if ! fixed {
1084
- bug .Reportf ("start < start of file" )
1085
- }
1086
- start = fileStart
1087
- }
1088
- if end < start {
1089
- // This can happen if End is zero (#66683)
1090
- // or a small positive displacement from zero
1091
- // due to recursive Node.End() computation.
1092
- // This usually arises from poor parser recovery
1093
- // of an incomplete term at EOF.
1094
- if ! fixed {
1095
- bug .Reportf ("end < start of file" )
1096
- }
1097
- end = fileEnd
1098
- }
1099
- if end > fileEnd + 1 {
1100
- if ! fixed {
1101
- bug .Reportf ("end > end of file + 1" )
1102
- }
1103
- end = fileEnd
1104
- }
1105
-
1106
- return mapper .PosLocation (tokFile , start , end )
1107
- }
1108
-
1109
1023
// Now run the (pkg, analyzer) action.
1110
1024
var diagnostics []gobDiagnostic
1111
1025
@@ -1130,7 +1044,7 @@ func (act *action) exec(ctx context.Context) (any, *actionSummary, error) {
1130
1044
bug .Reportf ("invalid SuggestedFixes: %v" , err )
1131
1045
d .SuggestedFixes = nil
1132
1046
}
1133
- diagnostic , err := toGobDiagnostic (posToLocation , analyzer , d )
1047
+ diagnostic , err := toGobDiagnostic (apkg . pkg , analyzer , d )
1134
1048
if err != nil {
1135
1049
// Don't bug.Report here: these errors all originate in
1136
1050
// posToLocation, and we can more accurately discriminate
@@ -1322,12 +1236,12 @@ type gobTextEdit struct {
1322
1236
1323
1237
// toGobDiagnostic converts an analysis.Diagnosic to a serializable gobDiagnostic,
1324
1238
// which requires expanding token.Pos positions into protocol.Location form.
1325
- func toGobDiagnostic (posToLocation func ( start , end token. Pos ) (protocol. Location , error ) , a * analysis.Analyzer , diag analysis.Diagnostic ) (gobDiagnostic , error ) {
1239
+ func toGobDiagnostic (pkg * Package , a * analysis.Analyzer , diag analysis.Diagnostic ) (gobDiagnostic , error ) {
1326
1240
var fixes []gobSuggestedFix
1327
1241
for _ , fix := range diag .SuggestedFixes {
1328
1242
var gobEdits []gobTextEdit
1329
1243
for _ , textEdit := range fix .TextEdits {
1330
- loc , err := posToLocation ( textEdit .Pos , textEdit .End )
1244
+ loc , err := diagnosticPosToLocation ( pkg , false , textEdit .Pos , textEdit .End )
1331
1245
if err != nil {
1332
1246
return gobDiagnostic {}, fmt .Errorf ("in SuggestedFixes: %w" , err )
1333
1247
}
@@ -1344,7 +1258,10 @@ func toGobDiagnostic(posToLocation func(start, end token.Pos) (protocol.Location
1344
1258
1345
1259
var related []gobRelatedInformation
1346
1260
for _ , r := range diag .Related {
1347
- loc , err := posToLocation (r .Pos , r .End )
1261
+ // The position of RelatedInformation may be
1262
+ // within another (dependency) package.
1263
+ const allowDeps = true
1264
+ loc , err := diagnosticPosToLocation (pkg , allowDeps , r .Pos , r .End )
1348
1265
if err != nil {
1349
1266
return gobDiagnostic {}, fmt .Errorf ("in Related: %w" , err )
1350
1267
}
@@ -1354,7 +1271,7 @@ func toGobDiagnostic(posToLocation func(start, end token.Pos) (protocol.Location
1354
1271
})
1355
1272
}
1356
1273
1357
- loc , err := posToLocation ( diag .Pos , diag .End )
1274
+ loc , err := diagnosticPosToLocation ( pkg , false , diag .Pos , diag .End )
1358
1275
if err != nil {
1359
1276
return gobDiagnostic {}, err
1360
1277
}
@@ -1382,6 +1299,126 @@ func toGobDiagnostic(posToLocation func(start, end token.Pos) (protocol.Location
1382
1299
}, nil
1383
1300
}
1384
1301
1302
+ // diagnosticPosToLocation converts from token.Pos to protocol form, in the
1303
+ // context of the specified package and, optionally, its dependencies.
1304
+ func diagnosticPosToLocation (pkg * Package , allowDeps bool , start , end token.Pos ) (protocol.Location , error ) {
1305
+ if end == token .NoPos {
1306
+ end = start
1307
+ }
1308
+
1309
+ fset := pkg .FileSet ()
1310
+ tokFile := fset .File (start )
1311
+
1312
+ // Find existing mapper by file name.
1313
+ // (Don't require an exact token.File match
1314
+ // as the analyzer may have re-parsed the file.)
1315
+ var (
1316
+ mapper * protocol.Mapper
1317
+ fixed bool
1318
+ )
1319
+ for _ , p := range pkg .CompiledGoFiles () {
1320
+ if p .Tok .Name () == tokFile .Name () {
1321
+ mapper = p .Mapper
1322
+ fixed = p .Fixed () // suppress some assertions after parser recovery
1323
+ break
1324
+ }
1325
+ }
1326
+ // TODO(adonovan): search pkg.AsmFiles too; see #71754.
1327
+ if mapper != nil {
1328
+ // debugging #64547
1329
+ fileStart := token .Pos (tokFile .Base ())
1330
+ fileEnd := fileStart + token .Pos (tokFile .Size ())
1331
+ if start < fileStart {
1332
+ if ! fixed {
1333
+ bug .Reportf ("start < start of file" )
1334
+ }
1335
+ start = fileStart
1336
+ }
1337
+ if end < start {
1338
+ // This can happen if End is zero (#66683)
1339
+ // or a small positive displacement from zero
1340
+ // due to recursive Node.End() computation.
1341
+ // This usually arises from poor parser recovery
1342
+ // of an incomplete term at EOF.
1343
+ if ! fixed {
1344
+ bug .Reportf ("end < start of file" )
1345
+ }
1346
+ end = fileEnd
1347
+ }
1348
+ if end > fileEnd + 1 {
1349
+ if ! fixed {
1350
+ bug .Reportf ("end > end of file + 1" )
1351
+ }
1352
+ end = fileEnd
1353
+ }
1354
+
1355
+ return mapper .PosLocation (tokFile , start , end )
1356
+ }
1357
+
1358
+ // Inv: the positions are not within this package.
1359
+
1360
+ if allowDeps {
1361
+ // Positions in Diagnostic.RelatedInformation may belong to a
1362
+ // dependency package. We cannot accurately map them to
1363
+ // protocol.Location coordinates without a Mapper for the
1364
+ // relevant file, but none exists if the file was loaded from
1365
+ // export data, and we have no means (Snapshot) of loading it.
1366
+ //
1367
+ // So, fall back to approximate conversion to UTF-16:
1368
+ // for non-ASCII text, the column numbers may be wrong.
1369
+ var (
1370
+ startPosn = safetoken .StartPosition (fset , start )
1371
+ endPosn = safetoken .EndPosition (fset , end )
1372
+ )
1373
+ return protocol.Location {
1374
+ URI : protocol .URIFromPath (startPosn .Filename ),
1375
+ Range : protocol.Range {
1376
+ Start : protocol.Position {
1377
+ Line : uint32 (startPosn .Line - 1 ),
1378
+ Character : uint32 (startPosn .Column - 1 ),
1379
+ },
1380
+ End : protocol.Position {
1381
+ Line : uint32 (endPosn .Line - 1 ),
1382
+ Character : uint32 (endPosn .Column - 1 ),
1383
+ },
1384
+ },
1385
+ }, nil
1386
+ }
1387
+
1388
+ // The start position was not among the package's parsed
1389
+ // Go files, indicating that the analyzer added new files
1390
+ // to the FileSet.
1391
+ //
1392
+ // For example, the cgocall analyzer re-parses and
1393
+ // type-checks some of the files in a special environment;
1394
+ // and asmdecl and other low-level runtime analyzers call
1395
+ // ReadFile to parse non-Go files.
1396
+ // (This is a supported feature, documented at go/analysis.)
1397
+ //
1398
+ // In principle these files could be:
1399
+ //
1400
+ // - OtherFiles (non-Go files such as asm).
1401
+ // However, we set Pass.OtherFiles=[] because
1402
+ // gopls won't service "diagnose" requests
1403
+ // for non-Go files, so there's no point
1404
+ // reporting diagnostics in them.
1405
+ //
1406
+ // - IgnoredFiles (files tagged for other configs).
1407
+ // However, we set Pass.IgnoredFiles=[] because,
1408
+ // in most cases, zero-config gopls should create
1409
+ // another view that covers these files.
1410
+ //
1411
+ // - Referents of //line directives, as in cgo packages.
1412
+ // The file names in this case are not known a priori.
1413
+ // gopls generally tries to avoid honoring line directives,
1414
+ // but analyzers such as cgocall may honor them.
1415
+ //
1416
+ // In short, it's unclear how this can be reached
1417
+ // other than due to an analyzer bug.
1418
+
1419
+ return protocol.Location {}, bug .Errorf ("diagnostic location is not among files of package: %s" , tokFile .Name ())
1420
+ }
1421
+
1385
1422
// effectiveURL computes the effective URL of diag,
1386
1423
// using the algorithm specified at Diagnostic.URL.
1387
1424
func effectiveURL (a * analysis.Analyzer , diag analysis.Diagnostic ) string {
0 commit comments