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