Source file
src/go/types/labels.go
1
2
3
4
5 package types
6
7 import (
8 "go/ast"
9 "go/token"
10 . "internal/types/errors"
11 )
12
13
14 func (check *Checker) labels(body *ast.BlockStmt) {
15
16 all := NewScope(nil, body.Pos(), body.End(), "label")
17
18 fwdJumps := check.blockBranches(all, nil, nil, body.List)
19
20
21
22
23
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
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
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
49 type block struct {
50 parent *block
51 lstmt *ast.LabeledStmt
52 labels map[string]*ast.LabeledStmt
53 }
54
55
56
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
71
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
82
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
93
94
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
104
105
106 recordVarDecl := func(pos token.Pos) {
107 varDeclPos = pos
108 badJumps = append(badJumps[:0], fwdJumps...)
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
124
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
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
144 } else {
145 b.insert(s)
146 check.recordDef(s.Label, lbl)
147 }
148
149 i := 0
150 for _, jmp := range fwdJumps {
151 if jmp.Label.Name == name {
152
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
164 }
165 } else {
166
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
179 }
180
181
182 name := s.Label.Name
183 switch s.Tok {
184 case token.BREAK:
185
186
187
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
202
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
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
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 }
275
View as plain text