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