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