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