[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;
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<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
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;

@ -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<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
particular, this deals with the following issues:
@ -35,12 +61,8 @@ open! IStd;
qualifiers to match
does not match: ["folly","someFunction<int>", "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<T>::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;
};

@ -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} =>

@ -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;
};

@ -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

@ -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

@ -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
[
(

Loading…
Cancel
Save