[objcmt] When rewriting to array/dictionary literals, add an explicit
cast to 'id' for any argument that requires it.

Part of rdar://11438360.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156782 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Edit/EditedSource.cpp b/lib/Edit/EditedSource.cpp
index 5b7fa4a..b2a1663 100644
--- a/lib/Edit/EditedSource.cpp
+++ b/lib/Edit/EditedSource.cpp
@@ -100,8 +100,11 @@
     FileOffset B = I->first;
     FileOffset E = B.getWithOffset(FA.RemoveLen);
 
+    if (BeginOffs == B)
+      break;
+
     if (BeginOffs < E) {
-      if (BeginOffs >= B) {
+      if (BeginOffs > B) {
         BeginOffs = E;
         ++I;
       }
diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp
index 24a0db1..8c3c7a5 100644
--- a/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -229,6 +229,9 @@
 // rewriteToArrayLiteral.
 //===----------------------------------------------------------------------===//
 
+/// \brief Adds an explicit cast to 'id' if the type is not objc object.
+static void objectifyExpr(const Expr *E, Commit &commit);
+
 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
                                   const NSAPI &NS, Commit &commit) {
   Selector Sel = Msg->getSelector();
@@ -244,6 +247,7 @@
   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
     if (Msg->getNumArgs() != 1)
       return false;
+    objectifyExpr(Msg->getArg(0), commit);
     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
     commit.replaceWithInner(MsgRange, ArgRange);
     commit.insertWrap("@[", ArgRange, "]");
@@ -257,6 +261,9 @@
     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
       return false;
 
+    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
+      objectifyExpr(Msg->getArg(i), commit);
+
     if (Msg->getNumArgs() == 1) {
       commit.replace(MsgRange, "@[]");
       return true;
@@ -291,6 +298,10 @@
                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
     if (Msg->getNumArgs() != 2)
       return false;
+
+    objectifyExpr(Msg->getArg(0), commit);
+    objectifyExpr(Msg->getArg(1), commit);
+
     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
     // Insert key before the value.
@@ -319,6 +330,9 @@
     }
 
     for (unsigned i = 0; i < SentinelIdx; i += 2) {
+      objectifyExpr(Msg->getArg(i), commit);
+      objectifyExpr(Msg->getArg(i+1), commit);
+
       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
       // Insert value after key.
@@ -585,3 +599,52 @@
   }
   return true;
 }
+
+static bool castOperatorNeedsParens(const Expr *FullExpr) {
+  const Expr* Expr = FullExpr->IgnoreImpCasts();
+  if (isa<ArraySubscriptExpr>(Expr) ||
+      isa<CallExpr>(Expr) ||
+      isa<DeclRefExpr>(Expr) ||
+      isa<CastExpr>(Expr) ||
+      isa<CXXNewExpr>(Expr) ||
+      isa<CXXConstructExpr>(Expr) ||
+      isa<CXXDeleteExpr>(Expr) ||
+      isa<CXXNoexceptExpr>(Expr) ||
+      isa<CXXPseudoDestructorExpr>(Expr) ||
+      isa<CXXScalarValueInitExpr>(Expr) ||
+      isa<CXXThisExpr>(Expr) ||
+      isa<CXXTypeidExpr>(Expr) ||
+      isa<CXXUnresolvedConstructExpr>(Expr) ||
+      isa<ObjCMessageExpr>(Expr) ||
+      isa<ObjCPropertyRefExpr>(Expr) ||
+      isa<ObjCProtocolExpr>(Expr) ||
+      isa<MemberExpr>(Expr) ||
+      isa<ParenExpr>(FullExpr) ||
+      isa<ParenListExpr>(Expr) ||
+      isa<SizeOfPackExpr>(Expr) ||
+      isa<UnaryOperator>(Expr))
+    return false;
+
+  return true;
+}
+
+static void objectifyExpr(const Expr *E, Commit &commit) {
+  if (!E) return;
+
+  QualType T = E->getType();
+  if (T->isObjCObjectPointerType()) {
+    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
+        return;
+    } else {
+      return;
+    }
+  } else if (!T->isPointerType()) {
+    return;
+  }
+
+  SourceRange Range = E->getSourceRange();
+  if (castOperatorNeedsParens(E))
+    commit.insertWrap("(", Range, ")");
+  commit.insertBefore(Range.getBegin(), "(id)");
+}
diff --git a/test/ARCMT/objcmt-numeric-literals.m b/test/ARCMT/objcmt-numeric-literals.m
index b86af4d..7f33dc2 100644
--- a/test/ARCMT/objcmt-numeric-literals.m
+++ b/test/ARCMT/objcmt-numeric-literals.m
@@ -1,6 +1,7 @@
 // RUN: rm -rf %t
 // RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++ 
 // RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c++ %s.result
 
 #define YES __objc_yes
 #define NO __objc_no
diff --git a/test/ARCMT/objcmt-numeric-literals.m.result b/test/ARCMT/objcmt-numeric-literals.m.result
index 1c4187aae..8e34345 100644
--- a/test/ARCMT/objcmt-numeric-literals.m.result
+++ b/test/ARCMT/objcmt-numeric-literals.m.result
@@ -1,6 +1,7 @@
 // RUN: rm -rf %t
 // RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c++ 
 // RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c++ %s.result
 
 #define YES __objc_yes
 #define NO __objc_no
diff --git a/test/ARCMT/objcmt-subscripting-literals.m b/test/ARCMT/objcmt-subscripting-literals.m
index 3d26efe..1475951 100644
--- a/test/ARCMT/objcmt-subscripting-literals.m
+++ b/test/ARCMT/objcmt-subscripting-literals.m
@@ -1,10 +1,13 @@
 // RUN: rm -rf %t
 // RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11 
 // RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
 
 typedef signed char BOOL;
 #define nil ((void*) 0)
 
+typedef const struct __CFString * CFStringRef;
+
 @interface NSObject
 + (id)alloc;
 @end
@@ -135,3 +138,17 @@
   o = [*parr objectAtIndex:2];
 }
 @end
+
+extern const CFStringRef globStr;
+
+void test1(NSString *str) {
+  NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: str, globStr, nil];
+  dict = [NSDictionary dictionaryWithObjectsAndKeys: globStr, str, nil];
+  dict = [NSDictionary dictionaryWithObject:str forKey:globStr];
+  dict = [NSDictionary dictionaryWithObject:globStr forKey:str];
+
+  NSArray *arr = [NSArray arrayWithObjects: globStr, globStr, nil];
+  arr = [NSArray arrayWithObjects: str, globStr, nil];
+  arr = [NSArray arrayWithObjects: globStr, str, nil];
+  arr = [NSArray arrayWithObject:globStr];
+}
diff --git a/test/ARCMT/objcmt-subscripting-literals.m.result b/test/ARCMT/objcmt-subscripting-literals.m.result
index 8ac6dcc..5c8a4ec 100644
--- a/test/ARCMT/objcmt-subscripting-literals.m.result
+++ b/test/ARCMT/objcmt-subscripting-literals.m.result
@@ -1,10 +1,13 @@
 // RUN: rm -rf %t
 // RUN: %clang_cc1 -objcmt-migrate-literals -objcmt-migrate-subscripting -mt-migrate-directory %t %s -x objective-c -triple x86_64-apple-darwin11 
 // RUN: c-arcmt-test -mt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s.result
 
 typedef signed char BOOL;
 #define nil ((void*) 0)
 
+typedef const struct __CFString * CFStringRef;
+
 @interface NSObject
 + (id)alloc;
 @end
@@ -135,3 +138,17 @@
   o = (*parr)[2];
 }
 @end
+
+extern const CFStringRef globStr;
+
+void test1(NSString *str) {
+  NSDictionary *dict = @{(id)globStr: str};
+  dict = @{str: (id)globStr};
+  dict = @{(id)globStr: str};
+  dict = @{str: (id)globStr};
+
+  NSArray *arr = @[(id)globStr, (id)globStr];
+  arr = @[str, (id)globStr];
+  arr = @[(id)globStr, str];
+  arr = @[(id)globStr];
+}