From: Fariborz Jahanian Date: Thu, 22 Aug 2013 18:35:27 +0000 (+0000) Subject: ObjectiveC migrator: Provide ARC annotations for X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d173d7885b6e7b40f2342cbc5aaaaef9c3e6c33b;p=clang ObjectiveC migrator: Provide ARC annotations for CF methods too. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189041 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 2d9afb03f8..59bac41ba0 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -57,8 +57,13 @@ class ObjCMigrateASTConsumer : public ASTConsumer { bool migrateAddFunctionAnnotation(ASTContext &Ctx, const FunctionDecl *FuncDecl); - void migrateObjCMethodDeclAnnotation(ASTContext &Ctx, - const ObjCMethodDecl *MethodDecl); + void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl); + + bool migrateAddMethodAnnotation(ASTContext &Ctx, + const ObjCMethodDecl *MethodDecl); + + void migrateMethodForCFAnnotation(ASTContext &Ctx, + const ObjCMethodDecl *MethodDecl); public: std::string MigrateDir; bool MigrateLiterals; @@ -73,7 +78,7 @@ public: Preprocessor &PP; bool IsOutputFile; llvm::SmallPtrSet ObjCProtocolDecls; - llvm::SmallVector CFFunctionIBCandidates; + llvm::SmallVector CFFunctionIBCandidates; ObjCMigrateASTConsumer(StringRef migrateDir, bool migrateLiterals, @@ -794,15 +799,17 @@ AuditedType (QualType AT, bool &IsPoniter) { } void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { + if (CFFunctionIBCandidates.empty()) + return; if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) { CFFunctionIBCandidates.clear(); FileId = 0; return; } // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED - const FunctionDecl *FirstFD = CFFunctionIBCandidates[0]; - const FunctionDecl *LastFD = - CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; + const Decl *FirstFD = CFFunctionIBCandidates[0]; + const Decl *LastFD = + CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n"; edit::Commit commit(*Editor); commit.insertBefore(FirstFD->getLocStart(), PragmaString); @@ -810,11 +817,15 @@ void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { SourceLocation EndLoc = LastFD->getLocEnd(); // get location just past end of function location. EndLoc = PP.getLocForEndOfToken(EndLoc); - Token Tok; - // get locaiton of token that comes after end of function. - bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); - if (!Failed) - EndLoc = Tok.getLocation(); + if (isa(LastFD)) { + // For Methods, EndLoc points to the ending semcolon. So, + // not of these extra work is needed. + Token Tok; + // get locaiton of token that comes after end of function. + bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); + if (!Failed) + EndLoc = Tok.getLocation(); + } commit.insertAfterToken(EndLoc, PragmaString); Editor->commit(commit); FileId = 0; @@ -838,7 +849,7 @@ void ObjCMigrateASTConsumer::migrateCFFunctions( if (!FileId) FileId = PP.getSourceManager().getFileID(FuncDecl->getLocation()).getHashValue(); } - else if (!CFFunctionIBCandidates.empty()) + else AnnotateImplicitBridging(Ctx); } @@ -912,14 +923,109 @@ bool ObjCMigrateASTConsumer::migrateAddFunctionAnnotation( return HasAtLeastOnePointer; } -void ObjCMigrateASTConsumer::migrateObjCMethodDeclAnnotation( +void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx, + ObjCContainerDecl *CDecl) { + if (!isa(CDecl)) + return; + + // migrate methods which can have instancetype as their result type. + for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), + MEnd = CDecl->meth_end(); + M != MEnd; ++M) { + ObjCMethodDecl *Method = (*M); + migrateMethodForCFAnnotation(Ctx, Method); + } +} + +void ObjCMigrateASTConsumer::migrateMethodForCFAnnotation( ASTContext &Ctx, const ObjCMethodDecl *MethodDecl) { - if (MethodDecl->hasAttr() || - MethodDecl->getAttr() || - MethodDecl->getAttr() || - MethodDecl->hasBody()) + if (MethodDecl->hasAttr()) { + assert(CFFunctionIBCandidates.empty() && + "Cannot have audited method inside user " + "provided CF_IMPLICIT_BRIDGING_ENABLE"); return; + } + + // Method must be annotated first. + bool Audited = migrateAddMethodAnnotation(Ctx, MethodDecl); + if (Audited) { + CFFunctionIBCandidates.push_back(MethodDecl); + if (!FileId) + FileId = PP.getSourceManager().getFileID(MethodDecl->getLocation()).getHashValue(); + } + else + AnnotateImplicitBridging(Ctx); +} + +bool ObjCMigrateASTConsumer::migrateAddMethodAnnotation(ASTContext &Ctx, + const ObjCMethodDecl *MethodDecl) { + if (MethodDecl->hasBody()) + return false; + + CallEffects CE = CallEffects::getEffect(MethodDecl); + bool MethodIsReturnAnnotated = (MethodDecl->getAttr() || + MethodDecl->getAttr()); + + // Trivial case of when funciton is annotated and has no argument. + if (MethodIsReturnAnnotated && + (MethodDecl->param_begin() == MethodDecl->param_end())) + return false; + + bool HasAtLeastOnePointer = MethodIsReturnAnnotated; + if (!MethodIsReturnAnnotated) { + RetEffect Ret = CE.getReturnValue(); + const char *AnnotationString = 0; + if (Ret.getObjKind() == RetEffect::CF && Ret.isOwned()) { + if (Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) + AnnotationString = " CF_RETURNS_RETAINED"; + } + else if (Ret.getObjKind() == RetEffect::CF && !Ret.isOwned()) { + if (Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) + AnnotationString = " CF_RETURNS_NOT_RETAINED"; + } + if (AnnotationString) { + edit::Commit commit(*Editor); + commit.insertBefore(MethodDecl->getLocEnd(), AnnotationString); + Editor->commit(commit); + HasAtLeastOnePointer = true; + } + else if (!AuditedType(MethodDecl->getResultType(), HasAtLeastOnePointer)) + return false; + } + + // At this point result type is either annotated or audited. + // Now, how about argument types. + llvm::ArrayRef AEArgs = CE.getArgs(); + unsigned i = 0; + for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), + pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { + const ParmVarDecl *pd = *pi; + ArgEffect AE = AEArgs[i]; + if (AE == DecRef /*CFConsumed annotated*/ || + AE == IncRef) { + if (AE == DecRef && !pd->getAttr() && + Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { + edit::Commit commit(*Editor); + commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); + Editor->commit(commit); + HasAtLeastOnePointer = true; + } + // When AE == IncRef, there is no attribute to annotate with. + // It is assumed that compiler will extract the info. from function + // API name. + HasAtLeastOnePointer = true; + continue; + } + + QualType AT = pd->getType(); + bool IsPointer; + if (!AuditedType(AT, IsPointer)) + return false; + else if (IsPointer) + HasAtLeastOnePointer = true; + } + return HasAtLeastOnePointer; } namespace { @@ -969,15 +1075,15 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { } else if (const FunctionDecl *FD = dyn_cast(*D)) migrateCFFunctions(Ctx, FD); - else if (const ObjCMethodDecl *MD = dyn_cast(*D)) - migrateObjCMethodDeclAnnotation(Ctx, MD); - // migrate methods which can have instancetype as their result type. - if (ObjCContainerDecl *CDecl = dyn_cast(*D)) + if (ObjCContainerDecl *CDecl = dyn_cast(*D)) { + // migrate methods which can have instancetype as their result type. migrateInstanceType(Ctx, CDecl); + // annotate methods with CF annotations. + migrateARCSafeAnnotation(Ctx, CDecl); + } } - if (!CFFunctionIBCandidates.empty()) - AnnotateImplicitBridging(Ctx); + AnnotateImplicitBridging(Ctx); } Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); diff --git a/test/ARCMT/objcmt-arc-cf-annotations.m.result b/test/ARCMT/objcmt-arc-cf-annotations.m.result index fbaa1ad44f..437369959f 100644 --- a/test/ARCMT/objcmt-arc-cf-annotations.m.result +++ b/test/ARCMT/objcmt-arc-cf-annotations.m.result @@ -197,7 +197,13 @@ typedef double NSTimeInterval; @interface NSString : NSObject - (NSUInteger)length; - (NSString *)stringByAppendingString:(NSString *)aString; + +CF_IMPLICIT_BRIDGING_ENABLED + - ( const char *)UTF8String; + +CF_IMPLICIT_BRIDGING_DISABLED + - (instancetype)initWithUTF8String:(const char *)nullTerminatedCString; + (instancetype)stringWithUTF8String:(const char *)nullTerminatedCString; @end @class NSString, NSURL, NSError; @@ -292,9 +298,9 @@ typedef const struct __DADissenter * DADissenterRef; extern DADissenterRef DADissenterCreate( CFAllocatorRef allocator, DAReturn status, CFStringRef string ) CF_RETURNS_RETAINED; @interface CIContext: NSObject { } -- (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r; -- (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r format:(CIFormat)f colorSpace:(CGColorSpaceRef)cs; -- (CGLayerRef)createCGLayerWithSize:(CGSize)size info:(CFDictionaryRef)d; +- (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r CF_RETURNS_RETAINED; +- (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r format:(CIFormat)f colorSpace:(CGColorSpaceRef)cs CF_RETURNS_RETAINED; +- (CGLayerRef)createCGLayerWithSize:(CGSize)size info:(CFDictionaryRef)d CF_RETURNS_RETAINED; @end extern NSString* const QCRendererEventKey; @protocol QCCompositionRenderer - (NSDictionary*) attributes; @end @interface QCRenderer : NSObject { @@ -819,8 +825,14 @@ void rdar_6866843() { typedef CFTypeRef OtherRef; @interface RDar6877235 : NSObject {} -- (CFTypeRef)_copyCFTypeRef; -- (OtherRef)_copyOtherRef; + +CF_IMPLICIT_BRIDGING_ENABLED + +- (CFTypeRef)_copyCFTypeRef CF_RETURNS_RETAINED; +- (OtherRef)_copyOtherRef CF_RETURNS_RETAINED; + +CF_IMPLICIT_BRIDGING_DISABLED + @end @implementation RDar6877235 @@ -954,7 +966,13 @@ static void PR4230_new(void) typedef struct s6893565* TD6893565; @interface RDar6893565 {} + +CF_IMPLICIT_BRIDGING_ENABLED + -(TD6893565)newThing; + +CF_IMPLICIT_BRIDGING_DISABLED + @end @implementation RDar6893565 @@ -1373,7 +1391,13 @@ typedef NSString* MyStringTy; - (int) returnsAnOwnedInt NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to methods that return an Objective-C object}} - (id) pseudoInit NS_CONSUMES_SELF NS_RETURNS_RETAINED; + (void) consume:(id) NS_CONSUMED x; + +CF_IMPLICIT_BRIDGING_ENABLED + + (void) consume2:(id) CF_CONSUMED x; + +CF_IMPLICIT_BRIDGING_DISABLED + @end static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions and methods}} @@ -1448,9 +1472,21 @@ void testattr4() { - (NSDate*) returnsCFRetained CF_RETURNS_RETAINED; - (CFDateRef) returnsCFRetainedAsCF CF_RETURNS_RETAINED; - (CFDateRef) newCFRetainedAsCF CF_RETURNS_NOT_RETAINED; -- (CFDateRef) newCFRetainedAsCFNoAttr; + +CF_IMPLICIT_BRIDGING_ENABLED + +- (CFDateRef) newCFRetainedAsCFNoAttr CF_RETURNS_RETAINED; + +CF_IMPLICIT_BRIDGING_DISABLED + - (NSDate*) alsoReturnsRetained; -- (CFDateRef) alsoReturnsRetainedAsCF; + +CF_IMPLICIT_BRIDGING_ENABLED + +- (CFDateRef) alsoReturnsRetainedAsCF CF_RETURNS_NOT_RETAINED; + +CF_IMPLICIT_BRIDGING_DISABLED + - (NSDate*) returnsNSRetained NS_RETURNS_RETAINED; @end