[objcmt] Add a modernization option to infer and suggest designated initializers.

rdar://15509284

llvm-svn: 196943
This commit is contained in:
Argyrios Kyrtzidis 2013-12-10 18:36:49 +00:00
parent e919fc20a6
commit 4f2ecc6177
6 changed files with 155 additions and 1 deletions

View File

@ -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">;

View File

@ -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;

View File

@ -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<SuperInitChecker> {
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<ObjCImplementationDecl>(*D)) {
if (ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer)
inferDesignatedInitializers(Ctx, ImplD);
}
}
if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
AnnotateImplicitBridging(Ctx);

View File

@ -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;

View File

@ -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

View File

@ -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