Release coccinelle-0.1.3
[bpt/coccinelle.git] / parsing_c / type_c.ml
diff --git a/parsing_c/type_c.ml b/parsing_c/type_c.ml
new file mode 100644 (file)
index 0000000..cc53391
--- /dev/null
@@ -0,0 +1,309 @@
+(* Copyright (C) 2007, 2008 Yoann Padioleau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License (GPL)
+ * version 2 as published by the Free Software Foundation.
+ * 
+ * This program 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
+ * file license.txt for more details.
+ *)
+
+open Common
+
+open Ast_c
+
+(*****************************************************************************)
+(* Types *)
+(*****************************************************************************)
+
+(* todo? define a new clean fulltype ? as julia did with type_cocci.ml
+ * without the parsing info, with some normalization (for instance have
+ * only structUnionName and enumName, and remove the ParenType), some
+ * abstractions (don't care for instance about name in parameters of
+ * functionType, or size of array), and with new types such as Unknown
+ * or PartialFunctionType (when don't have type of return when infer
+ * the type of function call not based on type of function but on the
+ * type of its arguments).
+ * 
+ * 
+ * 
+ *)
+
+type finalType = Ast_c.fullType
+
+(*****************************************************************************)
+(* expression exp_info annotation vs finalType *)
+(*****************************************************************************)
+
+(* builders, needed because julia added gradually more information in 
+ * the expression reference annotation in ast_c.
+ *)
+
+let make_info x = 
+  (Some x, Ast_c.NotTest)
+
+let make_exp_type t = 
+  (t, Ast_c.NotLocalVar)
+
+let make_info_def t = 
+  make_info (make_exp_type t)
+
+
+
+let noTypeHere = 
+  (None, Ast_c.NotTest)
+
+
+
+
+
+let do_with_type f (t,_test) = 
+  match t with
+  | None -> noTypeHere
+  | Some (t,_local) -> f t
+
+let get_opt_type e = 
+  match Ast_c.get_type_expr e with
+  | Some (t,_), _test -> Some t
+  | None, _test -> None
+
+
+
+(*****************************************************************************)
+(* Normalizers *)
+(*****************************************************************************)
+
+
+let structdef_to_struct_name ty = 
+  match ty with 
+  | qu, (StructUnion (su, sopt, fields), iis) -> 
+      (match sopt,iis with
+      (* todo? but what if correspond to a nested struct def ? *)
+      | Some s , [i1;i2;i3;i4] -> 
+          qu, (StructUnionName (su, s), [i1;i2])
+      | None, _ -> 
+          ty
+      | x -> raise Impossible
+      )
+  | _ -> raise Impossible
+
+
+(*****************************************************************************)
+(* Helpers *)
+(*****************************************************************************)
+
+
+let type_of_function (def,ii) = 
+  let ftyp = def.f_type in 
+
+  (* could use the info in the 'ii' ? *)
+
+  let fake = Ast_c.fakeInfo (Common.fake_parse_info) in
+  let fake_oparen = Ast_c.rewrap_str "(" fake in
+  let fake = Ast_c.fakeInfo (Common.fake_parse_info) in
+  let fake_cparen = Ast_c.rewrap_str ")" fake in
+
+  Ast_c.nQ, (FunctionType ftyp, [fake_oparen;fake_cparen])
+
+
+(* pre: only a single variable *)
+let type_of_decl decl = 
+  match decl with
+  | Ast_c.DeclList (xs,ii1) -> 
+      (match xs with
+      | [] -> raise Impossible
+          
+      (* todo? for other xs ? *)
+      | (x,ii2)::xs -> 
+          let {v_namei = _var; v_type = v_type;
+               v_storage = (_storage,_inline)} = x in
+
+          (* TODO normalize ? what if nested structure definition ? *)
+          v_type
+      )
+  | Ast_c.MacroDecl _ -> 
+      pr2_once "not handling MacroDecl type yet";
+      raise Todo
+
+
+
+(* pre: it is indeed a struct def decl, and only a single variable *)
+let structdef_of_decl decl = 
+
+  match decl with
+  | Ast_c.DeclList (xs,ii1) -> 
+      (match xs with
+      | [] -> raise Impossible
+          
+      (* todo? for other xs ? *)
+      | (x,ii2)::xs -> 
+          let {v_namei = var; v_type = v_type;
+               v_storage = (storage,inline)} = x in
+          
+          (match Ast_c.unwrap_typeC v_type with
+          | Ast_c.StructUnion (su, _must_be_some, fields) -> 
+              (su, fields)
+          | _ -> raise Impossible
+          )
+      )
+  | Ast_c.MacroDecl _ -> raise Impossible
+
+
+
+
+(*****************************************************************************)
+(* Type builder  *)
+(*****************************************************************************)
+
+let (fake_function_type: 
+   fullType option -> argument wrap2 list -> fullType option) = 
+ fun rettype args ->
+
+  let fake = Ast_c.fakeInfo (Common.fake_parse_info) in
+  let fake_oparen = Ast_c.rewrap_str "(" fake in
+  let fake = Ast_c.fakeInfo (Common.fake_parse_info) in
+  let fake_cparen = Ast_c.rewrap_str ")" fake in
+
+  let (tyargs: parameterType wrap2 list) = 
+    args +> Common.map_filter (fun (arg,ii) -> 
+      match arg with
+      | Left e -> 
+          (match Ast_c.get_onlytype_expr e with
+          | Some ft -> 
+              let paramtype = 
+                (false, None, ft), []
+              in
+              Some (paramtype, ii)
+          | None -> None
+          )
+      | Right _ -> None
+    )
+  in
+  if List.length args <> List.length tyargs
+  then None
+  else
+    rettype +> Common.map_option (fun rettype -> 
+      let (ftyp: functionType) = (rettype, (tyargs, (false,[]))) in
+      let (t: fullType) = 
+        (Ast_c.nQ, (FunctionType ftyp, [fake_oparen;fake_cparen]))  
+      in
+      t
+    )
+
+
+(*****************************************************************************)
+(* Typing rules *)
+(*****************************************************************************)
+
+
+(* todo: the rules are far more complex, but I prefer to simplify for now.
+ * todo: should take operator as a parameter.
+ * 
+ * todo: Also need handle pointer arithmetic! the type of 'pt + 2'
+ * is still the type of pt. cf parsing_cocci/type_infer.ml
+ * 
+ * (* pad: in pointer arithmetic, as in ptr+1, the lub must be ptr *)
+ * | (T.Pointer(ty1),T.Pointer(ty2)) ->
+ * T.Pointer(loop(ty1,ty2))
+ * | (ty1,T.Pointer(ty2)) -> T.Pointer(ty2)
+ * | (T.Pointer(ty1),ty2) -> T.Pointer(ty1)
+ * 
+*)
+let lub t1 t2 = 
+  let ftopt = 
+    match t1, t2 with
+    | None, None -> None
+    | Some t, None -> Some t
+    | None, Some t -> Some t
+    (* check equal ? no cos can have pointer arithmetic so t2 can be <> t1 
+     * 
+     * todo: right now I favor the first term because usually pointer 
+     * arithmetic are written with the pointer in the first position.
+     * 
+     * Also when an expression contain a typedef, as in 
+     * 'dma_addr + 1' where dma_addr was declared as a varialbe
+     * of type dma_addr_t, then again I want to have in the lub
+     * the typedef and it is often again in the first position.
+     * 
+    *)
+    | Some t1, Some t2 -> 
+        let t1bis = Ast_c.unwrap_typeC t1 in
+        let t2bis = Ast_c.unwrap_typeC t2 in
+        (match t1bis, t2bis with
+        (* todo, Pointer, Typedef, etc *)
+        | _, _ -> Some t1
+        )
+
+  in
+  match ftopt with
+  | None -> None, Ast_c.NotTest
+  | Some ft ->  Some (ft, Ast_c.NotLocalVar), Ast_c.NotTest
+
+
+
+(*****************************************************************************)
+(* type lookup *)
+(*****************************************************************************)
+
+(* old: was using some nested find_some, but easier use ref 
+ * update: handling union (used a lot in sparse)
+ * note: it is independent of the environment. 
+*)
+let (type_field: 
+  string -> (Ast_c.structUnion * Ast_c.structType) -> Ast_c.fullType) = 
+ fun fld (su, fields) -> 
+
+  let res = ref [] in
+    
+  let rec aux_fields fields = 
+    fields +> List.iter (fun x -> 
+      match Ast_c.unwrap x with
+      | DeclarationField (FieldDeclList (onefield_multivars, iiptvirg)) -> 
+          onefield_multivars +> List.iter (fun fieldkind -> 
+            match Ast_c.unwrap (Ast_c.unwrap fieldkind) with
+            | Simple (Some s, t) | BitField (Some s, t, _) -> 
+                if s = fld 
+                then Common.push2 t res
+                else ()
+                  
+            | Simple (None, t) -> 
+                (match Ast_c.unwrap_typeC t with
+
+                (* union *)
+                | StructUnion (Union, _, fields) -> 
+                    aux_fields fields
+                      
+                (* Special case of nested structure definition inside 
+                 * structure without associated field variable as in 
+                 * struct top = { ... struct xx { int subfield1; ... }; ... }
+                 * cf sparse source, where can access subfields directly.
+                 * It can also be used in conjunction with union.
+                 *)
+                | StructUnion (Struct, _, fields) -> 
+                    aux_fields fields
+                      
+                | _ -> ()
+                )
+            | _ -> ()
+          )
+            
+      | EmptyField -> ()
+      | MacroStructDeclTodo -> pr2_once "DeclTodo"; ()
+          
+      | CppDirectiveStruct _
+      | IfdefStruct _ -> pr2_once "StructCpp"; 
+    )
+  in
+  aux_fields fields;
+  match !res with
+  | [t] -> t
+  | [] -> 
+      raise Not_found
+  | x::y::xs -> 
+      pr2 ("MultiFound field: " ^ fld) ;
+      x
+    
+
+