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