Release coccinelle-0.2.2-rc1
[bpt/coccinelle.git] / parsing_cocci / get_constants2.ml
CommitLineData
9f8e26f4 1(*
ae4735db 2 * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
9f8e26f4
C
3 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
4 * This file is part of Coccinelle.
5 *
6 * Coccinelle is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, according to version 2 of the License.
9 *
10 * Coccinelle is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
20 *)
21
22
34e49164
C
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
951c7801 50let interpret_glimpse strict x =
34e49164
C
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"
951c7801
C
66 | _ -> Some [(loop x)]
67
68let interpret_google strict x =
69 (* convert to dnf *)
70 let rec dnf = function
71 Elem x -> [x]
72 | Or l -> List.fold_left Common.union_set [] (List.map dnf l)
73 | And l ->
74 let l = List.map dnf l in
75 List.fold_left
76 (function prev ->
77 function cur ->
78 List.fold_left Common.union_set []
79 (List.map
80 (function x ->
81 List.map (function y -> Printf.sprintf "%s %s" x y) prev)
82 cur))
83 [] l
84 | True -> ["True"]
85 | False ->
86 if strict
87 then failwith "False should not be in the final result. Perhaps your rule doesn't contain any +/-/* code"
88 else ["False"] in
89 match x with
90 True -> None
91 | False when strict ->
92 failwith "False should not be in the final result. Perhaps your rule doesn't contain any +/-/* code"
93 | _ -> Some (dnf x)
94
95let interpret strict x =
96 match !Flag.scanner with
97 Flag.Glimpse -> interpret_glimpse strict x
98 | Flag.Google _ -> interpret_google strict x
99 | _ -> failwith "not possible"
34e49164
C
100
101let combine2c x =
102 match interpret false x with
103 None -> "None"
951c7801 104 | Some x -> String.concat " || " x
34e49164
C
105
106let norm = function
107 And l -> And (List.sort compare l)
108 | Or l -> Or (List.sort compare l)
109 | x -> x
110
111let rec merge l1 l2 =
112 match (l1,l2) with
113 ([],l2) -> l2
114 | (l1,[]) -> l1
115 | (x::xs,y::ys) ->
116 (match compare x y with
117 -1 -> x::(merge xs l2)
118 | 0 -> x::(merge xs ys)
119 | 1 -> y::(merge l1 ys)
120 | _ -> failwith "not possible")
121
122let intersect l1 l2 = List.filter (function l1e -> List.mem l1e l2) l1
123
124let minus_set l1 l2 = List.filter (function l1e -> not (List.mem l1e l2)) l1
125
126let rec insert x l = merge [x] l
127
128let rec build_and x y =
129 if x = y
130 then x
131 else
132 match (x,y) with
133 (True,x) | (x,True) -> x
134 | (False,x) | (x,False) -> False
135 | (And l1,And l2) -> And (merge l1 l2)
136 | (x,Or l) when List.mem x l -> x
137 | (Or l,x) when List.mem x l -> x
138 | (Or l1,Or l2) when not ((intersect l1 l2) = []) ->
139 let inner =
140 build_and
141 (List.fold_left build_or False (minus_set l1 l2))
142 (List.fold_left build_or False (minus_set l2 l1)) in
143 List.fold_left build_or inner (intersect l1 l2)
144 | (x,And l) | (And l,x) ->
145 if List.mem x l
146 then And l
147 else
148 let others =
149 List.filter
150 (function
151 Or l -> not(List.mem x l)
152 | _ -> true)
153 l in
154 And (insert x others)
155 | (x,y) -> norm(And [x;y])
156
157and build_or x y =
158 if x = y
159 then x
160 else
161 match (x,y) with
162 (True,x) | (x,True) -> True
163 | (False,x) | (x,False) -> x
164 | (Or l1,Or l2) -> Or (merge l1 l2)
165 | (x,And l) when List.mem x l -> x
166 | (And l,x) when List.mem x l -> x
167 | (And l1,And l2) when not ((intersect l1 l2) = []) ->
168 let inner =
169 build_or
170 (List.fold_left build_and True (minus_set l1 l2))
171 (List.fold_left build_and True (minus_set l2 l1)) in
172 List.fold_left build_and inner (intersect l1 l2)
173 | (x,Or l) | (Or l,x) ->
174 if List.mem x l
175 then Or l
176 else
177 let others =
178 List.filter
179 (function
180 And l -> not(List.mem x l)
181 | _ -> true)
182 l in
183 Or (insert x others)
184 | (x,y) -> norm(Or [x;y])
185
186let keep x = Elem x
187let drop x = True
188
189let do_get_constants constants keywords env neg_pos =
190 let donothing r k e = k e in
191 let option_default = True in
192 let bind = build_and in
193 let inherited ((nm1,_) as x) =
ae4735db
C
194 (* ignore virtuals *)
195 if nm1 = "virtual" then option_default
34e49164 196 (* perhaps inherited, but value not required, so no constraints *)
ae4735db
C
197 else if List.mem x neg_pos then option_default
198 else (try List.assoc nm1 env with Not_found -> False) in
34e49164
C
199 let minherited name = inherited (Ast.unwrap_mcode name) in
200 let mcode _ x =
201 match Ast.get_pos_var x with
202 Ast.MetaPos(name,constraints,_,keep,inh) -> minherited name
203 | _ -> option_default in
204
205 (* if one branch gives no information, then we have to take anything *)
206 let disj_union_all = List.fold_left build_or False in
207
208 let ident r k i =
209 match Ast.unwrap i with
210 Ast.Id(name) ->
211 bind (k i)
212 (match Ast.unwrap_mcode name with
213 "NULL" -> keywords "NULL"
214 | nm -> constants nm)
215 | Ast.MetaId(name,_,_,_) | Ast.MetaFunc(name,_,_,_)
216 | Ast.MetaLocalFunc(name,_,_,_) -> bind (k i) (minherited name)
217 | _ -> k i in
218
219 let rec type_collect res = function
220 TC.ConstVol(_,ty) | TC.Pointer(ty) | TC.FunctionPointer(ty)
221 | TC.Array(ty) -> type_collect res ty
ae4735db
C
222 | TC.MetaType(tyname,_,_) ->
223 inherited tyname
34e49164 224 | TC.TypeName(s) -> constants s
faf9a90c 225 | TC.EnumName(false,s) -> constants s
34e49164
C
226 | TC.StructUnionName(_,false,s) -> constants s
227 | ty -> res in
228
229 (* no point to do anything special for records because glimpse is
230 word-oriented *)
231 let expression r k e =
232 match Ast.unwrap e with
233 Ast.Constant(const) ->
234 bind (k e)
235 (match Ast.unwrap_mcode const with
236 Ast.String s -> constants s
237 | Ast.Char "\\0" -> option_default (* glimpse doesn't like it *)
fc1ad971 238 | Ast.Char s -> option_default (* probably not chars either *)
34e49164 239 (* the following were eg keywords "1", but not good for glimpse *)
fc1ad971
C
240 | Ast.Int s -> option_default (* glimpse doesn't index integers *)
241 | Ast.Float s -> option_default (* probably not floats either *))
34e49164
C
242 | Ast.MetaExpr(name,_,_,Some type_list,_,_) ->
243 let types = List.fold_left type_collect option_default type_list in
244 bind (k e) (bind (minherited name) types)
245 | Ast.MetaErr(name,_,_,_) | Ast.MetaExpr(name,_,_,_,_,_) ->
246 bind (k e) (minherited name)
247 | Ast.MetaExprList(name,None,_,_) -> minherited name
248 | Ast.MetaExprList(name,Some (lenname,_,_),_,_) ->
249 bind (k e) (bind (minherited name) (minherited lenname))
250 | Ast.SizeOfExpr(sizeof,exp) -> bind (keywords "sizeof") (k e)
251 | Ast.SizeOfType(sizeof,lp,ty,rp) -> bind (keywords "sizeof") (k e)
252 | Ast.NestExpr(expr_dots,wc,false) -> option_default
253 | Ast.NestExpr(expr_dots,wc,true) ->
254 r.V.combiner_expression_dots expr_dots
255 | Ast.DisjExpr(exps) ->
256 disj_union_all (List.map r.V.combiner_expression exps)
257 | Ast.OptExp(exp) -> option_default
258 | Ast.Edots(_,_) | Ast.Ecircles(_,_) | Ast.Estars(_,_) -> option_default
259 | _ -> k e in
faf9a90c 260
34e49164
C
261 let fullType r k ft =
262 match Ast.unwrap ft with
263 Ast.DisjType(decls) ->
264 disj_union_all (List.map r.V.combiner_fullType decls)
265 | Ast.OptType(ty) -> option_default
266 | _ -> k ft in
faf9a90c 267
34e49164
C
268 let baseType = function
269 Ast.VoidType -> keywords "void "
270 | Ast.CharType -> keywords "char "
271 | Ast.ShortType -> keywords "short "
272 | Ast.IntType -> keywords "int "
273 | Ast.DoubleType -> keywords "double "
274 | Ast.FloatType -> keywords "float "
faf9a90c 275 | Ast.LongType | Ast.LongLongType -> keywords "long " in
34e49164
C
276
277 let typeC r k ty =
278 match Ast.unwrap ty with
faf9a90c 279 Ast.BaseType(ty1,strings) -> bind (k ty) (baseType ty1)
34e49164
C
280 | Ast.TypeName(name) -> bind (k ty) (constants (Ast.unwrap_mcode name))
281 | Ast.MetaType(name,_,_) -> bind (minherited name) (k ty)
282 | _ -> k ty in
283
284 let declaration r k d =
285 match Ast.unwrap d with
286 Ast.DisjDecl(decls) ->
287 disj_union_all (List.map r.V.combiner_declaration decls)
288 | Ast.OptDecl(decl) -> option_default
289 | Ast.Ddots(dots,whencode) -> option_default
290 | _ -> k d in
291
292 let initialiser r k i =
293 match Ast.unwrap i with
294 Ast.OptIni(ini) -> option_default
295 | _ -> k i in
296
297 let parameter r k p =
298 match Ast.unwrap p with
299 Ast.OptParam(param) -> option_default
300 | Ast.MetaParam(name,_,_) -> bind (k p) (minherited name)
301 | Ast.MetaParamList(name,None,_,_) -> bind (k p) (minherited name)
302 | Ast.MetaParamList(name,Some(lenname,_,_),_,_) ->
303 bind (minherited name) (bind (minherited lenname) (k p))
304 | _ -> k p in
faf9a90c 305
34e49164
C
306 let rule_elem r k re =
307 match Ast.unwrap re with
308 Ast.MetaRuleElem(name,_,_) | Ast.MetaStmt(name,_,_,_)
309 | Ast.MetaStmtList(name,_,_) -> bind (minherited name) (k re)
310 | Ast.WhileHeader(whl,lp,exp,rp) ->
311 bind (keywords "while") (k re)
312 | Ast.WhileTail(whl,lp,exp,rp,sem) ->
313 bind (keywords "do") (k re)
314 | Ast.ForHeader(fr,lp,e1,sem1,e2,sem2,e3,rp) ->
315 bind (keywords "for") (k re)
316 | Ast.SwitchHeader(switch,lp,exp,rp) ->
317 bind (keywords "switch") (k re)
318 | Ast.Break(br,sem) ->
319 bind (keywords "break") (k re)
320 | Ast.Continue(cont,sem) ->
321 bind (keywords "continue") (k re)
322 | Ast.Goto(_,i,_) ->
323 bind (keywords "goto") (k re)
324 | Ast.Default(def,colon) ->
325 bind (keywords "default") (k re)
326 | Ast.Include(inc,s) ->
327 bind (k re)
328 (match Ast.unwrap_mcode s with
329 Ast.Local l | Ast.NonLocal l ->
330 let strings =
331 List.fold_left
332 (function prev ->
333 function
334 (* just take the last thing, probably the most
335 specific. everything is necessary anyway. *)
336 Ast.IncPath s -> [Elem s]
337 | Ast.IncDots -> prev)
338 [] l in
339 (match strings with
340 [] -> True
341 | x::xs -> List.fold_left bind x xs))
342 | Ast.DisjRuleElem(res) ->
343 disj_union_all (List.map r.V.combiner_rule_elem res)
344 | _ -> k re in
faf9a90c 345
34e49164
C
346 let statement r k s =
347 match Ast.unwrap s with
348 Ast.Disj(stmt_dots) ->
349 disj_union_all (List.map r.V.combiner_statement_dots stmt_dots)
350 | Ast.Nest(stmt_dots,whn,false,_,_) -> option_default
351 | Ast.Nest(stmt_dots,whn,true,_,_) ->
352 r.V.combiner_statement_dots stmt_dots
353 | Ast.OptStm(s) -> option_default
354 | Ast.Dots(d,whn,_,_) | Ast.Circles(d,whn,_,_) | Ast.Stars(d,whn,_,_) ->
355 option_default
356 | _ -> k s in
357
358 V.combiner bind option_default
359 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
360 donothing donothing donothing donothing
361 ident expression fullType typeC initialiser parameter declaration
362 rule_elem statement donothing donothing donothing
363
364(* ------------------------------------------------------------------------ *)
365
366let filter_combine combine to_drop =
367 let rec and_loop = function
368 Elem x when List.mem x to_drop -> True
369 | Or l -> List.fold_left build_or False (List.map or_loop l)
370 | x -> x
371 and or_loop = function
372 Elem x when List.mem x to_drop -> False
373 | And l -> List.fold_left build_and True (List.map and_loop l)
374 | x -> x in
375 or_loop combine
376
377(* ------------------------------------------------------------------------ *)
378
379let get_all_constants minus_only =
380 let donothing r k e = k e in
381 let bind = Common.union_set in
382 let option_default = [] in
383 let mcode r (x,_,mcodekind,_) =
384 match mcodekind with
708f4980 385 Ast.MINUS(_,_,_,_) -> [x]
34e49164
C
386 | _ when minus_only -> []
387 | _ -> [x] in
388 let other r _ = [] in
389
390 V.combiner bind option_default
391 other mcode other other other other other other other other other other
34e49164
C
392
393 donothing donothing donothing donothing
394 donothing donothing donothing donothing donothing donothing donothing
395 donothing donothing donothing donothing donothing
396
397(* ------------------------------------------------------------------------ *)
398
399let get_plus_constants =
400 let donothing r k e = k e in
401 let bind = Common.union_set in
402 let option_default = [] in
403 let mcode r mc =
404 let mcodekind = Ast.get_mcodekind mc in
405 let recurse l =
406 List.fold_left
407 (List.fold_left
408 (function prev ->
409 function cur ->
410 bind ((get_all_constants false).V.combiner_anything cur) prev))
411 [] l in
412 match mcodekind with
708f4980 413 Ast.MINUS(_,_,_,anythings) -> recurse anythings
951c7801
C
414 | Ast.CONTEXT(_,Ast.BEFORE(a,_)) -> recurse a
415 | Ast.CONTEXT(_,Ast.AFTER(a,_)) -> recurse a
416 | Ast.CONTEXT(_,Ast.BEFOREAFTER(a1,a2,_)) ->
34e49164
C
417 Common.union_set (recurse a1) (recurse a2)
418 | _ -> [] in
419
420 V.combiner bind option_default
421 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
422 donothing donothing donothing donothing
423 donothing donothing donothing donothing donothing donothing donothing
424 donothing donothing donothing donothing donothing
425
426(* ------------------------------------------------------------------------ *)
427
428(* true means the rule should be analyzed, false means it should be ignored *)
429let rec dependencies env = function
430 Ast.Dep s -> (try List.assoc s env with Not_found -> False)
431 | Ast.AntiDep s -> True
432 | Ast.EverDep s -> (try List.assoc s env with Not_found -> False)
433 | Ast.NeverDep s -> True
434 | Ast.AndDep (d1,d2) -> build_and (dependencies env d1) (dependencies env d2)
435 | Ast.OrDep (d1,d2) -> build_or (dependencies env d1) (dependencies env d2)
436 | Ast.NoDep -> True
7f004419 437 | Ast.FailDep -> False
34e49164
C
438
439(* ------------------------------------------------------------------------ *)
440
441let all_context =
442 let bind x y = x && y in
443 let option_default = true in
444
445 let donothing recursor k e = k e in
446
447 let mcode r e =
448 match Ast.get_mcodekind e with
449 Ast.CONTEXT(_,Ast.NOTHING) -> true
450 | _ -> false in
451
452 V.combiner bind option_default
453 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
454 donothing donothing donothing donothing
455 donothing donothing donothing donothing donothing donothing
456 donothing donothing donothing donothing donothing donothing
457
458(* ------------------------------------------------------------------------ *)
459
460let rule_fn tls in_plus env neg_pos =
461 List.fold_left
462 (function (rest_info,in_plus) ->
463 function (cur,neg_pos) ->
464 let minuses =
ae4735db
C
465 let getter = do_get_constants keep drop env neg_pos in
466 getter.V.combiner_top_level cur in
34e49164
C
467 let all_minuses =
468 if !Flag.sgrep_mode2
469 then [] (* nothing removed for sgrep *)
470 else (get_all_constants true).V.combiner_top_level cur in
471 let plusses = get_plus_constants.V.combiner_top_level cur in
472 (* the following is for eg -foo(2) +foo(x) then in another rule
473 -foo(10); don't want to consider that foo is guaranteed to be
474 created by the rule. not sure this works completely: what if foo is
475 in both - and +, but in an or, so the cases aren't related?
476 not sure this whole thing is a good idea. how do we know that
477 something that is only in plus is really freshly created? *)
478 let plusses = Common.minus_set plusses all_minuses in
479 let was_bot = minuses = True in
480 let new_minuses = filter_combine minuses in_plus in
481 let new_plusses = Common.union_set plusses in_plus in
482 (* perhaps it should be build_and here? we don't realy have multiple
483 minirules anymore anyway. *)
484 match new_minuses with
485 True ->
ae4735db
C
486 let getter = do_get_constants drop keep env neg_pos in
487 let retry = getter.V.combiner_top_level cur in
34e49164
C
488 (match retry with
489 True when not was_bot -> (rest_info, new_plusses)
490 | x -> (build_or x rest_info, new_plusses))
491 | x -> (build_or x rest_info, new_plusses))
492 (False,in_plus) (List.combine tls neg_pos)
493
7f004419 494let get_constants rules neg_pos_vars =
951c7801
C
495 match !Flag.scanner with
496 Flag.NoScanner -> None
497 | Flag.Glimpse | Flag.Google _ ->
951c7801
C
498 let (info,_,_,_) =
499 List.fold_left
500 (function (rest_info,in_plus,env,locals(*dom of env*)) ->
501 function
502 (Ast.ScriptRule (_,deps,mv,_),_) ->
503 let extra_deps =
504 List.fold_left
505 (function prev ->
506 function (_,(rule,_)) ->
ae4735db
C
507 if rule = "virtual"
508 then prev
509 else Ast.AndDep (Ast.Dep rule,prev))
951c7801
C
510 deps mv in
511 (match dependencies env extra_deps with
512 False -> (rest_info, in_plus, env, locals)
513 | dependencies ->
514 (build_or dependencies rest_info, in_plus, env, locals))
c3e37e97
C
515 | (Ast.InitialScriptRule (_,deps,_),_)
516 | (Ast.FinalScriptRule (_,deps,_),_) ->
517 (match dependencies env deps with
518 False -> (rest_info, in_plus, env, locals)
519 | dependencies ->
520 (build_or dependencies rest_info, in_plus, env, locals))
951c7801
C
521 | (Ast.CocciRule (nm,(dep,_,_),cur,_,_),neg_pos_vars) ->
522 let (cur_info,cur_plus) =
ae4735db
C
523 rule_fn cur in_plus ((nm,True)::env)
524 neg_pos_vars in
951c7801
C
525 if List.for_all all_context.V.combiner_top_level cur
526 then (rest_info,cur_plus,(nm,cur_info)::env,nm::locals)
527 else
34e49164
C
528 (* no constants if dependent on another rule; then we need to
529 find the constants of that rule *)
951c7801
C
530 match dependencies env dep with
531 False -> (rest_info,cur_plus,env,locals)
532 | dependencies ->
533 (build_or (build_and dependencies cur_info) rest_info,
534 cur_plus,env,locals))
7f004419 535 (False,[],[],[])
951c7801
C
536 (List.combine (rules : Ast.rule list) neg_pos_vars) in
537 interpret true info