From: Argyrios Kyrtzidis Date: Tue, 10 Dec 2013 18:36:49 +0000 (+0000) Subject: [objcmt] Add a modernization option to infer and suggest designated initializers. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ce59229a4744c42fa40d22c9b676961aa3110bab;p=clang [objcmt] Add a modernization option to infer and suggest designated initializers. rdar://15509284 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@196943 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 2281777de3..db4d13ee49 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -184,6 +184,8 @@ def objcmt_returns_innerpointer_property : Flag<["-"], "objcmt-returns-innerpoin HelpText<"Enable migration to annotate property with NS_RETURNS_INNER_POINTER">; def objcmt_ns_nonatomic_iosonly: Flag<["-"], "objcmt-ns-nonatomic-iosonly">, Flags<[CC1Option]>, HelpText<"Enable migration to use NS_NONATOMIC_IOSONLY macro for setting property's 'atomic' attribute">; +def objcmt_migrate_designated_init : Flag<["-"], "objcmt-migrate-designated-init">, Flags<[CC1Option]>, + HelpText<"Enable migration to infer NS_DESIGNATED_INITIALIZER for initializer methods">; def objcmt_white_list_dir_path: Joined<["-"], "objcmt-white-list-dir-path=">, Flags<[CC1Option]>, HelpText<"Only modify files with a filename contained in the provided directory path">; diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 4b321e86d2..818427a4ef 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -179,10 +179,13 @@ public: ObjCMT_ReturnsInnerPointerProperty = 0x200, /// \brief use NS_NONATOMIC_IOSONLY for property 'atomic' attribute ObjCMT_NsAtomicIOSOnlyProperty = 0x400, + /// \brief Enable inferring NS_DESIGNATED_INITIALIZER for ObjC methods. + ObjCMT_DesignatedInitializer = 0x800, ObjCMT_MigrateDecls = (ObjCMT_ReadonlyProperty | ObjCMT_ReadwriteProperty | ObjCMT_Annotation | ObjCMT_Instancetype | ObjCMT_NsMacros | ObjCMT_ProtocolConformance | - ObjCMT_NsAtomicIOSOnlyProperty), + ObjCMT_NsAtomicIOSOnlyProperty | + ObjCMT_DesignatedInitializer), ObjCMT_MigrateAll = (ObjCMT_Literals | ObjCMT_Subscripting | ObjCMT_MigrateDecls) }; unsigned ObjCMTAction; diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 1615925eef..c14fca2738 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -75,6 +75,10 @@ class ObjCMigrateASTConsumer : public ASTConsumer { void migrateAddMethodAnnotation(ASTContext &Ctx, const ObjCMethodDecl *MethodDecl); + + void inferDesignatedInitializers(ASTContext &Ctx, + const ObjCImplementationDecl *ImplD); + public: std::string MigrateDir; unsigned ASTMigrateActions; @@ -1538,6 +1542,55 @@ void ObjCMigrateASTConsumer::migrateAddMethodAnnotation( return; } +namespace { +class SuperInitChecker : public RecursiveASTVisitor { +public: + bool shouldVisitTemplateInstantiations() const { return false; } + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) { + if (E->getMethodFamily() == OMF_init) + return false; + } + return true; + } +}; +} // anonymous namespace + +static bool hasSuperInitCall(const ObjCMethodDecl *MD) { + return !SuperInitChecker().TraverseStmt(MD->getBody()); +} + +void ObjCMigrateASTConsumer::inferDesignatedInitializers( + ASTContext &Ctx, + const ObjCImplementationDecl *ImplD) { + + const ObjCInterfaceDecl *IFace = ImplD->getClassInterface(); + if (!IFace || IFace->hasDesignatedInitializers()) + return; + if (!Ctx.Idents.get("NS_DESIGNATED_INITIALIZER").hasMacroDefinition()) + return; + + for (ObjCImplementationDecl::instmeth_iterator + I = ImplD->instmeth_begin(), E = ImplD->instmeth_end(); I != E; ++I) { + const ObjCMethodDecl *MD = *I; + if (MD->isDeprecated() || + MD->getMethodFamily() != OMF_init || + MD->isDesignatedInitializerForTheInterface()) + continue; + const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(), + /*isInstance=*/true); + if (!IFaceM) + continue; + if (hasSuperInitCall(MD)) { + edit::Commit commit(*Editor); + commit.insert(IFaceM->getLocEnd(), " NS_DESIGNATED_INITIALIZER"); + Editor->commit(commit); + } + } +} + namespace { class RewritesReceiver : public edit::EditsReceiver { @@ -1657,6 +1710,12 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) migrateARCSafeAnnotation(Ctx, CDecl); } + + if (const ObjCImplementationDecl * + ImplD = dyn_cast(*D)) { + if (ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) + inferDesignatedInitializers(Ctx, ImplD); + } } if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) AnnotateImplicitBridging(Ctx); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index dfa9ab2fe1..3ab5330cac 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -820,6 +820,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ObjCMTAction |= FrontendOptions::ObjCMT_AtomicProperty; if (Args.hasArg(OPT_objcmt_ns_nonatomic_iosonly)) Opts.ObjCMTAction |= FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty; + if (Args.hasArg(OPT_objcmt_migrate_designated_init)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_DesignatedInitializer; if (Args.hasArg(OPT_objcmt_migrate_all)) Opts.ObjCMTAction |= FrontendOptions::ObjCMT_MigrateDecls; diff --git a/test/ARCMT/objcmt-designated-initializer.m b/test/ARCMT/objcmt-designated-initializer.m new file mode 100644 index 0000000000..279d4f35d8 --- /dev/null +++ b/test/ARCMT/objcmt-designated-initializer.m @@ -0,0 +1,44 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -objcmt-migrate-designated-init %s -triple x86_64-apple-darwin11 -fobjc-arc -migrate -o %t.remap +// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -x objective-c -fobjc-arc %s.result + +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) + +@class NSString; + +@interface B1 +-(id)init; +@end + +@interface S1 : B1 +-(id)initWithFoo:(NSString*)foo; +@end + +@implementation S1 +-(id)initWithFoo:(NSString*)foo +{ + self = [super init]; + if (self) { + } + return self; +} +@end + +@interface B2 +-(id)init NS_DESIGNATED_INITIALIZER; +@end + +@interface S2 : B2 +-(id)init; +@end + +@implementation S2 +-(id)init +{ + self = [super init]; + if (self) { + } + return self; +} +@end diff --git a/test/ARCMT/objcmt-designated-initializer.m.result b/test/ARCMT/objcmt-designated-initializer.m.result new file mode 100644 index 0000000000..4c59b0cc58 --- /dev/null +++ b/test/ARCMT/objcmt-designated-initializer.m.result @@ -0,0 +1,44 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -objcmt-migrate-designated-init %s -triple x86_64-apple-darwin11 -fobjc-arc -migrate -o %t.remap +// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fsyntax-only -x objective-c -fobjc-arc %s.result + +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) + +@class NSString; + +@interface B1 +-(id)init; +@end + +@interface S1 : B1 +-(id)initWithFoo:(NSString*)foo NS_DESIGNATED_INITIALIZER; +@end + +@implementation S1 +-(id)initWithFoo:(NSString*)foo +{ + self = [super init]; + if (self) { + } + return self; +} +@end + +@interface B2 +-(id)init NS_DESIGNATED_INITIALIZER; +@end + +@interface S2 : B2 +-(id)init; +@end + +@implementation S2 +-(id)init +{ + self = [super init]; + if (self) { + } + return self; +} +@end