Coccinelle release-1.0.0-rc11
[bpt/coccinelle.git] / engine / transformation_c.ml
index 69b9050..464f92b 100644 (file)
@@ -1,5 +1,7 @@
 (*
- * Copyright 2010, INRIA, University of Copenhagen
+ * Copyright 2012, INRIA
+ * Julia Lawall, Gilles Muller
+ * Copyright 2010-2011, INRIA, University of Copenhagen
  * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
  * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
  * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
  *)
 
 
-(* Yoann Padioleau
- *
- * Copyright (C) 2006, 2007 Ecole des Mines de Nantes
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License (GPL)
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * file license.txt for more details.
- *
- * This file was part of Coccinelle.
- *)
 open Common
 
 module F = Control_flow_c
@@ -50,6 +37,7 @@ type xinfo = {
   optional_storage_iso : bool;
   optional_qualifier_iso : bool;
   value_format_iso : bool;
+  optional_declarer_semicolon_iso : bool;
   current_rule_name : string; (* used for errors *)
   index : int list (* witness tree indices *)
 }
@@ -118,8 +106,40 @@ module XTRANS = struct
   let value_format_flag f = fun tin ->
     f (tin.extra.value_format_iso) tin
 
+  let optional_declarer_semicolon_flag f = fun tin ->
+    f (tin.extra.optional_declarer_semicolon_iso) tin
+
   let mode = Cocci_vs_c.TransformMode
 
+  (* ------------------------------------------------------------------------*)
+  (* Env *)
+  (* ------------------------------------------------------------------------*)
+
+  (* When env is used in + code, have to strip it more to avoid circular
+  references due to local variable information *)
+
+  let clean_env env =
+    List.map
+      (function (v,vl) ->
+       match vl with
+       | Ast_c.MetaExprVal(e,ml) ->
+           (v,Ast_c.MetaExprVal(Lib_parsing_c.real_al_expr e,ml))
+       | Ast_c.MetaExprListVal(es) ->
+           (v,Ast_c.MetaExprListVal(Lib_parsing_c.real_al_arguments es))
+       | Ast_c.MetaTypeVal(ty) ->
+           (v,Ast_c.MetaTypeVal(Lib_parsing_c.real_al_type ty))
+       | Ast_c.MetaInitVal(i) ->
+           (v,Ast_c.MetaInitVal(Lib_parsing_c.real_al_init i))
+       | Ast_c.MetaInitListVal(is) ->
+           (v,Ast_c.MetaInitListVal(Lib_parsing_c.real_al_inits is))
+       | Ast_c.MetaDeclVal(d) ->
+           (v,Ast_c.MetaDeclVal(Lib_parsing_c.real_al_decl d))
+       | Ast_c.MetaStmtVal(s) ->
+           (v,Ast_c.MetaStmtVal(Lib_parsing_c.real_al_statement s))
+       | _ -> (v,vl))
+      env
+
+
   (* ------------------------------------------------------------------------*)
   (* Exp  *)
   (* ------------------------------------------------------------------------*)
@@ -198,6 +218,60 @@ module XTRANS = struct
         | None ->
             failwith "weird: dont have position info for the mcodekind"
 
+    (* these remove constraints, at least those that contain pcre regexps,
+       which cannot be compared (problem in the unparser) *)
+  let strip_anything anything =
+    let donothing r k e = k e in
+    let mcode mc = mc in
+    let ident r k e =
+      let e = k e in
+      match Ast_cocci.unwrap e with
+       Ast_cocci.MetaId(name,constraints,u,i) ->
+          Ast_cocci.rewrap e
+           (Ast_cocci.MetaId(name,Ast_cocci.IdNoConstraint,u,i))
+      |  Ast_cocci.MetaFunc(name,constraints,u,i) ->
+          Ast_cocci.rewrap e
+           (Ast_cocci.MetaFunc(name,Ast_cocci.IdNoConstraint,u,i))
+      |  Ast_cocci.MetaLocalFunc(name,constraints,u,i) ->
+          Ast_cocci.rewrap e
+           (Ast_cocci.MetaLocalFunc(name,Ast_cocci.IdNoConstraint,u,i))
+      |  _ -> e in
+    let expression r k e =
+      let e = k e in
+      match Ast_cocci.unwrap e with
+       Ast_cocci.MetaErr(name,constraints,u,i) ->
+         Ast_cocci.rewrap e
+           (Ast_cocci.MetaErr(name,Ast_cocci.NoConstraint,u,i))
+      | Ast_cocci.MetaExpr(name,constraints,u,ty,form,i) ->
+          Ast_cocci.rewrap e
+           (Ast_cocci.MetaExpr(name,Ast_cocci.NoConstraint,u,ty,form,i))
+      | _ -> e in
+    let fn = Visitor_ast.rebuilder
+       mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
+       donothing donothing donothing donothing donothing
+       ident expression donothing donothing donothing donothing
+       donothing donothing donothing donothing donothing donothing in
+
+  fn.Visitor_ast.rebuilder_anything anything
+
+  let strip_minus_code = function
+      Ast_cocci.REPLACEMENT(l,c) ->
+       Ast_cocci.REPLACEMENT(List.map (List.map strip_anything) l,c)
+    | Ast_cocci.NOREPLACEMENT -> Ast_cocci.NOREPLACEMENT
+  let strip_context_code = function
+      Ast_cocci.BEFORE(l,c) ->
+       Ast_cocci.BEFORE(List.map (List.map strip_anything) l,c)
+    | Ast_cocci.AFTER(l,c) ->
+       Ast_cocci.AFTER(List.map (List.map strip_anything) l,c)
+    | Ast_cocci.BEFOREAFTER(l1,l2,c) ->
+       Ast_cocci.BEFOREAFTER(List.map (List.map strip_anything) l1,
+                             List.map (List.map strip_anything) l2,c)
+    | Ast_cocci.NOTHING -> Ast_cocci.NOTHING
+  let strip_mck_code = function
+      Ast_cocci.MINUS(p,l,a,repl) ->
+       Ast_cocci.MINUS(p,l,a,strip_minus_code repl)
+    | Ast_cocci.CONTEXT(p,ba) -> Ast_cocci.CONTEXT(p,strip_context_code ba)
+    | Ast_cocci.PLUS(c) -> Ast_cocci.PLUS(c)
 
   let tag_with_mck mck ib = fun tin ->
 
@@ -215,15 +289,23 @@ module XTRANS = struct
     (match mck, Ast_c.pinfo_of_info ib with
     | _,                  Ast_c.AbstractLineTok _ -> raise Impossible
     | Ast_cocci.MINUS(_), Ast_c.ExpandedTok _ ->
-        failwith ("try to delete an expanded token: " ^ (Ast_c.str_of_info ib))
+        failwith
+         (Printf.sprintf
+            "%s: %d: try to delete an expanded token: %s"
+            (Ast_c.file_of_info ib)
+            (Ast_c.line_of_info ib) (Ast_c.str_of_info ib))
     | _ -> ()
     );
 
-    let many_count = function
+    let many_context_count = function
        Ast_cocci.BEFORE(_,Ast_cocci.MANY) | Ast_cocci.AFTER(_,Ast_cocci.MANY)
       |         Ast_cocci.BEFOREAFTER(_,_,Ast_cocci.MANY) -> true
       |        _ -> false in
 
+    let many_minus_count = function
+       Ast_cocci.REPLACEMENT(_,Ast_cocci.MANY) -> true
+      |        _ -> false in
+
     (match (oldmcode,mck) with
     | (Ast_cocci.CONTEXT(_,Ast_cocci.NOTHING),      _) ->
        (* nothing there, so take the new stuff *)
@@ -231,32 +313,55 @@ module XTRANS = struct
            Ast_cocci.MINUS (pos,_,adj,any_xxs) ->
              Ast_cocci.MINUS (pos,inst,adj,any_xxs)
          | mck -> mck in
-        cocciinforef := Some (update_inst tin.extra.index mck, [tin.binding])
+       let mck = strip_mck_code (update_inst tin.extra.index mck) in
+       (* clean_env actually only needed if there is an addition
+          not sure the extra efficiency would be worth duplicating the code *)
+        cocciinforef :=
+         Some (mck, [clean_env tin.binding])
     | (_,   Ast_cocci.CONTEXT(_,Ast_cocci.NOTHING)) ->
        (* can this case occur? stay with the old stuff *)
        ()
-    | (Ast_cocci.MINUS(old_pos,old_inst,old_adj,[]),
-       Ast_cocci.MINUS(new_pos,new_inst,new_adj,[]))
-       when old_pos = new_pos &&
-         (List.mem tin.binding oldenvs or !Flag.sgrep_mode2)
+    | (Ast_cocci.MINUS(old_pos,old_inst,old_adj,Ast_cocci.NOREPLACEMENT),
+       Ast_cocci.MINUS(new_pos,new_inst,new_adj,Ast_cocci.NOREPLACEMENT))
+       when old_pos = new_pos
+           (* not sure why the following condition is useful.
+              should be ok to double remove even if the environments are
+              different *)
+          (* &&
+         (List.mem tin.binding oldenvs or !Flag.sgrep_mode2) *)
            (* no way to combine adjacency information, just drop one *)
       ->
         cocciinforef := Some
          (Ast_cocci.MINUS
-            (old_pos,Common.union_set old_inst new_inst,old_adj,[]),
+            (old_pos,Common.union_set old_inst new_inst,old_adj,
+             Ast_cocci.NOREPLACEMENT),
           [tin.binding]);
         (if !Flag_matcher.show_misc
-        then pr2 "already tagged but only removed, so safe")
+        then pr2_once "already tagged but only removed, so safe")
+
+    (* ++ cases *)
+    | (Ast_cocci.MINUS(old_pos,old_inst,old_adj,old_modif),
+       Ast_cocci.MINUS(new_pos,new_inst,new_adj,new_modif))
+       when old_pos = new_pos &&
+         old_modif = strip_minus_code new_modif &&
+         many_minus_count old_modif ->
+
+          cocciinforef :=
+           Some(Ast_cocci.MINUS(old_pos,Common.union_set old_inst new_inst,
+                                old_adj,old_modif),
+                (clean_env tin.binding)::oldenvs)
 
     | (Ast_cocci.CONTEXT(old_pos,old_modif),
        Ast_cocci.CONTEXT(new_pos,new_modif))
        when old_pos = new_pos &&
-         old_modif = new_modif && many_count old_modif ->
+         old_modif = strip_context_code new_modif &&
+         many_context_count old_modif ->
            (* iteration only allowed on context; no way to replace something
               more than once; now no need for iterable; just check a flag *)
 
           cocciinforef :=
-           Some(Ast_cocci.CONTEXT(old_pos,old_modif),tin.binding::oldenvs)
+           Some(Ast_cocci.CONTEXT(old_pos,old_modif),
+                (clean_env tin.binding)::oldenvs)
 
     | _ ->
           (* coccionly:
@@ -346,9 +451,11 @@ module XTRANS = struct
           (fun ib ->
            tag_with_mck (Ast_cocci.MINUS (pos,inst,adj,any_xxs)) ib tin),
           (fun ib ->
-           tag_with_mck (Ast_cocci.MINUS (pos,inst,adj,[])) ib tin),
+           tag_with_mck
+             (Ast_cocci.MINUS (pos,inst,adj,Ast_cocci.NOREPLACEMENT)) ib tin),
           (fun ib ->
-           tag_with_mck (Ast_cocci.MINUS (pos,inst,adj,[])) ib tin),
+           tag_with_mck
+             (Ast_cocci.MINUS (pos,inst,adj,Ast_cocci.NOREPLACEMENT)) ib tin),
           (fun ib ->
            tag_with_mck (Ast_cocci.MINUS (pos,inst,adj,any_xxs)) ib tin)
         ) expr
@@ -417,9 +524,18 @@ module XTRANS = struct
   let distribute_mck_type (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
     Visitor_c.vk_type_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
 
+  let distribute_mck_decl (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
+    Visitor_c.vk_decl_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
+
+  let distribute_mck_field (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
+    Visitor_c.vk_struct_field_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
+
   let distribute_mck_ini (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
     Visitor_c.vk_ini_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
 
+  let distribute_mck_inis (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
+    Visitor_c.vk_inis_splitted_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
+
   let distribute_mck_param (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
     Visitor_c.vk_param_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
 
@@ -431,6 +547,12 @@ module XTRANS = struct
     Visitor_c.vk_node_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
       x
 
+  let distribute_mck_enum_fields (maxpos, minpos) =
+    fun (lop,mop,rop,bop) ->fun x ->
+      Visitor_c.vk_enum_fields_splitted_s
+       (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
+        x
+
   let distribute_mck_struct_fields (maxpos, minpos) =
     fun (lop,mop,rop,bop) ->fun x ->
       Visitor_c.vk_struct_fields_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
@@ -493,7 +615,12 @@ module XTRANS = struct
   let distrf_param  = distrf (Lib_parsing_c.ii_of_param, distribute_mck_param)
   let distrf_params = distrf (Lib_parsing_c.ii_of_params,distribute_mck_params)
   let distrf_ini = distrf (Lib_parsing_c.ii_of_ini,distribute_mck_ini)
+  let distrf_inis = distrf (Lib_parsing_c.ii_of_inis,distribute_mck_inis)
+  let distrf_decl = distrf (Lib_parsing_c.ii_of_decl,distribute_mck_decl)
+  let distrf_field = distrf (Lib_parsing_c.ii_of_field,distribute_mck_field)
   let distrf_node = distrf (Lib_parsing_c.ii_of_node,distribute_mck_node)
+  let distrf_enum_fields =
+    distrf (Lib_parsing_c.ii_of_enum_fields, distribute_mck_enum_fields)
   let distrf_struct_fields =
     distrf (Lib_parsing_c.ii_of_struct_fields, distribute_mck_struct_fields)
   let distrf_cst =
@@ -573,11 +700,12 @@ let (transform2: string (* rule name *) -> string list (* dropped_isos *) ->
   Lib_engine.metavars_binding (* inherited bindings *) ->
   Lib_engine.numbered_transformation_info -> F.cflow -> F.cflow) =
  fun rule_name dropped_isos binding0 xs cflow ->
-
    let extra = {
      optional_storage_iso   = not(List.mem "optional_storage" dropped_isos);
      optional_qualifier_iso = not(List.mem "optional_qualifier" dropped_isos);
      value_format_iso = not(List.mem "value_format" dropped_isos);
+     optional_declarer_semicolon_iso =
+       not(List.mem "optional_declarer_semicolon"   dropped_isos);
      current_rule_name = rule_name;
      index = [];
    } in
@@ -589,7 +717,7 @@ let (transform2: string (* rule name *) -> string list (* dropped_isos *) ->
       let node  = acc#nodes#assoc nodei in
 
       if !Flag.show_transinfo
-      then pr2 "transform one node";
+      then pr2 (Printf.sprintf "transform one node: %d" nodei);
 
       let tin = {
         XTRANS.extra = {extra with index = index};