2 * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
3 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
4 * This file is part of Coccinelle.
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.
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.
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/>.
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
23 (* For each rule return the list of variables that are used after it.
24 Also augment various parts of each rule with unitary, inherited, and freshness
27 (* metavar decls should be better integrated into computations of free
28 variables in plus code *)
30 module Ast
= Ast_cocci
31 module V
= Visitor_ast
32 module TC
= Type_cocci
34 let rec nub = function
36 | (x
::xs
) when (List.mem x xs
) -> nub xs
37 | (x
::xs
) -> x
::(nub xs
)
39 (* Collect all variable references in a minirule. For a disj, we collect
40 the maximum number (2 is enough) of references in any branch. *)
42 let collect_unitary_nonunitary free_usage
=
43 let free_usage = List.sort compare
free_usage in
44 let rec loop1 todrop
= function (* skips multiple occurrences *)
46 | (x
::xs
) as all
-> if x
= todrop
then loop1 todrop xs
else all
in
47 let rec loop2 = function
51 if x
= y
(* occurs more than once in free_usage *)
53 let (unitary
,non_unitary
) = loop2(loop1 x xs
) in
54 (unitary
,x
::non_unitary
)
55 else (* occurs only once in free_usage *)
56 let (unitary
,non_unitary
) = loop2 (y
::xs
) in
57 (x
::unitary
,non_unitary
) in
60 let collect_refs include_constraints
=
61 let bind x y
= x
@ y
in
62 let option_default = [] in
64 let donothing recursor k e
= k e
in (* just combine in the normal way *)
66 let donothing_a recursor k e
= (* anything is not wrapped *)
67 k e
in (* just combine in the normal way *)
69 (* the following considers that anything that occurs non-unitarily in one
70 branch occurs nonunitarily in all branches. This is not optimal, but
71 doing better seems to require a breadth-first traversal, which is
72 perhaps better to avoid. Also, unitarily is represented as occuring once,
73 while nonunitarily is represented as twice - more is irrelevant *)
74 (* cases for disjs and metavars *)
75 let bind_disj refs_branches
=
76 let (unitary
,nonunitary
) =
77 List.split
(List.map
collect_unitary_nonunitary refs_branches
) in
78 let unitary = nub (List.concat
unitary) in
79 let nonunitary = nub (List.concat
nonunitary) in
81 List.filter
(function x
-> not
(List.mem x
nonunitary)) unitary in
82 unitary@nonunitary@nonunitary in
84 let metaid (x
,_
,_
,_
) = x
in
86 let astfvident recursor k i
=
88 (match Ast.unwrap i
with
89 Ast.MetaId
(name
,_
,_
,_
) | Ast.MetaFunc
(name
,_
,_
,_
)
90 | Ast.MetaLocalFunc
(name
,_
,_
,_
) -> [metaid name
]
91 | _
-> option_default) in
93 let rec type_collect res
= function
94 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
95 | TC.Array
(ty
) -> type_collect res ty
96 | TC.MetaType
(tyname
,_
,_
) ->
98 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
101 let astfvexpr recursor k e
=
103 (match Ast.unwrap e
with
104 Ast.MetaExpr
(name
,_
,_
,Some type_list
,_
,_
) ->
105 let types = List.fold_left
type_collect option_default type_list
in
106 bind [metaid name
] types
107 | Ast.MetaErr
(name
,_
,_
,_
) | Ast.MetaExpr
(name
,_
,_
,_
,_
,_
) -> [metaid name
]
108 | Ast.MetaExprList
(name
,None
,_
,_
) -> [metaid name
]
109 | Ast.MetaExprList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
110 [metaid name
;metaid lenname
]
111 | Ast.DisjExpr
(exps
) -> bind_disj (List.map k exps
)
112 | _
-> option_default) in
114 let astfvdecls recursor k d
=
116 (match Ast.unwrap d
with
117 Ast.DisjDecl
(decls
) -> bind_disj (List.map k decls
)
118 | _
-> option_default) in
120 let astfvfullType recursor k ty
=
122 (match Ast.unwrap ty
with
123 Ast.DisjType
(types) -> bind_disj (List.map k
types)
124 | _
-> option_default) in
126 let astfvtypeC recursor k ty
=
128 (match Ast.unwrap ty
with
129 Ast.MetaType
(name
,_
,_
) -> [metaid name
]
130 | _
-> option_default) in
132 let astfvinit recursor k ty
=
134 (match Ast.unwrap ty
with
135 Ast.MetaInit
(name
,_
,_
) -> [metaid name
]
136 | _
-> option_default) in
138 let astfvparam recursor k p
=
140 (match Ast.unwrap p
with
141 Ast.MetaParam
(name
,_
,_
) -> [metaid name
]
142 | Ast.MetaParamList
(name
,None
,_
,_
) -> [metaid name
]
143 | Ast.MetaParamList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
144 [metaid name
;metaid lenname
]
145 | _
-> option_default) in
147 let astfvrule_elem recursor k re
=
148 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
151 (match Ast.unwrap re
with
152 Ast.MetaRuleElem
(name
,_
,_
) | Ast.MetaStmt
(name
,_
,_
,_
)
153 | Ast.MetaStmtList
(name
,_
,_
) -> [metaid name
]
154 | _
-> option_default)) in
156 let astfvstatement recursor k s
=
158 (match Ast.unwrap s
with
160 bind_disj (List.map recursor
.V.combiner_statement_dots stms
)
161 | _
-> option_default) in
164 if include_constraints
166 match Ast.get_pos_var mc
with
167 Ast.MetaPos
(name
,constraints
,_
,_
,_
) -> (metaid name
)::constraints
168 | _
-> option_default
169 else option_default in
171 V.combiner
bind option_default
172 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
173 donothing donothing donothing donothing
174 astfvident astfvexpr astfvfullType astfvtypeC astfvinit astfvparam
175 astfvdecls astfvrule_elem astfvstatement donothing donothing donothing_a
177 let collect_all_refs = collect_refs true
178 let collect_non_constraint_refs = collect_refs false
180 let collect_all_rule_refs minirules
=
181 List.fold_left
(@) []
182 (List.map
collect_all_refs.V.combiner_top_level minirules
)
184 let collect_all_minirule_refs = collect_all_refs.V.combiner_top_level
186 (* ---------------------------------------------------------------- *)
189 let bind = Common.union_set
in
190 let option_default = [] in
192 let donothing recursor k e
= k e
in (* just combine in the normal way *)
194 let metaid (x
,_
,_
,_
) = x
in
196 (* cases for metavariables *)
197 let astfvident recursor k i
=
199 (match Ast.unwrap i
with
200 Ast.MetaId
(name
,_
,TC.Saved
,_
)
201 | Ast.MetaFunc
(name
,_
,TC.Saved
,_
)
202 | Ast.MetaLocalFunc
(name
,_
,TC.Saved
,_
) ->
204 | _
-> option_default) in
206 let rec type_collect res
= function
207 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
208 | TC.Array
(ty
) -> type_collect res ty
209 | TC.MetaType
(tyname
,TC.Saved
,_
) ->
211 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
214 let astfvexpr recursor k e
=
216 match Ast.unwrap e
with
217 Ast.MetaExpr
(name
,_
,_
,Some type_list
,_
,_
) ->
218 List.fold_left
type_collect option_default type_list
222 (match Ast.unwrap e
with
223 Ast.MetaErr
(name
,_
,TC.Saved
,_
) | Ast.MetaExpr
(name
,_
,TC.Saved
,_
,_
,_
)
224 | Ast.MetaExprList
(name
,None
,TC.Saved
,_
) -> [metaid name
]
225 | Ast.MetaExprList
(name
,Some
(lenname
,ls
,_
),ns
,_
) ->
227 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
229 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
231 | _
-> option_default) in
234 let astfvtypeC recursor k ty
=
236 (match Ast.unwrap ty
with
237 Ast.MetaType
(name
,TC.Saved
,_
) -> [metaid name
]
238 | _
-> option_default) in
240 let astfvinit recursor k ty
=
242 (match Ast.unwrap ty
with
243 Ast.MetaInit
(name
,TC.Saved
,_
) -> [metaid name
]
244 | _
-> option_default) in
246 let astfvparam recursor k p
=
248 (match Ast.unwrap p
with
249 Ast.MetaParam
(name
,TC.Saved
,_
)
250 | Ast.MetaParamList
(name
,None
,_
,_
) -> [metaid name
]
251 | Ast.MetaParamList
(name
,Some
(lenname
,ls
,_
),ns
,_
) ->
253 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
255 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
257 | _
-> option_default) in
259 let astfvrule_elem recursor k re
=
260 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
263 (match Ast.unwrap re
with
264 Ast.MetaRuleElem
(name
,TC.Saved
,_
) | Ast.MetaStmt
(name
,TC.Saved
,_
,_
)
265 | Ast.MetaStmtList
(name
,TC.Saved
,_
) -> [metaid name
]
266 | _
-> option_default)) in
269 match Ast.get_pos_var e
with
270 Ast.MetaPos
(name
,_
,_
,TC.Saved
,_
) -> [metaid name
]
271 | _
-> option_default in
273 V.combiner
bind option_default
274 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
275 donothing donothing donothing donothing
276 astfvident astfvexpr donothing astfvtypeC astfvinit astfvparam
277 donothing astfvrule_elem donothing donothing donothing donothing
279 (* ---------------------------------------------------------------- *)
281 (* For the rules under a given metavariable declaration, collect all of the
282 variables that occur in the plus code *)
284 let cip_mcodekind r mck
=
285 let process_anything_list_list anythings
=
286 let astfvs = collect_all_refs.V.combiner_anything
in
287 List.fold_left
(@) []
288 (List.map
(function l
-> List.fold_left
(@) [] (List.map
astfvs l
))
291 Ast.MINUS
(_
,_
,_
,anythings
) -> process_anything_list_list anythings
292 | Ast.CONTEXT
(_
,befaft
) ->
294 Ast.BEFORE
(ll
,_
) -> process_anything_list_list ll
295 | Ast.AFTER
(ll
,_
) -> process_anything_list_list ll
296 | Ast.BEFOREAFTER
(llb
,lla
,_
) ->
297 (process_anything_list_list lla
) @
298 (process_anything_list_list llb
)
303 let collect_fresh_seed_env metavars l
=
308 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
309 ((Ast.get_meta_name x
),seed
)::prev
312 let (seed_env
,seeds
) =
314 (function (seed_env
,seeds
) as prev
->
317 (let v = List.assoc x
fresh in
324 Ast.SeedId
(id
) -> id
::prev
327 ((x
,ids)::seed_env
,Common.union_set
ids seeds
)
328 | _
-> ((x
,[])::seed_env
,seeds
))
329 with Not_found
-> prev
)
331 (List.rev seed_env
,List.rev seeds
)
333 let collect_fresh_seed metavars l
=
334 let (_
,seeds
) = collect_fresh_seed_env metavars l
in seeds
336 let collect_in_plus_term =
338 let bind x y
= x
@ y
in
339 let option_default = [] in
340 let donothing r k e
= k e
in
342 (* no positions in the + code *)
343 let mcode r
(_
,_
,mck
,_
) = cip_mcodekind r mck
in
345 (* case for things with bef/aft mcode *)
347 let astfvrule_elem recursor k re
=
348 match Ast.unwrap re
with
349 Ast.FunHeader
(bef
,_
,fi
,nm
,_
,params
,_
) ->
354 Ast.FType
(ty
) -> collect_all_refs.V.combiner_fullType ty
357 let nm_metas = collect_all_refs.V.combiner_ident nm
in
359 match Ast.unwrap params
with
360 Ast.DOTS
(params
) | Ast.CIRCLES
(params
) ->
364 match Ast.unwrap p
with
365 Ast.VoidParam
(t
) | Ast.Param
(t
,_
) ->
366 collect_all_refs.V.combiner_fullType t
369 | _
-> failwith
"not allowed for params" in
373 (bind (cip_mcodekind recursor bef
) (k re
))))
374 | Ast.Decl
(bef
,_
,_
) ->
375 bind (cip_mcodekind recursor bef
) (k re
)
378 let astfvstatement recursor k s
=
379 match Ast.unwrap s
with
380 Ast.IfThen
(_
,_
,(_
,_
,_
,aft
)) | Ast.IfThenElse
(_
,_
,_
,_
,(_
,_
,_
,aft
))
381 | Ast.While
(_
,_
,(_
,_
,_
,aft
)) | Ast.For
(_
,_
,(_
,_
,_
,aft
))
382 | Ast.Iterator
(_
,_
,(_
,_
,_
,aft
)) ->
383 bind (k s
) (cip_mcodekind recursor aft
)
386 V.combiner
bind option_default
387 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
388 donothing donothing donothing donothing
389 donothing donothing donothing donothing donothing donothing
390 donothing astfvrule_elem astfvstatement donothing donothing donothing
392 let collect_in_plus metavars minirules
=
394 (collect_fresh_seed metavars
396 (List.map
collect_in_plus_term.V.combiner_top_level minirules
)))
398 (* ---------------------------------------------------------------- *)
400 (* For the rules under a given metavariable declaration, collect all of the
401 variables that occur only once and more than once in the minus code *)
403 let collect_all_multirefs minirules
=
404 let refs = List.map
collect_all_refs.V.combiner_top_level minirules
in
405 collect_unitary_nonunitary (List.concat
refs)
407 (* ---------------------------------------------------------------- *)
409 (* classify as unitary (no binding) or nonunitary (env binding) or saved
412 let classify_variables metavar_decls minirules used_after
=
413 let metavars = List.map
Ast.get_meta_name metavar_decls
in
414 let (unitary,nonunitary) = collect_all_multirefs minirules
in
415 let inplus = collect_in_plus metavar_decls minirules
in
417 let donothing r k e
= k e
in
418 let check_unitary name inherited
=
419 if List.mem name
inplus or List.mem name used_after
421 else if not inherited
&& List.mem name
unitary
423 else TC.Nonunitary
in
425 let get_option f
= function Some x
-> Some
(f x
) | None
-> None
in
427 let classify (name
,_
,_
,_
) =
428 let inherited = not
(List.mem name
metavars) in
429 (check_unitary name
inherited,inherited) in
432 match Ast.get_pos_var mc
with
433 Ast.MetaPos
(name
,constraints
,per
,unitary,inherited) ->
434 let (unitary,inherited) = classify name
in
435 Ast.set_pos_var
(Ast.MetaPos
(name
,constraints
,per
,unitary,inherited))
441 match Ast.unwrap
e with
442 Ast.MetaId
(name
,constraints
,_
,_
) ->
443 let (unitary,inherited) = classify name
in
445 (Ast.MetaId
(name
,constraints
,unitary,inherited))
446 | Ast.MetaFunc
(name
,constraints
,_
,_
) ->
447 let (unitary,inherited) = classify name
in
448 Ast.rewrap
e (Ast.MetaFunc
(name
,constraints
,unitary,inherited))
449 | Ast.MetaLocalFunc
(name
,constraints
,_
,_
) ->
450 let (unitary,inherited) = classify name
in
451 Ast.rewrap
e (Ast.MetaLocalFunc
(name
,constraints
,unitary,inherited))
454 let rec type_infos = function
455 TC.ConstVol
(cv
,ty
) -> TC.ConstVol
(cv
,type_infos ty
)
456 | TC.Pointer
(ty
) -> TC.Pointer
(type_infos ty
)
457 | TC.FunctionPointer
(ty
) -> TC.FunctionPointer
(type_infos ty
)
458 | TC.Array
(ty
) -> TC.Array
(type_infos ty
)
459 | TC.MetaType
(name
,_
,_
) ->
460 let (unitary,inherited) = classify (name
,(),(),Ast.NoMetaPos
) in
461 Type_cocci.MetaType
(name
,unitary,inherited)
462 | TC.SignedT
(sgn
,Some ty
) -> TC.SignedT
(sgn
,Some
(type_infos ty
))
465 let expression r k
e =
467 match Ast.unwrap
e with
468 Ast.MetaErr
(name
,constraints
,_
,_
) ->
469 let (unitary,inherited) = classify name
in
470 Ast.rewrap
e (Ast.MetaErr
(name
,constraints
,unitary,inherited))
471 | Ast.MetaExpr
(name
,constraints
,_
,ty
,form
,_
) ->
472 let (unitary,inherited) = classify name
in
473 let ty = get_option (List.map
type_infos) ty in
474 Ast.rewrap
e (Ast.MetaExpr
(name
,constraints
,unitary,ty,form
,inherited))
475 | Ast.MetaExprList
(name
,None
,_
,_
) ->
476 (* lenname should have the same properties of being unitary or
478 let (unitary,inherited) = classify name
in
479 Ast.rewrap
e (Ast.MetaExprList
(name
,None
,unitary,inherited))
480 | Ast.MetaExprList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
481 (* lenname should have the same properties of being unitary or
483 let (unitary,inherited) = classify name
in
484 let (lenunitary
,leninherited
) = classify lenname
in
487 (name
,Some
(lenname
,lenunitary
,leninherited
),unitary,inherited))
492 match Ast.unwrap
e with
493 Ast.MetaType
(name
,_
,_
) ->
494 let (unitary,inherited) = classify name
in
495 Ast.rewrap
e (Ast.MetaType
(name
,unitary,inherited))
500 match Ast.unwrap
e with
501 Ast.MetaInit
(name
,_
,_
) ->
502 let (unitary,inherited) = classify name
in
503 Ast.rewrap
e (Ast.MetaInit
(name
,unitary,inherited))
508 match Ast.unwrap
e with
509 Ast.MetaParam
(name
,_
,_
) ->
510 let (unitary,inherited) = classify name
in
511 Ast.rewrap
e (Ast.MetaParam
(name
,unitary,inherited))
512 | Ast.MetaParamList
(name
,None
,_
,_
) ->
513 let (unitary,inherited) = classify name
in
514 Ast.rewrap
e (Ast.MetaParamList
(name
,None
,unitary,inherited))
515 | Ast.MetaParamList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
516 let (unitary,inherited) = classify name
in
517 let (lenunitary
,leninherited
) = classify lenname
in
520 (name
,Some
(lenname
,lenunitary
,leninherited
),unitary,inherited))
523 let rule_elem r k
e =
525 match Ast.unwrap
e with
526 Ast.MetaStmt
(name
,_
,msi
,_
) ->
527 let (unitary,inherited) = classify name
in
528 Ast.rewrap
e (Ast.MetaStmt
(name
,unitary,msi
,inherited))
529 | Ast.MetaStmtList
(name
,_
,_
) ->
530 let (unitary,inherited) = classify name
in
531 Ast.rewrap
e (Ast.MetaStmtList
(name
,unitary,inherited))
535 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
536 donothing donothing donothing donothing
537 ident expression donothing typeC init param donothing rule_elem
538 donothing donothing donothing donothing in
540 List.map
fn.V.rebuilder_top_level minirules
542 (* ---------------------------------------------------------------- *)
544 (* For a minirule, collect the set of non-local (not in "bound") variables that
545 are referenced. Store them in a hash table. *)
547 (* bound means the metavariable was declared previously, not locally *)
549 (* Highly inefficient, because we call collect_all_refs on nested code
550 multiple times. But we get the advantage of not having too many variants
551 of the same functions. *)
553 (* Inherited doesn't include position constraints. If they are not bound
554 then there is no constraint. *)
556 let astfvs metavars bound
=
561 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
562 ((Ast.get_meta_name x
),seed
)::prev
566 let collect_fresh l
=
567 let (matched
,freshvars
) =
569 (function (matched
,freshvars
) ->
571 try let v = List.assoc x
fresh in (matched
,(x
,v)::freshvars
)
572 with Not_found
-> (x
::matched
,freshvars
))
574 (List.rev matched
, List.rev freshvars
) in
576 (* cases for the elements of anything *)
577 let simple_setup getter k re
=
578 let minus_free = nub (getter
collect_all_refs re
) in
580 nub (getter
collect_non_constraint_refs re
) in
582 collect_fresh_seed metavars (getter
collect_in_plus_term re
) in
583 let free = Common.union_set
minus_free plus_free in
584 let nc_free = Common.union_set
minus_nc_free plus_free in
586 List.filter
(function x
-> not
(List.mem x bound
)) free in
588 List.filter
(function x
-> List.mem x bound
) nc_free in
590 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
591 let (matched
,fresh) = collect_fresh unbound in
593 Ast.free_vars
= matched
;
594 Ast.minus_free_vars
= munbound;
595 Ast.fresh_vars
= fresh;
596 Ast.inherited = inherited;
597 Ast.saved_witness
= []} in
599 let astfvrule_elem recursor k re
=
600 simple_setup (function x
-> x
.V.combiner_rule_elem
) k re
in
602 let astfvstatement recursor k s
=
603 let minus_free = nub (collect_all_refs.V.combiner_statement s
) in
605 nub (collect_non_constraint_refs.V.combiner_statement s
) in
607 collect_fresh_seed metavars
608 (collect_in_plus_term.V.combiner_statement s
) in
609 let free = Common.union_set
minus_free plus_free in
610 let nc_free = Common.union_set
minus_nc_free plus_free in
611 let classify free minus_free =
612 let (unbound,inherited) =
613 List.partition
(function x
-> not
(List.mem x bound
)) free in
615 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
616 let (matched
,fresh) = collect_fresh unbound in
617 (matched
,munbound,fresh,inherited) in
621 collect_fresh_seed metavars
622 (cip_mcodekind collect_in_plus_term aft
) in
623 match Ast.unwrap
res with
624 Ast.IfThen
(header
,branch
,(_
,_
,_
,aft
)) ->
625 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
626 Ast.IfThen
(header
,branch
,(unbound,fresh,inherited,aft
))
627 | Ast.IfThenElse
(header
,branch1
,els
,branch2
,(_
,_
,_
,aft
)) ->
628 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
629 Ast.IfThenElse
(header
,branch1
,els
,branch2
,
630 (unbound,fresh,inherited,aft
))
631 | Ast.While
(header
,body
,(_
,_
,_
,aft
)) ->
632 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
633 Ast.While
(header
,body
,(unbound,fresh,inherited,aft
))
634 | Ast.For
(header
,body
,(_
,_
,_
,aft
)) ->
635 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
636 Ast.For
(header
,body
,(unbound,fresh,inherited,aft
))
637 | Ast.Iterator
(header
,body
,(_
,_
,_
,aft
)) ->
638 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
639 Ast.Iterator
(header
,body
,(unbound,fresh,inherited,aft
))
642 let (matched
,munbound,fresh,_
) = classify free minus_free in
644 List.filter
(function x
-> List.mem x bound
) nc_free in
647 Ast.free_vars
= matched
;
648 Ast.minus_free_vars
= munbound;
649 Ast.fresh_vars
= fresh;
650 Ast.inherited = inherited;
651 Ast.saved_witness
= []} in
653 let astfvstatement_dots recursor k sd
=
654 simple_setup (function x
-> x
.V.combiner_statement_dots
) k sd
in
656 let astfvcase_line recursor k cl
=
657 simple_setup (function x
-> x
.V.combiner_case_line
) k cl
in
659 let astfvtoplevel recursor k tl
=
660 let saved = collect_saved.V.combiner_top_level tl
in
661 {(k tl
) with Ast.saved_witness
= saved} in
664 let donothing r k
e = k
e in
667 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
668 donothing donothing astfvstatement_dots donothing
669 donothing donothing donothing donothing donothing donothing donothing
670 astfvrule_elem astfvstatement astfvcase_line astfvtoplevel donothing
673 let collect_astfvs rules =
674 let rec loop bound = function
676 | (metavars,(nm,rule_info,minirules))::rules ->
678 Common.minus_set bound (List.map Ast.get_meta_name metavars) in
680 (List.map (astfvs metavars bound).V.rebuilder_top_level minirules))::
681 (loop ((List.map Ast.get_meta_name metavars)@bound) rules) in
685 let collect_astfvs rules
=
686 let rec loop bound = function
688 | (metavars, rule
)::rules
->
690 Ast.ScriptRule
(_
,_
,_
,_
)
691 | Ast.InitialScriptRule
(_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
) ->
692 (* bound stays as is because script rules have no names, so no
693 inheritance is possible *)
694 rule
::(loop bound rules
)
695 | Ast.CocciRule
(nm
, rule_info
, minirules
, isexp
, ruletype
) ->
697 Common.minus_set
bound (List.map
Ast.get_meta_name
metavars) in
700 (List.map
(astfvs metavars bound).V.rebuilder_top_level
703 (loop ((List.map
Ast.get_meta_name
metavars)@bound) rules
) in
706 (* ---------------------------------------------------------------- *)
707 (* position variables that appear as a constraint on another position variable.
708 a position variable also cannot appear both positively and negatively in a
711 let get_neg_pos_list (_
,rule
) used_after_list
=
712 let donothing r k
e = k
e in
713 let bind (p1
,np1
) (p2
,np2
) =
714 (Common.union_set p1 p2
, Common.union_set np1 np2
) in
715 let option_default = ([],[]) in
716 let metaid (x
,_
,_
,_
) = x
in
718 match Ast.get_pos_var mc
with
719 Ast.MetaPos
(name
,constraints
,Ast.PER
,_
,_
) ->
720 ([metaid name
],constraints
)
721 | Ast.MetaPos
(name
,constraints
,Ast.ALL
,_
,_
) ->
722 ([],(metaid name
)::constraints
)
723 | _
-> option_default in
725 V.combiner
bind option_default
726 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
727 donothing donothing donothing donothing
728 donothing donothing donothing donothing donothing donothing
729 donothing donothing donothing donothing donothing donothing in
731 Ast.CocciRule
(_
,_
,minirules
,_
,_
) ->
733 (function toplevel
->
734 let (positions
,neg_positions
) = v.V.combiner_top_level toplevel
in
735 (if List.exists
(function p
-> List.mem p neg_positions
) positions
738 "a variable cannot be used both as a position and a constraint");
741 | Ast.ScriptRule _
| Ast.InitialScriptRule _
| Ast.FinalScriptRule _
->
742 (*no negated positions*) []
744 (* ---------------------------------------------------------------- *)
746 (* collect used after lists, per minirule *)
748 (* defined is a list of variables that were declared in a previous metavar
751 (* Top-level used after: For each rule collect the set of variables that
752 are inherited, ie used but not defined. These are accumulated back to
753 their point of definition. *)
756 let collect_top_level_used_after metavar_rule_list
=
757 let drop_virt = List.filter
(function ("virtual",_
) -> false | _
-> true) in
758 let (used_after
,used_after_lists
) =
760 (function (metavar_list
,r
) ->
761 function (used_after
,used_after_lists
) ->
762 let locally_defined = List.map
Ast.get_meta_name metavar_list
in
763 let continue_propagation =
764 List.filter
(function x
-> not
(List.mem x
locally_defined))
768 Ast.ScriptRule
(_
,_
,mv
,_
) ->
769 drop_virt(List.map
(function (_
,(r
,v)) -> (r
,v)) mv
)
770 | Ast.InitialScriptRule
(_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
) -> []
771 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
773 (Common.union_set
(nub (collect_all_rule_refs rule
))
774 (collect_in_plus metavar_list rule
)) in
776 List.filter
(function x
-> not
(List.mem x
locally_defined))
778 (Common.union_set
inherited continue_propagation,
779 used_after
::used_after_lists
))
780 metavar_rule_list
([],[]) in
781 match used_after
with
782 [] -> used_after_lists
785 (Printf.sprintf
"collect_top_level_used_after: unbound variables %s"
786 (String.concat
" " (List.map
(function (_
,x
) -> x
) used_after
)))
788 let collect_local_used_after metavars minirules used_after
=
789 let locally_defined = List.map
Ast.get_meta_name
metavars in
790 let rec loop = function
791 [] -> (used_after
,[],[],[],[])
793 (* In a rule there are three kinds of local variables:
794 1. Variables referenced in the minus or context code.
795 These get a value by matching. This value can be used in
797 2. Fresh variables referenced in the plus code.
798 3. Variables referenced in the seeds of the fresh variables.
799 There are also non-local variables. These may either be variables
800 referenced in the minus, context, or plus code, or they may be
801 variables referenced in the seeds of the fresh variables. *)
802 (* Step 1: collect all references in minus/context, plus, seed
804 let variables_referenced_in_minus_context_code =
805 nub (collect_all_minirule_refs minirule
) in
806 let variables_referenced_in_plus_code =
807 collect_in_plus_term.V.combiner_top_level minirule
in
808 let (env_of_fresh_seeds
,seeds_and_plus
) =
809 collect_fresh_seed_env
810 metavars variables_referenced_in_plus_code in
812 Common.union_set
variables_referenced_in_minus_context_code
814 (* Step 2: identify locally defined ones *)
815 let local_fresh = List.map fst env_of_fresh_seeds
in
817 List.partition
(function x
-> List.mem x
locally_defined) in
818 let local_env_of_fresh_seeds =
819 (* these have to be restricted to only one value if the associated
820 fresh variable is used after *)
821 List.map
(function (f
,ss
) -> (f
,is_local ss
)) env_of_fresh_seeds
in
822 let (local_all_free_vars
,nonlocal_all_free_vars
) =
823 is_local all_free_vars in
824 (* Step 3, recurse on the rest of the rules, making available whatever
825 has been defined in this one *)
826 let (mini_used_after
,fvs_lists
,mini_used_after_lists
,
827 mini_fresh_used_after_lists
,mini_fresh_used_after_seeds
) =
829 (* Step 4: collect the results. These are:
830 1. All of the variables used non-locally in the rules starting
832 2. All of the free variables to the end of the semantic patch
833 3. The variables that are used afterwards and defined here by
834 matching (minus or context code)
835 4. The variables that are used afterwards and are defined here as
837 5. The variables that are used as seeds in computing the bindings
838 of the variables collected in part 4. *)
839 let (local_used_after
, nonlocal_used_after
) =
840 is_local mini_used_after
in
841 let (fresh_local_used_after
(*4*),matched_local_used_after
) =
842 List.partition
(function x
-> List.mem x
local_fresh)
844 let matched_local_used_after(*3*) =
845 Common.union_set
matched_local_used_after nonlocal_used_after
in
846 let new_used_after = (*1*)
847 Common.union_set nonlocal_all_free_vars nonlocal_used_after
in
848 let fresh_local_used_after_seeds =
850 (* no point to keep variables that already are gtd to have only
852 (function x
-> not
(List.mem x
matched_local_used_after))
853 (List.fold_left
(function p
-> function c
-> Common.union_set c p
)
857 fst
(List.assoc fua
local_env_of_fresh_seeds))
858 fresh_local_used_after
)) in
859 (new_used_after,all_free_vars::fvs_lists
(*2*),
860 matched_local_used_after::mini_used_after_lists
,
861 fresh_local_used_after
::mini_fresh_used_after_lists
,
862 fresh_local_used_after_seeds::mini_fresh_used_after_seeds
) in
863 let (_
,fvs_lists
,used_after_lists
(*ua*),
864 fresh_used_after_lists
(*fua*),fresh_used_after_lists_seeds
(*fuas*)) =
866 (fvs_lists
,used_after_lists
,
867 fresh_used_after_lists
,fresh_used_after_lists_seeds
)
871 let collect_used_after metavar_rule_list
=
872 let used_after_lists = collect_top_level_used_after metavar_rule_list
in
874 (function (metavars,r
) ->
875 function used_after
->
877 Ast.ScriptRule
(_
,_
,_
,_
)
878 | Ast.InitialScriptRule
(_
,_
,_
) | Ast.FinalScriptRule
(_
,_
,_
) ->
879 ([], [used_after
], [], [])
880 | Ast.CocciRule
(name
, rule_info
, minirules
, _
,_
) ->
881 collect_local_used_after metavars minirules used_after
883 metavar_rule_list
used_after_lists
885 let rec split4 = function
887 | (a
,b
,c
,d
)::l
-> let (a1
,b1
,c1
,d1
) = split4 l
in (a
::a1
,b
::b1
,c
::c1
,d
::d1
)
889 (* ---------------------------------------------------------------- *)
892 let free_vars rules
=
893 let metavars = List.map
(function (mv
,rule
) -> mv
) rules
in
894 let (fvs_lists
,used_after_matched_lists
,
895 fresh_used_after_lists
,fresh_used_after_lists_seeds
) =
896 split4 (collect_used_after rules
) in
898 List.map2
get_neg_pos_list rules used_after_matched_lists
in
899 let positions_list = (* for all rules, assume all positions are used after *)
904 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> []
905 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
909 function Ast.MetaPosDecl
(_
,nm
) -> nm
::prev
| _
-> prev
)
911 List.map
(function _
-> positions) rule
)
919 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> r
920 | Ast.CocciRule
(nm
, rule_info
, r
, is_exp
,ruletype
) ->
923 classify_variables mv r
924 ((List.concat ua
) @ (List.concat fua
)),
926 rules
(List.combine used_after_matched_lists fresh_used_after_lists
) in
927 let new_rules = collect_astfvs (List.combine
metavars new_rules) in
929 fvs_lists
,neg_pos_lists,
930 (used_after_matched_lists
,
931 fresh_used_after_lists
,fresh_used_after_lists_seeds
),