@@ -11,6 +11,7 @@ import (
11
11
"fmt"
12
12
"os"
13
13
"path/filepath"
14
+ "runtime"
14
15
"sort"
15
16
"strings"
16
17
"sync"
61
62
diagMap = map [protocol.DocumentURI ][]* cache.Diagnostic
62
63
)
63
64
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.
65
68
func hashDiagnostic (d * cache.Diagnostic ) file.Hash {
66
69
h := sha256 .New ()
67
70
for _ , t := range d .Tags {
@@ -822,23 +825,25 @@ func (s *server) updateOrphanedFileDiagnostics(ctx context.Context, modID uint64
822
825
//
823
826
// If the publication succeeds, it updates f.publishedHash and f.mustPublish.
824
827
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 ) {
833
840
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 })
839
842
}
843
+
844
+ // Construct the inverse mapping, from diagnostic (hash) to its suffixes (views).
840
845
for _ , diag := range f .orphanedFileDiagnostics {
841
- add (diag )
846
+ add (diag , "" )
842
847
}
843
848
for view , viewDiags := range f .byView {
844
849
if _ , ok := views [view ]; ! ok {
@@ -848,10 +853,53 @@ func (s *server) publishFileDiagnosticsLocked(ctx context.Context, views viewSet
848
853
if viewDiags .version != version {
849
854
continue // a payload of diagnostics applies to a specific file version
850
855
}
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
+
851
872
for _ , diag := range viewDiags .diagnostics {
852
- add (diag )
873
+ add (diag , suffix )
853
874
}
854
875
}
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
+ }
855
903
sortDiagnostics (unique )
856
904
857
905
// Publish, if necessary.
0 commit comments