[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
master
Andrzej Kotulski 8 years ago committed by Facebook Github Bot
parent 7c64d217f2
commit e6ebad394e

@ -8,48 +8,57 @@
*/ */
open! IStd; open! IStd;
type quals_matcher = Str.regexp; type t = list string;
let regexp_string_of_qualifiers quals => Str.quote (String.concat sep::"::" quals) ^ "$"; let empty = [];
let qualifiers_list_matcher quals_list => let append_qualifier quals qual::qual => List.append quals [qual];
(
if (List.is_empty quals_list) { let to_list quals => quals;
"a^" /* regexp that does not match anything */
} else { let of_list quals => quals;
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
};
/* This is simplistic and will give the wrong answer in some cases, eg /* This is simplistic and will give the wrong answer in some cases, eg
"foo<bar::baz<goo>>::someMethod" will get parsed as ["foo<bar", "baz<goo>>", "foo<bar::baz<goo>>::someMethod" will get parsed as ["foo<bar", "baz<goo>>",
"someMethod"]. Ideally, we would keep the list of qualifiers in the procname, which would save us "someMethod"]. Ideally, we would keep the list of qualifiers in the procname, which would save us
from having to properly parse them. */ from having to properly parse them. */
let qualifiers_of_qual_name = { let of_qual_string = {
let class_sep_regex = Str.regexp_string "::"; let class_sep_regex = Str.regexp_string "::";
/* wait until here to define the function so that [class_sep_regex] is only computed once */ /* wait until here to define the function so that [class_sep_regex] is only computed once */
Str.split class_sep_regex Str.split class_sep_regex
}; };
let qualifiers_of_fuzzy_qual_name qual_name => { let to_qual_string = String.concat sep::"::";
/* 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 let module Match = {
names. */ type quals_matcher = Str.regexp;
if (String.contains qual_name '<') { let regexp_string_of_qualifiers quals => Str.quote (String.concat sep::"::" quals) ^ "$";
failwithf "Unexpected template in fuzzy qualified name %s." qual_name 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;

@ -8,6 +8,32 @@
*/ */
open! IStd; open! IStd;
type t;
/** empty qualified name */
let empty: t;
/** attempts to parse the argument into a list::of::possibly::templated<T>::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 /* Module to match qualified C++ procnames "fuzzily", that is up to namescapes and templating. In
particular, this deals with the following issues: particular, this deals with the following issues:
@ -35,12 +61,8 @@ open! IStd;
qualifiers to match qualifiers to match
does not match: ["folly","someFunction<int>", "BAD"] - same as previous example does not match: ["folly","someFunction<int>", "BAD"] - same as previous example
*/ */
type quals_matcher; let module Match: {
type quals_matcher;
let quals_matcher_of_fuzzy_qual_names: list string => quals_matcher; let of_fuzzy_qual_names: list string => quals_matcher;
let match_qualifiers: quals_matcher => t => bool;
let match_qualifiers: quals_matcher => list string => bool; };
/** attempts to parse the argument into a list::of::possibly::templated<T>::qualifiers */
let qualifiers_of_qual_name: string => list string;

@ -882,18 +882,18 @@ let module Procname = {
let pp_set fmt set => Set.iter (fun pname => F.fprintf fmt "%a " pp pname) set; let pp_set fmt set => Set.iter (fun pname => F.fprintf fmt "%a " pp pname) set;
let get_qualifiers pname => let get_qualifiers pname =>
switch pname { switch pname {
| C {name} => QualifiedCppName.qualifiers_of_qual_name name | C {name} => QualifiedCppName.of_qual_string name
| ObjC_Cpp objc_cpp => | ObjC_Cpp objc_cpp =>
List.append QualifiedCppName.of_qual_string (Name.name objc_cpp.class_name) |>
(QualifiedCppName.qualifiers_of_qual_name (Name.name objc_cpp.class_name)) QualifiedCppName.append_qualifier qual::objc_cpp.method_name
[objc_cpp.method_name] | _ => QualifiedCppName.empty
| _ => []
}; };
/** Convert a proc name to a filename */ /** Convert a proc name to a filename */
let to_filename pname => { let to_filename pname => {
/* filenames for clang procs are REVERSED qualifiers with '#' as separator */ /* 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 = let proc_id =
switch pname { switch pname {
| C {mangled} => | C {mangled} =>

@ -424,7 +424,7 @@ let module Procname: {
/** Convert a proc name to a filename. */ /** Convert a proc name to a filename. */
let to_filename: t => string; let to_filename: t => string;
let get_qualifiers: t => list string; let get_qualifiers: t => QualifiedCppName.t;
}; };

@ -14,11 +14,11 @@ module L = Logging
module GlobalsAccesses = SiofTrace.GlobalsAccesses 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) = let is_whitelisted (pname : Typ.Procname.t) =
Typ.Procname.get_qualifiers pname Typ.Procname.get_qualifiers pname
|> QualifiedCppName.match_qualifiers methods_whitelist |> QualifiedCppName.Match.match_qualifiers methods_whitelist
type siof_model = { type siof_model = {
qual_name : string; (** (fuzzy) name of the method, eg "std::ios_base::Init::Init" *) 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 is_modelled =
let models_matcher = let models_matcher =
List.map models ~f:(fun {qual_name} -> qual_name) 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 -> fun pname ->
Typ.Procname.get_qualifiers pname Typ.Procname.get_qualifiers pname
|> QualifiedCppName.match_qualifiers models_matcher |> QualifiedCppName.Match.match_qualifiers models_matcher
module Summary = Summary.Make (struct module Summary = Summary.Make (struct
type summary = SiofDomain.astate type summary = SiofDomain.astate
@ -85,11 +85,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let filter_global_accesses initialized globals = let filter_global_accesses initialized globals =
let initialized_matcher = let initialized_matcher =
Domain.VarNames.elements initialized 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 *) (* gvar \notin initialized, up to some fuzzing *)
let f (gvar, _) = let f (gvar, _) =
QualifiedCppName.qualifiers_of_qual_name (Pvar.to_string gvar) QualifiedCppName.of_qual_string (Pvar.to_string gvar)
|> Fn.non (QualifiedCppName.match_qualifiers initialized_matcher) in |> Fn.non (QualifiedCppName.Match.match_qualifiers initialized_matcher) in
GlobalsAccesses.filter f globals GlobalsAccesses.filter f globals
let add_globals astate outer_loc 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 -> | Call (_, Const (Cfun callee_pname), _, _, _) when is_modelled callee_pname ->
let init = List.find_map_exn models let init = List.find_map_exn models
~f:(fun {qual_name; initialized_globals} -> ~f:(fun {qual_name; initialized_globals} ->
if QualifiedCppName.quals_matcher_of_fuzzy_qual_names [qual_name] if QualifiedCppName.Match.of_fuzzy_qual_names [qual_name]
|> Fn.flip QualifiedCppName.match_qualifiers |> Fn.flip QualifiedCppName.Match.match_qualifiers
(Typ.Procname.get_qualifiers callee_pname) then (Typ.Procname.get_qualifiers callee_pname) then
Some initialized_globals Some initialized_globals
else else

@ -136,16 +136,18 @@ struct
whether method should be translated based on method and class whitelists *) whether method should be translated based on method and class whitelists *)
let is_whitelisted_cpp_method = let is_whitelisted_cpp_method =
let method_matcher = 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 = 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 -> fun qual_method_rev ->
(* either the method is explictely whitelisted, or the whole class is whitelisted *) (* 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 match qual_method_rev with
| _::(_::_ as class_name_rev) -> | _::(_::_ as class_name_rev) ->
(* make sure the class name is not empty; in particular, it cannot be a C function *) (* 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 false

@ -13,8 +13,9 @@ open OUnit2
let test_fuzzy_match = let test_fuzzy_match =
let create_test fuzzy_qual_names qualifiers expected_match _ = let create_test fuzzy_qual_names qualifiers expected_match _ =
let output = let output =
let matcher = QualifiedCppName.quals_matcher_of_fuzzy_qual_names fuzzy_qual_names in let qualified_name = QualifiedCppName.of_list qualifiers in
QualifiedCppName.match_qualifiers matcher 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 assert_equal ~cmp:Bool.equal expected_match output in
[ [
( (

Loading…
Cancel
Save