2 * Copyright 2005-2009, 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
,_
,_
) -> bind [tyname
] res
97 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
100 let astfvexpr recursor k e
=
102 (match Ast.unwrap e
with
103 Ast.MetaExpr
(name
,_
,_
,Some type_list
,_
,_
) ->
104 let types = List.fold_left
type_collect option_default type_list
in
105 bind [metaid name
] types
106 | Ast.MetaErr
(name
,_
,_
,_
) | Ast.MetaExpr
(name
,_
,_
,_
,_
,_
) -> [metaid name
]
107 | Ast.MetaExprList
(name
,None
,_
,_
) -> [metaid name
]
108 | Ast.MetaExprList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
109 [metaid name
;metaid lenname
]
110 | Ast.DisjExpr
(exps
) -> bind_disj (List.map k exps
)
111 | _
-> option_default) in
113 let astfvdecls recursor k d
=
115 (match Ast.unwrap d
with
116 Ast.DisjDecl
(decls
) -> bind_disj (List.map k decls
)
117 | _
-> option_default) in
119 let astfvfullType recursor k ty
=
121 (match Ast.unwrap ty
with
122 Ast.DisjType
(types) -> bind_disj (List.map k
types)
123 | _
-> option_default) in
125 let astfvtypeC recursor k ty
=
127 (match Ast.unwrap ty
with
128 Ast.MetaType
(name
,_
,_
) -> [metaid name
]
129 | _
-> option_default) in
131 let astfvinit recursor k ty
=
133 (match Ast.unwrap ty
with
134 Ast.MetaInit
(name
,_
,_
) -> [metaid name
]
135 | _
-> option_default) in
137 let astfvparam recursor k p
=
139 (match Ast.unwrap p
with
140 Ast.MetaParam
(name
,_
,_
) -> [metaid name
]
141 | Ast.MetaParamList
(name
,None
,_
,_
) -> [metaid name
]
142 | Ast.MetaParamList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
143 [metaid name
;metaid lenname
]
144 | _
-> option_default) in
146 let astfvrule_elem recursor k re
=
147 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
150 (match Ast.unwrap re
with
151 Ast.MetaRuleElem
(name
,_
,_
) | Ast.MetaStmt
(name
,_
,_
,_
)
152 | Ast.MetaStmtList
(name
,_
,_
) -> [metaid name
]
153 | _
-> option_default)) in
155 let astfvstatement recursor k s
=
157 (match Ast.unwrap s
with
159 bind_disj (List.map recursor
.V.combiner_statement_dots stms
)
160 | _
-> option_default) in
163 if include_constraints
165 match Ast.get_pos_var mc
with
166 Ast.MetaPos
(name
,constraints
,_
,_
,_
) -> (metaid name
)::constraints
167 | _
-> option_default
168 else option_default in
170 V.combiner
bind option_default
171 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
172 donothing donothing donothing donothing
173 astfvident astfvexpr astfvfullType astfvtypeC astfvinit astfvparam
174 astfvdecls astfvrule_elem astfvstatement donothing donothing donothing_a
176 let collect_all_refs = collect_refs true
177 let collect_non_constraint_refs = collect_refs false
179 let collect_all_rule_refs minirules
=
180 List.fold_left
(@) []
181 (List.map
collect_all_refs.V.combiner_top_level minirules
)
183 let collect_all_minirule_refs = collect_all_refs.V.combiner_top_level
185 (* ---------------------------------------------------------------- *)
188 let bind = Common.union_set
in
189 let option_default = [] in
191 let donothing recursor k e
= k e
in (* just combine in the normal way *)
193 let metaid (x
,_
,_
,_
) = x
in
195 (* cases for metavariables *)
196 let astfvident recursor k i
=
198 (match Ast.unwrap i
with
199 Ast.MetaId
(name
,_
,TC.Saved
,_
) | Ast.MetaFunc
(name
,_
,TC.Saved
,_
)
200 | Ast.MetaLocalFunc
(name
,_
,TC.Saved
,_
) -> [metaid name
]
201 | _
-> option_default) in
203 let rec type_collect res
= function
204 TC.ConstVol
(_
,ty
) | TC.Pointer
(ty
) | TC.FunctionPointer
(ty
)
205 | TC.Array
(ty
) -> type_collect res ty
206 | TC.MetaType
(tyname
,TC.Saved
,_
) -> bind [tyname
] res
207 | TC.SignedT
(_
,Some ty
) -> type_collect res ty
210 let astfvexpr recursor k e
=
212 match Ast.unwrap e
with
213 Ast.MetaExpr
(name
,_
,_
,Some type_list
,_
,_
) ->
214 List.fold_left
type_collect option_default type_list
218 (match Ast.unwrap e
with
219 Ast.MetaErr
(name
,_
,TC.Saved
,_
) | Ast.MetaExpr
(name
,_
,TC.Saved
,_
,_
,_
)
220 | Ast.MetaExprList
(name
,None
,TC.Saved
,_
) -> [metaid name
]
221 | Ast.MetaExprList
(name
,Some
(lenname
,ls
,_
),ns
,_
) ->
223 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
225 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
227 | _
-> option_default) in
230 let astfvtypeC recursor k ty
=
232 (match Ast.unwrap ty
with
233 Ast.MetaType
(name
,TC.Saved
,_
) -> [metaid name
]
234 | _
-> option_default) in
236 let astfvinit recursor k ty
=
238 (match Ast.unwrap ty
with
239 Ast.MetaInit
(name
,TC.Saved
,_
) -> [metaid name
]
240 | _
-> option_default) in
242 let astfvparam recursor k p
=
244 (match Ast.unwrap p
with
245 Ast.MetaParam
(name
,TC.Saved
,_
)
246 | Ast.MetaParamList
(name
,None
,_
,_
) -> [metaid name
]
247 | Ast.MetaParamList
(name
,Some
(lenname
,ls
,_
),ns
,_
) ->
249 match ns
with TC.Saved
-> [metaid name
] | _
-> [] in
251 match ls
with TC.Saved
-> [metaid lenname
] | _
-> [] in
253 | _
-> option_default) in
255 let astfvrule_elem recursor k re
=
256 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
259 (match Ast.unwrap re
with
260 Ast.MetaRuleElem
(name
,TC.Saved
,_
) | Ast.MetaStmt
(name
,TC.Saved
,_
,_
)
261 | Ast.MetaStmtList
(name
,TC.Saved
,_
) -> [metaid name
]
262 | _
-> option_default)) in
265 match Ast.get_pos_var e
with
266 Ast.MetaPos
(name
,_
,_
,TC.Saved
,_
) -> [metaid name
]
267 | _
-> option_default in
269 V.combiner
bind option_default
270 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
271 donothing donothing donothing donothing
272 astfvident astfvexpr donothing astfvtypeC astfvinit astfvparam
273 donothing astfvrule_elem donothing donothing donothing donothing
275 (* ---------------------------------------------------------------- *)
277 (* For the rules under a given metavariable declaration, collect all of the
278 variables that occur in the plus code *)
280 let cip_mcodekind r mck
=
281 let process_anything_list_list anythings
=
282 let astfvs = collect_all_refs.V.combiner_anything
in
283 List.fold_left
(@) []
284 (List.map
(function l
-> List.fold_left
(@) [] (List.map
astfvs l
))
287 Ast.MINUS
(_
,_
,_
,anythings
) -> process_anything_list_list anythings
288 | Ast.CONTEXT
(_
,befaft
) ->
290 Ast.BEFORE
(ll
,_
) -> process_anything_list_list ll
291 | Ast.AFTER
(ll
,_
) -> process_anything_list_list ll
292 | Ast.BEFOREAFTER
(llb
,lla
,_
) ->
293 (process_anything_list_list lla
) @
294 (process_anything_list_list llb
)
299 let collect_fresh_seed_env metavars l
=
304 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
305 ((Ast.get_meta_name x
),seed
)::prev
308 let (seed_env
,seeds
) =
310 (function (seed_env
,seeds
) as prev
->
313 (let v = List.assoc x
fresh in
320 Ast.SeedId
(id
) -> id
::prev
323 ((x
,ids)::seed_env
,Common.union_set
ids seeds
)
324 | _
-> ((x
,[])::seed_env
,seeds
))
325 with Not_found
-> prev
)
327 (List.rev seed_env
,List.rev seeds
)
329 let collect_fresh_seed metavars l
=
330 let (_
,seeds
) = collect_fresh_seed_env metavars l
in seeds
332 let collect_in_plus_term =
334 let bind x y
= x
@ y
in
335 let option_default = [] in
336 let donothing r k e
= k e
in
338 (* no positions in the + code *)
339 let mcode r
(_
,_
,mck
,_
) = cip_mcodekind r mck
in
341 (* case for things with bef/aft mcode *)
343 let astfvrule_elem recursor k re
=
344 match Ast.unwrap re
with
345 Ast.FunHeader
(bef
,_
,fi
,nm
,_
,params
,_
) ->
350 Ast.FType
(ty
) -> collect_all_refs.V.combiner_fullType ty
353 let nm_metas = collect_all_refs.V.combiner_ident nm
in
355 match Ast.unwrap params
with
356 Ast.DOTS
(params
) | Ast.CIRCLES
(params
) ->
360 match Ast.unwrap p
with
361 Ast.VoidParam
(t
) | Ast.Param
(t
,_
) ->
362 collect_all_refs.V.combiner_fullType t
365 | _
-> failwith
"not allowed for params" in
369 (bind (cip_mcodekind recursor bef
) (k re
))))
370 | Ast.Decl
(bef
,_
,_
) ->
371 bind (cip_mcodekind recursor bef
) (k re
)
374 let astfvstatement recursor k s
=
375 match Ast.unwrap s
with
376 Ast.IfThen
(_
,_
,(_
,_
,_
,aft
)) | Ast.IfThenElse
(_
,_
,_
,_
,(_
,_
,_
,aft
))
377 | Ast.While
(_
,_
,(_
,_
,_
,aft
)) | Ast.For
(_
,_
,(_
,_
,_
,aft
))
378 | Ast.Iterator
(_
,_
,(_
,_
,_
,aft
)) ->
379 bind (k s
) (cip_mcodekind recursor aft
)
382 V.combiner
bind option_default
383 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
384 donothing donothing donothing donothing
385 donothing donothing donothing donothing donothing donothing
386 donothing astfvrule_elem astfvstatement donothing donothing donothing
388 let collect_in_plus metavars minirules
=
390 (collect_fresh_seed metavars
392 (List.map
collect_in_plus_term.V.combiner_top_level minirules
)))
394 (* ---------------------------------------------------------------- *)
396 (* For the rules under a given metavariable declaration, collect all of the
397 variables that occur only once and more than once in the minus code *)
399 let collect_all_multirefs minirules
=
400 let refs = List.map
collect_all_refs.V.combiner_top_level minirules
in
401 collect_unitary_nonunitary (List.concat
refs)
403 (* ---------------------------------------------------------------- *)
405 (* classify as unitary (no binding) or nonunitary (env binding) or saved
408 let classify_variables metavar_decls minirules used_after
=
409 let metavars = List.map
Ast.get_meta_name metavar_decls
in
410 let (unitary,nonunitary) = collect_all_multirefs minirules
in
411 let inplus = collect_in_plus metavar_decls minirules
in
413 let donothing r k e
= k e
in
414 let check_unitary name inherited
=
415 if List.mem name
inplus or List.mem name used_after
417 else if not inherited
&& List.mem name
unitary
419 else TC.Nonunitary
in
421 let get_option f
= function Some x
-> Some
(f x
) | None
-> None
in
423 let classify (name
,_
,_
,_
) =
424 let inherited = not
(List.mem name
metavars) in
425 (check_unitary name
inherited,inherited) in
428 match Ast.get_pos_var mc
with
429 Ast.MetaPos
(name
,constraints
,per
,unitary,inherited) ->
430 let (unitary,inherited) = classify name
in
431 Ast.set_pos_var
(Ast.MetaPos
(name
,constraints
,per
,unitary,inherited))
437 match Ast.unwrap
e with
438 Ast.MetaId
(name
,constraints
,_
,_
) ->
439 let (unitary,inherited) = classify name
in
440 Ast.rewrap
e (Ast.MetaId
(name
,constraints
,unitary,inherited))
441 | Ast.MetaFunc
(name
,constraints
,_
,_
) ->
442 let (unitary,inherited) = classify name
in
443 Ast.rewrap
e (Ast.MetaFunc
(name
,constraints
,unitary,inherited))
444 | Ast.MetaLocalFunc
(name
,constraints
,_
,_
) ->
445 let (unitary,inherited) = classify name
in
446 Ast.rewrap
e (Ast.MetaLocalFunc
(name
,constraints
,unitary,inherited))
449 let rec type_infos = function
450 TC.ConstVol
(cv
,ty
) -> TC.ConstVol
(cv
,type_infos ty
)
451 | TC.Pointer
(ty
) -> TC.Pointer
(type_infos ty
)
452 | TC.FunctionPointer
(ty
) -> TC.FunctionPointer
(type_infos ty
)
453 | TC.Array
(ty
) -> TC.Array
(type_infos ty
)
454 | TC.MetaType
(name
,_
,_
) ->
455 let (unitary,inherited) = classify (name
,(),(),Ast.NoMetaPos
) in
456 Type_cocci.MetaType
(name
,unitary,inherited)
457 | TC.SignedT
(sgn
,Some ty
) -> TC.SignedT
(sgn
,Some
(type_infos ty
))
460 let expression r k
e =
462 match Ast.unwrap
e with
463 Ast.MetaErr
(name
,constraints
,_
,_
) ->
464 let (unitary,inherited) = classify name
in
465 Ast.rewrap
e (Ast.MetaErr
(name
,constraints
,unitary,inherited))
466 | Ast.MetaExpr
(name
,constraints
,_
,ty
,form
,_
) ->
467 let (unitary,inherited) = classify name
in
468 let ty = get_option (List.map
type_infos) ty in
469 Ast.rewrap
e (Ast.MetaExpr
(name
,constraints
,unitary,ty,form
,inherited))
470 | Ast.MetaExprList
(name
,None
,_
,_
) ->
471 (* lenname should have the same properties of being unitary or
473 let (unitary,inherited) = classify name
in
474 Ast.rewrap
e (Ast.MetaExprList
(name
,None
,unitary,inherited))
475 | Ast.MetaExprList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
476 (* lenname should have the same properties of being unitary or
478 let (unitary,inherited) = classify name
in
479 let (lenunitary
,leninherited
) = classify lenname
in
482 (name
,Some
(lenname
,lenunitary
,leninherited
),unitary,inherited))
487 match Ast.unwrap
e with
488 Ast.MetaType
(name
,_
,_
) ->
489 let (unitary,inherited) = classify name
in
490 Ast.rewrap
e (Ast.MetaType
(name
,unitary,inherited))
495 match Ast.unwrap
e with
496 Ast.MetaInit
(name
,_
,_
) ->
497 let (unitary,inherited) = classify name
in
498 Ast.rewrap
e (Ast.MetaInit
(name
,unitary,inherited))
503 match Ast.unwrap
e with
504 Ast.MetaParam
(name
,_
,_
) ->
505 let (unitary,inherited) = classify name
in
506 Ast.rewrap
e (Ast.MetaParam
(name
,unitary,inherited))
507 | Ast.MetaParamList
(name
,None
,_
,_
) ->
508 let (unitary,inherited) = classify name
in
509 Ast.rewrap
e (Ast.MetaParamList
(name
,None
,unitary,inherited))
510 | Ast.MetaParamList
(name
,Some
(lenname
,_
,_
),_
,_
) ->
511 let (unitary,inherited) = classify name
in
512 let (lenunitary
,leninherited
) = classify lenname
in
515 (name
,Some
(lenname
,lenunitary
,leninherited
),unitary,inherited))
518 let rule_elem r k
e =
520 match Ast.unwrap
e with
521 Ast.MetaStmt
(name
,_
,msi
,_
) ->
522 let (unitary,inherited) = classify name
in
523 Ast.rewrap
e (Ast.MetaStmt
(name
,unitary,msi
,inherited))
524 | Ast.MetaStmtList
(name
,_
,_
) ->
525 let (unitary,inherited) = classify name
in
526 Ast.rewrap
e (Ast.MetaStmtList
(name
,unitary,inherited))
530 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
531 donothing donothing donothing donothing
532 ident expression donothing typeC init param donothing rule_elem
533 donothing donothing donothing donothing in
535 List.map
fn.V.rebuilder_top_level minirules
537 (* ---------------------------------------------------------------- *)
539 (* For a minirule, collect the set of non-local (not in "bound") variables that
540 are referenced. Store them in a hash table. *)
542 (* bound means the metavariable was declared previously, not locally *)
544 (* Highly inefficient, because we call collect_all_refs on nested code
545 multiple times. But we get the advantage of not having too many variants
546 of the same functions. *)
548 (* Inherited doesn't include position constraints. If they are not bound
549 then there is no constraint. *)
551 let astfvs metavars bound
=
556 Ast.MetaFreshIdDecl
(_
,seed
) as x
->
557 ((Ast.get_meta_name x
),seed
)::prev
561 let collect_fresh l
=
562 let (matched
,freshvars
) =
564 (function (matched
,freshvars
) ->
566 try let v = List.assoc x
fresh in (matched
,(x
,v)::freshvars
)
567 with Not_found
-> (x
::matched
,freshvars
))
569 (List.rev matched
, List.rev freshvars
) in
571 (* cases for the elements of anything *)
572 let simple_setup getter k re
=
573 let minus_free = nub (getter
collect_all_refs re
) in
575 nub (getter
collect_non_constraint_refs re
) in
577 collect_fresh_seed metavars (getter
collect_in_plus_term re
) in
578 let free = Common.union_set
minus_free plus_free in
579 let nc_free = Common.union_set
minus_nc_free plus_free in
581 List.filter
(function x
-> not
(List.mem x bound
)) free in
583 List.filter
(function x
-> List.mem x bound
) nc_free in
585 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
586 let (matched
,fresh) = collect_fresh unbound in
588 Ast.free_vars
= matched
;
589 Ast.minus_free_vars
= munbound;
590 Ast.fresh_vars
= fresh;
591 Ast.inherited = inherited;
592 Ast.saved_witness
= []} in
594 let astfvrule_elem recursor k re
=
595 simple_setup (function x
-> x
.V.combiner_rule_elem
) k re
in
597 let astfvstatement recursor k s
=
598 let minus_free = nub (collect_all_refs.V.combiner_statement s
) in
600 nub (collect_non_constraint_refs.V.combiner_statement s
) in
602 collect_fresh_seed metavars
603 (collect_in_plus_term.V.combiner_statement s
) in
604 let free = Common.union_set
minus_free plus_free in
605 let nc_free = Common.union_set
minus_nc_free plus_free in
606 let classify free minus_free =
607 let (unbound,inherited) =
608 List.partition
(function x
-> not
(List.mem x bound
)) free in
610 List.filter
(function x
-> not
(List.mem x bound
)) minus_free in
611 let (matched
,fresh) = collect_fresh unbound in
612 (matched
,munbound,fresh,inherited) in
616 collect_fresh_seed metavars
617 (cip_mcodekind collect_in_plus_term aft
) in
618 match Ast.unwrap
res with
619 Ast.IfThen
(header
,branch
,(_
,_
,_
,aft
)) ->
620 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
621 Ast.IfThen
(header
,branch
,(unbound,fresh,inherited,aft
))
622 | Ast.IfThenElse
(header
,branch1
,els
,branch2
,(_
,_
,_
,aft
)) ->
623 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
624 Ast.IfThenElse
(header
,branch1
,els
,branch2
,
625 (unbound,fresh,inherited,aft
))
626 | Ast.While
(header
,body
,(_
,_
,_
,aft
)) ->
627 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
628 Ast.While
(header
,body
,(unbound,fresh,inherited,aft
))
629 | Ast.For
(header
,body
,(_
,_
,_
,aft
)) ->
630 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
631 Ast.For
(header
,body
,(unbound,fresh,inherited,aft
))
632 | Ast.Iterator
(header
,body
,(_
,_
,_
,aft
)) ->
633 let (unbound,_
,fresh,inherited) = classify (cip_plus aft
) [] in
634 Ast.Iterator
(header
,body
,(unbound,fresh,inherited,aft
))
637 let (matched
,munbound,fresh,_
) = classify free minus_free in
639 List.filter
(function x
-> List.mem x bound
) nc_free in
642 Ast.free_vars
= matched
;
643 Ast.minus_free_vars
= munbound;
644 Ast.fresh_vars
= fresh;
645 Ast.inherited = inherited;
646 Ast.saved_witness
= []} in
648 let astfvstatement_dots recursor k sd
=
649 simple_setup (function x
-> x
.V.combiner_statement_dots
) k sd
in
651 let astfvcase_line recursor k cl
=
652 simple_setup (function x
-> x
.V.combiner_case_line
) k cl
in
654 let astfvtoplevel recursor k tl
=
655 let saved = collect_saved.V.combiner_top_level tl
in
656 {(k tl
) with Ast.saved_witness
= saved} in
659 let donothing r k
e = k
e in
662 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
663 donothing donothing astfvstatement_dots donothing
664 donothing donothing donothing donothing donothing donothing donothing
665 astfvrule_elem astfvstatement astfvcase_line astfvtoplevel donothing
668 let collect_astfvs rules =
669 let rec loop bound = function
671 | (metavars,(nm,rule_info,minirules))::rules ->
673 Common.minus_set bound (List.map Ast.get_meta_name metavars) in
675 (List.map (astfvs metavars bound).V.rebuilder_top_level minirules))::
676 (loop ((List.map Ast.get_meta_name metavars)@bound) rules) in
680 let collect_astfvs rules
=
681 let rec loop bound = function
683 | (metavars, rule
)::rules
->
685 Ast.ScriptRule
(_
,_
,_
,_
)
686 | Ast.InitialScriptRule
(_
,_
) | Ast.FinalScriptRule
(_
,_
) ->
687 (* bound stays as is because script rules have no names, so no
688 inheritance is possible *)
689 rule
::(loop bound rules
)
690 | Ast.CocciRule
(nm
, rule_info
, minirules
, isexp
, ruletype
) ->
692 Common.minus_set
bound (List.map
Ast.get_meta_name
metavars) in
695 (List.map
(astfvs metavars bound).V.rebuilder_top_level
698 (loop ((List.map
Ast.get_meta_name
metavars)@bound) rules
) in
701 (* ---------------------------------------------------------------- *)
702 (* position variables that appear as a constraint on another position variable.
703 a position variable also cannot appear both positively and negatively in a
706 let get_neg_pos_list (_
,rule
) used_after_list
=
707 let donothing r k
e = k
e in
708 let bind (p1
,np1
) (p2
,np2
) =
709 (Common.union_set p1 p2
, Common.union_set np1 np2
) in
710 let option_default = ([],[]) in
711 let metaid (x
,_
,_
,_
) = x
in
713 match Ast.get_pos_var mc
with
714 Ast.MetaPos
(name
,constraints
,Ast.PER
,_
,_
) ->
715 ([metaid name
],constraints
)
716 | Ast.MetaPos
(name
,constraints
,Ast.ALL
,_
,_
) ->
717 ([],(metaid name
)::constraints
)
718 | _
-> option_default in
720 V.combiner
bind option_default
721 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
722 donothing donothing donothing donothing
723 donothing donothing donothing donothing donothing donothing
724 donothing donothing donothing donothing donothing donothing in
726 Ast.CocciRule
(_
,_
,minirules
,_
,_
) ->
728 (function toplevel
->
729 let (positions
,neg_positions
) = v.V.combiner_top_level toplevel
in
730 (if List.exists
(function p
-> List.mem p neg_positions
) positions
733 "a variable cannot be used both as a position and a constraint");
736 | Ast.ScriptRule _
| Ast.InitialScriptRule _
| Ast.FinalScriptRule _
->
737 (*no negated positions*) []
739 (* ---------------------------------------------------------------- *)
741 (* collect used after lists, per minirule *)
743 (* defined is a list of variables that were declared in a previous metavar
746 (* Top-level used after: For each rule collect the set of variables that
747 are inherited, ie used but not defined. These are accumulated back to
748 their point of definition. *)
751 let collect_top_level_used_after metavar_rule_list
=
752 let (used_after
,used_after_lists
) =
754 (function (metavar_list
,r
) ->
755 function (used_after
,used_after_lists
) ->
756 let locally_defined = List.map
Ast.get_meta_name metavar_list
in
757 let continue_propagation =
758 List.filter
(function x
-> not
(List.mem x
locally_defined))
762 Ast.ScriptRule
(_
,_
,mv
,_
) ->
763 List.map
(function (_
,(r
,v)) -> (r
,v)) mv
764 | Ast.InitialScriptRule
(_
,_
) | Ast.FinalScriptRule
(_
,_
) -> []
765 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
766 Common.union_set
(nub (collect_all_rule_refs rule
))
767 (collect_in_plus metavar_list rule
) in
769 List.filter
(function x
-> not
(List.mem x
locally_defined))
771 (Common.union_set
inherited continue_propagation,
772 used_after
::used_after_lists
))
773 metavar_rule_list
([],[]) in
774 match used_after
with
775 [] -> used_after_lists
778 (Printf.sprintf
"collect_top_level_used_after: unbound variables %s"
779 (String.concat
" " (List.map
(function (_
,x
) -> x
) used_after
)))
781 let collect_local_used_after metavars minirules used_after
=
782 let locally_defined = List.map
Ast.get_meta_name
metavars in
783 let rec loop = function
784 [] -> (used_after
,[],[],[],[])
786 (* In a rule there are three kinds of local variables:
787 1. Variables referenced in the minus or context code.
788 These get a value by matching. This value can be used in
790 2. Fresh variables referenced in the plus code.
791 3. Variables referenced in the seeds of the fresh variables.
792 There are also non-local variables. These may either be variables
793 referenced in the minus, context, or plus code, or they may be
794 variables referenced in the seeds of the fresh variables. *)
795 (* Step 1: collect all references in minus/context, plus, seed
797 let variables_referenced_in_minus_context_code =
798 nub (collect_all_minirule_refs minirule
) in
799 let variables_referenced_in_plus_code =
800 collect_in_plus_term.V.combiner_top_level minirule
in
801 let (env_of_fresh_seeds
,seeds_and_plus
) =
802 collect_fresh_seed_env
803 metavars variables_referenced_in_plus_code in
805 Common.union_set
variables_referenced_in_minus_context_code
807 (* Step 2: identify locally defined ones *)
808 let local_fresh = List.map fst env_of_fresh_seeds
in
810 List.partition
(function x
-> List.mem x
locally_defined) in
811 let local_env_of_fresh_seeds =
812 (* these have to be restricted to only one value if the associated
813 fresh variable is used after *)
814 List.map
(function (f
,ss
) -> (f
,is_local ss
)) env_of_fresh_seeds
in
815 let (local_all_free_vars
,nonlocal_all_free_vars
) =
816 is_local all_free_vars in
817 (* Step 3, recurse on the rest of the rules, making available whatever
818 has been defined in this one *)
819 let (mini_used_after
,fvs_lists
,mini_used_after_lists
,
820 mini_fresh_used_after_lists
,mini_fresh_used_after_seeds
) =
822 (* Step 4: collect the results. These are:
823 1. All of the variables used non-locally in the rules starting
825 2. All of the free variables to the end of the semantic patch
826 3. The variables that are used afterwards and defined here by
827 matching (minus or context code)
828 4. The variables that are used afterwards and are defined here as
830 5. The variables that are used as seeds in computing the bindings
831 of the variables collected in part 4. *)
832 let (local_used_after
, nonlocal_used_after
) =
833 is_local mini_used_after
in
834 let (fresh_local_used_after
(*4*),matched_local_used_after
) =
835 List.partition
(function x
-> List.mem x
local_fresh)
837 let matched_local_used_after(*3*) =
838 Common.union_set
matched_local_used_after nonlocal_used_after
in
839 let new_used_after = (*1*)
840 Common.union_set nonlocal_all_free_vars nonlocal_used_after
in
841 let fresh_local_used_after_seeds =
843 (* no point to keep variables that already are gtd to have only
845 (function x
-> not
(List.mem x
matched_local_used_after))
846 (List.fold_left
(function p
-> function c
-> Common.union_set c p
)
850 fst
(List.assoc fua
local_env_of_fresh_seeds))
851 fresh_local_used_after
)) in
852 (new_used_after,all_free_vars::fvs_lists
(*2*),
853 matched_local_used_after::mini_used_after_lists
,
854 fresh_local_used_after
::mini_fresh_used_after_lists
,
855 fresh_local_used_after_seeds::mini_fresh_used_after_seeds
) in
856 let (_
,fvs_lists
,used_after_lists
(*ua*),
857 fresh_used_after_lists
(*fua*),fresh_used_after_lists_seeds
(*fuas*)) =
859 (fvs_lists
,used_after_lists
,
860 fresh_used_after_lists
,fresh_used_after_lists_seeds
)
864 let collect_used_after metavar_rule_list
=
865 let used_after_lists = collect_top_level_used_after metavar_rule_list
in
867 (function (metavars,r
) ->
868 function used_after
->
870 Ast.ScriptRule
(_
,_
,_
,_
)
871 | Ast.InitialScriptRule
(_
,_
) | Ast.FinalScriptRule
(_
,_
) ->
872 ([], [used_after
], [], [])
873 | Ast.CocciRule
(name
, rule_info
, minirules
, _
,_
) ->
874 collect_local_used_after metavars minirules used_after
876 metavar_rule_list
used_after_lists
878 let rec split4 = function
880 | (a
,b
,c
,d
)::l
-> let (a1
,b1
,c1
,d1
) = split4 l
in (a
::a1
,b
::b1
,c
::c1
,d
::d1
)
882 (* ---------------------------------------------------------------- *)
885 let free_vars rules
=
886 let metavars = List.map
(function (mv
,rule
) -> mv
) rules
in
887 let (fvs_lists
,used_after_matched_lists
,
888 fresh_used_after_lists
,fresh_used_after_lists_seeds
) =
889 split4 (collect_used_after rules
) in
891 List.map2
get_neg_pos_list rules used_after_matched_lists
in
892 let positions_list = (* for all rules, assume all positions are used after *)
897 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> []
898 | Ast.CocciRule
(_
,_
,rule
,_
,_
) ->
902 function Ast.MetaPosDecl
(_
,nm
) -> nm
::prev
| _
-> prev
)
904 List.map
(function _
-> positions) rule
)
912 | Ast.InitialScriptRule _
| Ast.FinalScriptRule _
-> r
913 | Ast.CocciRule
(nm
, rule_info
, r
, is_exp
,ruletype
) ->
916 classify_variables mv r
917 ((List.concat ua
) @ (List.concat fua
)),
919 rules
(List.combine used_after_matched_lists fresh_used_after_lists
) in
920 let new_rules = collect_astfvs (List.combine
metavars new_rules) in
922 fvs_lists
,neg_pos_lists,
923 (used_after_matched_lists
,
924 fresh_used_after_lists
,fresh_used_after_lists_seeds
),