github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/source/gc_annotations.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package source 6 7 import ( 8 "bytes" 9 "context" 10 "encoding/json" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "strings" 16 17 "github.com/april1989/origin-go-tools/internal/lsp/protocol" 18 "github.com/april1989/origin-go-tools/internal/span" 19 ) 20 21 func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[VersionedFileIdentity][]*Diagnostic, error) { 22 outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid())) 23 if err := os.MkdirAll(outDir, 0700); err != nil { 24 return nil, err 25 } 26 tmpFile, err := ioutil.TempFile(os.TempDir(), "gopls-x") 27 if err != nil { 28 return nil, err 29 } 30 defer os.Remove(tmpFile.Name()) 31 args := []string{fmt.Sprintf("-gcflags=-json=0,%s", outDir), 32 fmt.Sprintf("-o=%s", tmpFile.Name()), 33 pkgDir.Filename(), 34 } 35 err = snapshot.RunGoCommandDirect(ctx, "build", args) 36 if err != nil { 37 return nil, err 38 } 39 files, err := findJSONFiles(outDir) 40 if err != nil { 41 return nil, err 42 } 43 reports := make(map[VersionedFileIdentity][]*Diagnostic) 44 opts := snapshot.View().Options() 45 var parseError error 46 for _, fn := range files { 47 fname, v, err := parseDetailsFile(fn) 48 if err != nil { 49 // expect errors for all the files, save 1 50 parseError = err 51 } 52 if !strings.HasSuffix(fname, ".go") { 53 continue // <autogenerated> 54 } 55 uri := span.URIFromPath(fname) 56 x := snapshot.FindFile(uri) 57 if x == nil { 58 continue 59 } 60 v = filterDiagnostics(v, &opts) 61 reports[x.VersionedFileIdentity()] = v 62 } 63 return reports, parseError 64 } 65 66 func filterDiagnostics(v []*Diagnostic, o *Options) []*Diagnostic { 67 var ans []*Diagnostic 68 for _, x := range v { 69 if x.Source != "go compiler" { 70 continue 71 } 72 if o.Annotations["noInline"] && 73 (strings.HasPrefix(x.Message, "canInline") || 74 strings.HasPrefix(x.Message, "cannotInline") || 75 strings.HasPrefix(x.Message, "inlineCall")) { 76 continue 77 } else if o.Annotations["noEscape"] && 78 (strings.HasPrefix(x.Message, "escape") || x.Message == "leak") { 79 continue 80 } else if o.Annotations["noNilcheck"] && strings.HasPrefix(x.Message, "nilcheck") { 81 continue 82 } else if o.Annotations["noBounds"] && 83 (strings.HasPrefix(x.Message, "isInBounds") || 84 strings.HasPrefix(x.Message, "isSliceInBounds")) { 85 continue 86 } 87 ans = append(ans, x) 88 } 89 return ans 90 } 91 92 func parseDetailsFile(fn string) (string, []*Diagnostic, error) { 93 buf, err := ioutil.ReadFile(fn) 94 if err != nil { 95 return "", nil, err // This is an internal error. Likely ever file will fail. 96 } 97 var fname string 98 var ans []*Diagnostic 99 lines := bytes.Split(buf, []byte{'\n'}) 100 for i, l := range lines { 101 if len(l) == 0 { 102 continue 103 } 104 if i == 0 { 105 x := make(map[string]interface{}) 106 if err := json.Unmarshal(l, &x); err != nil { 107 return "", nil, fmt.Errorf("internal error (%v) parsing first line of json file %s", 108 err, fn) 109 } 110 fname = x["file"].(string) 111 continue 112 } 113 y := protocol.Diagnostic{} 114 if err := json.Unmarshal(l, &y); err != nil { 115 return "", nil, fmt.Errorf("internal error (%#v) parsing json file for %s", err, fname) 116 } 117 y.Range.Start.Line-- // change from 1-based to 0-based 118 y.Range.Start.Character-- 119 y.Range.End.Line-- 120 y.Range.End.Character-- 121 msg := y.Code.(string) 122 if y.Message != "" { 123 msg = fmt.Sprintf("%s(%s)", msg, y.Message) 124 } 125 x := Diagnostic{ 126 Range: y.Range, 127 Message: msg, 128 Source: y.Source, 129 Severity: y.Severity, 130 } 131 for _, ri := range y.RelatedInformation { 132 x.Related = append(x.Related, RelatedInformation{ 133 URI: ri.Location.URI.SpanURI(), 134 Range: ri.Location.Range, 135 Message: ri.Message, 136 }) 137 } 138 ans = append(ans, &x) 139 } 140 return fname, ans, nil 141 } 142 143 func findJSONFiles(dir string) ([]string, error) { 144 ans := []string{} 145 f := func(path string, fi os.FileInfo, err error) error { 146 if fi.IsDir() { 147 return nil 148 } 149 if strings.HasSuffix(path, ".json") { 150 ans = append(ans, path) 151 } 152 return nil 153 } 154 err := filepath.Walk(dir, f) 155 return ans, err 156 }