80900e6e865d609dfb1c0162d7ce72566bdf392b
[bpt/coccinelle.git] / parsing_cocci / free_vars.ml
1 (*
2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
9 *
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
13 *
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
24 *)
25
26
27 # 0 "./free_vars.ml"
28 (* For each rule return the list of variables that are used after it.
29 Also augment various parts of each rule with unitary, inherited, and freshness
30 informations *)
31
32 (* metavar decls should be better integrated into computations of free
33 variables in plus code *)
34
35 module Ast = Ast_cocci
36 module V = Visitor_ast
37 module TC = Type_cocci
38
39 let rec nub = function
40 [] -> []
41 | (x::xs) when (List.mem x xs) -> nub xs
42 | (x::xs) -> x::(nub xs)
43
44 (* Collect all variable references in a minirule. For a disj, we collect
45 the maximum number (2 is enough) of references in any branch. *)
46
47 let collect_unitary_nonunitary free_usage =
48 let free_usage = List.sort compare free_usage in
49 let rec loop1 todrop = function (* skips multiple occurrences *)
50 [] -> []
51 | (x::xs) as all -> if x = todrop then loop1 todrop xs else all in
52 let rec loop2 = function
53 [] -> ([],[])
54 | [x] -> ([x],[])
55 | x::y::xs ->
56 if x = y (* occurs more than once in free_usage *)
57 then
58 let (unitary,non_unitary) = loop2(loop1 x xs) in
59 (unitary,x::non_unitary)
60 else (* occurs only once in free_usage *)
61 let (unitary,non_unitary) = loop2 (y::xs) in
62 (x::unitary,non_unitary) in
63 loop2 free_usage
64
65 let collect_refs include_constraints =
66 let bind x y = x @ y in
67 let option_default = [] in
68
69 let donothing recursor k e = k e in (* just combine in the normal way *)
70
71 let donothing_a recursor k e = (* anything is not wrapped *)
72 k e in (* just combine in the normal way *)
73
74 (* the following considers that anything that occurs non-unitarily in one
75 branch occurs nonunitarily in all branches. This is not optimal, but
76 doing better seems to require a breadth-first traversal, which is
77 perhaps better to avoid. Also, unitarily is represented as occuring once,
78 while nonunitarily is represented as twice - more is irrelevant *)
79 (* cases for disjs and metavars *)
80 let bind_disj refs_branches =
81 let (unitary,nonunitary) =
82 List.split (List.map collect_unitary_nonunitary refs_branches) in
83 let unitary = nub (List.concat unitary) in
84 let nonunitary = nub (List.concat nonunitary) in
85 let unitary =
86 List.filter (function x -> not (List.mem x nonunitary)) unitary in
87 unitary@nonunitary@nonunitary in
88
89 let metaid (x,_,_,_) = x in
90
91 let astfvident recursor k i =
92 bind (k i)
93 (match Ast.unwrap i with
94 Ast.MetaId(name,idconstraint,_,_) | Ast.MetaFunc(name,idconstraint,_,_)
95 | Ast.MetaLocalFunc(name,idconstraint,_,_) ->
96 let metas =
97 if include_constraints
98 then
99 match idconstraint with
100 Ast.IdNegIdSet (_,metas) -> metas
101 | _ -> []
102 else [] in
103 bind (List.rev metas) [metaid name]
104 | Ast.DisjId(ids) -> bind_disj (List.map k ids)
105 | _ -> option_default) in
106
107 let rec type_collect res = function
108 TC.ConstVol(_,ty) | TC.Pointer(ty) | TC.FunctionPointer(ty)
109 | TC.Array(ty) -> type_collect res ty
110 | TC.EnumName(TC.MV(tyname,_,_)) ->
111 bind [tyname] res
112 | TC.StructUnionName(_,TC.MV(tyname,_,_)) ->
113 bind [tyname] res
114 | TC.MetaType(tyname,_,_) ->
115 bind [tyname] res
116 | TC.SignedT(_,Some ty) -> type_collect res ty
117 | ty -> res in
118
119 let astfvexpr recursor k e =
120 bind (k e)
121 (match Ast.unwrap e with
122 Ast.MetaExpr(name,constraints,_,Some type_list,_,_) ->
123 let types = List.fold_left type_collect option_default type_list in
124 let extra =
125 if include_constraints
126 then
127 match constraints with
128 Ast.SubExpCstrt l -> l
129 | _ -> []
130 else [] in
131 bind extra (bind [metaid name] types)
132 | Ast.MetaErr(name,constraints,_,_)
133 | Ast.MetaExpr(name,constraints,_,_,_,_) ->
134 let extra =
135 if include_constraints
136 then
137 match constraints with
138 Ast.SubExpCstrt l -> l
139 | _ -> []
140 else [] in
141 bind extra [metaid name]
142 | Ast.MetaExprList(name,Ast.MetaListLen (lenname,_,_),_,_) ->
143 [metaid name;metaid lenname]
144 | Ast.MetaExprList(name,_,_,_) -> [metaid name]
145 | Ast.DisjExpr(exps) -> bind_disj (List.map k exps)
146 | _ -> option_default) in
147
148 let astfvdecls recursor k d =
149 bind (k d)
150 (match Ast.unwrap d with
151 Ast.MetaDecl(name,_,_) | Ast.MetaField(name,_,_) -> [metaid name]
152 | Ast.MetaFieldList(name,Ast.MetaListLen(lenname,_,_),_,_) ->
153 [metaid name;metaid lenname]
154 | Ast.MetaFieldList(name,_,_,_) ->
155 [metaid name]
156 | Ast.DisjDecl(decls) -> bind_disj (List.map k decls)
157 | _ -> option_default) in
158
159 let astfvfullType recursor k ty =
160 bind (k ty)
161 (match Ast.unwrap ty with
162 Ast.DisjType(types) -> bind_disj (List.map k types)
163 | _ -> option_default) in
164
165 let astfvtypeC recursor k ty =
166 bind (k ty)
167 (match Ast.unwrap ty with
168 Ast.MetaType(name,_,_) -> [metaid name]
169 | _ -> option_default) in
170
171 let astfvinit recursor k ty =
172 bind (k ty)
173 (match Ast.unwrap ty with
174 Ast.MetaInit(name,_,_) -> [metaid name]
175 | Ast.MetaInitList(name,Ast.MetaListLen(lenname,_,_),_,_) ->
176 [metaid name;metaid lenname]
177 | Ast.MetaInitList(name,_,_,_) -> [metaid name]
178 | _ -> option_default) in
179
180 let astfvparam recursor k p =
181 bind (k p)
182 (match Ast.unwrap p with
183 Ast.MetaParam(name,_,_) -> [metaid name]
184 | Ast.MetaParamList(name,Ast.MetaListLen(lenname,_,_),_,_) ->
185 [metaid name;metaid lenname]
186 | Ast.MetaParamList(name,_,_,_) -> [metaid name]
187 | _ -> option_default) in
188
189 let astfvrule_elem recursor k re =
190 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
191 bind (k re)
192 (nub
193 (match Ast.unwrap re with
194 Ast.MetaRuleElem(name,_,_) | Ast.MetaStmt(name,_,_,_)
195 | Ast.MetaStmtList(name,_,_) -> [metaid name]
196 | _ -> option_default)) in
197
198 let astfvstatement recursor k s =
199 bind (k s)
200 (match Ast.unwrap s with
201 Ast.Disj(stms) ->
202 bind_disj (List.map recursor.V.combiner_statement_dots stms)
203 | _ -> option_default) in
204
205 let mcode r mc =
206 if include_constraints
207 then
208 List.concat
209 (List.map
210 (function Ast.MetaPos(name,constraints,_,_,_) ->
211 (metaid name)::constraints)
212 (Ast.get_pos_var mc))
213 else option_default in
214
215 V.combiner bind option_default
216 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
217 donothing donothing donothing donothing donothing
218 astfvident astfvexpr astfvfullType astfvtypeC astfvinit astfvparam
219 astfvdecls astfvrule_elem astfvstatement donothing donothing donothing_a
220
221 let collect_all_refs = collect_refs true
222 let collect_non_constraint_refs = collect_refs false
223
224 let collect_all_rule_refs minirules =
225 List.fold_left (@) []
226 (List.map collect_all_refs.V.combiner_top_level minirules)
227
228 let collect_all_minirule_refs = collect_all_refs.V.combiner_top_level
229
230 (* ---------------------------------------------------------------- *)
231
232 let collect_saved =
233 let bind = Common.union_set in
234 let option_default = [] in
235
236 let donothing recursor k e = k e in (* just combine in the normal way *)
237
238 let metaid (x,_,_,_) = x in
239
240 (* cases for metavariables *)
241 let astfvident recursor k i =
242 bind (k i)
243 (match Ast.unwrap i with
244 Ast.MetaId(name,_,TC.Saved,_)
245 | Ast.MetaFunc(name,_,TC.Saved,_)
246 | Ast.MetaLocalFunc(name,_,TC.Saved,_) -> [metaid name]
247 | _ -> option_default) in
248
249 let rec type_collect res = function
250 TC.ConstVol(_,ty) | TC.Pointer(ty) | TC.FunctionPointer(ty)
251 | TC.Array(ty) -> type_collect res ty
252 | TC.EnumName(TC.MV(tyname,TC.Saved,_)) ->
253 bind [tyname] res
254 | TC.StructUnionName(_,TC.MV(tyname,TC.Saved,_)) ->
255 bind [tyname] res
256 | TC.MetaType(tyname,TC.Saved,_) ->
257 bind [tyname] res
258 | TC.SignedT(_,Some ty) -> type_collect res ty
259 | ty -> res in
260
261 let astfvexpr recursor k e =
262 let tymetas =
263 match Ast.unwrap e with
264 Ast.MetaExpr(name,_,_,Some type_list,_,_) ->
265 List.fold_left type_collect option_default type_list
266 | _ -> [] in
267 let vars =
268 bind (k e)
269 (match Ast.unwrap e with
270 Ast.MetaErr(name,_,TC.Saved,_) | Ast.MetaExpr(name,_,TC.Saved,_,_,_)
271 -> [metaid name]
272 | Ast.MetaExprList(name,Ast.MetaListLen (lenname,ls,_),ns,_) ->
273 let namesaved =
274 match ns with TC.Saved -> [metaid name] | _ -> [] in
275 let lensaved =
276 match ls with TC.Saved -> [metaid lenname] | _ -> [] in
277 lensaved @ namesaved
278 | Ast.MetaExprList(name,_,TC.Saved,_) -> [metaid name]
279 | _ -> option_default) in
280 bind tymetas vars in
281
282 let astfvtypeC recursor k ty =
283 bind (k ty)
284 (match Ast.unwrap ty with
285 Ast.MetaType(name,TC.Saved,_) -> [metaid name]
286 | _ -> option_default) in
287
288 let astfvinit recursor k ty =
289 bind (k ty)
290 (match Ast.unwrap ty with
291 Ast.MetaInit(name,TC.Saved,_) -> [metaid name]
292 | Ast.MetaInitList(name,Ast.MetaListLen (lenname,ls,_),ns,_) ->
293 let namesaved =
294 match ns with TC.Saved -> [metaid name] | _ -> [] in
295 let lensaved =
296 match ls with TC.Saved -> [metaid lenname] | _ -> [] in
297 lensaved @ namesaved
298 | _ -> option_default) in
299
300 let astfvparam recursor k p =
301 bind (k p)
302 (match Ast.unwrap p with
303 Ast.MetaParam(name,TC.Saved,_) -> [metaid name]
304 | Ast.MetaParamList(name,Ast.MetaListLen (lenname,ls,_),ns,_) ->
305 let namesaved =
306 match ns with TC.Saved -> [metaid name] | _ -> [] in
307 let lensaved =
308 match ls with TC.Saved -> [metaid lenname] | _ -> [] in
309 lensaved @ namesaved
310 | Ast.MetaParamList(name,_,TC.Saved,_) -> [metaid name]
311 | _ -> option_default) in
312
313 let astfvdecls recursor k d =
314 bind (k d)
315 (match Ast.unwrap d with
316 Ast.MetaDecl(name,TC.Saved,_) | Ast.MetaField(name,TC.Saved,_) ->
317 [metaid name]
318 | Ast.MetaFieldList(name,Ast.MetaListLen (lenname,ls,_),ns,_) ->
319 let namesaved =
320 match ns with TC.Saved -> [metaid name] | _ -> [] in
321 let lensaved =
322 match ls with TC.Saved -> [metaid lenname] | _ -> [] in
323 lensaved @ namesaved
324 | Ast.MetaFieldList(name,_,TC.Saved,_) -> [metaid name]
325 | _ -> option_default) in
326
327 let astfvrule_elem recursor k re =
328 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
329 bind (k re)
330 (nub
331 (match Ast.unwrap re with
332 Ast.MetaRuleElem(name,TC.Saved,_) | Ast.MetaStmt(name,TC.Saved,_,_)
333 | Ast.MetaStmtList(name,TC.Saved,_) -> [metaid name]
334 | _ -> option_default)) in
335
336 let mcode r e =
337 List.fold_left
338 (function acc ->
339 function
340 Ast.MetaPos(name,_,_,TC.Saved,_) -> (metaid name) :: acc
341 | _ -> acc)
342 option_default (Ast.get_pos_var e) in
343
344 V.combiner bind option_default
345 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
346 donothing donothing donothing donothing donothing
347 astfvident astfvexpr donothing astfvtypeC astfvinit astfvparam
348 astfvdecls astfvrule_elem donothing donothing donothing donothing
349
350 (* ---------------------------------------------------------------- *)
351
352 (* For the rules under a given metavariable declaration, collect all of the
353 variables that occur in the plus code *)
354
355 let cip_mcodekind r mck =
356 let process_anything_list_list anythings =
357 let astfvs = collect_all_refs.V.combiner_anything in
358 List.fold_left (@) []
359 (List.map (function l -> List.fold_left (@) [] (List.map astfvs l))
360 anythings) in
361 match mck with
362 Ast.MINUS(_,_,_,replacement) ->
363 (match replacement with
364 Ast.REPLACEMENT(anythings,_) -> process_anything_list_list anythings
365 | Ast.NOREPLACEMENT -> [])
366 | Ast.CONTEXT(_,befaft) ->
367 (match befaft with
368 Ast.BEFORE(ll,_) -> process_anything_list_list ll
369 | Ast.AFTER(ll,_) -> process_anything_list_list ll
370 | Ast.BEFOREAFTER(llb,lla,_) ->
371 (process_anything_list_list lla) @
372 (process_anything_list_list llb)
373 | Ast.NOTHING -> [])
374 | Ast.PLUS _ -> []
375
376
377 let collect_fresh_seed_env metavars l =
378 let fresh =
379 List.fold_left
380 (function prev ->
381 function
382 Ast.MetaFreshIdDecl(_,seed) as x ->
383 ((Ast.get_meta_name x),seed)::prev
384 | _ -> prev)
385 [] metavars in
386 let (seed_env,seeds) =
387 List.fold_left
388 (function (seed_env,seeds) as prev ->
389 function x ->
390 try
391 (let v = List.assoc x fresh in
392 match v with
393 Ast.ListSeed l ->
394 let ids =
395 List.fold_left
396 (function prev ->
397 function
398 Ast.SeedId(id) -> id::prev
399 | _ -> prev)
400 [] l in
401 ((x,ids)::seed_env,Common.union_set ids seeds)
402 | _ -> ((x,[])::seed_env,seeds))
403 with Not_found -> prev)
404 ([],l) l in
405 (List.rev seed_env,List.rev seeds)
406
407 let collect_fresh_seed metavars l =
408 let (_,seeds) = collect_fresh_seed_env metavars l in seeds
409
410 let collect_in_plus_term =
411
412 let bind x y = x @ y in
413 let option_default = [] in
414 let donothing r k e = k e in
415
416 (* no positions in the + code *)
417 let mcode r (_,_,mck,_) = cip_mcodekind r mck in
418
419 (* case for things with bef/aft mcode *)
420
421 let astfvrule_elem recursor k re =
422 match Ast.unwrap re with
423 Ast.FunHeader(bef,_,fi,nm,_,params,_) ->
424 let fi_metas =
425 List.concat
426 (List.map
427 (function
428 Ast.FType(ty) -> collect_all_refs.V.combiner_fullType ty
429 | _ -> [])
430 fi) in
431 let nm_metas = collect_all_refs.V.combiner_ident nm in
432 let param_metas =
433 match Ast.unwrap params with
434 Ast.DOTS(params) | Ast.CIRCLES(params) ->
435 List.concat
436 (List.map
437 (function p ->
438 match Ast.unwrap p with
439 Ast.VoidParam(t) | Ast.Param(t,_) ->
440 collect_all_refs.V.combiner_fullType t
441 | _ -> [])
442 params)
443 | _ -> failwith "not allowed for params" in
444 bind fi_metas
445 (bind nm_metas
446 (bind param_metas
447 (bind (cip_mcodekind recursor bef) (k re))))
448 | Ast.Decl(bef,_,_) ->
449 bind (cip_mcodekind recursor bef) (k re)
450 | _ -> k re in
451
452 let astfvstatement recursor k s =
453 match Ast.unwrap s with
454 Ast.IfThen(_,_,(_,_,_,aft)) | Ast.IfThenElse(_,_,_,_,(_,_,_,aft))
455 | Ast.While(_,_,(_,_,_,aft)) | Ast.For(_,_,(_,_,_,aft))
456 | Ast.Iterator(_,_,(_,_,_,aft)) ->
457 bind (k s) (cip_mcodekind recursor aft)
458 | _ -> k s in
459
460 V.combiner bind option_default
461 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
462 donothing donothing donothing donothing donothing
463 donothing donothing donothing donothing donothing donothing
464 donothing astfvrule_elem astfvstatement donothing donothing donothing
465
466 let collect_in_plus metavars minirules =
467 nub
468 (collect_fresh_seed metavars
469 (List.concat
470 (List.map collect_in_plus_term.V.combiner_top_level minirules)))
471
472 (* ---------------------------------------------------------------- *)
473
474 (* For the rules under a given metavariable declaration, collect all of the
475 variables that occur only once and more than once in the minus code *)
476
477 let collect_all_multirefs minirules =
478 let refs = List.map collect_all_refs.V.combiner_top_level minirules in
479 collect_unitary_nonunitary (List.concat refs)
480
481 (* ---------------------------------------------------------------- *)
482
483 (* classify as unitary (no binding) or nonunitary (env binding) or saved
484 (witness binding) *)
485
486 let classify_variables metavar_decls minirules used_after =
487 let metavars = List.map Ast.get_meta_name metavar_decls in
488 let (unitary,nonunitary) = collect_all_multirefs minirules in
489 let inplus = collect_in_plus metavar_decls minirules in
490
491 let donothing r k e = k e in
492 let check_unitary name inherited =
493 if List.mem name inplus or List.mem name used_after
494 then TC.Saved
495 else if not inherited && List.mem name unitary
496 then TC.Unitary
497 else TC.Nonunitary in
498
499 let get_option f = function Some x -> Some (f x) | None -> None in
500
501 let classify (name,_,_,_) =
502 let inherited = not (List.mem name metavars) in
503 (check_unitary name inherited,inherited) in
504
505 let mcode mc =
506 let p =
507 List.map
508 (function Ast.MetaPos(name,constraints,per,unitary,inherited) ->
509 let (unitary,inherited) = classify name in
510 Ast.MetaPos(name,constraints,per,unitary,inherited))
511 (Ast.get_pos_var mc) in
512 Ast.set_pos_var p mc in
513
514 let ident r k e =
515 let e = k e in
516 match Ast.unwrap e with
517 Ast.MetaId(name,constraints,_,_) ->
518 let (unitary,inherited) = classify name in
519 Ast.rewrap e
520 (Ast.MetaId(name,constraints,unitary,inherited))
521 | Ast.MetaFunc(name,constraints,_,_) ->
522 let (unitary,inherited) = classify name in
523 Ast.rewrap e (Ast.MetaFunc(name,constraints,unitary,inherited))
524 | Ast.MetaLocalFunc(name,constraints,_,_) ->
525 let (unitary,inherited) = classify name in
526 Ast.rewrap e (Ast.MetaLocalFunc(name,constraints,unitary,inherited))
527 | _ -> e in
528
529 let rec type_infos = function
530 TC.ConstVol(cv,ty) -> TC.ConstVol(cv,type_infos ty)
531 | TC.Pointer(ty) -> TC.Pointer(type_infos ty)
532 | TC.FunctionPointer(ty) -> TC.FunctionPointer(type_infos ty)
533 | TC.Array(ty) -> TC.Array(type_infos ty)
534 | TC.EnumName(TC.MV(name,_,_)) ->
535 let (unitary,inherited) = classify (name,(),(),[]) in
536 TC.EnumName(TC.MV(name,unitary,inherited))
537 | TC.StructUnionName(su,TC.MV(name,_,_)) ->
538 let (unitary,inherited) = classify (name,(),(),[]) in
539 TC.StructUnionName(su,TC.MV(name,unitary,inherited))
540 | TC.MetaType(name,_,_) ->
541 let (unitary,inherited) = classify (name,(),(),[]) in
542 Type_cocci.MetaType(name,unitary,inherited)
543 | TC.SignedT(sgn,Some ty) -> TC.SignedT(sgn,Some (type_infos ty))
544 | ty -> ty in
545
546 let expression r k e =
547 let e = k e in
548 match Ast.unwrap e with
549 Ast.MetaErr(name,constraints,_,_) ->
550 let (unitary,inherited) = classify name in
551 Ast.rewrap e (Ast.MetaErr(name,constraints,unitary,inherited))
552 | Ast.MetaExpr(name,constraints,_,ty,form,_) ->
553 let (unitary,inherited) = classify name in
554 let ty = get_option (List.map type_infos) ty in
555 Ast.rewrap e (Ast.MetaExpr(name,constraints,unitary,ty,form,inherited))
556 | Ast.MetaExprList(name,Ast.MetaListLen(lenname,_,_),_,_) ->
557 (* lenname should have the same properties of being unitary or
558 inherited as name *)
559 let (unitary,inherited) = classify name in
560 let (lenunitary,leninherited) = classify lenname in
561 Ast.rewrap e
562 (Ast.MetaExprList
563 (name,
564 Ast.MetaListLen(lenname,lenunitary,leninherited),
565 unitary,inherited))
566 | Ast.MetaExprList(name,lenname,_,_) ->
567 (* lenname should have the same properties of being unitary or
568 inherited as name *)
569 let (unitary,inherited) = classify name in
570 Ast.rewrap e (Ast.MetaExprList(name,lenname,unitary,inherited))
571 | _ -> e in
572
573 let typeC r k e =
574 let e = k e in
575 match Ast.unwrap e with
576 Ast.MetaType(name,_,_) ->
577 let (unitary,inherited) = classify name in
578 Ast.rewrap e (Ast.MetaType(name,unitary,inherited))
579 | _ -> e in
580
581 let init r k e =
582 let e = k e in
583 match Ast.unwrap e with
584 Ast.MetaInit(name,_,_) ->
585 let (unitary,inherited) = classify name in
586 Ast.rewrap e (Ast.MetaInit(name,unitary,inherited))
587 | Ast.MetaInitList(name,Ast.MetaListLen (lenname,_,_),_,_) ->
588 let (unitary,inherited) = classify name in
589 let (lenunitary,leninherited) = classify lenname in
590 Ast.rewrap e
591 (Ast.MetaInitList
592 (name,Ast.MetaListLen(lenname,lenunitary,leninherited),
593 unitary,inherited))
594 | Ast.MetaInitList(name,lenname,_,_) ->
595 let (unitary,inherited) = classify name in
596 Ast.rewrap e (Ast.MetaInitList(name,lenname,unitary,inherited))
597 | _ -> e in
598
599 let param r k e =
600 let e = k e in
601 match Ast.unwrap e with
602 Ast.MetaParam(name,_,_) ->
603 let (unitary,inherited) = classify name in
604 Ast.rewrap e (Ast.MetaParam(name,unitary,inherited))
605 | Ast.MetaParamList(name,Ast.MetaListLen (lenname,_,_),_,_) ->
606 let (unitary,inherited) = classify name in
607 let (lenunitary,leninherited) = classify lenname in
608 Ast.rewrap e
609 (Ast.MetaParamList
610 (name,Ast.MetaListLen(lenname,lenunitary,leninherited),
611 unitary,inherited))
612 | Ast.MetaParamList(name,lenname,_,_) ->
613 let (unitary,inherited) = classify name in
614 Ast.rewrap e (Ast.MetaParamList(name,lenname,unitary,inherited))
615 | _ -> e in
616
617 let decl r k e =
618 let e = k e in
619 match Ast.unwrap e with
620 Ast.MetaDecl(name,_,_) ->
621 let (unitary,inherited) = classify name in
622 Ast.rewrap e (Ast.MetaDecl(name,unitary,inherited))
623 | Ast.MetaField(name,_,_) ->
624 let (unitary,inherited) = classify name in
625 Ast.rewrap e (Ast.MetaField(name,unitary,inherited))
626 | Ast.MetaFieldList(name,Ast.MetaListLen (lenname,_,_),_,_) ->
627 let (unitary,inherited) = classify name in
628 let (lenunitary,leninherited) = classify lenname in
629 Ast.rewrap e
630 (Ast.MetaFieldList
631 (name,Ast.MetaListLen(lenname,lenunitary,leninherited),
632 unitary,inherited))
633 | Ast.MetaFieldList(name,lenname,_,_) ->
634 let (unitary,inherited) = classify name in
635 Ast.rewrap e (Ast.MetaFieldList(name,lenname,unitary,inherited))
636 | _ -> e in
637
638 let rule_elem r k e =
639 let e = k e in
640 match Ast.unwrap e with
641 Ast.MetaStmt(name,_,msi,_) ->
642 let (unitary,inherited) = classify name in
643 Ast.rewrap e (Ast.MetaStmt(name,unitary,msi,inherited))
644 | Ast.MetaStmtList(name,_,_) ->
645 let (unitary,inherited) = classify name in
646 Ast.rewrap e (Ast.MetaStmtList(name,unitary,inherited))
647 | _ -> e in
648
649 let fn = V.rebuilder
650 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
651 donothing donothing donothing donothing donothing
652 ident expression donothing typeC init param decl rule_elem
653 donothing donothing donothing donothing in
654
655 List.map fn.V.rebuilder_top_level minirules
656
657 (* ---------------------------------------------------------------- *)
658
659 (* For a minirule, collect the set of non-local (not in "bound") variables that
660 are referenced. Store them in a hash table. *)
661
662 (* bound means the metavariable was declared previously, not locally *)
663
664 (* Highly inefficient, because we call collect_all_refs on nested code
665 multiple times. But we get the advantage of not having too many variants
666 of the same functions. *)
667
668 (* Inherited doesn't include position constraints. If they are not bound
669 then there is no constraint. *)
670
671 let astfvs metavars bound =
672 let fresh =
673 List.fold_left
674 (function prev ->
675 function
676 Ast.MetaFreshIdDecl(_,seed) as x ->
677 ((Ast.get_meta_name x),seed)::prev
678 | _ -> prev)
679 [] metavars in
680
681 let collect_fresh l =
682 let (matched,freshvars) =
683 List.fold_left
684 (function (matched,freshvars) ->
685 function x ->
686 try let v = List.assoc x fresh in (matched,(x,v)::freshvars)
687 with Not_found -> (x::matched,freshvars))
688 ([],[]) l in
689 (List.rev matched, List.rev freshvars) in
690
691 (* cases for the elements of anything *)
692 let simple_setup getter k re =
693 let minus_free = nub (getter collect_all_refs re) in
694 let minus_nc_free =
695 nub (getter collect_non_constraint_refs re) in
696 let plus_free =
697 collect_fresh_seed metavars (getter collect_in_plus_term re) in
698 let free = Common.union_set minus_free plus_free in
699 let nc_free = Common.union_set minus_nc_free plus_free in
700 let unbound =
701 List.filter (function x -> not(List.mem x bound)) free in
702 let inherited =
703 List.filter (function x -> List.mem x bound) nc_free in
704 let munbound =
705 List.filter (function x -> not(List.mem x bound)) minus_free in
706 let (matched,fresh) = collect_fresh unbound in
707 {(k re) with
708 Ast.free_vars = matched;
709 Ast.minus_free_vars = munbound;
710 Ast.fresh_vars = fresh;
711 Ast.inherited = inherited;
712 Ast.saved_witness = []} in
713
714 let astfvrule_elem recursor k re =
715 simple_setup (function x -> x.V.combiner_rule_elem) k re in
716
717 let astfvstatement recursor k s =
718 let minus_free = nub (collect_all_refs.V.combiner_statement s) in
719 let minus_nc_free =
720 nub (collect_non_constraint_refs.V.combiner_statement s) in
721 let plus_free =
722 collect_fresh_seed metavars
723 (collect_in_plus_term.V.combiner_statement s) in
724 let free = Common.union_set minus_free plus_free in
725 let nc_free = Common.union_set minus_nc_free plus_free in
726 let classify free minus_free =
727 let (unbound,inherited) =
728 List.partition (function x -> not(List.mem x bound)) free in
729 let munbound =
730 List.filter (function x -> not(List.mem x bound)) minus_free in
731 let (matched,fresh) = collect_fresh unbound in
732 (matched,munbound,fresh,inherited) in
733 let res = k s in
734 let s =
735 let cip_plus aft =
736 collect_fresh_seed metavars
737 (cip_mcodekind collect_in_plus_term aft) in
738 match Ast.unwrap res with
739 Ast.IfThen(header,branch,(_,_,_,aft)) ->
740 let (unbound,_,fresh,inherited) = classify (cip_plus aft) [] in
741 Ast.IfThen(header,branch,(unbound,fresh,inherited,aft))
742 | Ast.IfThenElse(header,branch1,els,branch2,(_,_,_,aft)) ->
743 let (unbound,_,fresh,inherited) = classify (cip_plus aft) [] in
744 Ast.IfThenElse(header,branch1,els,branch2,
745 (unbound,fresh,inherited,aft))
746 | Ast.While(header,body,(_,_,_,aft)) ->
747 let (unbound,_,fresh,inherited) = classify (cip_plus aft) [] in
748 Ast.While(header,body,(unbound,fresh,inherited,aft))
749 | Ast.For(header,body,(_,_,_,aft)) ->
750 let (unbound,_,fresh,inherited) = classify (cip_plus aft) [] in
751 Ast.For(header,body,(unbound,fresh,inherited,aft))
752 | Ast.Iterator(header,body,(_,_,_,aft)) ->
753 let (unbound,_,fresh,inherited) = classify (cip_plus aft) [] in
754 Ast.Iterator(header,body,(unbound,fresh,inherited,aft))
755 | s -> s in
756
757 let (matched,munbound,fresh,_) = classify free minus_free in
758 let inherited =
759 List.filter (function x -> List.mem x bound) nc_free in
760 {res with
761 Ast.node = s;
762 Ast.free_vars = matched;
763 Ast.minus_free_vars = munbound;
764 Ast.fresh_vars = fresh;
765 Ast.inherited = inherited;
766 Ast.saved_witness = []} in
767
768 let astfvstatement_dots recursor k sd =
769 simple_setup (function x -> x.V.combiner_statement_dots) k sd in
770
771 let astfvcase_line recursor k cl =
772 simple_setup (function x -> x.V.combiner_case_line) k cl in
773
774 let astfvtoplevel recursor k tl =
775 let saved = collect_saved.V.combiner_top_level tl in
776 {(k tl) with Ast.saved_witness = saved} in
777
778 let mcode x = x in
779 let donothing r k e = k e in
780
781 V.rebuilder
782 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
783 donothing donothing astfvstatement_dots donothing donothing
784 donothing donothing donothing donothing donothing donothing donothing
785 astfvrule_elem astfvstatement astfvcase_line astfvtoplevel donothing
786
787 (*
788 let collect_astfvs rules =
789 let rec loop bound = function
790 [] -> []
791 | (metavars,(nm,rule_info,minirules))::rules ->
792 let bound =
793 Common.minus_set bound (List.map Ast.get_meta_name metavars) in
794 (nm,rule_info,
795 (List.map (astfvs metavars bound).V.rebuilder_top_level minirules))::
796 (loop ((List.map Ast.get_meta_name metavars)@bound) rules) in
797 loop [] rules
798 *)
799
800 let collect_astfvs rules =
801 let rec loop bound = function
802 [] -> []
803 | (metavars, rule)::rules ->
804 match rule with
805 Ast.ScriptRule (_,_,_,_,script_vars,_) ->
806 (* why are metavars in rule, but outside for cocci rule??? *)
807 let bound = script_vars @ bound in
808 rule::(loop bound rules)
809 | Ast.InitialScriptRule (_,_,_,_) | Ast.FinalScriptRule (_,_,_,_) ->
810 (* bound stays as is because script rules have no names, so no
811 inheritance is possible *)
812 rule::(loop bound rules)
813 | Ast.CocciRule (nm, rule_info, minirules, isexp, ruletype) ->
814 let bound =
815 Common.minus_set bound (List.map Ast.get_meta_name metavars) in
816 (Ast.CocciRule
817 (nm, rule_info,
818 (List.map (astfvs metavars bound).V.rebuilder_top_level
819 minirules),
820 isexp, ruletype))::
821 (loop ((List.map Ast.get_meta_name metavars)@bound) rules) in
822 loop [] rules
823
824 (* ---------------------------------------------------------------- *)
825 (* position variables that appear as a constraint on another position variable.
826 a position variable also cannot appear both positively and negatively in a
827 single rule. *)
828
829 let get_neg_pos_list (_,rule) used_after_list =
830 let donothing r k e = k e in
831 let bind (p1,np1) (p2,np2) =
832 (Common.union_set p1 p2, Common.union_set np1 np2) in
833 let option_default = ([],[]) in
834 let metaid (x,_,_,_) = x in
835 let mcode r mc =
836 List.fold_left
837 (function (a,b) ->
838 (function
839 Ast.MetaPos(name,constraints,Ast.PER,_,_) ->
840 ((metaid name)::a,constraints@b)
841 | Ast.MetaPos(name,constraints,Ast.ALL,_,_) ->
842 (a,(metaid name)::constraints@b)))
843 option_default (Ast.get_pos_var mc) in
844 let v =
845 V.combiner bind option_default
846 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
847 donothing donothing donothing donothing donothing
848 donothing donothing donothing donothing donothing donothing
849 donothing donothing donothing donothing donothing donothing in
850 match rule with
851 Ast.CocciRule(_,_,minirules,_,_) ->
852 List.map
853 (function toplevel ->
854 let (positions,neg_positions) = v.V.combiner_top_level toplevel in
855 (if List.exists (function p -> List.mem p neg_positions) positions
856 then
857 failwith
858 "a variable cannot be used both as a position and a constraint");
859 neg_positions)
860 minirules
861 | Ast.ScriptRule _ | Ast.InitialScriptRule _ | Ast.FinalScriptRule _ ->
862 (*no negated positions*) []
863
864 (* ---------------------------------------------------------------- *)
865
866 (* collect used after lists, per minirule *)
867
868 (* defined is a list of variables that were declared in a previous metavar
869 declaration *)
870
871 (* Top-level used after: For each rule collect the set of variables that
872 are inherited, ie used but not defined. These are accumulated back to
873 their point of definition. *)
874
875
876 let collect_top_level_used_after metavar_rule_list =
877 let drop_virt = List.filter (function ("virtual",_) -> false | _ -> true) in
878 let (used_after,used_after_lists) =
879 List.fold_right
880 (function (metavar_list,r) ->
881 function (used_after,used_after_lists) ->
882 let locally_defined =
883 match r with
884 Ast.ScriptRule (_,_,_,_,free_vars,_) -> free_vars
885 | _ -> List.map Ast.get_meta_name metavar_list in
886 let continue_propagation =
887 List.filter (function x -> not(List.mem x locally_defined))
888 used_after in
889 let free_vars =
890 match r with
891 Ast.ScriptRule (_,_,_,mv,_,_) ->
892 drop_virt(List.map (function (_,(r,v),_) -> (r,v)) mv)
893 | Ast.InitialScriptRule (_,_,_,_)
894 | Ast.FinalScriptRule (_,_,_,_) -> []
895 | Ast.CocciRule (_,_,rule,_,_) ->
896 drop_virt
897 (Common.union_set (nub (collect_all_rule_refs rule))
898 (collect_in_plus metavar_list rule)) in
899 let inherited =
900 List.filter (function x -> not (List.mem x locally_defined))
901 free_vars in
902 (Common.union_set inherited continue_propagation,
903 used_after::used_after_lists))
904 metavar_rule_list ([],[]) in
905 match used_after with
906 [] -> used_after_lists
907 | _ ->
908 failwith
909 (Printf.sprintf "collect_top_level_used_after: unbound variables %s"
910 (String.concat " " (List.map (function (_,x) -> x) used_after)))
911
912 let collect_local_used_after metavars minirules used_after =
913 let locally_defined = List.map Ast.get_meta_name metavars in
914 let rec loop = function
915 [] -> (used_after,[],[],[],[])
916 | minirule::rest ->
917 (* In a rule there are three kinds of local variables:
918 1. Variables referenced in the minus or context code.
919 These get a value by matching. This value can be used in
920 subsequent rules.
921 2. Fresh variables referenced in the plus code.
922 3. Variables referenced in the seeds of the fresh variables.
923 There are also non-local variables. These may either be variables
924 referenced in the minus, context, or plus code, or they may be
925 variables referenced in the seeds of the fresh variables. *)
926 (* Step 1: collect all references in minus/context, plus, seed
927 code *)
928 let variables_referenced_in_minus_context_code =
929 nub (collect_all_minirule_refs minirule) in
930 let variables_referenced_in_plus_code =
931 collect_in_plus_term.V.combiner_top_level minirule in
932 let (env_of_fresh_seeds,seeds_and_plus) =
933 collect_fresh_seed_env
934 metavars variables_referenced_in_plus_code in
935 let all_free_vars =
936 Common.union_set variables_referenced_in_minus_context_code
937 seeds_and_plus in
938 (* Step 2: identify locally defined ones *)
939 let local_fresh = List.map fst env_of_fresh_seeds in
940 let is_local =
941 List.partition (function x -> List.mem x locally_defined) in
942 let local_env_of_fresh_seeds =
943 (* these have to be restricted to only one value if the associated
944 fresh variable is used after *)
945 List.map (function (f,ss) -> (f,is_local ss)) env_of_fresh_seeds in
946 let (local_all_free_vars,nonlocal_all_free_vars) =
947 is_local all_free_vars in
948 (* Step 3, recurse on the rest of the rules, making available whatever
949 has been defined in this one *)
950 let (mini_used_after,fvs_lists,mini_used_after_lists,
951 mini_fresh_used_after_lists,mini_fresh_used_after_seeds) =
952 loop rest in
953 (* Step 4: collect the results. These are:
954 1. All of the variables used non-locally in the rules starting
955 with this one
956 2. All of the free variables to the end of the semantic patch
957 3. The variables that are used afterwards and defined here by
958 matching (minus or context code)
959 4. The variables that are used afterwards and are defined here as
960 fresh
961 5. The variables that are used as seeds in computing the bindings
962 of the variables collected in part 4. *)
963 let (local_used_after, nonlocal_used_after) =
964 is_local mini_used_after in
965 let (fresh_local_used_after(*4*),matched_local_used_after) =
966 List.partition (function x -> List.mem x local_fresh)
967 local_used_after in
968 let matched_local_used_after(*3*) =
969 Common.union_set matched_local_used_after nonlocal_used_after in
970 let new_used_after = (*1*)
971 Common.union_set nonlocal_all_free_vars nonlocal_used_after in
972 let fresh_local_used_after_seeds =
973 List.filter
974 (* no point to keep variables that already are gtd to have only
975 one value *)
976 (function x -> not (List.mem x matched_local_used_after))
977 (List.fold_left (function p -> function c -> Common.union_set c p)
978 []
979 (List.map
980 (function fua ->
981 fst (List.assoc fua local_env_of_fresh_seeds))
982 fresh_local_used_after)) in
983 (new_used_after,all_free_vars::fvs_lists(*2*),
984 matched_local_used_after::mini_used_after_lists,
985 fresh_local_used_after::mini_fresh_used_after_lists,
986 fresh_local_used_after_seeds::mini_fresh_used_after_seeds) in
987 let (_,fvs_lists,used_after_lists(*ua*),
988 fresh_used_after_lists(*fua*),fresh_used_after_lists_seeds(*fuas*)) =
989 loop minirules in
990 (fvs_lists,used_after_lists,
991 fresh_used_after_lists,fresh_used_after_lists_seeds)
992
993
994
995 let collect_used_after metavar_rule_list =
996 let used_after_lists = collect_top_level_used_after metavar_rule_list in
997 List.map2
998 (function (metavars,r) ->
999 function used_after ->
1000 match r with
1001 Ast.ScriptRule (_,_,_,_,_,_) (* no minirules, so nothing to do? *)
1002 | Ast.InitialScriptRule (_,_,_,_) | Ast.FinalScriptRule (_,_,_,_) ->
1003 ([], [used_after], [[]], [])
1004 | Ast.CocciRule (name, rule_info, minirules, _,_) ->
1005 collect_local_used_after metavars minirules used_after
1006 )
1007 metavar_rule_list used_after_lists
1008
1009 let rec split4 = function
1010 [] -> ([],[],[],[])
1011 | (a,b,c,d)::l -> let (a1,b1,c1,d1) = split4 l in (a::a1,b::b1,c::c1,d::d1)
1012
1013 (* ---------------------------------------------------------------- *)
1014 (* entry point *)
1015
1016 let free_vars rules =
1017 let metavars = List.map (function (mv,rule) -> mv) rules in
1018 let (fvs_lists,used_after_matched_lists,
1019 fresh_used_after_lists,fresh_used_after_lists_seeds) =
1020 split4 (collect_used_after rules) in
1021 let neg_pos_lists =
1022 List.map2 get_neg_pos_list rules used_after_matched_lists in
1023 let positions_list = (* for all rules, assume all positions are used after *)
1024 List.map
1025 (function (mv, r) ->
1026 match r with
1027 Ast.ScriptRule _ (* doesn't declare position variables *)
1028 | Ast.InitialScriptRule _ | Ast.FinalScriptRule _ -> []
1029 | Ast.CocciRule (_,_,rule,_,_) ->
1030 let positions =
1031 List.fold_left
1032 (function prev ->
1033 function Ast.MetaPosDecl(_,nm) -> nm::prev | _ -> prev)
1034 [] mv in
1035 List.map (function _ -> positions) rule)
1036 rules in
1037 let new_rules =
1038 List.map2
1039 (function (mv,r) ->
1040 function (ua,fua) ->
1041 match r with
1042 Ast.ScriptRule _
1043 | Ast.InitialScriptRule _ | Ast.FinalScriptRule _ -> r
1044 | Ast.CocciRule (nm, rule_info, r, is_exp,ruletype) ->
1045 Ast.CocciRule
1046 (nm, rule_info,
1047 classify_variables mv r
1048 ((List.concat ua) @ (List.concat fua)),
1049 is_exp,ruletype))
1050 rules (List.combine used_after_matched_lists fresh_used_after_lists) in
1051 let new_rules = collect_astfvs (List.combine metavars new_rules) in
1052 (metavars,new_rules,
1053 fvs_lists,neg_pos_lists,
1054 (used_after_matched_lists,
1055 fresh_used_after_lists,fresh_used_after_lists_seeds),
1056 positions_list)