Release coccinelle-0.2.0rc1
[bpt/coccinelle.git] / parsing_cocci / check_meta.ml
1 (* For minus fragment, checks that all of the identifier metavariables that
2 are used are not declared as fresh, and check that all declared variables
3 are used. For plus fragment, just check that the variables declared as
4 fresh are used. What is the issue about error variables? (don't remember) *)
5
6 module Ast0 = Ast0_cocci
7 module Ast = Ast_cocci
8 module V0 = Visitor_ast0
9 module VT0 = Visitor_ast0_types
10
11 (* all fresh identifiers *)
12 let fresh_table = (Hashtbl.create(50) : ((string * string), unit) Hashtbl.t)
13
14 let warning s = Printf.fprintf stderr "warning: %s\n" s
15
16 let promote name = (name,(),Ast0.default_info(),(),None,-1)
17
18 (* --------------------------------------------------------------------- *)
19
20 let find_loop table name =
21 let rec loop = function
22 [] -> raise Not_found
23 | x::xs -> (try Hashtbl.find x name with Not_found -> loop xs) in
24 loop table
25
26 let check_table table minus (name,_,info,_,_,_) =
27 let rl = info.Ast0.pos_info.Ast0.line_start in
28 if minus
29 then
30 (try (find_loop table name) := true
31 with
32 Not_found ->
33 (try
34 Hashtbl.find fresh_table name;
35 let (_,name) = name in
36 failwith
37 (Printf.sprintf
38 "%d: unexpected use of a fresh identifier %s" rl name)
39 with Not_found -> ()))
40 else (try (find_loop table name) := true with Not_found -> ())
41
42 let get_opt fn = Common.do_option fn
43
44 (* --------------------------------------------------------------------- *)
45 (* Dots *)
46
47 let dots fn d =
48 match Ast0.unwrap d with
49 Ast0.DOTS(x) -> List.iter fn x
50 | Ast0.CIRCLES(x) -> List.iter fn x
51 | Ast0.STARS(x) -> List.iter fn x
52
53 (* --------------------------------------------------------------------- *)
54 (* Identifier *)
55
56 type context = ID | FIELD | FN | GLOBAL
57
58 (* heuristic for distinguishing ifdef variables from undeclared metavariables*)
59 let is_ifdef name =
60 String.length name > 2 && String.uppercase name = name
61
62 let ident context old_metas table minus i =
63 match Ast0.unwrap i with
64 Ast0.Id((name,_,info,_,_,_) : string Ast0.mcode) ->
65 let rl = info.Ast0.pos_info.Ast0.line_start in
66 let is_plus i =
67 match Ast0.get_mcodekind i with Ast0.PLUS _ -> true | _ -> false in
68 let err =
69 if List.exists (function x -> x = name) old_metas
70 && (minus || is_plus i)
71 then
72 begin
73 warning
74 (Printf.sprintf
75 "line %d: %s, previously declared as a metavariable, is used as an identifier" rl name);
76 true
77 end
78 else false in
79 (match context with
80 ID ->
81 if not (is_ifdef name) && minus && not err(* warn only once per id *)
82 then
83 warning
84 (Printf.sprintf "line %d: should %s be a metavariable?" rl name)
85 | _ -> ())
86 | Ast0.MetaId(name,_,_) -> check_table table minus name
87 | Ast0.MetaFunc(name,_,_) -> check_table table minus name
88 | Ast0.MetaLocalFunc(name,_,_) -> check_table table minus name
89 | Ast0.OptIdent(_) | Ast0.UniqueIdent(_) ->
90 failwith "unexpected code"
91
92 (* --------------------------------------------------------------------- *)
93 (* Expression *)
94
95 let rec expression context old_metas table minus e =
96 match Ast0.unwrap e with
97 Ast0.Ident(id) ->
98 ident context old_metas table minus id
99 | Ast0.FunCall(fn,lp,args,rp) ->
100 expression FN old_metas table minus fn;
101 dots (expression ID old_metas table minus) args
102 | Ast0.Assignment(left,op,right,_) ->
103 expression context old_metas table minus left;
104 expression ID old_metas table minus right
105 | Ast0.CondExpr(exp1,why,exp2,colon,exp3) ->
106 expression ID old_metas table minus exp1;
107 get_opt (expression ID old_metas table minus) exp2;
108 expression ID old_metas table minus exp3
109 | Ast0.Postfix(exp,op) ->
110 expression ID old_metas table minus exp
111 | Ast0.Infix(exp,op) ->
112 expression ID old_metas table minus exp
113 | Ast0.Unary(exp,op) ->
114 expression ID old_metas table minus exp
115 | Ast0.Binary(left,op,right) ->
116 expression ID old_metas table minus left;
117 expression ID old_metas table minus right
118 | Ast0.Paren(lp,exp,rp) ->
119 expression ID old_metas table minus exp
120 | Ast0.ArrayAccess(exp1,lb,exp2,rb) ->
121 expression ID old_metas table minus exp1;
122 expression ID old_metas table minus exp2
123 | Ast0.RecordAccess(exp,pt,field) ->
124 expression ID old_metas table minus exp;
125 ident FIELD old_metas table minus field
126 | Ast0.RecordPtAccess(exp,ar,field) ->
127 expression ID old_metas table minus exp;
128 ident FIELD old_metas table minus field
129 | Ast0.Cast(lp,ty,rp,exp) ->
130 typeC old_metas table minus ty; expression ID old_metas table minus exp
131 | Ast0.SizeOfExpr(szf,exp) -> expression ID old_metas table minus exp
132 | Ast0.SizeOfType(szf,lp,ty,rp) -> typeC old_metas table minus ty
133 | Ast0.TypeExp(ty) -> typeC old_metas table minus ty
134 | Ast0.MetaExpr(name,_,Some tys,_,_) ->
135 List.iter
136 (function x ->
137 match get_type_name x with
138 Some(ty) -> check_table table minus (promote ty)
139 | None -> ())
140 tys;
141 check_table table minus name
142 | Ast0.MetaExpr(name,_,_,_,_) | Ast0.MetaErr(name,_,_) ->
143 check_table table minus name
144 | Ast0.MetaExprList(name,None,_) ->
145 check_table table minus name
146 | Ast0.MetaExprList(name,Some lenname,_) ->
147 check_table table minus name;
148 check_table table minus lenname
149 | Ast0.DisjExpr(_,exps,_,_) ->
150 List.iter (expression context old_metas table minus) exps
151 | Ast0.NestExpr(_,exp_dots,_,w,_) ->
152 dots (expression ID old_metas table minus) exp_dots;
153 get_opt (expression ID old_metas table minus) w
154 | Ast0.Edots(_,Some x) | Ast0.Ecircles(_,Some x) | Ast0.Estars(_,Some x) ->
155 expression ID old_metas table minus x
156 | _ -> () (* no metavariable subterms *)
157
158 and get_type_name = function
159 Type_cocci.ConstVol(_,ty) | Type_cocci.SignedT(_,Some ty)
160 | Type_cocci.Pointer(ty)
161 | Type_cocci.FunctionPointer(ty) | Type_cocci.Array(ty) -> get_type_name ty
162 | Type_cocci.MetaType(nm,_,_) -> Some nm
163 | _ -> None
164
165 (* --------------------------------------------------------------------- *)
166 (* Types *)
167
168 and typeC old_metas table minus t =
169 match Ast0.unwrap t with
170 Ast0.ConstVol(cv,ty) -> typeC old_metas table minus ty
171 | Ast0.Signed(sgn,ty) ->
172 get_opt (typeC old_metas table minus) ty
173 | Ast0.Pointer(ty,star) -> typeC old_metas table minus ty
174 | Ast0.FunctionPointer(ty,lp1,star,rp1,lp2,params,rp2) ->
175 typeC old_metas table minus ty;
176 parameter_list old_metas table minus params
177 | Ast0.FunctionType(ty,lp1,params,rp1) ->
178 get_opt (typeC old_metas table minus) ty;
179 parameter_list old_metas table minus params
180 | Ast0.Array(ty,lb,size,rb) ->
181 typeC old_metas table minus ty;
182 get_opt (expression ID old_metas table minus) size
183 | Ast0.MetaType(name,_) ->
184 check_table table minus name
185 | Ast0.DisjType(_,types,_,_) ->
186 List.iter (typeC old_metas table minus) types
187 | Ast0.EnumName(en,id) -> ident GLOBAL old_metas table minus id
188 | Ast0.StructUnionName(su,Some id) -> ident GLOBAL old_metas table minus id
189 | Ast0.StructUnionDef(ty,lb,decls,rb) ->
190 typeC old_metas table minus ty;
191 dots (declaration GLOBAL old_metas table minus) decls
192 | Ast0.OptType(ty) | Ast0.UniqueType(ty) ->
193 failwith "unexpected code"
194 | _ -> () (* no metavariable subterms *)
195
196 (* --------------------------------------------------------------------- *)
197 (* Variable declaration *)
198 (* Even if the Cocci program specifies a list of declarations, they are
199 split out into multiple declarations of a single variable each. *)
200
201 and declaration context old_metas table minus d =
202 match Ast0.unwrap d with
203 Ast0.Init(stg,ty,id,eq,ini,sem) ->
204 (match Ast0.unwrap ini with
205 Ast0.InitExpr exp ->
206 typeC old_metas table minus ty;
207 ident context old_metas table minus id;
208 expression ID old_metas table minus exp
209 | _ ->
210 (*
211 if minus
212 then
213 failwith "complex initializer specification not allowed in - code"
214 else*)
215 (typeC old_metas table minus ty;
216 ident context old_metas table minus id;
217 initialiser old_metas table minus ini))
218 | Ast0.UnInit(stg,ty,id,sem) ->
219 typeC old_metas table minus ty; ident context old_metas table minus id
220 | Ast0.MacroDecl(name,lp,args,rp,sem) ->
221 ident GLOBAL old_metas table minus name;
222 dots (expression ID old_metas table minus) args
223 | Ast0.TyDecl(ty,sem) -> typeC old_metas table minus ty
224 | Ast0.Typedef(stg,ty,id,sem) ->
225 typeC old_metas table minus ty;
226 typeC old_metas table minus id
227 | Ast0.DisjDecl(_,decls,_,_) ->
228 List.iter (declaration ID old_metas table minus) decls
229 | Ast0.Ddots(_,Some x) -> declaration ID old_metas table minus x
230 | Ast0.Ddots(_,None) -> ()
231 | Ast0.OptDecl(_) | Ast0.UniqueDecl(_) ->
232 failwith "unexpected code"
233
234 (* --------------------------------------------------------------------- *)
235 (* Initialiser *)
236
237 and initialiser old_metas table minus ini =
238 match Ast0.unwrap ini with
239 Ast0.MetaInit(name,_) ->
240 check_table table minus name
241 | Ast0.InitExpr(exp) -> expression ID old_metas table minus exp
242 | Ast0.InitList(lb,initlist,rb) ->
243 dots (initialiser old_metas table minus) initlist
244 | Ast0.InitGccExt(designators,eq,ini) ->
245 List.iter (designator old_metas table minus) designators;
246 initialiser old_metas table minus ini
247 | Ast0.InitGccName(name,eq,ini) ->
248 ident FIELD old_metas table minus name;
249 initialiser old_metas table minus ini
250 | Ast0.Idots(_,Some x) -> initialiser old_metas table minus x
251 | Ast0.OptIni(_) | Ast0.UniqueIni(_) ->
252 failwith "unexpected code"
253 | _ -> () (* no metavariable subterms *)
254
255 and designator old_metas table minus = function
256 Ast0.DesignatorField(dot,id) ->
257 ident FIELD old_metas table minus id
258 | Ast0.DesignatorIndex(lb,exp,rb) ->
259 expression ID old_metas table minus exp
260 | Ast0.DesignatorRange(lb,min,dots,max,rb) ->
261 expression ID old_metas table minus min;
262 expression ID old_metas table minus max
263
264 and initialiser_list old_metas table minus =
265 dots (initialiser old_metas table minus)
266
267 (* --------------------------------------------------------------------- *)
268 (* Parameter *)
269
270 and parameterTypeDef old_metas table minus param =
271 match Ast0.unwrap param with
272 Ast0.Param(ty,id) ->
273 get_opt (ident ID old_metas table minus) id;
274 typeC old_metas table minus ty
275 | Ast0.MetaParam(name,_) ->
276 check_table table minus name
277 | Ast0.MetaParamList(name,None,_) ->
278 check_table table minus name
279 | Ast0.MetaParamList(name,Some lenname,_) ->
280 check_table table minus name;
281 check_table table minus lenname
282 | _ -> () (* no metavariable subterms *)
283
284 and parameter_list old_metas table minus =
285 dots (parameterTypeDef old_metas table minus)
286
287 (* --------------------------------------------------------------------- *)
288 (* Top-level code *)
289
290 and statement old_metas table minus s =
291 match Ast0.unwrap s with
292 Ast0.Decl(_,decl) -> declaration ID old_metas table minus decl
293 | Ast0.Seq(lbrace,body,rbrace) -> dots (statement old_metas table minus) body
294 | Ast0.ExprStatement(exp,sem) -> expression ID old_metas table minus exp
295 | Ast0.IfThen(iff,lp,exp,rp,branch,_) ->
296 expression ID old_metas table minus exp;
297 statement old_metas table minus branch
298 | Ast0.IfThenElse(iff,lp,exp,rp,branch1,els,branch2,_) ->
299 expression ID old_metas table minus exp;
300 statement old_metas table minus branch1;
301 statement old_metas table minus branch2
302 | Ast0.While(wh,lp,exp,rp,body,_) ->
303 expression ID old_metas table minus exp;
304 statement old_metas table minus body
305 | Ast0.Do(d,body,wh,lp,exp,rp,sem) ->
306 statement old_metas table minus body;
307 expression ID old_metas table minus exp
308 | Ast0.For(fr,lp,exp1,sem1,exp2,sem2,exp3,rp,body,_) ->
309 get_opt (expression ID old_metas table minus) exp1;
310 get_opt (expression ID old_metas table minus) exp2;
311 get_opt (expression ID old_metas table minus) exp3;
312 statement old_metas table minus body
313 | Ast0.Iterator(nm,lp,args,rp,body,_) ->
314 ident GLOBAL old_metas table minus nm;
315 dots (expression ID old_metas table minus) args;
316 statement old_metas table minus body
317 | Ast0.Switch(switch,lp,exp,rp,lb,decls,cases,rb) ->
318 expression ID old_metas table minus exp;
319 dots (statement old_metas table minus) decls;
320 dots (case_line old_metas table minus) cases
321 | Ast0.ReturnExpr(ret,exp,sem) -> expression ID old_metas table minus exp
322 | Ast0.MetaStmt(name,_) -> check_table table minus name
323 | Ast0.MetaStmtList(name,_) -> check_table table minus name
324 | Ast0.Exp(exp) -> expression ID old_metas table minus exp
325 | Ast0.TopExp(exp) -> expression ID old_metas table minus exp
326 | Ast0.Ty(ty) -> typeC old_metas table minus ty
327 | Ast0.TopInit(init) -> initialiser old_metas table minus init
328 | Ast0.Disj(_,rule_elem_dots_list,_,_) ->
329 List.iter (dots (statement old_metas table minus)) rule_elem_dots_list
330 | Ast0.Nest(_,rule_elem_dots,_,w,_) ->
331 dots (statement old_metas table minus) rule_elem_dots;
332 List.iter (whencode (dots (statement old_metas table minus))
333 (statement old_metas table minus)
334 (expression ID old_metas table minus))
335 w
336 | Ast0.Dots(_,x) | Ast0.Circles(_,x) | Ast0.Stars(_,x) ->
337 List.iter
338 (whencode (dots (statement old_metas table minus))
339 (statement old_metas table minus)
340 (expression ID old_metas table minus)) x
341 | Ast0.FunDecl(_,fi,name,lp,params,rp,lbrace,body,rbrace) ->
342 ident FN old_metas table minus name;
343 List.iter (fninfo old_metas table minus) fi;
344 parameter_list old_metas table minus params;
345 dots (statement old_metas table minus) body
346 | Ast0.Include(inc,s) -> () (* no metavariables possible *)
347 | Ast0.Define(def,id,params,body) ->
348 ident GLOBAL old_metas table minus id;
349 define_parameters old_metas table minus params;
350 dots (statement old_metas table minus) body
351 | Ast0.Label(i,_) -> ident ID old_metas table minus i
352 | Ast0.Goto(_,i,_) -> ident ID old_metas table minus i
353 | _ -> () (* no metavariable subterms *)
354
355 and define_param old_metas table minus p =
356 match Ast0.unwrap p with
357 Ast0.DParam(id) -> ident GLOBAL old_metas table minus id
358 | Ast0.DPComma(_) | Ast0.DPdots(_) | Ast0.DPcircles(_) ->
359 () (* no metavariable subterms *)
360 | Ast0.OptDParam(dp) -> define_param old_metas table minus dp
361 | Ast0.UniqueDParam(dp) -> define_param old_metas table minus dp
362
363 and define_parameters old_metas table minus x =
364 match Ast0.unwrap x with
365 Ast0.NoParams -> ()
366 | Ast0.DParams(lp,dp,rp) -> dots (define_param old_metas table minus) dp
367
368 and fninfo old_metas table minus = function
369 Ast0.FStorage(stg) -> ()
370 | Ast0.FType(ty) -> typeC old_metas table minus ty
371 | Ast0.FInline(inline) -> ()
372 | Ast0.FAttr(attr) -> ()
373
374 and whencode notfn alwaysfn expression = function
375 Ast0.WhenNot a -> notfn a
376 | Ast0.WhenAlways a -> alwaysfn a
377 | Ast0.WhenModifier(_) -> ()
378 | Ast0.WhenNotTrue a -> expression a
379 | Ast0.WhenNotFalse a -> expression a
380
381 and case_line old_metas table minus c =
382 match Ast0.unwrap c with
383 Ast0.Default(def,colon,code) ->
384 dots (statement old_metas table minus) code
385 | Ast0.Case(case,exp,colon,code) ->
386 dots (statement old_metas table minus) code
387 | Ast0.DisjCase(_,case_lines,_,_) ->
388 List.iter (case_line old_metas table minus) case_lines
389 | Ast0.OptCase(case) -> failwith "unexpected code"
390
391 (* --------------------------------------------------------------------- *)
392 (* Rules *)
393
394 let top_level old_metas table minus t =
395 match Ast0.unwrap t with
396 Ast0.DECL(stmt) -> statement old_metas table minus stmt
397 | Ast0.CODE(stmt_dots) -> dots (statement old_metas table minus) stmt_dots
398 | Ast0.ERRORWORDS(exps) ->
399 List.iter (expression FN old_metas table minus) exps
400 | _ -> () (* no metavariables possible *)
401
402 let rule old_metas table minus rules =
403 List.iter (top_level old_metas table minus) rules
404
405 (* --------------------------------------------------------------------- *)
406
407 let positions table rules =
408 let mcode x =
409 match Ast0.get_pos x with
410 Ast0.MetaPos(name,constraints,_) ->
411 let pos = Ast0.unwrap_mcode name in
412 (find_loop table pos) := true
413 | _ -> () in
414 let option_default = () in
415 let bind x y = () in
416 let donothing r k e = k e in
417 let fn =
418 V0.flat_combiner bind option_default
419 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
420 donothing donothing donothing donothing donothing donothing
421 donothing donothing donothing donothing donothing donothing donothing
422 donothing donothing in
423
424 List.iter fn.VT0.combiner_rec_top_level rules
425
426 let dup_positions rules =
427 let mcode x =
428 match Ast0.get_pos x with
429 Ast0.MetaPos(name,constraints,_) ->
430 let pos = Ast0.unwrap_mcode name in [pos]
431 | _ -> [] in
432 let option_default = [] in
433 let bind x y = x@y in
434
435 (* Case for everything that has a disj.
436 Note, no positions on ( | ) of a disjunction, so no need to recurse on
437 these. *)
438
439 let expression r k e =
440 match Ast0.unwrap e with
441 Ast0.DisjExpr(_,explist,_,_) ->
442 List.fold_left Common.union_set option_default
443 (List.map r.VT0.combiner_rec_expression explist)
444 | _ -> k e in
445
446 let typeC r k e = (* not sure relevent because "only after iso" *)
447 match Ast0.unwrap e with
448 Ast0.DisjType(_,types,_,_) ->
449 List.fold_left Common.union_set option_default
450 (List.map r.VT0.combiner_rec_typeC types)
451 | _ -> k e in
452
453 let declaration r k e =
454 match Ast0.unwrap e with
455 Ast0.DisjDecl(_,decls,_,_) ->
456 List.fold_left Common.union_set option_default
457 (List.map r.VT0.combiner_rec_declaration decls)
458 | _ -> k e in
459
460 let statement r k e =
461 match Ast0.unwrap e with
462 Ast0.Disj(_,stmts,_,_) ->
463 List.fold_left Common.union_set option_default
464 (List.map r.VT0.combiner_rec_statement_dots stmts)
465 | _ -> k e in
466
467 let donothing r k e = k e in
468 let fn =
469 V0.flat_combiner bind option_default
470 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
471 donothing donothing donothing donothing donothing donothing
472 donothing expression typeC donothing donothing declaration statement
473 donothing donothing in
474
475 let res =
476 List.sort compare
477 (List.fold_left Common.union_set option_default
478 (List.map fn.VT0.combiner_rec_top_level rules)) in
479 let rec loop = function
480 [] | [_] -> ()
481 | ((rule,name) as x)::y::_ when x = y ->
482 failwith (Printf.sprintf "duplicate use of %s.%s" rule name)
483 | _::xs -> loop xs in
484 loop res
485
486 (* --------------------------------------------------------------------- *)
487
488 let make_table l =
489 let table =
490 (Hashtbl.create(List.length l) :
491 ((string * string), bool ref) Hashtbl.t) in
492 List.iter
493 (function x -> Hashtbl.add table (Ast.get_meta_name x) (ref false)) l;
494 table
495
496 let add_to_fresh_table l =
497 List.iter
498 (function x ->
499 let name = Ast.get_meta_name x in Hashtbl.replace fresh_table name ())
500 l
501
502 let check_all_marked rname err table after_err =
503 Hashtbl.iter
504 (function name ->
505 function (cell) ->
506 if not (!cell)
507 then
508 let (_,name) = name in
509 warning
510 (Printf.sprintf "%s: %s %s not used %s" rname err name after_err))
511 table
512
513 let check_meta rname old_metas inherited_metavars metavars minus plus =
514 let old_metas =
515 List.map (function (_,x) -> x) (List.map Ast.get_meta_name old_metas) in
516 let (fresh,other) =
517 List.partition (function Ast.MetaFreshIdDecl(_,_) -> true | _ -> false)
518 metavars in
519 let (err,other) =
520 List.partition (function Ast.MetaErrDecl(_,_) -> true | _ -> false)
521 other in
522 let (ierr,iother) =
523 List.partition (function Ast.MetaErrDecl(_,_) -> true | _ -> false)
524 inherited_metavars in
525 let fresh_table = make_table fresh in
526 let err_table = make_table (err@ierr) in
527 let other_table = make_table other in
528 let iother_table = make_table iother in
529 add_to_fresh_table fresh;
530 rule old_metas [iother_table;other_table;err_table] true minus;
531 positions [iother_table;other_table] minus;
532 dup_positions minus;
533 check_all_marked rname "metavariable" other_table "in the - or context code";
534 rule old_metas [iother_table;fresh_table;err_table] false plus;
535 check_all_marked rname "inherited metavariable" iother_table
536 "in the -, +, or context code";
537 check_all_marked rname "metavariable" fresh_table "in the + code";
538 check_all_marked rname "error metavariable" err_table ""