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