Release coccinelle-0.2.4
[bpt/coccinelle.git] / parsing_cocci / get_constants.ml
CommitLineData
9bc82bae
C
1(*
2 * Copyright 2010, INRIA, University of Copenhagen
3 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
4 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
5 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
6 * This file is part of Coccinelle.
7 *
8 * Coccinelle is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, according to version 2 of the License.
11 *
12 * Coccinelle is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * The authors reserve the right to distribute this or future versions of
21 * Coccinelle under other licenses.
22 *)
23
24
c491d8ee
C
25(*
26 * Copyright 2010, INRIA, University of Copenhagen
27 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
28 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
29 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
30 * This file is part of Coccinelle.
31 *
32 * Coccinelle is free software: you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation, according to version 2 of the License.
35 *
36 * Coccinelle is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
43 *
44 * The authors reserve the right to distribute this or future versions of
45 * Coccinelle under other licenses.
46 *)
47
48
34e49164
C
49(* get a list of all of the constants in the - slice of a SmPL file, to be
50used to select which files to process *)
51
52(* This could be made more efficient, by finding only the important things.
53eg, if we have a function and its arguments, we could just pick the function.
54And we could try to pick only the things annotated with -, and only pick
55something else if there is no -. In general, we only want the most important
56constant, not all the constants. *)
57
58module Ast = Ast_cocci
59module V = Visitor_ast
60module TC = Type_cocci
61
62let keep_some_bind x y = match x with [] -> y | _ -> x
63let or_bind x y = match x with [] -> [] | _ -> x
64let keep_all_bind = Common.union_set
65
66let get_minus_constants bind orbind =
67 let donothing r k e = k e in
68 let option_default = [] in
69 let mcode _ _ = option_default in
70
71 (* if one branch gives no information, then we have to take anything *)
72 let disj_union_all l =
73 if List.exists (function [] -> true | _ -> false) l
74 then orbind [] (Common.union_all l)
75 else Common.union_all l in
76
77 (* need special cases for everything with a disj, because the bind above
78 would throw away all but the first disj *)
79
80 let ident r k e =
81 match Ast.unwrap e with
82 Ast.Id(name) ->
83 (match Ast.unwrap_mcode name with
84 "NULL" -> [] (* special case, because this is too generic *)
85 | nm -> [nm])
86 | _ -> k e in
87
88 let expression r k e =
89 match Ast.unwrap e with
90 Ast.RecordAccess(exp,_,fld) | Ast.RecordPtAccess(exp,_,fld) ->
91 bind
92 (Common.union_all
93 (List.map (function id -> ["."^id;"->"^id])
94 (r.V.combiner_ident fld)))
95 (r.V.combiner_expression exp)
96 | Ast.SizeOfExpr(sizeof,_) | Ast.SizeOfType(sizeof,_,_,_) ->
97 bind (k e) [Ast.unwrap_mcode sizeof]
98 | Ast.DisjExpr(exps) ->
99 disj_union_all (List.map r.V.combiner_expression exps)
100 | Ast.Edots(_,_) | Ast.Ecircles(_,_) | Ast.Estars(_,_) -> []
5636bb2c
C
101 | Ast.NestExpr(starter,expr_dots,ender,whencode,false) -> []
102 | Ast.NestExpr(starter,expr_dots,ender,whencode,true) ->
34e49164
C
103 r.V.combiner_expression_dots expr_dots
104 | _ -> k e in
105
106 let typeC r k e =
107 match Ast.unwrap e with
108 Ast.TypeName(ty) ->
109 if !Flag.sgrep_mode2
110 then
111 match ty with
708f4980 112 (_,_,Ast.MINUS(_,_,_,_),_) -> [Ast.unwrap_mcode ty]
34e49164
C
113 | _ -> []
114 else [Ast.unwrap_mcode ty]
115 | _ -> k e in
116
117 let fullType r k e =
118 match Ast.unwrap e with
119 Ast.DisjType(types) ->
120 disj_union_all (List.map r.V.combiner_fullType types)
121 | _ -> k e in
122
123 let declaration r k e =
124 match Ast.unwrap e with
125 Ast.DisjDecl(decls) ->
126 disj_union_all (List.map r.V.combiner_declaration decls)
127 | Ast.Ddots(dots,whencode) -> []
128 | _ -> k e in
129
130 let rule_elem r k e =
131 match Ast.unwrap e with
132 Ast.DisjRuleElem(res) ->
133 disj_union_all (List.map r.V.combiner_rule_elem res)
134 | _ -> k e in
135
136 let statement r k e =
137 match Ast.unwrap e with
138 Ast.Disj(stmt_dots) ->
139 disj_union_all (List.map r.V.combiner_statement_dots stmt_dots)
140 | Ast.Dots(d,whn,_,_) | Ast.Circles(d,whn,_,_) | Ast.Stars(d,whn,_,_) -> []
5636bb2c
C
141 | Ast.Nest(starter,stmt_dots,ender,whn,false,_,_) -> []
142 | Ast.Nest(starter,stmt_dots,ender,whn,true,_,_) ->
143 r.V.combiner_statement_dots stmt_dots
34e49164
C
144 | _ -> k e in
145
146 V.combiner bind option_default
147 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
148 donothing donothing donothing donothing
149 ident expression fullType typeC donothing donothing declaration
150 rule_elem statement donothing donothing donothing
151
152(* ------------------------------------------------------------------------ *)
153
154let get_all_minus_constants =
155 let donothing r k e = k e in
156 let bind = Common.union_set in
157 let option_default = [] in
158 let mcode r (x,_,mcodekind,_) =
159 match mcodekind with
708f4980 160 Ast.MINUS(_,_,_,_) -> [x]
34e49164
C
161 | _ -> [] in
162 let other r (x,_,mcodekind,_) = [] in
163
164 V.combiner bind option_default
165 other mcode other other other other other other other other other other
34e49164
C
166
167 donothing donothing donothing donothing
168 donothing donothing donothing donothing donothing donothing donothing
169 donothing donothing donothing donothing donothing
170(* ------------------------------------------------------------------------ *)
171
172let get_plus_constants =
173 let donothing r k e = k e in
174 let bind = Common.union_set in
175 let option_default = [] in
176 let mcode r (_,_,mcodekind,_) =
177 let recurse l =
178 List.fold_left
179 (List.fold_left
180 (function prev ->
181 function cur ->
182 let fn = get_minus_constants keep_all_bind keep_all_bind in
183 bind (fn.V.combiner_anything cur) prev))
184 [] l in
185 match mcodekind with
708f4980 186 Ast.MINUS(_,_,_,anythings) -> recurse anythings
951c7801
C
187 | Ast.CONTEXT(_,Ast.BEFORE(a,_)) -> recurse a
188 | Ast.CONTEXT(_,Ast.AFTER(a,_)) -> recurse a
189 | Ast.CONTEXT(_,Ast.BEFOREAFTER(a1,a2,_)) ->
34e49164
C
190 Common.union_set (recurse a1) (recurse a2)
191 | _ -> [] in
192
193 V.combiner bind option_default
194 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
195 donothing donothing donothing donothing
196 donothing donothing donothing donothing donothing donothing donothing
197 donothing donothing donothing donothing donothing
198
199(* ------------------------------------------------------------------------ *)
200(* see if there are any inherited variables that must be bound for this rule
201to match *)
202
203let check_inherited nm =
204 let donothing r k e = k e in
205 let option_default = false in
206 let bind x y = x or y in
207 let inherited (nm1,_) = not(nm = nm1) in
208 let minherited mc = inherited (Ast.unwrap_mcode mc) in
209 let mcode _ x =
210 match Ast.get_pos_var x with
211 Ast.MetaPos(name,constraints,_,keep,inh) -> minherited name
212 | _ -> option_default in
213
214 (* a case for everything for there is a metavariable, also disjunctions
215 or optional things *)
216
217 let strictident recursor k i =
218 match Ast.unwrap i with
219 Ast.MetaId(name,_,_,_) | Ast.MetaFunc(name,_,_,_)
220 | Ast.MetaLocalFunc(name,_,_,_) -> bind (k i) (minherited name)
221 | _ -> k i in
222
223 let rec type_collect res = function
224 TC.ConstVol(_,ty) | TC.Pointer(ty) | TC.FunctionPointer(ty)
225 | TC.Array(ty) -> type_collect res ty
ae4735db
C
226 | TC.MetaType(tyname,_,_) ->
227 inherited tyname
34e49164
C
228 | ty -> res in
229
230 let strictexpr recursor k e =
231 match Ast.unwrap e with
232 Ast.MetaExpr(name,_,_,Some type_list,_,_) ->
233 let types = List.fold_left type_collect option_default type_list in
234 bind (minherited name) types
235 | Ast.MetaErr(name,_,_,_) | Ast.MetaExpr(name,_,_,_,_,_) ->
236 bind (k e) (minherited name)
237 | Ast.MetaExprList(name,None,_,_) -> bind (k e) (minherited name)
238 | Ast.MetaExprList(name,Some (lenname,_,_),_,_) ->
239 bind (k e) (bind (minherited name) (minherited lenname))
240 | Ast.DisjExpr(exps) ->
241 (* could see if there are any variables that appear in all branches,
242 but perhaps not worth it *)
243 option_default
244 | _ -> k e in
245
246 let strictdecls recursor k d =
247 match Ast.unwrap d with
413ffc02
C
248 Ast.MetaDecl(name,_,_) | Ast.MetaField(name,_,_) ->
249 bind (k p) (minherited name)
250 | Ast.DisjDecl(decls) -> option_default
34e49164
C
251 | _ -> k d in
252
253 let strictfullType recursor k ty =
254 match Ast.unwrap ty with
255 Ast.DisjType(types) -> option_default
256 | _ -> k ty in
257
258 let stricttypeC recursor k ty =
259 match Ast.unwrap ty with
260 Ast.MetaType(name,_,_) -> bind (k ty) (minherited name)
261 | _ -> k ty in
262
263 let strictparam recursor k p =
264 match Ast.unwrap p with
265 Ast.MetaParam(name,_,_) -> bind (k p) (minherited name)
266 | Ast.MetaParamList(name,None,_,_) -> bind (k p) (minherited name)
267 | Ast.MetaParamList(name,Some(lenname,_,_),_,_) ->
268 bind (k p) (bind (minherited name) (minherited lenname))
269 | _ -> k p in
270
271 let strictrule_elem recursor k re =
272 (*within a rule_elem, pattern3 manages the coherence of the bindings*)
273 match Ast.unwrap re with
274 Ast.MetaRuleElem(name,_,_) | Ast.MetaStmt(name,_,_,_)
275 | Ast.MetaStmtList(name,_,_) -> bind (k re) (minherited name)
276 | _ -> k re in
277
278 let strictstatement recursor k s =
279 match Ast.unwrap s with
280 Ast.Disj(stms) -> option_default
281 | _ -> k s in
282
283 V.combiner bind option_default
284 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
285 donothing donothing donothing donothing
286 strictident strictexpr strictfullType stricttypeC donothing strictparam
287 strictdecls strictrule_elem strictstatement donothing donothing donothing
288
289(* ------------------------------------------------------------------------ *)
290
291let rec dependent = function
292 Ast.Dep s -> true
293 | Ast.AntiDep s -> false
294 | Ast.EverDep s -> true
295 | Ast.NeverDep s -> false
296 | Ast.AndDep (d1,d2) -> dependent d1 or dependent d2
297 | Ast.OrDep (d1,d2) -> dependent d1 && dependent d2
298 | Ast.NoDep -> false
7f004419 299 | Ast.FailDep -> true
34e49164
C
300
301(* ------------------------------------------------------------------------ *)
302
303let rule_fn tls in_plus =
304 List.fold_left
305 (function (rest_info,in_plus) ->
306 function cur ->
307 let mfn = get_minus_constants keep_some_bind or_bind in
308 let minuses = mfn.V.combiner_top_level cur in
309 let all_minuses =
310 if !Flag.sgrep_mode2
311 then [] (* nothing removed for sgrep *)
312 else get_all_minus_constants.V.combiner_top_level cur in
313 let plusses = get_plus_constants.V.combiner_top_level cur in
314 (* the following is for eg -foo(2) +foo(x) then in another rule
315 -foo(10); don't want to consider that foo is guaranteed to be
316 created by the rule. not sure this works completely: what if foo is
317 in both - and +, but in an or, so the cases aren't related?
318 not sure this whole thing is a good idea. how do we know that
319 something that is only in plus is really freshly created? *)
320 let plusses = Common.minus_set plusses all_minuses in
321 let new_minuses = Common.minus_set minuses in_plus in
322 let new_plusses = Common.union_set plusses in_plus in
323 (Common.union_set new_minuses rest_info, new_plusses))
324 ([],in_plus) tls
325
326exception No_info
327
328let get_constants rules =
329 try
330 let (info,_) =
331 List.fold_left
332 (function (rest_info,in_plus) ->
333 function r ->
334 match r with
b1b2de81 335 Ast.ScriptRule (_,_,_,_)
c3e37e97 336 | Ast.InitialScriptRule (_,_,_) | Ast.FinalScriptRule (_,_,_) ->
b1b2de81 337 (rest_info, in_plus)
faf9a90c 338 | Ast.CocciRule (nm, (dep,_,_), cur, _, _) ->
34e49164
C
339 let (cur_info,cur_plus) = rule_fn cur in_plus in
340 let cur_info =
341 (* no dependencies if dependent on another rule; then we
342 need to find the constants of that rule *)
343 if dependent dep or
344 List.for_all (check_inherited nm).V.combiner_top_level cur
345 then []
346 else
347 if cur_info = [] then raise No_info else cur_info in
348 (Common.union_set [cur_info] rest_info,cur_plus))
349 ([],[]) rules in
350 List.rev info
351 with No_info -> List.map (function _ -> []) rules