diff --git a/infer/src/clang/cFrontend.ml b/infer/src/clang/cFrontend.ml index ec69644d6..00c629697 100644 --- a/infer/src/clang/cFrontend.ml +++ b/infer/src/clang/cFrontend.ml @@ -83,8 +83,8 @@ let rec translate_one_declaration tenv cg cfg namespace parent_dec dec = | Some ptr -> Ast_utils.get_decl ptr | None -> Some parent_dec in (match class_decl with - | Some CXXRecordDecl(_, name_info, opt_type, _, _, _, _, _) -> - let class_name = CTypes_decl.get_record_name opt_type name_info in + | Some (CXXRecordDecl _ as d)-> + let class_name = CTypes_decl.get_record_name d in let curr_class = CContext.ContextCls(class_name, None, []) in if !CFrontend_config.testing_mode then CMethod_declImpl.process_methods tenv cg cfg curr_class namespace [dec] diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index c17cf9a28..158be6f56 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -273,6 +273,32 @@ struct CFrontend_config.sil_types_map := Clang_ast_main.PointerMap.add type_ptr sil_type !CFrontend_config.sil_types_map + let get_type type_ptr = + try + Some (Clang_ast_main.PointerMap.find type_ptr !CFrontend_config.pointer_type_index) + with Not_found -> Printing.log_stats "type with pointer %s not found\n" type_ptr; None + + let get_desugared_type type_ptr = + let typ_opt = get_type type_ptr in + match typ_opt with + | Some typ -> + let type_info = Clang_ast_proj.get_type_tuple typ in + (match type_info.Clang_ast_t.ti_desugared_type with + | Some ptr -> get_type ptr + | _ -> typ_opt) + | _ -> typ_opt + + let get_decl_from_typ_ptr typ_ptr = + let typ_opt = get_desugared_type typ_ptr in + let typ = match typ_opt with Some t -> t | _ -> assert false in + let get_decl_or_fail decl_ptr = match get_decl decl_ptr with + | Some d -> d + | None -> assert false in + (* it needs extending to handle objC types *) + match typ with + | Clang_ast_t.RecordType (ti, decl_ptr) -> get_decl_or_fail decl_ptr + | _ -> assert false + end (* Global counter for anonymous block*) diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 317efcbd2..73953cf9f 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -82,6 +82,14 @@ sig (** creates a string to append to a name from a list of qualifiers to a name *) val get_qualifier_string : Clang_ast_t.named_decl_info -> string + val get_type : Clang_ast_t.pointer -> Clang_ast_t.c_type option + + val get_desugared_type : Clang_ast_t.pointer -> Clang_ast_t.c_type option + + (** returns declaration of the type for certain types and crashes for others + NOTE: this function needs extending to handle objC types *) + val get_decl_from_typ_ptr : Clang_ast_t.type_ptr -> Clang_ast_t.decl + end module General_utils : diff --git a/infer/src/clang/cType_to_sil_type.ml b/infer/src/clang/cType_to_sil_type.ml index f8ffc2503..ece15bcb5 100644 --- a/infer/src/clang/cType_to_sil_type.ml +++ b/infer/src/clang/cType_to_sil_type.ml @@ -116,8 +116,8 @@ and sil_type_of_c_type translate_decl tenv c_type = | AttributedType type_info -> (match type_info.Clang_ast_t.ti_desugared_type with | Some type_ptr -> - (match Clang_ast_main.PointerMap.find type_ptr !CFrontend_config.pointer_type_index with - | ObjCObjectPointerType (type_info', type_ptr') -> + (match Ast_utils.get_type type_ptr with + | Some ObjCObjectPointerType (type_info', type_ptr') -> let typ = qual_type_ptr_to_sil_type translate_decl tenv type_ptr' in CTypes.sil_type_of_attr_pointer_type typ type_info.Clang_ast_t.ti_raw | _ -> qual_type_ptr_to_sil_type translate_decl tenv type_ptr) @@ -152,15 +152,12 @@ and qual_type_ptr_to_sil_type translate_decl tenv type_ptr = try Clang_ast_main.PointerMap.find type_ptr !CFrontend_config.sil_types_map with Not_found -> - try - let c_type = Clang_ast_main.PointerMap.find type_ptr !CFrontend_config.pointer_type_index in - let sil_type = sil_type_of_c_type translate_decl tenv c_type in - Ast_utils.update_sil_types_map type_ptr sil_type; - sil_type - with Not_found -> - Printing.log_err "Warning: Type pointer %s not found." - (Clang_ast_j.string_of_pointer type_ptr); - Sil.Tvoid + match Ast_utils.get_type type_ptr with + | Some c_type -> + let sil_type = sil_type_of_c_type translate_decl tenv c_type in + Ast_utils.update_sil_types_map type_ptr sil_type; + sil_type + | _ -> Sil.Tvoid and qual_type_to_sil_type translate_decl tenv qt = let type_ptr = qt.Clang_ast_t.qt_type_ptr in diff --git a/infer/src/clang/cTypes.ml b/infer/src/clang/cTypes.ml index dd211e8a1..1c347ba83 100644 --- a/infer/src/clang/cTypes.ml +++ b/infer/src/clang/cTypes.ml @@ -198,22 +198,20 @@ let sil_type_of_attr_pointer_type typ raw_type = let rec return_type_of_function_type_ptr type_ptr = let open Clang_ast_t in - try - let c_type = Clang_ast_main.PointerMap.find type_ptr !CFrontend_config.pointer_type_index in - match c_type with - | FunctionProtoType (type_info, function_type_info, _) - | FunctionNoProtoType (type_info, function_type_info) -> - function_type_info.Clang_ast_t.fti_return_type - | BlockPointerType (type_info, in_type_ptr) -> - return_type_of_function_type_ptr in_type_ptr - | _ -> - Printing.log_err "Warning: Type pointer %s is not a function type." - (Clang_ast_j.string_of_pointer type_ptr); - "" - with Not_found -> - Printing.log_err "Warning: Type pointer %s not found." - (Clang_ast_j.string_of_pointer type_ptr); - "" + match Ast_utils.get_type type_ptr with + | Some FunctionProtoType (type_info, function_type_info, _) + | Some FunctionNoProtoType (type_info, function_type_info) -> + function_type_info.Clang_ast_t.fti_return_type + | Some BlockPointerType (type_info, in_type_ptr) -> + return_type_of_function_type_ptr in_type_ptr + | Some _ -> + Printing.log_err "Warning: Type pointer %s is not a function type." + (Clang_ast_j.string_of_pointer type_ptr); + "" + | None -> + Printing.log_err "Warning: Type pointer %s not found." + (Clang_ast_j.string_of_pointer type_ptr); + "" let return_type_of_function_type qt = return_type_of_function_type_ptr qt.Clang_ast_t.qt_type_ptr diff --git a/infer/src/clang/cTypes_decl.ml b/infer/src/clang/cTypes_decl.ml index 64bc15b32..734a812f9 100644 --- a/infer/src/clang/cTypes_decl.ml +++ b/infer/src/clang/cTypes_decl.ml @@ -86,8 +86,11 @@ let get_record_name_csu opt_type name_info = if (String.length name_str = 0) then prefix ^ type_name else prefix ^ name_str in csu, name - -let get_record_name opt_type name_info = +let get_record_name decl = + let name_info, opt_type = match decl with + | Clang_ast_t.RecordDecl (_, name_info, opt_type, _, _, _, _) + | Clang_ast_t.CXXRecordDecl (_, name_info, opt_type, _, _, _, _, _) -> name_info, opt_type + | _ -> assert false in snd (get_record_name_csu opt_type name_info) let get_method_decls parent decl_list = @@ -112,6 +115,18 @@ let get_class_methods tenv class_name namespace decl_list = (* poor mans list_filter_map *) list_flatten_options (list_map process_method_decl decl_list) +(** fetches list of superclasses for C++ classes *) +let get_superclass_list decl = + match decl with + | Clang_ast_t.CXXRecordDecl (_, _, _, _, _, _, _, cxx_rec_info) -> + (* there is no concept of virtual inheritance in the backend right now *) + let base_ptr = cxx_rec_info.Clang_ast_t.xrdi_bases @ cxx_rec_info.Clang_ast_t.xrdi_vbases in + let base_decls = list_map Ast_utils.get_decl_from_typ_ptr base_ptr in + let decl_to_mangled_name decl = Mangled.from_string (get_record_name decl) in + let get_super_field super_decl = (Sil.Class, decl_to_mangled_name super_decl) in + list_map get_super_field base_decls + | _ -> [] + let add_struct_to_tenv tenv typ = let csu = match typ with | Sil.Tstruct(_, _, csu, _, _, _, _) -> csu @@ -155,7 +170,7 @@ and get_declaration_type tenv namespace decl = let sorted_non_static_fields = CFrontend_utils.General_utils.sort_fields non_static_fields' in let static_fields = [] in (* Warning for the moment we do not treat static field. *) let methods = get_class_methods tenv name namespace decl_list in (* C++ methods only *) - let superclasses = [] in + let superclasses = get_superclass_list decl in let item_annotation = Sil.item_annotation_empty in (* No annotations for struts *) let sil_type = Sil.Tstruct (sorted_non_static_fields, static_fields, csu, Some mangled_name, superclasses, methods, item_annotation) in diff --git a/infer/src/clang/cTypes_decl.mli b/infer/src/clang/cTypes_decl.mli index 6741b8857..82d7d341b 100644 --- a/infer/src/clang/cTypes_decl.mli +++ b/infer/src/clang/cTypes_decl.mli @@ -13,7 +13,7 @@ val get_declaration_type : Sil.tenv -> string option -> Clang_ast_t.decl -> Sil. val add_struct_to_tenv : Sil.tenv -> Sil.typ -> unit -val get_record_name : Clang_ast_t.opt_type -> Clang_ast_t.named_decl_info -> string +val get_record_name : Clang_ast_t.decl -> string val get_method_decls : Clang_ast_t.decl -> Clang_ast_t.decl list -> (Clang_ast_t.decl * Clang_ast_t.decl) list diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/inheritance.cpp b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance.cpp new file mode 100644 index 000000000..73beab7f5 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance.cpp @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2015 - 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. +*/ + +class Base { +public: + int fun() { return 1;} + int fun_redefine() { return 10; } // note that they are not virtual +}; + +class Sub : public Base { +public: + int fun_redefine() { return 20; } +}; + +void call_static_methods() { + Base *b = new Base; + Base *s1 = new Sub; // note the type of s1 + Sub *s2 = new Sub; + + b->fun(); + s1->fun(); + s2->fun(); + + b->fun_redefine(); + s1->fun_redefine(); // will call Base::fun_redefine + s2->fun_redefine(); // will call Sub::fun_redefine +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/inheritance.dot b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance.dot new file mode 100644 index 000000000..dedcd2b2b --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance.dot @@ -0,0 +1,78 @@ +digraph iCFG { +20 [label="20: DeclStmt \n n$14=_fun___new(sizeof(class Base ):unsigned long ) [line 22]\n *&b:class Base *=n$14 [line 22]\n REMOVE_TEMPS(n$14); [line 22]\n " shape="box"] + + + 20 -> 19 ; +19 [label="19: DeclStmt \n n$13=_fun___new(sizeof(class Sub ):unsigned long ) [line 23]\n *&s1:class Base *=n$13 [line 23]\n REMOVE_TEMPS(n$13); [line 23]\n " shape="box"] + + + 19 -> 18 ; +18 [label="18: DeclStmt \n n$12=_fun___new(sizeof(class Sub ):unsigned long ) [line 24]\n *&s2:class Sub *=n$12 [line 24]\n REMOVE_TEMPS(n$12); [line 24]\n " shape="box"] + + + 18 -> 17 ; +17 [label="17: Call _fun_Base_fun \n n$10=*&b:class Base * [line 26]\n n$11=_fun_Base_fun(n$10:class Base ) [line 26]\n REMOVE_TEMPS(n$10,n$11); [line 26]\n " shape="box"] + + + 17 -> 16 ; +16 [label="16: Call _fun_Base_fun \n n$8=*&s1:class Base * [line 27]\n n$9=_fun_Base_fun(n$8:class Base ) [line 27]\n REMOVE_TEMPS(n$8,n$9); [line 27]\n " shape="box"] + + + 16 -> 15 ; +15 [label="15: Call _fun_Base_fun \n n$6=*&s2:class Sub * [line 28]\n n$7=_fun_Base_fun(n$6:class Base ) [line 28]\n REMOVE_TEMPS(n$6,n$7); [line 28]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Call _fun_Base_fun_redefine \n n$4=*&b:class Base * [line 30]\n n$5=_fun_Base_fun_redefine(n$4:class Base ) [line 30]\n REMOVE_TEMPS(n$4,n$5); [line 30]\n NULLIFY(&b,false); [line 30]\n " shape="box"] + + + 14 -> 13 ; +13 [label="13: Call _fun_Base_fun_redefine \n n$2=*&s1:class Base * [line 31]\n n$3=_fun_Base_fun_redefine(n$2:class Base ) [line 31]\n REMOVE_TEMPS(n$2,n$3); [line 31]\n NULLIFY(&s1,false); [line 31]\n " shape="box"] + + + 13 -> 12 ; +12 [label="12: Call _fun_Sub_fun_redefine \n n$0=*&s2:class Sub * [line 32]\n n$1=_fun_Sub_fun_redefine(n$0:class Sub ) [line 32]\n REMOVE_TEMPS(n$0,n$1); [line 32]\n NULLIFY(&s2,false); [line 32]\n APPLY_ABSTRACTION; [line 32]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Exit call_static_methods \n " color=yellow style=filled] + + +10 [label="10: Start call_static_methods\nFormals: \nLocals: b:class Base * s1:class Base * s2:class Sub * \n DECLARE_LOCALS(&return,&b,&s1,&s2); [line 21]\n NULLIFY(&b,false); [line 21]\n NULLIFY(&s1,false); [line 21]\n NULLIFY(&s2,false); [line 21]\n " color=yellow style=filled] + + + 10 -> 20 ; +9 [label="9: Return Stmt \n *&return:int =20 [line 18]\n APPLY_ABSTRACTION; [line 18]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: Exit Sub_fun_redefine \n " color=yellow style=filled] + + +7 [label="7: Start Sub_fun_redefine\nFormals: this:struct Sub \nLocals: \n DECLARE_LOCALS(&return); [line 18]\n NULLIFY(&this,false); [line 18]\n " color=yellow style=filled] + + + 7 -> 9 ; +6 [label="6: Return Stmt \n *&return:int =10 [line 13]\n APPLY_ABSTRACTION; [line 13]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: Exit Base_fun_redefine \n " color=yellow style=filled] + + +4 [label="4: Start Base_fun_redefine\nFormals: this:struct Base \nLocals: \n DECLARE_LOCALS(&return); [line 13]\n NULLIFY(&this,false); [line 13]\n " color=yellow style=filled] + + + 4 -> 6 ; +3 [label="3: Return Stmt \n *&return:int =1 [line 12]\n APPLY_ABSTRACTION; [line 12]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit Base_fun \n " color=yellow style=filled] + + +1 [label="1: Start Base_fun\nFormals: this:struct Base \nLocals: \n DECLARE_LOCALS(&return); [line 12]\n NULLIFY(&this,false); [line 12]\n " color=yellow style=filled] + + + 1 -> 3 ; +} diff --git a/infer/tests/frontend/cpp/InheritanceTest.java b/infer/tests/frontend/cpp/InheritanceTest.java new file mode 100644 index 000000000..25669b2ee --- /dev/null +++ b/infer/tests/frontend/cpp/InheritanceTest.java @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2013 - 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. +*/ + +package frontend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.DotFilesEqual.dotFileEqualTo; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferRunner; + +public class InheritanceTest { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunCommaThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = + "infer/tests/codetoanalyze/cpp/frontend/types/inheritance.cpp"; + + String dotty = + "infer/tests/codetoanalyze/cpp/frontend/types/inheritance.dot"; + + ImmutableList inferCmd = + InferRunner.createCPPInferCommandFrontend( + folder, + src); + File newDotFile = InferRunner.runInferFrontend(inferCmd); + assertThat( + "In the capture of " + src + + " the dotty files should be the same.", + newDotFile, dotFileEqualTo(dotty)); + } +}