1#![allow(dead_code)]
13use crate::SourceRead;
14
15use super::super::objects::ScopeKey;
16use super::super::scope::Scope;
17use super::check::Checker;
18use go_parser::ast::{BlockStmt, BranchStmt, Decl, Node, Stmt};
19use go_parser::{AstObjects, LabeledStmtKey, Map, Pos, Token};
20use std::cell::RefCell;
21use std::rc::Rc;
22
23#[derive(Debug)]
24struct Block {
25 parent: Option<Rc<RefCell<Block>>>, lstmt: Option<LabeledStmtKey>, labels: Map<String, LabeledStmtKey>,
28}
29
30impl Block {
31 fn new(parent: Option<Rc<RefCell<Block>>>, stmt: Option<LabeledStmtKey>) -> Block {
32 Block {
33 parent: parent,
34 lstmt: stmt,
35 labels: Map::new(),
36 }
37 }
38
39 fn insert(&mut self, s: LabeledStmtKey, objs: &AstObjects) {
42 let name = objs.idents[objs.l_stmts[s].label].name.clone();
43 debug_assert!(self.goto_target(&name).is_none());
44 self.labels.insert(name, s);
45 }
46
47 fn search(
48 &self,
49 name: &str,
50 objs: Option<&AstObjects>,
51 f: Box<dyn Fn(&Block, &str, Option<&AstObjects>) -> Option<LabeledStmtKey>>,
52 ) -> Option<LabeledStmtKey> {
53 if let Some(l) = f(self, name, objs) {
54 return Some(l);
55 }
56 let mut block_op = self.parent.clone();
57 loop {
58 if let Some(b) = block_op {
59 if let Some(l) = f(&b.borrow(), name, objs) {
60 return Some(l);
61 }
62 block_op = b.borrow().parent.clone();
63 } else {
64 return None;
65 }
66 }
67 }
68
69 fn goto_target(&self, name: &str) -> Option<LabeledStmtKey> {
72 let f = |b: &Block, n: &str, _: Option<&AstObjects>| b.labels.get(n).map(|x| *x);
73 self.search(name, None, Box::new(f))
74 }
75
76 fn enclosing_target(&self, name: &str, objs: &AstObjects) -> Option<LabeledStmtKey> {
79 let f = |b: &Block, n: &str, o: Option<&AstObjects>| {
80 let objs = o.unwrap();
81 if let Some(s) = b.lstmt {
82 if &objs.idents[objs.l_stmts[s].label].name == n {
83 return Some(s);
84 }
85 }
86 None
87 };
88 self.search(name, Some(objs), Box::new(f))
89 }
90}
91
92struct StmtBranchesContext {
93 var_decl_pos: Option<Pos>,
94 fwd_jumps: Vec<Rc<BranchStmt>>,
95 bad_jumps: Vec<Rc<BranchStmt>>,
96 lstmt: Option<LabeledStmtKey>,
97}
98
99impl StmtBranchesContext {
100 fn new(lstmt: Option<LabeledStmtKey>) -> StmtBranchesContext {
101 StmtBranchesContext {
102 var_decl_pos: None,
103 fwd_jumps: vec![],
104 bad_jumps: vec![],
105 lstmt: lstmt,
106 }
107 }
108
109 fn record_var_decl(&mut self, p: Pos) {
113 self.var_decl_pos = Some(p);
114 self.bad_jumps = self.fwd_jumps.clone();
115 }
116
117 fn jumps_over_var_decl(&self, bs: &Rc<BranchStmt>) -> bool {
118 if let Some(_) = self.var_decl_pos {
119 self.bad_jumps.iter().find(|&x| Rc::ptr_eq(x, bs)).is_some()
120 } else {
121 false
122 }
123 }
124}
125
126impl<'a, S: SourceRead> Checker<'a, S> {
127 pub fn labels(&mut self, body: &Rc<BlockStmt>) {
128 let (pos, end) = (body.pos(), body.end());
129 let comment = "label".to_owned();
130 let all = self.tc_objs.new_scope(None, pos, end, comment, false);
131 let fwd_jumps = self.block_branches(all, None, None, &body.list);
132
133 let scope = &self.tc_objs.scopes[all];
138 for jump in fwd_jumps.iter() {
139 let ident = self.ast_ident(jump.label.unwrap());
140 let (pos, name) = (ident.pos, ident.name.clone());
141 let msg = if let Some(alt) = scope.lookup(&name) {
142 self.tc_objs.lobjs[*alt]
143 .entity_type_mut()
144 .label_set_used(true); format!("goto {} jumps into block", name)
146 } else {
147 format!("label {} not declared", name)
148 };
149 self.error(pos, msg);
150 }
151
152 for (_, okey) in scope.elems().iter() {
154 let lobj = self.lobj(*okey);
155 if !lobj.entity_type().label_used() {
156 self.soft_error(
157 lobj.pos(),
158 format!("label {} declared but not used", lobj.name()),
159 );
160 }
161 }
162 }
163
164 fn block_branches(
168 &mut self,
169 all: ScopeKey,
170 parent: Option<Rc<RefCell<Block>>>,
171 lstmt: Option<LabeledStmtKey>,
172 list: &Vec<Stmt>,
173 ) -> Vec<Rc<BranchStmt>> {
174 let b = Rc::new(RefCell::new(Block::new(parent, lstmt)));
175 let mut ctx = StmtBranchesContext::new(lstmt);
176
177 for s in list.iter() {
178 self.stmt_branches(all, b.clone(), s, &mut ctx);
179 }
180
181 ctx.fwd_jumps
182 }
183
184 fn stmt_branches(
185 &mut self,
186 all: ScopeKey,
187 block: Rc<RefCell<Block>>,
188 stmt: &Stmt,
189 ctx: &mut StmtBranchesContext,
190 ) {
191 let mut block_branches = |lstmt: Option<LabeledStmtKey>,
192 list: &Vec<Stmt>,
193 ctx: &mut StmtBranchesContext| {
194 ctx.fwd_jumps
197 .append(&mut self.block_branches(all, Some(block.clone()), lstmt, list));
198 };
199
200 match stmt {
201 Stmt::Decl(d) => match &**d {
202 Decl::Gen(gd) => {
203 if gd.token == Token::VAR {
204 ctx.record_var_decl(gd.token_pos)
205 }
206 }
207 _ => {}
208 },
209 Stmt::Labeled(lkey) => {
210 let ls = &self.ast_objs.l_stmts[*lkey];
211 let lable_stmt = ls.stmt.clone();
212 let label = &self.ast_objs.idents[ls.label];
213 let name = label.name.clone();
214 if name != "_" {
215 let lb = self
216 .tc_objs
217 .new_label(label.pos, Some(self.pkg), name.to_string());
218 if let Some(alt) = Scope::insert(all, lb, self.tc_objs) {
219 self.soft_error(label.pos, format!("label {} already declared", name));
221 self.report_alt_decl(alt);
222 } else {
223 block.borrow_mut().insert(*lkey, self.ast_objs);
224 self.result.record_def(ls.label, Some(lb));
225 }
226 ctx.fwd_jumps = ctx
228 .fwd_jumps
229 .iter()
230 .filter(|&x| {
231 let ikey = x.label.unwrap();
232 let ident = &self.ast_objs.idents[ikey];
233 let found = ident.name == name;
234 if found {
235 self.tc_objs.lobjs[lb]
236 .entity_type_mut()
237 .label_set_used(true);
238 self.result.record_use(ikey, lb);
239 if ctx.jumps_over_var_decl(x) {
240 self.soft_error(
241 ident.pos,
242 format!(
243 "goto {} jumps over variable declaration at line {}",
244 name,
245 self.position(ctx.var_decl_pos.unwrap()).line
246 ),
247 )
248 }
249 }
250 !found
251 })
252 .map(|x| x.clone())
253 .collect();
254
255 ctx.lstmt = Some(*lkey);
256 }
257 self.stmt_branches(all, block, &lable_stmt, ctx);
258 }
259 Stmt::Branch(bs) => {
260 if let Some(label) = bs.label {
261 let ident = &self.ast_ident(label);
262 let name = &ident.name;
263 match &bs.token {
265 Token::BREAK => {
266 let valid = match block.borrow().enclosing_target(name, self.ast_objs) {
270 Some(t) => match &self.ast_objs.l_stmts[t].stmt {
271 Stmt::Switch(_)
272 | Stmt::TypeSwitch(_)
273 | Stmt::Select(_)
274 | Stmt::For(_)
275 | Stmt::Range(_) => true,
276 _ => false,
277 },
278 None => false,
279 };
280 if !valid {
281 self.error(ident.pos, format!("invalid break label {}", name));
282 return;
283 }
284 }
285 Token::CONTINUE => {
286 let valid = match block.borrow().enclosing_target(name, self.ast_objs) {
287 Some(t) => match &self.ast_objs.l_stmts[t].stmt {
288 Stmt::For(_) | Stmt::Range(_) => true,
289 _ => false,
290 },
291 None => false,
292 };
293 if !valid {
294 self.error(ident.pos, format!("invalid continue label {}", name));
295 return;
296 }
297 }
298 Token::GOTO => {
299 if block.borrow().goto_target(name).is_none() {
300 ctx.fwd_jumps.push(bs.clone());
301 return;
302 }
303 }
304 _ => {
305 self.invalid_ast(
306 stmt.pos(self.ast_objs),
307 &format!("branch statement: {} {}", &bs.token, name),
308 );
309 return;
310 }
311 }
312
313 let scope = &self.tc_objs.scopes[all];
315 let lobj = *scope.lookup(name).unwrap();
316 self.tc_objs.lobjs[lobj]
317 .entity_type_mut()
318 .label_set_used(true);
319 self.result.record_use(label, lobj);
320 }
321 }
322 Stmt::Assign(akey) => {
323 let astmt = &self.ast_objs.a_stmts[*akey];
324 if astmt.token == Token::DEFINE {
325 ctx.record_var_decl(astmt.pos(self.ast_objs));
326 }
327 }
328 Stmt::Block(bs) => {
329 block_branches(ctx.lstmt, &bs.list, ctx);
330 }
331 Stmt::If(ifstmt) => {
332 let body = Stmt::Block(ifstmt.body.clone());
333 self.stmt_branches(all, block.clone(), &body, ctx);
334 if let Some(els) = &ifstmt.els {
335 self.stmt_branches(all, block, els, ctx);
336 }
337 }
338 Stmt::Case(cc) => {
339 block_branches(None, &cc.body, ctx);
340 }
341 Stmt::Switch(ss) => {
342 let body = Stmt::Block(ss.body.clone());
343 self.stmt_branches(all, block, &body, ctx);
344 }
345 Stmt::TypeSwitch(tss) => {
346 let body = Stmt::Block(tss.body.clone());
347 self.stmt_branches(all, block, &body, ctx);
348 }
349 Stmt::Comm(cc) => {
350 block_branches(None, &cc.body, ctx);
351 }
352 Stmt::Select(ss) => {
353 let body = Stmt::Block(ss.body.clone());
354 self.stmt_branches(all, block, &body, ctx);
355 }
356 Stmt::For(fs) => {
357 let body = Stmt::Block(fs.body.clone());
358 self.stmt_branches(all, block, &body, ctx);
359 }
360 Stmt::Range(rs) => {
361 let body = Stmt::Block(rs.body.clone());
362 self.stmt_branches(all, block, &body, ctx);
363 }
364 _ => {}
365 }
366 }
367}