diff --git a/SCKClangSourceFile.m b/SCKClangSourceFile.m index f17711d..9345de8 100644 --- a/SCKClangSourceFile.m +++ b/SCKClangSourceFile.m @@ -257,6 +257,7 @@ static BOOL isIBOutletFromPropertyOrIvar(CXCursor cursor) - (void) setLocation: (SCKSourceLocation*)aLocation forClass: (NSString*)aClassName withSuperclass: (NSString*)aSuperclassName + adoptedProtocols: (NSArray*)adoptedProtocolNames isDefinition: (BOOL)isDefinition isForwardDeclaration: (BOOL)isForwardDeclaration { @@ -274,6 +275,13 @@ - (void) setLocation: (SCKSourceLocation*)aLocation [class setSuperclass: [[self collection] classForName: aSuperclassName]]; } + if (nil != adoptedProtocolNames) + { + [self addAdoptedProtocolWithNames: adoptedProtocolNames + toProgramComponent: class + category: nil]; + } + if (isDefinition) { [class setDefinition: aLocation]; @@ -286,6 +294,7 @@ - (void) setLocation: (SCKSourceLocation*)aLocation - (void)setLocation: (SCKSourceLocation*)aLocation forCategory: (NSString*)aCategoryName + adoptedProtocols: (NSArray*)adoptedProtocolNames isDefinition: (BOOL)isDefinition ofClass: (NSString*)aClassName { @@ -301,6 +310,13 @@ - (void)setLocation: (SCKSourceLocation*)aLocation [[class categories] setObject: category forKey: aCategoryName]; } + if (nil != adoptedProtocolNames) + { + [self addAdoptedProtocolWithNames: adoptedProtocolNames + toProgramComponent: class + category: category]; + } + if (isDefinition) { [category setDefinition: aLocation]; @@ -490,6 +506,7 @@ - (void)setLocation: (SCKSourceLocation*)sourceLocation - (void) setLocation: (SCKSourceLocation*)sourceLocation forProtocol: (NSString*)protocolName + adoptedProtocols: (NSArray*)adoptedProtocolNames isForwardDeclaration: (BOOL)isForwardDeclaration { SCKProtocol *protocol = [[self collection] protocolForName: protocolName]; @@ -498,6 +515,14 @@ - (void) setLocation: (SCKSourceLocation*)sourceLocation { return; } + + if (nil != adoptedProtocolNames) + { + [self addAdoptedProtocolWithNames: adoptedProtocolNames + toProgramComponent: nil + category: nil]; + } + [protocol setDeclaration: sourceLocation]; [protocol setDefinition: sourceLocation]; } @@ -631,6 +656,61 @@ - (void)setLocation: (SCKSourceLocation*)sourceLocation } } +- (void)addAdoptedProtocolWithName: (NSString*)adoptedProtocolName + toClass: (SCKClass*)class + toProtocol: (SCKProtocol*)protocol + toCategory: (SCKCategory*)category +{ + SCKProtocol *adoptedProtocol = nil; + + if (nil != class) + { + adoptedProtocol = [[class adoptedProtocols] objectForKey: adoptedProtocolName]; + + if (nil == adoptedProtocol) + { + adoptedProtocol = [[self collection] protocolForName: adoptedProtocolName]; + [[class adoptedProtocols] setObject: adoptedProtocol forKey: adoptedProtocolName]; + } + + if (nil != category) + { + [[category adoptedProtocols] setObject: adoptedProtocol forKey: adoptedProtocolName]; + } + } + + if (nil != protocol) + { + adoptedProtocol = [[protocol adoptedProtocols] objectForKey: adoptedProtocolName]; + + if (nil == adoptedProtocol) + { + adoptedProtocol = [[self collection] protocolForName: adoptedProtocolName]; + [[protocol adoptedProtocols] setObject: adoptedProtocol forKey: adoptedProtocolName]; + } + } +} + +- (void)addAdoptedProtocolWithNames: (NSArray *)adoptedProtocolNames + toProgramComponent: (SCKProgramComponent *)component + category: (SCKCategory *)category +{ + if (nil == adoptedProtocolNames) + { + return; + } + + for (NSString *adoptedProtocolName in adoptedProtocolNames) + { + SCKProtocol *protocol = [[self collection] protocolForName: adoptedProtocolName]; + + [[(id)component adoptedProtocols] setObject: protocol forKey: adoptedProtocolName]; + + [[category adoptedProtocols] setObject: protocol forKey: adoptedProtocolName]; + } +} + + - (void)rebuildIndex { if (0 == translationUnit) { return; } @@ -655,10 +735,16 @@ - (void)rebuildIndex SCOPED_STR(className, clang_getCursorSpelling(cursor)); NSString __block *superclassName = nil; BOOL __block isForwardDeclaration = NO; - + NSMutableArray __block *adoptedProtocols = [NSMutableArray array]; + clang_visitChildrenWithBlock(cursor, ^ enum CXChildVisitResult (CXCursor classCursor, CXCursor parent) { + SCOPED_STR(name, clang_getCursorSpelling(classCursor)); + SCOPED_STR(typeEncoding, clang_getDeclObjCTypeEncoding(classCursor)); + SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] + initWithClangSourceLocation: clang_getCursorLocation(classCursor)]; + switch (classCursor.kind) { case CXCursor_ObjCClassRef: @@ -672,13 +758,14 @@ - (void)rebuildIndex superclassName = [NSString stringWithUTF8String: name]; break; } - case CXCursor_ObjCIvarDecl: + case CXCursor_ObjCProtocolRef: { - SCOPED_STR(name, clang_getCursorSpelling(classCursor)); - SCOPED_STR(typeEncoding, clang_getDeclObjCTypeEncoding(classCursor)); - SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] - initWithClangSourceLocation: clang_getCursorLocation(classCursor)]; - + [adoptedProtocols addObject: [NSString stringWithUTF8String: name]]; + + break; + } + case CXCursor_ObjCIvarDecl: + { [self setLocation: sourceLocation forIvar: [NSString stringWithUTF8String: name] withTypeEncoding: [NSString stringWithUTF8String: typeEncoding] @@ -688,10 +775,6 @@ - (void)rebuildIndex } case CXCursor_ObjCPropertyDecl: { - SCOPED_STR(name, clang_getCursorSpelling(classCursor)); - SCOPED_STR(type, clang_getDeclObjCTypeEncoding(classCursor)); - SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] - initWithClangSourceLocation: clang_getCursorLocation(classCursor)]; CXObjCPropertyAttrKind attributes = 0; #if CINDEX_VERSION >= 21 attributes = clang_Cursor_getObjCPropertyAttributes(classCursor, 0); @@ -699,7 +782,7 @@ - (void)rebuildIndex [self setLocation: sourceLocation forProperty: [NSString stringWithUTF8String: name] - withTypeEncoding: [NSString stringWithUTF8String: type] + withTypeEncoding: [NSString stringWithUTF8String: typeEncoding] attributes: attributes isIBOutlet: isIBOutletFromPropertyOrIvar(classCursor) inClass: [NSString stringWithUTF8String: className]]; @@ -707,15 +790,10 @@ - (void)rebuildIndex } case CXCursor_ObjCInstanceMethodDecl: case CXCursor_ObjCClassMethodDecl: - { - SCOPED_STR(name, clang_getCursorSpelling(classCursor)); - SCOPED_STR(type, clang_getDeclObjCTypeEncoding(classCursor)); - SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] - initWithClangSourceLocation: clang_getCursorLocation(classCursor)]; - + { [self setLocation: sourceLocation forMethod: [NSString stringWithUTF8String: name] - withTypeEncoding: [NSString stringWithUTF8String: type] + withTypeEncoding: [NSString stringWithUTF8String: typeEncoding] isClassMethod: (classCursor.kind == CXCursor_ObjCClassMethodDecl) isDefinition: clang_isCursorDefinition(classCursor) inClass: [NSString stringWithUTF8String: className] @@ -735,6 +813,7 @@ - (void)rebuildIndex [self setLocation: classLoc forClass: [NSString stringWithUTF8String: className] withSuperclass: superclassName + adoptedProtocols: adoptedProtocols isDefinition: clang_isCursorDefinition(cursor) isForwardDeclaration: isForwardDeclaration]; break; @@ -748,6 +827,7 @@ - (void)rebuildIndex [self setLocation: classLoc forClass: [NSString stringWithUTF8String: className] withSuperclass: nil + adoptedProtocols: nil isDefinition: clang_isCursorDefinition(cursor) isForwardDeclaration: NO]; @@ -784,25 +864,27 @@ - (void)rebuildIndex initWithClangSourceLocation: clang_getCursorLocation(cursor)]; SCOPED_STR(categoryName, clang_getCursorSpelling(cursor)); NSString *className = classNameFromCategory(cursor); - - [self setLocation: categoryLoc - forCategory: [NSString stringWithUTF8String: categoryName] - isDefinition: clang_isCursorDefinition(cursor) - ofClass: className]; + NSMutableArray __block *adoptedProtocols = [NSMutableArray array]; clang_visitChildrenWithBlock(cursor, ^ enum CXChildVisitResult (CXCursor categoryCursor, CXCursor parent) { + SCOPED_STR(name, clang_getCursorSpelling(categoryCursor)); + SCOPED_STR(type, clang_getDeclObjCTypeEncoding(categoryCursor)); + SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] + initWithClangSourceLocation: clang_getCursorLocation(categoryCursor)]; + switch (categoryCursor.kind) { + case CXCursor_ObjCProtocolRef: + { + [adoptedProtocols addObject: [NSString stringWithUTF8String: name]]; + + break; + } case CXCursor_ObjCInstanceMethodDecl: case CXCursor_ObjCClassMethodDecl: { - SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] - initWithClangSourceLocation: clang_getCursorLocation(categoryCursor)]; - SCOPED_STR(name, clang_getCursorSpelling(categoryCursor)); - SCOPED_STR(type, clang_getDeclObjCTypeEncoding(categoryCursor)); - [self setLocation: sourceLocation forMethod: [NSString stringWithUTF8String: name] withTypeEncoding: [NSString stringWithUTF8String: type] @@ -816,10 +898,6 @@ - (void)rebuildIndex case CXCursor_ObjCDynamicDecl: case CXCursor_ObjCPropertyDecl: { - SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] - initWithClangSourceLocation: clang_getCursorLocation(categoryCursor)]; - SCOPED_STR(name, clang_getCursorSpelling(categoryCursor)); - SCOPED_STR(type, clang_getDeclObjCTypeEncoding(categoryCursor)); CXObjCPropertyAttrKind attributes = 0; #if CINDEX_VERSION >= 21 attributes = clang_Cursor_getObjCPropertyAttributes(categoryCursor, 0); @@ -839,6 +917,12 @@ - (void)rebuildIndex } return CXChildVisit_Continue; }); + + [self setLocation: categoryLoc + forCategory: [NSString stringWithUTF8String: categoryName] + adoptedProtocols: adoptedProtocols + isDefinition: clang_isCursorDefinition(cursor) + ofClass: className]; break; } case CXCursor_ObjCProtocolDecl: @@ -847,12 +931,7 @@ - (void)rebuildIndex SCOPED_STR(protocolName, clang_getCursorSpelling(cursor)); SCKSourceLocation *sourceLocation = [[SCKSourceLocation alloc] initWithClangSourceLocation: clang_getCursorLocation(cursor)]; - - // NOTE: We could use CXCursor_ObjCProtocolDecl to parse protocol - // forward declarations as we do with CXCursor_ObjCClassDecl - [self setLocation: sourceLocation - forProtocol: [NSString stringWithUTF8String: protocolName] - isForwardDeclaration: (clang_isCursorDefinition(cursor) == NO)]; + NSMutableArray __block *adoptedProtocols = [NSMutableArray array]; clang_visitChildrenWithBlock(cursor, ^enum CXChildVisitResult(CXCursor protocolCursor, CXCursor parent) @@ -865,6 +944,12 @@ - (void)rebuildIndex switch (protocolCursor.kind) { + case CXCursor_ObjCProtocolRef: + { + [adoptedProtocols addObject: [NSString stringWithUTF8String: name]]; + + break; + } case CXCursor_ObjCPropertyDecl: { CXObjCPropertyAttrKind attributes = 0; @@ -907,6 +992,12 @@ - (void)rebuildIndex } return CXChildVisit_Recurse; }); + // NOTE: We could use CXCursor_ObjCProtocolDecl to parse protocol + // forward declarations as we do with CXCursor_ObjCClassDecl + [self setLocation: sourceLocation + forProtocol: [NSString stringWithUTF8String: protocolName] + adoptedProtocols: adoptedProtocols + isForwardDeclaration: (clang_isCursorDefinition(cursor) == NO)]; break; } case CXCursor_FunctionDecl: diff --git a/SCKIntrospection.h b/SCKIntrospection.h index 6cabb0e..f91c847 100644 --- a/SCKIntrospection.h +++ b/SCKIntrospection.h @@ -66,6 +66,7 @@ @property (nonatomic, readonly, retain) NSMutableArray *subclasses; @property (nonatomic, readonly, retain) NSMutableDictionary *categories; @property (nonatomic, readonly, retain) NSMutableDictionary *methods; +@property (nonatomic, readonly, retain) NSMutableDictionary *adoptedProtocols; @property (nonatomic, readonly, retain) NSMutableArray *ivars; @property (nonatomic, readonly, retain) NSMutableArray *properties; - (SCKIvar*)ivarForName: (NSString *)name; @@ -78,6 +79,7 @@ @property (nonatomic, readonly, retain) NSMutableDictionary *optionalMethods; @property (nonatomic, readonly, retain) NSMutableArray *requiredProperties; @property (nonatomic, readonly, retain) NSMutableArray *optionalProperties; +@property (nonatomic, readonly, retain) NSMutableDictionary *adoptedProtocols; - (SCKProperty*)requiredPropertyForName: (NSString *)aProperty; - (SCKProperty*)optionalPropertyForName: (NSString *)aProperty; @end @@ -85,6 +87,7 @@ @interface SCKCategory : SCKProgramComponent @property (nonatomic, readonly, retain) NSMutableDictionary *methods; @property (nonatomic, readonly, retain) NSMutableArray *properties; +@property (nonatomic, readonly, retain) NSMutableDictionary *adoptedProtocols; - (SCKProperty*)propertyForName: (NSString *)aProperty; @end diff --git a/SCKIntrospection.m b/SCKIntrospection.m index 9246834..6831f20 100644 --- a/SCKIntrospection.m +++ b/SCKIntrospection.m @@ -42,7 +42,7 @@ - (NSString*)description @end @implementation SCKClass -@synthesize subclasses, superclass, categories, methods, ivars, properties; +@synthesize subclasses, superclass, categories, methods, ivars, properties, adoptedProtocols; - (NSString*)description { NSMutableString *str = [self.name mutableCopy]; @@ -68,6 +68,7 @@ - (id)init methods = [NSMutableDictionary new]; ivars = [NSMutableArray new]; properties = [NSMutableArray new]; + adoptedProtocols = [NSMutableDictionary new]; return self; } - (id)initWithClass: (Class)cls @@ -141,7 +142,7 @@ - (SCKProperty*)propertyForName: (NSString*)name @end @implementation SCKProtocol -@synthesize requiredMethods, optionalMethods, requiredProperties, optionalProperties; +@synthesize requiredMethods, optionalMethods, requiredProperties, optionalProperties, adoptedProtocols; - (id)init { @@ -150,9 +151,35 @@ - (id)init requiredMethods = [NSMutableDictionary new]; optionalProperties = [NSMutableArray new]; requiredProperties = [NSMutableArray new]; + adoptedProtocols = [NSMutableDictionary new]; return self; } +- (NSString *)description +{ + NSMutableString *str = [NSMutableString stringWithFormat: @"%@ (%@)", self.parent.name, self.name]; + + for (id optionalMathod in [optionalMethods allValues]) + { + [str appendFormat: @"\n\t%@", optionalMathod]; + } + for (id requiredMethod in [requiredMethods allValues]) + { + [str appendFormat: @"\n\t%@", requiredMethod]; + } + for (id optionalProperty in optionalProperties) + { + [str appendFormat: @"\n\t%@", optionalProperty]; + } + for (id requiredProperty in requiredProperties) + { + [str appendFormat: @"\n\t%@", requiredProperty]; + } + + return str; +} + + - (SCKProperty*)requiredPropertyForName: (NSString*)name { return [[requiredProperties filteredCollectionWithBlock: ^ (SCKProperty *prop) @@ -172,12 +199,13 @@ - (SCKProperty*)optionalPropertyForName: (NSString*)name @end @implementation SCKCategory : SCKProgramComponent -@synthesize methods, properties; +@synthesize methods, properties, adoptedProtocols; - (id)init { SUPERINIT; methods = [NSMutableDictionary new]; properties = [NSMutableArray new]; + adoptedProtocols = [NSMutableDictionary new]; return self; } - (NSString*)description @@ -187,6 +215,10 @@ - (NSString*)description { [str appendFormat: @"\n\t%@", method]; } + for (id property in properties) + { + [str appendFormat: @"\n\t%@", property]; + } return str; } diff --git a/TODO b/TODO index 0842c83..fc94389 100644 --- a/TODO +++ b/TODO @@ -5,8 +5,6 @@ SourceCodeKit TODO - Macro parsing -- Adopted protocol parsing (for classes, categories and protocols) - - Documentation extraction - Capture the method parsing order (for DocGenerator needs) diff --git a/Tests/ParsingTestFiles/AB.h b/Tests/ParsingTestFiles/AB.h index 7d680a3..d40d1d0 100644 --- a/Tests/ParsingTestFiles/AB.h +++ b/Tests/ParsingTestFiles/AB.h @@ -30,11 +30,23 @@ enum enum2 {value4, value5, value6}; @protocol Protocol2; -@protocol Protocol3 +@protocol Protocol3 @end +@protocol Protocol4 -@interface A : NSObject ++ (void)haveHotChocolate; + +@end + +@protocol Protocol5 + +- (void)haveMilkshake; + +@end + + +@interface A : NSObject { NSString *text; } @@ -47,7 +59,7 @@ enum enum2 {value4, value5, value6}; @end -@interface A (AExtension) +@interface A (AExtension) @property NSString *propertyInsideCategory; diff --git a/Tests/TestClangParsing.m b/Tests/TestClangParsing.m index c5f3d4a..1d40320 100644 --- a/Tests/TestClangParsing.m +++ b/Tests/TestClangParsing.m @@ -77,13 +77,19 @@ - (void)testClass { SCKClass *classA = [self parsedClassForName: @"A"]; SCKClass *classB = [self parsedClassForName: @"B"]; + NSMutableDictionary *adoptedProtocols = [classA adoptedProtocols]; + NSSet *adoptedProtocolNames = S(@"Protocol1", @"Protocol3", @"Protocol4", @"Protocol5"); SCKFunction *function2 = [[self parsedFunctionsForNames: A(@"function2")] firstObject]; UKNotNil(classA); UKStringsEqual(@"A", [classA name]); UKStringsEqual(@"NSObject", [[classA superclass] name]); UKObjectsEqual([[classB superclass] name], [classA name]); - + + // Adopted protocol related tests + UKObjectsEqual(adoptedProtocolNames, SA([adoptedProtocols allKeys])); + UKObjectsEqual(SA([adoptedProtocols allKeys]), SA((id)[[[adoptedProtocols allValues] mappedCollection] name])); + // FIXME:UKStringsEqual(@"Dummy Class Description", [[class documentation] string]); UKStringsEqual(@"AB.h", [[[classA declaration] file] lastPathComponent]); @@ -100,12 +106,19 @@ - (void)testProtocol SCKProtocol *protocol1 = [self parsedProtocolForName: @"Protocol1"]; SCKProtocol *protocol2 = [self parsedProtocolForName: @"Protocol2"]; SCKProtocol *protocol3 = [self parsedProtocolForName: @"Protocol3"]; + NSMutableDictionary *adoptedProtocols = [protocol3 adoptedProtocols]; + NSSet *adoptedProtocolNames = S(@"Protocol1"); SCKFunction *function2 = [[self parsedFunctionsForNames: A(@"function2")] firstObject]; UKNotNil(protocol1); UKNotNil(protocol2); UKNotNil(protocol3); + // Adopted protocol related tests + UKObjectsEqual(adoptedProtocolNames, SA([adoptedProtocols allKeys])); + UKObjectsEqual(SA([adoptedProtocols allKeys]), SA((id)[[[adoptedProtocols allValues] mappedCollection] name])); + + UKFalse([protocol1 isForwardDeclaration]); UKTrue([protocol2 isForwardDeclaration]); UKFalse([protocol3 isForwardDeclaration]); @@ -195,17 +208,24 @@ - (void)testPropertyInProtocol UKTrue([[date declaration] offset] > [[string declaration] offset]); } + // NOTE: libclang versions prior to 21 parses all protocol properties as required. - (void)testCategory { SCKClass *classA = [self parsedClassForName: @"A"]; SCKClass *classB = [self parsedClassForName: @"B"]; NSMutableDictionary *categories = [classA categories]; + NSMutableDictionary *adoptedProtocols = [[[categories allValues] objectAtIndex: 0] adoptedProtocols]; + NSSet *adoptedProtocolNames = S(@"Protocol4", @"Protocol5"); SCKCategory *aExtension = [categories objectForKey: @"AExtension"]; UKNotNil(aExtension); UKStringsEqual(@"AExtension", [aExtension name]); UKStringsEqual(@"A", [[aExtension parent] name]); + // Adopted protocol related tests + UKObjectsEqual(adoptedProtocolNames, SA([adoptedProtocols allKeys])); + UKObjectsEqual(SA([adoptedProtocols allKeys]), SA((id)[[[adoptedProtocols allValues] mappedCollection] name])); + UKStringsEqual(@"AB.h", [[[aExtension declaration] file] lastPathComponent]); UKTrue([[classA declaration] offset] < [[aExtension declaration] offset]); UKTrue([[classB declaration] offset] > [[aExtension declaration] offset]); @@ -222,7 +242,7 @@ - (void)testMethodInCategory SCKCategory *aExtension = [categories objectForKey: @"AExtension"]; NSMutableDictionary *methods = [aExtension methods]; NSSet *methodNames = S(@"propertyInsideCategory", - @"setPropertyInsideCategory:", @"methodInCategory"); + @"setPropertyInsideCategory:", @"methodInCategory", @"haveHotChocolate", @"haveMilkshake"); UKObjectsEqual(methodNames, SA([methods allKeys])); UKObjectsEqual(SA([methods allKeys]), SA((id)[[[methods allValues] mappedCollection] name])); @@ -278,7 +298,7 @@ - (void)testMethod NSMutableDictionary *methods = [classA methods]; NSSet *methodNames = S(@"text", @"setText:", @"wakeUpAtDate:", @"sleepLater:", @"sleepNow", @"propertyInsideCategory", - @"setPropertyInsideCategory:", @"methodInCategory"); + @"setPropertyInsideCategory:", @"methodInCategory", @"haveHotChocolate", @"haveMilkshake"); UKObjectsEqual(methodNames, SA([methods allKeys])); UKObjectsEqual(SA([methods allKeys]), SA((id)[[[methods allValues] mappedCollection] name]));