github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/tools/checklocks/annotations.go (about) 1 // Copyright 2020 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 checklocks 16 17 import ( 18 "fmt" 19 20 "go/token" 21 "strconv" 22 "strings" 23 ) 24 25 const ( 26 checkLocksAnnotation = "// +checklocks:" 27 checkLocksAnnotationRead = "// +checklocksread:" 28 checkLocksAcquires = "// +checklocksacquire:" 29 checkLocksAcquiresRead = "// +checklocksacquireread:" 30 checkLocksReleases = "// +checklocksrelease:" 31 checkLocksReleasesRead = "// +checklocksreleaseread:" 32 checkLocksIgnore = "// +checklocksignore" 33 checkLocksForce = "// +checklocksforce" 34 checkLocksFail = "// +checklocksfail" 35 checkLocksAlias = "// +checklocksalias:" 36 checkAtomicAnnotation = "// +checkatomic" 37 ) 38 39 // failData indicates an expected failure. 40 type failData struct { 41 pos token.Pos 42 count int 43 seen int 44 } 45 46 // positionKey is a simple position string. 47 type positionKey string 48 49 // positionKey converts from a token.Pos to a key we can use to track failures 50 // as the position of the failure annotation is not the same as the position of 51 // the actual failure (different column/offsets). Hence we ignore these fields 52 // and only use the file/line numbers to track failures. 53 func (pc *passContext) positionKey(pos token.Pos) positionKey { 54 position := pc.pass.Fset.Position(pos) 55 return positionKey(fmt.Sprintf("%s:%d", position.Filename, position.Line)) 56 } 57 58 // addFailures adds an expected failure. 59 func (pc *passContext) addFailures(pos token.Pos, s string) { 60 count := 1 61 if len(s) > 0 && s[0] == ':' { 62 parsedCount, err := strconv.Atoi(s[1:]) 63 if err != nil { 64 pc.pass.Reportf(pos, "unable to parse failure annotation %q: %v", s[1:], err) 65 return 66 } 67 count = parsedCount 68 } 69 pc.failures[pc.positionKey(pos)] = &failData{ 70 pos: pos, 71 count: count, 72 } 73 } 74 75 // addExemption adds an exemption. 76 func (pc *passContext) addExemption(pos token.Pos) { 77 pc.exemptions[pc.positionKey(pos)] = struct{}{} 78 } 79 80 // addForce adds a force annotation. 81 func (pc *passContext) addForce(pos token.Pos) { 82 pc.forced[pc.positionKey(pos)] = struct{}{} 83 } 84 85 // maybeFail checks a potential failure against a specific failure map. 86 func (pc *passContext) maybeFail(pos token.Pos, fmtStr string, args ...any) { 87 if fd, ok := pc.failures[pc.positionKey(pos)]; ok { 88 fd.seen++ 89 return 90 } 91 if _, ok := pc.exemptions[pc.positionKey(pos)]; ok { 92 return // Ignored, not counted. 93 } 94 if !enableWrappers && !pos.IsValid() { 95 return // Ignored, implicit. 96 } 97 pc.pass.Reportf(pos, fmtStr, args...) 98 } 99 100 // checkFailure checks for the expected failure counts. 101 func (pc *passContext) checkFailures() { 102 for _, fd := range pc.failures { 103 if fd.count != fd.seen { 104 // We are missing expect failures, report as much as possible. 105 pc.pass.Reportf(fd.pos, "got %d failures, want %d failures", fd.seen, fd.count) 106 } 107 } 108 } 109 110 // extractAnnotations extracts annotations from text. 111 func (pc *passContext) extractAnnotations(s string, fns map[string]func(p string)) { 112 for prefix, fn := range fns { 113 if strings.HasPrefix(s, prefix) { 114 fn(s[len(prefix):]) 115 } 116 } 117 } 118 119 // extractLineFailures extracts all line-based exceptions. 120 // 121 // Note that this applies only to individual line exemptions, and does not 122 // consider function-wide exemptions, or specific field exemptions, which are 123 // extracted separately as part of the saved facts for those objects. 124 func (pc *passContext) extractLineFailures() { 125 for _, f := range pc.pass.Files { 126 for _, cg := range f.Comments { 127 for _, c := range cg.List { 128 pc.extractAnnotations(c.Text, map[string]func(string){ 129 checkLocksFail: func(p string) { pc.addFailures(c.Pos(), p) }, 130 checkLocksIgnore: func(string) { pc.addExemption(c.Pos()) }, 131 checkLocksForce: func(string) { pc.addForce(c.Pos()) }, 132 }) 133 } 134 } 135 } 136 }