mirror of
https://github.com/golang/go
synced 2024-11-18 16:34:51 -07:00
74512f09e4
https://go-review.googlesource.com/c/tools/+/246419/2 fixed a problem but introduced a new one, as go build treats -o directories differently depending on whether or not a main package is being built. (see https://github.com/golang/go/issues/36784) This change explicitly constructs a temporary file for go build to use. Change-Id: I096748e9af5014428dab8a5aad703f062fe88d50 Reviewed-on: https://go-review.googlesource.com/c/tools/+/247899 Run-TryBot: Peter Weinberger <pjw@google.com> Reviewed-by: Pontus Leitzler <leitzler@gmail.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
157 lines
4.0 KiB
Go
157 lines
4.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
|
|
}
|
|
tmpFile, err := ioutil.TempFile(os.TempDir(), "gopls-x")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer os.Remove(tmpFile.Name())
|
|
args := []string{fmt.Sprintf("-gcflags=-json=0,%s", outDir),
|
|
fmt.Sprintf("-o=%s", tmpFile.Name()),
|
|
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)
|
|
opts := snapshot.View().Options()
|
|
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
|
|
}
|
|
v = filterDiagnostics(v, &opts)
|
|
reports[x.VersionedFileIdentity()] = v
|
|
}
|
|
return reports, parseError
|
|
}
|
|
|
|
func filterDiagnostics(v []*Diagnostic, o *Options) []*Diagnostic {
|
|
var ans []*Diagnostic
|
|
for _, x := range v {
|
|
if x.Source != "go compiler" {
|
|
continue
|
|
}
|
|
if o.Annotations["noInline"] &&
|
|
(strings.HasPrefix(x.Message, "canInline") ||
|
|
strings.HasPrefix(x.Message, "cannotInline") ||
|
|
strings.HasPrefix(x.Message, "inlineCall")) {
|
|
continue
|
|
} else if o.Annotations["noEscape"] &&
|
|
(strings.HasPrefix(x.Message, "escape") || x.Message == "leak") {
|
|
continue
|
|
} else if o.Annotations["noNilcheck"] && strings.HasPrefix(x.Message, "nilcheck") {
|
|
continue
|
|
} else if o.Annotations["noBounds"] &&
|
|
(strings.HasPrefix(x.Message, "isInBounds") ||
|
|
strings.HasPrefix(x.Message, "isSliceInBounds")) {
|
|
continue
|
|
}
|
|
ans = append(ans, x)
|
|
}
|
|
return ans
|
|
}
|
|
|
|
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
|
|
}
|