1
2
3
4
5 package syntax
6
7 import "fmt"
8
9
10
11
12
13
14
15
16
17
18 func checkBranches(body *BlockStmt, errh ErrorHandler) {
19 if body == nil {
20 return
21 }
22
23
24 ls := &labelScope{errh: errh}
25 fwdGotos := ls.blockBranches(nil, targets{}, nil, body.Pos(), body.List)
26
27
28
29
30 for _, fwd := range fwdGotos {
31 name := fwd.Label.Value
32 if l := ls.labels[name]; l != nil {
33 l.used = true
34 ls.err(fwd.Label.Pos(), "goto %s jumps into block starting at %s", name, l.parent.start)
35 } else {
36 ls.err(fwd.Label.Pos(), "label %s not defined", name)
37 }
38 }
39
40
41 for _, l := range ls.labels {
42 if !l.used {
43 l := l.lstmt.Label
44 ls.err(l.Pos(), "label %s defined and not used", l.Value)
45 }
46 }
47 }
48
49 type labelScope struct {
50 errh ErrorHandler
51 labels map[string]*label
52 }
53
54 type label struct {
55 parent *block
56 lstmt *LabeledStmt
57 used bool
58 }
59
60 type block struct {
61 parent *block
62 start Pos
63 lstmt *LabeledStmt
64 }
65
66 func (ls *labelScope) err(pos Pos, format string, args ...interface{}) {
67 ls.errh(Error{pos, fmt.Sprintf(format, args...)})
68 }
69
70
71
72
73 func (ls *labelScope) declare(b *block, s *LabeledStmt) *label {
74 name := s.Label.Value
75 labels := ls.labels
76 if labels == nil {
77 labels = make(map[string]*label)
78 ls.labels = labels
79 } else if alt := labels[name]; alt != nil {
80 ls.err(s.Label.Pos(), "label %s already defined at %s", name, alt.lstmt.Label.Pos().String())
81 return alt
82 }
83 l := &label{b, s, false}
84 labels[name] = l
85 return l
86 }
87
88
89
90
91 func (ls *labelScope) gotoTarget(b *block, name string) *LabeledStmt {
92 if l := ls.labels[name]; l != nil {
93 l.used = true
94 for ; b != nil; b = b.parent {
95 if l.parent == b {
96 return l.lstmt
97 }
98 }
99 }
100 return nil
101 }
102
103 var invalid = new(LabeledStmt)
104
105
106
107
108 func (ls *labelScope) enclosingTarget(b *block, name string) *LabeledStmt {
109 if l := ls.labels[name]; l != nil {
110 l.used = true
111 for ; b != nil; b = b.parent {
112 if l.lstmt == b.lstmt {
113 return l.lstmt
114 }
115 }
116 return invalid
117 }
118 return nil
119 }
120
121
122
123 type targets struct {
124 breaks Stmt
125 continues *ForStmt
126 }
127
128
129
130
131
132 func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledStmt, start Pos, body []Stmt) []*BranchStmt {
133 b := &block{parent: parent, start: start, lstmt: lstmt}
134
135 var varPos Pos
136 var varName Expr
137 var fwdGotos, badGotos []*BranchStmt
138
139 recordVarDecl := func(pos Pos, name Expr) {
140 varPos = pos
141 varName = name
142
143
144
145
146 badGotos = append(badGotos[:0], fwdGotos...)
147 }
148
149 jumpsOverVarDecl := func(fwd *BranchStmt) bool {
150 if varPos.IsKnown() {
151 for _, bad := range badGotos {
152 if fwd == bad {
153 return true
154 }
155 }
156 }
157 return false
158 }
159
160 innerBlock := func(ctxt targets, start Pos, body []Stmt) {
161
162
163 fwdGotos = append(fwdGotos, ls.blockBranches(b, ctxt, lstmt, start, body)...)
164 }
165
166 for _, stmt := range body {
167 lstmt = nil
168 L:
169 switch s := stmt.(type) {
170 case *DeclStmt:
171 for _, d := range s.DeclList {
172 if v, ok := d.(*VarDecl); ok {
173 recordVarDecl(v.Pos(), v.NameList[0])
174 break
175 }
176 }
177
178 case *LabeledStmt:
179
180 if name := s.Label.Value; name != "_" {
181 l := ls.declare(b, s)
182
183 i := 0
184 for _, fwd := range fwdGotos {
185 if fwd.Label.Value == name {
186 fwd.Target = s
187 l.used = true
188 if jumpsOverVarDecl(fwd) {
189 ls.err(
190 fwd.Label.Pos(),
191 "goto %s jumps over declaration of %s at %s",
192 name, String(varName), varPos,
193 )
194 }
195 } else {
196
197 fwdGotos[i] = fwd
198 i++
199 }
200 }
201 fwdGotos = fwdGotos[:i]
202 lstmt = s
203 }
204
205 stmt = s.Stmt
206 goto L
207
208 case *BranchStmt:
209
210 if s.Label == nil {
211 switch s.Tok {
212 case _Break:
213 if t := ctxt.breaks; t != nil {
214 s.Target = t
215 } else {
216 ls.err(s.Pos(), "break is not in a loop, switch, or select")
217 }
218 case _Continue:
219 if t := ctxt.continues; t != nil {
220 s.Target = t
221 } else {
222 ls.err(s.Pos(), "continue is not in a loop")
223 }
224 case _Fallthrough:
225
226 case _Goto:
227 fallthrough
228 default:
229 panic("invalid BranchStmt")
230 }
231 break
232 }
233
234
235 name := s.Label.Value
236 switch s.Tok {
237 case _Break:
238
239
240
241 if t := ls.enclosingTarget(b, name); t != nil {
242 switch t := t.Stmt.(type) {
243 case *SwitchStmt, *SelectStmt, *ForStmt:
244 s.Target = t
245 default:
246 ls.err(s.Label.Pos(), "invalid break label %s", name)
247 }
248 } else {
249 ls.err(s.Label.Pos(), "break label not defined: %s", name)
250 }
251
252 case _Continue:
253
254
255 if t := ls.enclosingTarget(b, name); t != nil {
256 if t, ok := t.Stmt.(*ForStmt); ok {
257 s.Target = t
258 } else {
259 ls.err(s.Label.Pos(), "invalid continue label %s", name)
260 }
261 } else {
262 ls.err(s.Label.Pos(), "continue label not defined: %s", name)
263 }
264
265 case _Goto:
266 if t := ls.gotoTarget(b, name); t != nil {
267 s.Target = t
268 } else {
269
270 fwdGotos = append(fwdGotos, s)
271 }
272
273 case _Fallthrough:
274 fallthrough
275 default:
276 panic("invalid BranchStmt")
277 }
278
279 case *AssignStmt:
280 if s.Op == Def {
281 recordVarDecl(s.Pos(), s.Lhs)
282 }
283
284 case *BlockStmt:
285 innerBlock(ctxt, s.Pos(), s.List)
286
287 case *IfStmt:
288 innerBlock(ctxt, s.Then.Pos(), s.Then.List)
289 if s.Else != nil {
290 innerBlock(ctxt, s.Else.Pos(), []Stmt{s.Else})
291 }
292
293 case *ForStmt:
294 innerBlock(targets{s, s}, s.Body.Pos(), s.Body.List)
295
296 case *SwitchStmt:
297 inner := targets{s, ctxt.continues}
298 for _, cc := range s.Body {
299 innerBlock(inner, cc.Pos(), cc.Body)
300 }
301
302 case *SelectStmt:
303 inner := targets{s, ctxt.continues}
304 for _, cc := range s.Body {
305 innerBlock(inner, cc.Pos(), cc.Body)
306 }
307 }
308 }
309
310 return fwdGotos
311 }
312
View as plain text