Release coccinelle-0.2.5-rc2
[bpt/coccinelle.git] / parsing_cocci / insert_plus.ml
index 795d46c..e19ab82 100644 (file)
@@ -1,23 +1,25 @@
 (*
-* Copyright 2005-2008, Ecole des Mines de Nantes, University of Copenhagen
-* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller
-* This file is part of Coccinelle.
-* 
-* Coccinelle is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, according to version 2 of the License.
-* 
-* Coccinelle 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
-* GNU General Public License for more details.
-* 
-* You should have received a copy of the GNU General Public License
-* along with Coccinelle.  If not, see <http://www.gnu.org/licenses/>.
-* 
-* The authors reserve the right to distribute this or future versions of
-* Coccinelle under other licenses.
-*)
+ * Copyright 2010, 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
+ * This file is part of Coccinelle.
+ *
+ * Coccinelle is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, according to version 2 of the License.
+ *
+ * Coccinelle 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
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Coccinelle.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The authors reserve the right to distribute this or future versions of
+ * Coccinelle under other licenses.
+ *)
 
 
 (* The error message "no available token to attach to" often comes in an
@@ -32,8 +34,11 @@ tokens, and then merge them. *)
 module Ast = Ast_cocci
 module Ast0 = Ast0_cocci
 module V0 = Visitor_ast0
+module VT0 = Visitor_ast0_types
 module CN = Context_neg
 
+let empty_isos = ref false
+
 let get_option f = function
     None -> []
   | Some x -> f x
@@ -86,16 +91,15 @@ it *)
   let topfn r k e = Ast0.TopTag(e) :: (k e) in
 
   let res =
-    V0.combiner bind option_default
+    V0.flat_combiner bind option_default
       mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
-      mcode
       (donothing Ast0.dotsExpr) (donothing Ast0.dotsInit)
       (donothing Ast0.dotsParam) (donothing Ast0.dotsStmt)
       (donothing Ast0.dotsDecl) (donothing Ast0.dotsCase)
       (donothing Ast0.ident) expression (donothing Ast0.typeC) initialiser
       (donothing Ast0.param) (donothing Ast0.decl) statement
       (donothing Ast0.case_line) topfn in
-  res.V0.combiner_top_level e
+  res.VT0.combiner_rec_top_level e
 
 (* --------------------------------------------------------------------- *)
 (* --------------------------------------------------------------------- *)
@@ -156,8 +160,8 @@ let collect_minus_join_points root =
   let bind x y = x @ y in
   let option_default = [] in
 
-  let mcode (_,_,info,mcodekind,_) =
-    if List.mem (info.Ast0.offset) unfavored_tokens
+  let mcode (x,_,info,mcodekind,_,_) =
+    if List.mem (info.Ast0.pos_info.Ast0.offset) unfavored_tokens
     then [(Unfavored,info,mcodekind)]
     else [(Favored,info,mcodekind)] in
 
@@ -187,12 +191,12 @@ bind to that; not good for isomorphisms *)
     | Ast0.CIRCLES(l) -> multibind (List.map f l)
     | Ast0.STARS(l) -> multibind (List.map f l) in
 
-  let edots r k d = dots r.V0.combiner_expression k d in
-  let idots r k d = dots r.V0.combiner_initialiser k d in
-  let pdots r k d = dots r.V0.combiner_parameter k d in
-  let sdots r k d = dots r.V0.combiner_statement k d in
-  let ddots r k d = dots r.V0.combiner_declaration k d in
-  let cdots r k d = dots r.V0.combiner_case_line k d in
+  let edots r k d = dots r.VT0.combiner_rec_expression k d in
+  let idots r k d = dots r.VT0.combiner_rec_initialiser k d in
+  let pdots r k d = dots r.VT0.combiner_rec_parameter k d in
+  let sdots r k d = dots r.VT0.combiner_rec_statement k d in
+  let ddots r k d = dots r.VT0.combiner_rec_declaration k d in
+  let cdots r k d = dots r.VT0.combiner_rec_case_line k d in
 
   (* a case for everything that has a Opt *)
 
@@ -236,45 +240,46 @@ bind to that; not good for isomorphisms *)
        (Toplevel,info,bef)::(k s)
     | Ast0.Decl((info,bef),decl) -> (Decl,info,bef)::(k s)
     | Ast0.Nest(starter,stmt_dots,ender,whencode,multi) ->
-       mcode starter @ r.V0.combiner_statement_dots stmt_dots @ mcode ender
+       mcode starter @ r.VT0.combiner_rec_statement_dots stmt_dots @
+       mcode ender
     | Ast0.Dots(d,whencode) | Ast0.Circles(d,whencode)
     | Ast0.Stars(d,whencode) -> mcode d (* ignore whencode *)
     | Ast0.OptStm s | Ast0.UniqueStm s ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_statement s
+       r.VT0.combiner_rec_statement s
     | _ -> do_nothing r k s in
 
   let expression r k e =
     match Ast0.unwrap e with
       Ast0.NestExpr(starter,expr_dots,ender,whencode,multi) ->
        mcode starter @
-       r.V0.combiner_expression_dots expr_dots @ mcode ender
+       r.VT0.combiner_rec_expression_dots expr_dots @ mcode ender
     | Ast0.Edots(d,whencode) | Ast0.Ecircles(d,whencode)
     | Ast0.Estars(d,whencode) -> mcode d (* ignore whencode *)
     | Ast0.OptExp e | Ast0.UniqueExp e ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_expression e
+       r.VT0.combiner_rec_expression e
     | _ -> do_nothing r k e in
 
   let ident r k e =
     match Ast0.unwrap e with
       Ast0.OptIdent i | Ast0.UniqueIdent i ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_ident i
+       r.VT0.combiner_rec_ident i
     | _ -> do_nothing r k e in
 
   let typeC r k e =
     match Ast0.unwrap e with
       Ast0.OptType t | Ast0.UniqueType t ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_typeC t
+       r.VT0.combiner_rec_typeC t
     | _ -> do_nothing r k e in
 
   let decl r k e =
     match Ast0.unwrap e with
       Ast0.OptDecl d | Ast0.UniqueDecl d ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_declaration d
+       r.VT0.combiner_rec_declaration d
     | _ -> do_nothing r k e in
 
   let initialiser r k e =
@@ -282,28 +287,27 @@ bind to that; not good for isomorphisms *)
       Ast0.Idots(d,whencode) -> mcode d (* ignore whencode *)
     | Ast0.OptIni i | Ast0.UniqueIni i ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_initialiser i
+       r.VT0.combiner_rec_initialiser i
     | _ -> do_nothing r k e in
 
   let param r k e =
     match Ast0.unwrap e with
       Ast0.OptParam p | Ast0.UniqueParam p ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_parameter p
+       r.VT0.combiner_rec_parameter p
     | _ -> do_nothing r k e in
 
   let case_line r k e =
     match Ast0.unwrap e with
       Ast0.OptCase c ->
        (* put the + code on the thing, not on the opt *)
-       r.V0.combiner_case_line c
+       r.VT0.combiner_rec_case_line c
     | _ -> do_nothing r k e in
 
   let do_top r k (e: Ast0.top_level) = k e in
 
-  V0.combiner bind option_default
+  V0.flat_combiner bind option_default
     mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
-    mcode
     edots idots pdots sdots ddots cdots
     ident expression typeC initialiser param decl statement case_line do_top
 
@@ -315,51 +319,51 @@ let call_collect_minus context_nodes :
       match e with
        Ast0.DotsExprTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_expression_dots e)
+          (collect_minus_join_points e).VT0.combiner_rec_expression_dots e)
       | Ast0.DotsInitTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_initialiser_list e)
+          (collect_minus_join_points e).VT0.combiner_rec_initialiser_list e)
       | Ast0.DotsParamTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_parameter_list e)
+          (collect_minus_join_points e).VT0.combiner_rec_parameter_list e)
       | Ast0.DotsStmtTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_statement_dots e)
+          (collect_minus_join_points e).VT0.combiner_rec_statement_dots e)
       | Ast0.DotsDeclTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_declaration_dots e)
+          (collect_minus_join_points e).VT0.combiner_rec_declaration_dots e)
       | Ast0.DotsCaseTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_case_line_dots e)
+          (collect_minus_join_points e).VT0.combiner_rec_case_line_dots e)
       | Ast0.IdentTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_ident e)
+          (collect_minus_join_points e).VT0.combiner_rec_ident e)
       | Ast0.ExprTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_expression e)
+          (collect_minus_join_points e).VT0.combiner_rec_expression e)
       | Ast0.ArgExprTag(e) | Ast0.TestExprTag(e) ->
          failwith "not possible - iso only"
       | Ast0.TypeCTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_typeC e)
+          (collect_minus_join_points e).VT0.combiner_rec_typeC e)
       | Ast0.ParamTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_parameter e)
+          (collect_minus_join_points e).VT0.combiner_rec_parameter e)
       | Ast0.InitTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_initialiser e)
+          (collect_minus_join_points e).VT0.combiner_rec_initialiser e)
       | Ast0.DeclTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_declaration e)
+          (collect_minus_join_points e).VT0.combiner_rec_declaration e)
       | Ast0.StmtTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_statement e)
+          (collect_minus_join_points e).VT0.combiner_rec_statement e)
       | Ast0.CaseLineTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_case_line e)
+          (collect_minus_join_points e).VT0.combiner_rec_case_line e)
       | Ast0.TopTag(e) ->
          (Ast0.get_index e,
-          (collect_minus_join_points e).V0.combiner_top_level e)
+          (collect_minus_join_points e).VT0.combiner_rec_top_level e)
       | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
       | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
       | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
@@ -372,10 +376,10 @@ let verify l =
   let get_info = function
       (Favored,info,_) | (Unfavored,info,_) | (Toplevel,info,_)
     | (Decl,info,_) -> info in
-  let token_start_line      x = (get_info x).Ast0.logical_start in
-  let token_end_line        x = (get_info x).Ast0.logical_end in
-  let token_real_start_line x = (get_info x).Ast0.line_start in
-  let token_real_end_line   x = (get_info x).Ast0.line_end in
+  let token_start_line    x = (get_info x).Ast0.pos_info.Ast0.logical_start in
+  let token_end_line        x = (get_info x).Ast0.pos_info.Ast0.logical_end in
+  let token_real_start_line x = (get_info x).Ast0.pos_info.Ast0.line_start in
+  let token_real_end_line   x = (get_info x).Ast0.pos_info.Ast0.line_end in
   List.iter
     (function
        (index,((_::_) as l1)) ->
@@ -388,7 +392,7 @@ let verify l =
                  then
                    failwith
                      (Printf.sprintf
-                        "error in collection of - tokens %d less than %d"
+                        "error in collection of - tokens: line %d less than line %d"
                         (token_real_start_line cur) real_prev);
                  (token_end_line cur,token_real_end_line cur))
              (token_end_line (List.hd l1), token_real_end_line (List.hd l1))
@@ -398,6 +402,7 @@ let verify l =
     l
 
 let process_minus minus =
+  Hashtbl.clear root_token_table;
   create_root_token_table minus;
   List.concat
     (List.map
@@ -411,7 +416,6 @@ let process_minus minus =
 (* --------------------------------------------------------------------- *)
 (* collect the plus tokens *)
 
-let mk_baseType x         = Ast.BaseTypeTag x
 let mk_structUnion x      = Ast.StructUnionTag x
 let mk_sign x             = Ast.SignTag x
 let mk_ident x            = Ast.IdentTag (Ast0toast.ident x)
@@ -449,28 +453,70 @@ let collect_plus_nodes root =
   let bind x y = x @ y in
   let option_default = [] in
 
-  let mcode fn (term,_,info,mcodekind,_) =
-    match mcodekind with Ast0.PLUS -> [(info,fn term)] | _ -> [] in
+  let extract_strings info =
+    let adjust_info =
+      {info with Ast0.strings_before = [];  Ast0.strings_after = []} in
+    let extract = function
+       [] -> []
+      |        strings_before ->
+         let (_,first) = List.hd strings_before in
+         let (_,last) = List.hd (List.rev strings_before) in
+         let new_pos_info =
+           {Ast0.line_start = first.Ast0.line_start;
+             Ast0.line_end = last.Ast0.line_start;
+             Ast0.logical_start = first.Ast0.logical_start;
+             Ast0.logical_end = last.Ast0.logical_start;
+             Ast0.column = first.Ast0.column;
+             Ast0.offset = first.Ast0.offset} in
+         let new_info = {adjust_info with Ast0.pos_info = new_pos_info} in
+         let string = List.map (function (s,_) -> s) strings_before in
+         [(new_info,Ast.ONE(*?*),Ast.Pragma (string))] in
+    let bef = extract info.Ast0.strings_before in
+    let aft = extract info.Ast0.strings_after in
+    (bef,aft) in
+
+  let mcode fn (term,_,info,mcodekind,_,_) =
+    match mcodekind with
+      Ast0.PLUS c -> [(info,c,fn term)]
+    | Ast0.CONTEXT _ -> let (bef,aft) = extract_strings info in bef@aft
+    | _ -> [] in
 
-  let imcode fn (term,_,info,mcodekind,_) =
+  let imcode fn (term,_,info,mcodekind,_,_) =
     match mcodekind with
-      Ast0.PLUS -> [(info,fn term (Ast0toast.convert_info info))]
+      Ast0.PLUS c -> [(info,c,fn term (Ast0toast.convert_info info))]
+    | Ast0.CONTEXT _ -> let (bef,aft) = extract_strings info in bef@aft
     | _ -> [] in
 
+  let info (i,_) = let (bef,aft) = extract_strings i in bef@aft in
+
   let do_nothing fn r k e =
     match Ast0.get_mcodekind e with
       (Ast0.CONTEXT(_)) when not(Ast0.get_index e = root_index) -> []
-    | Ast0.PLUS -> [(Ast0.get_info e,fn e)]
+    | Ast0.PLUS c -> [(Ast0.get_info e,c,fn e)]
     | _ -> k e in
 
   (* case for everything that is just a wrapper for a simpler thing *)
+  (* case for things with bef aft *)
   let stmt r k e =
     match Ast0.unwrap e with
-      Ast0.Exp(exp) -> r.V0.combiner_expression exp
-    | Ast0.TopExp(exp) -> r.V0.combiner_expression exp
-    | Ast0.Ty(ty) -> r.V0.combiner_typeC ty
-    | Ast0.TopInit(init) -> r.V0.combiner_initialiser init
-    | Ast0.Decl(_,decl) -> r.V0.combiner_declaration decl
+      Ast0.Exp(exp) -> r.VT0.combiner_rec_expression exp
+    | Ast0.TopExp(exp) -> r.VT0.combiner_rec_expression exp
+    | Ast0.Ty(ty) -> r.VT0.combiner_rec_typeC ty
+    | Ast0.TopInit(init) -> r.VT0.combiner_rec_initialiser init
+    | Ast0.Decl(bef,decl) ->
+       (info bef) @ (do_nothing mk_statement r k e)
+    | Ast0.FunDecl(bef,fi,name,lp,params,rp,lbrace,body,rbrace) ->
+       (info bef) @ (do_nothing mk_statement r k e)
+    | Ast0.IfThen(iff,lp,exp,rp,branch1,aft) ->
+       (do_nothing mk_statement r k e) @ (info aft)
+    | Ast0.IfThenElse(iff,lp,exp,rp,branch1,els,branch2,aft) ->
+       (do_nothing mk_statement r k e) @ (info aft)
+    | Ast0.While(whl,lp,exp,rp,body,aft) ->
+       (do_nothing mk_statement r k e) @ (info aft)
+    | Ast0.For(fr,lp,e1,sem1,e2,sem2,e3,rp,body,aft) ->
+       (do_nothing mk_statement r k e) @ (info aft)
+    | Ast0.Iterator(nm,lp,args,rp,body,aft) ->
+       (do_nothing mk_statement r k e) @ (info aft)
     | _ -> do_nothing mk_statement r k e in
 
   (* statementTag is preferred, because it indicates that one statement is
@@ -478,22 +524,22 @@ let collect_plus_nodes root =
   let stmt_dots r k e =
     match Ast0.unwrap e with
       Ast0.DOTS([s]) | Ast0.CIRCLES([s]) | Ast0.STARS([s]) ->
-       r.V0.combiner_statement s
+       r.VT0.combiner_rec_statement s
     | _ -> do_nothing mk_stmtdots r k e in
 
   let toplevel r k e =
     match Ast0.unwrap e with
-      Ast0.DECL(s) -> r.V0.combiner_statement s
-    | Ast0.CODE(sdots) -> r.V0.combiner_statement_dots sdots
+      Ast0.DECL(s) -> r.VT0.combiner_rec_statement s
+    | Ast0.CODE(sdots) -> r.VT0.combiner_rec_statement_dots sdots
     | _ -> do_nothing mk_code r k e in
 
   let initdots r k e = k e in
 
-  V0.combiner bind option_default
+  V0.flat_combiner bind option_default
     (imcode mk_meta) (imcode mk_token) (mcode mk_constant) (mcode mk_assignOp)
     (mcode mk_fixOp)
     (mcode mk_unaryOp) (mcode mk_binaryOp) (mcode mk_const_vol)
-    (mcode mk_baseType) (mcode mk_sign) (mcode mk_structUnion)
+    (mcode mk_sign) (mcode mk_structUnion)
     (mcode mk_storage) (mcode mk_inc_file)
     (do_nothing mk_exprdots) initdots
     (do_nothing mk_paramdots) stmt_dots (do_nothing mk_decldots)
@@ -504,57 +550,57 @@ let collect_plus_nodes root =
     stmt (do_nothing mk_case_line) toplevel
 
 let call_collect_plus context_nodes :
-    (int * (Ast0.info * Ast.anything) list) list =
+    (int * (Ast0.info * Ast.count * Ast.anything) list) list =
   List.map
     (function e ->
       match e with
        Ast0.DotsExprTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_expression_dots e)
+          (collect_plus_nodes e).VT0.combiner_rec_expression_dots e)
       | Ast0.DotsInitTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_initialiser_list e)
+          (collect_plus_nodes e).VT0.combiner_rec_initialiser_list e)
       | Ast0.DotsParamTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_parameter_list e)
+          (collect_plus_nodes e).VT0.combiner_rec_parameter_list e)
       | Ast0.DotsStmtTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_statement_dots e)
+          (collect_plus_nodes e).VT0.combiner_rec_statement_dots e)
       | Ast0.DotsDeclTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_declaration_dots e)
+          (collect_plus_nodes e).VT0.combiner_rec_declaration_dots e)
       | Ast0.DotsCaseTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_case_line_dots e)
+          (collect_plus_nodes e).VT0.combiner_rec_case_line_dots e)
       | Ast0.IdentTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_ident e)
+          (collect_plus_nodes e).VT0.combiner_rec_ident e)
       | Ast0.ExprTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_expression e)
+          (collect_plus_nodes e).VT0.combiner_rec_expression e)
       | Ast0.ArgExprTag(_) | Ast0.TestExprTag(_) ->
          failwith "not possible - iso only"
       | Ast0.TypeCTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_typeC e)
+          (collect_plus_nodes e).VT0.combiner_rec_typeC e)
       | Ast0.InitTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_initialiser e)
+          (collect_plus_nodes e).VT0.combiner_rec_initialiser e)
       | Ast0.ParamTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_parameter e)
+          (collect_plus_nodes e).VT0.combiner_rec_parameter e)
       | Ast0.DeclTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_declaration e)
+          (collect_plus_nodes e).VT0.combiner_rec_declaration e)
       | Ast0.StmtTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_statement e)
+          (collect_plus_nodes e).VT0.combiner_rec_statement e)
       | Ast0.CaseLineTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_case_line e)
+          (collect_plus_nodes e).VT0.combiner_rec_case_line e)
       | Ast0.TopTag(e) ->
          (Ast0.get_index e,
-          (collect_plus_nodes e).V0.combiner_top_level e)
+          (collect_plus_nodes e).VT0.combiner_rec_top_level e)
       | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
       | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
       | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
@@ -570,42 +616,50 @@ line of n is one less than the starting line of n+1.
 Outer list: For any pair of successive elements, n and n+1, the ending
 line of n is more than one less than the starting line of n+1. *)
 
-let logstart info = info.Ast0.logical_start
-let logend info = info.Ast0.logical_end
+let logstart info = info.Ast0.pos_info.Ast0.logical_start
+let logend info = info.Ast0.pos_info.Ast0.logical_end
 
 let redo info start finish =
-  {{info with Ast0.logical_start = start} with Ast0.logical_end = finish}
+  let new_pos_info =
+    {info.Ast0.pos_info with
+      Ast0.logical_start = start;
+      Ast0.logical_end = finish} in
+  {info with Ast0.pos_info = new_pos_info}
 
 let rec find_neighbors (index,l) :
-    int * (Ast0.info * (Ast.anything list list)) list =
+    int * (Ast0.info * Ast.count * (Ast.anything list list)) list =
   let rec loop = function
       [] -> []
-    | (i,x)::rest ->
+    | (i,c,x)::rest ->
        (match loop rest with
-         ((i1,(x1::rest_inner))::rest_middle)::rest_outer ->
+         ((i1,c1,(x1::rest_inner))::rest_middle)::rest_outer ->
            let finish1 = logend i in
            let start2 = logstart i1 in
            if finish1 = start2
            then
-             ((redo i (logstart i) (logend i1),(x::x1::rest_inner))
+             ((if not (c = c1) then failwith "inconsistent + code");
+             ((redo i (logstart i) (logend i1),c,(x::x1::rest_inner))
               ::rest_middle)
-             ::rest_outer
+             ::rest_outer)
            else if finish1 + 1 = start2
-           then ((i,[x])::(i1,(x1::rest_inner))::rest_middle)::rest_outer
-           else [(i,[x])]::((i1,(x1::rest_inner))::rest_middle)::rest_outer
-       | _ -> [[(i,[x])]]) (* rest must be [] *) in
+           then ((i,c,[x])::(i1,c1,(x1::rest_inner))::rest_middle)::rest_outer
+           else
+             [(i,c,[x])]::((i1,c1,(x1::rest_inner))::rest_middle)::rest_outer
+       | _ -> [[(i,c,[x])]]) (* rest must be [] *) in
   let res =
     List.map
       (function l ->
-       let (start_info,_) = List.hd l in
-       let (end_info,_) = List.hd (List.rev l) in
+       let (start_info,start_count,_) = List.hd l in
+       let (end_info,end_count,_) = List.hd (List.rev l) in
+       (if not (start_count = end_count) then failwith "inconsistent + code");
        (redo start_info (logstart start_info) (logend end_info),
-        List.map (function (_,x) -> x) l))
+        start_count,
+        List.map (function (_,_,x) -> x) l))
       (loop l) in
   (index,res)
 
 let process_plus plus :
-    (int * (Ast0.info * Ast.anything list list) list) list =
+    (int * (Ast0.info * Ast.count * Ast.anything list list) list) list =
   List.concat
     (List.map
        (function x ->
@@ -633,11 +687,11 @@ let merge_one = function
 
 (* end of first argument < start/end of second argument *)
 let less_than_start info1 info2 =
-  info1.Ast0.logical_end < info2.Ast0.logical_start
+  info1.Ast0.pos_info.Ast0.logical_end < info2.Ast0.pos_info.Ast0.logical_start
 let less_than_end info1 info2 =
-  info1.Ast0.logical_end < info2.Ast0.logical_end
+  info1.Ast0.pos_info.Ast0.logical_end < info2.Ast0.pos_info.Ast0.logical_end
 let greater_than_end info1 info2 =
-  info1.Ast0.logical_start > info2.Ast0.logical_end
+  info1.Ast0.pos_info.Ast0.logical_start > info2.Ast0.pos_info.Ast0.logical_end
 let good_start info = info.Ast0.attachable_start
 let good_end info = info.Ast0.attachable_end
 
@@ -646,7 +700,12 @@ let decl = function Decl -> true | Favored | Unfavored | Toplevel -> false
 let favored = function Favored -> true | Unfavored | Toplevel | Decl -> false
 
 let top_code =
-  List.for_all (List.for_all (function Ast.Code _ -> true | _ -> false))
+  List.for_all
+    (List.for_all (function Ast.Code _ | Ast.Pragma _ -> true | _ -> false))
+
+let storage_code =
+  List.for_all
+    (List.for_all (function Ast.StorageTag _ -> true | _ -> false))
 
 (* The following is probably not correct.  The idea is to detect what
 should be placed completely before the declaration.  So type/storage
@@ -660,7 +719,8 @@ let predecl_code =
     | Ast.StatementTag _
     | Ast.Rule_elemTag _
     | Ast.StmtDotsTag _
-    | Ast.Code _ -> true
+    | Ast.Code _
+    | Ast.Pragma _ -> true
       (* the following should definitely be false *)
     | Ast.FullTypeTag _ | Ast.BaseTypeTag _ | Ast.StructUnionTag _
     | Ast.SignTag _
@@ -674,9 +734,9 @@ let pr = Printf.sprintf
 let insert thing thinginfo into intoinfo =
   let get_last l = let l = List.rev l in (List.rev(List.tl l),List.hd l) in
   let get_first l = (List.hd l,List.tl l) in
-  let thing_start = thinginfo.Ast0.logical_start in
-  let thing_end = thinginfo.Ast0.logical_end in
-  let thing_offset = thinginfo.Ast0.offset in
+  let thing_start = thinginfo.Ast0.pos_info.Ast0.logical_start in
+  let thing_end = thinginfo.Ast0.pos_info.Ast0.logical_end in
+  let thing_offset = thinginfo.Ast0.pos_info.Ast0.offset in
   let into_start = intoinfo.Ast0.tline_start in
   let into_end = intoinfo.Ast0.tline_end in
   let into_left_offset = intoinfo.Ast0.left_offset in
@@ -716,53 +776,65 @@ let insert thing thinginfo into intoinfo =
 
 let init thing info =
   (thing,
-   {Ast0.tline_start = info.Ast0.logical_start;
-     Ast0.tline_end = info.Ast0.logical_end;
-     Ast0.left_offset = info.Ast0.offset;
-     Ast0.right_offset = info.Ast0.offset})
+   {Ast0.tline_start = info.Ast0.pos_info.Ast0.logical_start;
+     Ast0.tline_end = info.Ast0.pos_info.Ast0.logical_end;
+     Ast0.left_offset = info.Ast0.pos_info.Ast0.offset;
+     Ast0.right_offset = info.Ast0.pos_info.Ast0.offset})
 
-let attachbefore (infop,p) = function
+let attachbefore (infop,c,p) = function
     Ast0.MINUS(replacements) ->
-      (match !replacements with
-       ([],ti) -> replacements := init p infop
-      |        (repl,ti) -> replacements := insert p infop repl ti)
+      let (repl,ti) = !replacements in
+      let (bef,ti) =
+       match repl with
+         [] -> init p infop
+       | repl -> insert p infop repl ti in
+      replacements := (bef,ti)
   | Ast0.CONTEXT(neighbors) ->
       let (repl,ti1,ti2) = !neighbors in
       (match repl with
-       Ast.BEFORE(bef) ->
+       Ast.BEFORE(bef,it) ->
          let (bef,ti1) = insert p infop bef ti1 in
-         neighbors := (Ast.BEFORE(bef),ti1,ti2)
-      |        Ast.AFTER(aft) -> 
+         let it = Ast.lub_count it c in
+         neighbors := (Ast.BEFORE(bef,it),ti1,ti2)
+      |        Ast.AFTER(aft,it) ->
          let (bef,ti1) = init p infop in
-         neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
-      |        Ast.BEFOREAFTER(bef,aft) ->
+         let it = Ast.lub_count it c in
+         neighbors := (Ast.BEFOREAFTER(bef,aft,it),ti1,ti2)
+      |        Ast.BEFOREAFTER(bef,aft,it) ->
          let (bef,ti1) = insert p infop bef ti1 in
-         neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
+         let it = Ast.lub_count it c in
+         neighbors := (Ast.BEFOREAFTER(bef,aft,it),ti1,ti2)
       |        Ast.NOTHING ->
          let (bef,ti1) = init p infop in
-         neighbors := (Ast.BEFORE(bef),ti1,ti2))
+         neighbors := (Ast.BEFORE(bef,c),ti1,ti2))
   | _ -> failwith "not possible for attachbefore"
 
-let attachafter (infop,p) = function
+let attachafter (infop,c,p) = function
     Ast0.MINUS(replacements) ->
-      (match !replacements with
-       ([],ti) -> replacements := init p infop
-      |        (repl,ti) -> replacements := insert p infop repl ti)
+       let (repl,ti) = !replacements in
+       let (aft,ti) =
+       match repl with
+         [] -> init p infop
+       | repl -> insert p infop repl ti in
+       replacements := (aft,ti)
   | Ast0.CONTEXT(neighbors) ->
       let (repl,ti1,ti2) = !neighbors in
       (match repl with
-       Ast.BEFORE(bef) ->
+       Ast.BEFORE(bef,it) ->
          let (aft,ti2) = init p infop in
-         neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
-      |        Ast.AFTER(aft) -> 
+         let it = Ast.lub_count it c in
+         neighbors := (Ast.BEFOREAFTER(bef,aft,it),ti1,ti2)
+      |        Ast.AFTER(aft,it) ->
          let (aft,ti2) = insert p infop aft ti2 in
-         neighbors := (Ast.AFTER(aft),ti1,ti2)
-      |        Ast.BEFOREAFTER(bef,aft) ->
+         let it = Ast.lub_count it c in
+         neighbors := (Ast.AFTER(aft,it),ti1,ti2)
+      |        Ast.BEFOREAFTER(bef,aft,it) ->
          let (aft,ti2) = insert p infop aft ti2 in
-         neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
+         let it = Ast.lub_count it c in
+         neighbors := (Ast.BEFOREAFTER(bef,aft,it),ti1,ti2)
       |        Ast.NOTHING ->
          let (aft,ti2) = init p infop in
-         neighbors := (Ast.AFTER(aft),ti1,ti2))
+         neighbors := (Ast.AFTER(aft,c),ti1,ti2))
   | _ -> failwith "not possible for attachbefore"
 
 let attach_all_before ps m =
@@ -772,9 +844,9 @@ let attach_all_after ps m =
   List.iter (function x -> attachafter x m) ps
 
 let split_at_end info ps =
-  let split_point =  info.Ast0.logical_end in
+  let split_point =  info.Ast0.pos_info.Ast0.logical_end in
   List.partition
-    (function (info,_) -> info.Ast0.logical_end < split_point)
+    (function (info,_,_) -> info.Ast0.pos_info.Ast0.logical_end < split_point)
     ps
 
 let allminus = function
@@ -783,20 +855,27 @@ let allminus = function
 
 let rec before_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
     [] -> ()
-  | (((infop,_) as p) :: ps) as all ->
+  | (((infop,_,pcode) as p) :: ps) as all ->
       if less_than_start infop infom1 or
        (allminus m1 && less_than_end infop infom1) (* account for trees *)
       then
-       if good_start infom1
-       then (attachbefore p m1; before_m1 x1 x2 rest ps)
+       if toplevel f1
+       then
+         if storage_code pcode
+         then before_m2 x2 rest all (* skip fake token for storage *)
+         else (attachbefore p m1; before_m1 x1 x2 rest ps)
        else
-         failwith
-           (pr "%d: no available token to attach to" infop.Ast0.line_start)
+         if good_start infom1
+         then (attachbefore p m1; before_m1 x1 x2 rest ps)
+         else
+           failwith
+             (pr "%d: no available token to attach to"
+                infop.Ast0.pos_info.Ast0.line_start)
       else after_m1 x1 x2 rest all
 
 and after_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
     [] -> ()
-  | (((infop,pcode) as p) :: ps) as all ->
+  | (((infop,count,pcode) as p) :: ps) as all ->
       (* if the following is false, then some + code is stuck in the middle
         of some context code (m1).  could drop down to the token level.
         this might require adjustments in ast0toast as well, when + code on
@@ -805,7 +884,7 @@ and after_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
         what it can infer from something being CONTEXT with no top-level
         modifications.  for the moment, we thus give an error, asking the
         user to rewrite the semantic patch. *)
-      if greater_than_end infop infom1
+      if greater_than_end infop infom1 or is_minus m1 or !empty_isos
       then
        if less_than_start infop infom2
        then
@@ -827,60 +906,77 @@ and after_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
          then before_m2 x2 rest all
          else
            failwith
-             (pr "%d: no available token to attach to" infop.Ast0.line_start)
+             (pr "%d: no available token to attach to"
+                infop.Ast0.pos_info.Ast0.line_start)
        else after_m2 x2 rest all
       else
        begin
          Printf.printf "between: p start %d p end %d m1 start %d m1 end %d m2 start %d m2 end %d\n"
-           infop.Ast0.line_start infop.Ast0.line_end
-           infom1.Ast0.line_start infom1.Ast0.line_end
-           infom2.Ast0.line_start infom2.Ast0.line_end;
+           infop.Ast0.pos_info.Ast0.line_start
+           infop.Ast0.pos_info.Ast0.line_end
+           infom1.Ast0.pos_info.Ast0.line_start
+           infom1.Ast0.pos_info.Ast0.line_end
+           infom2.Ast0.pos_info.Ast0.line_start
+           infom2.Ast0.pos_info.Ast0.line_end;
          Pretty_print_cocci.print_anything "" pcode;
          failwith
            "The semantic patch is structured in a way that may give bad results with isomorphisms.  Please try to rewrite it by moving + code out from -/context terms."
        end
 
+(* not sure this is safe.  if have iso problems, consider changing this
+to always return false *)
+and is_minus = function
+    Ast0.MINUS _ -> true
+  | _ -> false
+
 and before_m2 ((f2,infom2,m2) as x2) rest
-    (p : (Ast0.info * Ast.anything list list) list) =
+    (p : (Ast0.info * Ast.count * Ast.anything list list) list) =
   match (rest,p) with
     (_,[]) -> ()
-  | ([],((infop,_)::_)) ->
+  | ([],((infop,_,_)::_)) ->
       let (bef_m2,aft_m2) = split_at_end infom2 p in (* bef_m2 isn't empty *)
       if good_start infom2
       then (attach_all_before bef_m2 m2; after_m2 x2 rest aft_m2)
       else
        failwith
-         (pr "%d: no available token to attach to" infop.Ast0.line_start)
+         (pr "%d: no available token to attach to"
+            infop.Ast0.pos_info.Ast0.line_start)
   | (m::ms,_) -> before_m1 x2 m ms p
 
 and after_m2 ((f2,infom2,m2) as x2) rest
-    (p : (Ast0.info * Ast.anything list list) list) =
+    (p : (Ast0.info * Ast.count * Ast.anything list list) list) =
   match (rest,p) with
     (_,[]) -> ()
-  | ([],((infop,_)::_)) ->
+  | ([],((infop,_,_)::_)) ->
       if good_end infom2
       then attach_all_after p m2
       else
        failwith
-         (pr "%d: no available token to attach to" infop.Ast0.line_start)
+         (pr "%d: no available token to attach to"
+            infop.Ast0.pos_info.Ast0.line_start)
   | (m::ms,_) -> after_m1 x2 m ms p
 
 let merge_one : (minus_join_point * Ast0.info * 'a) list *
-    (Ast0.info * Ast.anything list list) list -> unit = function (m,p) ->
+    (Ast0.info * Ast.count * Ast.anything list list) list -> unit =
+      function (m,p) ->
   (*
   Printf.printf "minus code\n";
   List.iter
     (function (_,info,_) ->
       Printf.printf "start %d end %d real_start %d real_end %d\n"
-       info.Ast0.logical_start info.Ast0.logical_end
-       info.Ast0.line_start info.Ast0.line_end)
+       info.Ast0.pos_info.Ast0.logical_start
+       info.Ast0.pos_info.Ast0.logical_end
+       info.Ast0.pos_info.Ast0.line_start
+       info.Ast0.pos_info.Ast0.line_end)
     m;
   Printf.printf "plus code\n";
   List.iter
-    (function (info,p) ->
+    (function (info,_,p) ->
       Printf.printf "start %d end %d real_start %d real_end %d\n"
-       info.Ast0.logical_start info.Ast0.logical_end
-       info.Ast0.line_end info.Ast0.line_end;
+       info.Ast0.pos_info.Ast0.logical_start
+       info.Ast0.pos_info.Ast0.logical_end
+       info.Ast0.pos_info.Ast0.line_end
+       info.Ast0.pos_info.Ast0.line_end;
       Pretty_print_cocci.print_anything "" p;
       Format.print_newline())
     p;
@@ -915,7 +1011,12 @@ let reevaluate_contextness =
    let bind = (@) in
    let option_default = [] in
 
-   let mcode (_,_,_,mc,_) =
+   let mcode (_,_,_,mc,_,_) =
+     match mc with
+       Ast0.CONTEXT(mc) -> let (ba,_,_) = !mc in [ba]
+     | _ -> [] in
+
+   let info (_,mc) =
      match mc with
        Ast0.CONTEXT(mc) -> let (ba,_,_) = !mc in [ba]
      | _ -> [] in
@@ -928,19 +1029,38 @@ let reevaluate_contextness =
         []
      | _ -> let _ = k e in [] in
 
+   (* a case for everything with bef or aft *)
+   let stmt r k e =
+     match Ast0.unwrap e with
+       Ast0.Decl(bef,decl) ->
+        (info bef) @ (donothing r k e)
+     | Ast0.FunDecl(bef,fi,name,lp,params,rp,lbrace,body,rbrace) ->
+        (info bef) @ (donothing r k e)
+     | Ast0.IfThen(iff,lp,exp,rp,branch1,aft) ->
+        (donothing r k e) @ (info aft)
+     | Ast0.IfThenElse(iff,lp,exp,rp,branch1,els,branch2,aft) ->
+        (donothing r k e) @ (info aft)
+     | Ast0.While(whl,lp,exp,rp,body,aft) ->
+        (donothing r k e) @ (info aft)
+     | Ast0.For(fr,lp,e1,sem1,e2,sem2,e3,rp,body,aft) ->
+        (donothing r k e) @ (info aft)
+     | Ast0.Iterator(nm,lp,args,rp,body,aft) ->
+        (donothing r k e) @ (info aft)
+     | _ -> donothing r k e in
+
   let res =
-    V0.combiner bind option_default
+    V0.flat_combiner bind option_default
       mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
-      mcode
       donothing donothing donothing donothing donothing donothing donothing
       donothing
-      donothing donothing donothing donothing donothing donothing donothing in
-  res.V0.combiner_top_level
+      donothing donothing donothing donothing stmt donothing donothing in
+  res.VT0.combiner_rec_top_level
 
 (* --------------------------------------------------------------------- *)
 (* --------------------------------------------------------------------- *)
 
-let insert_plus minus plus =
+let insert_plus minus plus ei =
+  empty_isos := ei;
   let minus_stream = process_minus minus in
   let plus_stream = process_plus plus in
   merge minus_stream plus_stream;