2 * Copyright 2005-2008, Ecole des Mines de Nantes, University of Copenhagen
3 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller
4 * This file is part of Coccinelle.
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.
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.
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/>.
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
23 (* The error message "no available token to attach to" often comes in an
24 argument list of unbounded length. In this case, one should move a comma so
25 that there is a comma after the + code. *)
27 (* Start at all of the corresponding BindContext nodes in the minus and
28 plus trees, and traverse their children. We take the same strategy as
29 before: collect the list of minus/context nodes/tokens and the list of plus
30 tokens, and then merge them. *)
32 module Ast = Ast_cocci
33 module Ast0 = Ast0_cocci
34 module V0 = Visitor_ast0
35 module CN = Context_neg
37 let get_option f = function
41 (* --------------------------------------------------------------------- *)
42 (* Collect root and all context nodes in a tree *)
44 let collect_context e =
45 let bind x y = x @ y in
46 let option_default = [] in
50 let donothing builder r k e =
51 match Ast0.get_mcodekind e with
52 Ast0.CONTEXT(_) -> (builder e) :: (k e)
55 (* special case for everything that contains whencode, so that we skip over
57 let expression r k e =
58 donothing Ast0.expr r k
60 (match Ast0.unwrap e with
61 Ast0.NestExpr(starter,exp,ender,whencode,multi) ->
62 Ast0.NestExpr(starter,exp,ender,None,multi)
63 | Ast0.Edots(dots,whencode) -> Ast0.Edots(dots,None)
64 | Ast0.Ecircles(dots,whencode) -> Ast0.Ecircles(dots,None)
65 | Ast0.Estars(dots,whencode) -> Ast0.Estars(dots,None)
68 let initialiser r k i =
69 donothing Ast0.ini r k
71 (match Ast0.unwrap i with
72 Ast0.Idots(dots,whencode) -> Ast0.Idots(dots,None)
76 donothing Ast0.stmt r k
78 (match Ast0.unwrap s with
79 Ast0.Nest(started,stm_dots,ender,whencode,multi) ->
80 Ast0.Nest(started,stm_dots,ender,[],multi)
81 | Ast0.Dots(dots,whencode) -> Ast0.Dots(dots,[])
82 | Ast0.Circles(dots,whencode) -> Ast0.Circles(dots,[])
83 | Ast0.Stars(dots,whencode) -> Ast0.Stars(dots,[])
86 let topfn r k e = Ast0.TopTag(e) :: (k e) in
89 V0.combiner bind option_default
90 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
92 (donothing Ast0.dotsExpr) (donothing Ast0.dotsInit)
93 (donothing Ast0.dotsParam) (donothing Ast0.dotsStmt)
94 (donothing Ast0.dotsDecl) (donothing Ast0.dotsCase)
95 (donothing Ast0.ident) expression (donothing Ast0.typeC) initialiser
96 (donothing Ast0.param) (donothing Ast0.decl) statement
97 (donothing Ast0.case_line) topfn in
98 res.V0.combiner_top_level e
100 (* --------------------------------------------------------------------- *)
101 (* --------------------------------------------------------------------- *)
102 (* collect the possible join points, in order, among the children of a
103 BindContext. Dots are not allowed. Nests and disjunctions are no problem,
104 because their delimiters take up a line by themselves *)
106 (* An Unfavored token is one that is in a BindContext node; using this causes
107 the node to become Neither, meaning that isomorphisms can't be applied *)
108 (* Toplevel is for the bef token of a function declaration and is for
109 attaching top-level definitions that should come before the complete
111 type minus_join_point = Favored | Unfavored | Toplevel | Decl
113 (* Maps the index of a node to the indices of the mcodes it contains *)
114 let root_token_table = (Hashtbl.create(50) : (int, int list) Hashtbl.t)
116 let create_root_token_table minus =
122 Ast0.DotsExprTag(d) -> Ast0.get_index d
123 | Ast0.DotsInitTag(d) -> Ast0.get_index d
124 | Ast0.DotsParamTag(d) -> Ast0.get_index d
125 | Ast0.DotsStmtTag(d) -> Ast0.get_index d
126 | Ast0.DotsDeclTag(d) -> Ast0.get_index d
127 | Ast0.DotsCaseTag(d) -> Ast0.get_index d
128 | Ast0.IdentTag(d) -> Ast0.get_index d
129 | Ast0.ExprTag(d) -> Ast0.get_index d
130 | Ast0.ArgExprTag(d) | Ast0.TestExprTag(d) ->
131 failwith "not possible - iso only"
132 | Ast0.TypeCTag(d) -> Ast0.get_index d
133 | Ast0.ParamTag(d) -> Ast0.get_index d
134 | Ast0.InitTag(d) -> Ast0.get_index d
135 | Ast0.DeclTag(d) -> Ast0.get_index d
136 | Ast0.StmtTag(d) -> Ast0.get_index d
137 | Ast0.CaseLineTag(d) -> Ast0.get_index d
138 | Ast0.TopTag(d) -> Ast0.get_index d
139 | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
140 | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
141 | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
142 | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase"
144 Hashtbl.add root_token_table key tokens)
148 let index = Ast0.get_index r in
149 try let _ = Hashtbl.find root_token_table index in ()
150 with Not_found -> Hashtbl.add root_token_table index [])
153 let collect_minus_join_points root =
154 let root_index = Ast0.get_index root in
155 let unfavored_tokens = Hashtbl.find root_token_table root_index in
156 let bind x y = x @ y in
157 let option_default = [] in
159 let mcode (_,_,info,mcodekind,_) =
160 if List.mem (info.Ast0.offset) unfavored_tokens
161 then [(Unfavored,info,mcodekind)]
162 else [(Favored,info,mcodekind)] in
164 let do_nothing r k e =
165 let info = Ast0.get_info e in
166 let index = Ast0.get_index e in
167 match Ast0.get_mcodekind e with
168 (Ast0.MINUS(_)) as mc -> [(Favored,info,mc)]
169 | (Ast0.CONTEXT(_)) as mc when not(index = root_index) ->
170 (* This was unfavored at one point, but I don't remember why *)
174 (* don't want to attach to the outside of DOTS, because metavariables can't
175 bind to that; not good for isomorphisms *)
179 let rec loop = function
182 | x::xs -> bind x (loop xs) in
185 match Ast0.unwrap d with
186 Ast0.DOTS(l) -> multibind (List.map f l)
187 | Ast0.CIRCLES(l) -> multibind (List.map f l)
188 | Ast0.STARS(l) -> multibind (List.map f l) in
190 let edots r k d = dots r.V0.combiner_expression k d in
191 let idots r k d = dots r.V0.combiner_initialiser k d in
192 let pdots r k d = dots r.V0.combiner_parameter k d in
193 let sdots r k d = dots r.V0.combiner_statement k d in
194 let ddots r k d = dots r.V0.combiner_declaration k d in
195 let cdots r k d = dots r.V0.combiner_case_line k d in
197 (* a case for everything that has a Opt *)
199 let statement r k s =
201 let redo_branched res (ifinfo,aftmc) =
202 let redo fv info mc rest =
203 let new_info = {info with Ast0.attachable_end = false} in
204 List.rev ((Favored,ifinfo,aftmc)::(fv,new_info,mc)::rest) in
205 match List.rev res with
208 Ast0.MINUS(_) | Ast0.CONTEXT(_) ->
209 (* even for -, better for isos not to integrate code after an
211 but the problem is that this can extend the region in
212 which a variable is bound, because a variable bound in the
213 aft node would seem to have to be live in the whole if,
214 whereas we might like it to be live in only one branch.
215 ie ideally, if we can keep the minus code in the right
216 order, we would like to drop it as close to the bindings
217 of its free variables. This could be anywhere in the minus
218 code. Perhaps we would like to do this after the
219 application of isomorphisms, though.
223 | (fv,info,mc)::rest ->
225 Ast0.CONTEXT(_) -> redo fv info mc rest
227 | _ -> failwith "unexpected empty code" in *)
228 match Ast0.unwrap s with
229 (* Ast0.IfThen(_,_,_,_,_,aft)
230 | Ast0.IfThenElse(_,_,_,_,_,_,_,aft)
231 | Ast0.While(_,_,_,_,_,aft)
232 | Ast0.For(_,_,_,_,_,_,_,_,_,aft)
233 | Ast0.Iterator(_,_,_,_,_,aft) ->
234 redo_branched (do_nothing r k s) aft*)
235 | Ast0.FunDecl((info,bef),fninfo,name,lp,params,rp,lbrace,body,rbrace) ->
236 (Toplevel,info,bef)::(k s)
237 | Ast0.Decl((info,bef),decl) -> (Decl,info,bef)::(k s)
238 | Ast0.Nest(starter,stmt_dots,ender,whencode,multi) ->
239 mcode starter @ r.V0.combiner_statement_dots stmt_dots @ mcode ender
240 | Ast0.Dots(d,whencode) | Ast0.Circles(d,whencode)
241 | Ast0.Stars(d,whencode) -> mcode d (* ignore whencode *)
242 | Ast0.OptStm s | Ast0.UniqueStm s ->
243 (* put the + code on the thing, not on the opt *)
244 r.V0.combiner_statement s
245 | _ -> do_nothing r k s in
247 let expression r k e =
248 match Ast0.unwrap e with
249 Ast0.NestExpr(starter,expr_dots,ender,whencode,multi) ->
251 r.V0.combiner_expression_dots expr_dots @ mcode ender
252 | Ast0.Edots(d,whencode) | Ast0.Ecircles(d,whencode)
253 | Ast0.Estars(d,whencode) -> mcode d (* ignore whencode *)
254 | Ast0.OptExp e | Ast0.UniqueExp e ->
255 (* put the + code on the thing, not on the opt *)
256 r.V0.combiner_expression e
257 | _ -> do_nothing r k e in
260 match Ast0.unwrap e with
261 Ast0.OptIdent i | Ast0.UniqueIdent i ->
262 (* put the + code on the thing, not on the opt *)
263 r.V0.combiner_ident i
264 | _ -> do_nothing r k e in
267 match Ast0.unwrap e with
268 Ast0.OptType t | Ast0.UniqueType t ->
269 (* put the + code on the thing, not on the opt *)
270 r.V0.combiner_typeC t
271 | _ -> do_nothing r k e in
274 match Ast0.unwrap e with
275 Ast0.OptDecl d | Ast0.UniqueDecl d ->
276 (* put the + code on the thing, not on the opt *)
277 r.V0.combiner_declaration d
278 | _ -> do_nothing r k e in
280 let initialiser r k e =
281 match Ast0.unwrap e with
282 Ast0.Idots(d,whencode) -> mcode d (* ignore whencode *)
283 | Ast0.OptIni i | Ast0.UniqueIni i ->
284 (* put the + code on the thing, not on the opt *)
285 r.V0.combiner_initialiser i
286 | _ -> do_nothing r k e in
289 match Ast0.unwrap e with
290 Ast0.OptParam p | Ast0.UniqueParam p ->
291 (* put the + code on the thing, not on the opt *)
292 r.V0.combiner_parameter p
293 | _ -> do_nothing r k e in
295 let case_line r k e =
296 match Ast0.unwrap e with
298 (* put the + code on the thing, not on the opt *)
299 r.V0.combiner_case_line c
300 | _ -> do_nothing r k e in
302 let do_top r k (e: Ast0.top_level) = k e in
304 V0.combiner bind option_default
305 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
307 edots idots pdots sdots ddots cdots
308 ident expression typeC initialiser param decl statement case_line do_top
311 let call_collect_minus context_nodes :
312 (int * (minus_join_point * Ast0.info * Ast0.mcodekind) list) list =
316 Ast0.DotsExprTag(e) ->
318 (collect_minus_join_points e).V0.combiner_expression_dots e)
319 | Ast0.DotsInitTag(e) ->
321 (collect_minus_join_points e).V0.combiner_initialiser_list e)
322 | Ast0.DotsParamTag(e) ->
324 (collect_minus_join_points e).V0.combiner_parameter_list e)
325 | Ast0.DotsStmtTag(e) ->
327 (collect_minus_join_points e).V0.combiner_statement_dots e)
328 | Ast0.DotsDeclTag(e) ->
330 (collect_minus_join_points e).V0.combiner_declaration_dots e)
331 | Ast0.DotsCaseTag(e) ->
333 (collect_minus_join_points e).V0.combiner_case_line_dots e)
334 | Ast0.IdentTag(e) ->
336 (collect_minus_join_points e).V0.combiner_ident e)
339 (collect_minus_join_points e).V0.combiner_expression e)
340 | Ast0.ArgExprTag(e) | Ast0.TestExprTag(e) ->
341 failwith "not possible - iso only"
342 | Ast0.TypeCTag(e) ->
344 (collect_minus_join_points e).V0.combiner_typeC e)
345 | Ast0.ParamTag(e) ->
347 (collect_minus_join_points e).V0.combiner_parameter e)
350 (collect_minus_join_points e).V0.combiner_initialiser e)
353 (collect_minus_join_points e).V0.combiner_declaration e)
356 (collect_minus_join_points e).V0.combiner_statement e)
357 | Ast0.CaseLineTag(e) ->
359 (collect_minus_join_points e).V0.combiner_case_line e)
362 (collect_minus_join_points e).V0.combiner_top_level e)
363 | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
364 | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
365 | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
366 | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase")
369 (* result of collecting the join points should be sorted in nondecreasing
372 let get_info = function
373 (Favored,info,_) | (Unfavored,info,_) | (Toplevel,info,_)
374 | (Decl,info,_) -> info in
375 let token_start_line x = (get_info x).Ast0.logical_start in
376 let token_end_line x = (get_info x).Ast0.logical_end in
377 let token_real_start_line x = (get_info x).Ast0.line_start in
378 let token_real_end_line x = (get_info x).Ast0.line_end in
381 (index,((_::_) as l1)) ->
384 (function (prev,real_prev) ->
386 let ln = token_start_line cur in
391 "error in collection of - tokens %d less than %d"
392 (token_real_start_line cur) real_prev);
393 (token_end_line cur,token_real_end_line cur))
394 (token_end_line (List.hd l1), token_real_end_line (List.hd l1))
397 | _ -> ()) (* dots, in eg f() has no join points *)
400 let process_minus minus =
401 create_root_token_table minus;
405 let res = call_collect_minus (collect_context x) in
410 (* --------------------------------------------------------------------- *)
411 (* --------------------------------------------------------------------- *)
412 (* collect the plus tokens *)
414 let mk_baseType x = Ast.BaseTypeTag x
415 let mk_structUnion x = Ast.StructUnionTag x
416 let mk_sign x = Ast.SignTag x
417 let mk_ident x = Ast.IdentTag (Ast0toast.ident x)
418 let mk_expression x = Ast.ExpressionTag (Ast0toast.expression x)
419 let mk_constant x = Ast.ConstantTag x
420 let mk_unaryOp x = Ast.UnaryOpTag x
421 let mk_assignOp x = Ast.AssignOpTag x
422 let mk_fixOp x = Ast.FixOpTag x
423 let mk_binaryOp x = Ast.BinaryOpTag x
424 let mk_arithOp x = Ast.ArithOpTag x
425 let mk_logicalOp x = Ast.LogicalOpTag x
426 let mk_declaration x = Ast.DeclarationTag (Ast0toast.declaration x)
427 let mk_topdeclaration x = Ast.DeclarationTag (Ast0toast.declaration x)
428 let mk_storage x = Ast.StorageTag x
429 let mk_inc_file x = Ast.IncFileTag x
430 let mk_statement x = Ast.StatementTag (Ast0toast.statement x)
431 let mk_case_line x = Ast.CaseLineTag (Ast0toast.case_line x)
432 let mk_const_vol x = Ast.ConstVolTag x
433 let mk_token x info = Ast.Token (x,Some info)
434 let mk_meta (_,x) info = Ast.Token (x,Some info)
435 let mk_code x = Ast.Code (Ast0toast.top_level x)
437 let mk_exprdots x = Ast.ExprDotsTag (Ast0toast.expression_dots x)
438 let mk_paramdots x = Ast.ParamDotsTag (Ast0toast.parameter_list x)
439 let mk_stmtdots x = Ast.StmtDotsTag (Ast0toast.statement_dots x)
440 let mk_decldots x = Ast.DeclDotsTag (Ast0toast.declaration_dots x)
441 let mk_casedots x = failwith "+ case lines not supported"
442 let mk_typeC x = Ast.FullTypeTag (Ast0toast.typeC x)
443 let mk_init x = Ast.InitTag (Ast0toast.initialiser x)
444 let mk_param x = Ast.ParamTag (Ast0toast.parameterTypeDef x)
446 let collect_plus_nodes root =
447 let root_index = Ast0.get_index root in
449 let bind x y = x @ y in
450 let option_default = [] in
452 let mcode fn (term,_,info,mcodekind,_) =
453 match mcodekind with Ast0.PLUS -> [(info,fn term)] | _ -> [] in
455 let imcode fn (term,_,info,mcodekind,_) =
457 Ast0.PLUS -> [(info,fn term (Ast0toast.convert_info info))]
460 let do_nothing fn r k e =
461 match Ast0.get_mcodekind e with
462 (Ast0.CONTEXT(_)) when not(Ast0.get_index e = root_index) -> []
463 | Ast0.PLUS -> [(Ast0.get_info e,fn e)]
466 (* case for everything that is just a wrapper for a simpler thing *)
468 match Ast0.unwrap e with
469 Ast0.Exp(exp) -> r.V0.combiner_expression exp
470 | Ast0.TopExp(exp) -> r.V0.combiner_expression exp
471 | Ast0.Ty(ty) -> r.V0.combiner_typeC ty
472 | Ast0.Decl(_,decl) -> r.V0.combiner_declaration decl
473 | _ -> do_nothing mk_statement r k e in
475 (* statementTag is preferred, because it indicates that one statement is
476 replaced by one statement, in single_statement *)
477 let stmt_dots r k e =
478 match Ast0.unwrap e with
479 Ast0.DOTS([s]) | Ast0.CIRCLES([s]) | Ast0.STARS([s]) ->
480 r.V0.combiner_statement s
481 | _ -> do_nothing mk_stmtdots r k e in
484 match Ast0.unwrap e with
485 Ast0.DECL(s) -> r.V0.combiner_statement s
486 | Ast0.CODE(sdots) -> r.V0.combiner_statement_dots sdots
487 | _ -> do_nothing mk_code r k e in
489 let initdots r k e = k e in
491 V0.combiner bind option_default
492 (imcode mk_meta) (imcode mk_token) (mcode mk_constant) (mcode mk_assignOp)
494 (mcode mk_unaryOp) (mcode mk_binaryOp) (mcode mk_const_vol)
495 (mcode mk_baseType) (mcode mk_sign) (mcode mk_structUnion)
496 (mcode mk_storage) (mcode mk_inc_file)
497 (do_nothing mk_exprdots) initdots
498 (do_nothing mk_paramdots) stmt_dots (do_nothing mk_decldots)
499 (do_nothing mk_casedots)
500 (do_nothing mk_ident) (do_nothing mk_expression)
501 (do_nothing mk_typeC) (do_nothing mk_init) (do_nothing mk_param)
502 (do_nothing mk_declaration)
503 stmt (do_nothing mk_case_line) toplevel
505 let call_collect_plus context_nodes :
506 (int * (Ast0.info * Ast.anything) list) list =
510 Ast0.DotsExprTag(e) ->
512 (collect_plus_nodes e).V0.combiner_expression_dots e)
513 | Ast0.DotsInitTag(e) ->
515 (collect_plus_nodes e).V0.combiner_initialiser_list e)
516 | Ast0.DotsParamTag(e) ->
518 (collect_plus_nodes e).V0.combiner_parameter_list e)
519 | Ast0.DotsStmtTag(e) ->
521 (collect_plus_nodes e).V0.combiner_statement_dots e)
522 | Ast0.DotsDeclTag(e) ->
524 (collect_plus_nodes e).V0.combiner_declaration_dots e)
525 | Ast0.DotsCaseTag(e) ->
527 (collect_plus_nodes e).V0.combiner_case_line_dots e)
528 | Ast0.IdentTag(e) ->
530 (collect_plus_nodes e).V0.combiner_ident e)
533 (collect_plus_nodes e).V0.combiner_expression e)
534 | Ast0.ArgExprTag(_) | Ast0.TestExprTag(_) ->
535 failwith "not possible - iso only"
536 | Ast0.TypeCTag(e) ->
538 (collect_plus_nodes e).V0.combiner_typeC e)
541 (collect_plus_nodes e).V0.combiner_initialiser e)
542 | Ast0.ParamTag(e) ->
544 (collect_plus_nodes e).V0.combiner_parameter e)
547 (collect_plus_nodes e).V0.combiner_declaration e)
550 (collect_plus_nodes e).V0.combiner_statement e)
551 | Ast0.CaseLineTag(e) ->
553 (collect_plus_nodes e).V0.combiner_case_line e)
556 (collect_plus_nodes e).V0.combiner_top_level e)
557 | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
558 | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
559 | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
560 | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase")
563 (* The plus fragments are converted to a list of lists of lists.
564 Innermost list: Elements have type anything. For any pair of successive
565 elements, n and n+1, the ending line of n is the same as the starting line
567 Middle lists: For any pair of successive elements, n and n+1, the ending
568 line of n is one less than the starting line of n+1.
569 Outer list: For any pair of successive elements, n and n+1, the ending
570 line of n is more than one less than the starting line of n+1. *)
572 let logstart info = info.Ast0.logical_start
573 let logend info = info.Ast0.logical_end
575 let redo info start finish =
576 {{info with Ast0.logical_start = start} with Ast0.logical_end = finish}
578 let rec find_neighbors (index,l) :
579 int * (Ast0.info * (Ast.anything list list)) list =
580 let rec loop = function
583 (match loop rest with
584 ((i1,(x1::rest_inner))::rest_middle)::rest_outer ->
585 let finish1 = logend i in
586 let start2 = logstart i1 in
589 ((redo i (logstart i) (logend i1),(x::x1::rest_inner))
592 else if finish1 + 1 = start2
593 then ((i,[x])::(i1,(x1::rest_inner))::rest_middle)::rest_outer
594 else [(i,[x])]::((i1,(x1::rest_inner))::rest_middle)::rest_outer
595 | _ -> [[(i,[x])]]) (* rest must be [] *) in
599 let (start_info,_) = List.hd l in
600 let (end_info,_) = List.hd (List.rev l) in
601 (redo start_info (logstart start_info) (logend end_info),
602 List.map (function (_,x) -> x) l))
606 let process_plus plus :
607 (int * (Ast0.info * Ast.anything list list) list) list =
611 List.map find_neighbors (call_collect_plus (collect_context x)))
614 (* --------------------------------------------------------------------- *)
615 (* --------------------------------------------------------------------- *)
618 let merge_one = function
619 (m1::m2::minus_info,p::plus_info) ->
621 attach p to the beginning of m1.bef if m1 is Good, fail if it is bad
622 if p > m1 && p < m2, then consider the following possibilities, in order
623 m1 is Good and favored: attach to the beginning of m1.aft
624 m2 is Good and favored: attach to the beginning of m2.bef; drop m1
625 m1 is Good and unfavored: attach to the beginning of m1.aft
626 m2 is Good and unfavored: attach to the beginning of m2.bef; drop m1
627 also flip m1.bef if the first where > m1
628 if we drop m1, then flip m1.aft first
630 m2 is Good and favored: attach to the beginning of m2.aft; drop m1
633 (* end of first argument < start/end of second argument *)
634 let less_than_start info1 info2 =
635 info1.Ast0.logical_end < info2.Ast0.logical_start
636 let less_than_end info1 info2 =
637 info1.Ast0.logical_end < info2.Ast0.logical_end
638 let greater_than_end info1 info2 =
639 info1.Ast0.logical_start > info2.Ast0.logical_end
640 let good_start info = info.Ast0.attachable_start
641 let good_end info = info.Ast0.attachable_end
643 let toplevel = function Toplevel -> true | Favored | Unfavored | Decl -> false
644 let decl = function Decl -> true | Favored | Unfavored | Toplevel -> false
645 let favored = function Favored -> true | Unfavored | Toplevel | Decl -> false
648 List.for_all (List.for_all (function Ast.Code _ -> true | _ -> false))
650 (* The following is probably not correct. The idea is to detect what
651 should be placed completely before the declaration. So type/storage
652 related things do not fall into this category, and complete statements do
653 fall into this category. But perhaps other things should be in this
654 category as well, such as { or ;? *)
656 let tester = function
657 (* the following should definitely be true *)
663 (* the following should definitely be false *)
664 | Ast.FullTypeTag _ | Ast.BaseTypeTag _ | Ast.StructUnionTag _
666 | Ast.StorageTag _ | Ast.ConstVolTag _ | Ast.TypeCTag _ -> false
667 (* not sure about the rest *)
669 List.for_all (List.for_all tester)
671 let pr = Printf.sprintf
673 let insert thing thinginfo into intoinfo =
674 let get_last l = let l = List.rev l in (List.rev(List.tl l),List.hd l) in
675 let get_first l = (List.hd l,List.tl l) in
676 let thing_start = thinginfo.Ast0.logical_start in
677 let thing_end = thinginfo.Ast0.logical_end in
678 let thing_offset = thinginfo.Ast0.offset in
679 let into_start = intoinfo.Ast0.tline_start in
680 let into_end = intoinfo.Ast0.tline_end in
681 let into_left_offset = intoinfo.Ast0.left_offset in
682 let into_right_offset = intoinfo.Ast0.right_offset in
683 if thing_end < into_start && thing_start < into_start
685 {{intoinfo with Ast0.tline_start = thing_start}
686 with Ast0.left_offset = thing_offset})
687 else if thing_end = into_start && thing_offset < into_left_offset
689 let (prev,last) = get_last thing in
690 let (first,rest) = get_first into in
691 (prev@[last@first]@rest,
692 {{intoinfo with Ast0.tline_start = thing_start}
693 with Ast0.left_offset = thing_offset})
694 else if thing_start > into_end && thing_end > into_end
696 {{intoinfo with Ast0.tline_end = thing_end}
697 with Ast0.right_offset = thing_offset})
698 else if thing_start = into_end && thing_offset > into_right_offset
700 let (first,rest) = get_first thing in
701 let (prev,last) = get_last into in
702 (prev@[last@first]@rest,
703 {{intoinfo with Ast0.tline_end = thing_end}
704 with Ast0.right_offset = thing_offset})
707 Printf.printf "thing start %d thing end %d into start %d into end %d\n"
708 thing_start thing_end into_start into_end;
709 Printf.printf "thing offset %d left offset %d right offset %d\n"
710 thing_offset into_left_offset into_right_offset;
711 Pretty_print_cocci.print_anything "" thing;
712 Pretty_print_cocci.print_anything "" into;
713 failwith "can't figure out where to put the + code"
716 let init thing info =
718 {Ast0.tline_start = info.Ast0.logical_start;
719 Ast0.tline_end = info.Ast0.logical_end;
720 Ast0.left_offset = info.Ast0.offset;
721 Ast0.right_offset = info.Ast0.offset})
723 let attachbefore (infop,p) = function
724 Ast0.MINUS(replacements) ->
725 (match !replacements with
726 ([],ti) -> replacements := init p infop
727 | (repl,ti) -> replacements := insert p infop repl ti)
728 | Ast0.CONTEXT(neighbors) ->
729 let (repl,ti1,ti2) = !neighbors in
732 let (bef,ti1) = insert p infop bef ti1 in
733 neighbors := (Ast.BEFORE(bef),ti1,ti2)
735 let (bef,ti1) = init p infop in
736 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
737 | Ast.BEFOREAFTER(bef,aft) ->
738 let (bef,ti1) = insert p infop bef ti1 in
739 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
741 let (bef,ti1) = init p infop in
742 neighbors := (Ast.BEFORE(bef),ti1,ti2))
743 | _ -> failwith "not possible for attachbefore"
745 let attachafter (infop,p) = function
746 Ast0.MINUS(replacements) ->
747 (match !replacements with
748 ([],ti) -> replacements := init p infop
749 | (repl,ti) -> replacements := insert p infop repl ti)
750 | Ast0.CONTEXT(neighbors) ->
751 let (repl,ti1,ti2) = !neighbors in
754 let (aft,ti2) = init p infop in
755 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
757 let (aft,ti2) = insert p infop aft ti2 in
758 neighbors := (Ast.AFTER(aft),ti1,ti2)
759 | Ast.BEFOREAFTER(bef,aft) ->
760 let (aft,ti2) = insert p infop aft ti2 in
761 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
763 let (aft,ti2) = init p infop in
764 neighbors := (Ast.AFTER(aft),ti1,ti2))
765 | _ -> failwith "not possible for attachbefore"
767 let attach_all_before ps m =
768 List.iter (function x -> attachbefore x m) ps
770 let attach_all_after ps m =
771 List.iter (function x -> attachafter x m) ps
773 let split_at_end info ps =
774 let split_point = info.Ast0.logical_end in
776 (function (info,_) -> info.Ast0.logical_end < split_point)
779 let allminus = function
780 Ast0.MINUS(_) -> true
783 let rec before_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
785 | (((infop,_) as p) :: ps) as all ->
786 if less_than_start infop infom1 or
787 (allminus m1 && less_than_end infop infom1) (* account for trees *)
790 then (attachbefore p m1; before_m1 x1 x2 rest ps)
793 (pr "%d: no available token to attach to" infop.Ast0.line_start)
794 else after_m1 x1 x2 rest all
796 and after_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
798 | (((infop,pcode) as p) :: ps) as all ->
799 (* if the following is false, then some + code is stuck in the middle
800 of some context code (m1). could drop down to the token level.
801 this might require adjustments in ast0toast as well, when + code on
802 expressions is dropped down to + code on expressions. it might
803 also break some invariants on which iso depends, particularly on
804 what it can infer from something being CONTEXT with no top-level
805 modifications. for the moment, we thus give an error, asking the
806 user to rewrite the semantic patch. *)
807 if greater_than_end infop infom1
809 if less_than_start infop infom2
811 if predecl_code pcode && good_end infom1 && decl f1
812 then (attachafter p m1; after_m1 x1 x2 rest ps)
813 else if predecl_code pcode && good_start infom2 && decl f2
814 then before_m2 x2 rest all
815 else if top_code pcode && good_end infom1 && toplevel f1
816 then (attachafter p m1; after_m1 x1 x2 rest ps)
817 else if top_code pcode && good_start infom2 && toplevel f2
818 then before_m2 x2 rest all
819 else if good_end infom1 && favored f1
820 then (attachafter p m1; after_m1 x1 x2 rest ps)
821 else if good_start infom2 && favored f2
822 then before_m2 x2 rest all
823 else if good_end infom1
824 then (attachafter p m1; after_m1 x1 x2 rest ps)
825 else if good_start infom2
826 then before_m2 x2 rest all
829 (pr "%d: no available token to attach to" infop.Ast0.line_start)
830 else after_m2 x2 rest all
833 Printf.printf "between: p start %d p end %d m1 start %d m1 end %d m2 start %d m2 end %d\n"
834 infop.Ast0.line_start infop.Ast0.line_end
835 infom1.Ast0.line_start infom1.Ast0.line_end
836 infom2.Ast0.line_start infom2.Ast0.line_end;
837 Pretty_print_cocci.print_anything "" pcode;
839 "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."
842 and before_m2 ((f2,infom2,m2) as x2) rest
843 (p : (Ast0.info * Ast.anything list list) list) =
846 | ([],((infop,_)::_)) ->
847 let (bef_m2,aft_m2) = split_at_end infom2 p in (* bef_m2 isn't empty *)
849 then (attach_all_before bef_m2 m2; after_m2 x2 rest aft_m2)
852 (pr "%d: no available token to attach to" infop.Ast0.line_start)
853 | (m::ms,_) -> before_m1 x2 m ms p
855 and after_m2 ((f2,infom2,m2) as x2) rest
856 (p : (Ast0.info * Ast.anything list list) list) =
859 | ([],((infop,_)::_)) ->
861 then attach_all_after p m2
864 (pr "%d: no available token to attach to" infop.Ast0.line_start)
865 | (m::ms,_) -> after_m1 x2 m ms p
867 let merge_one : (minus_join_point * Ast0.info * 'a) list *
868 (Ast0.info * Ast.anything list list) list -> unit = function (m,p) ->
870 Printf.printf "minus code\n";
872 (function (_,info,_) ->
873 Printf.printf "start %d end %d real_start %d real_end %d\n"
874 info.Ast0.logical_start info.Ast0.logical_end
875 info.Ast0.line_start info.Ast0.line_end)
877 Printf.printf "plus code\n";
879 (function (info,p) ->
880 Printf.printf "start %d end %d real_start %d real_end %d\n"
881 info.Ast0.logical_start info.Ast0.logical_end
882 info.Ast0.line_end info.Ast0.line_end;
883 Pretty_print_cocci.print_anything "" p;
884 Format.print_newline())
889 | (m1::m2::restm,p) -> before_m1 m1 m2 restm p
890 | ([m],p) -> before_m2 m [] p
891 | ([],_) -> failwith "minus tree ran out before the plus tree"
893 let merge minus_list plus_list =
895 Printf.printf "minus list %s\n"
897 (List.map (function (x,_) -> string_of_int x) minus_list));
898 Printf.printf "plus list %s\n"
900 (List.map (function (x,_) -> string_of_int x) plus_list));
903 (function (index,minus_info) ->
904 let plus_info = List.assoc index plus_list in
905 merge_one (minus_info,plus_info))
908 (* --------------------------------------------------------------------- *)
909 (* --------------------------------------------------------------------- *)
910 (* Need to check that CONTEXT nodes have nothing attached to their tokens.
911 If they do, they become MIXED *)
913 let reevaluate_contextness =
915 let option_default = [] in
917 let mcode (_,_,_,mc,_) =
919 Ast0.CONTEXT(mc) -> let (ba,_,_) = !mc in [ba]
922 let donothing r k e =
923 match Ast0.get_mcodekind e with
925 if List.exists (function Ast.NOTHING -> false | _ -> true) (k e)
926 then Ast0.set_mcodekind e (Ast0.MIXED(mc));
928 | _ -> let _ = k e in [] in
931 V0.combiner bind option_default
932 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
934 donothing donothing donothing donothing donothing donothing donothing
936 donothing donothing donothing donothing donothing donothing donothing in
937 res.V0.combiner_top_level
939 (* --------------------------------------------------------------------- *)
940 (* --------------------------------------------------------------------- *)
942 let insert_plus minus plus =
943 let minus_stream = process_minus minus in
944 let plus_stream = process_plus plus in
945 merge minus_stream plus_stream;
946 List.iter (function x -> let _ = reevaluate_contextness x in ()) minus