github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/go/types/labels.go (about) 1 // Copyright 2013 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 types 6 7 import ( 8 "go/ast" 9 "go/token" 10 ) 11 12 // labels checks correct label use in body. 13 func (check *Checker) labels(body *ast.BlockStmt) { 14 // set of all labels in this body 15 all := NewScope(nil, body.Pos(), body.End(), "label") 16 17 fwdJumps := check.blockBranches(all, nil, nil, body.List) 18 19 // If there are any forward jumps left, no label was found for 20 // the corresponding goto statements. Either those labels were 21 // never defined, or they are inside blocks and not reachable 22 // for the respective gotos. 23 for _, jmp := range fwdJumps { 24 var msg string 25 name := jmp.Label.Name 26 if alt := all.Lookup(name); alt != nil { 27 msg = "goto %s jumps into block" 28 alt.(*Label).used = true // avoid another error 29 } else { 30 msg = "label %s not declared" 31 } 32 check.errorf(jmp.Label.Pos(), msg, name) 33 } 34 35 // spec: "It is illegal to define a label that is never used." 36 for _, obj := range all.elems { 37 if lbl := obj.(*Label); !lbl.used { 38 check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name) 39 } 40 } 41 } 42 43 // A block tracks label declarations in a block and its enclosing blocks. 44 type block struct { 45 parent *block // enclosing block 46 lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil 47 labels map[string]*ast.LabeledStmt // allocated lazily 48 } 49 50 // insert records a new label declaration for the current block. 51 // The label must not have been declared before in any block. 52 func (b *block) insert(s *ast.LabeledStmt) { 53 name := s.Label.Name 54 if debug { 55 assert(b.gotoTarget(name) == nil) 56 } 57 labels := b.labels 58 if labels == nil { 59 labels = make(map[string]*ast.LabeledStmt) 60 b.labels = labels 61 } 62 labels[name] = s 63 } 64 65 // gotoTarget returns the labeled statement in the current 66 // or an enclosing block with the given label name, or nil. 67 func (b *block) gotoTarget(name string) *ast.LabeledStmt { 68 for s := b; s != nil; s = s.parent { 69 if t := s.labels[name]; t != nil { 70 return t 71 } 72 } 73 return nil 74 } 75 76 // enclosingTarget returns the innermost enclosing labeled 77 // statement with the given label name, or nil. 78 func (b *block) enclosingTarget(name string) *ast.LabeledStmt { 79 for s := b; s != nil; s = s.parent { 80 if t := s.lstmt; t != nil && t.Label.Name == name { 81 return t 82 } 83 } 84 return nil 85 } 86 87 // blockBranches processes a block's statement list and returns the set of outgoing forward jumps. 88 // all is the scope of all declared labels, parent the set of labels declared in the immediately 89 // enclosing block, and lstmt is the labeled statement this block is associated with (or nil). 90 func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt { 91 b := &block{parent: parent, lstmt: lstmt} 92 93 var ( 94 varDeclPos token.Pos 95 fwdJumps, badJumps []*ast.BranchStmt 96 ) 97 98 // All forward jumps jumping over a variable declaration are possibly 99 // invalid (they may still jump out of the block and be ok). 100 // recordVarDecl records them for the given position. 101 recordVarDecl := func(pos token.Pos) { 102 varDeclPos = pos 103 badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps 104 } 105 106 jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool { 107 if varDeclPos.IsValid() { 108 for _, bad := range badJumps { 109 if jmp == bad { 110 return true 111 } 112 } 113 } 114 return false 115 } 116 117 blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) { 118 // Unresolved forward jumps inside the nested block 119 // become forward jumps in the current block. 120 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...) 121 } 122 123 var stmtBranches func(ast.Stmt) 124 stmtBranches = func(s ast.Stmt) { 125 switch s := s.(type) { 126 case *ast.DeclStmt: 127 if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR { 128 recordVarDecl(d.Pos()) 129 } 130 131 case *ast.LabeledStmt: 132 // declare non-blank label 133 if name := s.Label.Name; name != "_" { 134 lbl := NewLabel(s.Label.Pos(), check.pkg, name) 135 if alt := all.Insert(lbl); alt != nil { 136 check.softErrorf(lbl.pos, "label %s already declared", name) 137 check.reportAltDecl(alt) 138 // ok to continue 139 } else { 140 b.insert(s) 141 check.recordDef(s.Label, lbl) 142 } 143 // resolve matching forward jumps and remove them from fwdJumps 144 i := 0 145 for _, jmp := range fwdJumps { 146 if jmp.Label.Name == name { 147 // match 148 lbl.used = true 149 check.recordUse(jmp.Label, lbl) 150 if jumpsOverVarDecl(jmp) { 151 check.softErrorf( 152 jmp.Label.Pos(), 153 "goto %s jumps over variable declaration at line %d", 154 name, 155 check.fset.Position(varDeclPos).Line, 156 ) 157 // ok to continue 158 } 159 } else { 160 // no match - record new forward jump 161 fwdJumps[i] = jmp 162 i++ 163 } 164 } 165 fwdJumps = fwdJumps[:i] 166 lstmt = s 167 } 168 stmtBranches(s.Stmt) 169 170 case *ast.BranchStmt: 171 if s.Label == nil { 172 return // checked in 1st pass (check.stmt) 173 } 174 175 // determine and validate target 176 name := s.Label.Name 177 switch s.Tok { 178 case token.BREAK: 179 // spec: "If there is a label, it must be that of an enclosing 180 // "for", "switch", or "select" statement, and that is the one 181 // whose execution terminates." 182 valid := false 183 if t := b.enclosingTarget(name); t != nil { 184 switch t.Stmt.(type) { 185 case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt: 186 valid = true 187 } 188 } 189 if !valid { 190 check.errorf(s.Label.Pos(), "invalid break label %s", name) 191 return 192 } 193 194 case token.CONTINUE: 195 // spec: "If there is a label, it must be that of an enclosing 196 // "for" statement, and that is the one whose execution advances." 197 valid := false 198 if t := b.enclosingTarget(name); t != nil { 199 switch t.Stmt.(type) { 200 case *ast.ForStmt, *ast.RangeStmt: 201 valid = true 202 } 203 } 204 if !valid { 205 check.errorf(s.Label.Pos(), "invalid continue label %s", name) 206 return 207 } 208 209 case token.GOTO: 210 if b.gotoTarget(name) == nil { 211 // label may be declared later - add branch to forward jumps 212 fwdJumps = append(fwdJumps, s) 213 return 214 } 215 216 default: 217 check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name) 218 return 219 } 220 221 // record label use 222 obj := all.Lookup(name) 223 obj.(*Label).used = true 224 check.recordUse(s.Label, obj) 225 226 case *ast.AssignStmt: 227 if s.Tok == token.DEFINE { 228 recordVarDecl(s.Pos()) 229 } 230 231 case *ast.BlockStmt: 232 blockBranches(lstmt, s.List) 233 234 case *ast.IfStmt: 235 stmtBranches(s.Body) 236 if s.Else != nil { 237 stmtBranches(s.Else) 238 } 239 240 case *ast.CaseClause: 241 blockBranches(nil, s.Body) 242 243 case *ast.SwitchStmt: 244 stmtBranches(s.Body) 245 246 case *ast.TypeSwitchStmt: 247 stmtBranches(s.Body) 248 249 case *ast.CommClause: 250 blockBranches(nil, s.Body) 251 252 case *ast.SelectStmt: 253 stmtBranches(s.Body) 254 255 case *ast.ForStmt: 256 stmtBranches(s.Body) 257 258 case *ast.RangeStmt: 259 stmtBranches(s.Body) 260 } 261 } 262 263 for _, s := range list { 264 stmtBranches(s) 265 } 266 267 return fwdJumps 268 }