Release coccinelle-0.1.9
[bpt/coccinelle.git] / cocci.ml
CommitLineData
34e49164 1(*
faf9a90c 2* Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
34e49164
C
3* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller
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
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
b1b2de81 112 then failwith "no popl here" (* 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
155 match Common.fst(Compare_c.compare_default cfile outfile) with
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
C
512
513let rec interpret_dependencies local global = function
514 Ast_cocci.Dep s -> List.mem s local
515 | Ast_cocci.AntiDep s ->
516 (if !Flag_ctl.steps != None
517 then failwith "steps and ! dependency incompatible");
518 not (List.mem s local)
519 | Ast_cocci.EverDep s -> List.mem s global
520 | Ast_cocci.NeverDep s ->
521 (if !Flag_ctl.steps != None
522 then failwith "steps and ! dependency incompatible");
523 not (List.mem s global)
524 | Ast_cocci.AndDep(s1,s2) ->
525 (interpret_dependencies local global s1) &&
526 (interpret_dependencies local global s2)
527 | Ast_cocci.OrDep(s1,s2) ->
528 (interpret_dependencies local global s1) or
529 (interpret_dependencies local global s2)
530 | Ast_cocci.NoDep -> true
531
1be43e12
C
532let rec print_dependencies str local global dep =
533 if !Flag_cocci.show_dependencies
534 then
535 begin
536 pr2 str;
537 let seen = ref [] in
538 let rec loop = function
539 Ast_cocci.Dep s | Ast_cocci.AntiDep s ->
540 if not (List.mem s !seen)
541 then
542 begin
543 if List.mem s local
544 then pr2 (s^" satisfied")
545 else pr2 (s^" not satisfied");
546 seen := s :: !seen
547 end
548 | Ast_cocci.EverDep s | Ast_cocci.NeverDep s ->
549 if not (List.mem s !seen)
550 then
551 begin
552 if List.mem s global
553 then pr2 (s^" satisfied")
554 else pr2 (s^" not satisfied");
555 seen := s :: !seen
556 end
557 | Ast_cocci.AndDep(s1,s2) ->
558 loop s1;
559 loop s2
560 | Ast_cocci.OrDep(s1,s2) ->
561 loop s1;
562 loop s2
563 | Ast_cocci.NoDep -> () in
564 loop dep
565 end
34e49164
C
566
567
568
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
b1b2de81
C
704type cocci_info = toplevel_cocci_info list * string list list (* tokens *)
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
978fd7e5 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 =
853 ast_to_flow_with_error_messages c +> Common.map_option (fun flow ->
854 let flow = Ast_to_flow.annotate_loop_nodes flow in
855
856 (* remove the fake nodes for julia *)
857 let fixed_flow = CCI.fix_flow_ctl flow in
858
859 if !Flag_cocci.show_flow then print_flow fixed_flow;
860 if !Flag_cocci.show_before_fixed_flow then print_flow flow;
861
862 fixed_flow
863 )
864 in
865
866 {
867 ast_c = c; (* contain refs so can be modified *)
868 tokens_c = tokens;
869 fullstring = fullstr;
870
871 flow = flow;
872
873 contain_loop = contain_loop flow;
874
875 env_typing_before = enva;
876 env_typing_after = envb;
877
878 was_modified = ref false;
879 }
880 )
881
882
883
884(* Optimisation. Try not unparse/reparse the whole file when have modifs *)
885let rebuild_info_program cs file isexp =
886 cs +> List.map (fun c ->
887 if !(c.was_modified)
888 then
faf9a90c
C
889 let file = Common.new_temp_file "cocci_small_output" ".c" in
890 cfile_of_program
891 [(c.ast_c, (c.fullstring, c.tokens_c)), Unparse_c.PPnormal]
892 file;
34e49164 893
faf9a90c
C
894 (* Common.command2 ("cat " ^ file); *)
895 let cprogram = cprogram_of_file file in
896 let xs = build_info_program cprogram c.env_typing_before in
34e49164 897
faf9a90c
C
898 (* TODO: assert env has not changed,
899 * if yes then must also reparse what follows even if not modified.
900 * Do that only if contain_typedmetavar of course, so good opti.
901 *)
902 (* Common.list_init xs *) (* get rid of the FinalDef *)
903 xs
34e49164
C
904 else [c]
905 ) +> List.concat
906
907
908let rebuild_info_c_and_headers ccs isexp =
909 ccs +> List.iter (fun c_or_h ->
910 if c_or_h.asts +> List.exists (fun c -> !(c.was_modified))
911 then c_or_h.was_modified_once := true;
912 );
913 ccs +> List.map (fun c_or_h ->
914 { c_or_h with
915 asts = rebuild_info_program c_or_h.asts c_or_h.full_fname isexp }
916 )
917
918
919
920
921
922
923
faf9a90c 924let prepare_c files choose_includes : file_info list =
34e49164 925 let cprograms = List.map cprogram_of_file_cached files in
faf9a90c 926 let includes = includes_to_parse (zip files cprograms) choose_includes in
34e49164
C
927
928 (* todo?: may not be good to first have all the headers and then all the c *)
929 let all =
930 (includes +> List.map (fun hpath -> Right hpath))
931 ++
932 ((zip files cprograms) +> List.map (fun (file, asts) -> Left (file, asts)))
933 in
934
91eba41f 935 let env = ref !TAC.initial_env in
34e49164
C
936
937 let ccs = all +> Common.map_filter (fun x ->
938 match x with
939 | Right hpath ->
940 if not (Common.lfile_exists hpath)
941 then begin
942 pr2 ("TYPE: header " ^ hpath ^ " not found");
943 None
944 end
945 else
946 let h_cs = cprogram_of_file_cached hpath in
947 let info_h_cs = build_info_program h_cs !env in
948 env :=
949 if null info_h_cs
950 then !env
951 else last_env_toplevel_c_info info_h_cs
952 ;
953 Some {
954 fname = Common.basename hpath;
955 full_fname = hpath;
956 asts = info_h_cs;
957 was_modified_once = ref false;
958 fpath = hpath;
959 fkind = Header;
960 }
961 | Left (file, cprogram) ->
962 (* todo?: don't update env ? *)
963 let cs = build_info_program cprogram !env in
964 (* we do that only for the c, not for the h *)
965 ignore(update_include_rel_pos (cs +> List.map (fun x -> x.ast_c)));
966 Some {
967 fname = Common.basename file;
968 full_fname = file;
969 asts = cs;
970 was_modified_once = ref false;
971 fpath = file;
972 fkind = Source;
973 }
974 )
975 in
976 ccs
977
978
979(*****************************************************************************)
980(* Processing the ctls and toplevel C elements *)
981(*****************************************************************************)
982
983(* The main algorithm =~
984 * The algorithm is roughly:
985 * for_all ctl rules in SP
986 * for_all minirule in rule (no more)
987 * for_all binding (computed during previous phase)
988 * for_all C elements
989 * match control flow of function vs minirule
990 * with the binding and update the set of possible
991 * bindings, and returned the possibly modified function.
992 * pretty print modified C elements and reparse it.
993 *
994 *
995 * On ne prends que les newbinding ou returned_any_state est vrai.
996 * Si ca ne donne rien, on prends ce qu'il y avait au depart.
997 * Mais au nouveau depart de quoi ?
998 * - si ca donne rien apres avoir traité toutes les fonctions avec ce binding ?
999 * - ou alors si ca donne rien, apres avoir traité toutes les fonctions
1000 * avec tous les bindings du round d'avant ?
1001 *
1002 * Julia pense qu'il faut prendre la premiere solution.
1003 * Example: on a deux environnements candidats, E1 et E2 apres avoir traité
1004 * la regle ctl 1. On arrive sur la regle ctl 2.
1005 * E1 ne donne rien pour la regle 2, on garde quand meme E1 pour la regle 3.
1006 * E2 donne un match a un endroit et rend E2' alors on utilise ca pour
1007 * la regle 3.
1008 *
1009 * I have not to look at used_after_list to decide to restart from
1010 * scratch. I just need to look if the binding list is empty.
1011 * Indeed, let's suppose that a SP have 3 regions/rules. If we
1012 * don't find a match for the first region, then if this first
1013 * region does not bind metavariable used after, that is if
1014 * used_after_list is empty, then mysat(), even if does not find a
1015 * match, will return a Left, with an empty transformation_info,
1016 * and so current_binding will grow. On the contrary if the first
1017 * region must bind some metavariables used after, and that we
1018 * dont find any such region, then mysat() will returns lots of
1019 * Right, and current_binding will not grow, and so we will have
1020 * an empty list of binding, and we will catch such a case.
1021 *
1022 * opti: julia says that because the binding is
1023 * determined by the used_after_list, the items in the list
1024 * are kind of sorted, so could optimise the insert_set operations.
1025 *)
1026
1027
1028(* r(ule), c(element in C code), e(nvironment) *)
1029
708f4980
C
1030let findk f l =
1031 let rec loop k = function
1032 [] -> None
1033 | x::xs ->
1034 if f x
1035 then Some (x, function n -> k (n :: xs))
1036 else loop (function vs -> k (x :: vs)) xs in
1037 loop (function x -> x) l
1038
b1b2de81 1039let merge_env new_e old_e =
708f4980
C
1040 let (ext,old_e) =
1041 List.fold_left
1042 (function (ext,old_e) ->
1043 function (e,rules) as elem ->
1044 match findk (function (e1,_) -> e =*= e1) old_e with
1045 None -> (elem :: ext,old_e)
1046 | Some((_,old_rules),k) ->
1047 (ext,k (e,Common.union_set rules old_rules)))
1048 ([],old_e) new_e in
1049 old_e @ (List.rev ext)
b1b2de81
C
1050
1051let apply_python_rule r cache newes e rules_that_have_matched
34e49164
C
1052 rules_that_have_ever_matched =
1053 show_or_not_scr_rule_name r.scr_ruleid;
1054 if not(interpret_dependencies rules_that_have_matched
1055 !rules_that_have_ever_matched r.scr_dependencies)
1056 then
1057 begin
1be43e12
C
1058 print_dependencies "dependencies for script not satisfied:"
1059 rules_that_have_matched
1060 !rules_that_have_ever_matched r.scr_dependencies;
1061 show_or_not_binding "in environment" e;
34e49164
C
1062 (cache, (e, rules_that_have_matched)::newes)
1063 end
1064 else
1065 begin
1066 let (_, mv, _) = r.scr_ast_rule in
978fd7e5
C
1067 let not_bound x = not (Pycocci.contains_binding e x) in
1068 (match List.filter not_bound mv with
1069 [] ->
34e49164
C
1070 let relevant_bindings =
1071 List.filter
1072 (function ((re,rm),_) ->
b1b2de81 1073 List.exists (function (_,(r,m)) -> r =$= re && m =$= rm) mv)
34e49164
C
1074 e in
1075 let new_cache =
1076 if List.mem relevant_bindings cache
1077 then cache
1078 else
1079 begin
1be43e12
C
1080 print_dependencies "dependencies for script satisfied:"
1081 rules_that_have_matched
1082 !rules_that_have_ever_matched r.scr_dependencies;
1083 show_or_not_binding "in" e;
34e49164
C
1084 Pycocci.build_classes (List.map (function (x,y) -> x) e);
1085 Pycocci.construct_variables mv e;
1086 let _ = Pycocci.pyrun_simplestring
1087 ("import coccinelle\nfrom coccinelle "^
1088 "import *\ncocci = Cocci()\n" ^
1089 r.script_code) in
1090 relevant_bindings :: cache
1091 end in
1092 if !Pycocci.inc_match
1be43e12 1093 then (new_cache, merge_env [(e, rules_that_have_matched)] newes)
34e49164 1094 else (new_cache, newes)
978fd7e5
C
1095 | unbound ->
1096 (if !Flag_cocci.show_dependencies
1097 then
1098 let m2c (_,(r,x)) = r^"."^x in
1099 pr2 (Printf.sprintf "script not applied: %s not bound"
1100 (String.concat ", " (List.map m2c unbound))));
1101 (cache, merge_env [(e, rules_that_have_matched)] newes))
34e49164
C
1102 end
1103
b1b2de81
C
1104let rec apply_cocci_rule r rules_that_have_ever_matched es
1105 (ccs:file_info list ref) =
34e49164
C
1106 Common.profile_code r.rulename (fun () ->
1107 show_or_not_rule_name r.ast_rule r.ruleid;
1108 show_or_not_ctl_text r.ctl r.ast_rule r.ruleid;
1109
1110 let reorganized_env =
1111 reassociate_positions r.free_vars r.negated_pos_vars !es in
1112
1113 (* looping over the environments *)
1114 let (_,newes (* envs for next round/rule *)) =
1115 List.fold_left
1116 (function (cache,newes) ->
1117 function ((e,rules_that_have_matched),relevant_bindings) ->
1118 if not(interpret_dependencies rules_that_have_matched
1119 !rules_that_have_ever_matched r.dependencies)
1120 then
1121 begin
1be43e12
C
1122 print_dependencies
1123 ("dependencies for rule "^r.rulename^" not satisfied:")
1124 rules_that_have_matched
1125 !rules_that_have_ever_matched r.dependencies;
1126 show_or_not_binding "in environment" e;
34e49164 1127 (cache,
1be43e12 1128 merge_env
34e49164 1129 [(e +> List.filter (fun (s,v) -> List.mem s r.used_after),
1be43e12
C
1130 rules_that_have_matched)]
1131 newes)
34e49164
C
1132 end
1133 else
1134 let new_bindings =
1135 try List.assoc relevant_bindings cache
1136 with
1137 Not_found ->
1be43e12
C
1138 print_dependencies
1139 ("dependencies for rule "^r.rulename^" satisfied:")
1140 rules_that_have_matched
1141 !rules_that_have_ever_matched r.dependencies;
1142 show_or_not_binding "in" e;
1143 show_or_not_binding "relevant in" relevant_bindings;
34e49164 1144
faf9a90c
C
1145 (* applying the rule *)
1146 (match r.ruletype with
1147 Ast_cocci.Normal ->
34e49164
C
1148 (* looping over the functions and toplevel elements in
1149 .c and .h *)
708f4980
C
1150 List.rev
1151 (concat_headers_and_c !ccs +>
1152 List.fold_left (fun children_e (c,f) ->
1153 if c.flow <> None
1154 then
1155 (* does also some side effects on c and r *)
1156 let processed =
1157 process_a_ctl_a_env_a_toplevel r
1158 relevant_bindings c f in
1159 match processed with
1160 | None -> children_e
1161 | Some newbindings ->
1162 newbindings +>
1163 List.fold_left
1164 (fun children_e newbinding ->
1165 if List.mem newbinding children_e
1166 then children_e
1167 else newbinding :: children_e)
1168 children_e
1169 else children_e)
1170 [])
faf9a90c
C
1171 | Ast_cocci.Generated ->
1172 process_a_generated_a_env_a_toplevel r
1173 relevant_bindings !ccs;
1174 []) in
1175
34e49164
C
1176 let old_bindings_to_keep =
1177 Common.nub
1178 (e +> List.filter (fun (s,v) -> List.mem s r.used_after)) in
1179 let new_e =
1180 if null new_bindings
1181 then
1182 begin
1183 (*use the old bindings, specialized to the used_after_list*)
1184 if !Flag_ctl.partial_match
1185 then
1186 printf
1187 "Empty list of bindings, I will restart from old env";
1188 [(old_bindings_to_keep,rules_that_have_matched)]
1189 end
1190 else
1191 (* combine the new bindings with the old ones, and
1192 specialize to the used_after_list *)
1193 let old_variables = List.map fst old_bindings_to_keep in
1194 (* have to explicitly discard the inherited variables
1195 because we want the inherited value of the positions
1196 variables not the extended one created by
1197 reassociate_positions. want to reassociate freshly
1198 according to the free variables of each rule. *)
1199 let new_bindings_to_add =
1200 Common.nub
1201 (new_bindings +>
1202 List.map
1203 (List.filter
1204 (fun (s,v) ->
1205 List.mem s r.used_after &&
1206 not (List.mem s old_variables)))) in
1207 List.map
1208 (function new_binding_to_add ->
1be43e12
C
1209 (List.sort compare
1210 (Common.union_set
1211 old_bindings_to_keep new_binding_to_add),
34e49164
C
1212 r.rulename::rules_that_have_matched))
1213 new_bindings_to_add in
1214 ((relevant_bindings,new_bindings)::cache,
1be43e12 1215 merge_env new_e newes))
34e49164
C
1216 ([],[]) reorganized_env in (* end iter es *)
1217 if !(r.was_matched)
1218 then Common.push2 r.rulename rules_that_have_ever_matched;
1219
1220 es := newes;
1221
1222 (* apply the tagged modifs and reparse *)
1223 if not !Flag.sgrep_mode2
b1b2de81 1224 then ccs := rebuild_info_c_and_headers !ccs r.isexp)
34e49164
C
1225
1226and reassociate_positions free_vars negated_pos_vars envs =
1227 (* issues: isolate the bindings that are relevant to a given rule.
1228 separate out the position variables
1229 associate all of the position variables for a given set of relevant
1230 normal variable bindings with each set of relevant normal variable
1231 bindings. Goal: if eg if@p (E) matches in two places, then both inherited
1232 occurrences of E should see both bindings of p, not just its own.
1233 Otherwise, a position constraint for something that matches in two
1234 places will never be useful, because the position can always be
1235 different from the other one. *)
1236 let relevant =
1237 List.map
1238 (function (e,_) ->
1239 List.filter (function (x,_) -> List.mem x free_vars) e)
1240 envs in
1241 let splitted_relevant =
1242 (* separate the relevant variables into the non-position ones and the
1243 position ones *)
1244 List.map
1245 (function r ->
1246 List.fold_left
1247 (function (non_pos,pos) ->
1248 function (v,_) as x ->
1249 if List.mem v negated_pos_vars
1250 then (non_pos,x::pos)
1251 else (x::non_pos,pos))
1252 ([],[]) r)
1253 relevant in
1254 let splitted_relevant =
1255 List.map
1256 (function (non_pos,pos) ->
1257 (List.sort compare non_pos,List.sort compare pos))
1258 splitted_relevant in
1259 let non_poss =
1260 List.fold_left
1261 (function non_pos ->
1262 function (np,_) ->
1263 if List.mem np non_pos then non_pos else np::non_pos)
1264 [] splitted_relevant in
1265 let extended_relevant =
1266 (* extend the position variables with the values found at other identical
1267 variable bindings *)
1268 List.map
1269 (function non_pos ->
1270 let others =
1271 List.filter
1272 (function (other_non_pos,other_pos) ->
1273 (* do we want equal? or just somehow compatible? eg non_pos
1274 binds only E, but other_non_pos binds both E and E1 *)
b1b2de81 1275 non_pos =*= other_non_pos)
34e49164
C
1276 splitted_relevant in
1277 (non_pos,
1278 List.sort compare
1279 (non_pos @
1280 (combine_pos negated_pos_vars
1281 (List.map (function (_,x) -> x) others)))))
1282 non_poss in
1283 List.combine envs
1284 (List.map (function (non_pos,_) -> List.assoc non_pos extended_relevant)
1285 splitted_relevant)
1286
1287and combine_pos negated_pos_vars others =
1288 List.map
1289 (function posvar ->
1290 (posvar,
1291 Ast_c.MetaPosValList
1292 (List.sort compare
1293 (List.fold_left
1294 (function positions ->
1295 function other_list ->
1296 try
1297 match List.assoc posvar other_list with
1298 Ast_c.MetaPosValList l1 ->
1299 Common.union_set l1 positions
1300 | _ -> failwith "bad value for a position variable"
1301 with Not_found -> positions)
1302 [] others))))
1303 negated_pos_vars
1304
b1b2de81
C
1305and process_a_generated_a_env_a_toplevel2 r env = function
1306 [cfile] ->
1307 let free_vars =
1308 List.filter
1309 (function
1310 (rule,_) when rule =$= r.rulename -> false
1311 | (_,"ARGS") -> false
1312 | _ -> true)
1313 r.free_vars in
1314 let env_domain = List.map (function (nm,vl) -> nm) env in
1315 let metavars =
1316 List.filter
1317 (function md ->
1318 let (rl,_) = Ast_cocci.get_meta_name md in
1319 rl =$= r.rulename)
1320 r.metavars in
1321 if Common.include_set free_vars env_domain
1322 then Unparse_hrule.pp_rule metavars r.ast_rule env cfile.full_fname
1323 | _ -> failwith "multiple files not supported"
34e49164 1324
b1b2de81
C
1325and process_a_generated_a_env_a_toplevel rule env ccs =
1326 Common.profile_code "process_a_ctl_a_env_a_toplevel"
1327 (fun () -> process_a_generated_a_env_a_toplevel2 rule env ccs)
34e49164
C
1328
1329(* does side effects on C ast and on Cocci info rule *)
485bce71 1330and process_a_ctl_a_env_a_toplevel2 r e c f =
34e49164
C
1331 indent_do (fun () ->
1332 show_or_not_celem "trying" c.ast_c;
485bce71 1333 Flag.currentfile := Some (f ^ ":" ^get_celem c.ast_c);
1be43e12 1334 let (trans_info, returned_any_states, inherited_bindings, newbindings) =
34e49164
C
1335 Common.save_excursion Flag_ctl.loop_in_src_code (fun () ->
1336 Flag_ctl.loop_in_src_code := !Flag_ctl.loop_in_src_code||c.contain_loop;
1337
1338 (***************************************)
1339 (* !Main point! The call to the engine *)
1340 (***************************************)
1341 let model_ctl = CCI.model_for_ctl r.dropped_isos (Common.some c.flow) e
1342 in CCI.mysat model_ctl r.ctl (r.used_after, e)
1343 )
1344 in
1345 if not returned_any_states
1346 then None
1347 else begin
1348 show_or_not_celem "found match in" c.ast_c;
1349 show_or_not_trans_info trans_info;
1350 List.iter (show_or_not_binding "out") newbindings;
1351
1352 r.was_matched := true;
1353
1354 if not (null trans_info)
1355 then begin
1356 c.was_modified := true;
1357 try
1358 (* les "more than one var in a decl" et "already tagged token"
1359 * font crasher coccinelle. Si on a 5 fichiers, donc on a 5
1360 * failed. Le try limite le scope des crashes pendant la
1361 * trasformation au fichier concerne. *)
1362
1363 (* modify ast via side effect *)
485bce71 1364 ignore(Transformation_c.transform r.rulename r.dropped_isos
1be43e12 1365 inherited_bindings trans_info (Common.some c.flow));
34e49164
C
1366 with Timeout -> raise Timeout | UnixExit i -> raise (UnixExit i)
1367 end;
1368
1be43e12 1369 Some (List.map (function x -> x@inherited_bindings) newbindings)
34e49164
C
1370 end
1371 )
1372
485bce71 1373and process_a_ctl_a_env_a_toplevel a b c f=
34e49164 1374 Common.profile_code "process_a_ctl_a_env_a_toplevel"
485bce71 1375 (fun () -> process_a_ctl_a_env_a_toplevel2 a b c f)
faf9a90c 1376
34e49164 1377
b1b2de81
C
1378let rec bigloop2 rs (ccs: file_info list) =
1379 let es = ref [(Ast_c.emptyMetavarsBinding,[])] in
1380 let ccs = ref ccs in
1381 let rules_that_have_ever_matched = ref [] in
1382
1383 (* looping over the rules *)
1384 rs +> List.iter (fun r ->
1385 match r with
1386 InitialScriptRuleCocciInfo r | FinalScriptRuleCocciInfo r -> ()
1387 | ScriptRuleCocciInfo r ->
1388 if !Flag_cocci.show_ctl_text then begin
1389 Common.pr_xxxxxxxxxxxxxxxxx ();
1390 pr ("script: " ^ r.language);
1391 Common.pr_xxxxxxxxxxxxxxxxx ();
1392
1393 adjust_pp_with_indent (fun () ->
1394 Format.force_newline();
1395 let (l,mv,code) = r.scr_ast_rule in
1396 let deps = r.scr_dependencies in
1397 Pretty_print_cocci.unparse
1398 (Ast_cocci.ScriptRule (l,deps,mv,code)));
1399 end;
1400
1401 if !Flag.show_misc then print_endline "RESULT =";
1402
1403 let (_, newes) =
1404 List.fold_left
1405 (function (cache, newes) ->
1406 function (e, rules_that_have_matched) ->
1407 match r.language with
1408 "python" ->
1409 apply_python_rule r cache newes e rules_that_have_matched
1410 rules_that_have_ever_matched
1411 | "test" ->
1412 concat_headers_and_c !ccs +> List.iter (fun (c,_) ->
1413 if c.flow <> None
1414 then
1415 Printf.printf "Flow: %s\r\nFlow!\r\n%!" c.fullstring);
1416 (cache, newes)
1417 | _ ->
1418 Printf.printf "Unknown language: %s\n" r.language;
1419 (cache, newes)
1420 )
1421 ([],[]) !es in
1422
1423 es := newes;
1424 | CocciRuleCocciInfo r ->
1425 apply_cocci_rule r rules_that_have_ever_matched es ccs);
1426
1427 if !Flag.sgrep_mode2
1428 then begin
1429 (* sgrep can lead to code that is not parsable, but we must
1430 * still call rebuild_info_c_and_headers to pretty print the
1431 * action (MINUS), so that later the diff will show what was
1432 * matched by sgrep. But we don't want the parsing error message
1433 * hence the following flag setting. So this code propably
1434 * will generate a NotParsedCorrectly for the matched parts
1435 * and the very final pretty print and diff will work
1436 *)
1437 Flag_parsing_c.verbose_parsing := false;
1438 ccs := rebuild_info_c_and_headers !ccs false
1439 end;
1440 !ccs (* return final C asts *)
1441
1442let bigloop a b =
1443 Common.profile_code "bigloop" (fun () -> bigloop2 a b)
1444
1445let initial_final_bigloop2 ty rebuild r =
1446 if !Flag_cocci.show_ctl_text then
1447 begin
1448 Common.pr_xxxxxxxxxxxxxxxxx ();
1449 pr (ty ^ ": " ^ r.language);
1450 Common.pr_xxxxxxxxxxxxxxxxx ();
1451
1452 adjust_pp_with_indent (fun () ->
1453 Format.force_newline();
1454 Pretty_print_cocci.unparse(rebuild r.scr_ast_rule));
1455 end;
1456
1457 match r.language with
1458 "python" ->
1459 (* include_match makes no sense in an initial or final rule, although
1460 er have no way to prevent it *)
1461 let _ = apply_python_rule r [] [] [] [] (ref []) in ()
1462 | _ ->
1463 Printf.printf "Unknown language for initial/final script: %s\n"
1464 r.language
1465
1466let initial_final_bigloop a b c =
1467 Common.profile_code "initial_final_bigloop"
1468 (fun () -> initial_final_bigloop2 a b c)
34e49164
C
1469
1470(*****************************************************************************)
b1b2de81 1471(* The main functions *)
34e49164
C
1472(*****************************************************************************)
1473
b1b2de81
C
1474let pre_engine2 (coccifile, isofile) =
1475 show_or_not_cocci coccifile isofile;
34e49164
C
1476 Pycocci.set_coccifile coccifile;
1477
1478 let isofile =
1479 if not (Common.lfile_exists isofile)
1480 then begin
1481 pr2 ("warning: Can't find default iso file: " ^ isofile);
1482 None
1483 end
b1b2de81 1484 else Some isofile in
34e49164
C
1485
1486 (* useful opti when use -dir *)
faf9a90c 1487 let (metavars,astcocci,free_var_lists,negated_pos_lists,used_after_lists,
34e49164 1488 positions_lists,toks,_) =
b1b2de81
C
1489 sp_of_file coccifile isofile in
1490 let ctls = ctls_of_ast astcocci used_after_lists positions_lists in
34e49164 1491
b1b2de81 1492 g_contain_typedmetavar := sp_contain_typed_metavar astcocci;
34e49164 1493
b1b2de81 1494 check_macro_in_sp_and_adjust toks;
34e49164 1495
b1b2de81 1496 show_or_not_ctl_tex astcocci ctls;
34e49164 1497
b1b2de81
C
1498 let cocci_infos =
1499 prepare_cocci ctls free_var_lists negated_pos_lists
1500 used_after_lists positions_lists metavars astcocci in
34e49164 1501
b1b2de81
C
1502 let _ =
1503 List.fold_left
1504 (function languages ->
1505 function
1506 InitialScriptRuleCocciInfo(r) ->
1507 (if List.mem r.language languages
1508 then failwith ("double initializer found for "^r.language));
1509 initial_final_bigloop "initial"
1510 (function(x,_,y) -> Ast_cocci.InitialScriptRule(x,y))
1511 r;
1512 r.language::languages
1513 | _ -> languages)
1514 [] cocci_infos in
1515 (cocci_infos,toks)
34e49164 1516
b1b2de81
C
1517let pre_engine a =
1518 Common.profile_code "pre_engine" (fun () -> pre_engine2 a)
faf9a90c 1519
b1b2de81
C
1520let full_engine2 (cocci_infos,toks) cfiles =
1521
1522 show_or_not_cfiles cfiles;
1523
1524 (* optimisation allowing to launch coccinelle on all the drivers *)
1525 if !Flag_cocci.worth_trying_opt && not (worth_trying cfiles toks)
1526 then
1527 begin
1528 pr2 ("not worth trying:" ^ Common.join " " cfiles);
1529 cfiles +> List.map (fun s -> s, None)
1530 end
1531 else
1532 begin
1533
1534 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1535 if !Flag.show_misc then pr "let's go";
1536 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1537
1538 let choose_includes =
1539 match !Flag_cocci.include_options with
1540 Flag_cocci.I_UNSPECIFIED ->
1541 if !g_contain_typedmetavar
1542 then Flag_cocci.I_NORMAL_INCLUDES
1543 else Flag_cocci.I_NO_INCLUDES
1544 | x -> x in
1545 let c_infos = prepare_c cfiles choose_includes in
1546
1547 (* ! the big loop ! *)
1548 let c_infos' = bigloop cocci_infos c_infos in
1549
1550 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1551 if !Flag.show_misc then pr "Finished";
1552 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1553 if !Flag_ctl.graphical_trace then gen_pdf_graph ();
1554
1555 c_infos' +> List.map (fun c_or_h ->
1556 if !(c_or_h.was_modified_once)
1557 then
1558 begin
1559 let outfile =
1560 Common.new_temp_file "cocci-output" ("-" ^ c_or_h.fname) in
1561
1562 if c_or_h.fkind =*= Header
1563 then pr2 ("a header file was modified: " ^ c_or_h.fname);
1564
1565 (* and now unparse everything *)
1566 cfile_of_program (for_unparser c_or_h.asts) outfile;
1567
1568 let show_only_minus = !Flag.sgrep_mode2 in
1569 show_or_not_diff c_or_h.fpath outfile show_only_minus;
1570
1571 (c_or_h.fpath,
1572 if !Flag.sgrep_mode2 then None else Some outfile)
1573 end
1574 else (c_or_h.fpath, None))
1575 end
34e49164
C
1576
1577let full_engine a b =
1578 Common.profile_code "full_engine" (fun () -> full_engine2 a b)
1579
b1b2de81
C
1580let post_engine2 (cocci_infos,_) =
1581 let _ =
1582 List.fold_left
1583 (function languages ->
1584 function
1585 FinalScriptRuleCocciInfo(r) ->
1586 (if List.mem r.language languages
1587 then failwith ("double finalizer found for "^r.language));
1588 initial_final_bigloop "final"
1589 (function(x,_,y) -> Ast_cocci.FinalScriptRule(x,y))
1590 r;
1591 r.language::languages
1592 | _ -> languages)
1593 [] cocci_infos in
1594 ()
1595
1596let post_engine a =
1597 Common.profile_code "post_engine" (fun () -> post_engine2 a)
34e49164
C
1598
1599(*****************************************************************************)
1600(* check duplicate from result of full_engine *)
1601(*****************************************************************************)
1602
1603let check_duplicate_modif2 xs =
1604 (* opti: let groups = Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) xs *)
708f4980
C
1605 if !Flag_cocci.verbose_cocci
1606 then pr2 ("Check duplication for " ^ i_to_s (List.length xs) ^ " files");
1607
34e49164
C
1608 let groups = Common.group_assoc_bykey_eff xs in
1609 groups +> Common.map_filter (fun (file, xs) ->
1610 match xs with
1611 | [] -> raise Impossible
1612 | [res] -> Some (file, res)
1613 | res::xs ->
1614 match res with
1615 | None ->
b1b2de81 1616 if not (List.for_all (fun res2 -> res2 =*= None) xs)
34e49164
C
1617 then begin
1618 pr2 ("different modification result for " ^ file);
1619 None
1620 end
1621 else Some (file, None)
1622 | Some res ->
1623 if not(List.for_all (fun res2 ->
1624 match res2 with
1625 | None -> false
1626 | Some res2 ->
1627 let diff = Common.cmd_to_list ("diff -u -b -B "^res^" "^res2)
1628 in
1629 null diff
1630 ) xs) then begin
1631 pr2 ("different modification result for " ^ file);
1632 None
1633 end
1634 else Some (file, Some res)
1635
1636
1637 )
1638let check_duplicate_modif a =
1639 Common.profile_code "check_duplicate" (fun () -> check_duplicate_modif2 a)
1640