gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/nogo/check/findings.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package check 16 17 import ( 18 "encoding/gob" 19 "encoding/json" 20 "fmt" 21 "go/token" 22 "io" 23 "os" 24 "sort" 25 ) 26 27 // Finding is a single finding. 28 type Finding struct { 29 Category string 30 Position token.Position 31 Message string 32 GOARCH string 33 GOOS string 34 } 35 36 // String implements fmt.Stringer.String. 37 func (f *Finding) String() string { 38 if f.GOARCH == "" && f.GOOS == "" { 39 // Use the legacy simplified string. 40 return fmt.Sprintf("%s: %s: %s", f.Category, f.Position.String(), f.Message) 41 } 42 // Use the more complete information. 43 return fmt.Sprintf("%s: %s: %s (GOOARCH=%s, GOOS=%s)", f.Category, f.Position.String(), f.Message, f.GOARCH, f.GOOS) 44 } 45 46 // FindingSet is a collection of findings. 47 type FindingSet []Finding 48 49 // Sort sorts all findings. 50 func (fs FindingSet) Sort() { 51 sort.Slice(fs, func(i, j int) bool { 52 switch { 53 case fs[i].Position.Filename < fs[j].Position.Filename: 54 return true 55 case fs[i].Position.Filename > fs[j].Position.Filename: 56 return false 57 case fs[i].Position.Line < fs[j].Position.Line: 58 return true 59 case fs[i].Position.Line > fs[j].Position.Line: 60 return false 61 case fs[i].Position.Column < fs[j].Position.Column: 62 return true 63 case fs[i].Position.Column > fs[j].Position.Column: 64 return false 65 case fs[i].Category < fs[j].Category: 66 return true 67 case fs[i].Category > fs[j].Category: 68 return false 69 case fs[i].Message < fs[j].Message: 70 return true 71 case fs[i].Message > fs[j].Message: 72 return false 73 default: 74 return false 75 } 76 }) 77 } 78 79 // WriteFindingsTo serializes findings. 80 func WriteFindingsTo(w io.Writer, findings FindingSet, asJSON bool) error { 81 // N.B. Sort all the findings in order to maximize cacheability. 82 findings.Sort() 83 if asJSON { 84 enc := json.NewEncoder(w) 85 return enc.Encode(findings) 86 } 87 enc := gob.NewEncoder(w) 88 return enc.Encode(findings) 89 } 90 91 // ExtractFindingsFromFile loads findings from a file. 92 func ExtractFindingsFromFile(filename string, asJSON bool) (FindingSet, error) { 93 r, err := os.Open(filename) 94 if err != nil { 95 return nil, err 96 } 97 defer r.Close() 98 return ExtractFindingsFrom(r, asJSON) 99 } 100 101 // ExtractFindingsFrom loads findings from an io.Reader. 102 func ExtractFindingsFrom(r io.Reader, asJSON bool) (findings FindingSet, err error) { 103 if asJSON { 104 dec := json.NewDecoder(r) 105 err = dec.Decode(&findings) 106 } else { 107 dec := gob.NewDecoder(r) 108 err = dec.Decode(&findings) 109 } 110 return findings, err 111 } 112 113 func init() { 114 gob.Register((*Finding)(nil)) 115 gob.Register((*FindingSet)(nil)) 116 }