diff options
-rw-r--r-- | clang/lib/CodeGen/CGDebugInfo.cpp | 17 | ||||
-rw-r--r-- | clang/test/CodeGenObjC/debug-info-property-class-extension.m | 48 |
2 files changed, 64 insertions, 1 deletions
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 4305280a0c8..4aea4f8f945 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1791,7 +1791,7 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, } // Create entries for all of the properties. - for (const auto *PD : ID->properties()) { + auto AddProperty = [&](const ObjCPropertyDecl *PD) { SourceLocation Loc = PD->getLocation(); llvm::DIFile *PUnit = getOrCreateFile(Loc); unsigned PLine = getLineNumber(Loc); @@ -1805,6 +1805,21 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, : getSelectorName(PD->getSetterName()), PD->getPropertyAttributes(), getOrCreateType(PD->getType(), PUnit)); EltTys.push_back(PropertyNode); + }; + { + llvm::SmallPtrSet<const IdentifierInfo*, 16> PropertySet; + //for (const ObjCCategoryDecl *ClassExt : ID->known_extensions()) + // for (auto *PD : ClassExt->properties()) { + // PropertySet.insert(PD->getIdentifier()); + // AddProperty(PD); + // } + for (const auto *PD : ID->properties()) { + // Don't emit duplicate metadata for properties that were already in a + // class extension. + if (!PropertySet.insert(PD->getIdentifier()).second) + continue; + AddProperty(PD); + } } const ASTRecordLayout &RL = CGM.getContext().getASTObjCInterfaceLayout(ID); diff --git a/clang/test/CodeGenObjC/debug-info-property-class-extension.m b/clang/test/CodeGenObjC/debug-info-property-class-extension.m new file mode 100644 index 00000000000..ea2551799f3 --- /dev/null +++ b/clang/test/CodeGenObjC/debug-info-property-class-extension.m @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -S -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s + +// Checks debug info for properties from class extensions for a few cases. + + +// Readonly property in interface made readwrite in a category, with @impl +// The interesting bit is that when the ivar debug info is generated, the corresponding +// property is looked up and also gets debug info. If the debug info from the interface's +// declaration and from the ivar doesn't match, this will end up with two DIObjCProperty +// entries which would be bad. +@interface FooROWithImpl +// CHECK-NOT: !DIObjCProperty(name: "evolvingpropwithimpl"{{.*}}line: [[@LINE+1]] +@property (readonly) int evolvingpropwithimpl; +@end +@interface FooROWithImpl () +// CHECK: !DIObjCProperty(name: "evolvingpropwithimpl"{{.*}}line: [[@LINE+1]] +@property int evolvingpropwithimpl; +@end +@implementation FooROWithImpl +@synthesize evolvingpropwithimpl = _evolvingpropwithimpl; +@end + + +// Simple property from a class extension: +@interface Foo +@end +@interface Foo() +// CHECK: !DIObjCProperty(name: "myprop"{{.*}}line: [[@LINE+1]] +@property int myprop; +@end +// There's intentionally no @implementation for Foo, because that would +// generate debug info for the property via the backing ivar. + + +// Readonly property in interface made readwrite in a category: +@interface FooRO +// Shouldn't be here but in the class extension below. +// CHECK-NOT: !DIObjCProperty(name: "evolvingprop"{{.*}}line: [[@LINE+1]] +@property (readonly) int evolvingprop; +@end +@interface FooRO () +// CHECK: !DIObjCProperty(name: "evolvingprop"{{.*}}line: [[@LINE+1]] +@property int evolvingprop; +@end + + +// This references types in this file to force emission of their debug info. +void foo(Foo *f, FooRO *g, FooROWithImpl* h) { } |