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