Objective-C migration: Use NS_OPTIONS when enumerators
have shift/bitwise operators or are power of 2.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@186856 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp
index e4ec0c8..1bc0b8c 100644
--- a/lib/ARCMigrate/ObjCMT.cpp
+++ b/lib/ARCMigrate/ObjCMT.cpp
@@ -399,10 +399,12 @@
   return false;
 }
 
-static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
+static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
                                 const TypedefDecl *TypedefDcl,
-                                const NSAPI &NS, edit::Commit &commit) {
-  std::string ClassString = "NS_ENUM(NSInteger, ";
+                                const NSAPI &NS, edit::Commit &commit,
+                                 bool IsNSIntegerType) {
+  std::string ClassString =
+    IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
   ClassString += TypedefDcl->getIdentifier()->getName();
   ClassString += ')';
   SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
@@ -412,6 +414,29 @@
   return true;
 }
 
+static bool UseNSOptionsMacro(ASTContext &Ctx,
+                              const EnumDecl *EnumDcl) {
+  bool PowerOfTwo = true;
+  for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
+       EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
+    EnumConstantDecl *Enumerator = (*EI);
+    const Expr *InitExpr = Enumerator->getInitExpr();
+    if (!InitExpr) {
+      PowerOfTwo = false;
+      continue;
+    }
+    InitExpr = InitExpr->IgnoreImpCasts();
+    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
+      if (BO->isShiftOp() || BO->isBitwiseOp())
+        return true;
+    
+    uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
+    if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal))
+      PowerOfTwo = false;
+  }
+  return PowerOfTwo;
+}
+
 void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,   
                                             const ObjCImplementationDecl *ImpDecl) {
   const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
@@ -479,23 +504,29 @@
   QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
   bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
   bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
+  
   if (!IsNSIntegerType && !IsNSUIntegerType) {
     // Also check for typedef enum {...} TD;
     if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
       if (EnumTy->getDecl() == EnumDcl) {
-        // NS_ENUM must be available.
-        if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
+        bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
+        if (NSOptions) {
+          if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
+            return;
+        }
+        else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
           return;
         edit::Commit commit(*Editor);
-        rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit);
+        rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
         Editor->commit(commit);
-        return;
       }
-      else
-        return;
     }
-    else
-      return;
+    return;
+  }
+  if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
+    // We may still use NS_OPTIONS based on what we find in the enumertor list.
+    IsNSIntegerType = false;
+    IsNSUIntegerType = true;
   }
   
   // NS_ENUM must be available.
diff --git a/test/ARCMT/objcmt-ns-macros.m b/test/ARCMT/objcmt-ns-macros.m
index 42d7d2c..2ca8569 100644
--- a/test/ARCMT/objcmt-ns-macros.m
+++ b/test/ARCMT/objcmt-ns-macros.m
@@ -17,14 +17,13 @@
 
 enum {
     UIViewAutoresizingNone                 = 0,
-    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
-    UIViewAutoresizingFlexibleWidth        = 1 << 1,
-    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
-    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
-    UIViewAutoresizingFlexibleHeight       = 1 << 4,
-    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
+    UIViewAutoresizingFlexibleLeftMargin,
+    UIViewAutoresizingFlexibleWidth,
+    UIViewAutoresizingFlexibleRightMargin,
+    UIViewAutoresizingFlexibleTopMargin,
+    UIViewAutoresizingFlexibleHeight,
+    UIViewAutoresizingFlexibleBottomMargin
 };
-
 typedef NSUInteger UITableViewCellStyle;
 
 typedef enum {
@@ -35,6 +34,31 @@
     UIViewAnimationTransitionCurlDown,
 } UIViewAnimationTransition;
 
+typedef enum {
+    UIViewOne   = 0,
+    UIViewTwo   = 1 << 0,
+    UIViewThree = 1 << 1,
+    UIViewFour  = 1 << 2,
+    UIViewFive  = 1 << 3,
+    UIViewSix   = 1 << 4,
+    UIViewSeven = 1 << 5
+} UITableView;
+
+enum {
+  UIOne = 0,
+  UITwo = 0x1,
+  UIthree = 0x8,
+  UIFour = 0x100
+};
+typedef NSInteger UI;
+
+typedef enum {
+  UIP2One = 0,
+  UIP2Two = 0x1,
+  UIP2three = 0x8,
+  UIP2Four = 0x100
+} UIPOWER2;
+
 enum {
   UNOne,
   UNTwo
diff --git a/test/ARCMT/objcmt-ns-macros.m.result b/test/ARCMT/objcmt-ns-macros.m.result
index ee9b605..efd4fe8 100644
--- a/test/ARCMT/objcmt-ns-macros.m.result
+++ b/test/ARCMT/objcmt-ns-macros.m.result
@@ -17,16 +17,15 @@
 
 typedef NS_OPTIONS(NSUInteger, UITableViewCellStyle) {
     UIViewAutoresizingNone                 = 0,
-    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
-    UIViewAutoresizingFlexibleWidth        = 1 << 1,
-    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
-    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
-    UIViewAutoresizingFlexibleHeight       = 1 << 4,
-    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
+    UIViewAutoresizingFlexibleLeftMargin,
+    UIViewAutoresizingFlexibleWidth,
+    UIViewAutoresizingFlexibleRightMargin,
+    UIViewAutoresizingFlexibleTopMargin,
+    UIViewAutoresizingFlexibleHeight,
+    UIViewAutoresizingFlexibleBottomMargin
 };
 
 
-
 typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
     UIViewAnimationTransitionNone,
     UIViewAnimationTransitionFlipFromLeft,
@@ -35,6 +34,31 @@
     UIViewAnimationTransitionCurlDown,
 } ;
 
+typedef NS_OPTIONS(NSUInteger, UITableView) {
+    UIViewOne   = 0,
+    UIViewTwo   = 1 << 0,
+    UIViewThree = 1 << 1,
+    UIViewFour  = 1 << 2,
+    UIViewFive  = 1 << 3,
+    UIViewSix   = 1 << 4,
+    UIViewSeven = 1 << 5
+} ;
+
+typedef NS_OPTIONS(NSUInteger, UI) {
+  UIOne = 0,
+  UITwo = 0x1,
+  UIthree = 0x8,
+  UIFour = 0x100
+};
+
+
+typedef NS_OPTIONS(NSUInteger, UIPOWER2) {
+  UIP2One = 0,
+  UIP2Two = 0x1,
+  UIP2three = 0x8,
+  UIP2Four = 0x100
+} ;
+
 enum {
   UNOne,
   UNTwo