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