Release coccinelle-0.2.0rc1
[bpt/coccinelle.git] / cocci.ml
CommitLineData
34e49164
C
1open Common
2
3module CCI = Ctlcocci_integration
4module TAC = Type_annoter_c
5
708f4980
C
6module Ast_to_flow = Control_flow_c_build
7
34e49164
C
8(*****************************************************************************)
9(* This file is a kind of driver. It gathers all the important functions
10 * from coccinelle in one place. The different entities in coccinelle are:
11 * - files
12 * - astc
13 * - astcocci
14 * - flow (contain nodes)
15 * - ctl (contain rule_elems)
16 * This file contains functions to transform one in another.
17 *)
18(*****************************************************************************)
19
20(* --------------------------------------------------------------------- *)
21(* C related *)
22(* --------------------------------------------------------------------- *)
23let cprogram_of_file file =
978fd7e5 24 let (program2, _stat) = Parse_c.parse_c_and_cpp file in
34e49164
C
25 program2
26
27let cprogram_of_file_cached file =
28 let (program2, _stat) = Parse_c.parse_cache file in
485bce71
C
29 if !Flag_cocci.ifdef_to_if
30 then
31 program2 +> Parse_c.with_program2 (fun asts ->
32 Cpp_ast_c.cpp_ifdef_statementize asts
33 )
34 else program2
34e49164
C
35
36let cfile_of_program program2_with_ppmethod outf =
485bce71 37 Unparse_c.pp_program program2_with_ppmethod outf
34e49164
C
38
39(* for memoization, contains only one entry, the one for the SP *)
40let _hparse = Hashtbl.create 101
41let _hctl = Hashtbl.create 101
42
43(* --------------------------------------------------------------------- *)
44(* Cocci related *)
45(* --------------------------------------------------------------------- *)
46let sp_of_file2 file iso =
47 Common.memoized _hparse (file, iso) (fun () ->
48 Parse_cocci.process file iso false)
49let sp_of_file file iso =
50 Common.profile_code "parse cocci" (fun () -> sp_of_file2 file iso)
51
52
53(* --------------------------------------------------------------------- *)
54(* Flow related *)
55(* --------------------------------------------------------------------- *)
56let print_flow flow =
57 Ograph_extended.print_ograph_mutable flow "/tmp/test.dot" true
58
59
60let ast_to_flow_with_error_messages2 x =
61 let flowopt =
62 try Ast_to_flow.ast_to_control_flow x
63 with Ast_to_flow.Error x ->
64 Ast_to_flow.report_error x;
65 None
66 in
67 flowopt +> do_option (fun flow ->
68 (* This time even if there is a deadcode, we still have a
69 * flow graph, so I can try the transformation and hope the
70 * deadcode will not bother us.
71 *)
72 try Ast_to_flow.deadcode_detection flow
73 with Ast_to_flow.Error (Ast_to_flow.DeadCode x) ->
74 Ast_to_flow.report_error (Ast_to_flow.DeadCode x);
75 );
76 flowopt
77let ast_to_flow_with_error_messages a =
78 Common.profile_code "flow" (fun () -> ast_to_flow_with_error_messages2 a)
79
80
81(* --------------------------------------------------------------------- *)
82(* Ctl related *)
83(* --------------------------------------------------------------------- *)
b1b2de81 84
978fd7e5 85let ctls_of_ast2 ast (ua,fua,fuas) pos =
34e49164 86 List.map2
978fd7e5 87 (function ast -> function (ua,(fua,(fuas,pos))) ->
34e49164
C
88 List.combine
89 (if !Flag_cocci.popl
951c7801 90 then Popl.popl ast
978fd7e5 91 else Asttoctl2.asttoctl ast (ua,fua,fuas) pos)
34e49164 92 (Asttomember.asttomember ast ua))
978fd7e5 93 ast (List.combine ua (List.combine fua (List.combine fuas pos)))
34e49164
C
94
95let ctls_of_ast ast ua =
96 Common.profile_code "asttoctl2" (fun () -> ctls_of_ast2 ast ua)
97
98(*****************************************************************************)
99(* Some debugging functions *)
100(*****************************************************************************)
101
102(* the inputs *)
103
104let show_or_not_cfile2 cfile =
105 if !Flag_cocci.show_c then begin
106 Common.pr2_xxxxxxxxxxxxxxxxx ();
107 pr2 ("processing C file: " ^ cfile);
108 Common.pr2_xxxxxxxxxxxxxxxxx ();
109 Common.command2 ("cat " ^ cfile);
110 end
111let show_or_not_cfile a =
112 Common.profile_code "show_xxx" (fun () -> show_or_not_cfile2 a)
113
114let show_or_not_cfiles cfiles = List.iter show_or_not_cfile cfiles
115
116
117let show_or_not_cocci2 coccifile isofile =
118 if !Flag_cocci.show_cocci then begin
119 Common.pr2_xxxxxxxxxxxxxxxxx ();
120 pr2 ("processing semantic patch file: " ^ coccifile);
121 isofile +> (fun s -> pr2 ("with isos from: " ^ s));
122 Common.pr2_xxxxxxxxxxxxxxxxx ();
123 Common.command2 ("cat " ^ coccifile);
124 pr2 "";
125 end
126let show_or_not_cocci a b =
127 Common.profile_code "show_xxx" (fun () -> show_or_not_cocci2 a b)
128
34e49164
C
129(* the output *)
130
131let show_or_not_diff2 cfile outfile show_only_minus =
132 if !Flag_cocci.show_diff then begin
951c7801 133 match Common.fst(Compare_c.compare_to_original cfile outfile) with
34e49164
C
134 Compare_c.Correct -> () (* diff only in spacing, etc *)
135 | _ ->
136 (* may need --strip-trailing-cr under windows *)
137 pr2 "diff = ";
138
139 let line =
140 match !Flag_parsing_c.diff_lines with
141 | None -> "diff -u -p " ^ cfile ^ " " ^ outfile
142 | Some n -> "diff -U "^n^" -p "^cfile^" "^outfile in
143 let xs =
144 let res = Common.cmd_to_list line in
145 match (!Flag.patch,res) with
146 (* create something that looks like the output of patch *)
147 (Some prefix,minus_file::plus_file::rest) ->
148 let drop_prefix file =
b1b2de81 149 if prefix =$= ""
34e49164
C
150 then "/"^file
151 else
b1b2de81
C
152 let lp = String.length prefix in
153 String.sub file lp ((String.length file) - lp) in
34e49164
C
154 let diff_line =
155 match List.rev(Str.split (Str.regexp " ") line) with
156 new_file::old_file::cmdrev ->
157 if !Flag.sgrep_mode2
158 then
159 String.concat " "
160 (List.rev ("/tmp/nothing" :: old_file :: cmdrev))
161 else
162 let old_base_file = drop_prefix old_file in
163 String.concat " "
164 (List.rev
165 (("b"^old_base_file)::("a"^old_base_file)::cmdrev))
166 | _ -> failwith "bad command" in
167 let (minus_line,plus_line) =
168 if !Flag.sgrep_mode2
169 then (minus_file,plus_file)
170 else
171 match (Str.split (Str.regexp "[ \t]") minus_file,
172 Str.split (Str.regexp "[ \t]") plus_file) with
173 ("---"::old_file::old_rest,"+++"::new_file::new_rest) ->
174 let old_base_file = drop_prefix old_file in
175 (String.concat " "
176 ("---"::("a"^old_base_file)::old_rest),
177 String.concat " "
178 ("+++"::("b"^old_base_file)::new_rest))
179 | (l1,l2) ->
180 failwith
181 (Printf.sprintf "bad diff header lines: %s %s"
182 (String.concat ":" l1) (String.concat ":" l2)) in
183 diff_line::minus_line::plus_line::rest
b1b2de81 184 | _ -> res in
34e49164
C
185 xs +> List.iter (fun s ->
186 if s =~ "^\\+" && show_only_minus
187 then ()
188 else pr s)
189 end
190let show_or_not_diff a b c =
191 Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b c)
192
193
194(* the derived input *)
195
196let show_or_not_ctl_tex2 astcocci ctls =
197 if !Flag_cocci.show_ctl_tex then begin
198 Ctltotex.totex ("/tmp/__cocci_ctl.tex") astcocci ctls;
199 Common.command2 ("cd /tmp; latex __cocci_ctl.tex; " ^
200 "dvips __cocci_ctl.dvi -o __cocci_ctl.ps;" ^
201 "gv __cocci_ctl.ps &");
202 end
203let show_or_not_ctl_tex a b =
204 Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_tex2 a b)
205
206
207
208let show_or_not_rule_name ast rulenb =
209 if !Flag_cocci.show_ctl_text or !Flag.show_trying or
faf9a90c 210 !Flag.show_transinfo or !Flag_cocci.show_binding_in_out
34e49164
C
211 then
212 begin
213 let name =
214 match ast with
faf9a90c 215 Ast_cocci.CocciRule (nm, (deps, drops, exists), x, _, _) -> nm
34e49164
C
216 | _ -> i_to_s rulenb in
217 Common.pr_xxxxxxxxxxxxxxxxx ();
218 pr (name ^ " = ");
219 Common.pr_xxxxxxxxxxxxxxxxx ()
220 end
221
222let show_or_not_scr_rule_name rulenb =
223 if !Flag_cocci.show_ctl_text or !Flag.show_trying or
faf9a90c 224 !Flag.show_transinfo or !Flag_cocci.show_binding_in_out
34e49164
C
225 then
226 begin
227 let name = i_to_s rulenb in
228 Common.pr_xxxxxxxxxxxxxxxxx ();
229 pr ("script rule " ^ name ^ " = ");
230 Common.pr_xxxxxxxxxxxxxxxxx ()
231 end
232
233let show_or_not_ctl_text2 ctl ast rulenb =
234 if !Flag_cocci.show_ctl_text then begin
235
236 adjust_pp_with_indent (fun () ->
237 Format.force_newline();
238 Pretty_print_cocci.print_plus_flag := true;
239 Pretty_print_cocci.print_minus_flag := true;
240 Pretty_print_cocci.unparse ast;
241 );
242
243 pr "CTL = ";
244 let (ctl,_) = ctl in
245 adjust_pp_with_indent (fun () ->
246 Format.force_newline();
247 Pretty_print_engine.pp_ctlcocci
248 !Flag_cocci.show_mcodekind_in_ctl !Flag_cocci.inline_let_ctl ctl;
249 );
250 pr "";
251 end
252let show_or_not_ctl_text a b c =
253 Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_text2 a b c)
254
255
256
257(* running information *)
485bce71
C
258let get_celem celem : string =
259 match celem with
b1b2de81
C
260 Ast_c.Definition ({Ast_c.f_name = namefuncs;},_) ->
261 Ast_c.str_of_name namefuncs
485bce71 262 | Ast_c.Declaration
b1b2de81
C
263 (Ast_c.DeclList ([{Ast_c.v_namei = Some (name, _);}, _], _)) ->
264 Ast_c.str_of_name name
485bce71 265 | _ -> ""
34e49164
C
266
267let show_or_not_celem2 prelude celem =
485bce71 268 let (tag,trying) =
34e49164 269 (match celem with
b1b2de81
C
270 | Ast_c.Definition ({Ast_c.f_name = namefuncs},_) ->
271 let funcs = Ast_c.str_of_name namefuncs in
485bce71
C
272 Flag.current_element := funcs;
273 (" function: ",funcs)
34e49164 274 | Ast_c.Declaration
b1b2de81
C
275 (Ast_c.DeclList ([{Ast_c.v_namei = Some (name,_)}, _], _)) ->
276 let s = Ast_c.str_of_name name in
485bce71
C
277 Flag.current_element := s;
278 (" variable ",s);
279 | _ ->
280 Flag.current_element := "something_else";
281 (" ","something else");
282 ) in
283 if !Flag.show_trying then pr2 (prelude ^ tag ^ trying)
284
34e49164
C
285let show_or_not_celem a b =
286 Common.profile_code "show_xxx" (fun () -> show_or_not_celem2 a b)
287
288
289let show_or_not_trans_info2 trans_info =
708f4980
C
290 (* drop witness tree indices for printing *)
291 let trans_info =
292 List.map (function (index,trans_info) -> trans_info) trans_info in
faf9a90c 293 if !Flag.show_transinfo then begin
34e49164
C
294 if null trans_info then pr2 "transformation info is empty"
295 else begin
296 pr2 "transformation info returned:";
297 let trans_info =
298 List.sort (function (i1,_,_) -> function (i2,_,_) -> compare i1 i2)
299 trans_info
300 in
301 indent_do (fun () ->
302 trans_info +> List.iter (fun (i, subst, re) ->
303 pr2 ("transform state: " ^ (Common.i_to_s i));
304 indent_do (fun () ->
305 adjust_pp_with_indent_and_header "with rule_elem: " (fun () ->
306 Pretty_print_cocci.print_plus_flag := true;
307 Pretty_print_cocci.print_minus_flag := true;
308 Pretty_print_cocci.rule_elem "" re;
309 );
310 adjust_pp_with_indent_and_header "with binding: " (fun () ->
311 Pretty_print_engine.pp_binding subst;
312 );
313 )
314 );
315 )
316 end
317 end
318let show_or_not_trans_info a =
319 Common.profile_code "show_xxx" (fun () -> show_or_not_trans_info2 a)
320
321
322
323let show_or_not_binding2 s binding =
324 if !Flag_cocci.show_binding_in_out then begin
325 adjust_pp_with_indent_and_header ("binding " ^ s ^ " = ") (fun () ->
326 Pretty_print_engine.pp_binding binding
327 )
328 end
329let show_or_not_binding a b =
330 Common.profile_code "show_xxx" (fun () -> show_or_not_binding2 a b)
331
332
333
334(*****************************************************************************)
335(* Some helper functions *)
336(*****************************************************************************)
337
338let worth_trying cfiles tokens =
339 (* drop the following line for a list of list by rules. since we don't
340 allow multiple minirules, all the tokens within a rule should be in
341 a single CFG entity *)
342 let tokens = Common.union_all tokens in
343 if not !Flag_cocci.windows && not (null tokens)
344 then
345 (* could also modify the code in get_constants.ml *)
346 let tokens = tokens +> List.map (fun s ->
347 match () with
348 | _ when s =~ "^[A-Za-z_][A-Za-z_0-9]*$" ->
349 "\\b" ^ s ^ "\\b"
350
351 | _ when s =~ "^[A-Za-z_]" ->
352 "\\b" ^ s
353
354 | _ when s =~ ".*[A-Za-z_]$" ->
355 s ^ "\\b"
356 | _ -> s
357
358 ) in
359 let com = sprintf "egrep -q '(%s)' %s" (join "|" tokens) (join " " cfiles)
360 in
361 (match Sys.command com with
362 | 0 (* success *) -> true
363 | _ (* failure *) ->
364 (if !Flag.show_misc
365 then Printf.printf "grep failed: %s\n" com);
366 false (* no match, so not worth trying *)
367 )
368 else true
369
370let check_macro_in_sp_and_adjust tokens =
371 let tokens = Common.union_all tokens in
372 tokens +> List.iter (fun s ->
708f4980 373 if Hashtbl.mem !Parse_c._defs s
34e49164 374 then begin
708f4980
C
375 if !Flag_cocci.verbose_cocci then begin
376 pr2 "warning: macro in semantic patch was in macro definitions";
377 pr2 ("disabling macro expansion for " ^ s);
378 end;
379 Hashtbl.remove !Parse_c._defs s
34e49164
C
380 end
381 )
382
383
384let contain_loop gopt =
385 match gopt with
386 | Some g ->
387 g#nodes#tolist +> List.exists (fun (xi, node) ->
388 Control_flow_c.extract_is_loop node
389 )
390 | None -> true (* means nothing, if no g then will not model check *)
391
392
393
394let sp_contain_typed_metavar_z toplevel_list_list =
395 let bind x y = x or y in
396 let option_default = false in
397 let mcode _ _ = option_default in
398 let donothing r k e = k e in
399
400 let expression r k e =
401 match Ast_cocci.unwrap e with
402 | Ast_cocci.MetaExpr (_,_,_,Some t,_,_) -> true
403 | Ast_cocci.MetaExpr (_,_,_,_,Ast_cocci.LocalID,_) -> true
404 | _ -> k e
405 in
406
407 let combiner =
408 Visitor_ast.combiner bind option_default
409 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164
C
410 donothing donothing donothing donothing
411 donothing expression donothing donothing donothing donothing donothing
412 donothing donothing donothing donothing donothing
413 in
414 toplevel_list_list +>
415 List.exists
416 (function (nm,_,rule) ->
417 (List.exists combiner.Visitor_ast.combiner_top_level rule))
418
419
420let sp_contain_typed_metavar rules =
421 sp_contain_typed_metavar_z
422 (List.map
423 (function x ->
424 match x with
faf9a90c 425 Ast_cocci.CocciRule (a,b,c,d,_) -> (a,b,c)
34e49164
C
426 | _ -> failwith "error in filter")
427 (List.filter
428 (function x ->
faf9a90c
C
429 match x with
430 Ast_cocci.CocciRule (a,b,c,d,Ast_cocci.Normal) -> true
431 | _ -> false)
34e49164
C
432 rules))
433
434
435
436(* finding among the #include the one that we need to parse
437 * because they may contain useful type definition or because
438 * we may have to modify them
439 *
440 * For the moment we base in part our heuristic on the name of the file, e.g.
441 * serio.c is related we think to #include <linux/serio.h>
442 *)
443
b1b2de81
C
444let interpret_include_path _ =
445 match !Flag_cocci.include_path with
446 None -> "include"
447 | Some x -> x
448
faf9a90c
C
449let (includes_to_parse:
450 (Common.filename * Parse_c.program2) list ->
451 Flag_cocci.include_options -> 'a) = fun xs choose_includes ->
452 match choose_includes with
453 Flag_cocci.I_UNSPECIFIED -> failwith "not possible"
454 | Flag_cocci.I_NO_INCLUDES -> []
455 | x ->
b1b2de81 456 let all_includes = x =*= Flag_cocci.I_ALL_INCLUDES in
faf9a90c
C
457 xs +> List.map (fun (file, cs) ->
458 let dir = Common.dirname file in
459
460 cs +> Common.map_filter (fun (c,_info_item) ->
461 match c with
462 | Ast_c.CppTop
463 (Ast_c.Include
464 {Ast_c.i_include = ((x,ii)); i_rel_pos = info_h_pos;}) ->
465 (match x with
34e49164
C
466 | Ast_c.Local xs ->
467 let f = Filename.concat dir (Common.join "/" xs) in
468 (* for our tests, all the files are flat in the current dir *)
469 if not (Sys.file_exists f) && !Flag_cocci.relax_include_path
470 then
471 let attempt2 = Filename.concat dir (Common.last xs) in
faf9a90c 472 if not (Sys.file_exists f) && all_includes
b1b2de81 473 then Some (Filename.concat (interpret_include_path())
34e49164
C
474 (Common.join "/" xs))
475 else Some attempt2
476 else Some f
faf9a90c 477
34e49164 478 | Ast_c.NonLocal xs ->
faf9a90c 479 if all_includes ||
b1b2de81 480 Common.fileprefix (Common.last xs) =$= Common.fileprefix file
34e49164 481 then
b1b2de81 482 Some (Filename.concat (interpret_include_path())
34e49164
C
483 (Common.join "/" xs))
484 else None
0708f913 485 | Ast_c.Weird _ -> None
34e49164 486 )
faf9a90c
C
487 | _ -> None))
488 +> List.concat
489 +> Common.uniq
34e49164 490
7f004419
C
491let rec interpret_dependencies local global = function
492 Ast_cocci.Dep s -> List.mem s local
34e49164
C
493 | Ast_cocci.AntiDep s ->
494 (if !Flag_ctl.steps != None
495 then failwith "steps and ! dependency incompatible");
7f004419
C
496 not (List.mem s local)
497 | Ast_cocci.EverDep s -> List.mem s global
34e49164
C
498 | Ast_cocci.NeverDep s ->
499 (if !Flag_ctl.steps != None
500 then failwith "steps and ! dependency incompatible");
7f004419 501 not (List.mem s global)
34e49164 502 | Ast_cocci.AndDep(s1,s2) ->
7f004419
C
503 (interpret_dependencies local global s1) &&
504 (interpret_dependencies local global s2)
34e49164 505 | Ast_cocci.OrDep(s1,s2) ->
7f004419
C
506 (interpret_dependencies local global s1) or
507 (interpret_dependencies local global s2)
34e49164 508 | Ast_cocci.NoDep -> true
7f004419 509 | Ast_cocci.FailDep -> false
34e49164 510
7f004419 511let rec print_dependencies str local global dep =
1be43e12
C
512 if !Flag_cocci.show_dependencies
513 then
514 begin
515 pr2 str;
516 let seen = ref [] in
517 let rec loop = function
518 Ast_cocci.Dep s | Ast_cocci.AntiDep s ->
951c7801
C
519 if not (List.mem s !seen)
520 then
521 begin
7f004419 522 if List.mem s local
951c7801
C
523 then pr2 (s^" satisfied")
524 else pr2 (s^" not satisfied");
525 seen := s :: !seen
526 end
1be43e12 527 | Ast_cocci.EverDep s | Ast_cocci.NeverDep s ->
951c7801
C
528 if not (List.mem s !seen)
529 then
530 begin
7f004419 531 if List.mem s global
951c7801
C
532 then pr2 (s^" satisfied")
533 else pr2 (s^" not satisfied");
534 seen := s :: !seen
535 end
1be43e12
C
536 | Ast_cocci.AndDep(s1,s2) ->
537 loop s1;
538 loop s2
539 | Ast_cocci.OrDep(s1,s2) ->
540 loop s1;
541 loop s2
7f004419
C
542 | Ast_cocci.NoDep -> ()
543 | Ast_cocci.FailDep -> pr2 "False not satisfied" in
1be43e12
C
544 loop dep
545 end
951c7801 546
34e49164
C
547(* --------------------------------------------------------------------- *)
548(* #include relative position in the file *)
549(* --------------------------------------------------------------------- *)
550
551(* compute the set of new prefixes
552 * on
553 * "a/b/x"; (* in fact it is now a list of string so ["a";"b";"x"] *)
554 * "a/b/c/x";
555 * "a/x";
556 * "b/x";
557 * it would give for the first element
558 * ""; "a"; "a/b"; "a/b/x"
559 * for the second
560 * "a/b/c/x"
561 *
562 * update: if the include is inside a ifdef a put nothing. cf -test incl.
563 * this is because we dont want code added inside ifdef.
564 *)
565
566let compute_new_prefixes xs =
567 xs +> Common.map_withenv (fun already xs ->
568 let subdirs_prefixes = Common.inits xs in
569 let new_first = subdirs_prefixes +> List.filter (fun x ->
570 not (List.mem x already)
571 )
572 in
573 new_first,
574 new_first @ already
575 ) []
576 +> fst
577
578
579(* does via side effect on the ref in the Include in Ast_c *)
580let rec update_include_rel_pos cs =
581 let only_include = cs +> Common.map_filter (fun c ->
582 match c with
485bce71
C
583 | Ast_c.CppTop (Ast_c.Include {Ast_c.i_include = ((x,_));
584 i_rel_pos = aref;
585 i_is_in_ifdef = inifdef}) ->
34e49164 586 (match x with
0708f913 587 | Ast_c.Weird _ -> None
34e49164
C
588 | _ ->
589 if inifdef
590 then None
591 else Some (x, aref)
592 )
593 | _ -> None
594 )
595 in
596 let (locals, nonlocals) =
597 only_include +> Common.partition_either (fun (c, aref) ->
598 match c with
599 | Ast_c.Local x -> Left (x, aref)
600 | Ast_c.NonLocal x -> Right (x, aref)
0708f913 601 | Ast_c.Weird x -> raise Impossible
34e49164
C
602 ) in
603
604 update_rel_pos_bis locals;
605 update_rel_pos_bis nonlocals;
606 cs
607and update_rel_pos_bis xs =
608 let xs' = List.map fst xs in
609 let the_first = compute_new_prefixes xs' in
610 let the_last = List.rev (compute_new_prefixes (List.rev xs')) in
611 let merged = Common.zip xs (Common.zip the_first the_last) in
612 merged +> List.iter (fun ((x, aref), (the_first, the_last)) ->
613 aref := Some
614 {
615 Ast_c.first_of = the_first;
616 Ast_c.last_of = the_last;
617 }
618 )
619
620
621
622
623
624
625(*****************************************************************************)
626(* All the information needed around the C elements and Cocci rules *)
627(*****************************************************************************)
628
629type toplevel_c_info = {
630 ast_c: Ast_c.toplevel; (* contain refs so can be modified *)
631 tokens_c: Parser_c.token list;
632 fullstring: string;
633
634 flow: Control_flow_c.cflow option; (* it's the "fixed" flow *)
635 contain_loop: bool;
636
637 env_typing_before: TAC.environment;
638 env_typing_after: TAC.environment;
639
640 was_modified: bool ref;
641
642 (* id: int *)
643}
644
645type toplevel_cocci_info_script_rule = {
646 scr_ast_rule: string * (string * (string * string)) list * string;
647 language: string;
648 scr_dependencies: Ast_cocci.dependency;
649 scr_ruleid: int;
650 script_code: string;
651}
652
653type toplevel_cocci_info_cocci_rule = {
654 ctl: Lib_engine.ctlcocci * (CCI.pred list list);
faf9a90c 655 metavars: Ast_cocci.metavar list;
34e49164
C
656 ast_rule: Ast_cocci.rule;
657 isexp: bool; (* true if + code is an exp, only for Flag.make_hrule *)
658
659 rulename: string;
660 dependencies: Ast_cocci.dependency;
661 (* There are also some hardcoded rule names in parse_cocci.ml:
662 * let reserved_names = ["all";"optional_storage";"optional_qualifier"]
663 *)
664 dropped_isos: string list;
665 free_vars: Ast_cocci.meta_name list;
666 negated_pos_vars: Ast_cocci.meta_name list;
667 used_after: Ast_cocci.meta_name list;
668 positions: Ast_cocci.meta_name list;
669
670 ruleid: int;
faf9a90c 671 ruletype: Ast_cocci.ruletype;
34e49164
C
672
673 was_matched: bool ref;
674}
675
676type toplevel_cocci_info =
677 ScriptRuleCocciInfo of toplevel_cocci_info_script_rule
b1b2de81
C
678 | InitialScriptRuleCocciInfo of toplevel_cocci_info_script_rule
679 | FinalScriptRuleCocciInfo of toplevel_cocci_info_script_rule
34e49164
C
680 | CocciRuleCocciInfo of toplevel_cocci_info_cocci_rule
681
7f004419 682type cocci_info = toplevel_cocci_info list * string list list (* tokens *)
b1b2de81 683
34e49164
C
684type kind_file = Header | Source
685type file_info = {
686 fname : string;
687 full_fname : string;
688 was_modified_once: bool ref;
689 asts: toplevel_c_info list;
690 fpath : string;
691 fkind : kind_file;
692}
693
694let g_contain_typedmetavar = ref false
695
696
697let last_env_toplevel_c_info xs =
698 (Common.last xs).env_typing_after
699
485bce71
C
700let concat_headers_and_c (ccs: file_info list)
701 : (toplevel_c_info * string) list =
702 (List.concat (ccs +> List.map (fun x ->
703 x.asts +> List.map (fun x' ->
704 (x', x.fname)))))
34e49164
C
705
706let for_unparser xs =
707 xs +> List.map (fun x ->
485bce71 708 (x.ast_c, (x.fullstring, x.tokens_c)), Unparse_c.PPviastr
34e49164
C
709 )
710
485bce71
C
711let gen_pdf_graph () =
712 (Ctl_engine.get_graph_files ()) +> List.iter (fun outfile ->
713 Printf.printf "Generation of %s%!" outfile;
714 let filename_stack = Ctl_engine.get_graph_comp_files outfile in
715 List.iter (fun filename ->
716 ignore (Unix.system ("dot " ^ filename ^ " -Tpdf -o " ^ filename ^ ".pdf;"))
717 ) filename_stack;
718 let (head,tail) = (List.hd filename_stack, List.tl filename_stack) in
719 ignore(Unix.system ("cp " ^ head ^ ".pdf " ^ outfile ^ ".pdf;"));
720 tail +> List.iter (fun filename ->
721 ignore(Unix.system ("mv " ^ outfile ^ ".pdf /tmp/tmp.pdf;"));
722 ignore(Unix.system ("pdftk " ^ filename ^ ".pdf /tmp/tmp.pdf cat output " ^ outfile ^ ".pdf"));
723 );
724 ignore(Unix.system ("rm /tmp/tmp.pdf;"));
725 List.iter (fun filename ->
726 ignore (Unix.system ("rm " ^ filename ^ " " ^ filename ^ ".pdf;"))
727 ) filename_stack;
728 Printf.printf " - Done\n")
729
730
34e49164
C
731(* --------------------------------------------------------------------- *)
732let prepare_cocci ctls free_var_lists negated_pos_lists
951c7801 733 (ua,fua,fuas) positions_list metavars astcocci =
34e49164
C
734
735 let gathered = Common.index_list_1
978fd7e5
C
736 (zip (zip (zip (zip (zip (zip (zip (zip ctls metavars) astcocci)
737 free_var_lists)
738 negated_pos_lists) ua) fua) fuas) positions_list)
34e49164
C
739 in
740 gathered +> List.map
978fd7e5
C
741 (fun (((((((((ctl_toplevel_list,metavars),ast),free_var_list),
742 negated_pos_list),ua),fua),fuas),positions_list),rulenb) ->
34e49164
C
743
744 let is_script_rule r =
b1b2de81
C
745 match r with
746 Ast_cocci.ScriptRule _
747 | Ast_cocci.InitialScriptRule _ | Ast_cocci.FinalScriptRule _ -> true
748 | _ -> false in
34e49164 749
b1b2de81 750 if not (List.length ctl_toplevel_list =|= 1) && not (is_script_rule ast)
34e49164
C
751 then failwith "not handling multiple minirules";
752
753 match ast with
754 Ast_cocci.ScriptRule (lang,deps,mv,code) ->
755 let r =
756 {
757 scr_ast_rule = (lang, mv, code);
758 language = lang;
759 scr_dependencies = deps;
760 scr_ruleid = rulenb;
761 script_code = code;
762 }
763 in ScriptRuleCocciInfo r
b1b2de81
C
764 | Ast_cocci.InitialScriptRule (lang,code) ->
765 let mv = [] in
766 let deps = Ast_cocci.NoDep in
767 let r =
768 {
769 scr_ast_rule = (lang, mv, code);
770 language = lang;
771 scr_dependencies = deps;
772 scr_ruleid = rulenb;
773 script_code = code;
774 }
775 in InitialScriptRuleCocciInfo r
776 | Ast_cocci.FinalScriptRule (lang,code) ->
777 let mv = [] in
778 let deps = Ast_cocci.NoDep in
779 let r =
780 {
781 scr_ast_rule = (lang, mv, code);
782 language = lang;
783 scr_dependencies = deps;
784 scr_ruleid = rulenb;
785 script_code = code;
786 }
787 in FinalScriptRuleCocciInfo r
34e49164 788 | Ast_cocci.CocciRule
faf9a90c 789 (rulename,(dependencies,dropped_isos,z),restast,isexp,ruletype) ->
34e49164
C
790 CocciRuleCocciInfo (
791 {
792 ctl = List.hd ctl_toplevel_list;
faf9a90c 793 metavars = metavars;
34e49164
C
794 ast_rule = ast;
795 isexp = List.hd isexp;
796 rulename = rulename;
797 dependencies = dependencies;
798 dropped_isos = dropped_isos;
799 free_vars = List.hd free_var_list;
800 negated_pos_vars = List.hd negated_pos_list;
978fd7e5 801 used_after = (List.hd ua) @ (List.hd fua);
34e49164
C
802 positions = List.hd positions_list;
803 ruleid = rulenb;
faf9a90c 804 ruletype = ruletype;
34e49164
C
805 was_matched = ref false;
806 })
807 )
808
809
810(* --------------------------------------------------------------------- *)
811
812let build_info_program cprogram env =
0708f913
C
813
814 let (cs, parseinfos) =
815 Common.unzip cprogram in
816
817 let alltoks =
818 parseinfos +> List.map (fun (s,toks) -> toks) +> List.flatten in
34e49164 819
0708f913
C
820 (* I use cs' but really annotate_xxx work by doing side effects on cs *)
821 let cs' =
822 Comment_annotater_c.annotate_program alltoks cs in
823 let cs_with_envs =
824 Type_annoter_c.annotate_program env (*!g_contain_typedmetavar*) cs'
825 in
826
827 zip cs_with_envs parseinfos +> List.map (fun ((c, (enva,envb)), parseinfo)->
34e49164
C
828 let (fullstr, tokens) = parseinfo in
829
830 let flow =
951c7801
C
831 ast_to_flow_with_error_messages c +>
832 Common.map_option (fun flow ->
34e49164
C
833 let flow = Ast_to_flow.annotate_loop_nodes flow in
834
835 (* remove the fake nodes for julia *)
836 let fixed_flow = CCI.fix_flow_ctl flow in
837
838 if !Flag_cocci.show_flow then print_flow fixed_flow;
839 if !Flag_cocci.show_before_fixed_flow then print_flow flow;
840
841 fixed_flow
842 )
843 in
844
845 {
846 ast_c = c; (* contain refs so can be modified *)
847 tokens_c = tokens;
848 fullstring = fullstr;
849
850 flow = flow;
851
852 contain_loop = contain_loop flow;
853
854 env_typing_before = enva;
855 env_typing_after = envb;
856
857 was_modified = ref false;
858 }
859 )
860
861
862
863(* Optimisation. Try not unparse/reparse the whole file when have modifs *)
864let rebuild_info_program cs file isexp =
865 cs +> List.map (fun c ->
866 if !(c.was_modified)
867 then
faf9a90c
C
868 let file = Common.new_temp_file "cocci_small_output" ".c" in
869 cfile_of_program
870 [(c.ast_c, (c.fullstring, c.tokens_c)), Unparse_c.PPnormal]
871 file;
34e49164 872
faf9a90c
C
873 (* Common.command2 ("cat " ^ file); *)
874 let cprogram = cprogram_of_file file in
875 let xs = build_info_program cprogram c.env_typing_before in
34e49164 876
faf9a90c
C
877 (* TODO: assert env has not changed,
878 * if yes then must also reparse what follows even if not modified.
879 * Do that only if contain_typedmetavar of course, so good opti.
880 *)
881 (* Common.list_init xs *) (* get rid of the FinalDef *)
882 xs
34e49164
C
883 else [c]
884 ) +> List.concat
885
886
887let rebuild_info_c_and_headers ccs isexp =
888 ccs +> List.iter (fun c_or_h ->
889 if c_or_h.asts +> List.exists (fun c -> !(c.was_modified))
890 then c_or_h.was_modified_once := true;
891 );
892 ccs +> List.map (fun c_or_h ->
893 { c_or_h with
951c7801
C
894 asts =
895 rebuild_info_program c_or_h.asts c_or_h.full_fname isexp }
34e49164
C
896 )
897
898
899
900
901
902
903
faf9a90c 904let prepare_c files choose_includes : file_info list =
34e49164 905 let cprograms = List.map cprogram_of_file_cached files in
faf9a90c 906 let includes = includes_to_parse (zip files cprograms) choose_includes in
34e49164
C
907
908 (* todo?: may not be good to first have all the headers and then all the c *)
909 let all =
910 (includes +> List.map (fun hpath -> Right hpath))
911 ++
912 ((zip files cprograms) +> List.map (fun (file, asts) -> Left (file, asts)))
913 in
914
91eba41f 915 let env = ref !TAC.initial_env in
34e49164
C
916
917 let ccs = all +> Common.map_filter (fun x ->
918 match x with
919 | Right hpath ->
920 if not (Common.lfile_exists hpath)
921 then begin
922 pr2 ("TYPE: header " ^ hpath ^ " not found");
923 None
924 end
925 else
926 let h_cs = cprogram_of_file_cached hpath in
927 let info_h_cs = build_info_program h_cs !env in
928 env :=
929 if null info_h_cs
930 then !env
931 else last_env_toplevel_c_info info_h_cs
932 ;
933 Some {
934 fname = Common.basename hpath;
935 full_fname = hpath;
936 asts = info_h_cs;
937 was_modified_once = ref false;
938 fpath = hpath;
939 fkind = Header;
940 }
941 | Left (file, cprogram) ->
942 (* todo?: don't update env ? *)
943 let cs = build_info_program cprogram !env in
944 (* we do that only for the c, not for the h *)
945 ignore(update_include_rel_pos (cs +> List.map (fun x -> x.ast_c)));
946 Some {
947 fname = Common.basename file;
948 full_fname = file;
949 asts = cs;
950 was_modified_once = ref false;
951 fpath = file;
952 fkind = Source;
953 }
954 )
955 in
956 ccs
957
958
959(*****************************************************************************)
960(* Processing the ctls and toplevel C elements *)
961(*****************************************************************************)
962
963(* The main algorithm =~
964 * The algorithm is roughly:
965 * for_all ctl rules in SP
966 * for_all minirule in rule (no more)
967 * for_all binding (computed during previous phase)
968 * for_all C elements
969 * match control flow of function vs minirule
970 * with the binding and update the set of possible
971 * bindings, and returned the possibly modified function.
972 * pretty print modified C elements and reparse it.
973 *
974 *
975 * On ne prends que les newbinding ou returned_any_state est vrai.
976 * Si ca ne donne rien, on prends ce qu'il y avait au depart.
977 * Mais au nouveau depart de quoi ?
978 * - si ca donne rien apres avoir traité toutes les fonctions avec ce binding ?
979 * - ou alors si ca donne rien, apres avoir traité toutes les fonctions
980 * avec tous les bindings du round d'avant ?
981 *
982 * Julia pense qu'il faut prendre la premiere solution.
983 * Example: on a deux environnements candidats, E1 et E2 apres avoir traité
984 * la regle ctl 1. On arrive sur la regle ctl 2.
985 * E1 ne donne rien pour la regle 2, on garde quand meme E1 pour la regle 3.
986 * E2 donne un match a un endroit et rend E2' alors on utilise ca pour
987 * la regle 3.
988 *
989 * I have not to look at used_after_list to decide to restart from
990 * scratch. I just need to look if the binding list is empty.
991 * Indeed, let's suppose that a SP have 3 regions/rules. If we
992 * don't find a match for the first region, then if this first
993 * region does not bind metavariable used after, that is if
994 * used_after_list is empty, then mysat(), even if does not find a
995 * match, will return a Left, with an empty transformation_info,
996 * and so current_binding will grow. On the contrary if the first
997 * region must bind some metavariables used after, and that we
998 * dont find any such region, then mysat() will returns lots of
999 * Right, and current_binding will not grow, and so we will have
1000 * an empty list of binding, and we will catch such a case.
1001 *
1002 * opti: julia says that because the binding is
1003 * determined by the used_after_list, the items in the list
1004 * are kind of sorted, so could optimise the insert_set operations.
1005 *)
1006
1007
1008(* r(ule), c(element in C code), e(nvironment) *)
1009
708f4980
C
1010let findk f l =
1011 let rec loop k = function
1012 [] -> None
1013 | x::xs ->
1014 if f x
1015 then Some (x, function n -> k (n :: xs))
1016 else loop (function vs -> k (x :: vs)) xs in
1017 loop (function x -> x) l
1018
b1b2de81 1019let merge_env new_e old_e =
708f4980
C
1020 let (ext,old_e) =
1021 List.fold_left
1022 (function (ext,old_e) ->
1023 function (e,rules) as elem ->
1024 match findk (function (e1,_) -> e =*= e1) old_e with
1025 None -> (elem :: ext,old_e)
1026 | Some((_,old_rules),k) ->
1027 (ext,k (e,Common.union_set rules old_rules)))
1028 ([],old_e) new_e in
1029 old_e @ (List.rev ext)
b1b2de81
C
1030
1031let apply_python_rule r cache newes e rules_that_have_matched
7f004419 1032 rules_that_have_ever_matched =
951c7801 1033 Common.profile_code "python" (fun () ->
34e49164
C
1034 show_or_not_scr_rule_name r.scr_ruleid;
1035 if not(interpret_dependencies rules_that_have_matched
7f004419 1036 !rules_that_have_ever_matched r.scr_dependencies)
34e49164
C
1037 then
1038 begin
1be43e12
C
1039 print_dependencies "dependencies for script not satisfied:"
1040 rules_that_have_matched
7f004419 1041 !rules_that_have_ever_matched r.scr_dependencies;
1be43e12 1042 show_or_not_binding "in environment" e;
34e49164
C
1043 (cache, (e, rules_that_have_matched)::newes)
1044 end
1045 else
1046 begin
1047 let (_, mv, _) = r.scr_ast_rule in
978fd7e5
C
1048 let not_bound x = not (Pycocci.contains_binding e x) in
1049 (match List.filter not_bound mv with
1050 [] ->
34e49164
C
1051 let relevant_bindings =
1052 List.filter
1053 (function ((re,rm),_) ->
b1b2de81 1054 List.exists (function (_,(r,m)) -> r =$= re && m =$= rm) mv)
34e49164
C
1055 e in
1056 let new_cache =
1057 if List.mem relevant_bindings cache
951c7801
C
1058 then
1059 begin
1060 print_dependencies
1061 "dependencies for script satisfied, but cached:"
1062 rules_that_have_matched
7f004419 1063 !rules_that_have_ever_matched
951c7801
C
1064 r.scr_dependencies;
1065 show_or_not_binding "in" e;
1066 cache
1067 end
34e49164
C
1068 else
1069 begin
1be43e12
C
1070 print_dependencies "dependencies for script satisfied:"
1071 rules_that_have_matched
7f004419 1072 !rules_that_have_ever_matched
951c7801 1073 r.scr_dependencies;
1be43e12 1074 show_or_not_binding "in" e;
34e49164
C
1075 Pycocci.build_classes (List.map (function (x,y) -> x) e);
1076 Pycocci.construct_variables mv e;
1077 let _ = Pycocci.pyrun_simplestring
1078 ("import coccinelle\nfrom coccinelle "^
1079 "import *\ncocci = Cocci()\n" ^
1080 r.script_code) in
1081 relevant_bindings :: cache
1082 end in
1083 if !Pycocci.inc_match
1be43e12 1084 then (new_cache, merge_env [(e, rules_that_have_matched)] newes)
34e49164 1085 else (new_cache, newes)
978fd7e5
C
1086 | unbound ->
1087 (if !Flag_cocci.show_dependencies
1088 then
1089 let m2c (_,(r,x)) = r^"."^x in
1090 pr2 (Printf.sprintf "script not applied: %s not bound"
1091 (String.concat ", " (List.map m2c unbound))));
1092 (cache, merge_env [(e, rules_that_have_matched)] newes))
fc1ad971 1093 end)
34e49164 1094
7f004419 1095let rec apply_cocci_rule r rules_that_have_ever_matched es
b1b2de81 1096 (ccs:file_info list ref) =
34e49164
C
1097 Common.profile_code r.rulename (fun () ->
1098 show_or_not_rule_name r.ast_rule r.ruleid;
1099 show_or_not_ctl_text r.ctl r.ast_rule r.ruleid;
1100
1101 let reorganized_env =
1102 reassociate_positions r.free_vars r.negated_pos_vars !es in
1103
1104 (* looping over the environments *)
1105 let (_,newes (* envs for next round/rule *)) =
1106 List.fold_left
1107 (function (cache,newes) ->
1108 function ((e,rules_that_have_matched),relevant_bindings) ->
1109 if not(interpret_dependencies rules_that_have_matched
7f004419 1110 !rules_that_have_ever_matched
951c7801 1111 r.dependencies)
34e49164
C
1112 then
1113 begin
1be43e12
C
1114 print_dependencies
1115 ("dependencies for rule "^r.rulename^" not satisfied:")
1116 rules_that_have_matched
7f004419 1117 !rules_that_have_ever_matched r.dependencies;
1be43e12 1118 show_or_not_binding "in environment" e;
34e49164 1119 (cache,
1be43e12 1120 merge_env
34e49164 1121 [(e +> List.filter (fun (s,v) -> List.mem s r.used_after),
1be43e12
C
1122 rules_that_have_matched)]
1123 newes)
34e49164
C
1124 end
1125 else
1126 let new_bindings =
1127 try List.assoc relevant_bindings cache
1128 with
1129 Not_found ->
1be43e12
C
1130 print_dependencies
1131 ("dependencies for rule "^r.rulename^" satisfied:")
1132 rules_that_have_matched
7f004419 1133 !rules_that_have_ever_matched
951c7801 1134 r.dependencies;
1be43e12
C
1135 show_or_not_binding "in" e;
1136 show_or_not_binding "relevant in" relevant_bindings;
34e49164 1137
faf9a90c
C
1138 (* applying the rule *)
1139 (match r.ruletype with
1140 Ast_cocci.Normal ->
34e49164
C
1141 (* looping over the functions and toplevel elements in
1142 .c and .h *)
708f4980
C
1143 List.rev
1144 (concat_headers_and_c !ccs +>
1145 List.fold_left (fun children_e (c,f) ->
1146 if c.flow <> None
1147 then
1148 (* does also some side effects on c and r *)
1149 let processed =
1150 process_a_ctl_a_env_a_toplevel r
1151 relevant_bindings c f in
1152 match processed with
1153 | None -> children_e
1154 | Some newbindings ->
1155 newbindings +>
1156 List.fold_left
1157 (fun children_e newbinding ->
1158 if List.mem newbinding children_e
1159 then children_e
1160 else newbinding :: children_e)
1161 children_e
1162 else children_e)
1163 [])
faf9a90c
C
1164 | Ast_cocci.Generated ->
1165 process_a_generated_a_env_a_toplevel r
1166 relevant_bindings !ccs;
1167 []) in
1168
34e49164
C
1169 let old_bindings_to_keep =
1170 Common.nub
1171 (e +> List.filter (fun (s,v) -> List.mem s r.used_after)) in
1172 let new_e =
1173 if null new_bindings
1174 then
1175 begin
1176 (*use the old bindings, specialized to the used_after_list*)
1177 if !Flag_ctl.partial_match
1178 then
1179 printf
7f004419 1180 "Empty list of bindings, I will restart from old env\n";
34e49164
C
1181 [(old_bindings_to_keep,rules_that_have_matched)]
1182 end
1183 else
1184 (* combine the new bindings with the old ones, and
1185 specialize to the used_after_list *)
1186 let old_variables = List.map fst old_bindings_to_keep in
1187 (* have to explicitly discard the inherited variables
1188 because we want the inherited value of the positions
1189 variables not the extended one created by
1190 reassociate_positions. want to reassociate freshly
1191 according to the free variables of each rule. *)
1192 let new_bindings_to_add =
1193 Common.nub
1194 (new_bindings +>
1195 List.map
1196 (List.filter
1197 (fun (s,v) ->
1198 List.mem s r.used_after &&
1199 not (List.mem s old_variables)))) in
1200 List.map
1201 (function new_binding_to_add ->
1be43e12
C
1202 (List.sort compare
1203 (Common.union_set
1204 old_bindings_to_keep new_binding_to_add),
34e49164
C
1205 r.rulename::rules_that_have_matched))
1206 new_bindings_to_add in
1207 ((relevant_bindings,new_bindings)::cache,
1be43e12 1208 merge_env new_e newes))
34e49164
C
1209 ([],[]) reorganized_env in (* end iter es *)
1210 if !(r.was_matched)
1211 then Common.push2 r.rulename rules_that_have_ever_matched;
1212
1213 es := newes;
1214
1215 (* apply the tagged modifs and reparse *)
1216 if not !Flag.sgrep_mode2
b1b2de81 1217 then ccs := rebuild_info_c_and_headers !ccs r.isexp)
34e49164
C
1218
1219and reassociate_positions free_vars negated_pos_vars envs =
1220 (* issues: isolate the bindings that are relevant to a given rule.
1221 separate out the position variables
1222 associate all of the position variables for a given set of relevant
1223 normal variable bindings with each set of relevant normal variable
1224 bindings. Goal: if eg if@p (E) matches in two places, then both inherited
1225 occurrences of E should see both bindings of p, not just its own.
1226 Otherwise, a position constraint for something that matches in two
1227 places will never be useful, because the position can always be
1228 different from the other one. *)
1229 let relevant =
1230 List.map
1231 (function (e,_) ->
1232 List.filter (function (x,_) -> List.mem x free_vars) e)
1233 envs in
1234 let splitted_relevant =
1235 (* separate the relevant variables into the non-position ones and the
1236 position ones *)
1237 List.map
1238 (function r ->
1239 List.fold_left
1240 (function (non_pos,pos) ->
1241 function (v,_) as x ->
1242 if List.mem v negated_pos_vars
1243 then (non_pos,x::pos)
1244 else (x::non_pos,pos))
1245 ([],[]) r)
1246 relevant in
1247 let splitted_relevant =
1248 List.map
1249 (function (non_pos,pos) ->
1250 (List.sort compare non_pos,List.sort compare pos))
1251 splitted_relevant in
1252 let non_poss =
1253 List.fold_left
1254 (function non_pos ->
1255 function (np,_) ->
1256 if List.mem np non_pos then non_pos else np::non_pos)
1257 [] splitted_relevant in
1258 let extended_relevant =
1259 (* extend the position variables with the values found at other identical
1260 variable bindings *)
1261 List.map
1262 (function non_pos ->
1263 let others =
1264 List.filter
1265 (function (other_non_pos,other_pos) ->
1266 (* do we want equal? or just somehow compatible? eg non_pos
1267 binds only E, but other_non_pos binds both E and E1 *)
b1b2de81 1268 non_pos =*= other_non_pos)
34e49164
C
1269 splitted_relevant in
1270 (non_pos,
1271 List.sort compare
1272 (non_pos @
1273 (combine_pos negated_pos_vars
1274 (List.map (function (_,x) -> x) others)))))
1275 non_poss in
1276 List.combine envs
1277 (List.map (function (non_pos,_) -> List.assoc non_pos extended_relevant)
1278 splitted_relevant)
1279
1280and combine_pos negated_pos_vars others =
1281 List.map
1282 (function posvar ->
1283 (posvar,
1284 Ast_c.MetaPosValList
1285 (List.sort compare
1286 (List.fold_left
1287 (function positions ->
1288 function other_list ->
1289 try
1290 match List.assoc posvar other_list with
1291 Ast_c.MetaPosValList l1 ->
1292 Common.union_set l1 positions
1293 | _ -> failwith "bad value for a position variable"
1294 with Not_found -> positions)
1295 [] others))))
1296 negated_pos_vars
1297
b1b2de81
C
1298and process_a_generated_a_env_a_toplevel2 r env = function
1299 [cfile] ->
1300 let free_vars =
1301 List.filter
1302 (function
1303 (rule,_) when rule =$= r.rulename -> false
1304 | (_,"ARGS") -> false
1305 | _ -> true)
1306 r.free_vars in
1307 let env_domain = List.map (function (nm,vl) -> nm) env in
1308 let metavars =
1309 List.filter
1310 (function md ->
1311 let (rl,_) = Ast_cocci.get_meta_name md in
1312 rl =$= r.rulename)
1313 r.metavars in
1314 if Common.include_set free_vars env_domain
1315 then Unparse_hrule.pp_rule metavars r.ast_rule env cfile.full_fname
1316 | _ -> failwith "multiple files not supported"
34e49164 1317
b1b2de81
C
1318and process_a_generated_a_env_a_toplevel rule env ccs =
1319 Common.profile_code "process_a_ctl_a_env_a_toplevel"
1320 (fun () -> process_a_generated_a_env_a_toplevel2 rule env ccs)
34e49164
C
1321
1322(* does side effects on C ast and on Cocci info rule *)
485bce71 1323and process_a_ctl_a_env_a_toplevel2 r e c f =
34e49164
C
1324 indent_do (fun () ->
1325 show_or_not_celem "trying" c.ast_c;
485bce71 1326 Flag.currentfile := Some (f ^ ":" ^get_celem c.ast_c);
1be43e12 1327 let (trans_info, returned_any_states, inherited_bindings, newbindings) =
34e49164
C
1328 Common.save_excursion Flag_ctl.loop_in_src_code (fun () ->
1329 Flag_ctl.loop_in_src_code := !Flag_ctl.loop_in_src_code||c.contain_loop;
1330
1331 (***************************************)
1332 (* !Main point! The call to the engine *)
1333 (***************************************)
1334 let model_ctl = CCI.model_for_ctl r.dropped_isos (Common.some c.flow) e
1335 in CCI.mysat model_ctl r.ctl (r.used_after, e)
1336 )
1337 in
1338 if not returned_any_states
1339 then None
1340 else begin
1341 show_or_not_celem "found match in" c.ast_c;
1342 show_or_not_trans_info trans_info;
1343 List.iter (show_or_not_binding "out") newbindings;
1344
1345 r.was_matched := true;
1346
1347 if not (null trans_info)
1348 then begin
1349 c.was_modified := true;
1350 try
1351 (* les "more than one var in a decl" et "already tagged token"
1352 * font crasher coccinelle. Si on a 5 fichiers, donc on a 5
1353 * failed. Le try limite le scope des crashes pendant la
1354 * trasformation au fichier concerne. *)
1355
1356 (* modify ast via side effect *)
485bce71 1357 ignore(Transformation_c.transform r.rulename r.dropped_isos
1be43e12 1358 inherited_bindings trans_info (Common.some c.flow));
34e49164
C
1359 with Timeout -> raise Timeout | UnixExit i -> raise (UnixExit i)
1360 end;
1361
1be43e12 1362 Some (List.map (function x -> x@inherited_bindings) newbindings)
34e49164
C
1363 end
1364 )
1365
485bce71 1366and process_a_ctl_a_env_a_toplevel a b c f=
34e49164 1367 Common.profile_code "process_a_ctl_a_env_a_toplevel"
485bce71 1368 (fun () -> process_a_ctl_a_env_a_toplevel2 a b c f)
faf9a90c 1369
34e49164 1370
7f004419
C
1371let rec bigloop2 rs (ccs: file_info list) =
1372 let init_es = [(Ast_c.emptyMetavarsBinding,[])] in
1373 let es = ref init_es in
b1b2de81
C
1374 let ccs = ref ccs in
1375 let rules_that_have_ever_matched = ref [] in
1376
1377 (* looping over the rules *)
1378 rs +> List.iter (fun r ->
1379 match r with
1380 InitialScriptRuleCocciInfo r | FinalScriptRuleCocciInfo r -> ()
1381 | ScriptRuleCocciInfo r ->
1382 if !Flag_cocci.show_ctl_text then begin
1383 Common.pr_xxxxxxxxxxxxxxxxx ();
1384 pr ("script: " ^ r.language);
1385 Common.pr_xxxxxxxxxxxxxxxxx ();
1386
1387 adjust_pp_with_indent (fun () ->
1388 Format.force_newline();
1389 let (l,mv,code) = r.scr_ast_rule in
1390 let deps = r.scr_dependencies in
1391 Pretty_print_cocci.unparse
1392 (Ast_cocci.ScriptRule (l,deps,mv,code)));
1393 end;
1394
1395 if !Flag.show_misc then print_endline "RESULT =";
1396
1397 let (_, newes) =
1398 List.fold_left
1399 (function (cache, newes) ->
1400 function (e, rules_that_have_matched) ->
1401 match r.language with
1402 "python" ->
1403 apply_python_rule r cache newes e rules_that_have_matched
7f004419 1404 rules_that_have_ever_matched
b1b2de81
C
1405 | "test" ->
1406 concat_headers_and_c !ccs +> List.iter (fun (c,_) ->
1407 if c.flow <> None
1408 then
1409 Printf.printf "Flow: %s\r\nFlow!\r\n%!" c.fullstring);
1410 (cache, newes)
1411 | _ ->
1412 Printf.printf "Unknown language: %s\n" r.language;
1413 (cache, newes)
1414 )
1415 ([],[]) !es in
1416
7f004419 1417 es := (if newes = [] then init_es else newes);
b1b2de81 1418 | CocciRuleCocciInfo r ->
7f004419 1419 apply_cocci_rule r rules_that_have_ever_matched
951c7801 1420 es ccs);
b1b2de81
C
1421
1422 if !Flag.sgrep_mode2
1423 then begin
1424 (* sgrep can lead to code that is not parsable, but we must
1425 * still call rebuild_info_c_and_headers to pretty print the
1426 * action (MINUS), so that later the diff will show what was
1427 * matched by sgrep. But we don't want the parsing error message
1428 * hence the following flag setting. So this code propably
1429 * will generate a NotParsedCorrectly for the matched parts
1430 * and the very final pretty print and diff will work
1431 *)
1432 Flag_parsing_c.verbose_parsing := false;
1433 ccs := rebuild_info_c_and_headers !ccs false
1434 end;
1435 !ccs (* return final C asts *)
1436
1437let bigloop a b =
1438 Common.profile_code "bigloop" (fun () -> bigloop2 a b)
1439
1440let initial_final_bigloop2 ty rebuild r =
1441 if !Flag_cocci.show_ctl_text then
1442 begin
1443 Common.pr_xxxxxxxxxxxxxxxxx ();
1444 pr (ty ^ ": " ^ r.language);
1445 Common.pr_xxxxxxxxxxxxxxxxx ();
1446
1447 adjust_pp_with_indent (fun () ->
1448 Format.force_newline();
1449 Pretty_print_cocci.unparse(rebuild r.scr_ast_rule));
1450 end;
1451
1452 match r.language with
1453 "python" ->
1454 (* include_match makes no sense in an initial or final rule, although
1455 er have no way to prevent it *)
7f004419 1456 let _ = apply_python_rule r [] [] [] [] (ref []) in
951c7801 1457 ()
b1b2de81
C
1458 | _ ->
1459 Printf.printf "Unknown language for initial/final script: %s\n"
1460 r.language
1461
1462let initial_final_bigloop a b c =
1463 Common.profile_code "initial_final_bigloop"
1464 (fun () -> initial_final_bigloop2 a b c)
34e49164
C
1465
1466(*****************************************************************************)
b1b2de81 1467(* The main functions *)
34e49164
C
1468(*****************************************************************************)
1469
b1b2de81
C
1470let pre_engine2 (coccifile, isofile) =
1471 show_or_not_cocci coccifile isofile;
34e49164
C
1472 Pycocci.set_coccifile coccifile;
1473
1474 let isofile =
1475 if not (Common.lfile_exists isofile)
1476 then begin
1477 pr2 ("warning: Can't find default iso file: " ^ isofile);
1478 None
1479 end
b1b2de81 1480 else Some isofile in
34e49164
C
1481
1482 (* useful opti when use -dir *)
faf9a90c 1483 let (metavars,astcocci,free_var_lists,negated_pos_lists,used_after_lists,
7f004419 1484 positions_lists,toks,_) =
b1b2de81
C
1485 sp_of_file coccifile isofile in
1486 let ctls = ctls_of_ast astcocci used_after_lists positions_lists in
34e49164 1487
b1b2de81 1488 g_contain_typedmetavar := sp_contain_typed_metavar astcocci;
34e49164 1489
b1b2de81 1490 check_macro_in_sp_and_adjust toks;
34e49164 1491
b1b2de81 1492 show_or_not_ctl_tex astcocci ctls;
34e49164 1493
b1b2de81
C
1494 let cocci_infos =
1495 prepare_cocci ctls free_var_lists negated_pos_lists
1496 used_after_lists positions_lists metavars astcocci in
34e49164 1497
b1b2de81
C
1498 let _ =
1499 List.fold_left
1500 (function languages ->
1501 function
1502 InitialScriptRuleCocciInfo(r) ->
1503 (if List.mem r.language languages
1504 then failwith ("double initializer found for "^r.language));
1505 initial_final_bigloop "initial"
1506 (function(x,_,y) -> Ast_cocci.InitialScriptRule(x,y))
1507 r;
1508 r.language::languages
1509 | _ -> languages)
1510 [] cocci_infos in
951c7801 1511
7f004419 1512 (cocci_infos,toks)
34e49164 1513
b1b2de81
C
1514let pre_engine a =
1515 Common.profile_code "pre_engine" (fun () -> pre_engine2 a)
faf9a90c 1516
7f004419 1517let full_engine2 (cocci_infos,toks) cfiles =
b1b2de81
C
1518
1519 show_or_not_cfiles cfiles;
1520
1521 (* optimisation allowing to launch coccinelle on all the drivers *)
1522 if !Flag_cocci.worth_trying_opt && not (worth_trying cfiles toks)
1523 then
1524 begin
7f004419
C
1525 pr2 ("No matches found for " ^ (Common.join " " (Common.union_all toks))
1526 ^ "\nSkipping:" ^ (Common.join " " cfiles));
b1b2de81
C
1527 cfiles +> List.map (fun s -> s, None)
1528 end
1529 else
1530 begin
1531
1532 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1533 if !Flag.show_misc then pr "let's go";
1534 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1535
1536 let choose_includes =
1537 match !Flag_cocci.include_options with
1538 Flag_cocci.I_UNSPECIFIED ->
1539 if !g_contain_typedmetavar
1540 then Flag_cocci.I_NORMAL_INCLUDES
1541 else Flag_cocci.I_NO_INCLUDES
1542 | x -> x in
1543 let c_infos = prepare_c cfiles choose_includes in
1544
1545 (* ! the big loop ! *)
7f004419 1546 let c_infos' = bigloop cocci_infos c_infos in
b1b2de81
C
1547
1548 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1549 if !Flag.show_misc then pr "Finished";
1550 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1551 if !Flag_ctl.graphical_trace then gen_pdf_graph ();
1552
1553 c_infos' +> List.map (fun c_or_h ->
1554 if !(c_or_h.was_modified_once)
1555 then
1556 begin
1557 let outfile =
1558 Common.new_temp_file "cocci-output" ("-" ^ c_or_h.fname) in
1559
1560 if c_or_h.fkind =*= Header
1561 then pr2 ("a header file was modified: " ^ c_or_h.fname);
1562
1563 (* and now unparse everything *)
1564 cfile_of_program (for_unparser c_or_h.asts) outfile;
1565
1566 let show_only_minus = !Flag.sgrep_mode2 in
1567 show_or_not_diff c_or_h.fpath outfile show_only_minus;
1568
1569 (c_or_h.fpath,
1570 if !Flag.sgrep_mode2 then None else Some outfile)
1571 end
1572 else (c_or_h.fpath, None))
1573 end
34e49164
C
1574
1575let full_engine a b =
fc1ad971
C
1576 Common.profile_code "full_engine"
1577 (fun () -> let res = full_engine2 a b in (*Gc.print_stat stderr; *)res)
34e49164 1578
7f004419 1579let post_engine2 (cocci_infos,_) =
b1b2de81
C
1580 let _ =
1581 List.fold_left
1582 (function languages ->
1583 function
1584 FinalScriptRuleCocciInfo(r) ->
1585 (if List.mem r.language languages
1586 then failwith ("double finalizer found for "^r.language));
1587 initial_final_bigloop "final"
1588 (function(x,_,y) -> Ast_cocci.FinalScriptRule(x,y))
1589 r;
1590 r.language::languages
1591 | _ -> languages)
1592 [] cocci_infos in
1593 ()
1594
1595let post_engine a =
1596 Common.profile_code "post_engine" (fun () -> post_engine2 a)
34e49164
C
1597
1598(*****************************************************************************)
1599(* check duplicate from result of full_engine *)
1600(*****************************************************************************)
1601
1602let check_duplicate_modif2 xs =
1603 (* opti: let groups = Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) xs *)
708f4980
C
1604 if !Flag_cocci.verbose_cocci
1605 then pr2 ("Check duplication for " ^ i_to_s (List.length xs) ^ " files");
1606
34e49164
C
1607 let groups = Common.group_assoc_bykey_eff xs in
1608 groups +> Common.map_filter (fun (file, xs) ->
1609 match xs with
1610 | [] -> raise Impossible
1611 | [res] -> Some (file, res)
1612 | res::xs ->
1613 match res with
1614 | None ->
b1b2de81 1615 if not (List.for_all (fun res2 -> res2 =*= None) xs)
34e49164
C
1616 then begin
1617 pr2 ("different modification result for " ^ file);
1618 None
1619 end
1620 else Some (file, None)
1621 | Some res ->
1622 if not(List.for_all (fun res2 ->
1623 match res2 with
1624 | None -> false
1625 | Some res2 ->
1626 let diff = Common.cmd_to_list ("diff -u -b -B "^res^" "^res2)
1627 in
1628 null diff
1629 ) xs) then begin
1630 pr2 ("different modification result for " ^ file);
1631 None
1632 end
1633 else Some (file, Some res)
1634
1635
1636 )
1637let check_duplicate_modif a =
1638 Common.profile_code "check_duplicate" (fun () -> check_duplicate_modif2 a)
1639