Coccinelle release-1.0.0-rc11
[bpt/coccinelle.git] / parsing_c / cpp_analysis_c.ml
index 04d0522..e7523fd 100644 (file)
@@ -1,53 +1,54 @@
 (* Yoann Padioleau
- * 
+ *
+ * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
  * Copyright (C) 2009 University of Urbana Champaign
  *
  * 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 Common
 
-open Oset 
+open Oset
 
-open Parser_c 
+open Parser_c
 
 (*****************************************************************************)
 (* Prelude  *)
 (*****************************************************************************)
 (*
- * Is this module make all the tricks used in parsing_hacks and 
- * most definitions in standard.h obsolete ? It depends. In a 
+ * Is this module make all the tricks used in parsing_hacks and
+ * most definitions in standard.h obsolete ? It depends. In a
  * static analysis context we want to be accurate, and so expand
  * all the code that will make our type/callgraph analysis simpler.
  * So we want to expand many macros, based on heuristics in this file.
  * In a transformation context, we want to let the programmer
- * match over certain constructs such as declarator, iterator, 
+ * match over certain constructs such as declarator, iterator,
  * macro_field, etc, and in this case we want to parse as-is.
- * 
+ *
  * What could be done is that some of the analysis performed in this
  * file could then be injected in parsing_hacks, for instance via
  * hints, to make the parse as-is job easier too.
- * 
- * 
+ *
+ *
  *
  * todo: right now I find dangerous macro based on ## and go upward
  * to also include calling macros. But this dangerous macro itself
  * may use other macros that looks ok but that should also be expanded
  * because it defines some entities. So also recurse downward ?
- * 
+ *
  * todo? do analysis a la Astec ? try infer the meaning of the macro
  * from its body but also from its context of use ? Can then
  * do a taxonomy of macro ? not just foreach or declarator but
- * polymorphic function (e.g. MAX), type generator, etc. Cf astec paper 
+ * polymorphic function (e.g. MAX), type generator, etc. Cf astec paper
  * or Ernst cpp study paper ?
- * 
+ *
  *)
 
 (*****************************************************************************)
@@ -62,16 +63,16 @@ type edge = Direct
 type callgraph_macros = (key, node, edge) Ograph_simple.ograph_mutable
 
 let rootname = "__ROOT__"
-  
+
 (*****************************************************************************)
 (* Helpers  *)
 (*****************************************************************************)
 let bodytoks_of_body body =
   match body with
-  | Cpp_token_c.DefineHint _ -> 
+  | Cpp_token_c.DefineHint _ ->
       pr2 "weird, hint in cpp_analysis_c";
       []
-  | Cpp_token_c.DefineBody xs -> 
+  | Cpp_token_c.DefineBody xs ->
       xs
 
 
@@ -82,58 +83,58 @@ let build_empty_set () = new Osetb.osetb Setb.empty
 (* Builder  *)
 (*****************************************************************************)
 
-let build_callgraph_macros xs = 
+let build_callgraph_macros xs =
   let (g: callgraph_macros) = new Ograph_simple.ograph_mutable in
 
   g#add_node rootname (ref []);
 
   (* build nodes *)
-  xs +> List.iter (fun (file, (x, def)) -> 
+  xs +> List.iter (fun (file, (x, def)) ->
     (* todo? if exist already ? *)
     g#add_node x (ref []);
     g#add_arc (rootname, x) Direct;
   );
-  xs +> List.iter (fun (file, (x, def)) -> 
+  xs +> List.iter (fun (file, (x, def)) ->
     let node = g#nodes#find x in
     Common.push2 (file, def) node;
   );
 
   (* build edges *)
-  xs +> List.iter (fun (file, (x, def)) -> 
-    let (s, params, body) = def in 
+  xs +> List.iter (fun (file, (x, def)) ->
+    let (s, params, body) = def in
     let toks = bodytoks_of_body body in
-    toks +> List.iter (fun tok -> 
+    toks +> List.iter (fun tok ->
       match tok with
-      | TIdent (x2,ii) -> 
-          (try 
+      | TIdent (x2,ii) ->
+          (try
             let _ = g#nodes#find x2 in
             g#add_arc (x, x2) Direct;
-          with 
+          with
            Not_found -> ()
           )
-      | _ -> 
+      | _ ->
           ()
     );
-    
+
   );
   g
 
 
 (* ---------------------------------------------------------------------- *)
-let check_no_loop_graph g = 
+let check_no_loop_graph g =
 
-  let self_referential         = ref [] in 
+  let self_referential         = ref [] in
   let macros_in_loop_with_path = ref [] in
 
   let already = Hashtbl.create 101 in
 
   let already_error_msg = Hashtbl.create 101 in
 
-  let rec aux_dfs path xi = 
+  let rec aux_dfs path xi =
     if Hashtbl.mem already xi && List.mem xi path
     then begin
       let node = g#nodes#find xi in
-      let file = 
+      let file =
         match !node with
         | (file, _)::xs -> file
         | [] -> raise Impossible
@@ -142,10 +143,10 @@ let check_no_loop_graph g =
        * we get some __ROOT__ -> CreateMutexA -> CreateMutexA because
        * the macro is self referential. Probably cpp has
        * some special handling of such case and does not expand
-       * recursively. 
-       * 
+       * recursively.
+       *
        *)
-      let is_self_reference = 
+      let is_self_reference =
         match xi::path with
         | x::y::z -> x = y
         | _ -> false
@@ -157,7 +158,7 @@ let check_no_loop_graph g =
         pr2 (spf "path is: %s" (Common.join " -> " (List.rev (xi::path))));
         Common.push2 (xi, path) macros_in_loop_with_path;
       end
-      else begin 
+      else begin
         Common.push2 xi self_referential;
       end
     end else begin
@@ -165,28 +166,28 @@ let check_no_loop_graph g =
       (* f xi path; *)
       let succ = g#successors xi in
       let succ' = succ#tolist +> List.map fst in
-      succ' +> List.iter (fun yi -> 
+      succ' +> List.iter (fun yi ->
           aux_dfs (xi::path) yi
       );
     end
   in
   aux_dfs [] rootname;
   !self_referential, !macros_in_loop_with_path
-  
+
 (* ---------------------------------------------------------------------- *)
 let slice_of_callgraph_macros (g: callgraph_macros) goodnodes =
 
   let (g': callgraph_macros) = new Ograph_simple.ograph_mutable in
 
-  goodnodes#tolist +> List.iter (fun k -> 
+  goodnodes#tolist +> List.iter (fun k ->
     let v = g#nodes#find k in
     g'#add_node k v;
   );
-  goodnodes#tolist +> List.iter (fun k -> 
+  goodnodes#tolist +> List.iter (fun k ->
     let succ = g#successors k in
     let succ = Oset.mapo (fun (k', edge) -> k') (build_empty_set()) succ in
     let inter = succ $**$ goodnodes in
-    inter#tolist +> List.iter (fun k' -> 
+    inter#tolist +> List.iter (fun k' ->
       g'#add_arc (k, k') Direct;
     )
   );
@@ -196,31 +197,31 @@ let slice_of_callgraph_macros (g: callgraph_macros) goodnodes =
 (* Macros expansion  *)
 (*****************************************************************************)
 
-(* get the longuest one ? or the one that contains the dangerous macro ? *) 
+(* get the longuest one ? or the one that contains the dangerous macro ? *)
 let get_single_file_and_def_of_node k v =
   match !v with
   | [] -> raise Impossible
   | [file, def] -> file, def
-  | (file, def)::y::ys -> 
+  | (file, def)::y::ys ->
       pr2 (spf "multiple def for %s but I kept only one" k);
       file, def
 
 module TV = Token_views_c
 
-let (macro_expand: 
-     (string, Cpp_token_c.define_def) Hashtbl.t -> 
+let (macro_expand:
+     (string, Cpp_token_c.define_def) Hashtbl.t ->
       Cpp_token_c.define_def -> Cpp_token_c.define_def) =
- fun current_def def -> 
-  let (s, params, body) = def in 
-  let body' = 
+ fun current_def def ->
+  let (s, params, body) = def in
+  let body' =
     match body with
-    | Cpp_token_c.DefineHint _ -> 
+    | Cpp_token_c.DefineHint _ ->
         body
-    | Cpp_token_c.DefineBody xs -> 
+    | Cpp_token_c.DefineBody xs ->
         (* bugfix: we dont want to evalute the x ## b at this moment.
          * so can not use fix_tokens_cpp in the same we use it
          * to parse C code.
-        let xs' = 
+        let xs' =
           Parsing_hacks.fix_tokens_cpp ~macro_defs:current_def xs
         in
          *)
@@ -229,21 +230,21 @@ let (macro_expand:
         let cleaner = !tokens2 +> Parsing_hacks.filter_cpp_stuff in
         let paren_grouped = TV.mk_parenthised  cleaner in
         Cpp_token_c.apply_macro_defs
-          ~msg_apply_known_macro:(fun s2 -> 
+          ~msg_apply_known_macro:(fun s2 ->
             pr2 (spf "APPLYING: %s in definition of %s" s2 s))
-          ~msg_apply_known_macro_hint:(fun s -> 
+          ~msg_apply_known_macro_hint:(fun s ->
             pr2 "hint")
-          ~evaluate_concatop:false 
+          ~evaluate_concatop:false
           ~inplace_when_single:false
           current_def paren_grouped;
         (* because the before field is used by apply_macro_defs *)
-        tokens2 := TV.rebuild_tokens_extented !tokens2; 
+        tokens2 := TV.rebuild_tokens_extented !tokens2;
 
         (* bugfix *)
         let cleaner = !tokens2 +> Parsing_hacks.filter_cpp_stuff in
 
-        let xs' = 
-          Parsing_hacks.insert_virtual_positions 
+        let xs' =
+          Parsing_hacks.insert_virtual_positions
             (cleaner +> Common.acc_map (fun x -> x.TV.tok))
         in
 
@@ -252,33 +253,33 @@ let (macro_expand:
   (s, params, body')
 
 
-(* work by side effect as both the binding and callgraph are mutable 
+(* work by side effect as both the binding and callgraph are mutable
  * data structure
  *)
 let no_inlining = ref false
 
-let rec (recurse_expand_macro_topological_order: 
-         int -> (string, Cpp_token_c.define_def) Hashtbl.t -> 
+let rec (recurse_expand_macro_topological_order:
+         int -> (string, Cpp_token_c.define_def) Hashtbl.t ->
          callgraph_macros -> unit) =
  fun depth current_def g ->
 
   (* naive: *)
-  if !no_inlining then 
-    g#nodes#tolist +> List.iter (fun (k, v) -> 
-      if k =$= rootname then () 
+  if !no_inlining then
+    g#nodes#tolist +> List.iter (fun (k, v) ->
+      if k =$= rootname then ()
       else
         let def = get_single_file_and_def_of_node k v +> snd in
         Hashtbl.add current_def k def
     )
-  else 
+  else
     let remaining = g#nodes#tolist in
     (match remaining with
     | [] -> raise Impossible
-    | [(k,n)] -> 
+    | [(k,n)] ->
         assert (k = rootname);
         (* end recursion *)
         ()
-    | x::y::xs -> 
+    | x::y::xs ->
         let leafs = (g#leaf_nodes ())#tolist in
         pr2 (spf "step: %d, %s" depth (leafs +> Common.join " "));
 
@@ -292,16 +293,16 @@ let rec (recurse_expand_macro_topological_order:
 
 
         (* little specialisation to avoid useless work *)
-        if depth = 0 
+        if depth = 0
         then begin
-          leafs +> List.iter (fun k -> 
+          leafs +> List.iter (fun k ->
             let node = g#nodes#find k in
             let def = get_single_file_and_def_of_node k node +> snd in
             Hashtbl.add current_def k def
           )
         end else begin
-          let new_defs = 
-            leafs +> List.map (fun k -> 
+          let new_defs =
+            leafs +> List.map (fun k ->
               let node = g#nodes#find k in
               let def = get_single_file_and_def_of_node k node +> snd in
               let def' = macro_expand current_def def in
@@ -320,14 +321,14 @@ let rec (recurse_expand_macro_topological_order:
 (* Macros def analysis  *)
 (*****************************************************************************)
 
-let is_dangerous_macro def = 
-  let (s, params, body) = def in 
+let is_dangerous_macro def =
+  let (s, params, body) = def in
   let toks = bodytoks_of_body body in
 
   (match params, body with
 
   (* ex: APU_DECLARE_DATA *)
-  | Cpp_token_c.NoParam, Cpp_token_c.DefineBody [] -> 
+  | Cpp_token_c.NoParam, Cpp_token_c.DefineBody [] ->
       if s =~ ".*_H_*"
       then false
       else true
@@ -355,33 +356,33 @@ let is_dangerous_macro def =
 
   | _ -> false
   ) ||
-  
 
-  (toks +> List.exists (fun tok -> 
+
+  (toks +> List.exists (fun tok ->
     match tok with
     | TCppConcatOp _ -> true
 
     | Tattribute (ii) -> true
-    | TattributeNoarg (ii) -> true 
+    | TattributeNoarg (ii) -> true
 
 (* FP with local variable.
-    | TIdent (s,ii) -> 
-        s ==~ Parsing_hacks.regexp_annot && not (List.mem s 
+    | TIdent (s,ii) ->
+        s ==~ Parsing_hacks.regexp_annot && not (List.mem s
           ["__FILE__";"__LINE__";"__FUNCTION__"])
 *)
     | _ -> false
   ))
 
 
-let is_trivial_macro def = 
-  let (s, params, body) = def in 
+let is_trivial_macro def =
+  let (s, params, body) = def in
   match params, body with
   | Cpp_token_c.NoParam, Cpp_token_c.DefineBody [Parser_c.TInt _]
 (* no!!! those are not trivial macro, they are dangerous too.
-  | Cpp_token_c.NoParam, Cpp_token_c.DefineBody [] -> 
+  | Cpp_token_c.NoParam, Cpp_token_c.DefineBody [] ->
       true
 *)
-  | _ -> 
+  | _ ->
       false
 
 (*
@@ -389,37 +390,37 @@ let is_trivial_macro def =
             | () when List.exists (function
                  (*| Parser_c.Tattribute _ -> true*)
                  | Parser_c.TCppConcatOp _ -> true
-                 | _ -> false) bodytoks 
+                 | _ -> false) bodytoks
                -> true
             | () -> false
           in
 *)
 
-  
+
 (*****************************************************************************)
 (* Main entry point *)
 (*****************************************************************************)
 
-let extract_dangerous_macros xs = 
+let extract_dangerous_macros xs =
 
   (* prepare initial set of macro definitions to work on *)
-  let all_macros = 
-    xs +> List.map (fun (file, defs) -> 
+  let all_macros =
+    xs +> List.map (fun (file, defs) ->
       defs +> List.map (fun def -> file, def)
     ) +> List.flatten
   in
-  let macros = 
+  let macros =
     all_macros +> Common.exclude(fun (file,(x,def)) -> is_trivial_macro def) in
 
   (* initial set of problematic macros *)
-  let problematic_macros = 
+  let problematic_macros =
     macros +> Common.filter (fun (file, (x, def)) -> is_dangerous_macro def) in
 
 
   (* include the ancestors of problematic macros *)
-  let g = 
+  let g =
     build_callgraph_macros macros in
-  let self_referiential, macros_in_loop_with_path = 
+  let self_referiential, macros_in_loop_with_path =
     check_no_loop_graph g in
 
   Ograph_simple.print_ograph_generic
@@ -427,29 +428,28 @@ let extract_dangerous_macros xs =
     ~str_of_node:(fun k node -> k)
     "/tmp/graph.dot"
     g;
-  let start = 
+  let start =
     problematic_macros +> List.map (fun (file, (x, def)) -> x) +> Common.nub in
-  
-  let finalset = 
-    start +> List.fold_left (fun acc x -> 
+
+  let finalset =
+    start +> List.fold_left (fun acc x ->
       if List.exists (fun y -> fst y = x) macros_in_loop_with_path
         || List.mem x self_referiential
       then begin
         pr2 (spf "PB: ignoring %s macro as it is in a loop" x);
         acc
-        end 
-      else 
+        end
+      else
         let acc = acc#add x in
         let ancestors = g#ancestors x in
         acc $++$ ancestors
     ) (build_empty_set ())
   in
 
-
   (* Now prepare for fixpoint expansion of macros to avoid doing
    * the work in cpp_engine.
    *)
-  let sliced_g = 
+  let sliced_g =
     slice_of_callgraph_macros g finalset
   in
   Ograph_simple.print_ograph_generic
@@ -460,16 +460,16 @@ let extract_dangerous_macros xs =
 
 
   (* do fixpoint expansion *)
-  let (binding: (string, Cpp_token_c.define_def) Hashtbl.t) = 
+  let (binding: (string, Cpp_token_c.define_def) Hashtbl.t) =
     Hashtbl.create 101 in
   (* work by side effects on the hashtbl and graph *)
   recurse_expand_macro_topological_order 0 binding sliced_g;
 
 
+
   (* prepare final result *)
-  let final_macros = 
-    binding +> Common.hash_to_list +> List.map (fun (x, def) -> 
+  let final_macros =
+    binding +> Common.hash_to_list +> List.map (fun (x, def) ->
       let node = g#nodes#find x in
       let file = get_single_file_and_def_of_node x node +> fst in
       (file, (x, def))