]> granicus.if.org Git - clang/commitdiff
[objcmt] Add a modernization option to infer and suggest designated initializers.
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Tue, 10 Dec 2013 18:36:49 +0000 (18:36 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Tue, 10 Dec 2013 18:36:49 +0000 (18:36 +0000)
rdar://15509284

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@196943 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Driver/Options.td
include/clang/Frontend/FrontendOptions.h
lib/ARCMigrate/ObjCMT.cpp
lib/Frontend/CompilerInvocation.cpp
test/ARCMT/objcmt-designated-initializer.m [new file with mode: 0644]
test/ARCMT/objcmt-designated-initializer.m.result [new file with mode: 0644]

index 2281777de31c49f294cd376f911e01a5209fdd63..db4d13ee496f5c18818d6a14eef47ab9d5499510 100644 (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">;
 
index 4b321e86d29ca6b15a603cab98e90ccc4a9fd8de..818427a4ef6592b707bcf37eca8d0a2a75bacb2a 100644 (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;
index 1615925eefca599ef99a3e503c096f0656aa397a..c14fca2738db976d2ed2cfd9105aa070bc156cba 100644 (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);
index dfa9ab2fe1af8f9d62ca1aada22380bab5baa6d1..3ab5330cac34c769df230655c100d0a0777e7f1f 100644 (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;
 
diff --git a/test/ARCMT/objcmt-designated-initializer.m b/test/ARCMT/objcmt-designated-initializer.m
new file mode 100644 (file)
index 0000000..279d4f3
--- /dev/null
@@ -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 (file)
index 0000000..4c59b0c
--- /dev/null
@@ -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