[C++] Introduce mechanism to write generic models

Summary:
Make it possible to write one model which will be used by all template instantiations.
There is one big missing piece: infer never tries to do template instantiation by itself. With current code, it's possible to use generic models
as long as header contains `__infer_generic_model` annotation (see the test as an example).
This is not viable to modify all headers with this annotation hence infer will try to do template instantiation for generic models in later diffs.

Reviewed By: jberdine

Differential Revision: D4826365

fbshipit-source-id: 2233e42
master
Andrzej Kotulski 8 years ago committed by Facebook Github Bot
parent a90525f627
commit 5503487704

@ -25,7 +25,7 @@ let create_objc_class_method class_name method_name =
let method_kind = Typ.Procname.ObjCClassMethod in let method_kind = Typ.Procname.ObjCClassMethod in
let tname = Typ.Name.Objc.from_string class_name in let tname = Typ.Name.Objc.from_string class_name in
let pname = Typ.Procname.ObjC_Cpp let pname = Typ.Procname.ObjC_Cpp
(Typ.Procname.objc_cpp tname method_name method_kind Typ.NoTemplate) in (Typ.Procname.objc_cpp tname method_name method_kind Typ.NoTemplate ~is_generic_model:false) in
register pname; register pname;
pname pname

@ -406,7 +406,12 @@ let module Procname = {
[@@deriving compare]; [@@deriving compare];
/** Type of c procedure names. */ /** Type of c procedure names. */
type c = {name: QualifiedCppName.t, mangled: option string, template_args: template_spec_info} type c = {
name: QualifiedCppName.t,
mangled: option string,
template_args: template_spec_info,
is_generic_model: bool
}
[@@deriving compare]; [@@deriving compare];
type objc_cpp_method_kind = type objc_cpp_method_kind =
| CPPMethod (option string) /** with mangling */ | CPPMethod (option string) /** with mangling */
@ -421,7 +426,8 @@ let module Procname = {
method_name: string, method_name: string,
class_name: Name.t, class_name: Name.t,
kind: objc_cpp_method_kind, kind: objc_cpp_method_kind,
template_args: template_spec_info template_args: template_spec_info,
is_generic_model: bool
} }
[@@deriving compare]; [@@deriving compare];
@ -491,13 +497,19 @@ let module Procname = {
| None => (None, package_classname) | None => (None, package_classname)
}; };
let split_typename typename => split_classname (Name.name typename); let split_typename typename => split_classname (Name.name typename);
let c (name: QualifiedCppName.t) (mangled: string) (template_args: template_spec_info) => { let c name mangled template_args is_generic_model::is_generic_model => {
name, name,
mangled: Some mangled, mangled: Some mangled,
template_args template_args,
is_generic_model
}; };
let from_string_c_fun (name: string) => let from_string_c_fun (name: string) =>
C {name: QualifiedCppName.of_qual_string name, mangled: None, template_args: NoTemplate}; C {
name: QualifiedCppName.of_qual_string name,
mangled: None,
template_args: NoTemplate,
is_generic_model: false
};
let java class_name return_type method_name parameters kind => { let java class_name return_type method_name parameters kind => {
class_name, class_name,
return_type, return_type,
@ -507,14 +519,16 @@ let module Procname = {
}; };
/** Create an objc procedure name from a class_name and method_name. */ /** Create an objc procedure name from a class_name and method_name. */
let objc_cpp class_name method_name kind template_args => { let objc_cpp class_name method_name kind template_args is_generic_model::is_generic_model => {
class_name, class_name,
method_name, method_name,
kind, kind,
template_args template_args,
is_generic_model
}; };
let get_default_objc_class_method objc_class => { let get_default_objc_class_method objc_class => {
let objc_cpp = objc_cpp objc_class "__find_class_" ObjCInternalMethod NoTemplate; let objc_cpp =
objc_cpp objc_class "__find_class_" ObjCInternalMethod NoTemplate is_generic_model::false;
ObjC_Cpp objc_cpp ObjC_Cpp objc_cpp
}; };
@ -921,7 +935,13 @@ let module Procname = {
String.concat sep::"#"; String.concat sep::"#";
Escape.escape_filename @@ SourceFile.append_crc_cutoff proc_id Escape.escape_filename @@ SourceFile.append_crc_cutoff proc_id
}; };
let to_filename = to_concrete_filename; let to_filename pname =>
switch pname {
| C {is_generic_model}
| ObjC_Cpp {is_generic_model} when Bool.equal is_generic_model true =>
to_generic_filename pname
| _ => to_concrete_filename pname
};
}; };

@ -269,7 +269,7 @@ let module Procname: {
let module Set: PrettyPrintable.PPSet with type elt = t; let module Set: PrettyPrintable.PPSet with type elt = t;
/** Create a C procedure name from plain and mangled name. */ /** Create a C procedure name from plain and mangled name. */
let c: QualifiedCppName.t => string => template_spec_info => c; let c: QualifiedCppName.t => string => template_spec_info => is_generic_model::bool => c;
/** Empty block name. */ /** Empty block name. */
let empty_block: t; let empty_block: t;
@ -324,7 +324,13 @@ let module Procname: {
let mangled_objc_block: string => t; let mangled_objc_block: string => t;
/** Create an objc procedure name from a class_name and method_name. */ /** Create an objc procedure name from a class_name and method_name. */
let objc_cpp: Name.t => string => objc_cpp_method_kind => template_spec_info => objc_cpp; let objc_cpp:
Name.t =>
string =>
objc_cpp_method_kind =>
template_spec_info =>
is_generic_model::bool =>
objc_cpp;
let get_default_objc_class_method: Name.t => t; let get_default_objc_class_method: Name.t => t;
/** Get the class name of a Objective-C/C++ procedure name. */ /** Get the class name of a Objective-C/C++ procedure name. */

@ -275,7 +275,8 @@ let objc_method_to_procname objc_method =
let method_kind = Typ.Procname.objc_method_kind_of_bool (not objc_method.is_static) in let method_kind = Typ.Procname.objc_method_kind_of_bool (not objc_method.is_static) in
let typename = Typ.Name.Objc.from_string objc_method.classname in let typename = Typ.Name.Objc.from_string objc_method.classname in
Typ.Procname.ObjC_Cpp Typ.Procname.ObjC_Cpp
(Typ.Procname.objc_cpp typename objc_method.method_name method_kind Typ.NoTemplate) (Typ.Procname.objc_cpp typename objc_method.method_name method_kind
Typ.NoTemplate ~is_generic_model:false)
let taint_spec_to_taint_info taint_spec = let taint_spec_to_taint_info taint_spec =
let taint_source = let taint_source =

@ -38,6 +38,13 @@ let get_template_info tenv (fdi : Clang_ast_t.function_decl_info) : Typ.template
| _ -> None)) | _ -> None))
| None -> Typ.NoTemplate | None -> Typ.NoTemplate
let is_decl_info_generic_model {Clang_ast_t.di_attributes} =
let f = function
| Clang_ast_t.AnnotateAttr {ai_parameters=[_; name; _]}
when String.equal name "__infer_generic_model" -> true
| _ -> false in
List.exists ~f di_attributes
let mk_c_function translation_unit_context ?tenv name function_decl_info_opt = let mk_c_function translation_unit_context ?tenv name function_decl_info_opt =
let file = let file =
match function_decl_info_opt with match function_decl_info_opt with
@ -55,14 +62,15 @@ let mk_c_function translation_unit_context ?tenv name function_decl_info_opt =
let mangled_name = match mangled_opt with let mangled_name = match mangled_opt with
| Some m when CGeneral_utils.is_cpp_translation translation_unit_context -> m | Some m when CGeneral_utils.is_cpp_translation translation_unit_context -> m
| _ -> "" in | _ -> "" in
let template_info = match function_decl_info_opt, tenv with let template_info, is_generic_model = match function_decl_info_opt, tenv with
| Some (_, function_decl_info), Some t -> get_template_info t function_decl_info | Some (decl_info, function_decl_info), Some t ->
| _ -> Typ.NoTemplate in get_template_info t function_decl_info, is_decl_info_generic_model decl_info
| _ -> Typ.NoTemplate, false in
let mangled = file ^ mangled_name in let mangled = file ^ mangled_name in
if String.is_empty mangled then if String.is_empty mangled then
Typ.Procname.from_string_c_fun (QualifiedCppName.to_qual_string name) Typ.Procname.from_string_c_fun (QualifiedCppName.to_qual_string name)
else else
Typ.Procname.C (Typ.Procname.c name mangled template_info) Typ.Procname.C (Typ.Procname.c name mangled template_info ~is_generic_model)
let mk_cpp_method ?tenv class_name method_name ?meth_decl mangled = let mk_cpp_method ?tenv class_name method_name ?meth_decl mangled =
let open Clang_ast_t in let open Clang_ast_t in
@ -71,22 +79,30 @@ let mk_cpp_method ?tenv class_name method_name ?meth_decl mangled =
Typ.Procname.CPPConstructor (mangled, xmdi_is_constexpr) Typ.Procname.CPPConstructor (mangled, xmdi_is_constexpr)
| _ -> | _ ->
Typ.Procname.CPPMethod mangled in Typ.Procname.CPPMethod mangled in
let template_info = match meth_decl with let template_info, is_generic_model = match meth_decl with
| Some (CXXMethodDecl (_, _, _, fdi, _)) | Some (CXXMethodDecl (di, _, _, fdi, _))
| Some (CXXConstructorDecl (_, _, _, fdi, _)) | Some (CXXConstructorDecl (di, _, _, fdi, _))
| Some (CXXConversionDecl (_, _, _, fdi, _)) | Some (CXXConversionDecl (di, _, _, fdi, _))
| Some (CXXDestructorDecl (_, _, _, fdi, _)) -> ( | Some (CXXDestructorDecl (di, _, _, fdi, _)) -> (
match tenv with let templ_info = match tenv with
| Some t -> get_template_info t fdi | Some t -> get_template_info t fdi
| None -> Typ.NoTemplate | None -> Typ.NoTemplate in
let is_gen_model = is_decl_info_generic_model di ||
(* read whether parent class is annoatated as generic model *)
di.di_parent_pointer
|> Option.value_map ~f:CAst_utils.get_decl ~default:None
|> Option.map ~f:Clang_ast_proj.get_decl_tuple
|> Option.value_map ~f:is_decl_info_generic_model ~default:false in
templ_info, is_gen_model
) )
| _ -> Typ.NoTemplate in | _ -> Typ.NoTemplate, false in
Typ.Procname.ObjC_Cpp Typ.Procname.ObjC_Cpp
(Typ.Procname.objc_cpp class_name method_name method_kind template_info) (Typ.Procname.objc_cpp class_name method_name method_kind template_info ~is_generic_model)
let mk_objc_method class_typename method_name method_kind = let mk_objc_method class_typename method_name method_kind =
Typ.Procname.ObjC_Cpp Typ.Procname.ObjC_Cpp
(Typ.Procname.objc_cpp class_typename method_name method_kind Typ.NoTemplate) (Typ.Procname.objc_cpp class_typename method_name method_kind Typ.NoTemplate
~is_generic_model:false)
let block_procname_with_index defining_proc i = let block_procname_with_index defining_proc i =
Config.anonymous_block_prefix ^ Config.anonymous_block_prefix ^

@ -19,6 +19,7 @@ SOURCES = \
include_header/include_templ.cpp \ include_header/include_templ.cpp \
$(wildcard memory_leaks/*.cpp) \ $(wildcard memory_leaks/*.cpp) \
$(wildcard models/*.cpp) \ $(wildcard models/*.cpp) \
$(wildcard generic_models/*.cpp) \
$(wildcard mutex/*.cpp) \ $(wildcard mutex/*.cpp) \
$(wildcard npe/*.cpp) \ $(wildcard npe/*.cpp) \
$(wildcard numeric/*.cpp) \ $(wildcard numeric/*.cpp) \

@ -0,0 +1,35 @@
/*
* 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 "generic_model.h"
template <class T>
T* GenericModelClass<T>::get() {
return nullptr;
}
template <class T>
T* NonGenericModelClass<T>::get() {
return nullptr;
}
template <class T>
T* genericModelFunction() {
return nullptr;
}
template <class T>
T* nonGenericModelFunction() {
return nullptr;
}
// explicit instantiations with <long long> as template argument
template class GenericModelClass<long long>;
template class NonGenericModelClass<long long>;
template long long* genericModelFunction<long long>();
template long long* nonGenericModelFunction<long long>();

@ -0,0 +1,23 @@
/*
* 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.
*/
template <class T>
struct __attribute__((annotate("__infer_generic_model"))) GenericModelClass {
T* get();
};
template <class T>
struct NonGenericModelClass {
T* get();
};
template <class T>
__attribute__((annotate("__infer_generic_model"))) T* genericModelFunction();
template <class T>
T* nonGenericModelFunction();

@ -0,0 +1,37 @@
/*
* 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 "generic_model.h"
/* This code uses <int> template instantiations, but those are never
instantiated because there is no implementation of templated code in
"generic_model.h"
However, there is instantation for <long long> coming from
"generic_model.cpp". If generic model is truly generic, then infer will pick
up specs for those and use them
*/
int genericModelNPE() {
GenericModelClass<int> x;
auto ptr = x.get();
return *ptr;
}
int nonGenericModelNoNPE() {
NonGenericModelClass<int> x;
auto ptr = x.get(); // this will be skip function
return *ptr;
}
int genericModelFunctionNPE() {
auto ptr = genericModelFunction<int>();
return *ptr;
}
int nonGenericModelFunctionNPE() {
auto ptr = nonGenericModelFunction<int>(); // this will be skip function
return *ptr;
}

@ -7,6 +7,8 @@ codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, malloc_memory_leak_is_reported, 0,
codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, memcpy_spec_is_found, 3, NULL_DEREFERENCE, [start of procedure memcpy_spec_is_found()] codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, memcpy_spec_is_found, 3, NULL_DEREFERENCE, [start of procedure memcpy_spec_is_found()]
codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, resource_leak_is_reported, 0, RESOURCE_LEAK, [start of procedure resource_leak_is_reported()] codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, resource_leak_is_reported, 0, RESOURCE_LEAK, [start of procedure resource_leak_is_reported()]
codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, resource_leak_is_reported, 0, RETURN_VALUE_IGNORED, [start of procedure resource_leak_is_reported()] codetoanalyze/cpp/errors/c_tests/c_bugs.cpp, resource_leak_is_reported, 0, RETURN_VALUE_IGNORED, [start of procedure resource_leak_is_reported()]
codetoanalyze/cpp/errors/generic_models/generic_model_test.cpp, genericModelFunctionNPE, 2, NULL_DEREFERENCE, [start of procedure genericModelFunctionNPE(),start of procedure genericModelFunction<long_long>(),return from a call to genericModelFunction<long_long>]
codetoanalyze/cpp/errors/generic_models/generic_model_test.cpp, genericModelNPE, 3, NULL_DEREFERENCE, [start of procedure genericModelNPE(),start of procedure GenericModelClass,return from a call to GenericModelClass<int>_GenericModelClass,start of procedure get,return from a call to GenericModelClass<long_long>_get]
codetoanalyze/cpp/errors/include_header/header.h, header::A_div0, 0, DIVIDE_BY_ZERO, [start of procedure div0] codetoanalyze/cpp/errors/include_header/header.h, header::A_div0, 0, DIVIDE_BY_ZERO, [start of procedure div0]
codetoanalyze/cpp/errors/include_header/header.h, header::div0_fun, 0, DIVIDE_BY_ZERO, [start of procedure header::div0_fun()] codetoanalyze/cpp/errors/include_header/header.h, header::div0_fun, 0, DIVIDE_BY_ZERO, [start of procedure header::div0_fun()]
codetoanalyze/cpp/errors/include_header/header2.h, header2::B<A>_div0, 0, DIVIDE_BY_ZERO, [start of procedure div0] codetoanalyze/cpp/errors/include_header/header2.h, header2::B<A>_div0, 0, DIVIDE_BY_ZERO, [start of procedure div0]

Loading…
Cancel
Save