Release coccinelle-0.1.2
[bpt/coccinelle.git] / engine / transformation_c.ml
CommitLineData
485bce71
C
1(* Copyright (C) 2006, 2007 Yoann Padioleau
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License (GPL)
5 * version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * file license.txt for more details.
11 *
12 * This file was part of Coccinelle.
13 *)
34e49164
C
14open Common
15
16module F = Control_flow_c
17
485bce71 18module Flag = Flag_matcher
34e49164
C
19(*****************************************************************************)
20(* The functor argument *)
21(*****************************************************************************)
22
23(* info passed recursively in monad in addition to binding *)
24type xinfo = {
25 optional_storage_iso : bool;
26 optional_qualifier_iso : bool;
27 value_format_iso : bool;
28 current_rule_name : string; (* used for errors *)
29}
30
31module XTRANS = struct
32
33 (* ------------------------------------------------------------------------*)
34 (* Combinators history *)
35 (* ------------------------------------------------------------------------*)
36 (*
37 * version0:
38 * type ('a, 'b) transformer =
39 * 'a -> 'b -> Lib_engine.metavars_binding -> 'b
40 * exception NoMatch
41 *
42 * version1:
43 * type ('a, 'b) transformer =
44 * 'a -> 'b -> Lib_engine.metavars_binding -> 'b option
45 * use an exception monad
46 *
47 * version2:
48 * type tin = Lib_engine.metavars_binding
49 *)
50
51 (* ------------------------------------------------------------------------*)
52 (* Standard type and operators *)
53 (* ------------------------------------------------------------------------*)
54
55 type tin = {
56 extra: xinfo;
57 binding: Lib_engine.metavars_binding;
1be43e12 58 binding0: Lib_engine.metavars_binding; (* inherited variable *)
34e49164
C
59 }
60 type 'x tout = 'x option
61
62 type ('a, 'b) matcher = 'a -> 'b -> tin -> ('a * 'b) tout
63
64 let (>>=) m f = fun tin ->
65 match m tin with
66 | None -> None
67 | Some (a,b) -> f a b tin
68
69 let return = fun x -> fun tin ->
70 Some x
71
72 (* can have fail in transform now that the process is deterministic ? *)
73 let fail = fun tin ->
74 None
75
76 let (>||>) m1 m2 = fun tin ->
77 match m1 tin with
78 | None -> m2 tin
79 | Some x -> Some x (* stop as soon as have found something *)
80
81 let (>|+|>) m1 m2 = m1 >||> m2
82
83 let (>&&>) f m = fun tin ->
84 if f tin then m tin else fail tin
85
86 let optional_storage_flag f = fun tin ->
87 f (tin.extra.optional_storage_iso) tin
88
89 let optional_qualifier_flag f = fun tin ->
90 f (tin.extra.optional_qualifier_iso) tin
91
92 let value_format_flag f = fun tin ->
93 f (tin.extra.value_format_iso) tin
94
485bce71 95 let mode = Cocci_vs_c.TransformMode
34e49164
C
96
97 (* ------------------------------------------------------------------------*)
98 (* Exp *)
99 (* ------------------------------------------------------------------------*)
100 let cocciExp = fun expf expa node -> fun tin ->
101
102 let bigf = {
103 Visitor_c.default_visitor_c_s with
104 Visitor_c.kexpr_s = (fun (k, bigf) expb ->
105 match expf expa expb tin with
106 | None -> (* failed *) k expb
107 | Some (x, expb) -> expb);
108 }
109 in
110 Some (expa, Visitor_c.vk_node_s bigf node)
111
112
113 (* same as cocciExp, but for expressions in an expression, not expressions
114 in a node *)
115 let cocciExpExp = fun expf expa expb -> fun tin ->
116
117 let bigf = {
118 Visitor_c.default_visitor_c_s with
119 Visitor_c.kexpr_s = (fun (k, bigf) expb ->
120 match expf expa expb tin with
121 | None -> (* failed *) k expb
122 | Some (x, expb) -> expb);
123 }
124 in
125 Some (expa, Visitor_c.vk_expr_s bigf expb)
126
127
128 let cocciTy = fun expf expa node -> fun tin ->
129
130 let bigf = {
131 Visitor_c.default_visitor_c_s with
132 Visitor_c.ktype_s = (fun (k, bigf) expb ->
133 match expf expa expb tin with
134 | None -> (* failed *) k expb
135 | Some (x, expb) -> expb);
136 }
137 in
138 Some (expa, Visitor_c.vk_node_s bigf node)
139
1be43e12
C
140 let cocciInit = fun expf expa node -> fun tin ->
141
142 let bigf = {
143 Visitor_c.default_visitor_c_s with
144 Visitor_c.kini_s = (fun (k, bigf) expb ->
145 match expf expa expb tin with
146 | None -> (* failed *) k expb
147 | Some (x, expb) -> expb);
148 }
149 in
150 Some (expa, Visitor_c.vk_node_s bigf node)
151
34e49164
C
152
153 (* ------------------------------------------------------------------------*)
154 (* Tokens *)
155 (* ------------------------------------------------------------------------*)
156 let check_pos info mck pos =
157 match mck with
158 | Ast_cocci.PLUS -> raise Impossible
159 | Ast_cocci.CONTEXT (Ast_cocci.FixPos (i1,i2),_)
160 | Ast_cocci.MINUS (Ast_cocci.FixPos (i1,i2),_) ->
161 pos <= i2 && pos >= i1
162 | Ast_cocci.CONTEXT (Ast_cocci.DontCarePos,_)
163 | Ast_cocci.MINUS (Ast_cocci.DontCarePos,_) ->
164 true
165 | _ ->
166 match info with
167 Some info ->
168 failwith
169 (Printf.sprintf
170 "wierd: dont have position info for the mcodekind in line %d column %d"
171 info.Ast_cocci.line info.Ast_cocci.column)
172 | None ->
173 failwith "wierd: dont have position info for the mcodekind"
174
175
176 let tag_with_mck mck ib = fun tin ->
177
178 let cocciinforef = ib.Ast_c.cocci_tag in
179 let (oldmcode, oldenv) = !cocciinforef in
180
181 let mck =
485bce71 182 (* coccionly:
34e49164
C
183 if !Flag_parsing_cocci.sgrep_mode
184 then Sgrep.process_sgrep ib mck
485bce71
C
185 else
186 *)
187 mck
34e49164
C
188 in
189 (match mck, Ast_c.pinfo_of_info ib with
190 | _, Ast_c.AbstractLineTok _ -> raise Impossible
191 | Ast_cocci.MINUS(_), Ast_c.ExpandedTok _ ->
192 failwith ("try to delete an expanded token: " ^ (Ast_c.str_of_info ib))
193 | _ -> ()
194 );
195
196 match (oldmcode,mck) with
197 | (Ast_cocci.CONTEXT(_,Ast_cocci.NOTHING), _)
198 | (_, Ast_cocci.CONTEXT(_,Ast_cocci.NOTHING))
199 ->
200 cocciinforef := (mck, tin.binding);
201 ib
202
203 | _ ->
204 if (oldmcode, oldenv) = (mck, tin.binding)
205 then begin
206 if !Flag.show_misc
207 then pr2 "already tagged but with same mcode, so safe";
208 ib
209 end
210 else
485bce71 211 (* coccionly:
34e49164
C
212 if !Flag.sgrep_mode2
213 then ib (* safe *)
214 else
485bce71
C
215 *)
216 begin
217 (* coccionly:
34e49164
C
218 Format.set_formatter_out_channel stderr;
219 Common.pr2 "SP mcode ";
220 Pretty_print_cocci.print_mcodekind oldmcode;
221 Format.print_newline();
222 Common.pr2 "C code mcode ";
223 Pretty_print_cocci.print_mcodekind mck;
224 Format.print_newline();
225 Format.print_flush();
485bce71 226 *)
34e49164
C
227 failwith
228 (Common.sprintf "%s: already tagged token:\n%s"
229 tin.extra.current_rule_name
230 (Common.error_message (Ast_c.file_of_info ib)
231 (Ast_c.str_of_info ib, Ast_c.opos_of_info ib)))
232 end
233
234 let tokenf ia ib = fun tin ->
235 let (_,i,mck,_) = ia in
236 let pos = Ast_c.info_to_fixpos ib in
237 if check_pos (Some i) mck pos
238 then return (ia, tag_with_mck mck ib tin) tin
239 else fail tin
240
241 let tokenf_mck mck ib = fun tin ->
242 let pos = Ast_c.info_to_fixpos ib in
243 if check_pos None mck pos
244 then return (mck, tag_with_mck mck ib tin) tin
245 else fail tin
246
247
248 (* ------------------------------------------------------------------------*)
249 (* Distribute mcode *)
250 (* ------------------------------------------------------------------------*)
251
252 (* When in the SP we attach something to a metavariable, or delete it, as in
253 * - S
254 * + foo();
255 * we have to minusize all the token that compose S in the C code, and
256 * attach the 'foo();' to the right token, the one at the very right.
257 *)
258
259 type 'a distributer =
260 (Ast_c.info -> Ast_c.info) * (* what to do on left *)
261 (Ast_c.info -> Ast_c.info) * (* what to do on middle *)
262 (Ast_c.info -> Ast_c.info) * (* what to do on right *)
263 (Ast_c.info -> Ast_c.info) -> (* what to do on both *)
264 'a -> 'a
265
266 let distribute_mck mcodekind distributef expr tin =
267 match mcodekind with
268 | Ast_cocci.MINUS (pos,any_xxs) ->
269 distributef (
270 (fun ib -> tag_with_mck (Ast_cocci.MINUS (pos,any_xxs)) ib tin),
271 (fun ib -> tag_with_mck (Ast_cocci.MINUS (pos,[])) ib tin),
272 (fun ib -> tag_with_mck (Ast_cocci.MINUS (pos,[])) ib tin),
273 (fun ib -> tag_with_mck (Ast_cocci.MINUS (pos,any_xxs)) ib tin)
274 ) expr
275 | Ast_cocci.CONTEXT (pos,any_befaft) ->
276 (match any_befaft with
277 | Ast_cocci.NOTHING -> expr
278
279 | Ast_cocci.BEFORE xxs ->
280 distributef (
281 (fun ib -> tag_with_mck
282 (Ast_cocci.CONTEXT (pos,Ast_cocci.BEFORE xxs)) ib tin),
283 (fun x -> x),
284 (fun x -> x),
285 (fun ib -> tag_with_mck
286 (Ast_cocci.CONTEXT (pos,Ast_cocci.BEFORE xxs)) ib tin)
287 ) expr
288 | Ast_cocci.AFTER xxs ->
289 distributef (
290 (fun x -> x),
291 (fun x -> x),
292 (fun ib -> tag_with_mck
293 (Ast_cocci.CONTEXT (pos,Ast_cocci.AFTER xxs)) ib tin),
294 (fun ib -> tag_with_mck
295 (Ast_cocci.CONTEXT (pos,Ast_cocci.AFTER xxs)) ib tin)
296 ) expr
297
298 | Ast_cocci.BEFOREAFTER (xxs, yys) ->
299 distributef (
300 (fun ib -> tag_with_mck
301 (Ast_cocci.CONTEXT (pos,Ast_cocci.BEFORE xxs)) ib tin),
302 (fun x -> x),
303 (fun ib -> tag_with_mck
304 (Ast_cocci.CONTEXT (pos,Ast_cocci.AFTER yys)) ib tin),
305 (fun ib -> tag_with_mck
306 (Ast_cocci.CONTEXT (pos,Ast_cocci.BEFOREAFTER (xxs,yys)))
307 ib tin)
308 ) expr
309
310 )
311 | Ast_cocci.PLUS -> raise Impossible
312
313
314 (* use new strategy, collect ii, sort, recollect and tag *)
315
316 let mk_bigf (maxpos, minpos) (lop,mop,rop,bop) =
317 let bigf = {
318 Visitor_c.default_visitor_c_s with
319 Visitor_c.kinfo_s = (fun (k,bigf) i ->
320 let pos = Ast_c.info_to_fixpos i in
321 match () with
322 | _ when Ast_cocci.equal_pos pos maxpos &&
323 Ast_cocci.equal_pos pos minpos -> bop i
324 | _ when Ast_cocci.equal_pos pos maxpos -> rop i
325 | _ when Ast_cocci.equal_pos pos minpos -> lop i
326 | _ -> mop i
327 )
328 } in
329 bigf
330
331 let distribute_mck_expr (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
332 Visitor_c.vk_expr_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
333
334 let distribute_mck_args (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
335 Visitor_c.vk_args_splitted_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
336
337 let distribute_mck_type (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
338 Visitor_c.vk_type_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
339
340 let distribute_mck_ini (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
341 Visitor_c.vk_ini_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
342
343 let distribute_mck_param (maxpos, minpos) = fun (lop,mop,rop,bop) -> fun x ->
344 Visitor_c.vk_param_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop)) x
345
346 let distribute_mck_params (maxpos, minpos) = fun (lop,mop,rop,bop) ->fun x ->
347 Visitor_c.vk_params_splitted_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
348 x
349
350 let distribute_mck_node (maxpos, minpos) = fun (lop,mop,rop,bop) ->fun x ->
351 Visitor_c.vk_node_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
352 x
353
354 let distribute_mck_struct_fields (maxpos, minpos) =
355 fun (lop,mop,rop,bop) ->fun x ->
356 Visitor_c.vk_struct_fields_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
357 x
358
359 let distribute_mck_cst (maxpos, minpos) =
360 fun (lop,mop,rop,bop) ->fun x ->
361 Visitor_c.vk_cst_s (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
362 x
363
364
365 let distribute_mck_define_params (maxpos, minpos) = fun (lop,mop,rop,bop) ->
366 fun x ->
367 Visitor_c.vk_define_params_splitted_s
368 (mk_bigf (maxpos, minpos) (lop,mop,rop,bop))
369 x
370
371 let get_pos mck =
372 match mck with
373 | Ast_cocci.PLUS -> raise Impossible
374 | Ast_cocci.CONTEXT (Ast_cocci.FixPos (i1,i2),_)
375 | Ast_cocci.MINUS (Ast_cocci.FixPos (i1,i2),_) ->
376 Ast_cocci.FixPos (i1,i2)
377 | Ast_cocci.CONTEXT (Ast_cocci.DontCarePos,_)
378 | Ast_cocci.MINUS (Ast_cocci.DontCarePos,_) ->
379 Ast_cocci.DontCarePos
380 | _ -> failwith "wierd: dont have position info for the mcodekind"
381
382 let distrf (ii_of_x_f, distribute_mck_x_f) =
383 fun ia x -> fun tin ->
384 let mck = Ast_cocci.get_mcodekind ia in
385 let (max, min) = Lib_parsing_c.max_min_by_pos (ii_of_x_f x)
386 in
387 if
388 (* bug: check_pos mck max && check_pos mck min
389 *
390 * if do that then if have - f(...); and in C f(1,2); then we
391 * would get a "already tagged" because the '...' would sucess in
392 * transformaing both '1' and '1,2'. So being in the range is not
393 * enough. We must be equal exactly to the range!
394 *)
395 (match get_pos mck with
396 | Ast_cocci.DontCarePos -> true
397 | Ast_cocci.FixPos (i1, i2) ->
398 i1 = min && i2 = max
399 | _ -> raise Impossible
400 )
401
402 then
403 return (
404 ia,
405 distribute_mck mck (distribute_mck_x_f (max,min)) x tin
406 ) tin
407 else fail tin
408
409
410 let distrf_e = distrf (Lib_parsing_c.ii_of_expr, distribute_mck_expr)
411 let distrf_args = distrf (Lib_parsing_c.ii_of_args, distribute_mck_args)
412 let distrf_type = distrf (Lib_parsing_c.ii_of_type, distribute_mck_type)
413 let distrf_param = distrf (Lib_parsing_c.ii_of_param, distribute_mck_param)
414 let distrf_params = distrf (Lib_parsing_c.ii_of_params,distribute_mck_params)
415 let distrf_ini = distrf (Lib_parsing_c.ii_of_ini,distribute_mck_ini)
416 let distrf_node = distrf (Lib_parsing_c.ii_of_node,distribute_mck_node)
417 let distrf_struct_fields =
418 distrf (Lib_parsing_c.ii_of_struct_fields, distribute_mck_struct_fields)
419 let distrf_cst =
420 distrf (Lib_parsing_c.ii_of_cst, distribute_mck_cst)
421 let distrf_define_params =
422 distrf (Lib_parsing_c.ii_of_define_params,distribute_mck_define_params)
423
424
425 (* ------------------------------------------------------------------------*)
426 (* Environment *)
427 (* ------------------------------------------------------------------------*)
428 let meta_name_to_str (s1, s2) =
429 s1 ^ "." ^ s2
430
431 let envf keep _inherited = fun (s, value, _) f tin ->
432 let s = Ast_cocci.unwrap_mcode s in
433 let v =
434 if keep = Type_cocci.Saved
435 then (
436 try Some (List.assoc s tin.binding)
437 with Not_found ->
438 pr2(sprintf
439 "Don't find value for metavariable %s in the environment"
440 (meta_name_to_str s));
441 None)
442 else
443 (* not raise Impossible! *)
444 Some (value)
445 in
446 match v with
447 | None -> fail tin
448 | Some (value') ->
449
450 (* Ex: in cocci_vs_c someone wants to add a binding. Here in
451 * transformation3 the value for this var may be already in the
452 * env, because for instance its value were fixed in a previous
453 * SmPL rule. So here we want to check that this is the same value.
454 * If forget to do the check, what can happen ? Because of Exp
455 * and other disjunctive feature of cocci_vs_c (>||>), we
456 * may accept a match at a wrong position. Maybe later this
457 * will be detected via the pos system on tokens, but maybe
458 * not. So safer to keep the check.
459 *)
460
461 (*f () tin*)
485bce71 462 if Cocci_vs_c.equal_metavarval value value'
34e49164
C
463 then f () tin
464 else fail tin
465
466
467 let check_constraints matcher constraints exp = fun f tin -> f () tin
468
469 (* ------------------------------------------------------------------------*)
470 (* Environment, allbounds *)
471 (* ------------------------------------------------------------------------*)
472 let (all_bound : Ast_cocci.meta_name list -> tin -> bool) = fun l tin ->
473 true (* in transform we don't care ? *)
474
475end
476
477(*****************************************************************************)
478(* Entry point *)
479(*****************************************************************************)
485bce71 480module TRANS = Cocci_vs_c.COCCI_VS_C (XTRANS)
34e49164
C
481
482
483let transform_re_node a b tin =
484 match TRANS.rule_elem_node a b tin with
485 | None -> raise Impossible
486 | Some (_sp, b') -> b'
487
34e49164 488let (transform2: string (* rule name *) -> string list (* dropped_isos *) ->
1be43e12 489 Lib_engine.metavars_binding (* inherited bindings *) ->
34e49164 490 Lib_engine.transformation_info -> F.cflow -> F.cflow) =
1be43e12 491 fun rule_name dropped_isos binding0 xs cflow ->
34e49164
C
492
493 let extra = {
494 optional_storage_iso = not(List.mem "optional_storage" dropped_isos);
495 optional_qualifier_iso = not(List.mem "optional_qualifier" dropped_isos);
496 value_format_iso = not(List.mem "value_format" dropped_isos);
497 current_rule_name = rule_name;
498 } in
499
500 (* find the node, transform, update the node, and iter for all elements *)
501
502 xs +> List.fold_left (fun acc (nodei, binding, rule_elem) ->
503 (* subtil: not cflow#nodes but acc#nodes *)
504 let node = acc#nodes#assoc nodei in
505
506 if !Flag.show_misc
507 then pr2 "transform one node";
508
509 let tin = {
510 XTRANS.extra = extra;
1be43e12
C
511 XTRANS.binding = binding0@binding;
512 XTRANS.binding0 = []; (* not used - everything constant for trans *)
34e49164
C
513 } in
514
515 let node' = transform_re_node rule_elem node tin in
516
517 (* assert that have done something. But with metaruleElem sometimes
518 dont modify fake nodes. So special case before on Fake nodes. *)
519 (match F.unwrap node with
520 | F.Enter | F.Exit | F.ErrorExit
521 | F.EndStatement _ | F.CaseNode _
522 | F.Fake
523 | F.TrueNode | F.FalseNode | F.AfterNode | F.FallThroughNode
524 -> ()
525 | _ -> () (* assert (not (node =*= node')); *)
526 );
527
528 (* useless, we dont go back from flow to ast now *)
529 (* let node' = lastfix_comma_struct node' in *)
530
531 acc#replace_node (nodei, node');
532 acc
533 ) cflow
534
535
536
1be43e12 537let transform a b c d e =
34e49164 538 Common.profile_code "Transformation3.transform"
1be43e12 539 (fun () -> transform2 a b c d e)