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