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