Release coccinelle-0.1
[bpt/coccinelle.git] / parsing_cocci / get_constants2.ml
CommitLineData
34e49164
C
1(*
2* Copyright 2005-2008, Ecole des Mines de Nantes, University of Copenhagen
3* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller
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
23module Ast = Ast_cocci
24module V = Visitor_ast
25module TC = Type_cocci
26
27(* Issues:
28
291. If a rule X depends on a rule Y (in a positive way), then we can ignore
30 the constants in X.
31
322. If a rule X contains a metavariable that is not under a disjunction and
33 that is inherited from rule Y, then we can ignore the constants in X.
34
353. If a rule contains a constant x in + code then subsequent rules that
36 have it in - or context should not include it in their list of required
37 constants.
38*)
39
40(* ----------------------------------------------------------------------- *)
41(* This phase collects everything. One can then filter out what it not
42wanted *)
43
44(* True means nothing was found
45 False should never drift to the top, it is the neutral element of or
46 and an or is never empty *)
47type combine =
48 And of combine list | Or of combine list | Elem of string | False | True
49
50let interpret strict x =
51 let rec loop = function
52 Elem x -> x
53 | And [x] -> loop x
54 | Or [x] -> loop x
55 | And l -> Printf.sprintf "{%s}" (String.concat ";" (List.map loop l))
56 | Or l -> Printf.sprintf "{%s}" (String.concat "," (List.map loop l))
57 | True -> "True"
58 | False ->
59 if strict
60 then failwith "False should not be in the final result. Perhaps your rule doesn't contain any +/-/* code"
61 else "False" in
62 match x with
63 True -> None
64 | False when strict ->
65 failwith "False should not be in the final result. Perhaps your rule doesn't contain any +/-/* code"
66 | _ -> Some (loop x)
67
68let combine2c x =
69 match interpret false x with
70 None -> "None"
71 | Some x -> x
72
73let norm = function
74 And l -> And (List.sort compare l)
75 | Or l -> Or (List.sort compare l)
76 | x -> x
77
78let rec merge l1 l2 =
79 match (l1,l2) with
80 ([],l2) -> l2
81 | (l1,[]) -> l1
82 | (x::xs,y::ys) ->
83 (match compare x y with
84 -1 -> x::(merge xs l2)
85 | 0 -> x::(merge xs ys)
86 | 1 -> y::(merge l1 ys)
87 | _ -> failwith "not possible")
88
89let intersect l1 l2 = List.filter (function l1e -> List.mem l1e l2) l1
90
91let minus_set l1 l2 = List.filter (function l1e -> not (List.mem l1e l2)) l1
92
93let rec insert x l = merge [x] l
94
95let rec build_and x y =
96 if x = y
97 then x
98 else
99 match (x,y) with
100 (True,x) | (x,True) -> x
101 | (False,x) | (x,False) -> False
102 | (And l1,And l2) -> And (merge l1 l2)
103 | (x,Or l) when List.mem x l -> x
104 | (Or l,x) when List.mem x l -> x
105 | (Or l1,Or l2) when not ((intersect l1 l2) = []) ->
106 let inner =
107 build_and
108 (List.fold_left build_or False (minus_set l1 l2))
109 (List.fold_left build_or False (minus_set l2 l1)) in
110 List.fold_left build_or inner (intersect l1 l2)
111 | (x,And l) | (And l,x) ->
112 if List.mem x l
113 then And l
114 else
115 let others =
116 List.filter
117 (function
118 Or l -> not(List.mem x l)
119 | _ -> true)
120 l in
121 And (insert x others)
122 | (x,y) -> norm(And [x;y])
123
124and build_or x y =
125 if x = y
126 then x
127 else
128 match (x,y) with
129 (True,x) | (x,True) -> True
130 | (False,x) | (x,False) -> x
131 | (Or l1,Or l2) -> Or (merge l1 l2)
132 | (x,And l) when List.mem x l -> x
133 | (And l,x) when List.mem x l -> x
134 | (And l1,And l2) when not ((intersect l1 l2) = []) ->
135 let inner =
136 build_or
137 (List.fold_left build_and True (minus_set l1 l2))
138 (List.fold_left build_and True (minus_set l2 l1)) in
139 List.fold_left build_and inner (intersect l1 l2)
140 | (x,Or l) | (Or l,x) ->
141 if List.mem x l
142 then Or l
143 else
144 let others =
145 List.filter
146 (function
147 And l -> not(List.mem x l)
148 | _ -> true)
149 l in
150 Or (insert x others)
151 | (x,y) -> norm(Or [x;y])
152
153let keep x = Elem x
154let drop x = True
155
156let do_get_constants constants keywords env neg_pos =
157 let donothing r k e = k e in
158 let option_default = True in
159 let bind = build_and in
160 let inherited ((nm1,_) as x) =
161 (* perhaps inherited, but value not required, so no constraints *)
162 if List.mem x neg_pos then option_default
163 else try List.assoc nm1 env with Not_found -> False in
164 let minherited name = inherited (Ast.unwrap_mcode name) in
165 let mcode _ x =
166 match Ast.get_pos_var x with
167 Ast.MetaPos(name,constraints,_,keep,inh) -> minherited name
168 | _ -> option_default in
169
170 (* if one branch gives no information, then we have to take anything *)
171 let disj_union_all = List.fold_left build_or False in
172
173 let ident r k i =
174 match Ast.unwrap i with
175 Ast.Id(name) ->
176 bind (k i)
177 (match Ast.unwrap_mcode name with
178 "NULL" -> keywords "NULL"
179 | nm -> constants nm)
180 | Ast.MetaId(name,_,_,_) | Ast.MetaFunc(name,_,_,_)
181 | Ast.MetaLocalFunc(name,_,_,_) -> bind (k i) (minherited name)
182 | _ -> k i in
183
184 let rec type_collect res = function
185 TC.ConstVol(_,ty) | TC.Pointer(ty) | TC.FunctionPointer(ty)
186 | TC.Array(ty) -> type_collect res ty
187 | TC.MetaType(tyname,_,_) -> inherited tyname
188 | TC.TypeName(s) -> constants s
189 | TC.StructUnionName(_,false,s) -> constants s
190 | ty -> res in
191
192 (* no point to do anything special for records because glimpse is
193 word-oriented *)
194 let expression r k e =
195 match Ast.unwrap e with
196 Ast.Constant(const) ->
197 bind (k e)
198 (match Ast.unwrap_mcode const with
199 Ast.String s -> constants s
200 | Ast.Char "\\0" -> option_default (* glimpse doesn't like it *)
201 | Ast.Char s -> constants s
202 (* the following were eg keywords "1", but not good for glimpse *)
203 | Ast.Int "0" -> option_default (* glimpse doesn't like it *)
204 | Ast.Int "1" -> option_default (* glimpse doesn't like it *)
205 | Ast.Int s -> constants s
206 | Ast.Float s -> constants s)
207 | Ast.MetaExpr(name,_,_,Some type_list,_,_) ->
208 let types = List.fold_left type_collect option_default type_list in
209 bind (k e) (bind (minherited name) types)
210 | Ast.MetaErr(name,_,_,_) | Ast.MetaExpr(name,_,_,_,_,_) ->
211 bind (k e) (minherited name)
212 | Ast.MetaExprList(name,None,_,_) -> minherited name
213 | Ast.MetaExprList(name,Some (lenname,_,_),_,_) ->
214 bind (k e) (bind (minherited name) (minherited lenname))
215 | Ast.SizeOfExpr(sizeof,exp) -> bind (keywords "sizeof") (k e)
216 | Ast.SizeOfType(sizeof,lp,ty,rp) -> bind (keywords "sizeof") (k e)
217 | Ast.NestExpr(expr_dots,wc,false) -> option_default
218 | Ast.NestExpr(expr_dots,wc,true) ->
219 r.V.combiner_expression_dots expr_dots
220 | Ast.DisjExpr(exps) ->
221 disj_union_all (List.map r.V.combiner_expression exps)
222 | Ast.OptExp(exp) -> option_default
223 | Ast.Edots(_,_) | Ast.Ecircles(_,_) | Ast.Estars(_,_) -> option_default
224 | _ -> k e in
225
226 let fullType r k ft =
227 match Ast.unwrap ft with
228 Ast.DisjType(decls) ->
229 disj_union_all (List.map r.V.combiner_fullType decls)
230 | Ast.OptType(ty) -> option_default
231 | _ -> k ft in
232
233 let baseType = function
234 Ast.VoidType -> keywords "void "
235 | Ast.CharType -> keywords "char "
236 | Ast.ShortType -> keywords "short "
237 | Ast.IntType -> keywords "int "
238 | Ast.DoubleType -> keywords "double "
239 | Ast.FloatType -> keywords "float "
240 | Ast.LongType -> keywords "long " in
241
242 let typeC r k ty =
243 match Ast.unwrap ty with
244 Ast.BaseType(ty1,sgn) -> bind (k ty) (baseType (Ast.unwrap_mcode ty1))
245 | Ast.TypeName(name) -> bind (k ty) (constants (Ast.unwrap_mcode name))
246 | Ast.MetaType(name,_,_) -> bind (minherited name) (k ty)
247 | _ -> k ty in
248
249 let declaration r k d =
250 match Ast.unwrap d with
251 Ast.DisjDecl(decls) ->
252 disj_union_all (List.map r.V.combiner_declaration decls)
253 | Ast.OptDecl(decl) -> option_default
254 | Ast.Ddots(dots,whencode) -> option_default
255 | _ -> k d in
256
257 let initialiser r k i =
258 match Ast.unwrap i with
259 Ast.OptIni(ini) -> option_default
260 | _ -> k i in
261
262 let parameter r k p =
263 match Ast.unwrap p with
264 Ast.OptParam(param) -> option_default
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 (minherited name) (bind (minherited lenname) (k p))
269 | _ -> k p in
270
271 let rule_elem r k re =
272 match Ast.unwrap re with
273 Ast.MetaRuleElem(name,_,_) | Ast.MetaStmt(name,_,_,_)
274 | Ast.MetaStmtList(name,_,_) -> bind (minherited name) (k re)
275 | Ast.WhileHeader(whl,lp,exp,rp) ->
276 bind (keywords "while") (k re)
277 | Ast.WhileTail(whl,lp,exp,rp,sem) ->
278 bind (keywords "do") (k re)
279 | Ast.ForHeader(fr,lp,e1,sem1,e2,sem2,e3,rp) ->
280 bind (keywords "for") (k re)
281 | Ast.SwitchHeader(switch,lp,exp,rp) ->
282 bind (keywords "switch") (k re)
283 | Ast.Break(br,sem) ->
284 bind (keywords "break") (k re)
285 | Ast.Continue(cont,sem) ->
286 bind (keywords "continue") (k re)
287 | Ast.Goto(_,i,_) ->
288 bind (keywords "goto") (k re)
289 | Ast.Default(def,colon) ->
290 bind (keywords "default") (k re)
291 | Ast.Include(inc,s) ->
292 bind (k re)
293 (match Ast.unwrap_mcode s with
294 Ast.Local l | Ast.NonLocal l ->
295 let strings =
296 List.fold_left
297 (function prev ->
298 function
299 (* just take the last thing, probably the most
300 specific. everything is necessary anyway. *)
301 Ast.IncPath s -> [Elem s]
302 | Ast.IncDots -> prev)
303 [] l in
304 (match strings with
305 [] -> True
306 | x::xs -> List.fold_left bind x xs))
307 | Ast.DisjRuleElem(res) ->
308 disj_union_all (List.map r.V.combiner_rule_elem res)
309 | _ -> k re in
310
311 let statement r k s =
312 match Ast.unwrap s with
313 Ast.Disj(stmt_dots) ->
314 disj_union_all (List.map r.V.combiner_statement_dots stmt_dots)
315 | Ast.Nest(stmt_dots,whn,false,_,_) -> option_default
316 | Ast.Nest(stmt_dots,whn,true,_,_) ->
317 r.V.combiner_statement_dots stmt_dots
318 | Ast.OptStm(s) -> option_default
319 | Ast.Dots(d,whn,_,_) | Ast.Circles(d,whn,_,_) | Ast.Stars(d,whn,_,_) ->
320 option_default
321 | _ -> k s in
322
323 V.combiner bind option_default
324 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
325 mcode
326 donothing donothing donothing donothing
327 ident expression fullType typeC initialiser parameter declaration
328 rule_elem statement donothing donothing donothing
329
330(* ------------------------------------------------------------------------ *)
331
332let filter_combine combine to_drop =
333 let rec and_loop = function
334 Elem x when List.mem x to_drop -> True
335 | Or l -> List.fold_left build_or False (List.map or_loop l)
336 | x -> x
337 and or_loop = function
338 Elem x when List.mem x to_drop -> False
339 | And l -> List.fold_left build_and True (List.map and_loop l)
340 | x -> x in
341 or_loop combine
342
343(* ------------------------------------------------------------------------ *)
344
345let get_all_constants minus_only =
346 let donothing r k e = k e in
347 let bind = Common.union_set in
348 let option_default = [] in
349 let mcode r (x,_,mcodekind,_) =
350 match mcodekind with
351 Ast.MINUS(_,_) -> [x]
352 | _ when minus_only -> []
353 | _ -> [x] in
354 let other r _ = [] in
355
356 V.combiner bind option_default
357 other mcode other other other other other other other other other other
358 other
359
360 donothing donothing donothing donothing
361 donothing donothing donothing donothing donothing donothing donothing
362 donothing donothing donothing donothing donothing
363
364(* ------------------------------------------------------------------------ *)
365
366let get_plus_constants =
367 let donothing r k e = k e in
368 let bind = Common.union_set in
369 let option_default = [] in
370 let mcode r mc =
371 let mcodekind = Ast.get_mcodekind mc in
372 let recurse l =
373 List.fold_left
374 (List.fold_left
375 (function prev ->
376 function cur ->
377 bind ((get_all_constants false).V.combiner_anything cur) prev))
378 [] l in
379 match mcodekind with
380 Ast.MINUS(_,anythings) -> recurse anythings
381 | Ast.CONTEXT(_,Ast.BEFORE(a)) -> recurse a
382 | Ast.CONTEXT(_,Ast.AFTER(a)) -> recurse a
383 | Ast.CONTEXT(_,Ast.BEFOREAFTER(a1,a2)) ->
384 Common.union_set (recurse a1) (recurse a2)
385 | _ -> [] in
386
387 V.combiner bind option_default
388 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
389 mcode
390 donothing donothing donothing donothing
391 donothing donothing donothing donothing donothing donothing donothing
392 donothing donothing donothing donothing donothing
393
394(* ------------------------------------------------------------------------ *)
395
396(* true means the rule should be analyzed, false means it should be ignored *)
397let rec dependencies env = function
398 Ast.Dep s -> (try List.assoc s env with Not_found -> False)
399 | Ast.AntiDep s -> True
400 | Ast.EverDep s -> (try List.assoc s env with Not_found -> False)
401 | Ast.NeverDep s -> True
402 | Ast.AndDep (d1,d2) -> build_and (dependencies env d1) (dependencies env d2)
403 | Ast.OrDep (d1,d2) -> build_or (dependencies env d1) (dependencies env d2)
404 | Ast.NoDep -> True
405
406(* ------------------------------------------------------------------------ *)
407
408let all_context =
409 let bind x y = x && y in
410 let option_default = true in
411
412 let donothing recursor k e = k e in
413
414 let mcode r e =
415 match Ast.get_mcodekind e with
416 Ast.CONTEXT(_,Ast.NOTHING) -> true
417 | _ -> false in
418
419 V.combiner bind option_default
420 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
421 mcode
422 donothing donothing donothing donothing
423 donothing donothing donothing donothing donothing donothing
424 donothing donothing donothing donothing donothing donothing
425
426(* ------------------------------------------------------------------------ *)
427
428let rule_fn tls in_plus env neg_pos =
429 List.fold_left
430 (function (rest_info,in_plus) ->
431 function (cur,neg_pos) ->
432 let minuses =
433 (do_get_constants keep drop env neg_pos).V.combiner_top_level cur in
434 let all_minuses =
435 if !Flag.sgrep_mode2
436 then [] (* nothing removed for sgrep *)
437 else (get_all_constants true).V.combiner_top_level cur in
438 let plusses = get_plus_constants.V.combiner_top_level cur in
439 (* the following is for eg -foo(2) +foo(x) then in another rule
440 -foo(10); don't want to consider that foo is guaranteed to be
441 created by the rule. not sure this works completely: what if foo is
442 in both - and +, but in an or, so the cases aren't related?
443 not sure this whole thing is a good idea. how do we know that
444 something that is only in plus is really freshly created? *)
445 let plusses = Common.minus_set plusses all_minuses in
446 let was_bot = minuses = True in
447 let new_minuses = filter_combine minuses in_plus in
448 let new_plusses = Common.union_set plusses in_plus in
449 (* perhaps it should be build_and here? we don't realy have multiple
450 minirules anymore anyway. *)
451 match new_minuses with
452 True ->
453 let retry =
454 (do_get_constants drop keep env neg_pos).V.combiner_top_level
455 cur in
456 (match retry with
457 True when not was_bot -> (rest_info, new_plusses)
458 | x -> (build_or x rest_info, new_plusses))
459 | x -> (build_or x rest_info, new_plusses))
460 (False,in_plus) (List.combine tls neg_pos)
461
462let get_constants rules neg_pos_vars =
463 if not !Flag.use_glimpse
464 then None
465 else
466 let (info,_,_,_) =
467 List.fold_left
468 (function (rest_info,in_plus,env,locals(*dom of env*)) ->
469 function
470 (Ast.ScriptRule (_,deps,mv,_),_) ->
471 let extra_deps =
472 List.fold_left
473 (function prev ->
474 function (_,(rule,_)) -> Ast.AndDep (Ast.Dep rule,prev))
475 deps mv in
476 (match dependencies env extra_deps with
477 False -> (rest_info, in_plus, env, locals)
478 | dependencies ->
479 (build_or dependencies rest_info, in_plus, env, locals))
480 | (Ast.CocciRule (nm,(dep,_,_),cur,_),neg_pos_vars) ->
481 let (cur_info,cur_plus) =
482 rule_fn cur in_plus ((nm,True)::env) neg_pos_vars in
483 if List.for_all all_context.V.combiner_top_level cur
484 then (rest_info,cur_plus,(nm,cur_info)::env,nm::locals)
485 else
486 (* no constants if dependent on another rule; then we need to
487 find the constants of that rule *)
488 match dependencies env dep with
489 False -> (rest_info,cur_plus,env,locals)
490 | dependencies ->
491 (build_or (build_and dependencies cur_info) rest_info,
492 cur_plus,env,locals))
493 (False,[],[],[]) (List.combine (rules : Ast.rule list) neg_pos_vars) in
494 interpret true info