Source file
src/go/types/labels.go
1
2
3
4
5 package types
6
7 import (
8 "go/ast"
9 "go/token"
10 )
11
12
13 func (check *Checker) labels(body *ast.BlockStmt) {
14
15 all := NewScope(nil, body.Pos(), body.End(), "label")
16
17 fwdJumps := check.blockBranches(all, nil, nil, body.List)
18
19
20
21
22
23 for _, jmp := range fwdJumps {
24 var msg string
25 var code errorCode
26 name := jmp.Label.Name
27 if alt := all.Lookup(name); alt != nil {
28 msg = "goto %s jumps into block"
29 alt.(*Label).used = true
30 code = _JumpIntoBlock
31 } else {
32 msg = "label %s not declared"
33 code = _UndeclaredLabel
34 }
35 check.errorf(jmp.Label, code, msg, name)
36 }
37
38
39 for name, obj := range all.elems {
40 obj = resolve(name, obj)
41 if lbl := obj.(*Label); !lbl.used {
42 check.softErrorf(lbl, _UnusedLabel, "label %s declared but not used", lbl.name)
43 }
44 }
45 }
46
47
48 type block struct {
49 parent *block
50 lstmt *ast.LabeledStmt
51 labels map[string]*ast.LabeledStmt
52 }
53
54
55
56 func (b *block) insert(s *ast.LabeledStmt) {
57 name := s.Label.Name
58 if debug {
59 assert(b.gotoTarget(name) == nil)
60 }
61 labels := b.labels
62 if labels == nil {
63 labels = make(map[string]*ast.LabeledStmt)
64 b.labels = labels
65 }
66 labels[name] = s
67 }
68
69
70
71 func (b *block) gotoTarget(name string) *ast.LabeledStmt {
72 for s := b; s != nil; s = s.parent {
73 if t := s.labels[name]; t != nil {
74 return t
75 }
76 }
77 return nil
78 }
79
80
81
82 func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
83 for s := b; s != nil; s = s.parent {
84 if t := s.lstmt; t != nil && t.Label.Name == name {
85 return t
86 }
87 }
88 return nil
89 }
90
91
92
93
94 func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
95 b := &block{parent: parent, lstmt: lstmt}
96
97 var (
98 varDeclPos token.Pos
99 fwdJumps, badJumps []*ast.BranchStmt
100 )
101
102
103
104
105 recordVarDecl := func(pos token.Pos) {
106 varDeclPos = pos
107 badJumps = append(badJumps[:0], fwdJumps...)
108 }
109
110 jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
111 if varDeclPos.IsValid() {
112 for _, bad := range badJumps {
113 if jmp == bad {
114 return true
115 }
116 }
117 }
118 return false
119 }
120
121 blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
122
123
124 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
125 }
126
127 var stmtBranches func(ast.Stmt)
128 stmtBranches = func(s ast.Stmt) {
129 switch s := s.(type) {
130 case *ast.DeclStmt:
131 if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
132 recordVarDecl(d.Pos())
133 }
134
135 case *ast.LabeledStmt:
136
137 if name := s.Label.Name; name != "_" {
138 lbl := NewLabel(s.Label.Pos(), check.pkg, name)
139 if alt := all.Insert(lbl); alt != nil {
140 check.softErrorf(lbl, _DuplicateLabel, "label %s already declared", name)
141 check.reportAltDecl(alt)
142
143 } else {
144 b.insert(s)
145 check.recordDef(s.Label, lbl)
146 }
147
148 i := 0
149 for _, jmp := range fwdJumps {
150 if jmp.Label.Name == name {
151
152 lbl.used = true
153 check.recordUse(jmp.Label, lbl)
154 if jumpsOverVarDecl(jmp) {
155 check.softErrorf(
156 jmp.Label,
157 _JumpOverDecl,
158 "goto %s jumps over variable declaration at line %d",
159 name,
160 check.fset.Position(varDeclPos).Line,
161 )
162
163 }
164 } else {
165
166 fwdJumps[i] = jmp
167 i++
168 }
169 }
170 fwdJumps = fwdJumps[:i]
171 lstmt = s
172 }
173 stmtBranches(s.Stmt)
174
175 case *ast.BranchStmt:
176 if s.Label == nil {
177 return
178 }
179
180
181 name := s.Label.Name
182 switch s.Tok {
183 case token.BREAK:
184
185
186
187 valid := false
188 if t := b.enclosingTarget(name); t != nil {
189 switch t.Stmt.(type) {
190 case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
191 valid = true
192 }
193 }
194 if !valid {
195 check.errorf(s.Label, _MisplacedLabel, "invalid break label %s", name)
196 return
197 }
198
199 case token.CONTINUE:
200
201
202 valid := false
203 if t := b.enclosingTarget(name); t != nil {
204 switch t.Stmt.(type) {
205 case *ast.ForStmt, *ast.RangeStmt:
206 valid = true
207 }
208 }
209 if !valid {
210 check.errorf(s.Label, _MisplacedLabel, "invalid continue label %s", name)
211 return
212 }
213
214 case token.GOTO:
215 if b.gotoTarget(name) == nil {
216
217 fwdJumps = append(fwdJumps, s)
218 return
219 }
220
221 default:
222 check.invalidAST(s, "branch statement: %s %s", s.Tok, name)
223 return
224 }
225
226
227 obj := all.Lookup(name)
228 obj.(*Label).used = true
229 check.recordUse(s.Label, obj)
230
231 case *ast.AssignStmt:
232 if s.Tok == token.DEFINE {
233 recordVarDecl(s.Pos())
234 }
235
236 case *ast.BlockStmt:
237 blockBranches(lstmt, s.List)
238
239 case *ast.IfStmt:
240 stmtBranches(s.Body)
241 if s.Else != nil {
242 stmtBranches(s.Else)
243 }
244
245 case *ast.CaseClause:
246 blockBranches(nil, s.Body)
247
248 case *ast.SwitchStmt:
249 stmtBranches(s.Body)
250
251 case *ast.TypeSwitchStmt:
252 stmtBranches(s.Body)
253
254 case *ast.CommClause:
255 blockBranches(nil, s.Body)
256
257 case *ast.SelectStmt:
258 stmtBranches(s.Body)
259
260 case *ast.ForStmt:
261 stmtBranches(s.Body)
262
263 case *ast.RangeStmt:
264 stmtBranches(s.Body)
265 }
266 }
267
268 for _, s := range list {
269 stmtBranches(s)
270 }
271
272 return fwdJumps
273 }
274
View as plain text