github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/syntax/branches.go (about) 1 // Copyright 2017 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 syntax 6 7 import "fmt" 8 9 // TODO(gri) consider making this part of the parser code 10 11 // checkBranches checks correct use of labels and branch 12 // statements (break, continue, goto) in a function body. 13 // It catches: 14 // - misplaced breaks and continues 15 // - bad labeled breaks and continues 16 // - invalid, unused, duplicate, and missing labels 17 // - gotos jumping over variable declarations and into blocks 18 func checkBranches(body *BlockStmt, errh ErrorHandler) { 19 if body == nil { 20 return 21 } 22 23 // scope of all labels in this body 24 ls := &labelScope{errh: errh} 25 fwdGotos := ls.blockBranches(nil, targets{}, nil, body.Pos(), body.List) 26 27 // If there are any forward gotos left, no matching label was 28 // found for them. Either those labels were never defined, or 29 // they are inside blocks and not reachable from the gotos. 30 for _, fwd := range fwdGotos { 31 name := fwd.Label.Value 32 if l := ls.labels[name]; l != nil { 33 l.used = true // avoid "defined and not used" error 34 ls.err(fwd.Label.Pos(), "goto %s jumps into block starting at %s", name, l.parent.start) 35 } else { 36 ls.err(fwd.Label.Pos(), "label %s not defined", name) 37 } 38 } 39 40 // spec: "It is illegal to define a label that is never used." 41 for _, l := range ls.labels { 42 if !l.used { 43 l := l.lstmt.Label 44 ls.err(l.Pos(), "label %s defined and not used", l.Value) 45 } 46 } 47 } 48 49 type labelScope struct { 50 errh ErrorHandler 51 labels map[string]*label // all label declarations inside the function; allocated lazily 52 } 53 54 type label struct { 55 parent *block // block containing this label declaration 56 lstmt *LabeledStmt // statement declaring the label 57 used bool // whether the label is used or not 58 } 59 60 type block struct { 61 parent *block // immediately enclosing block, or nil 62 start Pos // start of block 63 lstmt *LabeledStmt // labeled statement associated with this block, or nil 64 } 65 66 func (ls *labelScope) err(pos Pos, format string, args ...interface{}) { 67 ls.errh(Error{pos, fmt.Sprintf(format, args...)}) 68 } 69 70 // declare declares the label introduced by s in block b and returns 71 // the new label. If the label was already declared, declare reports 72 // and error and the existing label is returned instead. 73 func (ls *labelScope) declare(b *block, s *LabeledStmt) *label { 74 name := s.Label.Value 75 labels := ls.labels 76 if labels == nil { 77 labels = make(map[string]*label) 78 ls.labels = labels 79 } else if alt := labels[name]; alt != nil { 80 ls.err(s.Label.Pos(), "label %s already defined at %s", name, alt.lstmt.Label.Pos().String()) 81 return alt 82 } 83 l := &label{b, s, false} 84 labels[name] = l 85 return l 86 } 87 88 // gotoTarget returns the labeled statement matching the given name and 89 // declared in block b or any of its enclosing blocks. The result is nil 90 // if the label is not defined, or doesn't match a valid labeled statement. 91 func (ls *labelScope) gotoTarget(b *block, name string) *LabeledStmt { 92 if l := ls.labels[name]; l != nil { 93 l.used = true // even if it's not a valid target 94 for ; b != nil; b = b.parent { 95 if l.parent == b { 96 return l.lstmt 97 } 98 } 99 } 100 return nil 101 } 102 103 var invalid = new(LabeledStmt) // singleton to signal invalid enclosing target 104 105 // enclosingTarget returns the innermost enclosing labeled statement matching 106 // the given name. The result is nil if the label is not defined, and invalid 107 // if the label is defined but doesn't label a valid labeled statement. 108 func (ls *labelScope) enclosingTarget(b *block, name string) *LabeledStmt { 109 if l := ls.labels[name]; l != nil { 110 l.used = true // even if it's not a valid target (see e.g., test/fixedbugs/bug136.go) 111 for ; b != nil; b = b.parent { 112 if l.lstmt == b.lstmt { 113 return l.lstmt 114 } 115 } 116 return invalid 117 } 118 return nil 119 } 120 121 // targets describes the target statements within which break 122 // or continue statements are valid. 123 type targets struct { 124 breaks Stmt // *ForStmt, *SwitchStmt, *SelectStmt, or nil 125 continues *ForStmt // or nil 126 } 127 128 // blockBranches processes a block's body starting at start and returns the 129 // list of unresolved (forward) gotos. parent is the immediately enclosing 130 // block (or nil), ctxt provides information about the enclosing statements, 131 // and lstmt is the labeled statement associated with this block, or nil. 132 func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledStmt, start Pos, body []Stmt) []*BranchStmt { 133 b := &block{parent: parent, start: start, lstmt: lstmt} 134 135 var varPos Pos 136 var varName Expr 137 var fwdGotos, badGotos []*BranchStmt 138 139 recordVarDecl := func(pos Pos, name Expr) { 140 varPos = pos 141 varName = name 142 // Any existing forward goto jumping over the variable 143 // declaration is invalid. The goto may still jump out 144 // of the block and be ok, but we don't know that yet. 145 // Remember all forward gotos as potential bad gotos. 146 badGotos = append(badGotos[:0], fwdGotos...) 147 } 148 149 jumpsOverVarDecl := func(fwd *BranchStmt) bool { 150 if varPos.IsKnown() { 151 for _, bad := range badGotos { 152 if fwd == bad { 153 return true 154 } 155 } 156 } 157 return false 158 } 159 160 innerBlock := func(ctxt targets, start Pos, body []Stmt) { 161 // Unresolved forward gotos from the inner block 162 // become forward gotos for the current block. 163 fwdGotos = append(fwdGotos, ls.blockBranches(b, ctxt, lstmt, start, body)...) 164 } 165 166 for _, stmt := range body { 167 lstmt = nil 168 L: 169 switch s := stmt.(type) { 170 case *DeclStmt: 171 for _, d := range s.DeclList { 172 if v, ok := d.(*VarDecl); ok { 173 recordVarDecl(v.Pos(), v.NameList[0]) 174 break // the first VarDecl will do 175 } 176 } 177 178 case *LabeledStmt: 179 // declare non-blank label 180 if name := s.Label.Value; name != "_" { 181 l := ls.declare(b, s) 182 // resolve matching forward gotos 183 i := 0 184 for _, fwd := range fwdGotos { 185 if fwd.Label.Value == name { 186 fwd.Target = s 187 l.used = true 188 if jumpsOverVarDecl(fwd) { 189 ls.err( 190 fwd.Label.Pos(), 191 "goto %s jumps over declaration of %s at %s", 192 name, String(varName), varPos, 193 ) 194 } 195 } else { 196 // no match - keep forward goto 197 fwdGotos[i] = fwd 198 i++ 199 } 200 } 201 fwdGotos = fwdGotos[:i] 202 lstmt = s 203 } 204 // process labeled statement 205 stmt = s.Stmt 206 goto L 207 208 case *BranchStmt: 209 // unlabeled branch statement 210 if s.Label == nil { 211 switch s.Tok { 212 case _Break: 213 if t := ctxt.breaks; t != nil { 214 s.Target = t 215 } else { 216 ls.err(s.Pos(), "break is not in a loop, switch, or select") 217 } 218 case _Continue: 219 if t := ctxt.continues; t != nil { 220 s.Target = t 221 } else { 222 ls.err(s.Pos(), "continue is not in a loop") 223 } 224 case _Fallthrough: 225 // nothing to do 226 case _Goto: 227 fallthrough // should always have a label 228 default: 229 panic("invalid BranchStmt") 230 } 231 break 232 } 233 234 // labeled branch statement 235 name := s.Label.Value 236 switch s.Tok { 237 case _Break: 238 // spec: "If there is a label, it must be that of an enclosing 239 // "for", "switch", or "select" statement, and that is the one 240 // whose execution terminates." 241 if t := ls.enclosingTarget(b, name); t != nil { 242 switch t := t.Stmt.(type) { 243 case *SwitchStmt, *SelectStmt, *ForStmt: 244 s.Target = t 245 default: 246 ls.err(s.Label.Pos(), "invalid break label %s", name) 247 } 248 } else { 249 ls.err(s.Label.Pos(), "break label not defined: %s", name) 250 } 251 252 case _Continue: 253 // spec: "If there is a label, it must be that of an enclosing 254 // "for" statement, and that is the one whose execution advances." 255 if t := ls.enclosingTarget(b, name); t != nil { 256 if t, ok := t.Stmt.(*ForStmt); ok { 257 s.Target = t 258 } else { 259 ls.err(s.Label.Pos(), "invalid continue label %s", name) 260 } 261 } else { 262 ls.err(s.Label.Pos(), "continue label not defined: %s", name) 263 } 264 265 case _Goto: 266 if t := ls.gotoTarget(b, name); t != nil { 267 s.Target = t 268 } else { 269 // label may be declared later - add goto to forward gotos 270 fwdGotos = append(fwdGotos, s) 271 } 272 273 case _Fallthrough: 274 fallthrough // should never have a label 275 default: 276 panic("invalid BranchStmt") 277 } 278 279 case *AssignStmt: 280 if s.Op == Def { 281 recordVarDecl(s.Pos(), s.Lhs) 282 } 283 284 case *BlockStmt: 285 innerBlock(ctxt, s.Pos(), s.List) 286 287 case *IfStmt: 288 innerBlock(ctxt, s.Then.Pos(), s.Then.List) 289 if s.Else != nil { 290 innerBlock(ctxt, s.Else.Pos(), []Stmt{s.Else}) 291 } 292 293 case *ForStmt: 294 innerBlock(targets{s, s}, s.Body.Pos(), s.Body.List) 295 296 case *SwitchStmt: 297 inner := targets{s, ctxt.continues} 298 for _, cc := range s.Body { 299 innerBlock(inner, cc.Pos(), cc.Body) 300 } 301 302 case *SelectStmt: 303 inner := targets{s, ctxt.continues} 304 for _, cc := range s.Body { 305 innerBlock(inner, cc.Pos(), cc.Body) 306 } 307 } 308 } 309 310 return fwdGotos 311 }