[objcmt] Rewrite messages to NSString's stringWithUTF8String:/stringWithCString:
to use the @() boxing syntax.

It will also rewrite uses of stringWithCString:encoding: where the encoding that is
used is NSASCIIStringEncoding or NSUTF8StringEncoding.

rdar://11438360

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156868 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/NSAPI.h b/include/clang/AST/NSAPI.h
index 7e61c96..3fcd8b4 100644
--- a/include/clang/AST/NSAPI.h
+++ b/include/clang/AST/NSAPI.h
@@ -16,6 +16,7 @@
 namespace clang {
   class ASTContext;
   class QualType;
+  class Expr;
 
 // \brief Provides info and caches identifiers/selectors for NSFoundation API.
 class NSAPI {
@@ -37,15 +38,33 @@
 
   enum NSStringMethodKind {
     NSStr_stringWithString,
+    NSStr_stringWithUTF8String,
+    NSStr_stringWithCStringEncoding,
+    NSStr_stringWithCString,
     NSStr_initWithString
   };
-  static const unsigned NumNSStringMethods = 2;
+  static const unsigned NumNSStringMethods = 5;
 
   IdentifierInfo *getNSClassId(NSClassIdKindKind K) const;
 
   /// \brief The Objective-C NSString selectors.
   Selector getNSStringSelector(NSStringMethodKind MK) const;
 
+  /// \brief Return NSStringMethodKind if \param Sel is such a selector.
+  llvm::Optional<NSStringMethodKind> getNSStringMethodKind(Selector Sel) const;
+
+  /// \brief Returns true if the expression \param E is a reference of
+  /// "NSUTF8StringEncoding" enum constant.
+  bool isNSUTF8StringEncodingConstant(const Expr *E) const {
+    return isObjCEnumerator(E, "NSUTF8StringEncoding", NSUTF8StringEncodingId);
+  }
+
+  /// \brief Returns true if the expression \param E is a reference of
+  /// "NSASCIIStringEncoding" enum constant.
+  bool isNSASCIIStringEncodingConstant(const Expr *E) const {
+    return isObjCEnumerator(E, "NSASCIIStringEncoding",NSASCIIStringEncodingId);
+  }
+
   /// \brief Enumerates the NSArray methods used to generate literals.
   enum NSArrayMethodKind {
     NSArr_array,
@@ -138,6 +157,8 @@
 
 private:
   bool isObjCTypedef(QualType T, StringRef name, IdentifierInfo *&II) const;
+  bool isObjCEnumerator(const Expr *E,
+                        StringRef name, IdentifierInfo *&II) const;
 
   ASTContext &Ctx;
 
@@ -156,6 +177,7 @@
   mutable Selector NSNumberInstanceSelectors[NumNSNumberLiteralMethods];
 
   mutable IdentifierInfo *BOOLId, *NSIntegerId, *NSUIntegerId;
+  mutable IdentifierInfo *NSASCIIStringEncodingId, *NSUTF8StringEncodingId;
 };
 
 }  // end namespace clang
diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp
index c56e6c1..72251d3 100644
--- a/lib/AST/NSAPI.cpp
+++ b/lib/AST/NSAPI.cpp
@@ -9,11 +9,13 @@
 
 #include "clang/AST/NSAPI.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
 
 using namespace clang;
 
 NSAPI::NSAPI(ASTContext &ctx)
-  : Ctx(ctx), ClassIds(), BOOLId(0), NSIntegerId(0), NSUIntegerId(0) {
+  : Ctx(ctx), ClassIds(), BOOLId(0), NSIntegerId(0), NSUIntegerId(0),
+    NSASCIIStringEncodingId(0), NSUTF8StringEncodingId(0) {
 }
 
 IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const {
@@ -40,6 +42,21 @@
     case NSStr_stringWithString:
       Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithString"));
       break;
+    case NSStr_stringWithUTF8String:
+      Sel = Ctx.Selectors.getUnarySelector(
+                                       &Ctx.Idents.get("stringWithUTF8String"));
+      break;
+    case NSStr_stringWithCStringEncoding: {
+      IdentifierInfo *KeyIdents[] = {
+        &Ctx.Idents.get("stringWithCString"),
+        &Ctx.Idents.get("encoding")
+      };
+      Sel = Ctx.Selectors.getSelector(2, KeyIdents);
+      break;
+    }
+    case NSStr_stringWithCString:
+      Sel= Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithCString"));
+      break;
     case NSStr_initWithString:
       Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithString"));
       break;
@@ -50,6 +67,17 @@
   return NSStringSelectors[MK];
 }
 
+llvm::Optional<NSAPI::NSStringMethodKind>
+NSAPI::getNSStringMethodKind(Selector Sel) const {
+  for (unsigned i = 0; i != NumNSStringMethods; ++i) {
+    NSStringMethodKind MK = NSStringMethodKind(i);
+    if (Sel == getNSStringSelector(MK))
+      return MK;
+  }
+
+  return llvm::Optional<NSStringMethodKind>();
+}
+
 Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const {
   if (NSArraySelectors[MK].isNull()) {
     Selector Sel;
@@ -353,3 +381,21 @@
 
   return false;
 }
+
+bool NSAPI::isObjCEnumerator(const Expr *E,
+                             StringRef name, IdentifierInfo *&II) const {
+  if (!Ctx.getLangOpts().ObjC1)
+    return false;
+  if (!E)
+    return false;
+
+  if (!II)
+    II = &Ctx.Idents.get(name);
+
+  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
+    if (const EnumConstantDecl *
+          EnumD = dyn_cast_or_null<EnumConstantDecl>(DRE->getDecl()))
+      return EnumD->getIdentifier() == II;
+
+  return false;
+}
diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp
index 1d368d6..38584d6 100644
--- a/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -211,6 +211,8 @@
                                   const NSAPI &NS, Commit &commit);
 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
                                             const NSAPI &NS, Commit &commit);
+static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
+                                           const NSAPI &NS, Commit &commit);
 
 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
                                       const NSAPI &NS, Commit &commit) {
@@ -224,6 +226,8 @@
     return rewriteToDictionaryLiteral(Msg, NS, commit);
   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
     return rewriteToNumberLiteral(Msg, NS, commit);
+  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
+    return rewriteToStringBoxedExpression(Msg, NS, commit);
 
   return false;
 }
@@ -791,7 +795,7 @@
     return false;
 
   SourceRange ArgRange = OrigArg->getSourceRange();
-  commit.replaceWithInner(Msg->getSourceRange(), OrigArg->getSourceRange());
+  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
 
   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
     commit.insertBefore(ArgRange.getBegin(), "@");
@@ -800,3 +804,68 @@
 
   return true;
 }
+
+//===----------------------------------------------------------------------===//
+// rewriteToStringBoxedExpression.
+//===----------------------------------------------------------------------===//
+
+static bool doRewriteToUTF8StringBoxedExpressionHelper(
+                                              const ObjCMessageExpr *Msg,
+                                              const NSAPI &NS, Commit &commit) {
+  const Expr *Arg = Msg->getArg(0);
+  if (Arg->isTypeDependent())
+    return false;
+
+  const Expr *OrigArg = Arg->IgnoreImpCasts();
+  QualType OrigTy = OrigArg->getType();
+
+  if (const StringLiteral *
+        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
+    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
+    commit.insert(StrE->getLocStart(), "@");
+    return true;
+  }
+
+  ASTContext &Ctx = NS.getASTContext();
+
+  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
+    QualType PointeeType = PT->getPointeeType();
+    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
+      SourceRange ArgRange = OrigArg->getSourceRange();
+      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
+
+      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
+        commit.insertBefore(ArgRange.getBegin(), "@");
+      else
+        commit.insertWrap("@(", ArgRange, ")");
+      
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
+                                           const NSAPI &NS, Commit &commit) {
+  Selector Sel = Msg->getSelector();
+
+  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
+      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
+    if (Msg->getNumArgs() != 1)
+      return false;
+    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
+  }
+
+  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
+    if (Msg->getNumArgs() != 2)
+      return false;
+
+    const Expr *encodingArg = Msg->getArg(1);
+    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
+        NS.isNSASCIIStringEncodingConstant(encodingArg))
+      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
+  }
+
+  return false;
+}
diff --git a/test/ARCMT/objcmt-boxing.m b/test/ARCMT/objcmt-boxing.m
index faff5a9..625a5a1 100644
--- a/test/ARCMT/objcmt-boxing.m
+++ b/test/ARCMT/objcmt-boxing.m
@@ -54,6 +54,22 @@
 + (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
 @end
 
+enum {
+    NSASCIIStringEncoding = 1,
+    NSUTF8StringEncoding = 4,
+    NSUnicodeStringEncoding = 10
+};
+typedef NSUInteger NSStringEncoding;
+
+@interface NSString : NSObject
+@end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc;
++ (id)stringWithCString:(const char *)bytes;
+@end
+
 enum MyEnm {
   ME_foo
 };
@@ -66,3 +82,17 @@
   [NSNumber numberWithInteger:myenum];
   [NSNumber numberWithInteger:ME_foo];
 }
+
+void boxString() {
+  NSString *s = [NSString stringWithUTF8String:"box"];
+  const char *cstr1;
+  char *cstr2;
+  s = [NSString stringWithUTF8String:cstr1];
+  s = [NSString stringWithUTF8String:cstr2];
+  s = [NSString stringWithCString:cstr1 encoding:NSASCIIStringEncoding];
+  s = [NSString stringWithCString:cstr1 encoding:NSUTF8StringEncoding];
+  s = [NSString stringWithCString:cstr1 encoding: NSUnicodeStringEncoding];
+  NSStringEncoding encode;
+  s = [NSString stringWithCString:cstr1 encoding:encode];
+  s = [NSString stringWithCString:cstr1];
+}
diff --git a/test/ARCMT/objcmt-boxing.m.result b/test/ARCMT/objcmt-boxing.m.result
index fa8ab11..3e945d4 100644
--- a/test/ARCMT/objcmt-boxing.m.result
+++ b/test/ARCMT/objcmt-boxing.m.result
@@ -54,6 +54,22 @@
 + (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
 @end
 
+enum {
+    NSASCIIStringEncoding = 1,
+    NSUTF8StringEncoding = 4,
+    NSUnicodeStringEncoding = 10
+};
+typedef NSUInteger NSStringEncoding;
+
+@interface NSString : NSObject
+@end
+
+@interface NSString (NSStringExtensionMethods)
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc;
++ (id)stringWithCString:(const char *)bytes;
+@end
+
 enum MyEnm {
   ME_foo
 };
@@ -66,3 +82,17 @@
   @(myenum);
   @(ME_foo);
 }
+
+void boxString() {
+  NSString *s = @"box";
+  const char *cstr1;
+  char *cstr2;
+  s = @(cstr1);
+  s = @(cstr2);
+  s = @(cstr1);
+  s = @(cstr1);
+  s = [NSString stringWithCString:cstr1 encoding: NSUnicodeStringEncoding];
+  NSStringEncoding encode;
+  s = [NSString stringWithCString:cstr1 encoding:encode];
+  s = @(cstr1);
+}