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