diff --git a/infer/src/IR/BuiltinDecl.ml b/infer/src/IR/BuiltinDecl.ml index 14bf16cb0..088465727 100644 --- a/infer/src/IR/BuiltinDecl.ml +++ b/infer/src/IR/BuiltinDecl.ml @@ -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 diff --git a/infer/src/IR/Typ.re b/infer/src/IR/Typ.re index 7ee248713..92fcd00d1 100644 --- a/infer/src/IR/Typ.re +++ b/infer/src/IR/Typ.re @@ -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 + }; }; diff --git a/infer/src/IR/Typ.rei b/infer/src/IR/Typ.rei index ce4ab106b..590f4ecbb 100644 --- a/infer/src/IR/Typ.rei +++ b/infer/src/IR/Typ.rei @@ -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. */ diff --git a/infer/src/backend/taint.ml b/infer/src/backend/taint.ml index cd93b6b09..ed527abd3 100644 --- a/infer/src/backend/taint.ml +++ b/infer/src/backend/taint.ml @@ -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 = diff --git a/infer/src/clang/CProcname.ml b/infer/src/clang/CProcname.ml index f03cb20dc..6a4bd4444 100644 --- a/infer/src/clang/CProcname.ml +++ b/infer/src/clang/CProcname.ml @@ -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 ^ diff --git a/infer/tests/codetoanalyze/cpp/errors/Makefile b/infer/tests/codetoanalyze/cpp/errors/Makefile index fdf9f84c7..18c4d66ec 100644 --- a/infer/tests/codetoanalyze/cpp/errors/Makefile +++ b/infer/tests/codetoanalyze/cpp/errors/Makefile @@ -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) \ diff --git a/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model.cpp b/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model.cpp new file mode 100644 index 000000000..cde4cee12 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model.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 +T* GenericModelClass::get() { + return nullptr; +} + +template +T* NonGenericModelClass::get() { + return nullptr; +} + +template +T* genericModelFunction() { + return nullptr; +} + +template +T* nonGenericModelFunction() { + return nullptr; +} + +// explicit instantiations with as template argument +template class GenericModelClass; +template class NonGenericModelClass; +template long long* genericModelFunction(); +template long long* nonGenericModelFunction(); diff --git a/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model.h b/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model.h new file mode 100644 index 000000000..e61b80551 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model.h @@ -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 +struct __attribute__((annotate("__infer_generic_model"))) GenericModelClass { + T* get(); +}; + +template +struct NonGenericModelClass { + T* get(); +}; + +template +__attribute__((annotate("__infer_generic_model"))) T* genericModelFunction(); + +template +T* nonGenericModelFunction(); diff --git a/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model_test.cpp b/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model_test.cpp new file mode 100644 index 000000000..421679463 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/generic_models/generic_model_test.cpp @@ -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 template instantiations, but those are never + instantiated because there is no implementation of templated code in + "generic_model.h" + However, there is instantation for 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 x; + auto ptr = x.get(); + return *ptr; +} + +int nonGenericModelNoNPE() { + NonGenericModelClass x; + auto ptr = x.get(); // this will be skip function + return *ptr; +} + +int genericModelFunctionNPE() { + auto ptr = genericModelFunction(); + return *ptr; +} +int nonGenericModelFunctionNPE() { + auto ptr = nonGenericModelFunction(); // this will be skip function + return *ptr; +} diff --git a/infer/tests/codetoanalyze/cpp/errors/issues.exp b/infer/tests/codetoanalyze/cpp/errors/issues.exp index 2e4541bf2..347c9ab6e 100644 --- a/infer/tests/codetoanalyze/cpp/errors/issues.exp +++ b/infer/tests/codetoanalyze/cpp/errors/issues.exp @@ -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(),return from a call to genericModelFunction] +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_GenericModelClass,start of procedure get,return from a call to GenericModelClass_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_div0, 0, DIVIDE_BY_ZERO, [start of procedure div0]