From 55f9fb9d5e94fbcd71867d10cc46e216a447d8d9 Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Mon, 16 Nov 2015 05:45:30 -0800 Subject: [PATCH] Add base class fields to C++ classes Summary: public Fix oversight where fields of base classes were not exported. Reviewed By: dulmarod Differential Revision: D2652218 fb-gh-sync-id: 75b93ed --- infer/src/clang/cTypes_decl.ml | 27 ++-- .../cpp/frontend/types/inheritance_field.cpp | 60 +++++++++ .../frontend/types/inheritance_field.cpp.dot | 119 ++++++++++++++++++ .../endtoend/cpp/InheritanceFieldTest.java | 67 ++++++++++ infer/tests/frontend/cpp/InheritanceTest.java | 19 ++- 5 files changed, 279 insertions(+), 13 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp create mode 100644 infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot create mode 100644 infer/tests/endtoend/cpp/InheritanceFieldTest.java diff --git a/infer/src/clang/cTypes_decl.ml b/infer/src/clang/cTypes_decl.ml index fd1dacd4b..fb13c50fb 100644 --- a/infer/src/clang/cTypes_decl.ml +++ b/infer/src/clang/cTypes_decl.ml @@ -124,18 +124,21 @@ let get_class_methods tenv class_name namespace decl_list = (* poor mans list_filter_map *) IList.flatten_options (IList.map process_method_decl decl_list) -(** fetches list of superclasses for C++ classes *) -let get_superclass_list decl = +let get_superclass_decls 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 = IList.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 - IList.map get_super_field base_decls + IList.map Ast_utils.get_decl_from_typ_ptr base_ptr | _ -> [] +(** fetches list of superclasses for C++ classes *) +let get_superclass_list decl = + let base_decls = get_superclass_decls decl 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 + IList.map get_super_field base_decls + let add_struct_to_tenv tenv typ = let csu = match typ with | Sil.Tstruct(_, _, csu, _, _, _, _) -> csu @@ -144,8 +147,12 @@ let add_struct_to_tenv tenv typ = let typename = Sil.TN_csu(csu, mangled) in Sil.tenv_add tenv typename typ -let rec get_struct_fields tenv record_name namespace decl_list = +let rec get_struct_fields tenv namespace decl = let open Clang_ast_t in + let decl_list = match decl with + | CXXRecordDecl (_, _, _, _, decl_list, _, _, _) + | RecordDecl (_, _, _, _, decl_list, _, _) -> decl_list + | _ -> [] in let do_one_decl decl = match decl with | FieldDecl (_, name_info, type_ptr, _) -> let id = General_utils.mk_class_field_name name_info in @@ -158,7 +165,9 @@ let rec get_struct_fields tenv record_name namespace decl_list = if not decl_info.Clang_ast_t.di_is_implicit then ignore (add_types_from_decl_to_tenv tenv namespace decl); [] | _ -> [] in - IList.flatten (IList.map do_one_decl decl_list) + let base_decls = get_superclass_decls decl in + let base_class_fields = IList.map (get_struct_fields tenv namespace) base_decls in + IList.flatten (base_class_fields @ (IList.map do_one_decl decl_list)) (* For a record declaration it returns/constructs the type *) and get_strct_cpp_class_declaration_type tenv namespace decl = @@ -174,7 +183,7 @@ and get_strct_cpp_class_declaration_type tenv namespace decl = if not record_decl_info.Clang_ast_t.rdi_is_complete_definition then Printing.log_err " ...Warning, definition incomplete. The full definition will probably be later \n@."; - let non_static_fields = get_struct_fields tenv name namespace decl_list in + let non_static_fields = get_struct_fields tenv namespace decl in let non_static_fields' = if CTrans_models.is_objc_memory_model_controlled name then General_utils.append_no_duplicates_fields [Sil.objc_ref_counter_field] non_static_fields else non_static_fields in diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp new file mode 100644 index 000000000..27c3d6c69 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp @@ -0,0 +1,60 @@ +/* +* 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. +*/ + +struct Base1 { + int b1; +}; + +struct Base2 { + int b2; +}; + +struct Sub : public Base1, public Base2 { + int s; +}; + +int div0_b1(Sub s) { + s.b1 = 0; + return 1 / s.b1; +} + +int div0_b2(Sub s) { + s.b2 = 0; + return 1 / s.b2; +} + +int div0_s(Sub s) { + s.s = 0; + return 1 / s.s; +} + +int div0_cast(Sub *s) { + s->b1 = 0; + Base1 *b = s; + return 1 / b->b1; +} + +int div0_cast_ref(Sub s) { + s.b1 = 0; + Base1 &b = s; + return 1 / b.b1; +} + + +int div1_b1(Sub s) { + s.b1 = 1; + return 1 / s.b1; +} + +int div1_cast(Sub *s) { + s->b1 = 1; + Base1 *b = s; + return 1 / b->b1; +} + diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot new file mode 100644 index 000000000..663489be2 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot @@ -0,0 +1,119 @@ +digraph iCFG { +31 [label="31: BinaryOperatorStmt: Assign \n n$3=*&s:class Sub * [line 56]\n *n$3.b1:int =1 [line 56]\n REMOVE_TEMPS(n$3); [line 56]\n " shape="box"] + + + 31 -> 30 ; +30 [label="30: DeclStmt \n n$2=*&s:class Sub * [line 57]\n *&b:struct Base1 *=n$2 [line 57]\n REMOVE_TEMPS(n$2); [line 57]\n NULLIFY(&s,false); [line 57]\n " shape="box"] + + + 30 -> 29 ; +29 [label="29: Return Stmt \n n$0=*&b:struct Base1 * [line 58]\n n$1=*n$0.b1:int [line 58]\n *&return:int =(1 / n$1) [line 58]\n REMOVE_TEMPS(n$0,n$1); [line 58]\n NULLIFY(&b,false); [line 58]\n APPLY_ABSTRACTION; [line 58]\n " shape="box"] + + + 29 -> 28 ; +28 [label="28: Exit div1_cast \n " color=yellow style=filled] + + +27 [label="27: Start div1_cast\nFormals: s:class Sub *\nLocals: b:struct Base1 * \n DECLARE_LOCALS(&return,&b); [line 55]\n NULLIFY(&b,false); [line 55]\n " color=yellow style=filled] + + + 27 -> 31 ; +26 [label="26: BinaryOperatorStmt: Assign \n *&s.b1:int =1 [line 51]\n " shape="box"] + + + 26 -> 25 ; +25 [label="25: Return Stmt \n n$0=*&s.b1:int [line 52]\n *&return:int =(1 / n$0) [line 52]\n REMOVE_TEMPS(n$0); [line 52]\n NULLIFY(&s,false); [line 52]\n APPLY_ABSTRACTION; [line 52]\n " shape="box"] + + + 25 -> 24 ; +24 [label="24: Exit div1_b1 \n " color=yellow style=filled] + + +23 [label="23: Start div1_b1\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 50]\n " color=yellow style=filled] + + + 23 -> 26 ; +22 [label="22: BinaryOperatorStmt: Assign \n *&s.b1:int =0 [line 44]\n " shape="box"] + + + 22 -> 21 ; +21 [label="21: DeclStmt \n *&b:struct Base1 &=&s [line 45]\n " shape="box"] + + + 21 -> 20 ; +20 [label="20: Return Stmt \n n$0=*&b:struct Base1 & [line 46]\n n$1=*n$0.b1:int [line 46]\n *&return:int =(1 / n$1) [line 46]\n REMOVE_TEMPS(n$0,n$1); [line 46]\n NULLIFY(&b,false); [line 46]\n NULLIFY(&s,false); [line 46]\n APPLY_ABSTRACTION; [line 46]\n " shape="box"] + + + 20 -> 19 ; +19 [label="19: Exit div0_cast_ref \n " color=yellow style=filled] + + +18 [label="18: Start div0_cast_ref\nFormals: s:class Sub \nLocals: b:struct Base1 & \n DECLARE_LOCALS(&return,&b); [line 43]\n NULLIFY(&b,false); [line 43]\n " color=yellow style=filled] + + + 18 -> 22 ; +17 [label="17: BinaryOperatorStmt: Assign \n n$3=*&s:class Sub * [line 38]\n *n$3.b1:int =0 [line 38]\n REMOVE_TEMPS(n$3); [line 38]\n " shape="box"] + + + 17 -> 16 ; +16 [label="16: DeclStmt \n n$2=*&s:class Sub * [line 39]\n *&b:struct Base1 *=n$2 [line 39]\n REMOVE_TEMPS(n$2); [line 39]\n NULLIFY(&s,false); [line 39]\n " shape="box"] + + + 16 -> 15 ; +15 [label="15: Return Stmt \n n$0=*&b:struct Base1 * [line 40]\n n$1=*n$0.b1:int [line 40]\n *&return:int =(1 / n$1) [line 40]\n REMOVE_TEMPS(n$0,n$1); [line 40]\n NULLIFY(&b,false); [line 40]\n APPLY_ABSTRACTION; [line 40]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Exit div0_cast \n " color=yellow style=filled] + + +13 [label="13: Start div0_cast\nFormals: s:class Sub *\nLocals: b:struct Base1 * \n DECLARE_LOCALS(&return,&b); [line 37]\n NULLIFY(&b,false); [line 37]\n " color=yellow style=filled] + + + 13 -> 17 ; +12 [label="12: BinaryOperatorStmt: Assign \n *&s.s:int =0 [line 33]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Return Stmt \n n$0=*&s.s:int [line 34]\n *&return:int =(1 / n$0) [line 34]\n REMOVE_TEMPS(n$0); [line 34]\n NULLIFY(&s,false); [line 34]\n APPLY_ABSTRACTION; [line 34]\n " shape="box"] + + + 11 -> 10 ; +10 [label="10: Exit div0_s \n " color=yellow style=filled] + + +9 [label="9: Start div0_s\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 32]\n " color=yellow style=filled] + + + 9 -> 12 ; +8 [label="8: BinaryOperatorStmt: Assign \n *&s.b2:int =0 [line 28]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: Return Stmt \n n$0=*&s.b2:int [line 29]\n *&return:int =(1 / n$0) [line 29]\n REMOVE_TEMPS(n$0); [line 29]\n NULLIFY(&s,false); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] + + + 7 -> 6 ; +6 [label="6: Exit div0_b2 \n " color=yellow style=filled] + + +5 [label="5: Start div0_b2\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 27]\n " color=yellow style=filled] + + + 5 -> 8 ; +4 [label="4: BinaryOperatorStmt: Assign \n *&s.b1:int =0 [line 23]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: Return Stmt \n n$0=*&s.b1:int [line 24]\n *&return:int =(1 / n$0) [line 24]\n REMOVE_TEMPS(n$0); [line 24]\n NULLIFY(&s,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit div0_b1 \n " color=yellow style=filled] + + +1 [label="1: Start div0_b1\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 22]\n " color=yellow style=filled] + + + 1 -> 4 ; +} diff --git a/infer/tests/endtoend/cpp/InheritanceFieldTest.java b/infer/tests/endtoend/cpp/InheritanceFieldTest.java new file mode 100644 index 000000000..e13180732 --- /dev/null +++ b/infer/tests/endtoend/cpp/InheritanceFieldTest.java @@ -0,0 +1,67 @@ +/* +* 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. +*/ + +package endtoend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class InheritanceFieldTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp"; + + private static ImmutableList inferCmd; + + public static final String DIVIDE_BY_ZERO = "DIVIDE_BY_ZERO"; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsOnDiv0MethodsErrorIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + String[] procedures = { + "div0_b1", + "div0_b2", + "div0_s", + "div0_cast", + "div0_cast_ref", + }; + assertThat( + "Results should contain the expected divide by zero", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/frontend/cpp/InheritanceTest.java b/infer/tests/frontend/cpp/InheritanceTest.java index 68744004f..410d75195 100644 --- a/infer/tests/frontend/cpp/InheritanceTest.java +++ b/infer/tests/frontend/cpp/InheritanceTest.java @@ -20,14 +20,25 @@ import utils.ClangFrontendUtils; public class InheritanceTest { + + String basePath = "infer/tests/codetoanalyze/cpp/frontend/types/"; + @Rule public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + void frontendTest(String fileRelative) throws InterruptedException, IOException, InferException { + ClangFrontendUtils.createAndCompareCppDotFiles(folder, basePath + fileRelative); + } + + @Test + public void testInheritanceDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("inheritance.cpp"); + } + @Test - public void whenCaptureRunCommaThenDotFilesAreTheSame() + public void testInheritanceFieldDotFilesMatch() throws InterruptedException, IOException, InferException { - String src = - "infer/tests/codetoanalyze/cpp/frontend/types/inheritance.cpp"; - ClangFrontendUtils.createAndCompareCppDotFiles(folder, src); + frontendTest("inheritance_field.cpp"); } }