Skip to content

Commit df9c1c7

Browse files
committed
gopls/internal/server: disambiguate diagnostics by OS,ARCH
This change adds a disambiguating suffix such as " [windows,arm64]" to diagnostics that do not appear in the default build configuration. Fixes golang/go#65496 Change-Id: Ided7a7110ff630e57b7a96a311a240fef210ca93 Reviewed-on: https://go-review.googlesource.com/c/tools/+/563876 Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent e325405 commit df9c1c7

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed

gopls/internal/server/diagnostics.go

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"os"
1313
"path/filepath"
14+
"runtime"
1415
"sort"
1516
"strings"
1617
"sync"
@@ -61,7 +62,9 @@ type (
6162
diagMap = map[protocol.DocumentURI][]*cache.Diagnostic
6263
)
6364

64-
// hashDiagnostics computes a hash to identify a diagnostic.
65+
// hashDiagnostic computes a hash to identify a diagnostic.
66+
// The hash is for deduplicating within a file,
67+
// so it need not incorporate d.URI.
6568
func hashDiagnostic(d *cache.Diagnostic) file.Hash {
6669
h := sha256.New()
6770
for _, t := range d.Tags {
@@ -822,23 +825,25 @@ func (s *server) updateOrphanedFileDiagnostics(ctx context.Context, modID uint64
822825
//
823826
// If the publication succeeds, it updates f.publishedHash and f.mustPublish.
824827
func (s *server) publishFileDiagnosticsLocked(ctx context.Context, views viewSet, uri protocol.DocumentURI, version int32, f *fileDiagnostics) error {
825-
// Check that the set of views is up-to-date, and de-dupe diagnostics
826-
// across views.
827-
var (
828-
diagHashes = make(map[file.Hash]unit) // unique diagnostic hashes
829-
hash file.Hash // XOR of diagnostic hashes
830-
unique []*cache.Diagnostic // unique diagnostics
831-
)
832-
add := func(diag *cache.Diagnostic) {
828+
829+
// We add a disambiguating suffix (e.g. " [darwin,arm64]") to
830+
// each diagnostic that doesn't occur in the default view;
831+
// see golang/go#65496.
832+
type diagSuffix struct {
833+
diag *cache.Diagnostic
834+
suffix string // "" for default build (or orphans)
835+
}
836+
837+
// diagSuffixes records the set of view suffixes for a given diagnostic.
838+
diagSuffixes := make(map[file.Hash][]diagSuffix)
839+
add := func(diag *cache.Diagnostic, suffix string) {
833840
h := hashDiagnostic(diag)
834-
if _, ok := diagHashes[h]; !ok {
835-
diagHashes[h] = unit{}
836-
unique = append(unique, diag)
837-
hash.XORWith(h)
838-
}
841+
diagSuffixes[h] = append(diagSuffixes[h], diagSuffix{diag, suffix})
839842
}
843+
844+
// Construct the inverse mapping, from diagnostic (hash) to its suffixes (views).
840845
for _, diag := range f.orphanedFileDiagnostics {
841-
add(diag)
846+
add(diag, "")
842847
}
843848
for view, viewDiags := range f.byView {
844849
if _, ok := views[view]; !ok {
@@ -848,10 +853,53 @@ func (s *server) publishFileDiagnosticsLocked(ctx context.Context, views viewSet
848853
if viewDiags.version != version {
849854
continue // a payload of diagnostics applies to a specific file version
850855
}
856+
857+
// Compute the view's suffix (e.g. " [darwin,arm64]").
858+
var suffix string
859+
{
860+
var words []string
861+
if view.GOOS() != runtime.GOOS {
862+
words = append(words, view.GOOS())
863+
}
864+
if view.GOARCH() != runtime.GOARCH {
865+
words = append(words, view.GOARCH())
866+
}
867+
if len(words) > 0 {
868+
suffix = fmt.Sprintf(" [%s]", strings.Join(words, ","))
869+
}
870+
}
871+
851872
for _, diag := range viewDiags.diagnostics {
852-
add(diag)
873+
add(diag, suffix)
853874
}
854875
}
876+
877+
// De-dup diagnostics across views by hash, and sort.
878+
var (
879+
hash file.Hash
880+
unique []*cache.Diagnostic
881+
)
882+
for h, items := range diagSuffixes {
883+
// Sort the items by ascending suffix, so that the
884+
// default view (if present) is first.
885+
// (The others are ordered arbitrarily.)
886+
sort.Slice(items, func(i, j int) bool {
887+
return items[i].suffix < items[j].suffix
888+
})
889+
890+
// If the diagnostic was not present in
891+
// the default view, add the view suffix.
892+
first := items[0]
893+
if first.suffix != "" {
894+
diag2 := *first.diag // shallow copy
895+
diag2.Message += first.suffix
896+
first.diag = &diag2
897+
h = hashDiagnostic(&diag2) // update the hash
898+
}
899+
900+
hash.XORWith(h)
901+
unique = append(unique, first.diag)
902+
}
855903
sortDiagnostics(unique)
856904

857905
// Publish, if necessary.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
This test verifies that we add an [os,arch] suffix to each diagnostic
2+
that doesn't appear in the default build (=runtime.{GOOS,GOARCH}).
3+
4+
See golang/go#65496.
5+
6+
The two p/*.go files below are written to trigger the same diagnostic
7+
(range, message, source, etc) but varying only by URI.
8+
9+
In the q test, a single location in the common code q.go has two
10+
diagnostics, one of which is tagged.
11+
12+
This test would fail on openbsd/mips64 because it will be
13+
the same as the default build, so we skip that platform.
14+
15+
-- flags --
16+
-skip_goos=openbsd
17+
18+
-- go.mod --
19+
module example.com
20+
21+
-- p/p.go --
22+
package p
23+
24+
var _ fmt.Stringer //@diag("fmt", re"unde.*: fmt$")
25+
26+
-- p/p_openbsd_mips64.go --
27+
package p
28+
29+
var _ fmt.Stringer //@diag("fmt", re"unde.*: fmt \\[openbsd,mips64\\]")
30+
31+
-- q/q_default.go --
32+
//+build !openbsd && !mips64
33+
34+
package q
35+
36+
func f(int) int
37+
38+
-- q/q_openbsd_mips64.go --
39+
package q
40+
41+
func f(string) int
42+
43+
-- q/q.go --
44+
package q
45+
46+
var _ = f() //@ diag(")", re`.*want \(string\) \[openbsd,mips64\]`), diag(")", re`.*want \(int\)$`)

0 commit comments

Comments
 (0)