[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 tname = Typ.Name.Objc.from_string class_name in
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;
pname

@ -406,7 +406,12 @@ let module Procname = {
[@@deriving compare];
/** 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];
type objc_cpp_method_kind =
| CPPMethod (option string) /** with mangling */
@ -421,7 +426,8 @@ let module Procname = {
method_name: string,
class_name: Name.t,
kind: objc_cpp_method_kind,
template_args: template_spec_info
template_args: template_spec_info,
is_generic_model: bool
}
[@@deriving compare];
@ -491,13 +497,19 @@ let module Procname = {
| None => (None, package_classname)
};
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,
mangled: Some mangled,
template_args
template_args,
is_generic_model
};
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 => {
class_name,
return_type,
@ -507,14 +519,16 @@ let module Procname = {
};
/** 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,
method_name,
kind,
template_args
template_args,
is_generic_model
};
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
};
@ -921,7 +935,13 @@ let module Procname = {
String.concat sep::"#";
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;
/** 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. */
let empty_block: t;
@ -324,7 +324,13 @@ let module Procname: {
let mangled_objc_block: string => t;
/** 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;
/** 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 typename = Typ.Name.Objc.from_string objc_method.classname in
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_source =

@ -38,6 +38,13 @@ let get_template_info tenv (fdi : Clang_ast_t.function_decl_info) : Typ.template
| _ -> None))
| 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 file =
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
| Some m when CGeneral_utils.is_cpp_translation translation_unit_context -> m
| _ -> "" in
let template_info = match function_decl_info_opt, tenv with
| Some (_, function_decl_info), Some t -> get_template_info t function_decl_info
| _ -> Typ.NoTemplate in
let template_info, is_generic_model = match function_decl_info_opt, tenv with
| Some (decl_info, function_decl_info), Some t ->
get_template_info t function_decl_info, is_decl_info_generic_model decl_info
| _ -> Typ.NoTemplate, false in
let mangled = file ^ mangled_name in
if String.is_empty mangled then
Typ.Procname.from_string_c_fun (QualifiedCppName.to_qual_string name)
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 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.CPPMethod mangled in
let template_info = match meth_decl with
| Some (CXXMethodDecl (_, _, _, fdi, _))
| Some (CXXConstructorDecl (_, _, _, fdi, _))
| Some (CXXConversionDecl (_, _, _, fdi, _))
| Some (CXXDestructorDecl (_, _, _, fdi, _)) -> (
match tenv with
let template_info, is_generic_model = match meth_decl with
| Some (CXXMethodDecl (di, _, _, fdi, _))
| Some (CXXConstructorDecl (di, _, _, fdi, _))
| Some (CXXConversionDecl (di, _, _, fdi, _))
| Some (CXXDestructorDecl (di, _, _, fdi, _)) -> (
let templ_info = match tenv with
| 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 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 =
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 =
Config.anonymous_block_prefix ^

@ -19,6 +19,7 @@ SOURCES = \
include_header/include_templ.cpp \
$(wildcard memory_leaks/*.cpp) \
$(wildcard models/*.cpp) \
$(wildcard generic_models/*.cpp) \
$(wildcard mutex/*.cpp) \
$(wildcard npe/*.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, 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/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::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]

Loading…
Cancel
Save