Release coccinelle-0.1.1
[bpt/coccinelle.git] / parsing_cocci / insert_plus.ml
CommitLineData
34e49164
C
1(*
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.
5*
6* Coccinelle is free software: you can redistribute it and/or modify
7* it under the terms of the GNU General Public License as published by
8* the Free Software Foundation, according to version 2 of the License.
9*
10* Coccinelle is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
17*
18* The authors reserve the right to distribute this or future versions of
19* Coccinelle under other licenses.
20*)
21
22
23(* The error message "no available token to attach to" often comes in an
24argument list of unbounded length. In this case, one should move a comma so
25that there is a comma after the + code. *)
26
27(* Start at all of the corresponding BindContext nodes in the minus and
28plus trees, and traverse their children. We take the same strategy as
29before: collect the list of minus/context nodes/tokens and the list of plus
30tokens, and then merge them. *)
31
32module Ast = Ast_cocci
33module Ast0 = Ast0_cocci
34module V0 = Visitor_ast0
35module CN = Context_neg
36
37let get_option f = function
38 None -> []
39 | Some x -> f x
40
41(* --------------------------------------------------------------------- *)
42(* Collect root and all context nodes in a tree *)
43
44let collect_context e =
45 let bind x y = x @ y in
46 let option_default = [] in
47
48 let mcode _ = [] in
49
50 let donothing builder r k e =
51 match Ast0.get_mcodekind e with
52 Ast0.CONTEXT(_) -> (builder e) :: (k e)
53 | _ -> k e in
54
55(* special case for everything that contains whencode, so that we skip over
56it *)
57 let expression r k e =
58 donothing Ast0.expr r k
59 (Ast0.rewrap e
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)
66 | e -> e)) in
67
68 let initialiser r k i =
69 donothing Ast0.ini r k
70 (Ast0.rewrap i
71 (match Ast0.unwrap i with
72 Ast0.Idots(dots,whencode) -> Ast0.Idots(dots,None)
73 | i -> i)) in
74
75 let statement r k s =
76 donothing Ast0.stmt r k
77 (Ast0.rewrap s
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,[])
84 | s -> s)) in
85
86 let topfn r k e = Ast0.TopTag(e) :: (k e) in
87
88 let res =
89 V0.combiner bind option_default
90 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
91 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
99
100(* --------------------------------------------------------------------- *)
101(* --------------------------------------------------------------------- *)
102(* collect the possible join points, in order, among the children of a
103BindContext. Dots are not allowed. Nests and disjunctions are no problem,
104because their delimiters take up a line by themselves *)
105
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
109attaching top-level definitions that should come before the complete
110declaration *)
111type minus_join_point = Favored | Unfavored | Toplevel | Decl
112
113(* Maps the index of a node to the indices of the mcodes it contains *)
114let root_token_table = (Hashtbl.create(50) : (int, int list) Hashtbl.t)
115
116let create_root_token_table minus =
117 Hashtbl.iter
118 (function tokens ->
119 function (node,_) ->
120 let key =
121 match node with
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"
1be43e12
C
140 | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
141 | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
34e49164
C
142 | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase"
143 in
144 Hashtbl.add root_token_table key tokens)
145 CN.minus_table;
146 List.iter
147 (function r ->
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 [])
151 minus
152
153let 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
158
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
163
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 *)
171 [(Favored,info,mc)]
172 | _ -> k e in
173
174(* don't want to attach to the outside of DOTS, because metavariables can't
175bind to that; not good for isomorphisms *)
176
177 let dots f k d =
178 let multibind l =
179 let rec loop = function
180 [] -> option_default
181 | [x] -> x
182 | x::xs -> bind x (loop xs) in
183 loop l in
184
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
189
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
196
197 (* a case for everything that has a Opt *)
198
199 let statement r k s =
200 (*
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
206 [(fv,info,mc)] ->
207 (match mc with
208 Ast0.MINUS(_) | Ast0.CONTEXT(_) ->
209 (* even for -, better for isos not to integrate code after an
210 if into the if body.
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.
220 *)
221 redo fv info mc []
222 | _ -> res)
223 | (fv,info,mc)::rest ->
224 (match mc with
225 Ast0.CONTEXT(_) -> redo fv info mc rest
226 | _ -> res)
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
246
247 let expression r k e =
248 match Ast0.unwrap e with
249 Ast0.NestExpr(starter,expr_dots,ender,whencode,multi) ->
250 mcode starter @
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
258
259 let ident r k e =
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
265
266 let typeC r k e =
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
272
273 let decl r k e =
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
279
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
287
288 let param r k e =
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
294
295 let case_line r k e =
296 match Ast0.unwrap e with
297 Ast0.OptCase c ->
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
301
302 let do_top r k (e: Ast0.top_level) = k e in
303
304 V0.combiner bind option_default
305 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
306 mcode
307 edots idots pdots sdots ddots cdots
308 ident expression typeC initialiser param decl statement case_line do_top
309
310
311let call_collect_minus context_nodes :
312 (int * (minus_join_point * Ast0.info * Ast0.mcodekind) list) list =
313 List.map
314 (function e ->
315 match e with
316 Ast0.DotsExprTag(e) ->
317 (Ast0.get_index e,
318 (collect_minus_join_points e).V0.combiner_expression_dots e)
319 | Ast0.DotsInitTag(e) ->
320 (Ast0.get_index e,
321 (collect_minus_join_points e).V0.combiner_initialiser_list e)
322 | Ast0.DotsParamTag(e) ->
323 (Ast0.get_index e,
324 (collect_minus_join_points e).V0.combiner_parameter_list e)
325 | Ast0.DotsStmtTag(e) ->
326 (Ast0.get_index e,
327 (collect_minus_join_points e).V0.combiner_statement_dots e)
328 | Ast0.DotsDeclTag(e) ->
329 (Ast0.get_index e,
330 (collect_minus_join_points e).V0.combiner_declaration_dots e)
331 | Ast0.DotsCaseTag(e) ->
332 (Ast0.get_index e,
333 (collect_minus_join_points e).V0.combiner_case_line_dots e)
334 | Ast0.IdentTag(e) ->
335 (Ast0.get_index e,
336 (collect_minus_join_points e).V0.combiner_ident e)
337 | Ast0.ExprTag(e) ->
338 (Ast0.get_index 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) ->
343 (Ast0.get_index e,
344 (collect_minus_join_points e).V0.combiner_typeC e)
345 | Ast0.ParamTag(e) ->
346 (Ast0.get_index e,
347 (collect_minus_join_points e).V0.combiner_parameter e)
348 | Ast0.InitTag(e) ->
349 (Ast0.get_index e,
350 (collect_minus_join_points e).V0.combiner_initialiser e)
351 | Ast0.DeclTag(e) ->
352 (Ast0.get_index e,
353 (collect_minus_join_points e).V0.combiner_declaration e)
354 | Ast0.StmtTag(e) ->
355 (Ast0.get_index e,
356 (collect_minus_join_points e).V0.combiner_statement e)
357 | Ast0.CaseLineTag(e) ->
358 (Ast0.get_index e,
359 (collect_minus_join_points e).V0.combiner_case_line e)
360 | Ast0.TopTag(e) ->
361 (Ast0.get_index e,
362 (collect_minus_join_points e).V0.combiner_top_level e)
363 | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
1be43e12
C
364 | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
365 | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
34e49164
C
366 | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase")
367 context_nodes
368
369(* result of collecting the join points should be sorted in nondecreasing
370 order by line *)
371let verify l =
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
379 List.iter
380 (function
381 (index,((_::_) as l1)) ->
382 let _ =
383 List.fold_left
384 (function (prev,real_prev) ->
385 function cur ->
386 let ln = token_start_line cur in
387 if ln < prev
388 then
389 failwith
390 (Printf.sprintf
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))
395 (List.tl l1) in
396 ()
397 | _ -> ()) (* dots, in eg f() has no join points *)
398 l
399
400let process_minus minus =
401 create_root_token_table minus;
402 List.concat
403 (List.map
404 (function x ->
405 let res = call_collect_minus (collect_context x) in
406 verify res;
407 res)
408 minus)
409
410(* --------------------------------------------------------------------- *)
411(* --------------------------------------------------------------------- *)
412(* collect the plus tokens *)
413
414let mk_baseType x = Ast.BaseTypeTag x
415let mk_structUnion x = Ast.StructUnionTag x
416let mk_sign x = Ast.SignTag x
417let mk_ident x = Ast.IdentTag (Ast0toast.ident x)
418let mk_expression x = Ast.ExpressionTag (Ast0toast.expression x)
419let mk_constant x = Ast.ConstantTag x
420let mk_unaryOp x = Ast.UnaryOpTag x
421let mk_assignOp x = Ast.AssignOpTag x
422let mk_fixOp x = Ast.FixOpTag x
423let mk_binaryOp x = Ast.BinaryOpTag x
424let mk_arithOp x = Ast.ArithOpTag x
425let mk_logicalOp x = Ast.LogicalOpTag x
426let mk_declaration x = Ast.DeclarationTag (Ast0toast.declaration x)
427let mk_topdeclaration x = Ast.DeclarationTag (Ast0toast.declaration x)
428let mk_storage x = Ast.StorageTag x
429let mk_inc_file x = Ast.IncFileTag x
430let mk_statement x = Ast.StatementTag (Ast0toast.statement x)
431let mk_case_line x = Ast.CaseLineTag (Ast0toast.case_line x)
432let mk_const_vol x = Ast.ConstVolTag x
433let mk_token x info = Ast.Token (x,Some info)
434let mk_meta (_,x) info = Ast.Token (x,Some info)
435let mk_code x = Ast.Code (Ast0toast.top_level x)
436
437let mk_exprdots x = Ast.ExprDotsTag (Ast0toast.expression_dots x)
438let mk_paramdots x = Ast.ParamDotsTag (Ast0toast.parameter_list x)
439let mk_stmtdots x = Ast.StmtDotsTag (Ast0toast.statement_dots x)
440let mk_decldots x = Ast.DeclDotsTag (Ast0toast.declaration_dots x)
441let mk_casedots x = failwith "+ case lines not supported"
442let mk_typeC x = Ast.FullTypeTag (Ast0toast.typeC x)
443let mk_init x = Ast.InitTag (Ast0toast.initialiser x)
444let mk_param x = Ast.ParamTag (Ast0toast.parameterTypeDef x)
445
446let collect_plus_nodes root =
447 let root_index = Ast0.get_index root in
448
449 let bind x y = x @ y in
450 let option_default = [] in
451
452 let mcode fn (term,_,info,mcodekind,_) =
453 match mcodekind with Ast0.PLUS -> [(info,fn term)] | _ -> [] in
454
455 let imcode fn (term,_,info,mcodekind,_) =
456 match mcodekind with
457 Ast0.PLUS -> [(info,fn term (Ast0toast.convert_info info))]
458 | _ -> [] in
459
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)]
464 | _ -> k e in
465
466 (* case for everything that is just a wrapper for a simpler thing *)
467 let stmt r k e =
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
1be43e12 472 | Ast0.TopInit(init) -> r.V0.combiner_initialiser init
34e49164
C
473 | Ast0.Decl(_,decl) -> r.V0.combiner_declaration decl
474 | _ -> do_nothing mk_statement r k e in
475
476 (* statementTag is preferred, because it indicates that one statement is
477 replaced by one statement, in single_statement *)
478 let stmt_dots r k e =
479 match Ast0.unwrap e with
480 Ast0.DOTS([s]) | Ast0.CIRCLES([s]) | Ast0.STARS([s]) ->
481 r.V0.combiner_statement s
482 | _ -> do_nothing mk_stmtdots r k e in
483
484 let toplevel r k e =
485 match Ast0.unwrap e with
486 Ast0.DECL(s) -> r.V0.combiner_statement s
487 | Ast0.CODE(sdots) -> r.V0.combiner_statement_dots sdots
488 | _ -> do_nothing mk_code r k e in
489
490 let initdots r k e = k e in
491
492 V0.combiner bind option_default
493 (imcode mk_meta) (imcode mk_token) (mcode mk_constant) (mcode mk_assignOp)
494 (mcode mk_fixOp)
495 (mcode mk_unaryOp) (mcode mk_binaryOp) (mcode mk_const_vol)
496 (mcode mk_baseType) (mcode mk_sign) (mcode mk_structUnion)
497 (mcode mk_storage) (mcode mk_inc_file)
498 (do_nothing mk_exprdots) initdots
499 (do_nothing mk_paramdots) stmt_dots (do_nothing mk_decldots)
500 (do_nothing mk_casedots)
501 (do_nothing mk_ident) (do_nothing mk_expression)
502 (do_nothing mk_typeC) (do_nothing mk_init) (do_nothing mk_param)
503 (do_nothing mk_declaration)
504 stmt (do_nothing mk_case_line) toplevel
505
506let call_collect_plus context_nodes :
507 (int * (Ast0.info * Ast.anything) list) list =
508 List.map
509 (function e ->
510 match e with
511 Ast0.DotsExprTag(e) ->
512 (Ast0.get_index e,
513 (collect_plus_nodes e).V0.combiner_expression_dots e)
514 | Ast0.DotsInitTag(e) ->
515 (Ast0.get_index e,
516 (collect_plus_nodes e).V0.combiner_initialiser_list e)
517 | Ast0.DotsParamTag(e) ->
518 (Ast0.get_index e,
519 (collect_plus_nodes e).V0.combiner_parameter_list e)
520 | Ast0.DotsStmtTag(e) ->
521 (Ast0.get_index e,
522 (collect_plus_nodes e).V0.combiner_statement_dots e)
523 | Ast0.DotsDeclTag(e) ->
524 (Ast0.get_index e,
525 (collect_plus_nodes e).V0.combiner_declaration_dots e)
526 | Ast0.DotsCaseTag(e) ->
527 (Ast0.get_index e,
528 (collect_plus_nodes e).V0.combiner_case_line_dots e)
529 | Ast0.IdentTag(e) ->
530 (Ast0.get_index e,
531 (collect_plus_nodes e).V0.combiner_ident e)
532 | Ast0.ExprTag(e) ->
533 (Ast0.get_index e,
534 (collect_plus_nodes e).V0.combiner_expression e)
535 | Ast0.ArgExprTag(_) | Ast0.TestExprTag(_) ->
536 failwith "not possible - iso only"
537 | Ast0.TypeCTag(e) ->
538 (Ast0.get_index e,
539 (collect_plus_nodes e).V0.combiner_typeC e)
540 | Ast0.InitTag(e) ->
541 (Ast0.get_index e,
542 (collect_plus_nodes e).V0.combiner_initialiser e)
543 | Ast0.ParamTag(e) ->
544 (Ast0.get_index e,
545 (collect_plus_nodes e).V0.combiner_parameter e)
546 | Ast0.DeclTag(e) ->
547 (Ast0.get_index e,
548 (collect_plus_nodes e).V0.combiner_declaration e)
549 | Ast0.StmtTag(e) ->
550 (Ast0.get_index e,
551 (collect_plus_nodes e).V0.combiner_statement e)
552 | Ast0.CaseLineTag(e) ->
553 (Ast0.get_index e,
554 (collect_plus_nodes e).V0.combiner_case_line e)
555 | Ast0.TopTag(e) ->
556 (Ast0.get_index e,
557 (collect_plus_nodes e).V0.combiner_top_level e)
558 | Ast0.IsoWhenTag(_) -> failwith "only within iso phase"
1be43e12
C
559 | Ast0.IsoWhenTTag(_) -> failwith "only within iso phase"
560 | Ast0.IsoWhenFTag(_) -> failwith "only within iso phase"
34e49164
C
561 | Ast0.MetaPosTag(p) -> failwith "metapostag only within iso phase")
562 context_nodes
563
564(* The plus fragments are converted to a list of lists of lists.
565Innermost list: Elements have type anything. For any pair of successive
566elements, n and n+1, the ending line of n is the same as the starting line
567of n+1.
568Middle lists: For any pair of successive elements, n and n+1, the ending
569line of n is one less than the starting line of n+1.
570Outer list: For any pair of successive elements, n and n+1, the ending
571line of n is more than one less than the starting line of n+1. *)
572
573let logstart info = info.Ast0.logical_start
574let logend info = info.Ast0.logical_end
575
576let redo info start finish =
577 {{info with Ast0.logical_start = start} with Ast0.logical_end = finish}
578
579let rec find_neighbors (index,l) :
580 int * (Ast0.info * (Ast.anything list list)) list =
581 let rec loop = function
582 [] -> []
583 | (i,x)::rest ->
584 (match loop rest with
585 ((i1,(x1::rest_inner))::rest_middle)::rest_outer ->
586 let finish1 = logend i in
587 let start2 = logstart i1 in
588 if finish1 = start2
589 then
590 ((redo i (logstart i) (logend i1),(x::x1::rest_inner))
591 ::rest_middle)
592 ::rest_outer
593 else if finish1 + 1 = start2
594 then ((i,[x])::(i1,(x1::rest_inner))::rest_middle)::rest_outer
595 else [(i,[x])]::((i1,(x1::rest_inner))::rest_middle)::rest_outer
596 | _ -> [[(i,[x])]]) (* rest must be [] *) in
597 let res =
598 List.map
599 (function l ->
600 let (start_info,_) = List.hd l in
601 let (end_info,_) = List.hd (List.rev l) in
602 (redo start_info (logstart start_info) (logend end_info),
603 List.map (function (_,x) -> x) l))
604 (loop l) in
605 (index,res)
606
607let process_plus plus :
608 (int * (Ast0.info * Ast.anything list list) list) list =
609 List.concat
610 (List.map
611 (function x ->
612 List.map find_neighbors (call_collect_plus (collect_context x)))
613 plus)
614
615(* --------------------------------------------------------------------- *)
616(* --------------------------------------------------------------------- *)
617(* merge *)
618(*
619let merge_one = function
620 (m1::m2::minus_info,p::plus_info) ->
621 if p < m1, then
622 attach p to the beginning of m1.bef if m1 is Good, fail if it is bad
623 if p > m1 && p < m2, then consider the following possibilities, in order
624 m1 is Good and favored: attach to the beginning of m1.aft
625 m2 is Good and favored: attach to the beginning of m2.bef; drop m1
626 m1 is Good and unfavored: attach to the beginning of m1.aft
627 m2 is Good and unfavored: attach to the beginning of m2.bef; drop m1
628 also flip m1.bef if the first where > m1
629 if we drop m1, then flip m1.aft first
630 if p > m2
631 m2 is Good and favored: attach to the beginning of m2.aft; drop m1
632*)
633
634(* end of first argument < start/end of second argument *)
635let less_than_start info1 info2 =
636 info1.Ast0.logical_end < info2.Ast0.logical_start
637let less_than_end info1 info2 =
638 info1.Ast0.logical_end < info2.Ast0.logical_end
639let greater_than_end info1 info2 =
640 info1.Ast0.logical_start > info2.Ast0.logical_end
641let good_start info = info.Ast0.attachable_start
642let good_end info = info.Ast0.attachable_end
643
644let toplevel = function Toplevel -> true | Favored | Unfavored | Decl -> false
645let decl = function Decl -> true | Favored | Unfavored | Toplevel -> false
646let favored = function Favored -> true | Unfavored | Toplevel | Decl -> false
647
648let top_code =
649 List.for_all (List.for_all (function Ast.Code _ -> true | _ -> false))
650
651(* The following is probably not correct. The idea is to detect what
652should be placed completely before the declaration. So type/storage
653related things do not fall into this category, and complete statements do
654fall into this category. But perhaps other things should be in this
655category as well, such as { or ;? *)
656let predecl_code =
657 let tester = function
658 (* the following should definitely be true *)
659 Ast.DeclarationTag _
660 | Ast.StatementTag _
661 | Ast.Rule_elemTag _
662 | Ast.StmtDotsTag _
663 | Ast.Code _ -> true
664 (* the following should definitely be false *)
665 | Ast.FullTypeTag _ | Ast.BaseTypeTag _ | Ast.StructUnionTag _
666 | Ast.SignTag _
667 | Ast.StorageTag _ | Ast.ConstVolTag _ | Ast.TypeCTag _ -> false
668 (* not sure about the rest *)
669 | _ -> false in
670 List.for_all (List.for_all tester)
671
672let pr = Printf.sprintf
673
674let insert thing thinginfo into intoinfo =
675 let get_last l = let l = List.rev l in (List.rev(List.tl l),List.hd l) in
676 let get_first l = (List.hd l,List.tl l) in
677 let thing_start = thinginfo.Ast0.logical_start in
678 let thing_end = thinginfo.Ast0.logical_end in
679 let thing_offset = thinginfo.Ast0.offset in
680 let into_start = intoinfo.Ast0.tline_start in
681 let into_end = intoinfo.Ast0.tline_end in
682 let into_left_offset = intoinfo.Ast0.left_offset in
683 let into_right_offset = intoinfo.Ast0.right_offset in
34e49164
C
684 if thing_end < into_start && thing_start < into_start
685 then (thing@into,
686 {{intoinfo with Ast0.tline_start = thing_start}
687 with Ast0.left_offset = thing_offset})
688 else if thing_end = into_start && thing_offset < into_left_offset
689 then
690 let (prev,last) = get_last thing in
691 let (first,rest) = get_first into in
692 (prev@[last@first]@rest,
693 {{intoinfo with Ast0.tline_start = thing_start}
694 with Ast0.left_offset = thing_offset})
695 else if thing_start > into_end && thing_end > into_end
696 then (into@thing,
697 {{intoinfo with Ast0.tline_end = thing_end}
698 with Ast0.right_offset = thing_offset})
699 else if thing_start = into_end && thing_offset > into_right_offset
700 then
701 let (first,rest) = get_first thing in
702 let (prev,last) = get_last into in
703 (prev@[last@first]@rest,
704 {{intoinfo with Ast0.tline_end = thing_end}
705 with Ast0.right_offset = thing_offset})
706 else
707 begin
708 Printf.printf "thing start %d thing end %d into start %d into end %d\n"
709 thing_start thing_end into_start into_end;
710 Printf.printf "thing offset %d left offset %d right offset %d\n"
711 thing_offset into_left_offset into_right_offset;
712 Pretty_print_cocci.print_anything "" thing;
1be43e12 713 Pretty_print_cocci.print_anything "" into;
34e49164
C
714 failwith "can't figure out where to put the + code"
715 end
716
717let init thing info =
718 (thing,
719 {Ast0.tline_start = info.Ast0.logical_start;
720 Ast0.tline_end = info.Ast0.logical_end;
721 Ast0.left_offset = info.Ast0.offset;
722 Ast0.right_offset = info.Ast0.offset})
723
724let attachbefore (infop,p) = function
725 Ast0.MINUS(replacements) ->
726 (match !replacements with
727 ([],ti) -> replacements := init p infop
728 | (repl,ti) -> replacements := insert p infop repl ti)
729 | Ast0.CONTEXT(neighbors) ->
730 let (repl,ti1,ti2) = !neighbors in
731 (match repl with
732 Ast.BEFORE(bef) ->
733 let (bef,ti1) = insert p infop bef ti1 in
734 neighbors := (Ast.BEFORE(bef),ti1,ti2)
735 | Ast.AFTER(aft) ->
736 let (bef,ti1) = init p infop in
737 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
738 | Ast.BEFOREAFTER(bef,aft) ->
739 let (bef,ti1) = insert p infop bef ti1 in
740 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
741 | Ast.NOTHING ->
742 let (bef,ti1) = init p infop in
743 neighbors := (Ast.BEFORE(bef),ti1,ti2))
744 | _ -> failwith "not possible for attachbefore"
745
746let attachafter (infop,p) = function
747 Ast0.MINUS(replacements) ->
748 (match !replacements with
749 ([],ti) -> replacements := init p infop
750 | (repl,ti) -> replacements := insert p infop repl ti)
751 | Ast0.CONTEXT(neighbors) ->
752 let (repl,ti1,ti2) = !neighbors in
753 (match repl with
754 Ast.BEFORE(bef) ->
755 let (aft,ti2) = init p infop in
756 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
757 | Ast.AFTER(aft) ->
758 let (aft,ti2) = insert p infop aft ti2 in
759 neighbors := (Ast.AFTER(aft),ti1,ti2)
760 | Ast.BEFOREAFTER(bef,aft) ->
761 let (aft,ti2) = insert p infop aft ti2 in
762 neighbors := (Ast.BEFOREAFTER(bef,aft),ti1,ti2)
763 | Ast.NOTHING ->
764 let (aft,ti2) = init p infop in
765 neighbors := (Ast.AFTER(aft),ti1,ti2))
766 | _ -> failwith "not possible for attachbefore"
767
768let attach_all_before ps m =
769 List.iter (function x -> attachbefore x m) ps
770
771let attach_all_after ps m =
772 List.iter (function x -> attachafter x m) ps
773
774let split_at_end info ps =
775 let split_point = info.Ast0.logical_end in
776 List.partition
777 (function (info,_) -> info.Ast0.logical_end < split_point)
778 ps
779
780let allminus = function
781 Ast0.MINUS(_) -> true
782 | _ -> false
783
784let rec before_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
785 [] -> ()
786 | (((infop,_) as p) :: ps) as all ->
787 if less_than_start infop infom1 or
788 (allminus m1 && less_than_end infop infom1) (* account for trees *)
789 then
790 if good_start infom1
791 then (attachbefore p m1; before_m1 x1 x2 rest ps)
792 else
793 failwith
794 (pr "%d: no available token to attach to" infop.Ast0.line_start)
795 else after_m1 x1 x2 rest all
796
797and after_m1 ((f1,infom1,m1) as x1) ((f2,infom2,m2) as x2) rest = function
798 [] -> ()
799 | (((infop,pcode) as p) :: ps) as all ->
800 (* if the following is false, then some + code is stuck in the middle
801 of some context code (m1). could drop down to the token level.
802 this might require adjustments in ast0toast as well, when + code on
803 expressions is dropped down to + code on expressions. it might
804 also break some invariants on which iso depends, particularly on
805 what it can infer from something being CONTEXT with no top-level
806 modifications. for the moment, we thus give an error, asking the
807 user to rewrite the semantic patch. *)
808 if greater_than_end infop infom1
809 then
810 if less_than_start infop infom2
811 then
812 if predecl_code pcode && good_end infom1 && decl f1
813 then (attachafter p m1; after_m1 x1 x2 rest ps)
814 else if predecl_code pcode && good_start infom2 && decl f2
815 then before_m2 x2 rest all
816 else if top_code pcode && good_end infom1 && toplevel f1
817 then (attachafter p m1; after_m1 x1 x2 rest ps)
818 else if top_code pcode && good_start infom2 && toplevel f2
819 then before_m2 x2 rest all
820 else if good_end infom1 && favored f1
821 then (attachafter p m1; after_m1 x1 x2 rest ps)
822 else if good_start infom2 && favored f2
823 then before_m2 x2 rest all
824 else if good_end infom1
825 then (attachafter p m1; after_m1 x1 x2 rest ps)
826 else if good_start infom2
827 then before_m2 x2 rest all
828 else
829 failwith
830 (pr "%d: no available token to attach to" infop.Ast0.line_start)
831 else after_m2 x2 rest all
832 else
833 begin
834 Printf.printf "between: p start %d p end %d m1 start %d m1 end %d m2 start %d m2 end %d\n"
835 infop.Ast0.line_start infop.Ast0.line_end
836 infom1.Ast0.line_start infom1.Ast0.line_end
837 infom2.Ast0.line_start infom2.Ast0.line_end;
838 Pretty_print_cocci.print_anything "" pcode;
839 failwith
840 "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."
841 end
842
843and before_m2 ((f2,infom2,m2) as x2) rest
844 (p : (Ast0.info * Ast.anything list list) list) =
845 match (rest,p) with
846 (_,[]) -> ()
847 | ([],((infop,_)::_)) ->
848 let (bef_m2,aft_m2) = split_at_end infom2 p in (* bef_m2 isn't empty *)
849 if good_start infom2
850 then (attach_all_before bef_m2 m2; after_m2 x2 rest aft_m2)
851 else
852 failwith
853 (pr "%d: no available token to attach to" infop.Ast0.line_start)
854 | (m::ms,_) -> before_m1 x2 m ms p
855
856and after_m2 ((f2,infom2,m2) as x2) rest
857 (p : (Ast0.info * Ast.anything list list) list) =
858 match (rest,p) with
859 (_,[]) -> ()
860 | ([],((infop,_)::_)) ->
861 if good_end infom2
862 then attach_all_after p m2
863 else
864 failwith
865 (pr "%d: no available token to attach to" infop.Ast0.line_start)
866 | (m::ms,_) -> after_m1 x2 m ms p
867
868let merge_one : (minus_join_point * Ast0.info * 'a) list *
869 (Ast0.info * Ast.anything list list) list -> unit = function (m,p) ->
870 (*
871 Printf.printf "minus code\n";
872 List.iter
873 (function (_,info,_) ->
874 Printf.printf "start %d end %d real_start %d real_end %d\n"
875 info.Ast0.logical_start info.Ast0.logical_end
876 info.Ast0.line_start info.Ast0.line_end)
877 m;
878 Printf.printf "plus code\n";
879 List.iter
880 (function (info,p) ->
881 Printf.printf "start %d end %d real_start %d real_end %d\n"
882 info.Ast0.logical_start info.Ast0.logical_end
883 info.Ast0.line_end info.Ast0.line_end;
884 Pretty_print_cocci.print_anything "" p;
885 Format.print_newline())
886 p;
887 *)
888 match (m,p) with
889 (_,[]) -> ()
890 | (m1::m2::restm,p) -> before_m1 m1 m2 restm p
891 | ([m],p) -> before_m2 m [] p
892 | ([],_) -> failwith "minus tree ran out before the plus tree"
893
894let merge minus_list plus_list =
895 (*
896 Printf.printf "minus list %s\n"
897 (String.concat " "
898 (List.map (function (x,_) -> string_of_int x) minus_list));
899 Printf.printf "plus list %s\n"
900 (String.concat " "
901 (List.map (function (x,_) -> string_of_int x) plus_list));
902 *)
903 List.iter
904 (function (index,minus_info) ->
905 let plus_info = List.assoc index plus_list in
906 merge_one (minus_info,plus_info))
907 minus_list
908
909(* --------------------------------------------------------------------- *)
910(* --------------------------------------------------------------------- *)
911(* Need to check that CONTEXT nodes have nothing attached to their tokens.
912If they do, they become MIXED *)
913
914let reevaluate_contextness =
915 let bind = (@) in
916 let option_default = [] in
917
918 let mcode (_,_,_,mc,_) =
919 match mc with
920 Ast0.CONTEXT(mc) -> let (ba,_,_) = !mc in [ba]
921 | _ -> [] in
922
923 let donothing r k e =
924 match Ast0.get_mcodekind e with
925 Ast0.CONTEXT(mc) ->
926 if List.exists (function Ast.NOTHING -> false | _ -> true) (k e)
927 then Ast0.set_mcodekind e (Ast0.MIXED(mc));
928 []
929 | _ -> let _ = k e in [] in
930
931 let res =
932 V0.combiner bind option_default
933 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
934 mcode
935 donothing donothing donothing donothing donothing donothing donothing
936 donothing
937 donothing donothing donothing donothing donothing donothing donothing in
938 res.V0.combiner_top_level
939
940(* --------------------------------------------------------------------- *)
941(* --------------------------------------------------------------------- *)
942
943let insert_plus minus plus =
944 let minus_stream = process_minus minus in
945 let plus_stream = process_plus plus in
946 merge minus_stream plus_stream;
947 List.iter (function x -> let _ = reevaluate_contextness x in ()) minus