diff --git a/infer/models/cpp/src/folly_split.cpp b/infer/models/cpp/src/folly_split.cpp new file mode 100644 index 000000000..a40c9e02c --- /dev/null +++ b/infer/models/cpp/src/folly_split.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include + +namespace folly { + +#define TYPEVAR(Name) \ + struct __attribute__((annotate("__infer_type_var"))) Name {}; + +TYPEVAR(a1); +TYPEVAR(a2); +TYPEVAR(a3); + +template +void split(const Delim& delimiter, + const String& input, + std::vector& out, + bool ignoreEmpty = false) { + out.resize(1); + return; +} + +template __attribute__((annotate("__infer_generic_model"))) void +split(const a1& delimiter, + const a2& input, + std::vector& out, + const bool ignoreEmpty); +} diff --git a/infer/src/IR/Typ.re b/infer/src/IR/Typ.re index 3216b8380..66a56a3b7 100644 --- a/infer/src/IR/Typ.re +++ b/infer/src/IR/Typ.re @@ -1084,6 +1084,61 @@ module Procname = { to_generic_filename pname | _ => to_concrete_filename pname }; + let get_template_args_mapping generic_procname concrete_procname => { + + /** given two template arguments, try to generate mapping from generic ones to concrete ones. */ + let mapping_for_template_args (generic_name, generic_args) (concrete_name, concrete_args) => + switch (generic_args, concrete_args) { + | (Template generic_typs, Template concrete_typs) + when QualifiedCppName.equal generic_name concrete_name => + try ( + `Valid ( + List.fold2_exn + generic_typs + concrete_typs + init::[] + f::( + /* result will be reversed list. Ordering in template mapping doesn't matter so it's ok */ + fun result gtyp ctyp => + switch (gtyp, ctyp) { + | (Some {desc: TVar name}, Some concrete) => [(name, concrete), ...result] + | _ => result + } + ) + ) + ) { + | Invalid_argument _ => + /* fold2_exn throws on length mismatch, we need to handle it */ `Invalid + } + | (NoTemplate, NoTemplate) => `NoTemplate + | _ => `Invalid + }; + let combine_mappings mapping1 mapping2 => + switch (mapping1, mapping2) { + | (`Valid m1, `Valid m2) => `Valid (List.append m1 m2) + | (`NoTemplate, a) + | (a, `NoTemplate) => /* no template is no-op state, simply return the other state */ a + | _ => /* otherwise there is no valid mapping */ `Invalid + }; + let extract_mapping = + fun + | `Invalid + | `NoTemplate => None + | `Valid m => Some m; + let empty_qual = QualifiedCppName.of_qual_string "FIXME" /* TODO we should look at procedure names */; + switch (generic_procname, concrete_procname) { + | (C {template_args: args1}, C {template_args: args2}) /* template function */ => + mapping_for_template_args (empty_qual, args1) (empty_qual, args2) |> extract_mapping + | ( + ObjC_Cpp {template_args: args1, class_name: CppClass name1 class_args1}, + ObjC_Cpp {template_args: args2, class_name: CppClass name2 class_args2} + ) /* template methods/template classes/both */ => + combine_mappings + (mapping_for_template_args (name1, class_args1) (name2, class_args2)) + (mapping_for_template_args (empty_qual, args1) (empty_qual, args2)) |> extract_mapping + | _ => None + } + }; }; diff --git a/infer/src/IR/Typ.rei b/infer/src/IR/Typ.rei index b11b5a380..426b91de5 100644 --- a/infer/src/IR/Typ.rei +++ b/infer/src/IR/Typ.rei @@ -481,6 +481,14 @@ module Procname: { /** get qualifiers of a class owning objc/C++ method */ let objc_cpp_get_class_qualifiers: objc_cpp => QualifiedCppName.t; + + /** Return type substitution that would produce concrete procname from generic procname. Returns None if + such substitution doesn't exist + NOTE: this function doesn't check if such substitution is correct in terms of return + type/function parameters. + NOTE: this function doesn't deal with nested template classes, it only extracts mapping for function + and/or direct parent (class that defines the method) if it exists. */ + let get_template_args_mapping: t => t => option type_subst_t; }; diff --git a/infer/src/clang/CType_decl.ml b/infer/src/clang/CType_decl.ml index 7751e8921..0a04d87fb 100644 --- a/infer/src/clang/CType_decl.ml +++ b/infer/src/clang/CType_decl.ml @@ -110,6 +110,11 @@ and get_record_declaration_type tenv decl = | None -> get_record_struct_type tenv definition_decl and get_record_custom_type tenv definition_decl = + let result = get_record_friend_decl_type tenv definition_decl in + let result = if Option.is_none result then get_record_as_typevar definition_decl else result in + result + +and get_record_friend_decl_type tenv definition_decl = let open Clang_ast_t in match definition_decl with | ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _, _) @@ -117,6 +122,21 @@ and get_record_custom_type tenv definition_decl = Option.map ~f:(qual_type_to_sil_type tenv) (get_translate_as_friend_decl decl_list) | _ -> None +and get_record_as_typevar (definition_decl : Clang_ast_t.decl) = + let open Clang_ast_t in + match definition_decl with + | CXXRecordDecl (decl_info, name_info, _, _, _, _, _, _) -> + let is_infer_typevar = function + | AnnotateAttr {ai_parameters=[_; name; _]} + when String.equal name "__infer_type_var" -> true + | _ -> false in + if List.exists ~f:is_infer_typevar decl_info.di_attributes then + let tname = CAst_utils.get_qualified_name name_info |> QualifiedCppName.to_qual_string in + Some (Typ.mk (TVar tname)) + else + None + | _ -> None + (* We need to take the name out of the type as the struct can be anonymous If tenv is not passed, then template instantiaion information may be incorrect, as it defaults to Typ.NoTemplate *)