2 * Copyright 2005-2009, 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
31 module VT0
= Visitor_ast0_types
33 (* all fresh identifiers *)
34 let fresh_table = (Hashtbl.create
(50) : ((string * string), unit) Hashtbl.t
)
36 let warning s
= Printf.fprintf stderr
"warning: %s\n" s
38 let promote name
= (name
,(),Ast0.default_info
(),(),None
)
40 (* --------------------------------------------------------------------- *)
42 let find_loop table name
=
43 let rec loop = function
45 | x
::xs
-> (try Hashtbl.find x name
with Not_found
-> loop xs
) in
48 let check_table table minus
(name
,_
,info
,_
,_
) =
49 let rl = info
.Ast0.pos_info
.Ast0.line_start
in
52 (try (find_loop table name
) := true
56 Hashtbl.find
fresh_table name
;
57 let (_
,name
) = name
in
60 "%d: unexpected use of a fresh identifier %s" rl name
)
61 with Not_found
-> ()))
62 else (try (find_loop table name
) := true with Not_found
-> ())
64 let get_opt fn
= Common.do_option fn
66 (* --------------------------------------------------------------------- *)
70 match Ast0.unwrap d
with
71 Ast0.DOTS
(x
) -> List.iter fn x
72 | Ast0.CIRCLES
(x
) -> List.iter fn x
73 | Ast0.STARS
(x
) -> List.iter fn x
75 (* --------------------------------------------------------------------- *)
78 type context
= ID
| FIELD
| FN
| GLOBAL
80 (* heuristic for distinguishing ifdef variables from undeclared metavariables*)
82 String.length name
> 2 && String.uppercase name
= name
84 let ident context old_metas table minus i
=
85 match Ast0.unwrap i
with
86 Ast0.Id
((name
,_
,info
,_
,_
) : string Ast0.mcode
) ->
87 let rl = info
.Ast0.pos_info
.Ast0.line_start
in
89 if List.exists
(function x
-> x
= name
) old_metas
90 && (minus
|| Ast0.get_mcodekind i
= Ast0.PLUS
)
95 "line %d: %s, previously declared as a metavariable, is used as an identifier" rl name
);
101 if not
(is_ifdef name
) && minus
&& not
err(* warn only once per id *)
104 (Printf.sprintf
"line %d: should %s be a metavariable?" rl name
)
106 | Ast0.MetaId
(name
,_
,_
) -> check_table table minus name
107 | Ast0.MetaFunc
(name
,_
,_
) -> check_table table minus name
108 | Ast0.MetaLocalFunc
(name
,_
,_
) -> check_table table minus name
109 | Ast0.OptIdent
(_
) | Ast0.UniqueIdent
(_
) ->
110 failwith
"unexpected code"
112 (* --------------------------------------------------------------------- *)
115 let rec expression context old_metas table minus e
=
116 match Ast0.unwrap e
with
118 ident context old_metas table minus id
119 | Ast0.FunCall
(fn
,lp
,args
,rp
) ->
120 expression FN old_metas table minus fn
;
121 dots (expression ID old_metas table minus
) args
122 | Ast0.Assignment
(left
,op
,right
,_
) ->
123 expression context old_metas table minus left
;
124 expression ID old_metas table minus right
125 | Ast0.CondExpr
(exp1
,why
,exp2
,colon
,exp3
) ->
126 expression ID old_metas table minus exp1
;
127 get_opt (expression ID old_metas table minus
) exp2
;
128 expression ID old_metas table minus exp3
129 | Ast0.Postfix
(exp
,op
) ->
130 expression ID old_metas table minus exp
131 | Ast0.Infix
(exp
,op
) ->
132 expression ID old_metas table minus exp
133 | Ast0.Unary
(exp
,op
) ->
134 expression ID old_metas table minus exp
135 | Ast0.Binary
(left
,op
,right
) ->
136 expression ID old_metas table minus left
;
137 expression ID old_metas table minus right
138 | Ast0.Paren
(lp
,exp
,rp
) ->
139 expression ID old_metas table minus exp
140 | Ast0.ArrayAccess
(exp1
,lb
,exp2
,rb
) ->
141 expression ID old_metas table minus exp1
;
142 expression ID old_metas table minus exp2
143 | Ast0.RecordAccess
(exp
,pt
,field
) ->
144 expression ID old_metas table minus exp
;
145 ident FIELD old_metas table minus field
146 | Ast0.RecordPtAccess
(exp
,ar
,field
) ->
147 expression ID old_metas table minus exp
;
148 ident FIELD old_metas table minus field
149 | Ast0.Cast
(lp
,ty
,rp
,exp
) ->
150 typeC old_metas table minus ty
; expression ID old_metas table minus exp
151 | Ast0.SizeOfExpr
(szf
,exp
) -> expression ID old_metas table minus exp
152 | Ast0.SizeOfType
(szf
,lp
,ty
,rp
) -> typeC old_metas table minus ty
153 | Ast0.TypeExp
(ty
) -> typeC old_metas table minus ty
154 | Ast0.MetaExpr
(name
,_
,Some tys
,_
,_
) ->
157 match get_type_name x
with
158 Some
(ty
) -> check_table table minus
(promote ty
)
161 check_table table minus name
162 | Ast0.MetaExpr
(name
,_
,_
,_
,_
) | Ast0.MetaErr
(name
,_
,_
) ->
163 check_table table minus name
164 | Ast0.MetaExprList
(name
,None
,_
) ->
165 check_table table minus name
166 | Ast0.MetaExprList
(name
,Some lenname
,_
) ->
167 check_table table minus name
;
168 check_table table minus lenname
169 | Ast0.DisjExpr
(_
,exps
,_
,_
) ->
170 List.iter
(expression context old_metas table minus
) exps
171 | Ast0.NestExpr
(_
,exp_dots
,_
,w
,_
) ->
172 dots (expression ID old_metas table minus
) exp_dots
;
173 get_opt (expression ID old_metas table minus
) w
174 | Ast0.Edots
(_
,Some x
) | Ast0.Ecircles
(_
,Some x
) | Ast0.Estars
(_
,Some x
) ->
175 expression ID old_metas table minus x
176 | _
-> () (* no metavariable subterms *)
178 and get_type_name
= function
179 Type_cocci.ConstVol
(_
,ty
) | Type_cocci.SignedT
(_
,Some ty
)
180 | Type_cocci.Pointer
(ty
)
181 | Type_cocci.FunctionPointer
(ty
) | Type_cocci.Array
(ty
) -> get_type_name ty
182 | Type_cocci.MetaType
(nm
,_
,_
) -> Some nm
185 (* --------------------------------------------------------------------- *)
188 and typeC old_metas table minus t
=
189 match Ast0.unwrap t
with
190 Ast0.ConstVol
(cv
,ty
) -> typeC old_metas table minus ty
191 | Ast0.Signed
(sgn
,ty
) ->
192 get_opt (typeC old_metas table minus
) ty
193 | Ast0.Pointer
(ty
,star
) -> typeC old_metas table minus ty
194 | Ast0.FunctionPointer
(ty
,lp1
,star
,rp1
,lp2
,params
,rp2
) ->
195 typeC old_metas table minus ty
;
196 parameter_list old_metas table minus params
197 | Ast0.FunctionType
(ty
,lp1
,params
,rp1
) ->
198 get_opt (typeC old_metas table minus
) ty
;
199 parameter_list old_metas table minus params
200 | Ast0.Array
(ty
,lb
,size
,rb
) ->
201 typeC old_metas table minus ty
;
202 get_opt (expression ID old_metas table minus
) size
203 | Ast0.MetaType
(name
,_
) ->
204 check_table table minus name
205 | Ast0.DisjType
(_
,types
,_
,_
) ->
206 List.iter
(typeC old_metas table minus
) types
207 | Ast0.EnumName
(en
,id
) -> ident GLOBAL old_metas table minus id
208 | Ast0.StructUnionName
(su
,Some id
) -> ident GLOBAL old_metas table minus id
209 | Ast0.StructUnionDef
(ty
,lb
,decls
,rb
) ->
210 typeC old_metas table minus ty
;
211 dots (declaration GLOBAL old_metas table minus
) decls
212 | Ast0.OptType
(ty
) | Ast0.UniqueType
(ty
) ->
213 failwith
"unexpected code"
214 | _
-> () (* no metavariable subterms *)
216 (* --------------------------------------------------------------------- *)
217 (* Variable declaration *)
218 (* Even if the Cocci program specifies a list of declarations, they are
219 split out into multiple declarations of a single variable each. *)
221 and declaration context old_metas table minus d
=
222 match Ast0.unwrap d
with
223 Ast0.Init
(stg
,ty
,id
,eq
,ini
,sem
) ->
224 (match Ast0.unwrap ini
with
226 typeC old_metas table minus ty
;
227 ident context old_metas table minus id
;
228 expression ID old_metas table minus exp
233 failwith "complex initializer specification not allowed in - code"
235 (typeC old_metas table minus ty
;
236 ident context old_metas table minus id
;
237 initialiser old_metas table minus ini
))
238 | Ast0.UnInit
(stg
,ty
,id
,sem
) ->
239 typeC old_metas table minus ty
; ident context old_metas table minus id
240 | Ast0.MacroDecl
(name
,lp
,args
,rp
,sem
) ->
241 ident GLOBAL old_metas table minus name
;
242 dots (expression ID old_metas table minus
) args
243 | Ast0.TyDecl
(ty
,sem
) -> typeC old_metas table minus ty
244 | Ast0.Typedef
(stg
,ty
,id
,sem
) ->
245 typeC old_metas table minus ty
;
246 typeC old_metas table minus id
247 | Ast0.DisjDecl
(_
,decls
,_
,_
) ->
248 List.iter
(declaration ID old_metas table minus
) decls
249 | Ast0.Ddots
(_
,Some x
) -> declaration ID old_metas table minus x
250 | Ast0.Ddots
(_
,None
) -> ()
251 | Ast0.OptDecl
(_
) | Ast0.UniqueDecl
(_
) ->
252 failwith
"unexpected code"
254 (* --------------------------------------------------------------------- *)
257 and initialiser old_metas table minus ini
=
258 match Ast0.unwrap ini
with
259 Ast0.MetaInit
(name
,_
) ->
260 check_table table minus name
261 | Ast0.InitExpr
(exp
) -> expression ID old_metas table minus exp
262 | Ast0.InitList
(lb
,initlist
,rb
) ->
263 dots (initialiser old_metas table minus
) initlist
264 | Ast0.InitGccExt
(designators
,eq
,ini
) ->
265 List.iter
(designator old_metas table minus
) designators
;
266 initialiser old_metas table minus ini
267 | Ast0.InitGccName
(name
,eq
,ini
) ->
268 ident FIELD old_metas table minus name
;
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 designator old_metas table minus
= function
276 Ast0.DesignatorField
(dot
,id
) ->
277 ident FIELD old_metas table minus id
278 | Ast0.DesignatorIndex
(lb
,exp
,rb
) ->
279 expression ID old_metas table minus exp
280 | Ast0.DesignatorRange
(lb
,min
,dots,max
,rb
) ->
281 expression ID old_metas table minus min
;
282 expression ID old_metas table minus max
284 and initialiser_list old_metas table minus
=
285 dots (initialiser old_metas table minus
)
287 (* --------------------------------------------------------------------- *)
290 and parameterTypeDef old_metas table minus param
=
291 match Ast0.unwrap param
with
293 get_opt (ident ID old_metas table minus
) id
;
294 typeC old_metas table minus ty
295 | Ast0.MetaParam
(name
,_
) ->
296 check_table table minus name
297 | Ast0.MetaParamList
(name
,None
,_
) ->
298 check_table table minus name
299 | Ast0.MetaParamList
(name
,Some lenname
,_
) ->
300 check_table table minus name
;
301 check_table table minus lenname
302 | _
-> () (* no metavariable subterms *)
304 and parameter_list old_metas table minus
=
305 dots (parameterTypeDef old_metas table minus
)
307 (* --------------------------------------------------------------------- *)
310 and statement old_metas table minus s
=
311 match Ast0.unwrap s
with
312 Ast0.Decl
(_
,decl
) -> declaration ID old_metas table minus decl
313 | Ast0.Seq
(lbrace
,body
,rbrace
) -> dots (statement old_metas table minus
) body
314 | Ast0.ExprStatement
(exp
,sem
) -> expression ID old_metas table minus exp
315 | Ast0.IfThen
(iff
,lp
,exp
,rp
,branch
,_
) ->
316 expression ID old_metas table minus exp
;
317 statement old_metas table minus branch
318 | Ast0.IfThenElse
(iff
,lp
,exp
,rp
,branch1
,els
,branch2
,_
) ->
319 expression ID old_metas table minus exp
;
320 statement old_metas table minus branch1
;
321 statement old_metas table minus branch2
322 | Ast0.While
(wh
,lp
,exp
,rp
,body
,_
) ->
323 expression ID old_metas table minus exp
;
324 statement old_metas table minus body
325 | Ast0.Do
(d
,body
,wh
,lp
,exp
,rp
,sem
) ->
326 statement old_metas table minus body
;
327 expression ID old_metas table minus exp
328 | Ast0.For
(fr
,lp
,exp1
,sem1
,exp2
,sem2
,exp3
,rp
,body
,_
) ->
329 get_opt (expression ID old_metas table minus
) exp1
;
330 get_opt (expression ID old_metas table minus
) exp2
;
331 get_opt (expression ID old_metas table minus
) exp3
;
332 statement old_metas table minus body
333 | Ast0.Iterator
(nm
,lp
,args
,rp
,body
,_
) ->
334 ident GLOBAL old_metas table minus nm
;
335 dots (expression ID old_metas table minus
) args
;
336 statement old_metas table minus body
337 | Ast0.Switch
(switch
,lp
,exp
,rp
,lb
,cases
,rb
) ->
338 expression ID old_metas table minus exp
;
339 dots (case_line old_metas table minus
) cases
340 | Ast0.ReturnExpr
(ret
,exp
,sem
) -> expression ID old_metas table minus exp
341 | Ast0.MetaStmt
(name
,_
) -> check_table table minus name
342 | Ast0.MetaStmtList
(name
,_
) -> check_table table minus name
343 | Ast0.Exp
(exp
) -> expression ID old_metas table minus exp
344 | Ast0.TopExp
(exp
) -> expression ID old_metas table minus exp
345 | Ast0.Ty
(ty
) -> typeC old_metas table minus ty
346 | Ast0.TopInit
(init
) -> initialiser old_metas table minus init
347 | Ast0.Disj
(_
,rule_elem_dots_list
,_
,_
) ->
348 List.iter
(dots (statement old_metas table minus
)) rule_elem_dots_list
349 | Ast0.Nest
(_
,rule_elem_dots
,_
,w
,_
) ->
350 dots (statement old_metas table minus
) rule_elem_dots
;
351 List.iter
(whencode
(dots (statement old_metas table minus
))
352 (statement old_metas table minus
)
353 (expression ID old_metas table minus
))
355 | Ast0.Dots
(_
,x
) | Ast0.Circles
(_
,x
) | Ast0.Stars
(_
,x
) ->
357 (whencode
(dots (statement old_metas table minus
))
358 (statement old_metas table minus
)
359 (expression ID old_metas table minus
)) x
360 | Ast0.FunDecl
(_
,fi
,name
,lp
,params
,rp
,lbrace
,body
,rbrace
) ->
361 ident FN old_metas table minus name
;
362 List.iter
(fninfo old_metas table minus
) fi
;
363 parameter_list old_metas table minus params
;
364 dots (statement old_metas table minus
) body
365 | Ast0.Include
(inc
,s
) -> () (* no metavariables possible *)
366 | Ast0.Define
(def
,id
,_
,body
) ->
367 ident GLOBAL old_metas table minus id
;
368 dots (statement old_metas table minus
) body
369 | Ast0.Goto
(_
,i
,_
) -> ident ID old_metas table minus i
370 | _
-> () (* no metavariable subterms *)
372 and fninfo old_metas table minus
= function
373 Ast0.FStorage
(stg
) -> ()
374 | Ast0.FType
(ty
) -> typeC old_metas table minus ty
375 | Ast0.FInline
(inline
) -> ()
376 | Ast0.FAttr
(attr
) -> ()
378 and whencode notfn alwaysfn
expression = function
379 Ast0.WhenNot a
-> notfn a
380 | Ast0.WhenAlways a
-> alwaysfn a
381 | Ast0.WhenModifier
(_
) -> ()
382 | Ast0.WhenNotTrue a
-> expression a
383 | Ast0.WhenNotFalse a
-> expression a
385 and case_line old_metas table minus c
=
386 match Ast0.unwrap c
with
387 Ast0.Default
(def
,colon
,code
) ->
388 dots (statement old_metas table minus
) code
389 | Ast0.Case
(case
,exp
,colon
,code
) ->
390 dots (statement old_metas table minus
) code
391 | Ast0.OptCase
(case
) -> failwith
"unexpected code"
393 (* --------------------------------------------------------------------- *)
396 let top_level old_metas table minus t
=
397 match Ast0.unwrap t
with
398 Ast0.DECL
(stmt
) -> statement old_metas table minus stmt
399 | Ast0.CODE
(stmt_dots
) -> dots (statement old_metas table minus
) stmt_dots
400 | Ast0.ERRORWORDS
(exps
) ->
401 List.iter
(expression FN old_metas table minus
) exps
402 | _
-> () (* no metavariables possible *)
404 let rule old_metas table minus rules
=
405 List.iter
(top_level old_metas table minus
) rules
407 (* --------------------------------------------------------------------- *)
409 let positions table rules
=
411 match Ast0.get_pos x
with
412 Ast0.MetaPos
(name
,constraints
,_
) ->
413 let pos = Ast0.unwrap_mcode name
in
414 (find_loop table
pos) := true
416 let option_default = () in
418 let donothing r k e
= k e
in
420 V0.flat_combiner
bind option_default
421 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
422 donothing donothing donothing donothing donothing donothing
423 donothing donothing donothing donothing donothing donothing donothing
424 donothing donothing in
426 List.iter
fn.VT0.combiner_rec_top_level rules
428 let dup_positions rules
=
430 match Ast0.get_pos x
with
431 Ast0.MetaPos
(name
,constraints
,_
) ->
432 let pos = Ast0.unwrap_mcode name
in [pos]
434 let option_default = [] in
435 let bind x y
= x
@y
in
437 (* Case for everything that has a disj.
438 Note, no positions on ( | ) of a disjunction, so no need to recurse on
441 let expression r k e
=
442 match Ast0.unwrap e
with
443 Ast0.DisjExpr
(_
,explist
,_
,_
) ->
444 List.fold_left
Common.union_set
option_default
445 (List.map r
.VT0.combiner_rec_expression explist
)
448 let typeC r k e
= (* not sure relevent because "only after iso" *)
449 match Ast0.unwrap e
with
450 Ast0.DisjType
(_
,types
,_
,_
) ->
451 List.fold_left
Common.union_set
option_default
452 (List.map r
.VT0.combiner_rec_typeC types
)
455 let declaration r k e
=
456 match Ast0.unwrap e
with
457 Ast0.DisjDecl
(_
,decls
,_
,_
) ->
458 List.fold_left
Common.union_set
option_default
459 (List.map r
.VT0.combiner_rec_declaration decls
)
462 let statement r k e
=
463 match Ast0.unwrap e
with
464 Ast0.Disj
(_
,stmts
,_
,_
) ->
465 List.fold_left
Common.union_set
option_default
466 (List.map r
.VT0.combiner_rec_statement_dots stmts
)
469 let donothing r k e
= k e
in
471 V0.flat_combiner
bind option_default
472 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
473 donothing donothing donothing donothing donothing donothing
474 donothing expression typeC donothing donothing declaration statement
475 donothing donothing in
479 (List.fold_left
Common.union_set
option_default
480 (List.map
fn.VT0.combiner_rec_top_level rules
)) in
481 let rec loop = function
483 | ((rule,name
) as x
)::y
::_
when x
= y
->
484 failwith
(Printf.sprintf
"duplicate use of %s.%s" rule name
)
485 | _
::xs
-> loop xs
in
488 (* --------------------------------------------------------------------- *)
492 (Hashtbl.create
(List.length l
) :
493 ((string * string), bool ref) Hashtbl.t
) in
495 (function x
-> Hashtbl.add
table (Ast.get_meta_name x
) (ref false)) l
;
498 let add_to_fresh_table l
=
501 let name = Ast.get_meta_name x
in Hashtbl.replace
fresh_table name ())
504 let check_all_marked rname
err table after_err
=
510 let (_
,name) = name in
512 (Printf.sprintf
"%s: %s %s not used %s" rname
err name after_err
))
515 let check_meta rname old_metas inherited_metavars metavars minus plus
=
517 List.map
(function (_
,x
) -> x
) (List.map
Ast.get_meta_name
old_metas) in
519 List.partition
(function Ast.MetaFreshIdDecl
(_
,_
) -> true | _
-> false)
522 List.partition
(function Ast.MetaErrDecl
(_
,_
) -> true | _
-> false)
525 List.partition
(function Ast.MetaErrDecl
(_
,_
) -> true | _
-> false)
526 inherited_metavars
in
527 let fresh_table = make_table fresh
in
528 let err_table = make_table (err@ierr
) in
529 let other_table = make_table other
in
530 let iother_table = make_table iother
in
531 add_to_fresh_table fresh
;
532 rule old_metas [iother_table;other_table;err_table] true minus
;
533 positions [iother_table;other_table] minus
;
535 check_all_marked rname
"metavariable" other_table "in the - or context code";
536 rule old_metas [iother_table;fresh_table;err_table] false plus
;
537 check_all_marked rname
"inherited metavariable" iother_table
538 "in the -, +, or context code";
539 check_all_marked rname
"metavariable" fresh_table "in the + code";
540 check_all_marked rname
"error metavariable" err_table ""