diff --git a/lldb/bindings/interface/SBType.i b/lldb/bindings/interface/SBType.i --- a/lldb/bindings/interface/SBType.i +++ b/lldb/bindings/interface/SBType.i @@ -837,6 +837,21 @@ lldb::SBTypeMemberFunction GetMemberFunctionAtIndex (uint32_t idx); + %feature("docstring", + "Returns true if the type is completely defined. + + Language-specific behaviour: + + * C: Returns false for struct types that were only forward declared in the + type's `SBTarget`/`SBModule`. Otherwise returns true. + * C++: Returns false for template/non-template struct/class types and + scoped enums that were only forward declared inside the type's + `SBTarget`/`SBModule`. Otherwise returns true. + * Objective-C: Follows the same behavior as C for struct types. Objective-C + classes are considered complete unless they were only forward declared via + ``@class ClassName`` in the type's `SBTarget`/`SBModule`. Otherwise + returns true. + ") IsTypeComplete; bool IsTypeComplete (); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2940,7 +2940,12 @@ } bool TypeSystemClang::IsCompleteType(lldb::opaque_compiler_type_t type) { - const bool allow_completion = false; + // If the type hasn't been lazily completed yet, complete it now so that we + // can give the caller an accurate answer whether the type actually has a + // definition. Without completing the type now we would just tell the user + // the current (internal) completeness state of the type and most users don't + // care (or even know) about this behavior. + const bool allow_completion = true; return GetCompleteQualType(&getASTContext(), GetQualType(type), allow_completion); } diff --git a/lldb/test/API/functionalities/type_completion/TestTypeCompletion.py b/lldb/test/API/functionalities/type_completion/TestTypeCompletion.py deleted file mode 100644 --- a/lldb/test/API/functionalities/type_completion/TestTypeCompletion.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Check that types only get completed when necessary. -""" - - - -import lldb -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class TypeCompletionTestCase(TestBase): - - mydir = TestBase.compute_mydir(__file__) - - @expectedFailureAll( - compiler="icc", - bugnumber="often fails with 'NameAndAddress should be valid.") - # Fails with gcc 4.8.1 with llvm.org/pr15301 LLDB prints incorrect sizes - # of STL containers - def test_with_run_command(self): - """Check that types only get completed when necessary.""" - self.build() - self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) - - lldbutil.run_break_set_by_source_regexp( - self, "// Set break point at this line.") - - self.runCmd("run", RUN_SUCCEEDED) - - # The stop reason of the thread should be breakpoint. - self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, - substrs=['stopped', - 'stop reason = breakpoint']) - - # This is the function to remove the custom formats in order to have a - # clean slate for the next test case. - def cleanup(): - self.runCmd('type category enable -l c++', check=False) - - self.runCmd('type category disable -l c++', check=False) - - # Execute the cleanup function during test case tear down. - self.addTearDownHook(cleanup) - - p_vector = self.dbg.GetSelectedTarget().GetProcess( - ).GetSelectedThread().GetSelectedFrame().FindVariable('p') - p_type = p_vector.GetType() - self.assertFalse( - p_type.IsTypeComplete(), - 'vector complete but it should not be') - - self.runCmd("continue") - - p_vector = self.dbg.GetSelectedTarget().GetProcess( - ).GetSelectedThread().GetSelectedFrame().FindVariable('p') - p_type = p_vector.GetType() - self.assertFalse( - p_type.IsTypeComplete(), - 'vector complete but it should not be') - - self.runCmd("continue") - - self.runCmd("frame variable p --show-types") - - p_vector = self.dbg.GetSelectedTarget().GetProcess( - ).GetSelectedThread().GetSelectedFrame().FindVariable('p') - p_type = p_vector.GetType() - self.assertTrue( - p_type.IsTypeComplete(), - 'vector should now be complete') - name_address_type = p_type.GetTemplateArgumentType(0) - self.assertTrue( - name_address_type.IsValid(), - 'NameAndAddress should be valid') - self.assertFalse( - name_address_type.IsTypeComplete(), - 'NameAndAddress complete but it should not be') - - self.runCmd("continue") - - self.runCmd("frame variable guy --show-types") - - p_vector = self.dbg.GetSelectedTarget().GetProcess( - ).GetSelectedThread().GetSelectedFrame().FindVariable('p') - p_type = p_vector.GetType() - self.assertTrue( - p_type.IsTypeComplete(), - 'vector should now be complete') - name_address_type = p_type.GetTemplateArgumentType(0) - self.assertTrue( - name_address_type.IsValid(), - 'NameAndAddress should be valid') - self.assertTrue( - name_address_type.IsTypeComplete(), - 'NameAndAddress should now be complete') - field0 = name_address_type.GetFieldAtIndex(0) - self.assertTrue( - field0.IsValid(), - 'NameAndAddress::m_name should be valid') - string = field0.GetType().GetPointeeType() - self.assertTrue(string.IsValid(), 'CustomString should be valid') - self.assertFalse(string.IsTypeComplete(), - 'CustomString complete but it should not be') - - self.runCmd("continue") - - p_vector = self.dbg.GetSelectedTarget().GetProcess( - ).GetSelectedThread().GetSelectedFrame().FindVariable('p') - p_type = p_vector.GetType() - self.assertTrue( - p_type.IsTypeComplete(), - 'vector should now be complete') - name_address_type = p_type.GetTemplateArgumentType(0) - self.assertTrue( - name_address_type.IsValid(), - 'NameAndAddress should be valid') - self.assertTrue( - name_address_type.IsTypeComplete(), - 'NameAndAddress should now be complete') - field0 = name_address_type.GetFieldAtIndex(0) - self.assertTrue( - field0.IsValid(), - 'NameAndAddress::m_name should be valid') - string = field0.GetType().GetPointeeType() - self.assertTrue(string.IsValid(), 'CustomString should be valid') - self.assertFalse(string.IsTypeComplete(), - 'CustomString complete but it should not be') - - self.runCmd('type category enable -l c++', check=False) - self.runCmd('frame variable guy --show-types --ptr-depth=1') - - p_vector = self.dbg.GetSelectedTarget().GetProcess( - ).GetSelectedThread().GetSelectedFrame().FindVariable('p') - p_type = p_vector.GetType() - self.assertTrue( - p_type.IsTypeComplete(), - 'vector should now be complete') - name_address_type = p_type.GetTemplateArgumentType(0) - self.assertTrue( - name_address_type.IsValid(), - 'NameAndAddress should be valid') - self.assertTrue( - name_address_type.IsTypeComplete(), - 'NameAndAddress should now be complete') - field0 = name_address_type.GetFieldAtIndex(0) - self.assertTrue( - field0.IsValid(), - 'NameAndAddress::m_name should be valid') - string = field0.GetType().GetPointeeType() - self.assertTrue(string.IsValid(), 'CustomString should be valid') - self.assertTrue( - string.IsTypeComplete(), - 'CustomString should now be complete') diff --git a/lldb/test/API/functionalities/type_completion/main.cpp b/lldb/test/API/functionalities/type_completion/main.cpp deleted file mode 100644 --- a/lldb/test/API/functionalities/type_completion/main.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include - -class CustomString -{ -public: - CustomString (const char* buffer) : - m_buffer(nullptr) - { - if (buffer) - { - auto l = strlen(buffer); - m_buffer = new char[1 + l]; - strcpy(m_buffer, buffer); - } - } - - ~CustomString () - { - delete[] m_buffer; - } - - const char* - GetBuffer () - { - return m_buffer; - } - -private: - char *m_buffer; -}; - -class NameAndAddress - { - public: - CustomString& GetName() { return *m_name; } - CustomString& GetAddress() { return *m_address; } - NameAndAddress(const char* N, const char* A) : m_name(new CustomString(N)), m_address(new CustomString(A)) - { - } - ~NameAndAddress() - { - } - - private: - CustomString* m_name; - CustomString* m_address; -}; - -typedef std::vector People; - -int main (int argc, const char * argv[]) -{ - People p; - p.push_back(NameAndAddress("Enrico","123 Main Street")); - p.push_back(NameAndAddress("Foo","10710 Johnson Avenue")); // Set break point at this line. - p.push_back(NameAndAddress("Arpia","6956 Florey Street")); - p.push_back(NameAndAddress("Apple","1 Infinite Loop")); // Set break point at this line. - p.push_back(NameAndAddress("Richard","9500 Gilman Drive")); - p.push_back(NameAndAddress("Bar","3213 Windsor Rd")); - - for (int j = 0; j") + + # Record types without a defining declaration are not complete. + self.assertPointeeIncomplete("FwdClass *", "fwd_class") + self.assertPointeeIncomplete("FwdClassTypedef *", "fwd_class_typedef") + self.assertPointeeIncomplete("FwdTemplateClass<> *", "fwd_template_class") + + # A pointer type is complete even when it points to an incomplete type. + fwd_class_ptr = self.expect_expr("fwd_class", result_type="FwdClass *") + self.assertTrue(fwd_class_ptr.GetType().IsTypeComplete()) + + @no_debug_info_test + def test_builtin_types(self): + """ Tests builtin types and types derived from them. """ + self.build() + self.createTestTarget() + + # Void is complete. + void_type = self.target().FindFirstType("void") + self.assertTrue(void_type.IsValid()) + self.assertTrue(void_type.IsTypeComplete()) + + # Builtin types are also complete. + int_type = self.target().FindFirstType("int") + self.assertTrue(int_type.IsValid()) + self.assertTrue(int_type.IsTypeComplete()) + + # References to builtin types are also complete. + int_ref_type = int_type.GetReferenceType() + self.assertTrue(int_ref_type.IsValid()) + self.assertTrue(int_ref_type.IsTypeComplete()) + + # Pointer types to basic types are always complete. + int_ptr_type = int_type.GetReferenceType() + self.assertTrue(int_ptr_type.IsValid()) + self.assertTrue(int_ptr_type.IsTypeComplete()) diff --git a/lldb/test/API/lang/cpp/complete-type-check/main.cpp b/lldb/test/API/lang/cpp/complete-type-check/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/complete-type-check/main.cpp @@ -0,0 +1,36 @@ +struct EmptyClass {}; +struct DefinedClass { + int i; +}; +typedef DefinedClass DefinedClassTypedef; + +struct FwdClass; +typedef FwdClass FwdClassTypedef; + +template struct DefinedTemplateClass {}; +template <> struct DefinedTemplateClass {}; + +template struct FwdTemplateClass; +template <> struct FwdTemplateClass; + +enum class EnumClassFwd; + +enum DefinedEnum { Case1 }; +enum DefinedEnumClass { Case2 }; + +EmptyClass empty_class; +DefinedClass defined_class; +DefinedClassTypedef defined_class_typedef; + +FwdClass *fwd_class; +FwdClassTypedef *fwd_class_typedef; + +DefinedTemplateClass defined_template_class; +FwdTemplateClass *fwd_template_class; + +EnumClassFwd *fwd_enum_class = nullptr; + +DefinedEnum defined_enum = Case1; +DefinedEnumClass defined_enum_class = DefinedEnumClass::Case2; + +int main() {} diff --git a/lldb/test/API/lang/objc/complete-type-check/Makefile b/lldb/test/API/lang/objc/complete-type-check/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/objc/complete-type-check/Makefile @@ -0,0 +1,5 @@ +OBJC_SOURCES := main.m +LD_EXTRAS := -framework Foundation +CFLAGS_EXTRAS := -fobjc-arc + +include Makefile.rules diff --git a/lldb/test/API/lang/objc/complete-type-check/TestObjCIsTypeComplete.py b/lldb/test/API/lang/objc/complete-type-check/TestObjCIsTypeComplete.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/objc/complete-type-check/TestObjCIsTypeComplete.py @@ -0,0 +1,39 @@ +""" Tests SBType.IsTypeComplete on Objective-C types. """ + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + @no_debug_info_test + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.m")) + + # A class that is only forward declared is not complete. + incomplete = self.expect_expr("incomplete", result_type="IncompleteClass *") + self.assertTrue(incomplete.IsValid()) + incomplete_class = incomplete.GetType().GetPointeeType() + self.assertTrue(incomplete_class.IsValid()) + self.assertFalse(incomplete_class.IsTypeComplete()) + + # A class that has its interface fully declared is complete. + complete = self.expect_expr("complete", result_type="CompleteClass *") + self.assertTrue(complete.IsValid()) + complete_class = complete.GetType().GetPointeeType() + self.assertTrue(complete_class.IsValid()) + self.assertTrue(complete_class.IsTypeComplete()) + + # A class that has its interface fully declared and an implementation + # is also complete. + complete_with_impl = self.expect_expr("complete_impl", + result_type="CompleteClassWithImpl *") + self.assertTrue(complete_with_impl.IsValid()) + complete_class_with_impl = complete_with_impl.GetType().GetPointeeType() + self.assertTrue(complete_class_with_impl.IsValid()) + self.assertTrue(complete_class_with_impl.IsTypeComplete()) diff --git a/lldb/test/API/lang/objc/complete-type-check/main.m b/lldb/test/API/lang/objc/complete-type-check/main.m new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/objc/complete-type-check/main.m @@ -0,0 +1,19 @@ +#include + +@class IncompleteClass; + +@interface CompleteClass : NSObject +@end + +@interface CompleteClassWithImpl : NSObject +@end +@implementation CompleteClassWithImpl +@end + +IncompleteClass *incomplete = 0; +CompleteClass *complete = 0; +CompleteClassWithImpl *complete_impl = 0; + +int main() { + return 0; // break here +}