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