From e6ebad394e4a371dd783c80940ed5b65fc3d00b8 Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Tue, 21 Mar 2017 10:13:35 -0700 Subject: [PATCH] [IR] Make qualified names type safe Summary: Add `QualifiedCppName.t` and some functions to manipulate it. More places will start using this type (such as `Procnames` or `Typ.Name`) in later diff Reviewed By: jberdine Differential Revision: D4738991 fbshipit-source-id: 8f20dd6 --- infer/src/IR/QualifiedCppName.re | 75 +++++++++++-------- infer/src/IR/QualifiedCppName.rei | 40 +++++++--- infer/src/IR/Typ.re | 12 +-- infer/src/IR/Typ.rei | 2 +- infer/src/checkers/Siof.ml | 18 ++--- infer/src/clang/cFrontend_decl.ml | 10 ++- infer/src/unit/clang/QualifiedCppNameTests.ml | 5 +- 7 files changed, 98 insertions(+), 64 deletions(-) diff --git a/infer/src/IR/QualifiedCppName.re b/infer/src/IR/QualifiedCppName.re index d17e293f7..56c4ea771 100644 --- a/infer/src/IR/QualifiedCppName.re +++ b/infer/src/IR/QualifiedCppName.re @@ -8,48 +8,57 @@ */ open! IStd; -type quals_matcher = Str.regexp; - -let regexp_string_of_qualifiers quals => Str.quote (String.concat sep::"::" quals) ^ "$"; - -let qualifiers_list_matcher quals_list => - ( - if (List.is_empty quals_list) { - "a^" /* regexp that does not match anything */ - } else { - List.map f::regexp_string_of_qualifiers quals_list |> String.concat sep::"\\|" - } - ) |> Str.regexp; - -let match_qualifiers matcher quals => { - let normalized_qualifiers = { - /* qual_name may have qualifiers with template parameters - drop them to whitelist all - instantiations */ - let no_template_name s => List.hd_exn (String.split on::'<' s); - List.map f::no_template_name quals - }; - Str.string_match matcher (String.concat sep::"::" normalized_qualifiers) 0 -}; +type t = list string; + +let empty = []; + +let append_qualifier quals qual::qual => List.append quals [qual]; + +let to_list quals => quals; + +let of_list quals => quals; /* This is simplistic and will give the wrong answer in some cases, eg "foo>::someMethod" will get parsed as ["foo>", "someMethod"]. Ideally, we would keep the list of qualifiers in the procname, which would save us from having to properly parse them. */ -let qualifiers_of_qual_name = { +let of_qual_string = { let class_sep_regex = Str.regexp_string "::"; /* wait until here to define the function so that [class_sep_regex] is only computed once */ Str.split class_sep_regex }; -let qualifiers_of_fuzzy_qual_name qual_name => { - /* Fail if we detect templates in the fuzzy name. Template instantiations are not taken into - account when fuzzy matching, and templates may produce wrong results when parsing qualified - names. */ - if (String.contains qual_name '<') { - failwithf "Unexpected template in fuzzy qualified name %s." qual_name +let to_qual_string = String.concat sep::"::"; + +let module Match = { + type quals_matcher = Str.regexp; + let regexp_string_of_qualifiers quals => Str.quote (String.concat sep::"::" quals) ^ "$"; + let qualifiers_list_matcher quals_list => + ( + if (List.is_empty quals_list) { + "a^" /* regexp that does not match anything */ + } else { + List.map f::regexp_string_of_qualifiers quals_list |> String.concat sep::"\\|" + } + ) |> Str.regexp; + let qualifiers_of_fuzzy_qual_name qual_name => { + /* Fail if we detect templates in the fuzzy name. Template instantiations are not taken into + account when fuzzy matching, and templates may produce wrong results when parsing qualified + names. */ + if (String.contains qual_name '<') { + failwithf "Unexpected template in fuzzy qualified name %s." qual_name + }; + of_qual_string qual_name + }; + let of_fuzzy_qual_names fuzzy_qual_names => + List.map fuzzy_qual_names f::qualifiers_of_fuzzy_qual_name |> qualifiers_list_matcher; + let match_qualifiers matcher quals => { + let normalized_qualifiers = { + /* qual_name may have qualifiers with template parameters - drop them to whitelist all + instantiations */ + let no_template_name s => List.hd_exn (String.split on::'<' s); + List.map f::no_template_name quals + }; + Str.string_match matcher (String.concat sep::"::" normalized_qualifiers) 0 }; - qualifiers_of_qual_name qual_name }; - -let quals_matcher_of_fuzzy_qual_names fuzzy_qual_names => - List.map fuzzy_qual_names f::qualifiers_of_fuzzy_qual_name |> qualifiers_list_matcher; diff --git a/infer/src/IR/QualifiedCppName.rei b/infer/src/IR/QualifiedCppName.rei index fcd49254d..8f9cfc2b4 100644 --- a/infer/src/IR/QualifiedCppName.rei +++ b/infer/src/IR/QualifiedCppName.rei @@ -8,6 +8,32 @@ */ open! IStd; +type t; + + +/** empty qualified name */ +let empty: t; + + +/** attempts to parse the argument into a list::of::possibly::templated::qualifiers */ +let of_qual_string: string => t; + + +/** returns qualified name as a string with "::" as a separator between qualifiers */ +let to_qual_string: t => string; + + +/** append qualifier to the end (innermost scope) of the qualified name */ +let append_qualifier: t => qual::string => t; + + +/** returns list of qualifers */ +let to_list: t => list string; + + +/** given list of qualifiers in normal order produce qualified name ["std", "move"] */ +let of_list: list string => t; + /* Module to match qualified C++ procnames "fuzzily", that is up to namescapes and templating. In particular, this deals with the following issues: @@ -35,12 +61,8 @@ open! IStd; qualifiers to match does not match: ["folly","someFunction", "BAD"] - same as previous example */ -type quals_matcher; - -let quals_matcher_of_fuzzy_qual_names: list string => quals_matcher; - -let match_qualifiers: quals_matcher => list string => bool; - - -/** attempts to parse the argument into a list::of::possibly::templated::qualifiers */ -let qualifiers_of_qual_name: string => list string; +let module Match: { + type quals_matcher; + let of_fuzzy_qual_names: list string => quals_matcher; + let match_qualifiers: quals_matcher => t => bool; +}; diff --git a/infer/src/IR/Typ.re b/infer/src/IR/Typ.re index 7f8851ea3..88fd98eba 100644 --- a/infer/src/IR/Typ.re +++ b/infer/src/IR/Typ.re @@ -882,18 +882,18 @@ let module Procname = { let pp_set fmt set => Set.iter (fun pname => F.fprintf fmt "%a " pp pname) set; let get_qualifiers pname => switch pname { - | C {name} => QualifiedCppName.qualifiers_of_qual_name name + | C {name} => QualifiedCppName.of_qual_string name | ObjC_Cpp objc_cpp => - List.append - (QualifiedCppName.qualifiers_of_qual_name (Name.name objc_cpp.class_name)) - [objc_cpp.method_name] - | _ => [] + QualifiedCppName.of_qual_string (Name.name objc_cpp.class_name) |> + QualifiedCppName.append_qualifier qual::objc_cpp.method_name + | _ => QualifiedCppName.empty }; /** Convert a proc name to a filename */ let to_filename pname => { /* filenames for clang procs are REVERSED qualifiers with '#' as separator */ - let get_qual_name_str pname => get_qualifiers pname |> List.rev |> String.concat sep::"#"; + let get_qual_name_str pname => + get_qualifiers pname |> QualifiedCppName.to_list |> List.rev |> String.concat sep::"#"; let proc_id = switch pname { | C {mangled} => diff --git a/infer/src/IR/Typ.rei b/infer/src/IR/Typ.rei index 93c6f65e7..513260e7f 100644 --- a/infer/src/IR/Typ.rei +++ b/infer/src/IR/Typ.rei @@ -424,7 +424,7 @@ let module Procname: { /** Convert a proc name to a filename. */ let to_filename: t => string; - let get_qualifiers: t => list string; + let get_qualifiers: t => QualifiedCppName.t; }; diff --git a/infer/src/checkers/Siof.ml b/infer/src/checkers/Siof.ml index acb8facc3..163a11e1d 100644 --- a/infer/src/checkers/Siof.ml +++ b/infer/src/checkers/Siof.ml @@ -14,11 +14,11 @@ module L = Logging module GlobalsAccesses = SiofTrace.GlobalsAccesses -let methods_whitelist = QualifiedCppName.quals_matcher_of_fuzzy_qual_names Config.siof_safe_methods +let methods_whitelist = QualifiedCppName.Match.of_fuzzy_qual_names Config.siof_safe_methods let is_whitelisted (pname : Typ.Procname.t) = Typ.Procname.get_qualifiers pname - |> QualifiedCppName.match_qualifiers methods_whitelist + |> QualifiedCppName.Match.match_qualifiers methods_whitelist type siof_model = { qual_name : string; (** (fuzzy) name of the method, eg "std::ios_base::Init::Init" *) @@ -40,10 +40,10 @@ let models = List.map ~f:parse_siof_model [ let is_modelled = let models_matcher = List.map models ~f:(fun {qual_name} -> qual_name) - |> QualifiedCppName.quals_matcher_of_fuzzy_qual_names in + |> QualifiedCppName.Match.of_fuzzy_qual_names in fun pname -> Typ.Procname.get_qualifiers pname - |> QualifiedCppName.match_qualifiers models_matcher + |> QualifiedCppName.Match.match_qualifiers models_matcher module Summary = Summary.Make (struct type summary = SiofDomain.astate @@ -85,11 +85,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct let filter_global_accesses initialized globals = let initialized_matcher = Domain.VarNames.elements initialized - |> QualifiedCppName.quals_matcher_of_fuzzy_qual_names in + |> QualifiedCppName.Match.of_fuzzy_qual_names in (* gvar \notin initialized, up to some fuzzing *) let f (gvar, _) = - QualifiedCppName.qualifiers_of_qual_name (Pvar.to_string gvar) - |> Fn.non (QualifiedCppName.match_qualifiers initialized_matcher) in + QualifiedCppName.of_qual_string (Pvar.to_string gvar) + |> Fn.non (QualifiedCppName.Match.match_qualifiers initialized_matcher) in GlobalsAccesses.filter f globals let add_globals astate outer_loc globals = @@ -127,8 +127,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Call (_, Const (Cfun callee_pname), _, _, _) when is_modelled callee_pname -> let init = List.find_map_exn models ~f:(fun {qual_name; initialized_globals} -> - if QualifiedCppName.quals_matcher_of_fuzzy_qual_names [qual_name] - |> Fn.flip QualifiedCppName.match_qualifiers + if QualifiedCppName.Match.of_fuzzy_qual_names [qual_name] + |> Fn.flip QualifiedCppName.Match.match_qualifiers (Typ.Procname.get_qualifiers callee_pname) then Some initialized_globals else diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 4478ffd73..da74a6ecf 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -136,16 +136,18 @@ struct whether method should be translated based on method and class whitelists *) let is_whitelisted_cpp_method = let method_matcher = - QualifiedCppName.quals_matcher_of_fuzzy_qual_names Config.whitelisted_cpp_methods in + QualifiedCppName.Match.of_fuzzy_qual_names Config.whitelisted_cpp_methods in let class_matcher = - QualifiedCppName.quals_matcher_of_fuzzy_qual_names Config.whitelisted_cpp_classes in + QualifiedCppName.Match.of_fuzzy_qual_names Config.whitelisted_cpp_classes in fun qual_method_rev -> (* either the method is explictely whitelisted, or the whole class is whitelisted *) - QualifiedCppName.match_qualifiers method_matcher (List.rev qual_method_rev) || + QualifiedCppName.Match.match_qualifiers method_matcher + (List.rev qual_method_rev |> QualifiedCppName.of_list) || match qual_method_rev with | _::(_::_ as class_name_rev) -> (* make sure the class name is not empty; in particular, it cannot be a C function *) - QualifiedCppName.match_qualifiers class_matcher (List.rev class_name_rev) + QualifiedCppName.Match.match_qualifiers class_matcher + (List.rev class_name_rev |> QualifiedCppName.of_list) | _ -> false diff --git a/infer/src/unit/clang/QualifiedCppNameTests.ml b/infer/src/unit/clang/QualifiedCppNameTests.ml index 4c8d3ee6b..6e41fcc47 100644 --- a/infer/src/unit/clang/QualifiedCppNameTests.ml +++ b/infer/src/unit/clang/QualifiedCppNameTests.ml @@ -13,8 +13,9 @@ open OUnit2 let test_fuzzy_match = let create_test fuzzy_qual_names qualifiers expected_match _ = let output = - let matcher = QualifiedCppName.quals_matcher_of_fuzzy_qual_names fuzzy_qual_names in - QualifiedCppName.match_qualifiers matcher qualifiers in + let qualified_name = QualifiedCppName.of_list qualifiers in + let matcher = QualifiedCppName.Match.of_fuzzy_qual_names fuzzy_qual_names in + QualifiedCppName.Match.match_qualifiers matcher qualified_name in assert_equal ~cmp:Bool.equal expected_match output in [ (