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