Release coccinelle-0.1.1
[bpt/coccinelle.git] / parsing_cocci / .#check_meta.ml.1.79
diff --git a/parsing_cocci/.#check_meta.ml.1.79 b/parsing_cocci/.#check_meta.ml.1.79
new file mode 100644 (file)
index 0000000..28da910
--- /dev/null
@@ -0,0 +1,528 @@
+(*
+* Copyright 2005-2008, Ecole des Mines de Nantes, University of Copenhagen
+* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller
+* This file is part of Coccinelle.
+* 
+* Coccinelle is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, according to version 2 of the License.
+* 
+* Coccinelle is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+* 
+* You should have received a copy of the GNU General Public License
+* along with Coccinelle.  If not, see <http://www.gnu.org/licenses/>.
+* 
+* The authors reserve the right to distribute this or future versions of
+* Coccinelle under other licenses.
+*)
+
+
+(* For minus fragment, checks that all of the identifier metavariables that
+are used are not declared as fresh, and check that all declared variables
+are used.  For plus fragment, just check that the variables declared as
+fresh are used.  What is the issue about error variables? (don't remember) *)
+
+module Ast0 = Ast0_cocci
+module Ast = Ast_cocci
+module V0 = Visitor_ast0
+
+(* all fresh identifiers *)
+let fresh_table = (Hashtbl.create(50) : ((string * string), unit) Hashtbl.t)
+
+let warning s = Printf.fprintf stderr "warning: %s\n" s
+
+let promote name = (name,(),Ast0.default_info(),(),None)
+
+(* --------------------------------------------------------------------- *)
+
+let find_loop table name =
+  let rec loop = function
+      [] -> raise Not_found
+    | x::xs -> (try Hashtbl.find x name with Not_found -> loop xs) in
+  loop table
+
+let check_table table minus (name,_,info,_,_) =
+  let rl = info.Ast0.line_start in
+  if minus
+  then
+    (try (find_loop table name) := true
+    with
+      Not_found ->
+       (try
+         Hashtbl.find fresh_table name;
+         let (_,name) = name in
+         failwith
+           (Printf.sprintf
+              "%d: unexpected use of a fresh identifier %s" rl name)
+       with Not_found -> ()))
+  else (try (find_loop table name) := true with Not_found -> ())
+
+let get_opt fn = Common.do_option fn
+
+(* --------------------------------------------------------------------- *)
+(* Dots *)
+
+let dots fn d =
+  match Ast0.unwrap d with
+    Ast0.DOTS(x) -> List.iter fn x
+  | Ast0.CIRCLES(x) -> List.iter fn x
+  | Ast0.STARS(x) -> List.iter fn x
+
+(* --------------------------------------------------------------------- *)
+(* Identifier *)
+
+type context = ID | FIELD | FN | GLOBAL
+
+(* heuristic for distinguishing ifdef variables from undeclared metavariables*)
+let is_ifdef name =
+  String.length name > 2 && String.uppercase name = name
+
+let ident context old_metas table minus i =
+  match Ast0.unwrap i with
+    Ast0.Id((name,_,info,_,_) : string Ast0.mcode) ->
+      let rl = info.Ast0.line_start in
+      let err =
+       if List.exists (function x -> x = name) old_metas
+           && (minus || Ast0.get_mcodekind i = Ast0.PLUS)
+       then
+         begin
+           warning
+             (Printf.sprintf
+                "line %d: %s, previously declared as a metavariable, is used as an identifier" rl name);
+             true
+         end
+       else false in
+      (match context with
+       ID ->
+         if not (is_ifdef name) && minus && not err(* warn only once per id *)
+         then
+           warning
+             (Printf.sprintf "line %d: should %s be a metavariable?" rl name)
+      | _ -> ())
+  | Ast0.MetaId(name,_,_) -> check_table table minus name
+  | Ast0.MetaFunc(name,_,_) -> check_table table minus name
+  | Ast0.MetaLocalFunc(name,_,_) -> check_table table minus name
+  | Ast0.OptIdent(_) | Ast0.UniqueIdent(_) ->
+      failwith "unexpected code"
+
+(* --------------------------------------------------------------------- *)
+(* Expression *)
+
+let rec expression context old_metas table minus e =
+  match Ast0.unwrap e with
+    Ast0.Ident(id) ->
+      ident context old_metas table minus id
+  | Ast0.FunCall(fn,lp,args,rp) ->
+      expression FN old_metas table minus fn;
+      dots (expression ID old_metas table minus) args
+  | Ast0.Assignment(left,op,right,_) ->
+      expression context old_metas table minus left;
+      expression ID old_metas table minus right
+  | Ast0.CondExpr(exp1,why,exp2,colon,exp3) ->
+      expression ID old_metas table minus exp1;
+      get_opt (expression ID old_metas table minus) exp2;
+      expression ID old_metas table minus exp3
+  | Ast0.Postfix(exp,op) ->
+      expression ID old_metas table minus exp
+  | Ast0.Infix(exp,op) ->
+      expression ID old_metas table minus exp
+  | Ast0.Unary(exp,op) ->
+      expression ID old_metas table minus exp
+  | Ast0.Binary(left,op,right) ->
+      expression ID old_metas table minus left;
+      expression ID old_metas table minus right
+  | Ast0.Paren(lp,exp,rp) ->
+      expression ID old_metas table minus exp
+  | Ast0.ArrayAccess(exp1,lb,exp2,rb) ->
+      expression ID old_metas table minus exp1;
+      expression ID old_metas table minus exp2
+  | Ast0.RecordAccess(exp,pt,field) ->
+      expression ID old_metas table minus exp;
+      ident FIELD old_metas table minus field
+  | Ast0.RecordPtAccess(exp,ar,field) ->
+      expression ID old_metas table minus exp;
+      ident FIELD old_metas table minus field
+  | Ast0.Cast(lp,ty,rp,exp) ->
+      typeC old_metas table minus ty; expression ID old_metas table minus exp
+  | Ast0.SizeOfExpr(szf,exp) -> expression ID old_metas table minus exp
+  | Ast0.SizeOfType(szf,lp,ty,rp) -> typeC old_metas table minus ty
+  | Ast0.TypeExp(ty) -> typeC old_metas table minus ty
+  | Ast0.MetaExpr(name,_,Some tys,_,_) ->
+      List.iter
+       (function x ->
+         match get_type_name x with
+           Some(ty) -> check_table table minus (promote ty)
+         | None -> ())
+       tys;
+      check_table table minus name
+  | Ast0.MetaExpr(name,_,_,_,_) | Ast0.MetaErr(name,_,_) ->
+      check_table table minus name
+  | Ast0.MetaExprList(name,None,_) ->
+      check_table table minus name
+  | Ast0.MetaExprList(name,Some lenname,_) ->
+      check_table table minus name;
+      check_table table minus lenname
+  | Ast0.DisjExpr(_,exps,_,_) ->
+      List.iter (expression ID old_metas table minus) exps
+  | Ast0.NestExpr(_,exp_dots,_,w,_) ->
+      dots (expression ID old_metas table minus) exp_dots;
+      get_opt (expression ID old_metas table minus) w
+  | Ast0.Edots(_,Some x) | Ast0.Ecircles(_,Some x) | Ast0.Estars(_,Some x) ->
+      expression ID old_metas table minus x
+  | _ -> () (* no metavariable subterms *)
+
+and get_type_name = function
+    Type_cocci.ConstVol(_,ty) | Type_cocci.Pointer(ty)
+  | Type_cocci.FunctionPointer(ty) | Type_cocci.Array(ty) -> get_type_name ty
+  | Type_cocci.MetaType(nm,_,_) -> Some nm
+  | _ -> None
+
+(* --------------------------------------------------------------------- *)
+(* Types *)
+
+and typeC old_metas table minus t =
+  match Ast0.unwrap t with
+    Ast0.ConstVol(cv,ty) -> typeC old_metas table minus ty
+  | Ast0.Pointer(ty,star) -> typeC old_metas table minus ty
+  | Ast0.FunctionPointer(ty,lp1,star,rp1,lp2,params,rp2) ->
+      typeC old_metas table minus ty;
+      parameter_list old_metas table minus params
+  | Ast0.FunctionType(ty,lp1,params,rp1) ->
+      get_opt (typeC old_metas table minus) ty;
+      parameter_list old_metas table minus params
+  | Ast0.Array(ty,lb,size,rb) ->
+      typeC old_metas table minus ty;
+      get_opt (expression ID old_metas table minus) size
+  | Ast0.MetaType(name,_) ->
+      check_table table minus name
+  | Ast0.DisjType(_,types,_,_) ->
+      List.iter (typeC old_metas table minus) types
+  | Ast0.StructUnionName(su,Some id) -> ident GLOBAL old_metas table minus id
+  | Ast0.StructUnionDef(ty,lb,decls,rb) ->
+      typeC old_metas table minus ty;
+      dots (declaration GLOBAL old_metas table minus) decls
+  | Ast0.OptType(ty) | Ast0.UniqueType(ty) ->
+      failwith "unexpected code"
+  | _ -> () (* no metavariable subterms *)
+
+(* --------------------------------------------------------------------- *)
+(* Variable declaration *)
+(* Even if the Cocci program specifies a list of declarations, they are
+   split out into multiple declarations of a single variable each. *)
+
+and declaration context old_metas table minus d =
+  match Ast0.unwrap d with
+    Ast0.Init(stg,ty,id,eq,ini,sem) ->
+      (match Ast0.unwrap ini with
+       Ast0.InitExpr exp ->
+         typeC old_metas table minus ty;
+         ident context old_metas table minus id;
+         expression ID old_metas table minus exp
+      |        _ ->
+         (*
+         if minus
+         then
+           failwith "complex initializer specification not allowed in - code"
+         else*)
+           (typeC old_metas table minus ty;
+            ident context old_metas table minus id;
+            initialiser old_metas table minus ini))
+  | Ast0.UnInit(stg,ty,id,sem) ->
+      typeC old_metas table minus ty; ident context old_metas table minus id
+  | Ast0.MacroDecl(name,lp,args,rp,sem) ->
+      ident ID old_metas table minus name;
+      dots (expression ID old_metas table minus) args
+  | Ast0.TyDecl(ty,sem) -> typeC old_metas table minus ty
+  | Ast0.Typedef(stg,ty,id,sem) ->
+      typeC old_metas table minus ty;
+      typeC old_metas table minus id
+  | Ast0.DisjDecl(_,decls,_,_) ->
+      List.iter (declaration ID old_metas table minus) decls
+  | Ast0.Ddots(_,Some x) -> declaration ID old_metas table minus x
+  | Ast0.Ddots(_,None) -> ()
+  | Ast0.OptDecl(_) | Ast0.UniqueDecl(_) ->
+      failwith "unexpected code"
+
+(* --------------------------------------------------------------------- *)
+(* Initialiser *)
+
+and initialiser old_metas table minus ini =
+  match Ast0.unwrap ini with
+    Ast0.InitExpr(exp) -> expression ID old_metas table minus exp
+  | Ast0.InitList(lb,initlist,rb) ->
+      dots (initialiser old_metas table minus) initlist
+  | Ast0.InitGccDotName(dot,name,eq,ini) ->
+      ident FIELD old_metas table minus name;
+      initialiser old_metas table minus ini
+  | Ast0.InitGccName(name,eq,ini) ->
+      ident FIELD old_metas table minus name;
+      initialiser old_metas table minus ini
+  | Ast0.InitGccIndex(lb,exp,rb,eq,ini) ->
+      expression ID old_metas table minus exp;
+      initialiser old_metas table minus ini
+  | Ast0.InitGccRange(lb,exp1,dots,exp2,rb,eq,ini) ->
+      expression ID old_metas table minus exp1;
+      expression ID old_metas table minus exp2;
+      initialiser old_metas table minus ini
+  | Ast0.Idots(_,Some x) -> initialiser old_metas table minus x
+  | Ast0.OptIni(_) | Ast0.UniqueIni(_) ->
+      failwith "unexpected code"
+  | _ -> () (* no metavariable subterms *)
+
+and initialiser_list old_metas table minus =
+  dots (initialiser old_metas table minus)
+
+(* --------------------------------------------------------------------- *)
+(* Parameter *)
+
+and parameterTypeDef old_metas table minus param =
+  match Ast0.unwrap param with
+    Ast0.Param(ty,id) ->
+      get_opt (ident ID old_metas table minus) id;
+      typeC old_metas table minus ty
+  | Ast0.MetaParam(name,_) ->
+      check_table table minus name
+  | Ast0.MetaParamList(name,None,_) ->
+      check_table table minus name
+  | Ast0.MetaParamList(name,Some lenname,_) ->
+      check_table table minus name;
+      check_table table minus lenname
+  | _ -> () (* no metavariable subterms *)
+
+and parameter_list old_metas table minus =
+  dots (parameterTypeDef old_metas table minus)
+
+(* --------------------------------------------------------------------- *)
+(* Top-level code *)
+
+and statement old_metas table minus s =
+  match Ast0.unwrap s with
+    Ast0.Decl(_,decl) -> declaration ID old_metas table minus decl
+  | Ast0.Seq(lbrace,body,rbrace) -> dots (statement old_metas table minus) body
+  | Ast0.ExprStatement(exp,sem) -> expression ID old_metas table minus exp
+  | Ast0.IfThen(iff,lp,exp,rp,branch,_) ->
+      expression ID old_metas table minus exp;
+      statement old_metas table minus branch
+  | Ast0.IfThenElse(iff,lp,exp,rp,branch1,els,branch2,_) ->
+      expression ID old_metas table minus exp;
+      statement old_metas table minus branch1;
+      statement old_metas table minus branch2
+  | Ast0.While(wh,lp,exp,rp,body,_) ->
+      expression ID old_metas table minus exp;
+      statement old_metas table minus body
+  | Ast0.Do(d,body,wh,lp,exp,rp,sem) ->
+      statement old_metas table minus body;
+      expression ID old_metas table minus exp
+  | Ast0.For(fr,lp,exp1,sem1,exp2,sem2,exp3,rp,body,_) ->
+      get_opt (expression ID old_metas table minus) exp1;
+      get_opt (expression ID old_metas table minus) exp2;
+      get_opt (expression ID old_metas table minus) exp3;
+      statement old_metas table minus body
+  | Ast0.Iterator(nm,lp,args,rp,body,_) ->
+      ident ID old_metas table minus nm;
+      dots (expression ID old_metas table minus) args;
+      statement old_metas table minus body
+  | Ast0.Switch(switch,lp,exp,rp,lb,cases,rb) ->
+      expression ID old_metas table minus exp;
+      dots (case_line old_metas table minus) cases
+  | Ast0.ReturnExpr(ret,exp,sem) -> expression ID old_metas table minus exp
+  | Ast0.MetaStmt(name,_) ->     check_table table minus name
+  | Ast0.MetaStmtList(name,_) -> check_table table minus name
+  | Ast0.Exp(exp) -> expression ID old_metas table minus exp
+  | Ast0.TopExp(exp) -> expression ID old_metas table minus exp
+  | Ast0.Ty(ty) -> typeC old_metas table minus ty
+  | Ast0.Disj(_,rule_elem_dots_list,_,_) ->
+      List.iter (dots (statement old_metas table minus)) rule_elem_dots_list
+  | Ast0.Nest(_,rule_elem_dots,_,w,_) ->
+      dots (statement old_metas table minus) rule_elem_dots;
+      List.iter (whencode (dots (statement old_metas table minus))
+                  (statement old_metas table minus))
+       w
+  | Ast0.Dots(_,x) | Ast0.Circles(_,x) | Ast0.Stars(_,x) ->
+      List.iter
+       (whencode (dots (statement old_metas table minus))
+          (statement old_metas table minus)) x
+  | Ast0.FunDecl(_,fi,name,lp,params,rp,lbrace,body,rbrace) ->
+      ident FN old_metas table minus name;
+      List.iter (fninfo old_metas table minus) fi;
+      parameter_list old_metas table minus params;
+      dots (statement old_metas table minus) body
+  | Ast0.Include(inc,s) -> () (* no metavariables possible *)
+  | Ast0.Define(def,id,_,body) ->
+      ident GLOBAL old_metas table minus id;
+      dots (statement old_metas table minus) body
+  | Ast0.Goto(_,i,_) -> ident ID old_metas table minus i
+  | _ -> () (* no metavariable subterms *)
+
+and fninfo old_metas table minus = function
+    Ast0.FStorage(stg) -> ()
+  | Ast0.FType(ty) -> typeC old_metas table minus ty
+  | Ast0.FInline(inline) -> ()
+  | Ast0.FAttr(attr) -> ()
+
+and whencode notfn alwaysfn = function
+    Ast0.WhenNot a -> notfn a
+  | Ast0.WhenAlways a -> alwaysfn a
+  | Ast0.WhenModifier(_) -> ()
+
+and case_line old_metas table minus c =
+  match Ast0.unwrap c with
+    Ast0.Default(def,colon,code) ->
+      dots (statement old_metas table minus) code
+  | Ast0.Case(case,exp,colon,code) ->
+      dots (statement old_metas table minus) code
+  | Ast0.OptCase(case) -> failwith "unexpected code"
+
+(* --------------------------------------------------------------------- *)
+(* Rules *)
+
+let top_level old_metas table minus t =
+  match Ast0.unwrap t with
+    Ast0.DECL(stmt) -> statement old_metas table minus stmt
+  | Ast0.CODE(stmt_dots) -> dots (statement old_metas table minus) stmt_dots
+  | Ast0.ERRORWORDS(exps) ->
+      List.iter (expression FN old_metas table minus) exps
+  | _ -> () (* no metavariables possible *)
+
+let rule old_metas table minus rules =
+  List.iter (top_level old_metas table minus) rules
+
+(* --------------------------------------------------------------------- *)
+
+let positions table rules =
+  let mcode x =
+    match Ast0.get_pos x with
+      Ast0.MetaPos(name,constraints,_) ->
+       let pos = Ast0.unwrap_mcode name in
+       (find_loop table pos) := true
+    | _ -> () in
+  let option_default = () in
+  let bind x y = () in
+  let donothing r k e = k e in
+  let fn =
+    V0.combiner bind option_default
+      mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
+      mcode
+      donothing donothing donothing donothing donothing donothing
+      donothing donothing donothing donothing donothing donothing donothing
+      donothing donothing in
+
+  List.iter fn.V0.combiner_top_level rules
+
+let dup_positions rules =
+  let mcode x =
+    match Ast0.get_pos x with
+      Ast0.MetaPos(name,constraints,_) ->
+       let pos = Ast0.unwrap_mcode name in [pos]
+    | _ -> [] in
+  let option_default = [] in
+  let bind x y = x@y in
+
+  (* Case for everything that has a disj.
+     Note, no positions on ( | ) of a disjunction, so no need to recurse on
+     these. *)
+
+  let expression r k e =
+    match Ast0.unwrap e with
+      Ast0.DisjExpr(_,explist,_,_) ->
+       List.fold_left Common.union_set option_default
+         (List.map r.V0.combiner_expression explist)
+    | _ -> k e in
+
+  let typeC r k e = (* not sure relevent because "only after iso" *)
+    match Ast0.unwrap e with
+      Ast0.DisjType(_,types,_,_) ->
+       List.fold_left Common.union_set option_default
+         (List.map r.V0.combiner_typeC types)
+    | _ -> k e in
+
+  let declaration r k e =
+    match Ast0.unwrap e with
+      Ast0.DisjDecl(_,decls,_,_) ->
+       List.fold_left Common.union_set option_default
+         (List.map r.V0.combiner_declaration decls)
+    | _ -> k e in
+
+  let statement r k e =
+    match Ast0.unwrap e with
+      Ast0.Disj(_,stmts,_,_) ->
+       List.fold_left Common.union_set option_default
+         (List.map r.V0.combiner_statement_dots stmts)
+    | _ -> k e in
+
+  let donothing r k e = k e in
+  let fn =
+    V0.combiner bind option_default
+      mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
+      mcode
+      donothing donothing donothing donothing donothing donothing
+      donothing expression typeC donothing donothing declaration statement
+      donothing donothing in
+
+  let res =
+    List.sort compare
+      (List.fold_left Common.union_set option_default
+        (List.map fn.V0.combiner_top_level rules)) in
+  let rec loop = function
+      [] | [_] -> ()
+    | ((rule,name) as x)::y::_ when x = y ->
+       failwith (Printf.sprintf "duplicate use of %s.%s" rule name)
+    | _::xs -> loop xs in
+  loop res
+
+(* --------------------------------------------------------------------- *)
+
+let make_table l =
+  let table =
+    (Hashtbl.create(List.length l) :
+       ((string * string), bool ref) Hashtbl.t) in
+  List.iter
+    (function x -> Hashtbl.add table (Ast.get_meta_name x) (ref false)) l;
+  table
+
+let add_to_fresh_table l =
+  List.iter
+    (function x ->
+      let name = Ast.get_meta_name x in Hashtbl.replace fresh_table name ())
+    l
+
+let check_all_marked rname err table after_err =
+  Hashtbl.iter
+    (function name ->
+      function (cell) ->
+       if not (!cell)
+       then
+         let (_,name) = name in
+         warning
+           (Printf.sprintf "%s: %s %s not used %s" rname err name after_err))
+    table
+
+let check_meta rname old_metas inherited_metavars metavars minus plus =
+  let old_metas =
+    List.map (function (_,x) -> x) (List.map Ast.get_meta_name old_metas) in
+  let (fresh,other) =
+    List.partition (function Ast.MetaFreshIdDecl(_,_) -> true | _ -> false)
+      metavars in
+  let (err,other) =
+    List.partition (function Ast.MetaErrDecl(_,_) -> true | _ -> false)
+      other in
+  let (ierr,iother) =
+    List.partition (function Ast.MetaErrDecl(_,_) -> true | _ -> false)
+      inherited_metavars in
+  let fresh_table = make_table fresh in
+  let err_table = make_table (err@ierr) in
+  let other_table = make_table other in
+  let iother_table = make_table iother in
+  add_to_fresh_table fresh;
+  rule old_metas [iother_table;other_table;err_table] true minus;
+  positions [iother_table;other_table] minus;
+  dup_positions minus;
+  check_all_marked rname "metavariable" other_table "in the - or context code";
+  rule old_metas [iother_table;fresh_table;err_table] false plus;
+  check_all_marked rname "fresh identifier metavariable" iother_table
+    "in the -, +, or context code";
+  check_all_marked rname "metavariable" fresh_table "in the + code";
+  check_all_marked rname "error metavariable" err_table ""