func (s *Server) storeDiagnostics(snapshot source.Snapshot, uri span.URI, dsource diagnosticSource, diags []*source.Diagnostic) {
	fh := snapshot.FindFile(uri)
	if fh == nil {
		return
	}
	s.diagnosticsMu.Lock()
	defer s.diagnosticsMu.Unlock()
	if s.diagnostics[uri] == nil {
		s.diagnostics[uri] = &fileReports{
			publishedHash: hashDiagnostics(),  
			reports:       map[diagnosticSource]diagnosticReport{},
		}
	}
	report := s.diagnostics[uri].reports[dsource]
	if report.snapshotID > snapshot.ID() {
		return
	}
	if report.diags == nil || report.snapshotID != snapshot.ID() {
		report.diags = map[string]*source.Diagnostic{}
	}
	report.snapshotID = snapshot.ID()
	for _, d := range diags {
		report.diags[hashDiagnostics(d)] = d
	}
	s.diagnostics[uri].reports[dsource] = report
}
storeDiagnostics stores results from a single diagnostic source. If merge is
true, it merges results into any existing results for this snapshot.
package lsp
import (
	"context"
	"crypto/sha256"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/event"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/debug/log"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/debug/tag"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/mod"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/protocol"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/source"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/template"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/span"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/xcontext"
	errors "golang.org/x/xerrors"
)
type diagnosticSource int
const (
	modSource diagnosticSource = iota
	gcDetailsSource
	analysisSource
	typeCheckSource
	orphanedSource
)
type diagnosticReport struct {
	snapshotID    uint64
	publishedHash string
	diags         map[string]*source.Diagnostic
}
type fileReports struct {
	snapshotID    uint64
	publishedHash string
	reports       map[diagnosticSource]diagnosticReport
}
func (d diagnosticSource) String() string {
	switch d {
	case modSource:
		return "FromSource"
	case gcDetailsSource:
		return "FromGCDetails"
	case analysisSource:
		return "FromAnalysis"
	case typeCheckSource:
		return "FromTypeChecking"
	case orphanedSource:
		return "FromOrphans"
	default:
		return fmt.Sprintf("From?%d?", d)
	}
}
func hashDiagnostics(diags ...*source.Diagnostic) string {
	source.SortDiagnostics(diags)
	h := sha256.New()
	for _, d := range diags {
		for _, t := range d.Tags {
			fmt.Fprintf(h, "%s", t)
		}
		for _, r := range d.Related {
			fmt.Fprintf(h, "%s%s%s", r.URI, r.Message, r.Range)
		}
		fmt.Fprintf(h, "%s%s%s%s", d.Message, d.Range, d.Severity, d.Source)
	}
	return fmt.Sprintf("%x", h.Sum(nil))
}
func (s *Server) diagnoseDetached(snapshot source.Snapshot) {
	ctx := snapshot.BackgroundContext()
	ctx = xcontext.Detach(ctx)
	s.diagnose(ctx, snapshot, false)
	s.publishDiagnostics(ctx, true, snapshot)
}
func (s *Server) diagnoseSnapshots(snapshots map[source.Snapshot][]span.URI, onDisk bool) {
	var diagnosticWG sync.WaitGroup
	for snapshot, uris := range snapshots {
		diagnosticWG.Add(1)
		go func(snapshot source.Snapshot, uris []span.URI) {
			defer diagnosticWG.Done()
			s.diagnoseSnapshot(snapshot, uris, onDisk)
		}(snapshot, uris)
	}
	diagnosticWG.Wait()
}
func (s *Server) diagnoseSnapshot(snapshot source.Snapshot, changedURIs []span.URI, onDisk bool) {
	ctx := snapshot.BackgroundContext()
	ctx, done := event.Start(ctx, "Server.diagnoseSnapshot", tag.Snapshot.Of(snapshot.ID()))
	defer done()
	delay := snapshot.View().Options().DiagnosticsDelay
	if delay > 0 {
		s.diagnoseChangedFiles(ctx, snapshot, changedURIs, onDisk)
		s.publishDiagnostics(ctx, false, snapshot)
		if ok := <-s.diagDebouncer.debounce(snapshot.View().Name(), snapshot.ID(), time.After(delay)); ok {
			s.diagnose(ctx, snapshot, false)
			s.publishDiagnostics(ctx, true, snapshot)
		}
		return
	}
	s.diagnose(ctx, snapshot, false)
	s.publishDiagnostics(ctx, true, snapshot)
}
func (s *Server) diagnoseChangedFiles(ctx context.Context, snapshot source.Snapshot, uris []span.URI, onDisk bool) {
	ctx, done := event.Start(ctx, "Server.diagnoseChangedFiles", tag.Snapshot.Of(snapshot.ID()))
	defer done()
	packages := make(map[source.Package]struct{})
	for _, uri := range uris {
		if onDisk && !snapshot.IsOpen(uri) {
			continue
		}
		if snapshot.FindFile(uri) == nil {
			continue
		}
		if snapshot.IsBuiltin(ctx, uri) {
			continue
		}
		pkgs, err := snapshot.PackagesForFile(ctx, uri, source.TypecheckFull, false)
		if err != nil {
			continue
		}
		for _, pkg := range pkgs {
			packages[pkg] = struct{}{}
		}
	}
	var wg sync.WaitGroup
	for pkg := range packages {
		wg.Add(1)
		go func(pkg source.Package) {
			defer wg.Done()
			s.diagnosePkg(ctx, snapshot, pkg, false)
		}(pkg)
	}
	wg.Wait()
}
func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, forceAnalysis bool) {
	ctx, done := event.Start(ctx, "Server.diagnose", tag.Snapshot.Of(snapshot.ID()))
	defer done()
	select {
	case <-ctx.Done():
		return
	case s.diagnosticsSema <- struct{}{}:
	}
	defer func() {
		<-s.diagnosticsSema
	}()
	modReports, modErr := mod.Diagnostics(ctx, snapshot)
	if ctx.Err() != nil {
		log.Trace.Log(ctx, "diagnose cancelled")
		return
	}
	if modErr != nil {
		event.Error(ctx, "warning: diagnose go.mod", modErr, tag.Directory.Of(snapshot.View().Folder().Filename()), tag.Snapshot.Of(snapshot.ID()))
	}
	for id, diags := range modReports {
		if id.URI == "" {
			event.Error(ctx, "missing URI for module diagnostics", fmt.Errorf("empty URI"), tag.Directory.Of(snapshot.View().Folder().Filename()))
			continue
		}
		s.storeDiagnostics(snapshot, id.URI, modSource, diags)
	}
	wsPkgs, err := snapshot.ActivePackages(ctx)
	if s.shouldIgnoreError(ctx, snapshot, err) {
		return
	}
	criticalErr := snapshot.GetCriticalError(ctx)
	s.showCriticalErrorStatus(ctx, snapshot, criticalErr)
	for _, f := range snapshot.Templates() {
		diags := template.Diagnose(f)
		s.storeDiagnostics(snapshot, f.URI(), typeCheckSource, diags)
	}
	if len(wsPkgs) == 0 {
		return
	}
	var (
		wg   sync.WaitGroup
		seen = map[span.URI]struct{}{}
	)
	for _, pkg := range wsPkgs {
		wg.Add(1)
		for _, pgf := range pkg.CompiledGoFiles() {
			seen[pgf.URI] = struct{}{}
		}
		go func(pkg source.Package) {
			defer wg.Done()
			s.diagnosePkg(ctx, snapshot, pkg, forceAnalysis)
		}(pkg)
	}
	wg.Wait()
	for _, o := range s.session.Overlays() {
		if _, ok := seen[o.URI()]; ok {
			continue
		}
		diagnostic := s.checkForOrphanedFile(ctx, snapshot, o)
		if diagnostic == nil {
			continue
		}
		s.storeDiagnostics(snapshot, o.URI(), orphanedSource, []*source.Diagnostic{diagnostic})
	}
}
func (s *Server) diagnosePkg(ctx context.Context, snapshot source.Snapshot, pkg source.Package, alwaysAnalyze bool) {
	ctx, done := event.Start(ctx, "Server.diagnosePkg", tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
	defer done()
	enableDiagnostics := false
	includeAnalysis := alwaysAnalyze  
	for _, pgf := range pkg.CompiledGoFiles() {
		enableDiagnostics = enableDiagnostics || !snapshot.IgnoredFile(pgf.URI)
		includeAnalysis = includeAnalysis || snapshot.IsOpen(pgf.URI)
	}
	if !enableDiagnostics {
		return
	}
	pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg)
	if err != nil {
		event.Error(ctx, "warning: diagnosing package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
		return
	}
	for _, cgf := range pkg.CompiledGoFiles() {
		s.storeDiagnostics(snapshot, cgf.URI, typeCheckSource, pkgDiagnostics[cgf.URI])
	}
	if includeAnalysis && !pkg.HasListOrParseErrors() {
		reports, err := source.Analyze(ctx, snapshot, pkg, false)
		if err != nil {
			event.Error(ctx, "warning: analyzing package", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
			return
		}
		for _, cgf := range pkg.CompiledGoFiles() {
			s.storeDiagnostics(snapshot, cgf.URI, analysisSource, reports[cgf.URI])
		}
	}
	s.gcOptimizationDetailsMu.Lock()
	_, enableGCDetails := s.gcOptimizationDetails[pkg.ID()]
	s.gcOptimizationDetailsMu.Unlock()
	if enableGCDetails {
		gcReports, err := source.GCOptimizationDetails(ctx, snapshot, pkg)
		if err != nil {
			event.Error(ctx, "warning: gc details", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
		}
		s.gcOptimizationDetailsMu.Lock()
		_, enableGCDetails := s.gcOptimizationDetails[pkg.ID()]
		if enableGCDetails {
			for id, diags := range gcReports {
				fh := snapshot.FindFile(id.URI)
				if fh == nil || !fh.Saved() {
					continue
				}
				s.storeDiagnostics(snapshot, id.URI, gcDetailsSource, diags)
			}
		}
		s.gcOptimizationDetailsMu.Unlock()
	}
}