permit multiline comments and strings in macros
[bpt/coccinelle.git] / parsing_cocci / function_prototypes.ml
CommitLineData
f537ebc4 1(*
17ba0788
C
2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
f537ebc4
C
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
d6ce1786
C
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
9 *
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
13 *
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
24 *)
25
26
feec80c3 27# 0 "./function_prototypes.ml"
34e49164
C
28module Ast0 = Ast0_cocci
29module Ast = Ast_cocci
30module V0 = Visitor_ast0
b1b2de81 31module VT0 = Visitor_ast0_types
34e49164 32
ae4735db 33type id = Id of string | Meta of Ast.meta_name
34e49164
C
34
35let rec get_name name =
36 match Ast0.unwrap name with
d3f655c6 37 Ast0.Id(nm) -> [Id(Ast0.unwrap_mcode nm)]
8babbc8f 38 | Ast0.MetaId(nm,_,_,_) | Ast0.MetaFunc(nm,_,_)
d3f655c6 39 | Ast0.MetaLocalFunc(nm,_,_) -> [Meta(Ast0.unwrap_mcode nm)]
d6ce1786 40 | Ast0.AsIdent(id1,id2) -> failwith "not supported"
d3f655c6
C
41 | Ast0.DisjId(_,id_list,_,_) -> List.concat (List.map get_name id_list)
42 | Ast0.OptIdent(id) | Ast0.UniqueIdent(id) ->
43 get_name id
34e49164
C
44
45(* --------------------------------------------------------------------- *)
46(* collect all of the functions *)
47
708f4980
C
48let brace_to_semi (_,arity,info,mcodekind,pos,adj) =
49 let info =
50 (* drop column information, so that with -smpl_spacing the semicolon
51 will come out right after the close parenthesis *)
52 {info with Ast0.pos_info = {info.Ast0.pos_info with Ast0.column = -1}} in
53 (";",Ast0.NONE,info,mcodekind,pos,adj)
34e49164
C
54
55let collect_function (stm : Ast0.statement) =
56 match Ast0.unwrap stm with
57 Ast0.FunDecl(_,fninfo,name,lp,params,rp,lbrace,body,rbrace) ->
58 let stg =
59 match
60 List.filter (function Ast0.FStorage(_) -> true | _ -> false)
61 fninfo with [Ast0.FStorage(s)] -> Some s | _ -> None in
62 let ty =
63 match
64 List.filter (function Ast0.FType(_) -> true | _ -> false)
65 fninfo with [Ast0.FType(t)] -> Some t | _ -> None in
d3f655c6
C
66 List.map
67 (function nm ->
68 (nm,stm,
69 Ast0.copywrap stm
70 (Ast0.Decl((Ast0.default_info(),Ast0.context_befaft()),
71 Ast0.copywrap stm
72 (Ast0.UnInit
73 (stg,
74 Ast0.copywrap stm
75 (Ast0.FunctionType(ty,lp,params,rp)),
76 name,brace_to_semi lbrace))))))
77 (get_name name)
34e49164
C
78 | _ -> []
79
80let collect_functions stmt_dots =
81 List.concat (List.map collect_function (Ast0.undots stmt_dots))
82
65038c61
C
83let drop_positions =
84 let mcode (term,arity,info,mc,_,adj) =
85 (term,arity,info,mc,ref [],adj) in
86 let donothing r k e = k e in
87 let res =
88 V0.flat_rebuilder
89 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
90 donothing donothing donothing donothing donothing donothing
91 donothing donothing donothing donothing donothing donothing donothing
755320b0 92 donothing donothing donothing in
65038c61
C
93 res.VT0.rebuilder_rec_statement
94
34e49164
C
95let get_all_functions rule =
96 let res =
97 match Ast0.unwrap rule with
65038c61 98 Ast0.NONDECL(stmt) -> collect_function stmt
34e49164
C
99 | Ast0.CODE(rule_elem_dots) -> collect_functions rule_elem_dots
100 | _ -> [] in
101 List.map
102 (function (nm,def,vl) ->
b1b2de81 103 (nm,
65038c61
C
104 (def,
105 drop_positions
106 ((Iso_pattern.rebuild_mcode None).VT0.rebuilder_rec_statement vl))))
34e49164
C
107 res
108
109(* --------------------------------------------------------------------- *)
110(* try to match up the functions *)
111
112(* pass through the - and + functions in lockstep, until one runs out.
113Then process the remaining minuses, if any. If we can find another
114function of the same name for either the current - or + function, take that
115one. Otherwise, align the two current ones. *)
116
117let rec align all_minus all_plus =
118 let rec loop = function
119 ([],_) -> []
120 | ((mname,(mdef,mproto))::minus,[]) ->
121 (try
122 let (_,pproto) = List.assoc mname all_plus in
123 (mname,mdef,mproto,Some pproto)::(loop (minus,[]))
124 with Not_found -> (mname,mdef,mproto,None)::(loop (minus, [])))
125 | ((mname,(mdef,mproto))::minus,(pname,(pdef,pproto))::plus) ->
126 if mname = pname
127 then (mname,mdef,mproto,Some pproto)::(loop (minus, []))
128 else
129 (try
130 let (_,pproto_for_minus) = List.assoc mname all_plus in
131 (try
132 let _ = List.assoc mname all_plus in
133 (* protos that match both *)
134 (mname,mdef,mproto,Some pproto_for_minus)::(loop (minus, plus))
135 with Not_found ->
136 (* proto that matches only minus *)
137 (mname,mdef,mproto,Some pproto_for_minus)::
138 (loop (minus, ((pname,(pdef,pproto))::plus))))
139 with Not_found ->
140 (try
141 let _ = List.assoc mname all_plus in
142 (* proto only for plus *)
143 (mname,mdef,mproto,None)::(loop (minus, plus))
144 with Not_found ->
145 (* protos for no one *)
146 (mname,mdef,mproto,Some pproto)::(loop (minus, plus)))) in
147 List.filter changed_proto (loop (all_minus, all_plus))
148
149(* --------------------------------------------------------------------- *)
150
151and strip =
152 let donothing r k e =
951c7801
C
153 {(Ast0.wrap (Ast0.unwrap (k e))) with
154 Ast0.mcodekind = ref (Ast0.PLUS Ast.ONE)} in
708f4980 155 let mcode (mc,_,_,_,_,_) =
951c7801 156 (mc,Ast0.NONE,Ast0.default_info(),Ast0.PLUS Ast.ONE,
8f657093 157 ref [],-1) in
34e49164
C
158
159 (* need a case for everything that has an unvisited component and can be in
97111a47
C
160 a function prototype. Also get rid of constraints because pcre
161 constraints cannot be compared. *)
34e49164
C
162
163 let ident r k e =
164 donothing r k
165 (Ast0.rewrap e
166 (match Ast0.unwrap e with
8babbc8f 167 Ast0.MetaId(nm,constraints,seed,pure) ->
97111a47 168 Ast0.MetaId(nm,Ast.IdNoConstraint,seed,Ast0.Pure)
34e49164 169 | Ast0.MetaFunc(nm,constraints,pure) ->
97111a47 170 Ast0.MetaFunc(nm,Ast.IdNoConstraint,Ast0.Pure)
34e49164 171 | Ast0.MetaLocalFunc(nm,constraints,pure) ->
97111a47 172 Ast0.MetaLocalFunc(nm,Ast.IdNoConstraint,Ast0.Pure)
34e49164
C
173 | e -> e)) in
174
175 let typeC r k e =
176 donothing r k
177 (Ast0.rewrap e
178 (match Ast0.unwrap e with
179 Ast0.MetaType(nm,pure) -> Ast0.MetaType(nm,Ast0.Pure)
180 | e -> e)) in
181
182 let param r k e =
183 donothing r k
184 (Ast0.rewrap e
185 (match Ast0.unwrap e with
186 Ast0.MetaParam(nm,pure) ->
187 Ast0.MetaParam(nm,Ast0.Pure)
188 | Ast0.MetaParamList(nm,lenname,pure) ->
189 Ast0.MetaParamList(nm,lenname,Ast0.Pure)
190 | e -> e)) in
191
b1b2de81 192 V0.flat_rebuilder
34e49164 193 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
34e49164 194 donothing donothing donothing donothing donothing donothing
755320b0 195 ident donothing typeC donothing param donothing donothing donothing
34e49164
C
196 donothing donothing
197
198and changed_proto = function
199 (mname,mdef,mproto,None) -> true
200 | (mname,mdef,mproto,Some pproto) ->
b1b2de81
C
201 not ((strip.VT0.rebuilder_rec_statement mproto) =
202 (strip.VT0.rebuilder_rec_statement pproto))
34e49164
C
203
204(* --------------------------------------------------------------------- *)
205(* make rules *)
206
207let rec drop_param_name p =
208 Ast0.rewrap p
209 (match Ast0.unwrap p with
210 Ast0.Param(p,_) -> Ast0.Param(p,None)
211 | Ast0.OptParam(p) -> Ast0.OptParam(drop_param_name p)
212 | Ast0.UniqueParam(p) -> Ast0.UniqueParam(p)
213 | p -> p)
214
215let drop_names dec =
b1b2de81 216 let dec = (Iso_pattern.rebuild_mcode None).VT0.rebuilder_rec_statement dec in
34e49164
C
217 match Ast0.unwrap dec with
218 Ast0.Decl(info,uninit) ->
219 (match Ast0.unwrap uninit with
220 Ast0.UnInit(stg,typ,name,sem) ->
221 (match Ast0.unwrap typ with
222 Ast0.FunctionType(ty,lp,params,rp) ->
223 let params =
224 match Ast0.unwrap params with
225 Ast0.DOTS(l) ->
226 Ast0.rewrap params (Ast0.DOTS(List.map drop_param_name l))
227 | Ast0.CIRCLES(l) ->
228 Ast0.rewrap params
229 (Ast0.CIRCLES(List.map drop_param_name l))
230 | Ast0.STARS(l) -> failwith "unexpected stars" in
231 Ast0.rewrap dec
232 (Ast0.Decl
233 (info,
234 Ast0.rewrap uninit
235 (Ast0.UnInit
236 (stg,
237 Ast0.rewrap typ
238 (Ast0.FunctionType(ty,lp,params,rp)),
239 name,sem))))
240 | _ -> failwith "function prototypes: unexpected type")
241 | _ -> failwith "unexpected declaration")
242 | _ -> failwith "unexpected term"
243
244let ct = ref 0
245
faf9a90c 246let new_name name =
34e49164
C
247 let n = !ct in
248 ct := !ct + 1;
249 name^"__"^(string_of_int n)
250
251let rec rename_param old_name all param =
252 match Ast0.unwrap param with
253 Ast0.Param(ty,Some id) when all ->
254 (match Ast0.unwrap id with
708f4980 255 Ast0.MetaId
8babbc8f 256 (((_,name),arity,info,mcodekind,pos,adj),constraints,seed,pure) ->
34e49164
C
257 let nm = ("__no_name__",new_name name) in
258 let new_id =
259 Ast0.rewrap id
260 (Ast0.MetaId
8babbc8f 261 ((nm,arity,info,mcodekind,pos,adj),constraints,seed,
ae4735db 262 Ast0.Pure)) in
34e49164
C
263 ([Ast.MetaIdDecl(Ast.NONE,nm)],
264 Ast0.rewrap param (Ast0.Param(ty,Some new_id)))
265 | _ -> ([],param))
266 | Ast0.Pdots(d) ->
267 let nm = (old_name,new_name "__P") in
268 let nml = (old_name,new_name "__n") in
269 let new_id =
270 Ast0.rewrap param
271 (Ast0.MetaParamList(Ast0.rewrap_mcode d nm,
88e71198 272 Ast0.MetaListLen (Ast0.rewrap_mcode d nml),
34e49164 273 Ast0.Pure)) in
88e71198
C
274 ([Ast.MetaParamListDecl(Ast.NONE,nm,Ast.MetaLen nml);
275 Ast.MetaListlenDecl(nml)],
34e49164
C
276 new_id)
277 | Ast0.OptParam(p) ->
278 let (metavars,p) = rename_param old_name all p in
279 (metavars,Ast0.rewrap param (Ast0.OptParam(p)))
280 | Ast0.UniqueParam(p) ->
281 let (metavars,p) = rename_param old_name all p in
282 (metavars,Ast0.rewrap param (Ast0.UniqueParam(p)))
283 | _ -> ([],param)
284
285(* try to convert names in the - parameter list to new metavariables, to
286account for spelling mistakes on the part of the programmer *)
287let fresh_names old_name mdef dec =
288 let res = ([],[],dec,mdef) in
289 match Ast0.unwrap dec with
290 Ast0.Decl(info,uninit) ->
291 (match Ast0.unwrap uninit with
292 Ast0.UnInit(stg,typ,name,sem) ->
293 (match Ast0.unwrap typ with
294 Ast0.FunctionType(ty,lp,params,rp) ->
295 let (metavars,newdec) =
296 let (metavars,l) =
297 List.split
298 (List.map (rename_param old_name true)
299 (Ast0.undots params)) in
300 (List.concat metavars,
301 Ast0.rewrap dec
302 (Ast0.Decl
303 (info,
304 Ast0.rewrap uninit
305 (Ast0.UnInit
306 (stg,
307 Ast0.rewrap typ
308 (Ast0.FunctionType
309 (ty,lp,Ast0.rewrap params (Ast0.DOTS(l)),
310 rp)),
311 name,sem))))) in
312 let (def_metavars,newdef) =
313 match Ast0.unwrap mdef with
314 Ast0.FunDecl(x,fninfo,name,lp,params,rp,lb,body,rb) ->
315 let (def_metavars,def_l) =
316 List.split
317 (List.map (rename_param old_name false)
318 (Ast0.undots params)) in
319 (List.concat def_metavars,
320 Ast0.rewrap mdef
321 (Ast0.FunDecl(x,fninfo,name,lp,
322 Ast0.rewrap params (Ast0.DOTS(def_l)),
323 rp,lb,body,rb)))
324 | _ -> failwith "unexpected function definition" in
325 (metavars,def_metavars,newdec,newdef)
326 | _ -> res)
327 | _ -> res)
328 | _ -> res
329
330(* since there is no + counterpart, the function must be completely deleted *)
331let no_names dec =
332 match Ast0.unwrap dec with
333 Ast0.Decl(info,uninit) ->
334 (match Ast0.unwrap uninit with
335 Ast0.UnInit(stg,typ,name,sem) ->
336 (match Ast0.unwrap typ with
337 Ast0.FunctionType(ty,lp,params,rp) ->
338 Ast0.rewrap dec
339 (Ast0.Decl
340 (info,
341 Ast0.rewrap uninit
342 (Ast0.UnInit
343 (stg,
344 Ast0.rewrap typ
345 (Ast0.FunctionType
346 (ty,lp,
347 Ast0.rewrap params
348 (let info = Ast0.get_info params in
349 let mcodekind =
350 (* use the mcodekind of an atomic minused
351 thing *)
352 Ast0.get_mcode_mcodekind lp in
353 let pdots =
354 ("...",Ast0.NONE,info,mcodekind,
8f657093 355 ref [],-1) in
34e49164
C
356 Ast0.DOTS
357 ([Ast0.rewrap params
358 (Ast0.Pdots(pdots))])),
359 rp)),
360 name,sem))))
361 | _ -> dec)
362 | _ -> dec)
363 | _ -> dec
364
65038c61
C
365let mkcode proto =
366 Ast0.copywrap proto (Ast0.CODE(Ast0.copywrap proto (Ast0.DOTS [proto])))
367
34e49164 368let merge mproto pproto =
65038c61
C
369 let mproto = Compute_lines.compute_lines true [mkcode mproto] in
370 let pproto = Compute_lines.compute_lines true [mkcode pproto] in
34e49164 371 let (m,p) = List.split(Context_neg.context_neg mproto pproto) in
faf9a90c 372 Insert_plus.insert_plus m p true (* no isos for protos *);
34e49164 373 (* convert to ast so that the + code will fall down to the tokens
65038c61 374 and off the artificially added Ast0.CODE *)
34e49164
C
375 let mproto = Ast0toast.ast0toast_toplevel (List.hd mproto) in
376 (* clean up the wrapping added above *)
377 match Ast.unwrap mproto with
65038c61 378 Ast.CODE mproto -> List.hd (Ast.undots mproto)
34e49164
C
379 | _ -> failwith "not possible"
380
381let make_rule rule_name = function
382 (mname,mdef,mproto,Some pproto) ->
383 let (metavars,mdef_metavars,mproto,mdef) =
384 fresh_names rule_name mdef mproto in
385 let no_name_mproto = drop_names mproto in
386 let no_name_pproto = drop_names pproto in
387 (metavars,mdef_metavars,
388 [merge mproto pproto; merge no_name_mproto no_name_pproto],mdef)
389 | (mname,mdef,mproto,None) ->
390 ([],[],[Ast0toast.statement(no_names mproto)],mdef)
391
392(* --------------------------------------------------------------------- *)
393
394let reinsert mdefs minus =
395 let table =
396 List.map
397 (function x ->
398 match Ast0.unwrap x with
399 Ast0.FunDecl(_,fninfo,name,lp,params,rp,lbrace,body,rbrace) ->
400 (name,x)
401 | _ -> failwith "bad mdef")
402 mdefs in
403 List.map
404 (function x ->
405 match Ast0.unwrap x with
65038c61 406 Ast0.NONDECL(stmt) ->
34e49164
C
407 (match Ast0.unwrap stmt with
408 Ast0.FunDecl(_,fninfo,name,lp,params,rp,lbrace,body,rbrace) ->
65038c61 409 (try Ast0.rewrap x (Ast0.NONDECL(List.assoc name table))
34e49164
C
410 with Not_found -> x)
411 | _ -> x)
412 | Ast0.CODE(rule_elem_dots) ->
413 (* let's hope there are no functions in here... *)
414 x
415 | _ -> x)
416 minus
417
418(* --------------------------------------------------------------------- *)
419(* entry point *)
420
421let rec split4 = function
422 [] -> ([],[],[],[])
423 | (a,b,c,d)::rest ->
424 let (ax,bx,cx,dx) = split4 rest in (a::ax,b::bx,c::cx,d::dx)
425
65038c61
C
426let mk_ast_code proto =
427 Ast.rewrap proto (Ast.CODE(Ast.rewrap proto (Ast.DOTS [proto])))
428
faf9a90c 429let process rule_name rule_metavars dropped_isos minus plus ruletype =
34e49164
C
430 let minus_functions = List.concat (List.map get_all_functions minus) in
431 match minus_functions with
432 [] -> ((rule_metavars,minus),None)
433 | _ ->
434 let plus_functions =
435 List.concat (List.map get_all_functions plus) in
436 let protos = align minus_functions plus_functions in
437 let (metavars,mdef_metavars,rules,mdefs) =
438 split4(List.map (make_rule rule_name) protos) in
439 let metavars = List.concat metavars in
440 let mdef_metavars = (List.concat mdef_metavars) @ rule_metavars in
441 let rules = List.concat rules in
442 let minus = reinsert mdefs minus in
443 match rules with
444 [] -> ((rule_metavars,minus),None)
445 | [x] ->
446 (* probably not possible, since there is always the version with
447 variables and the version without *)
448 ((mdef_metavars,minus),
faf9a90c 449 Some
34e49164
C
450 (metavars,
451 Ast.CocciRule
452 ("proto for "^rule_name,
453 (Ast.Dep rule_name,dropped_isos,Ast.Forall),
65038c61 454 [mk_ast_code x],
faf9a90c 455 [false],ruletype)))
34e49164
C
456 | x::_ ->
457 let drules =
458 List.map (function x -> Ast.rewrap x (Ast.DOTS [x])) rules in
459 let res =
460 Ast.CocciRule
461 ("proto for "^rule_name,
462 (Ast.Dep rule_name,dropped_isos,Ast.Forall),
65038c61 463 [mk_ast_code (Ast.rewrap x (Ast.Disj drules))],
faf9a90c 464 [false],ruletype) in
34e49164 465 ((mdef_metavars,minus),Some(metavars,res))