1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:14:43 -07:00
go/internal/lsp/source/gc_annotations.go
Heschi Kreinick c9619e8fac internal/lsp: separate LSP files from FS files
FileHandle currently includes LSP-level information about Version and
Session. That's dangerous, because the cache operates in terms of
URIs and content only -- we explicitly want to share results across
sessions and versions if they happen to be the same.

Split the LSP information into separate types, VersionedFileHandle and
VersionedFileIdentity.

Change-Id: I158646b783375b58245468599301e2a29c657e71
Reviewed-on: https://go-review.googlesource.com/c/tools/+/245058
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
2020-08-03 22:16:06 +00:00

121 lines
3.0 KiB
Go

// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package source
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
)
func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[VersionedFileIdentity][]*Diagnostic, error) {
outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
if err := os.MkdirAll(outDir, 0700); err != nil {
return nil, err
}
args := []string{fmt.Sprintf("-gcflags=-json=0,%s", outDir), pkgDir.Filename()}
err := snapshot.RunGoCommandDirect(ctx, "build", args)
if err != nil {
return nil, err
}
files, err := findJSONFiles(outDir)
if err != nil {
return nil, err
}
reports := make(map[VersionedFileIdentity][]*Diagnostic)
var parseError error
for _, fn := range files {
fname, v, err := parseDetailsFile(fn)
if err != nil {
// expect errors for all the files, save 1
parseError = err
}
if !strings.HasSuffix(fname, ".go") {
continue // <autogenerated>
}
uri := span.URIFromPath(fname)
x := snapshot.FindFile(uri)
if x == nil {
continue
}
reports[x.VersionedFileIdentity()] = v
}
return reports, parseError
}
func parseDetailsFile(fn string) (string, []*Diagnostic, error) {
buf, err := ioutil.ReadFile(fn)
if err != nil {
return "", nil, err // This is an internal error. Likely ever file will fail.
}
var fname string
var ans []*Diagnostic
lines := bytes.Split(buf, []byte{'\n'})
for i, l := range lines {
if len(l) == 0 {
continue
}
if i == 0 {
x := make(map[string]interface{})
if err := json.Unmarshal(l, &x); err != nil {
return "", nil, fmt.Errorf("internal error (%v) parsing first line of json file %s",
err, fn)
}
fname = x["file"].(string)
continue
}
y := protocol.Diagnostic{}
if err := json.Unmarshal(l, &y); err != nil {
return "", nil, fmt.Errorf("internal error (%#v) parsing json file for %s", err, fname)
}
y.Range.Start.Line-- // change from 1-based to 0-based
y.Range.Start.Character--
y.Range.End.Line--
y.Range.End.Character--
msg := y.Code.(string)
if y.Message != "" {
msg = fmt.Sprintf("%s(%s)", msg, y.Message)
}
x := Diagnostic{
Range: y.Range,
Message: msg,
Source: y.Source,
Severity: y.Severity,
}
for _, ri := range y.RelatedInformation {
x.Related = append(x.Related, RelatedInformation{
URI: ri.Location.URI.SpanURI(),
Range: ri.Location.Range,
Message: ri.Message,
})
}
ans = append(ans, &x)
}
return fname, ans, nil
}
func findJSONFiles(dir string) ([]string, error) {
ans := []string{}
f := func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
if strings.HasSuffix(path, ".json") {
ans = append(ans, path)
}
return nil
}
err := filepath.Walk(dir, f)
return ans, err
}