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 (* For minus fragment, checks that all of the identifier metavariables that
24 are used are not declared as fresh, and check that all declared variables
25 are used. For plus fragment, just check that the variables declared as
26 fresh are used. What is the issue about error variables? (don't remember) *)
28 module Ast0
= Ast0_cocci
29 module Ast
= Ast_cocci
30 module V0
= Visitor_ast0
32 (* all fresh identifiers *)
33 let fresh_table = (Hashtbl.create
(50) : ((string * string), unit) Hashtbl.t
)
35 let warning s
= Printf.fprintf stderr
"warning: %s\n" s
37 let promote name
= (name
,(),Ast0.default_info
(),(),None
)
39 (* --------------------------------------------------------------------- *)
41 let find_loop table name
=
42 let rec loop = function
44 | x
::xs
-> (try Hashtbl.find x name
with Not_found
-> loop xs
) in
47 let check_table table minus
(name
,_
,info
,_
,_
) =
48 let rl = info
.Ast0.line_start
in
51 (try (find_loop table name
) := true
55 Hashtbl.find
fresh_table name
;
56 let (_
,name
) = name
in
59 "%d: unexpected use of a fresh identifier %s" rl name
)
60 with Not_found
-> ()))
61 else (try (find_loop table name
) := true with Not_found
-> ())
63 let get_opt fn
= Common.do_option fn
65 (* --------------------------------------------------------------------- *)
69 match Ast0.unwrap d
with
70 Ast0.DOTS
(x
) -> List.iter fn x
71 | Ast0.CIRCLES
(x
) -> List.iter fn x
72 | Ast0.STARS
(x
) -> List.iter fn x
74 (* --------------------------------------------------------------------- *)
77 type context
= ID
| FIELD
| FN
| GLOBAL
79 (* heuristic for distinguishing ifdef variables from undeclared metavariables*)
81 String.length name
> 2 && String.uppercase name
= name
83 let ident context old_metas table minus i
=
84 match Ast0.unwrap i
with
85 Ast0.Id
((name
,_
,info
,_
,_
) : string Ast0.mcode
) ->
86 let rl = info
.Ast0.line_start
in
88 if List.exists
(function x
-> x
= name
) old_metas
89 && (minus
|| Ast0.get_mcodekind i
= Ast0.PLUS
)
94 "line %d: %s, previously declared as a metavariable, is used as an identifier" rl name
);
100 if not
(is_ifdef name
) && minus
&& not
err(* warn only once per id *)
103 (Printf.sprintf
"line %d: should %s be a metavariable?" rl name
)
105 | Ast0.MetaId
(name
,_
,_
) -> check_table table minus name
106 | Ast0.MetaFunc
(name
,_
,_
) -> check_table table minus name
107 | Ast0.MetaLocalFunc
(name
,_
,_
) -> check_table table minus name
108 | Ast0.OptIdent
(_
) | Ast0.UniqueIdent
(_
) ->
109 failwith
"unexpected code"
111 (* --------------------------------------------------------------------- *)
114 let rec expression context old_metas table minus e
=
115 match Ast0.unwrap e
with
117 ident context old_metas table minus id
118 | Ast0.FunCall
(fn
,lp
,args
,rp
) ->
119 expression FN old_metas table minus fn
;
120 dots (expression ID old_metas table minus
) args
121 | Ast0.Assignment
(left
,op
,right
,_
) ->
122 expression context old_metas table minus left
;
123 expression ID old_metas table minus right
124 | Ast0.CondExpr
(exp1
,why
,exp2
,colon
,exp3
) ->
125 expression ID old_metas table minus exp1
;
126 get_opt (expression ID old_metas table minus
) exp2
;
127 expression ID old_metas table minus exp3
128 | Ast0.Postfix
(exp
,op
) ->
129 expression ID old_metas table minus exp
130 | Ast0.Infix
(exp
,op
) ->
131 expression ID old_metas table minus exp
132 | Ast0.Unary
(exp
,op
) ->
133 expression ID old_metas table minus exp
134 | Ast0.Binary
(left
,op
,right
) ->
135 expression ID old_metas table minus left
;
136 expression ID old_metas table minus right
137 | Ast0.Paren
(lp
,exp
,rp
) ->
138 expression ID old_metas table minus exp
139 | Ast0.ArrayAccess
(exp1
,lb
,exp2
,rb
) ->
140 expression ID old_metas table minus exp1
;
141 expression ID old_metas table minus exp2
142 | Ast0.RecordAccess
(exp
,pt
,field
) ->
143 expression ID old_metas table minus exp
;
144 ident FIELD old_metas table minus field
145 | Ast0.RecordPtAccess
(exp
,ar
,field
) ->
146 expression ID old_metas table minus exp
;
147 ident FIELD old_metas table minus field
148 | Ast0.Cast
(lp
,ty
,rp
,exp
) ->
149 typeC old_metas table minus ty
; expression ID old_metas table minus exp
150 | Ast0.SizeOfExpr
(szf
,exp
) -> expression ID old_metas table minus exp
151 | Ast0.SizeOfType
(szf
,lp
,ty
,rp
) -> typeC old_metas table minus ty
152 | Ast0.TypeExp
(ty
) -> typeC old_metas table minus ty
153 | Ast0.MetaExpr
(name
,_
,Some tys
,_
,_
) ->
156 match get_type_name x
with
157 Some
(ty
) -> check_table table minus
(promote ty
)
160 check_table table minus name
161 | Ast0.MetaExpr
(name
,_
,_
,_
,_
) | Ast0.MetaErr
(name
,_
,_
) ->
162 check_table table minus name
163 | Ast0.MetaExprList
(name
,None
,_
) ->
164 check_table table minus name
165 | Ast0.MetaExprList
(name
,Some lenname
,_
) ->
166 check_table table minus name
;
167 check_table table minus lenname
168 | Ast0.DisjExpr
(_
,exps
,_
,_
) ->
169 List.iter
(expression ID old_metas table minus
) exps
170 | Ast0.NestExpr
(_
,exp_dots
,_
,w
,_
) ->
171 dots (expression ID old_metas table minus
) exp_dots
;
172 get_opt (expression ID old_metas table minus
) w
173 | Ast0.Edots
(_
,Some x
) | Ast0.Ecircles
(_
,Some x
) | Ast0.Estars
(_
,Some x
) ->
174 expression ID old_metas table minus x
175 | _
-> () (* no metavariable subterms *)
177 and get_type_name
= function
178 Type_cocci.ConstVol
(_
,ty
) | Type_cocci.Pointer
(ty
)
179 | Type_cocci.FunctionPointer
(ty
) | Type_cocci.Array
(ty
) -> get_type_name ty
180 | Type_cocci.MetaType
(nm
,_
,_
) -> Some nm
183 (* --------------------------------------------------------------------- *)
186 and typeC old_metas table minus t
=
187 match Ast0.unwrap t
with
188 Ast0.ConstVol
(cv
,ty
) -> typeC old_metas table minus ty
189 | Ast0.Pointer
(ty
,star
) -> typeC old_metas table minus ty
190 | Ast0.FunctionPointer
(ty
,lp1
,star
,rp1
,lp2
,params
,rp2
) ->
191 typeC old_metas table minus ty
;
192 parameter_list old_metas table minus params
193 | Ast0.FunctionType
(ty
,lp1
,params
,rp1
) ->
194 get_opt (typeC old_metas table minus
) ty
;
195 parameter_list old_metas table minus params
196 | Ast0.Array
(ty
,lb
,size
,rb
) ->
197 typeC old_metas table minus ty
;
198 get_opt (expression ID old_metas table minus
) size
199 | Ast0.MetaType
(name
,_
) ->
200 check_table table minus name
201 | Ast0.DisjType
(_
,types
,_
,_
) ->
202 List.iter
(typeC old_metas table minus
) types
203 | Ast0.StructUnionName
(su
,Some id
) -> ident GLOBAL old_metas table minus id
204 | Ast0.StructUnionDef
(ty
,lb
,decls
,rb
) ->
205 typeC old_metas table minus ty
;
206 dots (declaration GLOBAL old_metas table minus
) decls
207 | Ast0.OptType
(ty
) | Ast0.UniqueType
(ty
) ->
208 failwith
"unexpected code"
209 | _
-> () (* no metavariable subterms *)
211 (* --------------------------------------------------------------------- *)
212 (* Variable declaration *)
213 (* Even if the Cocci program specifies a list of declarations, they are
214 split out into multiple declarations of a single variable each. *)
216 and declaration context old_metas table minus d
=
217 match Ast0.unwrap d
with
218 Ast0.Init
(stg
,ty
,id
,eq
,ini
,sem
) ->
219 (match Ast0.unwrap ini
with
221 typeC old_metas table minus ty
;
222 ident context old_metas table minus id
;
223 expression ID old_metas table minus exp
228 failwith "complex initializer specification not allowed in - code"
230 (typeC old_metas table minus ty
;
231 ident context old_metas table minus id
;
232 initialiser old_metas table minus ini
))
233 | Ast0.UnInit
(stg
,ty
,id
,sem
) ->
234 typeC old_metas table minus ty
; ident context old_metas table minus id
235 | Ast0.MacroDecl
(name
,lp
,args
,rp
,sem
) ->
236 ident ID old_metas table minus name
;
237 dots (expression ID old_metas table minus
) args
238 | Ast0.TyDecl
(ty
,sem
) -> typeC old_metas table minus ty
239 | Ast0.Typedef
(stg
,ty
,id
,sem
) ->
240 typeC old_metas table minus ty
;
241 typeC old_metas table minus id
242 | Ast0.DisjDecl
(_
,decls
,_
,_
) ->
243 List.iter
(declaration ID old_metas table minus
) decls
244 | Ast0.Ddots
(_
,Some x
) -> declaration ID old_metas table minus x
245 | Ast0.Ddots
(_
,None
) -> ()
246 | Ast0.OptDecl
(_
) | Ast0.UniqueDecl
(_
) ->
247 failwith
"unexpected code"
249 (* --------------------------------------------------------------------- *)
252 and initialiser old_metas table minus ini
=
253 match Ast0.unwrap ini
with
254 Ast0.InitExpr
(exp
) -> expression ID old_metas table minus exp
255 | Ast0.InitList
(lb
,initlist
,rb
) ->
256 dots (initialiser old_metas table minus
) initlist
257 | Ast0.InitGccDotName
(dot
,name
,eq
,ini
) ->
258 ident FIELD old_metas table minus name
;
259 initialiser old_metas table minus ini
260 | Ast0.InitGccName
(name
,eq
,ini
) ->
261 ident FIELD old_metas table minus name
;
262 initialiser old_metas table minus ini
263 | Ast0.InitGccIndex
(lb
,exp
,rb
,eq
,ini
) ->
264 expression ID old_metas table minus exp
;
265 initialiser old_metas table minus ini
266 | Ast0.InitGccRange
(lb
,exp1
,dots,exp2
,rb
,eq
,ini
) ->
267 expression ID old_metas table minus exp1
;
268 expression ID old_metas table minus exp2
;
269 initialiser old_metas table minus ini
270 | Ast0.Idots
(_
,Some x
) -> initialiser old_metas table minus x
271 | Ast0.OptIni
(_
) | Ast0.UniqueIni
(_
) ->
272 failwith
"unexpected code"
273 | _
-> () (* no metavariable subterms *)
275 and initialiser_list old_metas table minus
=
276 dots (initialiser old_metas table minus
)
278 (* --------------------------------------------------------------------- *)
281 and parameterTypeDef old_metas table minus param
=
282 match Ast0.unwrap param
with
284 get_opt (ident ID old_metas table minus
) id
;
285 typeC old_metas table minus ty
286 | Ast0.MetaParam
(name
,_
) ->
287 check_table table minus name
288 | Ast0.MetaParamList
(name
,None
,_
) ->
289 check_table table minus name
290 | Ast0.MetaParamList
(name
,Some lenname
,_
) ->
291 check_table table minus name
;
292 check_table table minus lenname
293 | _
-> () (* no metavariable subterms *)
295 and parameter_list old_metas table minus
=
296 dots (parameterTypeDef old_metas table minus
)
298 (* --------------------------------------------------------------------- *)
301 and statement old_metas table minus s
=
302 match Ast0.unwrap s
with
303 Ast0.Decl
(_
,decl
) -> declaration ID old_metas table minus decl
304 | Ast0.Seq
(lbrace
,body
,rbrace
) -> dots (statement old_metas table minus
) body
305 | Ast0.ExprStatement
(exp
,sem
) -> expression ID old_metas table minus exp
306 | Ast0.IfThen
(iff
,lp
,exp
,rp
,branch
,_
) ->
307 expression ID old_metas table minus exp
;
308 statement old_metas table minus branch
309 | Ast0.IfThenElse
(iff
,lp
,exp
,rp
,branch1
,els
,branch2
,_
) ->
310 expression ID old_metas table minus exp
;
311 statement old_metas table minus branch1
;
312 statement old_metas table minus branch2
313 | Ast0.While
(wh
,lp
,exp
,rp
,body
,_
) ->
314 expression ID old_metas table minus exp
;
315 statement old_metas table minus body
316 | Ast0.Do
(d
,body
,wh
,lp
,exp
,rp
,sem
) ->
317 statement old_metas table minus body
;
318 expression ID old_metas table minus exp
319 | Ast0.For
(fr
,lp
,exp1
,sem1
,exp2
,sem2
,exp3
,rp
,body
,_
) ->
320 get_opt (expression ID old_metas table minus
) exp1
;
321 get_opt (expression ID old_metas table minus
) exp2
;
322 get_opt (expression ID old_metas table minus
) exp3
;
323 statement old_metas table minus body
324 | Ast0.Iterator
(nm
,lp
,args
,rp
,body
,_
) ->
325 ident ID old_metas table minus nm
;
326 dots (expression ID old_metas table minus
) args
;
327 statement old_metas table minus body
328 | Ast0.Switch
(switch
,lp
,exp
,rp
,lb
,cases
,rb
) ->
329 expression ID old_metas table minus exp
;
330 dots (case_line old_metas table minus
) cases
331 | Ast0.ReturnExpr
(ret
,exp
,sem
) -> expression ID old_metas table minus exp
332 | Ast0.MetaStmt
(name
,_
) -> check_table table minus name
333 | Ast0.MetaStmtList
(name
,_
) -> check_table table minus name
334 | Ast0.Exp
(exp
) -> expression ID old_metas table minus exp
335 | Ast0.TopExp
(exp
) -> expression ID old_metas table minus exp
336 | Ast0.Ty
(ty
) -> typeC old_metas table minus ty
337 | Ast0.TopInit
(init
) -> initialiser old_metas table minus init
338 | Ast0.Disj
(_
,rule_elem_dots_list
,_
,_
) ->
339 List.iter
(dots (statement old_metas table minus
)) rule_elem_dots_list
340 | Ast0.Nest
(_
,rule_elem_dots
,_
,w
,_
) ->
341 dots (statement old_metas table minus
) rule_elem_dots
;
342 List.iter
(whencode
(dots (statement old_metas table minus
))
343 (statement old_metas table minus
)
344 (expression ID old_metas table minus
))
346 | Ast0.Dots
(_
,x
) | Ast0.Circles
(_
,x
) | Ast0.Stars
(_
,x
) ->
348 (whencode
(dots (statement old_metas table minus
))
349 (statement old_metas table minus
)
350 (expression ID old_metas table minus
)) x
351 | Ast0.FunDecl
(_
,fi
,name
,lp
,params
,rp
,lbrace
,body
,rbrace
) ->
352 ident FN old_metas table minus name
;
353 List.iter
(fninfo old_metas table minus
) fi
;
354 parameter_list old_metas table minus params
;
355 dots (statement old_metas table minus
) body
356 | Ast0.Include
(inc
,s
) -> () (* no metavariables possible *)
357 | Ast0.Define
(def
,id
,_
,body
) ->
358 ident GLOBAL old_metas table minus id
;
359 dots (statement old_metas table minus
) body
360 | Ast0.Goto
(_
,i
,_
) -> ident ID old_metas table minus i
361 | _
-> () (* no metavariable subterms *)
363 and fninfo old_metas table minus
= function
364 Ast0.FStorage
(stg
) -> ()
365 | Ast0.FType
(ty
) -> typeC old_metas table minus ty
366 | Ast0.FInline
(inline
) -> ()
367 | Ast0.FAttr
(attr
) -> ()
369 and whencode notfn alwaysfn
expression = function
370 Ast0.WhenNot a
-> notfn a
371 | Ast0.WhenAlways a
-> alwaysfn a
372 | Ast0.WhenModifier
(_
) -> ()
373 | Ast0.WhenNotTrue a
-> expression a
374 | Ast0.WhenNotFalse a
-> expression a
376 and case_line old_metas table minus c
=
377 match Ast0.unwrap c
with
378 Ast0.Default
(def
,colon
,code
) ->
379 dots (statement old_metas table minus
) code
380 | Ast0.Case
(case
,exp
,colon
,code
) ->
381 dots (statement old_metas table minus
) code
382 | Ast0.OptCase
(case
) -> failwith
"unexpected code"
384 (* --------------------------------------------------------------------- *)
387 let top_level old_metas table minus t
=
388 match Ast0.unwrap t
with
389 Ast0.DECL
(stmt
) -> statement old_metas table minus stmt
390 | Ast0.CODE
(stmt_dots
) -> dots (statement old_metas table minus
) stmt_dots
391 | Ast0.ERRORWORDS
(exps
) ->
392 List.iter
(expression FN old_metas table minus
) exps
393 | _
-> () (* no metavariables possible *)
395 let rule old_metas table minus rules
=
396 List.iter
(top_level old_metas table minus
) rules
398 (* --------------------------------------------------------------------- *)
400 let positions table rules
=
402 match Ast0.get_pos x
with
403 Ast0.MetaPos
(name
,constraints
,_
) ->
404 let pos = Ast0.unwrap_mcode name
in
405 (find_loop table
pos) := true
407 let option_default = () in
409 let donothing r k e
= k e
in
411 V0.combiner
bind option_default
412 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
414 donothing donothing donothing donothing donothing donothing
415 donothing donothing donothing donothing donothing donothing donothing
416 donothing donothing in
418 List.iter
fn.V0.combiner_top_level rules
420 let dup_positions rules
=
422 match Ast0.get_pos x
with
423 Ast0.MetaPos
(name
,constraints
,_
) ->
424 let pos = Ast0.unwrap_mcode name
in [pos]
426 let option_default = [] in
427 let bind x y
= x
@y
in
429 (* Case for everything that has a disj.
430 Note, no positions on ( | ) of a disjunction, so no need to recurse on
433 let expression r k e
=
434 match Ast0.unwrap e
with
435 Ast0.DisjExpr
(_
,explist
,_
,_
) ->
436 List.fold_left
Common.union_set
option_default
437 (List.map r
.V0.combiner_expression explist
)
440 let typeC r k e
= (* not sure relevent because "only after iso" *)
441 match Ast0.unwrap e
with
442 Ast0.DisjType
(_
,types
,_
,_
) ->
443 List.fold_left
Common.union_set
option_default
444 (List.map r
.V0.combiner_typeC types
)
447 let declaration r k e
=
448 match Ast0.unwrap e
with
449 Ast0.DisjDecl
(_
,decls
,_
,_
) ->
450 List.fold_left
Common.union_set
option_default
451 (List.map r
.V0.combiner_declaration decls
)
454 let statement r k e
=
455 match Ast0.unwrap e
with
456 Ast0.Disj
(_
,stmts
,_
,_
) ->
457 List.fold_left
Common.union_set
option_default
458 (List.map r
.V0.combiner_statement_dots stmts
)
461 let donothing r k e
= k e
in
463 V0.combiner
bind option_default
464 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
466 donothing donothing donothing donothing donothing donothing
467 donothing expression typeC donothing donothing declaration statement
468 donothing donothing in
472 (List.fold_left
Common.union_set
option_default
473 (List.map
fn.V0.combiner_top_level rules
)) in
474 let rec loop = function
476 | ((rule,name
) as x
)::y
::_
when x
= y
->
477 failwith
(Printf.sprintf
"duplicate use of %s.%s" rule name
)
478 | _
::xs
-> loop xs
in
481 (* --------------------------------------------------------------------- *)
485 (Hashtbl.create
(List.length l
) :
486 ((string * string), bool ref) Hashtbl.t
) in
488 (function x
-> Hashtbl.add
table (Ast.get_meta_name x
) (ref false)) l
;
491 let add_to_fresh_table l
=
494 let name = Ast.get_meta_name x
in Hashtbl.replace
fresh_table name ())
497 let check_all_marked rname
err table after_err
=
503 let (_
,name) = name in
505 (Printf.sprintf
"%s: %s %s not used %s" rname
err name after_err
))
508 let check_meta rname old_metas inherited_metavars metavars minus plus
=
510 List.map
(function (_
,x
) -> x
) (List.map
Ast.get_meta_name
old_metas) in
512 List.partition
(function Ast.MetaFreshIdDecl
(_
,_
) -> true | _
-> false)
515 List.partition
(function Ast.MetaErrDecl
(_
,_
) -> true | _
-> false)
518 List.partition
(function Ast.MetaErrDecl
(_
,_
) -> true | _
-> false)
519 inherited_metavars
in
520 let fresh_table = make_table fresh
in
521 let err_table = make_table (err@ierr
) in
522 let other_table = make_table other
in
523 let iother_table = make_table iother
in
524 add_to_fresh_table fresh
;
525 rule old_metas [iother_table;other_table;err_table] true minus
;
526 positions [iother_table;other_table] minus
;
528 check_all_marked rname
"metavariable" other_table "in the - or context code";
529 rule old_metas [iother_table;fresh_table;err_table] false plus
;
530 check_all_marked rname
"fresh identifier metavariable" iother_table
531 "in the -, +, or context code";
532 check_all_marked rname
"metavariable" fresh_table "in the + code";
533 check_all_marked rname
"error metavariable" err_table ""