Coccinelle release 1.0.0-rc12
[bpt/coccinelle.git] / parsing_cocci / context_neg.ml
index 67b3317..5f9b8e7 100644 (file)
@@ -1,25 +1,30 @@
 (*
-* Copyright 2005-2009, 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 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
+ * 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.
+ *)
+
+
+# 0 "./context_neg.ml"
 (* Detects subtrees that are all minus/plus and nodes that are "binding
 context nodes".  The latter is a node whose structure and immediate tokens
 are the same in the minus and plus trees, and such that for every child,
@@ -29,6 +34,7 @@ plus subtrees. *)
 module Ast = Ast_cocci
 module Ast0 = Ast0_cocci
 module V0 = Visitor_ast0
+module VT0 = Visitor_ast0_types
 module U = Unparse_ast0
 
 (* --------------------------------------------------------------------- *)
@@ -56,7 +62,8 @@ let set_mcodekind x mcodekind =
   | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
   | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
   | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
-  | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase"
+  | Ast0.MetaPosTag(p) -> failwith "invisible at this stage"
+  | Ast0.HiddenVarTag(p) -> failwith "hiddenvar only within iso phase"
 
 let set_index x index =
   match x with
@@ -80,7 +87,8 @@ let set_index x index =
   | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
   | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
   | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
-  | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase"
+  | Ast0.MetaPosTag(p) -> failwith "invisible at this stage"
+  | Ast0.HiddenVarTag(p) -> failwith "hiddenvar only within iso phase"
 
 let get_index = function
     Ast0.DotsExprTag(d) -> Index.expression_dots d
@@ -103,7 +111,8 @@ let get_index = function
   | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
   | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
   | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
-  | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase"
+  | Ast0.MetaPosTag(p) -> failwith "invisible at this stage"
+  | Ast0.HiddenVarTag(p) -> failwith "hiddenvar only within iso phase"
 
 (* --------------------------------------------------------------------- *)
 (* Collect the line numbers of the plus code.  This is used for disjunctions.
@@ -138,21 +147,22 @@ let collect_plus_lines top =
   let bind x y = () in
   let option_default = () in
   let donothing r k e = k e in
-  let mcode (_,_,info,mcodekind,_) =
+  let mcode (_,_,info,mcodekind,_,_) =
     match mcodekind with
-      Ast0.PLUS -> insert info.Ast0.line_start
+      Ast0.PLUS _ -> insert info.Ast0.pos_info.Ast0.line_start
     | _ -> () in
   let fn =
-    V0.combiner bind option_default
+    V0.flat_combiner bind option_default
       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
-  fn.V0.combiner_top_level top
+  fn.VT0.combiner_rec_top_level top
 
 (* --------------------------------------------------------------------- *)
 
-type kind = Neutral | AllMarked | NotAllMarked (* marked means + or - *)
+type kind =
+    Neutral | AllMarked of Ast.count | NotAllMarked (* marked means + or - *)
 
 (* --------------------------------------------------------------------- *)
 (* The first part analyzes each of the minus tree and the plus tree
@@ -175,7 +185,7 @@ type node =
 
 let kind2c = function
     Neutral -> "neutral"
-  | AllMarked -> "allmarked"
+  | AllMarked -> "allmarked"
   | NotAllMarked -> "notallmarked"
 
 let node2c = function
@@ -188,8 +198,8 @@ tokens *)
 let bind c1 c2 =
   let lub = function
       (k1,k2) when k1 = k2 -> k1
-    | (Neutral,AllMarked) -> AllMarked
-    | (AllMarked,Neutral) -> AllMarked
+    | (Neutral,AllMarked c) -> AllMarked c
+    | (AllMarked c,Neutral) -> AllMarked c
     | _ -> NotAllMarked in
   match (c1,c2) with
     (* token/token *)
@@ -227,22 +237,39 @@ let bind c1 c2 =
 let option_default = (*Bind(Neutral,[],[],[],[],[])*)
   Recursor(Neutral,[],[],[])
 
-let mcode (_,_,info,mcodekind,pos) =
-  let offset = info.Ast0.offset in
+let mcode (_,_,info,mcodekind,pos,_) =
+  let offset = info.Ast0.pos_info.Ast0.offset in
   match mcodekind with
-    Ast0.MINUS(_) -> Token(AllMarked,offset,mcodekind,[])
-  | Ast0.PLUS -> Token(AllMarked,offset,mcodekind,[])
+    Ast0.MINUS(_) -> Token(AllMarked Ast.ONE,offset,mcodekind,[])
+  | Ast0.PLUS c -> Token(AllMarked c,offset,mcodekind,[])
   | Ast0.CONTEXT(_) -> Token(NotAllMarked,offset,mcodekind,[offset])
   | _ -> failwith "not possible"
 
-let neutral_mcode (_,_,info,mcodekind,pos) =
-  let offset = info.Ast0.offset in
+let neutral_mcode (_,_,info,mcodekind,pos,_) =
+  let offset = info.Ast0.pos_info.Ast0.offset in
   match mcodekind with
     Ast0.MINUS(_) -> Token(Neutral,offset,mcodekind,[])
-  | Ast0.PLUS -> Token(Neutral,offset,mcodekind,[])
+  | Ast0.PLUS -> Token(Neutral,offset,mcodekind,[])
   | Ast0.CONTEXT(_) -> Token(Neutral,offset,mcodekind,[offset])
   | _ -> failwith "not possible"
 
+(* neutral for context; used for mcode in bef aft nodes that don't represent
+anything if they don't contain some information *)
+let nc_mcode (_,_,info,mcodekind,pos,_) =
+  (* distinguish from the offset of some real token *)
+  let offset = (-1) * info.Ast0.pos_info.Ast0.offset in
+  match mcodekind with
+    Ast0.MINUS(_) -> Token(AllMarked Ast.ONE,offset,mcodekind,[])
+  | Ast0.PLUS c -> Token(AllMarked c,offset,mcodekind,[])
+  | Ast0.CONTEXT(_) ->
+      (* Unlike the other mcode cases, we drop the offset from the context
+        offsets.  This is because we don't know whether the term this is
+        associated with is - or context.  In any case, the context offsets are
+        used for identification, and this invisible node should not be needed
+        for this purpose. *)
+      Token(Neutral,offset,mcodekind,[])
+  | _ -> failwith "not possible"
+
 let is_context = function Ast0.CONTEXT(_) -> true | _ -> false
 
 let union_all l = List.fold_left Common.union_set [] l
@@ -251,9 +278,10 @@ let union_all l = List.fold_left Common.union_set [] l
 intermingled with plus code.  it is used in disj_cases *)
 let classify is_minus all_marked table code =
   let mkres builder k il tl bil btl l e =
-    (if k = AllMarked
-    then Ast0.set_mcodekind e (all_marked()) (* definitive *)
-    else
+    (match k with
+      AllMarked count ->
+       Ast0.set_mcodekind e (all_marked count) (* definitive *)
+    | _ ->
       let check_index il tl =
        if List.for_all is_context tl
        then
@@ -263,7 +291,7 @@ let classify is_minus all_marked table code =
            let _ = Hashtbl.find table index in
            failwith
              (Printf.sprintf "line %d: index %s already used\n"
-                (Ast0.get_info e).Ast0.line_start
+                (Ast0.get_info e).Ast0.pos_info.Ast0.line_start
                 (String.concat " " (List.map string_of_int index)))
          with Not_found -> Hashtbl.add table index (e1,l)) in
       if il = [] then check_index bil btl else check_index il tl);
@@ -308,6 +336,13 @@ let classify is_minus all_marked table code =
 
   (* no whencode in plus tree so have to drop it *)
   (* need special cases for dots, nests, and disjs *)
+  let ident r k e =
+    compute_result Ast0.ident e
+      (match Ast0.unwrap e with
+       Ast0.DisjId(starter,id_list,_,ender) ->
+         disj_cases e starter id_list r.VT0.combiner_rec_ident ender
+      |        _ -> k e) in
+
   let expression r k e =
     compute_result Ast0.expr e
       (match Ast0.unwrap e with
@@ -320,16 +355,17 @@ let classify is_minus all_marked table code =
       | Ast0.Estars(dots,whencode) ->
          k (Ast0.rewrap e (Ast0.Estars(dots,None)))
       | Ast0.DisjExpr(starter,expr_list,_,ender) ->
-         disj_cases e starter expr_list r.V0.combiner_expression ender
+         disj_cases e starter expr_list r.VT0.combiner_rec_expression ender
       |        _ -> k e) in
 
   (* not clear why we have the next two cases, since DisjDecl and
   DisjType shouldn't have been constructed yet, as they only come from isos *)
+  (* actually, DisjDecl now allowed in source struct decls *)
   let declaration r k e =
     compute_result Ast0.decl e
       (match Ast0.unwrap e with
        Ast0.DisjDecl(starter,decls,_,ender) ->
-         disj_cases e starter decls r.V0.combiner_declaration ender
+         disj_cases e starter decls r.VT0.combiner_rec_declaration ender
       | Ast0.Ddots(dots,whencode) ->
          k (Ast0.rewrap e (Ast0.Ddots(dots,None)))
        (* Need special cases for the following so that the type will be
@@ -343,14 +379,14 @@ let classify is_minus all_marked table code =
           reordering their components. *)
       |        Ast0.Init(stg,ty,id,eq,ini,sem) ->
          bind (match stg with Some stg -> mcode stg | _ -> option_default)
-           (bind (r.V0.combiner_typeC ty)
-              (bind (r.V0.combiner_ident id)
+           (bind (r.VT0.combiner_rec_typeC ty)
+              (bind (r.VT0.combiner_rec_ident id)
                  (bind (mcode eq)
-                    (bind (r.V0.combiner_initialiser ini) (mcode sem)))))
+                    (bind (r.VT0.combiner_rec_initialiser ini) (mcode sem)))))
       | Ast0.UnInit(stg,ty,id,sem) ->
          bind (match stg with Some stg -> mcode stg | _ -> option_default)
-           (bind (r.V0.combiner_typeC ty)
-              (bind (r.V0.combiner_ident id) (mcode sem)))
+           (bind (r.VT0.combiner_rec_typeC ty)
+              (bind (r.VT0.combiner_rec_ident id) (mcode sem)))
       |        _ -> k e) in
 
   let param r k e =
@@ -358,14 +394,14 @@ let classify is_minus all_marked table code =
       (match Ast0.unwrap e with
        Ast0.Param(ty,Some id) ->
          (* needed for the same reason as in the Init and UnInit cases *)
-         bind (r.V0.combiner_typeC ty) (r.V0.combiner_ident id)
+         bind (r.VT0.combiner_rec_typeC ty) (r.VT0.combiner_rec_ident id)
       |        _ -> k e) in
 
   let typeC r k e =
     compute_result Ast0.typeC e
       (match Ast0.unwrap e with
        Ast0.DisjType(starter,types,_,ender) ->
-         disj_cases e starter types r.V0.combiner_typeC ender
+         disj_cases e starter types r.VT0.combiner_rec_typeC ender
       |        _ -> k e) in
 
   let initialiser r k i =
@@ -375,6 +411,13 @@ let classify is_minus all_marked table code =
          k (Ast0.rewrap i (Ast0.Idots(dots,None)))
       |        _ -> k i) in
 
+  let case_line r k e =
+    compute_result Ast0.case_line e
+      (match Ast0.unwrap e with
+       Ast0.DisjCase(starter,case_list,_,ender) ->
+         disj_cases e starter case_list r.VT0.combiner_rec_case_line ender
+      |        _ -> k e) in
+
   let statement r k s =
     compute_result Ast0.stmt s
       (match Ast0.unwrap s with
@@ -387,20 +430,19 @@ let classify is_minus all_marked table code =
       | Ast0.Stars(dots,whencode) ->
          k (Ast0.rewrap s (Ast0.Stars(dots,[])))
       | Ast0.Disj(starter,statement_dots_list,_,ender) ->
-         disj_cases s starter statement_dots_list r.V0.combiner_statement_dots
+         disj_cases s starter statement_dots_list
+           r.VT0.combiner_rec_statement_dots
            ender
-(*  Why? There is nothing there
        (* cases for everything with extra mcode *)
       |        Ast0.FunDecl((info,bef),_,_,_,_,_,_,_,_)
       | Ast0.Decl((info,bef),_) ->
-         bind (mcode ((),(),info,bef)) (k s)
+         bind (nc_mcode ((),(),info,bef,(),-1)) (k s)
       | Ast0.IfThen(_,_,_,_,_,(info,aft))
       | Ast0.IfThenElse(_,_,_,_,_,_,_,(info,aft))
-      | Ast0.While(_,_,_,_,_,(info,aft)) ->
-      | Ast0.For(_,_,_,_,_,_,_,_,_,(info,aft)) ->
-         bind (k s) (mcode ((),(),info,aft))
       | Ast0.Iterator(_,_,_,_,_,(info,aft))
-*)
+      | Ast0.While(_,_,_,_,_,(info,aft))
+      | Ast0.For(_,_,_,_,_,_,_,_,_,(info,aft)) ->
+         bind (k s) (nc_mcode ((),(),info,aft,(),-1))
       |        _ -> k s
 
 ) in
@@ -408,14 +450,14 @@ let classify is_minus all_marked table code =
   let do_top builder r k e = compute_result builder e (k e) in
 
   let combiner =
-    V0.combiner bind option_default
+    V0.flat_combiner bind option_default
       mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
       (do_nothing Ast0.dotsExpr) (do_nothing Ast0.dotsInit)
       (do_nothing Ast0.dotsParam) (do_nothing Ast0.dotsStmt)
       (do_nothing Ast0.dotsDecl) (do_nothing Ast0.dotsCase)
-      (do_nothing Ast0.ident) expression typeC initialiser param declaration
-      statement (do_nothing Ast0.case_line) (do_top Ast0.top) in
-  combiner.V0.combiner_top_level code
+      ident expression typeC initialiser param declaration
+      statement case_line (do_top Ast0.top) in
+  combiner.VT0.combiner_rec_top_level code
 
 (* --------------------------------------------------------------------- *)
 (* Traverse the hash tables and find corresponding context nodes that have
@@ -423,8 +465,8 @@ the same context children *)
 
 (* this is just a sanity check - really only need to look at the top-level
    structure *)
-let equal_mcode (_,_,info1,_,_) (_,_,info2,_,_) =
-  info1.Ast0.offset = info2.Ast0.offset
+let equal_mcode (_,_,info1,_,_,_) (_,_,info2,_,_,_) =
+  info1.Ast0.pos_info.Ast0.offset = info2.Ast0.pos_info.Ast0.offset
 
 let equal_option e1 e2 =
   match (e1,e2) with
@@ -442,12 +484,17 @@ let dots fn d1 d2 =
 let rec equal_ident i1 i2 =
   match (Ast0.unwrap i1,Ast0.unwrap i2) with
     (Ast0.Id(name1),Ast0.Id(name2)) -> equal_mcode name1 name2
-  | (Ast0.MetaId(name1,_,_),Ast0.MetaId(name2,_,_)) ->
+  | (Ast0.MetaId(name1,_,_,_),Ast0.MetaId(name2,_,_,_)) ->
       equal_mcode name1 name2
   | (Ast0.MetaFunc(name1,_,_),Ast0.MetaFunc(name2,_,_)) ->
       equal_mcode name1 name2
   | (Ast0.MetaLocalFunc(name1,_,_),Ast0.MetaLocalFunc(name2,_,_)) ->
       equal_mcode name1 name2
+  | (Ast0.DisjId(starter1,_,mids1,ender1),
+     Ast0.DisjId(starter2,_,mids2,ender2)) ->
+      equal_mcode starter1 starter2 &&
+      List.for_all2 equal_mcode mids1 mids2 &&
+      equal_mcode ender1 ender2
   | (Ast0.OptIdent(_),Ast0.OptIdent(_)) -> true
   | (Ast0.UniqueIdent(_),Ast0.UniqueIdent(_)) -> true
   | _ -> false
@@ -460,6 +507,8 @@ let rec equal_expression e1 e2 =
       equal_mcode lp1 lp2 && equal_mcode rp1 rp2
   | (Ast0.Assignment(_,op1,_,_),Ast0.Assignment(_,op2,_,_)) ->
       equal_mcode op1 op2
+  | (Ast0.Sequence(_,op1,_),Ast0.Sequence(_,op2,_)) ->
+      equal_mcode op1 op2
   | (Ast0.CondExpr(_,why1,_,colon1,_),Ast0.CondExpr(_,why2,_,colon2,_)) ->
       equal_mcode why1 why2 && equal_mcode colon1 colon2
   | (Ast0.Postfix(_,op1),Ast0.Postfix(_,op2)) -> equal_mcode op1 op2
@@ -481,6 +530,8 @@ let rec equal_expression e1 e2 =
   | (Ast0.SizeOfType(szf1,lp1,_,rp1),Ast0.SizeOfType(szf2,lp2,_,rp2)) ->
       equal_mcode szf1 szf2 && equal_mcode lp1 lp2 && equal_mcode rp1 rp2
   | (Ast0.TypeExp(_),Ast0.TypeExp(_)) -> true
+  | (Ast0.Constructor(lp1,_,rp1,_),Ast0.Constructor(lp2,_,rp2,_)) ->
+      equal_mcode lp1 lp2 && equal_mcode rp1 rp2
   | (Ast0.MetaErr(name1,_,_),Ast0.MetaErr(name2,_,_))
   | (Ast0.MetaExpr(name1,_,_,_,_),Ast0.MetaExpr(name2,_,_,_,_))
   | (Ast0.MetaExprList(name1,_,_),Ast0.MetaExprList(name2,_,_)) ->
@@ -514,6 +565,8 @@ let rec equal_typeC t1 t2 =
       equal_mcode lb1 lb2 && equal_mcode rb1 rb2
   | (Ast0.EnumName(kind1,_),Ast0.EnumName(kind2,_)) ->
       equal_mcode kind1 kind2
+  | (Ast0.EnumDef(_,lb1,_,rb1),Ast0.EnumDef(_,lb2,_,rb2)) ->
+       equal_mcode lb1 lb2 && equal_mcode rb1 rb2
   | (Ast0.StructUnionName(kind1,_),Ast0.StructUnionName(kind2,_)) ->
       equal_mcode kind1 kind2
   | (Ast0.FunctionType(ty1,lp1,p1,rp1),Ast0.FunctionType(ty2,lp2,p2,rp2)) ->
@@ -535,18 +588,29 @@ let rec equal_typeC t1 t2 =
 
 let equal_declaration d1 d2 =
   match (Ast0.unwrap d1,Ast0.unwrap d2) with
-    (Ast0.Init(stg1,_,_,eq1,_,sem1),Ast0.Init(stg2,_,_,eq2,_,sem2)) ->
+    (Ast0.MetaDecl(name1,_),Ast0.MetaDecl(name2,_))
+  | (Ast0.MetaField(name1,_),Ast0.MetaField(name2,_))
+  | (Ast0.MetaFieldList(name1,_,_),Ast0.MetaFieldList(name2,_,_)) ->
+      equal_mcode name1 name2
+  | (Ast0.Init(stg1,_,_,eq1,_,sem1),Ast0.Init(stg2,_,_,eq2,_,sem2)) ->
       equal_option stg1 stg2 && equal_mcode eq1 eq2 && equal_mcode sem1 sem2
   | (Ast0.UnInit(stg1,_,_,sem1),Ast0.UnInit(stg2,_,_,sem2)) ->
       equal_option stg1 stg2 && equal_mcode sem1 sem2
-  | (Ast0.MacroDecl(nm1,lp1,_,rp1,sem1),Ast0.MacroDecl(nm2,lp2,_,rp2,sem2)) ->
+  | (Ast0.MacroDecl(nm1,lp1,_,rp1,sem1),Ast0.MacroDecl(nm2,lp2,_,rp2,sem2))->
       equal_mcode lp1 lp2 && equal_mcode rp1 rp2 && equal_mcode sem1 sem2
+  | (Ast0.MacroDeclInit(nm1,lp1,_,rp1,eq1,_,sem1),
+     Ast0.MacroDeclInit(nm2,lp2,_,rp2,eq2,_,sem2))->
+      equal_mcode lp1 lp2 && equal_mcode rp1 rp2 && equal_mcode eq1 eq2
+        && equal_mcode sem1 sem2
   | (Ast0.TyDecl(_,sem1),Ast0.TyDecl(_,sem2)) -> equal_mcode sem1 sem2
   | (Ast0.Ddots(dots1,_),Ast0.Ddots(dots2,_)) -> equal_mcode dots1 dots2
   | (Ast0.OptDecl(_),Ast0.OptDecl(_)) -> true
   | (Ast0.UniqueDecl(_),Ast0.UniqueDecl(_)) -> true
-  | (Ast0.DisjDecl _,_) | (_,Ast0.DisjDecl _) ->
-      failwith "DisjDecl not expected here"
+  | (Ast0.DisjDecl(starter1,_,mids1,ender1),
+     Ast0.DisjDecl(starter2,_,mids2,ender2)) ->
+       equal_mcode starter1 starter2 &&
+       List.for_all2 equal_mcode mids1 mids2 &&
+       equal_mcode ender1 ender2
   | _ -> false
 
 let equal_designator d1 d2 =
@@ -565,8 +629,12 @@ let equal_initialiser i1 i2 =
   match (Ast0.unwrap i1,Ast0.unwrap i2) with
     (Ast0.MetaInit(name1,_),Ast0.MetaInit(name2,_)) ->
       equal_mcode name1 name2
+  | (Ast0.MetaInitList(name1,_,_),Ast0.MetaInitList(name2,_,_)) ->
+      equal_mcode name1 name2
   | (Ast0.InitExpr(_),Ast0.InitExpr(_)) -> true
-  | (Ast0.InitList(lb1,_,rb1),Ast0.InitList(lb2,_,rb2)) ->
+  | (Ast0.InitList(lb1,_,rb1,o1),Ast0.InitList(lb2,_,rb2,o2)) ->
+      (* can't compare orderedness, because this can differ between -
+        and + code *)
       (equal_mcode lb1 lb2) && (equal_mcode rb1 rb2)
   | (Ast0.InitGccExt(designators1,eq1,_),
      Ast0.InitGccExt(designators2,eq2,_)) ->
@@ -625,8 +693,8 @@ let rec equal_statement s1 s2 =
        equal_mcode rp1 rp2
   | (Ast0.Iterator(nm1,lp1,_,rp1,_,_),Ast0.Iterator(nm2,lp2,_,rp2,_,_)) ->
       equal_mcode lp1 lp2 && equal_mcode rp1 rp2
-  | (Ast0.Switch(switch1,lp1,_,rp1,lb1,case1,rb1),
-     Ast0.Switch(switch2,lp2,_,rp2,lb2,case2,rb2)) ->
+  | (Ast0.Switch(switch1,lp1,_,rp1,lb1,_,_,rb1),
+     Ast0.Switch(switch2,lp2,_,rp2,lb2,_,_,rb2)) ->
        equal_mcode switch1 switch2 && equal_mcode lp1 lp2 &&
        equal_mcode rp1 rp2 && equal_mcode lb1 lb2 &&
        equal_mcode rb1 rb2
@@ -660,6 +728,8 @@ let rec equal_statement s1 s2 =
   | (Ast0.Stars(d1,_),Ast0.Stars(d2,_)) -> equal_mcode d1 d2
   | (Ast0.Include(inc1,name1),Ast0.Include(inc2,name2)) ->
       equal_mcode inc1 inc2 && equal_mcode name1 name2
+  | (Ast0.Undef(def1,_),Ast0.Undef(def2,_)) ->
+      equal_mcode def1 def2
   | (Ast0.Define(def1,_,_,_),Ast0.Define(def2,_,_,_)) ->
       equal_mcode def1 def2
   | (Ast0.OptStm(_),Ast0.OptStm(_)) -> true
@@ -680,12 +750,17 @@ let equal_case_line c1 c2 =
       equal_mcode def1 def2 && equal_mcode colon1 colon2
   | (Ast0.Case(case1,_,colon1,_),Ast0.Case(case2,_,colon2,_)) ->
       equal_mcode case1 case2 && equal_mcode colon1 colon2
+  | (Ast0.DisjCase(starter1,_,mids1,ender1),
+     Ast0.DisjCase(starter2,_,mids2,ender2)) ->
+       equal_mcode starter1 starter2 &&
+       List.for_all2 equal_mcode mids1 mids2 &&
+       equal_mcode ender1 ender2
   | (Ast0.OptCase(_),Ast0.OptCase(_)) -> true
   | _ -> false
 
 let rec equal_top_level t1 t2 =
   match (Ast0.unwrap t1,Ast0.unwrap t2) with
-    (Ast0.DECL(_),Ast0.DECL(_)) -> true
+    (Ast0.NONDECL(_),Ast0.NONDECL(_)) -> true
   | (Ast0.FILEINFO(old_file1,new_file1),Ast0.FILEINFO(old_file2,new_file2)) ->
       equal_mcode old_file1 old_file2 && equal_mcode new_file1 new_file2
   | (Ast0.CODE(_),Ast0.CODE(_)) -> true
@@ -745,7 +820,7 @@ let contextify_all =
   let mcode x = () in
   let do_nothing r k e = Ast0.set_mcodekind e (default_context()); 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
     do_nothing do_nothing do_nothing do_nothing do_nothing do_nothing
     do_nothing do_nothing do_nothing do_nothing do_nothing do_nothing
@@ -754,8 +829,6 @@ let contextify_all =
 let contextify_whencode =
   let bind x y = () in
   let option_default = () in
-  let mcode x = () in
-  let do_nothing r k e = k e in
 
   let expression r k e =
     k e;
@@ -764,21 +837,21 @@ let contextify_whencode =
     | Ast0.Edots(_,Some whencode)
     | Ast0.Ecircles(_,Some whencode)
     | Ast0.Estars(_,Some whencode) ->
-       contextify_all.V0.combiner_expression whencode
+       contextify_all.VT0.combiner_rec_expression whencode
     | _ -> () in
 
   let initialiser r k i =
     match Ast0.unwrap i with
       Ast0.Idots(dots,Some whencode) ->
-       contextify_all.V0.combiner_initialiser whencode
+       contextify_all.VT0.combiner_rec_initialiser whencode
     | _ -> k i in
 
   let whencode = function
-      Ast0.WhenNot sd -> contextify_all.V0.combiner_statement_dots sd
-    | Ast0.WhenAlways s -> contextify_all.V0.combiner_statement s
+      Ast0.WhenNot sd -> contextify_all.VT0.combiner_rec_statement_dots sd
+    | Ast0.WhenAlways s -> contextify_all.VT0.combiner_rec_statement s
     | Ast0.WhenModifier(_) -> ()
-    | Ast0.WhenNotTrue(e) -> contextify_all.V0.combiner_expression e
-    | Ast0.WhenNotFalse(e) -> contextify_all.V0.combiner_expression e in
+    | Ast0.WhenNotTrue(e) -> contextify_all.VT0.combiner_rec_expression e
+    | Ast0.WhenNotFalse(e) -> contextify_all.VT0.combiner_rec_expression e in
 
   let statement r k (s : Ast0.statement) =
     k s;
@@ -790,13 +863,11 @@ let contextify_whencode =
 
   let combiner =
     V0.combiner bind option_default
-      mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
-      do_nothing do_nothing do_nothing do_nothing do_nothing do_nothing
-      do_nothing
-      expression
-      do_nothing initialiser do_nothing do_nothing statement do_nothing
-      do_nothing in
-  combiner.V0.combiner_top_level
+      {V0.combiner_functions with
+       VT0.combiner_exprfn = expression;
+       VT0.combiner_initfn = initialiser;
+       VT0.combiner_stmtfn = statement} in
+  combiner.VT0.combiner_rec_top_level
 
 (* --------------------------------------------------------------------- *)
 
@@ -809,10 +880,11 @@ let plus_table =
 
 let iscode t =
   match Ast0.unwrap t with
-    Ast0.DECL(_) -> true
+    Ast0.NONDECL(_) -> true
   | Ast0.FILEINFO(_) -> true
   | Ast0.ERRORWORDS(_) -> false
   | Ast0.CODE(_) -> true
+  | Ast0.TOPCODE(_)
   | Ast0.OTHER(_) -> failwith "unexpected top level code"
 
 (* ------------------------------------------------------------------- *)
@@ -826,7 +898,7 @@ let concat = function
          [] -> []
        | x::rest ->
            (match Ast0.unwrap x with
-             Ast0.DECL(s) -> let stms = loop rest in s::stms
+             Ast0.NONDECL(s) -> let stms = loop rest in s::stms
            | Ast0.CODE(ss) ->
                let stms = loop rest in
                (match Ast0.unwrap ss with
@@ -834,18 +906,18 @@ let concat = function
                | _ -> failwith "no dots allowed in pure plus code")
            | _ -> failwith "plus code is being discarded") in
       let res =
-       Compute_lines.statement_dots
+       Compute_lines.compute_statement_dots_lines false
          (Ast0.rewrap (List.hd l) (Ast0.DOTS (loop l))) in
       [Ast0.rewrap res (Ast0.CODE res)]
 
 let collect_up_to m plus =
   let minfo = Ast0.get_info m in
-  let mend = minfo.Ast0.logical_end in
+  let mend = minfo.Ast0.pos_info.Ast0.logical_end in
   let rec loop = function
       [] -> ([],[])
     | p::plus ->
        let pinfo = Ast0.get_info p in
-       let pstart = pinfo.Ast0.logical_start in
+       let pstart = pinfo.Ast0.pos_info.Ast0.logical_start in
        if pstart > mend
        then ([],p::plus)
        else let (plus,rest) = loop plus in (p::plus,rest) in
@@ -907,14 +979,17 @@ let rec is_toplevel s =
     Ast0.Decl(_,e) -> true
   | Ast0.FunDecl(_,_,_,_,_,_,_,_,_) -> true
   | Ast0.Disj(_,stmts,_,_) -> isall is_toplevel stmts
-  | Ast0.ExprStatement(fc,_) ->
+  | Ast0.ExprStatement(Some fc,_) ->
       (match Ast0.unwrap fc with
        Ast0.FunCall(_,_,_,_) -> true
       |        _ -> false)
   | Ast0.Include(_,_) -> true
+  | Ast0.Undef(_,_) -> true
   | Ast0.Define(_,_,_,_) -> true
   | _ -> false
 
+(* consider code and topcode to be the same; difference handled
+in top_level.ml *)
 let check_compatible m p =
   let fail _ =
     failwith
@@ -922,17 +997,21 @@ let check_compatible m p =
         "incompatible minus and plus code starting on lines %d and %d"
         (Ast0.get_line m) (Ast0.get_line p)) in
   match (Ast0.unwrap m, Ast0.unwrap p) with
-    (Ast0.DECL(decl1),Ast0.DECL(decl2)) ->
+    (Ast0.NONDECL(decl1),Ast0.NONDECL(decl2)) ->
       if not (is_decl decl1 && is_decl decl2)
       then fail()
-  | (Ast0.DECL(decl1),Ast0.CODE(code2)) ->
+  | (Ast0.NONDECL(decl1),Ast0.CODE(code2)) ->
+      (* This is probably the only important case.  We don't want to
+        replace top-level declarations by arbitrary code. *)
       let v1 = is_decl decl1 in
       let v2 = List.for_all is_toplevel (Ast0.undots code2) in
-      if !Flag.make_hrule = None && v1 && not v2 then fail()
-  | (Ast0.CODE(code1),Ast0.DECL(decl2)) ->
+      if !Flag.make_hrule = None && v1 && not v2
+      then fail()
+  | (Ast0.CODE(code1),Ast0.NONDECL(decl2)) ->
       let v1 = List.for_all is_toplevel (Ast0.undots code1) in
       let v2 = is_decl decl2 in
-      if v1 && not v2 then fail()
+      if v1 && not v2
+      then fail()
   | (Ast0.CODE(code1),Ast0.CODE(code2)) ->
       let v1 = isonly is_init code1 in
       let v2a = isonly is_init code2 in
@@ -950,11 +1029,29 @@ let check_compatible m p =
          testers;
        let v1 = isonly is_fndecl code1 in
        let v2 = List.for_all is_toplevel (Ast0.undots code2) in
-       if !Flag.make_hrule = None && v1 && not v2 then fail()
+       if !Flag.make_hrule = None && v1 && not v2
+       then fail()
   | (Ast0.FILEINFO(_,_),Ast0.FILEINFO(_,_)) -> ()
   | (Ast0.OTHER(_),Ast0.OTHER(_)) -> ()
   | _ -> fail()
 
+(* can't just remove expressions or types, not sure if all cases are needed. *)
+let check_complete m =
+   match Ast0.unwrap m with
+     Ast0.NONDECL(code) ->
+       if is_exp code or is_ty code
+       then
+        failwith
+          (Printf.sprintf "invalid minus starting on line %d"
+             (Ast0.get_line m))
+   | Ast0.CODE(code) ->
+       if isonly is_exp code or isonly is_ty code
+       then
+        failwith
+           (Printf.sprintf "invalid minus starting on line %d"
+              (Ast0.get_line m))
+   | _ -> ()
+
 (* ------------------------------------------------------------------- *)
 
 (* returns a list of corresponding minus and plus trees *)
@@ -968,22 +1065,24 @@ let context_neg minus plus =
     | ([],l) ->
        failwith (Printf.sprintf "%d plus things remaining" (List.length l))
     | (minus,[]) ->
+       List.iter check_complete minus;
        plus_lines := [];
        let _ =
          List.map
            (function m ->
              classify true
-               (function _ -> Ast0.MINUS(ref([],Ast0.default_token_info)))
+               (function _ ->
+                 Ast0.MINUS(ref(Ast.NOREPLACEMENT,Ast0.default_token_info)))
                minus_table m)
            minus in
        []
     | (((m::minus) as mall),((p::plus) as pall)) ->
        let minfo = Ast0.get_info m in
        let pinfo = Ast0.get_info p in
-       let mstart = minfo.Ast0.logical_start in
-       let mend = minfo.Ast0.logical_end in
-       let pstart = pinfo.Ast0.logical_start in
-       let pend = pinfo.Ast0.logical_end in
+       let mstart = minfo.Ast0.pos_info.Ast0.logical_start in
+       let mend = minfo.Ast0.pos_info.Ast0.logical_end in
+       let pstart = pinfo.Ast0.pos_info.Ast0.logical_start in
+       let pend = pinfo.Ast0.pos_info.Ast0.logical_end in
        if (iscode m or iscode p) &&
          (mend + 1 = pstart or pend + 1 = mstart or (* adjacent *)
           (mstart <= pstart && mend >= pstart) or
@@ -999,9 +1098,10 @@ let context_neg minus plus =
            collect_plus_lines p;
            let _ =
              classify true
-               (function _ -> Ast0.MINUS(ref([],Ast0.default_token_info)))
+               (function _ ->
+                 Ast0.MINUS(ref(Ast.NOREPLACEMENT,Ast0.default_token_info)))
                minus_table m in
-           let _ = classify false (function _ -> Ast0.PLUS) plus_table p in
+           let _ = classify false (function c -> Ast0.PLUS c) plus_table p in
            traverse minus_table plus_table;
            (m,p)::loop(minus,plus)
          end
@@ -1015,7 +1115,9 @@ let context_neg minus plus =
                plus_lines := [];
                let _ =
                  classify true
-                   (function _ -> Ast0.MINUS(ref([],Ast0.default_token_info)))
+                   (function _ ->
+                     Ast0.MINUS(ref(Ast.NOREPLACEMENT,
+                                    Ast0.default_token_info)))
                    minus_table m in
                loop(minus,pall)
              end