diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Edit/RewriteObjCFoundationAPI.cpp')
| -rw-r--r-- | contrib/llvm/tools/clang/lib/Edit/RewriteObjCFoundationAPI.cpp | 1179 | 
1 files changed, 1179 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/contrib/llvm/tools/clang/lib/Edit/RewriteObjCFoundationAPI.cpp new file mode 100644 index 000000000000..b53a70d87582 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -0,0 +1,1179 @@ +//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Rewrites legacy method calls to modern syntax. +// +//===----------------------------------------------------------------------===// + +#include "clang/Edit/Rewriters.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/NSAPI.h" +#include "clang/AST/ParentMap.h" +#include "clang/Edit/Commit.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; +using namespace edit; + +static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, +                                    IdentifierInfo *&ClassId, +                                    const LangOptions &LangOpts) { +  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) +    return false; + +  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); +  if (!Receiver) +    return false; +  ClassId = Receiver->getIdentifier(); + +  if (Msg->getReceiverKind() == ObjCMessageExpr::Class) +    return true; + +  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, +  // since the change from +1 to +0 will be handled fine by ARC. +  if (LangOpts.ObjCAutoRefCount) { +    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { +      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( +                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { +        if (Rec->getMethodFamily() == OMF_alloc) +          return true; +      } +    } +  } + +  return false; +} + +//===----------------------------------------------------------------------===// +// rewriteObjCRedundantCallWithLiteral. +//===----------------------------------------------------------------------===// + +bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, +                                              const NSAPI &NS, Commit &commit) { +  IdentifierInfo *II = nullptr; +  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) +    return false; +  if (Msg->getNumArgs() != 1) +    return false; + +  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); +  Selector Sel = Msg->getSelector(); + +  if ((isa<ObjCStringLiteral>(Arg) && +       NS.getNSClassId(NSAPI::ClassId_NSString) == II && +       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || +        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   || + +      (isa<ObjCArrayLiteral>(Arg) && +       NS.getNSClassId(NSAPI::ClassId_NSArray) == II && +       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || +        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     || + +      (isa<ObjCDictionaryLiteral>(Arg) && +       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && +       (NS.getNSDictionarySelector( +                              NSAPI::NSDict_dictionaryWithDictionary) == Sel || +        NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) { + +    commit.replaceWithInner(Msg->getSourceRange(), +                           Msg->getArg(0)->getSourceRange()); +    return true; +  } + +  return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToObjCSubscriptSyntax. +//===----------------------------------------------------------------------===// + +/// Check for classes that accept 'objectForKey:' (or the other selectors +/// that the migrator handles) but return their instances as 'id', resulting +/// in the compiler resolving 'objectForKey:' as the method from NSDictionary. +/// +/// When checking if we can convert to subscripting syntax, check whether +/// the receiver is a result of a class method from a hardcoded list of +/// such classes. In such a case return the specific class as the interface +/// of the receiver. +/// +/// FIXME: Remove this when these classes start using 'instancetype'. +static const ObjCInterfaceDecl * +maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, +                                         const Expr *Receiver, +                                         ASTContext &Ctx) { +  assert(IFace && Receiver); + +  // If the receiver has type 'id'... +  if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) +    return IFace; + +  const ObjCMessageExpr * +    InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); +  if (!InnerMsg) +    return IFace; + +  QualType ClassRec; +  switch (InnerMsg->getReceiverKind()) { +  case ObjCMessageExpr::Instance: +  case ObjCMessageExpr::SuperInstance: +    return IFace; + +  case ObjCMessageExpr::Class: +    ClassRec = InnerMsg->getClassReceiver(); +    break; +  case ObjCMessageExpr::SuperClass: +    ClassRec = InnerMsg->getSuperType(); +    break; +  } + +  if (ClassRec.isNull()) +    return IFace; + +  // ...and it is the result of a class message... + +  const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); +  if (!ObjTy) +    return IFace; +  const ObjCInterfaceDecl *OID = ObjTy->getInterface(); + +  // ...and the receiving class is NSMapTable or NSLocale, return that +  // class as the receiving interface. +  if (OID->getName() == "NSMapTable" || +      OID->getName() == "NSLocale") +    return OID; + +  return IFace; +} + +static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, +                                        const ObjCMessageExpr *Msg, +                                        ASTContext &Ctx, +                                        Selector subscriptSel) { +  const Expr *Rec = Msg->getInstanceReceiver(); +  if (!Rec) +    return false; +  IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); + +  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { +    if (!MD->isUnavailable()) +      return true; +  } +  return false; +} + +static bool subscriptOperatorNeedsParens(const Expr *FullExpr); + +static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { +  if (subscriptOperatorNeedsParens(Receiver)) { +    SourceRange RecRange = Receiver->getSourceRange(); +    commit.insertWrap("(", RecRange, ")"); +  } +} + +static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, +                                        Commit &commit) { +  if (Msg->getNumArgs() != 1) +    return false; +  const Expr *Rec = Msg->getInstanceReceiver(); +  if (!Rec) +    return false; + +  SourceRange MsgRange = Msg->getSourceRange(); +  SourceRange RecRange = Rec->getSourceRange(); +  SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); + +  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), +                                                       ArgRange.getBegin()), +                         CharSourceRange::getTokenRange(RecRange)); +  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), +                         ArgRange); +  commit.insertWrap("[", ArgRange, "]"); +  maybePutParensOnReceiver(Rec, commit); +  return true; +} + +static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, +                                       const ObjCMessageExpr *Msg, +                                       const NSAPI &NS, +                                       Commit &commit) { +  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), +                                   NS.getObjectAtIndexedSubscriptSelector())) +    return false; +  return rewriteToSubscriptGetCommon(Msg, commit); +} + +static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, +                                            const ObjCMessageExpr *Msg, +                                            const NSAPI &NS, +                                            Commit &commit) { +  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), +                                  NS.getObjectForKeyedSubscriptSelector())) +    return false; +  return rewriteToSubscriptGetCommon(Msg, commit); +} + +static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, +                                       const ObjCMessageExpr *Msg, +                                       const NSAPI &NS, +                                       Commit &commit) { +  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), +                                   NS.getSetObjectAtIndexedSubscriptSelector())) +    return false; + +  if (Msg->getNumArgs() != 2) +    return false; +  const Expr *Rec = Msg->getInstanceReceiver(); +  if (!Rec) +    return false; + +  SourceRange MsgRange = Msg->getSourceRange(); +  SourceRange RecRange = Rec->getSourceRange(); +  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); +  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); + +  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), +                                                       Arg0Range.getBegin()), +                         CharSourceRange::getTokenRange(RecRange)); +  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), +                                                       Arg1Range.getBegin()), +                         CharSourceRange::getTokenRange(Arg0Range)); +  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), +                         Arg1Range); +  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), +                                                       Arg1Range.getBegin()), +                    "] = "); +  maybePutParensOnReceiver(Rec, commit); +  return true; +} + +static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, +                                            const ObjCMessageExpr *Msg, +                                            const NSAPI &NS, +                                            Commit &commit) { +  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), +                                   NS.getSetObjectForKeyedSubscriptSelector())) +    return false; + +  if (Msg->getNumArgs() != 2) +    return false; +  const Expr *Rec = Msg->getInstanceReceiver(); +  if (!Rec) +    return false; + +  SourceRange MsgRange = Msg->getSourceRange(); +  SourceRange RecRange = Rec->getSourceRange(); +  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); +  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); + +  SourceLocation LocBeforeVal = Arg0Range.getBegin(); +  commit.insertBefore(LocBeforeVal, "] = "); +  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, +                         /*beforePreviousInsertions=*/true); +  commit.insertBefore(LocBeforeVal, "["); +  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), +                                                       Arg0Range.getBegin()), +                         CharSourceRange::getTokenRange(RecRange)); +  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), +                         Arg0Range); +  maybePutParensOnReceiver(Rec, commit); +  return true; +} + +bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, +                                        const NSAPI &NS, Commit &commit) { +  if (!Msg || Msg->isImplicit() || +      Msg->getReceiverKind() != ObjCMessageExpr::Instance) +    return false; +  const ObjCMethodDecl *Method = Msg->getMethodDecl(); +  if (!Method) +    return false; + +  const ObjCInterfaceDecl *IFace = +      NS.getASTContext().getObjContainingInterface(Method); +  if (!IFace) +    return false; +  Selector Sel = Msg->getSelector(); + +  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) +    return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); + +  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) +    return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); + +  if (Msg->getNumArgs() != 2) +    return false; + +  if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) +    return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); + +  if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) +    return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); + +  return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToObjCLiteralSyntax. +//===----------------------------------------------------------------------===// + +static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, +                                  const NSAPI &NS, Commit &commit, +                                  const ParentMap *PMap); +static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, +                                  const NSAPI &NS, Commit &commit); +static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, +                                  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, +                                      const ParentMap *PMap) { +  IdentifierInfo *II = nullptr; +  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) +    return false; + +  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) +    return rewriteToArrayLiteral(Msg, NS, commit, PMap); +  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) +    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; +} + +/// Returns true if the immediate message arguments of \c Msg should not +/// be rewritten because it will interfere with the rewrite of the parent +/// message expression. e.g. +/// \code +///   [NSDictionary dictionaryWithObjects: +///                                 [NSArray arrayWithObjects:@"1", @"2", nil] +///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; +/// \endcode +/// It will return true for this because we are going to rewrite this directly +/// to a dictionary literal without any array literals. +static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, +                                                 const NSAPI &NS); + +//===----------------------------------------------------------------------===// +// rewriteToArrayLiteral. +//===----------------------------------------------------------------------===// + +/// 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, +                                  const ParentMap *PMap) { +  if (PMap) { +    const ObjCMessageExpr *ParentMsg = +        dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg)); +    if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS)) +      return false; +  } + +  Selector Sel = Msg->getSelector(); +  SourceRange MsgRange = Msg->getSourceRange(); + +  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { +    if (Msg->getNumArgs() != 0) +      return false; +    commit.replace(MsgRange, "@[]"); +    return true; +  } + +  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, "]"); +    return true; +  } + +  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || +      Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { +    if (Msg->getNumArgs() == 0) +      return false; +    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); +    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; +    } +    SourceRange ArgRange(Msg->getArg(0)->getLocStart(), +                         Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); +    commit.replaceWithInner(MsgRange, ArgRange); +    commit.insertWrap("@[", ArgRange, "]"); +    return true; +  } + +  return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToDictionaryLiteral. +//===----------------------------------------------------------------------===// + +/// If \c Msg is an NSArray creation message or literal, this gets the +/// objects that were used to create it. +/// \returns true if it is an NSArray and we got objects, or false otherwise. +static bool getNSArrayObjects(const Expr *E, const NSAPI &NS, +                              SmallVectorImpl<const Expr *> &Objs) { +  if (!E) +    return false; + +  E = E->IgnoreParenCasts(); +  if (!E) +    return false; + +  if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { +    IdentifierInfo *Cls = nullptr; +    if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts())) +      return false; + +    if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray)) +      return false; + +    Selector Sel = Msg->getSelector(); +    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) +      return true; // empty array. + +    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { +      if (Msg->getNumArgs() != 1) +        return false; +      Objs.push_back(Msg->getArg(0)); +      return true; +    } + +    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || +        Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { +      if (Msg->getNumArgs() == 0) +        return false; +      const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); +      if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) +        return false; + +      for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) +        Objs.push_back(Msg->getArg(i)); +      return true; +    } + +  } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) { +    for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i) +      Objs.push_back(ArrLit->getElement(i)); +    return true; +  } + +  return false; +} + +static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, +                                       const NSAPI &NS, Commit &commit) { +  Selector Sel = Msg->getSelector(); +  SourceRange MsgRange = Msg->getSourceRange(); + +  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { +    if (Msg->getNumArgs() != 0) +      return false; +    commit.replace(MsgRange, "@{}"); +    return true; +  } + +  if (Sel == NS.getNSDictionarySelector( +                                    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. +    commit.insertBefore(ValRange.getBegin(), ": "); +    commit.insertFromRange(ValRange.getBegin(), +                           CharSourceRange::getTokenRange(KeyRange), +                       /*afterToken=*/false, /*beforePreviousInsertions=*/true); +    commit.insertBefore(ValRange.getBegin(), "@{"); +    commit.insertAfterToken(ValRange.getEnd(), "}"); +    commit.replaceWithInner(MsgRange, ValRange); +    return true; +  } + +  if (Sel == NS.getNSDictionarySelector( +                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) || +      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { +    if (Msg->getNumArgs() % 2 != 1) +      return false; +    unsigned SentinelIdx = Msg->getNumArgs() - 1; +    const Expr *SentinelExpr = Msg->getArg(SentinelIdx); +    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) +      return false; + +    if (Msg->getNumArgs() == 1) { +      commit.replace(MsgRange, "@{}"); +      return true; +    } + +    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. +      commit.insertAfterToken(KeyRange.getEnd(), ": "); +      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); +      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), +                                                  KeyRange.getBegin())); +    } +    // Range of arguments up until and including the last key. +    // The sentinel and first value are cut off, the value will move after the +    // key. +    SourceRange ArgRange(Msg->getArg(1)->getLocStart(), +                         Msg->getArg(SentinelIdx-1)->getLocEnd()); +    commit.insertWrap("@{", ArgRange, "}"); +    commit.replaceWithInner(MsgRange, ArgRange); +    return true; +  } + +  if (Sel == NS.getNSDictionarySelector( +                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) || +      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { +    if (Msg->getNumArgs() != 2) +      return false; + +    SmallVector<const Expr *, 8> Vals; +    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) +      return false; + +    SmallVector<const Expr *, 8> Keys; +    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) +      return false; + +    if (Vals.size() != Keys.size()) +      return false; + +    if (Vals.empty()) { +      commit.replace(MsgRange, "@{}"); +      return true; +    } + +    for (unsigned i = 0, n = Vals.size(); i < n; ++i) { +      objectifyExpr(Vals[i], commit); +      objectifyExpr(Keys[i], commit); + +      SourceRange ValRange = Vals[i]->getSourceRange(); +      SourceRange KeyRange = Keys[i]->getSourceRange(); +      // Insert value after key. +      commit.insertAfterToken(KeyRange.getEnd(), ": "); +      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); +    } +    // Range of arguments up until and including the last key. +    // The first value is cut off, the value will move after the key. +    SourceRange ArgRange(Keys.front()->getLocStart(), +                         Keys.back()->getLocEnd()); +    commit.insertWrap("@{", ArgRange, "}"); +    commit.replaceWithInner(MsgRange, ArgRange); +    return true; +  } + +  return false; +} + +static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, +                                                 const NSAPI &NS) { +  if (!Msg) +    return false; + +  IdentifierInfo *II = nullptr; +  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) +    return false; + +  if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary)) +    return false; + +  Selector Sel = Msg->getSelector(); +  if (Sel == NS.getNSDictionarySelector( +                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) || +      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { +    if (Msg->getNumArgs() != 2) +      return false; + +    SmallVector<const Expr *, 8> Vals; +    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) +      return false; + +    SmallVector<const Expr *, 8> Keys; +    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) +      return false; + +    if (Vals.size() != Keys.size()) +      return false; + +    return true; +  } + +  return false; +} + +//===----------------------------------------------------------------------===// +// rewriteToNumberLiteral. +//===----------------------------------------------------------------------===// + +static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, +                                   const CharacterLiteral *Arg, +                                   const NSAPI &NS, Commit &commit) { +  if (Arg->getKind() != CharacterLiteral::Ascii) +    return false; +  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, +                                   Msg->getSelector())) { +    SourceRange ArgRange = Arg->getSourceRange(); +    commit.replaceWithInner(Msg->getSourceRange(), ArgRange); +    commit.insert(ArgRange.getBegin(), "@"); +    return true; +  } + +  return rewriteToNumericBoxedExpression(Msg, NS, commit); +} + +static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, +                                   const Expr *Arg, +                                   const NSAPI &NS, Commit &commit) { +  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, +                                   Msg->getSelector())) { +    SourceRange ArgRange = Arg->getSourceRange(); +    commit.replaceWithInner(Msg->getSourceRange(), ArgRange); +    commit.insert(ArgRange.getBegin(), "@"); +    return true; +  } + +  return rewriteToNumericBoxedExpression(Msg, NS, commit); +} + +namespace { + +struct LiteralInfo { +  bool Hex, Octal; +  StringRef U, F, L, LL; +  CharSourceRange WithoutSuffRange; +}; + +} + +static bool getLiteralInfo(SourceRange literalRange, +                           bool isFloat, bool isIntZero, +                          ASTContext &Ctx, LiteralInfo &Info) { +  if (literalRange.getBegin().isMacroID() || +      literalRange.getEnd().isMacroID()) +    return false; +  StringRef text = Lexer::getSourceText( +                                  CharSourceRange::getTokenRange(literalRange), +                                  Ctx.getSourceManager(), Ctx.getLangOpts()); +  if (text.empty()) +    return false; + +  Optional<bool> UpperU, UpperL; +  bool UpperF = false; + +  struct Suff { +    static bool has(StringRef suff, StringRef &text) { +      if (text.endswith(suff)) { +        text = text.substr(0, text.size()-suff.size()); +        return true; +      } +      return false; +    } +  }; + +  while (1) { +    if (Suff::has("u", text)) { +      UpperU = false; +    } else if (Suff::has("U", text)) { +      UpperU = true; +    } else if (Suff::has("ll", text)) { +      UpperL = false; +    } else if (Suff::has("LL", text)) { +      UpperL = true; +    } else if (Suff::has("l", text)) { +      UpperL = false; +    } else if (Suff::has("L", text)) { +      UpperL = true; +    } else if (isFloat && Suff::has("f", text)) { +      UpperF = false; +    } else if (isFloat && Suff::has("F", text)) { +      UpperF = true; +    } else +      break; +  } + +  if (!UpperU.hasValue() && !UpperL.hasValue()) +    UpperU = UpperL = true; +  else if (UpperU.hasValue() && !UpperL.hasValue()) +    UpperL = UpperU; +  else if (UpperL.hasValue() && !UpperU.hasValue()) +    UpperU = UpperL; + +  Info.U = *UpperU ? "U" : "u"; +  Info.L = *UpperL ? "L" : "l"; +  Info.LL = *UpperL ? "LL" : "ll"; +  Info.F = UpperF ? "F" : "f"; + +  Info.Hex = Info.Octal = false; +  if (text.startswith("0x")) +    Info.Hex = true; +  else if (!isFloat && !isIntZero && text.startswith("0")) +    Info.Octal = true; + +  SourceLocation B = literalRange.getBegin(); +  Info.WithoutSuffRange = +      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); +  return true; +} + +static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, +                                   const NSAPI &NS, Commit &commit) { +  if (Msg->getNumArgs() != 1) +    return false; + +  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); +  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) +    return rewriteToCharLiteral(Msg, CharE, NS, commit); +  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) +    return rewriteToBoolLiteral(Msg, BE, NS, commit); +  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) +    return rewriteToBoolLiteral(Msg, BE, NS, commit); + +  const Expr *literalE = Arg; +  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { +    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) +      literalE = UOE->getSubExpr(); +  } + +  // Only integer and floating literals, otherwise try to rewrite to boxed +  // expression. +  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) +    return rewriteToNumericBoxedExpression(Msg, NS, commit); + +  ASTContext &Ctx = NS.getASTContext(); +  Selector Sel = Msg->getSelector(); +  Optional<NSAPI::NSNumberLiteralMethodKind> +    MKOpt = NS.getNSNumberLiteralMethodKind(Sel); +  if (!MKOpt) +    return false; +  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; + +  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; +  bool CallIsFloating = false, CallIsDouble = false; + +  switch (MK) { +  // We cannot have these calls with int/float literals. +  case NSAPI::NSNumberWithChar: +  case NSAPI::NSNumberWithUnsignedChar: +  case NSAPI::NSNumberWithShort: +  case NSAPI::NSNumberWithUnsignedShort: +  case NSAPI::NSNumberWithBool: +    return rewriteToNumericBoxedExpression(Msg, NS, commit); + +  case NSAPI::NSNumberWithUnsignedInt: +  case NSAPI::NSNumberWithUnsignedInteger: +    CallIsUnsigned = true; +    LLVM_FALLTHROUGH; +  case NSAPI::NSNumberWithInt: +  case NSAPI::NSNumberWithInteger: +    break; + +  case NSAPI::NSNumberWithUnsignedLong: +    CallIsUnsigned = true; +    LLVM_FALLTHROUGH; +  case NSAPI::NSNumberWithLong: +    CallIsLong = true; +    break; + +  case NSAPI::NSNumberWithUnsignedLongLong: +    CallIsUnsigned = true; +    LLVM_FALLTHROUGH; +  case NSAPI::NSNumberWithLongLong: +    CallIsLongLong = true; +    break; + +  case NSAPI::NSNumberWithDouble: +    CallIsDouble = true; +    LLVM_FALLTHROUGH; +  case NSAPI::NSNumberWithFloat: +    CallIsFloating = true; +    break; +  } + +  SourceRange ArgRange = Arg->getSourceRange(); +  QualType ArgTy = Arg->getType(); +  QualType CallTy = Msg->getArg(0)->getType(); + +  // Check for the easy case, the literal maps directly to the call. +  if (Ctx.hasSameType(ArgTy, CallTy)) { +    commit.replaceWithInner(Msg->getSourceRange(), ArgRange); +    commit.insert(ArgRange.getBegin(), "@"); +    return true; +  } + +  // We will need to modify the literal suffix to get the same type as the call. +  // Try with boxed expression if it came from a macro. +  if (ArgRange.getBegin().isMacroID()) +    return rewriteToNumericBoxedExpression(Msg, NS, commit); + +  bool LitIsFloat = ArgTy->isFloatingType(); +  // For a float passed to integer call, don't try rewriting to objc literal. +  // It is difficult and a very uncommon case anyway. +  // But try with boxed expression. +  if (LitIsFloat && !CallIsFloating) +    return rewriteToNumericBoxedExpression(Msg, NS, commit); + +  // Try to modify the literal make it the same type as the method call. +  // -Modify the suffix, and/or +  // -Change integer to float + +  LiteralInfo LitInfo; +  bool isIntZero = false; +  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) +    isIntZero = !IntE->getValue().getBoolValue(); +  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) +    return rewriteToNumericBoxedExpression(Msg, NS, commit); + +  // Not easy to do int -> float with hex/octal and uncommon anyway. +  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) +    return rewriteToNumericBoxedExpression(Msg, NS, commit); + +  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); +  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); + +  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), +                         LitInfo.WithoutSuffRange); +  commit.insert(LitB, "@"); + +  if (!LitIsFloat && CallIsFloating) +    commit.insert(LitE, ".0"); + +  if (CallIsFloating) { +    if (!CallIsDouble) +      commit.insert(LitE, LitInfo.F); +  } else { +    if (CallIsUnsigned) +      commit.insert(LitE, LitInfo.U); + +    if (CallIsLong) +      commit.insert(LitE, LitInfo.L); +    else if (CallIsLongLong) +      commit.insert(LitE, LitInfo.LL); +  } +  return true; +} + +// FIXME: Make determination of operator precedence more general and +// make it broadly available. +static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { +  const Expr* Expr = FullExpr->IgnoreImpCasts(); +  if (isa<ArraySubscriptExpr>(Expr) || +      isa<CallExpr>(Expr) || +      isa<DeclRefExpr>(Expr) || +      isa<CXXNamedCastExpr>(Expr) || +      isa<CXXConstructExpr>(Expr) || +      isa<CXXThisExpr>(Expr) || +      isa<CXXTypeidExpr>(Expr) || +      isa<CXXUnresolvedConstructExpr>(Expr) || +      isa<ObjCMessageExpr>(Expr) || +      isa<ObjCPropertyRefExpr>(Expr) || +      isa<ObjCProtocolExpr>(Expr) || +      isa<MemberExpr>(Expr) || +      isa<ObjCIvarRefExpr>(Expr) || +      isa<ParenExpr>(FullExpr) || +      isa<ParenListExpr>(Expr) || +      isa<SizeOfPackExpr>(Expr)) +    return false; + +  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<ObjCIvarRefExpr>(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)"); +} + +//===----------------------------------------------------------------------===// +// rewriteToNumericBoxedExpression. +//===----------------------------------------------------------------------===// + +static bool isEnumConstant(const Expr *E) { +  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) +    if (const ValueDecl *VD = DRE->getDecl()) +      return isa<EnumConstantDecl>(VD); + +  return false; +} + +static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, +                                            const NSAPI &NS, Commit &commit) { +  if (Msg->getNumArgs() != 1) +    return false; + +  const Expr *Arg = Msg->getArg(0); +  if (Arg->isTypeDependent()) +    return false; + +  ASTContext &Ctx = NS.getASTContext(); +  Selector Sel = Msg->getSelector(); +  Optional<NSAPI::NSNumberLiteralMethodKind> +    MKOpt = NS.getNSNumberLiteralMethodKind(Sel); +  if (!MKOpt) +    return false; +  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; + +  const Expr *OrigArg = Arg->IgnoreImpCasts(); +  QualType FinalTy = Arg->getType(); +  QualType OrigTy = OrigArg->getType(); +  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); +  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); + +  bool isTruncated = FinalTySize < OrigTySize; +  bool needsCast = false; + +  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { +    switch (ICE->getCastKind()) { +    case CK_LValueToRValue: +    case CK_NoOp: +    case CK_UserDefinedConversion: +      break; + +    case CK_IntegralCast: { +      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) +        break; +      // Be more liberal with Integer/UnsignedInteger which are very commonly +      // used. +      if ((MK == NSAPI::NSNumberWithInteger || +           MK == NSAPI::NSNumberWithUnsignedInteger) && +          !isTruncated) { +        if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) +          break; +        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && +            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) +          break; +      } + +      needsCast = true; +      break; +    } + +    case CK_PointerToBoolean: +    case CK_IntegralToBoolean: +    case CK_IntegralToFloating: +    case CK_FloatingToIntegral: +    case CK_FloatingToBoolean: +    case CK_FloatingCast: +    case CK_FloatingComplexToReal: +    case CK_FloatingComplexToBoolean: +    case CK_IntegralComplexToReal: +    case CK_IntegralComplexToBoolean: +    case CK_AtomicToNonAtomic: +    case CK_AddressSpaceConversion: +      needsCast = true; +      break; + +    case CK_Dependent: +    case CK_BitCast: +    case CK_LValueBitCast: +    case CK_BaseToDerived: +    case CK_DerivedToBase: +    case CK_UncheckedDerivedToBase: +    case CK_Dynamic: +    case CK_ToUnion: +    case CK_ArrayToPointerDecay: +    case CK_FunctionToPointerDecay: +    case CK_NullToPointer: +    case CK_NullToMemberPointer: +    case CK_BaseToDerivedMemberPointer: +    case CK_DerivedToBaseMemberPointer: +    case CK_MemberPointerToBoolean: +    case CK_ReinterpretMemberPointer: +    case CK_ConstructorConversion: +    case CK_IntegralToPointer: +    case CK_PointerToIntegral: +    case CK_ToVoid: +    case CK_VectorSplat: +    case CK_CPointerToObjCPointerCast: +    case CK_BlockPointerToObjCPointerCast: +    case CK_AnyPointerToBlockPointerCast: +    case CK_ObjCObjectLValueCast: +    case CK_FloatingRealToComplex: +    case CK_FloatingComplexCast: +    case CK_FloatingComplexToIntegralComplex: +    case CK_IntegralRealToComplex: +    case CK_IntegralComplexCast: +    case CK_IntegralComplexToFloatingComplex: +    case CK_ARCProduceObject: +    case CK_ARCConsumeObject: +    case CK_ARCReclaimReturnedObject: +    case CK_ARCExtendBlockObject: +    case CK_NonAtomicToAtomic: +    case CK_CopyAndAutoreleaseBlockObject: +    case CK_BuiltinFnToFnPtr: +    case CK_ZeroToOCLEvent: +    case CK_ZeroToOCLQueue: +    case CK_IntToOCLSampler: +      return false; + +    case CK_BooleanToSignedIntegral: +      llvm_unreachable("OpenCL-specific cast in Objective-C?"); +    } +  } + +  if (needsCast) { +    DiagnosticsEngine &Diags = Ctx.getDiagnostics(); +    // FIXME: Use a custom category name to distinguish migration diagnostics. +    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, +                       "converting to boxing syntax requires casting %0 to %1"); +    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy +        << Msg->getSourceRange(); +    return false; +  } + +  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; +} + +//===----------------------------------------------------------------------===// +// rewriteToStringBoxedExpression. +//===----------------------------------------------------------------------===// + +static bool doRewriteToUTF8StringBoxedExpressionHelper( +                                              const ObjCMessageExpr *Msg, +                                              const NSAPI &NS, Commit &commit) { +  const Expr *Arg = Msg->getArg(0); +  if (Arg->isTypeDependent()) +    return false; + +  ASTContext &Ctx = NS.getASTContext(); + +  const Expr *OrigArg = Arg->IgnoreImpCasts(); +  QualType OrigTy = OrigArg->getType(); +  if (OrigTy->isArrayType()) +    OrigTy = Ctx.getArrayDecayedType(OrigTy); + +  if (const StringLiteral * +        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { +    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); +    commit.insert(StrE->getLocStart(), "@"); +    return true; +  } + +  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) || +      Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) { +    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; +}  | 
