brintos

brintos / llvm-project-archived public Read only

0
0
Text · 40.9 KiB · 40f8348 Raw
1191 lines · cpp
1//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Rewrites legacy method calls to modern syntax.10//11//===----------------------------------------------------------------------===//12 13#include "clang/Edit/Rewriters.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/ExprCXX.h"16#include "clang/AST/ExprObjC.h"17#include "clang/AST/NSAPI.h"18#include "clang/AST/ParentMap.h"19#include "clang/Edit/Commit.h"20#include "clang/Lex/Lexer.h"21#include <optional>22 23using namespace clang;24using namespace edit;25 26static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,27                                    IdentifierInfo *&ClassId,28                                    const LangOptions &LangOpts) {29  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())30    return false;31 32  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();33  if (!Receiver)34    return false;35  ClassId = Receiver->getIdentifier();36 37  if (Msg->getReceiverKind() == ObjCMessageExpr::Class)38    return true;39 40  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,41  // since the change from +1 to +0 will be handled fine by ARC.42  if (LangOpts.ObjCAutoRefCount) {43    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {44      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(45                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {46        if (Rec->getMethodFamily() == OMF_alloc)47          return true;48      }49    }50  }51 52  return false;53}54 55//===----------------------------------------------------------------------===//56// rewriteObjCRedundantCallWithLiteral.57//===----------------------------------------------------------------------===//58 59bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,60                                              const NSAPI &NS, Commit &commit) {61  IdentifierInfo *II = nullptr;62  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))63    return false;64  if (Msg->getNumArgs() != 1)65    return false;66 67  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();68  Selector Sel = Msg->getSelector();69 70  if ((isa<ObjCStringLiteral>(Arg) &&71       NS.getNSClassId(NSAPI::ClassId_NSString) == II &&72       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||73        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||74 75      (isa<ObjCArrayLiteral>(Arg) &&76       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&77       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||78        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||79 80      (isa<ObjCDictionaryLiteral>(Arg) &&81       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&82       (NS.getNSDictionarySelector(83                              NSAPI::NSDict_dictionaryWithDictionary) == Sel ||84        NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {85 86    commit.replaceWithInner(Msg->getSourceRange(),87                           Msg->getArg(0)->getSourceRange());88    return true;89  }90 91  return false;92}93 94//===----------------------------------------------------------------------===//95// rewriteToObjCSubscriptSyntax.96//===----------------------------------------------------------------------===//97 98/// Check for classes that accept 'objectForKey:' (or the other selectors99/// that the migrator handles) but return their instances as 'id', resulting100/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.101///102/// When checking if we can convert to subscripting syntax, check whether103/// the receiver is a result of a class method from a hardcoded list of104/// such classes. In such a case return the specific class as the interface105/// of the receiver.106///107/// FIXME: Remove this when these classes start using 'instancetype'.108static const ObjCInterfaceDecl *109maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,110                                         const Expr *Receiver,111                                         ASTContext &Ctx) {112  assert(IFace && Receiver);113 114  // If the receiver has type 'id'...115  if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))116    return IFace;117 118  const ObjCMessageExpr *119    InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());120  if (!InnerMsg)121    return IFace;122 123  QualType ClassRec;124  switch (InnerMsg->getReceiverKind()) {125  case ObjCMessageExpr::Instance:126  case ObjCMessageExpr::SuperInstance:127    return IFace;128 129  case ObjCMessageExpr::Class:130    ClassRec = InnerMsg->getClassReceiver();131    break;132  case ObjCMessageExpr::SuperClass:133    ClassRec = InnerMsg->getSuperType();134    break;135  }136 137  if (ClassRec.isNull())138    return IFace;139 140  // ...and it is the result of a class message...141 142  const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();143  if (!ObjTy)144    return IFace;145  const ObjCInterfaceDecl *OID = ObjTy->getInterface();146 147  // ...and the receiving class is NSMapTable or NSLocale, return that148  // class as the receiving interface.149  if (OID->getName() == "NSMapTable" ||150      OID->getName() == "NSLocale")151    return OID;152 153  return IFace;154}155 156static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,157                                        const ObjCMessageExpr *Msg,158                                        ASTContext &Ctx,159                                        Selector subscriptSel) {160  const Expr *Rec = Msg->getInstanceReceiver();161  if (!Rec)162    return false;163  IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);164 165  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {166    if (!MD->isUnavailable())167      return true;168  }169  return false;170}171 172static bool subscriptOperatorNeedsParens(const Expr *FullExpr);173 174static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {175  if (subscriptOperatorNeedsParens(Receiver)) {176    SourceRange RecRange = Receiver->getSourceRange();177    commit.insertWrap("(", RecRange, ")");178  }179}180 181static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,182                                        Commit &commit) {183  if (Msg->getNumArgs() != 1)184    return false;185  const Expr *Rec = Msg->getInstanceReceiver();186  if (!Rec)187    return false;188 189  SourceRange MsgRange = Msg->getSourceRange();190  SourceRange RecRange = Rec->getSourceRange();191  SourceRange ArgRange = Msg->getArg(0)->getSourceRange();192 193  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),194                                                       ArgRange.getBegin()),195                         CharSourceRange::getTokenRange(RecRange));196  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),197                         ArgRange);198  commit.insertWrap("[", ArgRange, "]");199  maybePutParensOnReceiver(Rec, commit);200  return true;201}202 203static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,204                                       const ObjCMessageExpr *Msg,205                                       const NSAPI &NS,206                                       Commit &commit) {207  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),208                                   NS.getObjectAtIndexedSubscriptSelector()))209    return false;210  return rewriteToSubscriptGetCommon(Msg, commit);211}212 213static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,214                                            const ObjCMessageExpr *Msg,215                                            const NSAPI &NS,216                                            Commit &commit) {217  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),218                                  NS.getObjectForKeyedSubscriptSelector()))219    return false;220  return rewriteToSubscriptGetCommon(Msg, commit);221}222 223static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,224                                       const ObjCMessageExpr *Msg,225                                       const NSAPI &NS,226                                       Commit &commit) {227  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),228                                   NS.getSetObjectAtIndexedSubscriptSelector()))229    return false;230 231  if (Msg->getNumArgs() != 2)232    return false;233  const Expr *Rec = Msg->getInstanceReceiver();234  if (!Rec)235    return false;236 237  SourceRange MsgRange = Msg->getSourceRange();238  SourceRange RecRange = Rec->getSourceRange();239  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();240  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();241 242  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),243                                                       Arg0Range.getBegin()),244                         CharSourceRange::getTokenRange(RecRange));245  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),246                                                       Arg1Range.getBegin()),247                         CharSourceRange::getTokenRange(Arg0Range));248  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),249                         Arg1Range);250  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),251                                                       Arg1Range.getBegin()),252                    "] = ");253  maybePutParensOnReceiver(Rec, commit);254  return true;255}256 257static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,258                                            const ObjCMessageExpr *Msg,259                                            const NSAPI &NS,260                                            Commit &commit) {261  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),262                                   NS.getSetObjectForKeyedSubscriptSelector()))263    return false;264 265  if (Msg->getNumArgs() != 2)266    return false;267  const Expr *Rec = Msg->getInstanceReceiver();268  if (!Rec)269    return false;270 271  SourceRange MsgRange = Msg->getSourceRange();272  SourceRange RecRange = Rec->getSourceRange();273  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();274  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();275 276  SourceLocation LocBeforeVal = Arg0Range.getBegin();277  commit.insertBefore(LocBeforeVal, "] = ");278  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,279                         /*beforePreviousInsertions=*/true);280  commit.insertBefore(LocBeforeVal, "[");281  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),282                                                       Arg0Range.getBegin()),283                         CharSourceRange::getTokenRange(RecRange));284  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),285                         Arg0Range);286  maybePutParensOnReceiver(Rec, commit);287  return true;288}289 290bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,291                                        const NSAPI &NS, Commit &commit) {292  if (!Msg || Msg->isImplicit() ||293      Msg->getReceiverKind() != ObjCMessageExpr::Instance)294    return false;295  const ObjCMethodDecl *Method = Msg->getMethodDecl();296  if (!Method)297    return false;298 299  const ObjCInterfaceDecl *IFace =300      NS.getASTContext().getObjContainingInterface(Method);301  if (!IFace)302    return false;303  Selector Sel = Msg->getSelector();304 305  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))306    return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);307 308  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))309    return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);310 311  if (Msg->getNumArgs() != 2)312    return false;313 314  if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))315    return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);316 317  if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))318    return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);319 320  return false;321}322 323//===----------------------------------------------------------------------===//324// rewriteToObjCLiteralSyntax.325//===----------------------------------------------------------------------===//326 327static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,328                                  const NSAPI &NS, Commit &commit,329                                  const ParentMap *PMap);330static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,331                                  const NSAPI &NS, Commit &commit);332static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,333                                  const NSAPI &NS, Commit &commit);334static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,335                                            const NSAPI &NS, Commit &commit);336static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,337                                           const NSAPI &NS, Commit &commit);338 339bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,340                                      const NSAPI &NS, Commit &commit,341                                      const ParentMap *PMap) {342  IdentifierInfo *II = nullptr;343  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))344    return false;345 346  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))347    return rewriteToArrayLiteral(Msg, NS, commit, PMap);348  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))349    return rewriteToDictionaryLiteral(Msg, NS, commit);350  if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))351    return rewriteToNumberLiteral(Msg, NS, commit);352  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))353    return rewriteToStringBoxedExpression(Msg, NS, commit);354 355  return false;356}357 358/// Returns true if the immediate message arguments of \c Msg should not359/// be rewritten because it will interfere with the rewrite of the parent360/// message expression. e.g.361/// \code362///   [NSDictionary dictionaryWithObjects:363///                                 [NSArray arrayWithObjects:@"1", @"2", nil]364///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];365/// \endcode366/// It will return true for this because we are going to rewrite this directly367/// to a dictionary literal without any array literals.368static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,369                                                 const NSAPI &NS);370 371//===----------------------------------------------------------------------===//372// rewriteToArrayLiteral.373//===----------------------------------------------------------------------===//374 375/// Adds an explicit cast to 'id' if the type is not objc object.376static void objectifyExpr(const Expr *E, Commit &commit);377 378static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,379                                  const NSAPI &NS, Commit &commit,380                                  const ParentMap *PMap) {381  if (PMap) {382    const ObjCMessageExpr *ParentMsg =383        dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));384    if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))385      return false;386  }387 388  Selector Sel = Msg->getSelector();389  SourceRange MsgRange = Msg->getSourceRange();390 391  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {392    if (Msg->getNumArgs() != 0)393      return false;394    commit.replace(MsgRange, "@[]");395    return true;396  }397 398  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {399    if (Msg->getNumArgs() != 1)400      return false;401    objectifyExpr(Msg->getArg(0), commit);402    SourceRange ArgRange = Msg->getArg(0)->getSourceRange();403    commit.replaceWithInner(MsgRange, ArgRange);404    commit.insertWrap("@[", ArgRange, "]");405    return true;406  }407 408  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||409      Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {410    if (Msg->getNumArgs() == 0)411      return false;412    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);413    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))414      return false;415 416    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)417      objectifyExpr(Msg->getArg(i), commit);418 419    if (Msg->getNumArgs() == 1) {420      commit.replace(MsgRange, "@[]");421      return true;422    }423    SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),424                         Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());425    commit.replaceWithInner(MsgRange, ArgRange);426    commit.insertWrap("@[", ArgRange, "]");427    return true;428  }429 430  return false;431}432 433//===----------------------------------------------------------------------===//434// rewriteToDictionaryLiteral.435//===----------------------------------------------------------------------===//436 437/// If \c Msg is an NSArray creation message or literal, this gets the438/// objects that were used to create it.439/// \returns true if it is an NSArray and we got objects, or false otherwise.440static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,441                              SmallVectorImpl<const Expr *> &Objs) {442  if (!E)443    return false;444 445  E = E->IgnoreParenCasts();446  if (!E)447    return false;448 449  if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {450    IdentifierInfo *Cls = nullptr;451    if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))452      return false;453 454    if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))455      return false;456 457    Selector Sel = Msg->getSelector();458    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))459      return true; // empty array.460 461    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {462      if (Msg->getNumArgs() != 1)463        return false;464      Objs.push_back(Msg->getArg(0));465      return true;466    }467 468    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||469        Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {470      if (Msg->getNumArgs() == 0)471        return false;472      const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);473      if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))474        return false;475 476      for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)477        Objs.push_back(Msg->getArg(i));478      return true;479    }480 481  } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {482    for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)483      Objs.push_back(ArrLit->getElement(i));484    return true;485  }486 487  return false;488}489 490static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,491                                       const NSAPI &NS, Commit &commit) {492  Selector Sel = Msg->getSelector();493  SourceRange MsgRange = Msg->getSourceRange();494 495  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {496    if (Msg->getNumArgs() != 0)497      return false;498    commit.replace(MsgRange, "@{}");499    return true;500  }501 502  if (Sel == NS.getNSDictionarySelector(503                                    NSAPI::NSDict_dictionaryWithObjectForKey)) {504    if (Msg->getNumArgs() != 2)505      return false;506 507    objectifyExpr(Msg->getArg(0), commit);508    objectifyExpr(Msg->getArg(1), commit);509 510    SourceRange ValRange = Msg->getArg(0)->getSourceRange();511    SourceRange KeyRange = Msg->getArg(1)->getSourceRange();512    // Insert key before the value.513    commit.insertBefore(ValRange.getBegin(), ": ");514    commit.insertFromRange(ValRange.getBegin(),515                           CharSourceRange::getTokenRange(KeyRange),516                       /*afterToken=*/false, /*beforePreviousInsertions=*/true);517    commit.insertBefore(ValRange.getBegin(), "@{");518    commit.insertAfterToken(ValRange.getEnd(), "}");519    commit.replaceWithInner(MsgRange, ValRange);520    return true;521  }522 523  if (Sel == NS.getNSDictionarySelector(524                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||525      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {526    if (Msg->getNumArgs() % 2 != 1)527      return false;528    unsigned SentinelIdx = Msg->getNumArgs() - 1;529    const Expr *SentinelExpr = Msg->getArg(SentinelIdx);530    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))531      return false;532 533    if (Msg->getNumArgs() == 1) {534      commit.replace(MsgRange, "@{}");535      return true;536    }537 538    for (unsigned i = 0; i < SentinelIdx; i += 2) {539      objectifyExpr(Msg->getArg(i), commit);540      objectifyExpr(Msg->getArg(i+1), commit);541 542      SourceRange ValRange = Msg->getArg(i)->getSourceRange();543      SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();544      // Insert value after key.545      commit.insertAfterToken(KeyRange.getEnd(), ": ");546      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);547      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),548                                                  KeyRange.getBegin()));549    }550    // Range of arguments up until and including the last key.551    // The sentinel and first value are cut off, the value will move after the552    // key.553    SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),554                         Msg->getArg(SentinelIdx - 1)->getEndLoc());555    commit.insertWrap("@{", ArgRange, "}");556    commit.replaceWithInner(MsgRange, ArgRange);557    return true;558  }559 560  if (Sel == NS.getNSDictionarySelector(561                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||562      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {563    if (Msg->getNumArgs() != 2)564      return false;565 566    SmallVector<const Expr *, 8> Vals;567    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))568      return false;569 570    SmallVector<const Expr *, 8> Keys;571    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))572      return false;573 574    if (Vals.size() != Keys.size())575      return false;576 577    if (Vals.empty()) {578      commit.replace(MsgRange, "@{}");579      return true;580    }581 582    for (unsigned i = 0, n = Vals.size(); i < n; ++i) {583      objectifyExpr(Vals[i], commit);584      objectifyExpr(Keys[i], commit);585 586      SourceRange ValRange = Vals[i]->getSourceRange();587      SourceRange KeyRange = Keys[i]->getSourceRange();588      // Insert value after key.589      commit.insertAfterToken(KeyRange.getEnd(), ": ");590      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);591    }592    // Range of arguments up until and including the last key.593    // The first value is cut off, the value will move after the key.594    SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());595    commit.insertWrap("@{", ArgRange, "}");596    commit.replaceWithInner(MsgRange, ArgRange);597    return true;598  }599 600  return false;601}602 603static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,604                                                 const NSAPI &NS) {605  if (!Msg)606    return false;607 608  IdentifierInfo *II = nullptr;609  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))610    return false;611 612  if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))613    return false;614 615  Selector Sel = Msg->getSelector();616  if (Sel == NS.getNSDictionarySelector(617                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||618      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {619    if (Msg->getNumArgs() != 2)620      return false;621 622    SmallVector<const Expr *, 8> Vals;623    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))624      return false;625 626    SmallVector<const Expr *, 8> Keys;627    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))628      return false;629 630    if (Vals.size() != Keys.size())631      return false;632 633    return true;634  }635 636  return false;637}638 639//===----------------------------------------------------------------------===//640// rewriteToNumberLiteral.641//===----------------------------------------------------------------------===//642 643static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,644                                   const CharacterLiteral *Arg,645                                   const NSAPI &NS, Commit &commit) {646  if (Arg->getKind() != CharacterLiteralKind::Ascii)647    return false;648  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,649                                   Msg->getSelector())) {650    SourceRange ArgRange = Arg->getSourceRange();651    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);652    commit.insert(ArgRange.getBegin(), "@");653    return true;654  }655 656  return rewriteToNumericBoxedExpression(Msg, NS, commit);657}658 659static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,660                                   const Expr *Arg,661                                   const NSAPI &NS, Commit &commit) {662  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,663                                   Msg->getSelector())) {664    SourceRange ArgRange = Arg->getSourceRange();665    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);666    commit.insert(ArgRange.getBegin(), "@");667    return true;668  }669 670  return rewriteToNumericBoxedExpression(Msg, NS, commit);671}672 673namespace {674 675struct LiteralInfo {676  bool Hex, Octal;677  StringRef U, F, L, LL;678  CharSourceRange WithoutSuffRange;679};680 681}682 683static bool getLiteralInfo(SourceRange literalRange,684                           bool isFloat, bool isIntZero,685                          ASTContext &Ctx, LiteralInfo &Info) {686  if (literalRange.getBegin().isMacroID() ||687      literalRange.getEnd().isMacroID())688    return false;689  StringRef text = Lexer::getSourceText(690                                  CharSourceRange::getTokenRange(literalRange),691                                  Ctx.getSourceManager(), Ctx.getLangOpts());692  if (text.empty())693    return false;694 695  std::optional<bool> UpperU, UpperL;696  bool UpperF = false;697 698  struct Suff {699    static bool has(StringRef suff, StringRef &text) {700      return text.consume_back(suff);701    }702  };703 704  while (true) {705    if (Suff::has("u", text)) {706      UpperU = false;707    } else if (Suff::has("U", text)) {708      UpperU = true;709    } else if (Suff::has("ll", text)) {710      UpperL = false;711    } else if (Suff::has("LL", text)) {712      UpperL = true;713    } else if (Suff::has("l", text)) {714      UpperL = false;715    } else if (Suff::has("L", text)) {716      UpperL = true;717    } else if (isFloat && Suff::has("f", text)) {718      UpperF = false;719    } else if (isFloat && Suff::has("F", text)) {720      UpperF = true;721    } else722      break;723  }724 725  if (!UpperU && !UpperL)726    UpperU = UpperL = true;727  else if (UpperU && !UpperL)728    UpperL = UpperU;729  else if (UpperL && !UpperU)730    UpperU = UpperL;731 732  Info.U = *UpperU ? "U" : "u";733  Info.L = *UpperL ? "L" : "l";734  Info.LL = *UpperL ? "LL" : "ll";735  Info.F = UpperF ? "F" : "f";736 737  Info.Hex = Info.Octal = false;738  if (text.starts_with("0x"))739    Info.Hex = true;740  else if (!isFloat && !isIntZero && text.starts_with("0"))741    Info.Octal = true;742 743  SourceLocation B = literalRange.getBegin();744  Info.WithoutSuffRange =745      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));746  return true;747}748 749static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,750                                   const NSAPI &NS, Commit &commit) {751  if (Msg->getNumArgs() != 1)752    return false;753 754  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();755  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))756    return rewriteToCharLiteral(Msg, CharE, NS, commit);757  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))758    return rewriteToBoolLiteral(Msg, BE, NS, commit);759  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))760    return rewriteToBoolLiteral(Msg, BE, NS, commit);761 762  const Expr *literalE = Arg;763  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {764    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)765      literalE = UOE->getSubExpr();766  }767 768  // Only integer and floating literals, otherwise try to rewrite to boxed769  // expression.770  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))771    return rewriteToNumericBoxedExpression(Msg, NS, commit);772 773  ASTContext &Ctx = NS.getASTContext();774  Selector Sel = Msg->getSelector();775  std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =776      NS.getNSNumberLiteralMethodKind(Sel);777  if (!MKOpt)778    return false;779  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;780 781  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;782  bool CallIsFloating = false, CallIsDouble = false;783 784  switch (MK) {785  // We cannot have these calls with int/float literals.786  case NSAPI::NSNumberWithChar:787  case NSAPI::NSNumberWithUnsignedChar:788  case NSAPI::NSNumberWithShort:789  case NSAPI::NSNumberWithUnsignedShort:790  case NSAPI::NSNumberWithBool:791    return rewriteToNumericBoxedExpression(Msg, NS, commit);792 793  case NSAPI::NSNumberWithUnsignedInt:794  case NSAPI::NSNumberWithUnsignedInteger:795    CallIsUnsigned = true;796    [[fallthrough]];797  case NSAPI::NSNumberWithInt:798  case NSAPI::NSNumberWithInteger:799    break;800 801  case NSAPI::NSNumberWithUnsignedLong:802    CallIsUnsigned = true;803    [[fallthrough]];804  case NSAPI::NSNumberWithLong:805    CallIsLong = true;806    break;807 808  case NSAPI::NSNumberWithUnsignedLongLong:809    CallIsUnsigned = true;810    [[fallthrough]];811  case NSAPI::NSNumberWithLongLong:812    CallIsLongLong = true;813    break;814 815  case NSAPI::NSNumberWithDouble:816    CallIsDouble = true;817    [[fallthrough]];818  case NSAPI::NSNumberWithFloat:819    CallIsFloating = true;820    break;821  }822 823  SourceRange ArgRange = Arg->getSourceRange();824  QualType ArgTy = Arg->getType();825  QualType CallTy = Msg->getArg(0)->getType();826 827  // Check for the easy case, the literal maps directly to the call.828  if (Ctx.hasSameType(ArgTy, CallTy)) {829    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);830    commit.insert(ArgRange.getBegin(), "@");831    return true;832  }833 834  // We will need to modify the literal suffix to get the same type as the call.835  // Try with boxed expression if it came from a macro.836  if (ArgRange.getBegin().isMacroID())837    return rewriteToNumericBoxedExpression(Msg, NS, commit);838 839  bool LitIsFloat = ArgTy->isFloatingType();840  // For a float passed to integer call, don't try rewriting to objc literal.841  // It is difficult and a very uncommon case anyway.842  // But try with boxed expression.843  if (LitIsFloat && !CallIsFloating)844    return rewriteToNumericBoxedExpression(Msg, NS, commit);845 846  // Try to modify the literal make it the same type as the method call.847  // -Modify the suffix, and/or848  // -Change integer to float849 850  LiteralInfo LitInfo;851  bool isIntZero = false;852  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))853    isIntZero = !IntE->getValue().getBoolValue();854  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))855    return rewriteToNumericBoxedExpression(Msg, NS, commit);856 857  // Not easy to do int -> float with hex/octal and uncommon anyway.858  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))859    return rewriteToNumericBoxedExpression(Msg, NS, commit);860 861  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();862  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();863 864  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),865                         LitInfo.WithoutSuffRange);866  commit.insert(LitB, "@");867 868  if (!LitIsFloat && CallIsFloating)869    commit.insert(LitE, ".0");870 871  if (CallIsFloating) {872    if (!CallIsDouble)873      commit.insert(LitE, LitInfo.F);874  } else {875    if (CallIsUnsigned)876      commit.insert(LitE, LitInfo.U);877 878    if (CallIsLong)879      commit.insert(LitE, LitInfo.L);880    else if (CallIsLongLong)881      commit.insert(LitE, LitInfo.LL);882  }883  return true;884}885 886// FIXME: Make determination of operator precedence more general and887// make it broadly available.888static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {889  const Expr* Expr = FullExpr->IgnoreImpCasts();890  if (isa<ArraySubscriptExpr>(Expr) ||891      isa<CallExpr>(Expr) ||892      isa<DeclRefExpr>(Expr) ||893      isa<CXXNamedCastExpr>(Expr) ||894      isa<CXXConstructExpr>(Expr) ||895      isa<CXXThisExpr>(Expr) ||896      isa<CXXTypeidExpr>(Expr) ||897      isa<CXXUnresolvedConstructExpr>(Expr) ||898      isa<ObjCMessageExpr>(Expr) ||899      isa<ObjCPropertyRefExpr>(Expr) ||900      isa<ObjCProtocolExpr>(Expr) ||901      isa<MemberExpr>(Expr) ||902      isa<ObjCIvarRefExpr>(Expr) ||903      isa<ParenExpr>(FullExpr) ||904      isa<ParenListExpr>(Expr) ||905      isa<SizeOfPackExpr>(Expr))906    return false;907 908  return true;909}910static bool castOperatorNeedsParens(const Expr *FullExpr) {911  const Expr* Expr = FullExpr->IgnoreImpCasts();912  if (isa<ArraySubscriptExpr>(Expr) ||913      isa<CallExpr>(Expr) ||914      isa<DeclRefExpr>(Expr) ||915      isa<CastExpr>(Expr) ||916      isa<CXXNewExpr>(Expr) ||917      isa<CXXConstructExpr>(Expr) ||918      isa<CXXDeleteExpr>(Expr) ||919      isa<CXXNoexceptExpr>(Expr) ||920      isa<CXXPseudoDestructorExpr>(Expr) ||921      isa<CXXScalarValueInitExpr>(Expr) ||922      isa<CXXThisExpr>(Expr) ||923      isa<CXXTypeidExpr>(Expr) ||924      isa<CXXUnresolvedConstructExpr>(Expr) ||925      isa<ObjCMessageExpr>(Expr) ||926      isa<ObjCPropertyRefExpr>(Expr) ||927      isa<ObjCProtocolExpr>(Expr) ||928      isa<MemberExpr>(Expr) ||929      isa<ObjCIvarRefExpr>(Expr) ||930      isa<ParenExpr>(FullExpr) ||931      isa<ParenListExpr>(Expr) ||932      isa<SizeOfPackExpr>(Expr) ||933      isa<UnaryOperator>(Expr))934    return false;935 936  return true;937}938 939static void objectifyExpr(const Expr *E, Commit &commit) {940  if (!E) return;941 942  QualType T = E->getType();943  if (T->isObjCObjectPointerType()) {944    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {945      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)946        return;947    } else {948      return;949    }950  } else if (!T->isPointerType()) {951    return;952  }953 954  SourceRange Range = E->getSourceRange();955  if (castOperatorNeedsParens(E))956    commit.insertWrap("(", Range, ")");957  commit.insertBefore(Range.getBegin(), "(id)");958}959 960//===----------------------------------------------------------------------===//961// rewriteToNumericBoxedExpression.962//===----------------------------------------------------------------------===//963 964static bool isEnumConstant(const Expr *E) {965  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))966    if (const ValueDecl *VD = DRE->getDecl())967      return isa<EnumConstantDecl>(VD);968 969  return false;970}971 972static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,973                                            const NSAPI &NS, Commit &commit) {974  if (Msg->getNumArgs() != 1)975    return false;976 977  const Expr *Arg = Msg->getArg(0);978  if (Arg->isTypeDependent())979    return false;980 981  ASTContext &Ctx = NS.getASTContext();982  Selector Sel = Msg->getSelector();983  std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =984      NS.getNSNumberLiteralMethodKind(Sel);985  if (!MKOpt)986    return false;987  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;988 989  const Expr *OrigArg = Arg->IgnoreImpCasts();990  QualType FinalTy = Arg->getType();991  QualType OrigTy = OrigArg->getType();992  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);993  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);994 995  bool isTruncated = FinalTySize < OrigTySize;996  bool needsCast = false;997 998  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {999    switch (ICE->getCastKind()) {1000    case CK_LValueToRValue:1001    case CK_NoOp:1002    case CK_UserDefinedConversion:1003    case CK_HLSLArrayRValue:1004      break;1005 1006    case CK_IntegralCast: {1007      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())1008        break;1009      // Be more liberal with Integer/UnsignedInteger which are very commonly1010      // used.1011      if ((MK == NSAPI::NSNumberWithInteger ||1012           MK == NSAPI::NSNumberWithUnsignedInteger) &&1013          !isTruncated) {1014        if (OrigTy->isEnumeralType() || isEnumConstant(OrigArg))1015          break;1016        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&1017            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))1018          break;1019      }1020 1021      needsCast = true;1022      break;1023    }1024 1025    case CK_PointerToBoolean:1026    case CK_IntegralToBoolean:1027    case CK_IntegralToFloating:1028    case CK_FloatingToIntegral:1029    case CK_FloatingToBoolean:1030    case CK_FloatingCast:1031    case CK_FloatingComplexToReal:1032    case CK_FloatingComplexToBoolean:1033    case CK_IntegralComplexToReal:1034    case CK_IntegralComplexToBoolean:1035    case CK_AtomicToNonAtomic:1036    case CK_AddressSpaceConversion:1037      needsCast = true;1038      break;1039 1040    case CK_Dependent:1041    case CK_BitCast:1042    case CK_LValueBitCast:1043    case CK_LValueToRValueBitCast:1044    case CK_BaseToDerived:1045    case CK_DerivedToBase:1046    case CK_UncheckedDerivedToBase:1047    case CK_Dynamic:1048    case CK_ToUnion:1049    case CK_ArrayToPointerDecay:1050    case CK_FunctionToPointerDecay:1051    case CK_NullToPointer:1052    case CK_NullToMemberPointer:1053    case CK_BaseToDerivedMemberPointer:1054    case CK_DerivedToBaseMemberPointer:1055    case CK_MemberPointerToBoolean:1056    case CK_ReinterpretMemberPointer:1057    case CK_ConstructorConversion:1058    case CK_IntegralToPointer:1059    case CK_PointerToIntegral:1060    case CK_ToVoid:1061    case CK_VectorSplat:1062    case CK_CPointerToObjCPointerCast:1063    case CK_BlockPointerToObjCPointerCast:1064    case CK_AnyPointerToBlockPointerCast:1065    case CK_ObjCObjectLValueCast:1066    case CK_FloatingRealToComplex:1067    case CK_FloatingComplexCast:1068    case CK_FloatingComplexToIntegralComplex:1069    case CK_IntegralRealToComplex:1070    case CK_IntegralComplexCast:1071    case CK_IntegralComplexToFloatingComplex:1072    case CK_ARCProduceObject:1073    case CK_ARCConsumeObject:1074    case CK_ARCReclaimReturnedObject:1075    case CK_ARCExtendBlockObject:1076    case CK_NonAtomicToAtomic:1077    case CK_CopyAndAutoreleaseBlockObject:1078    case CK_BuiltinFnToFnPtr:1079    case CK_ZeroToOCLOpaqueType:1080    case CK_IntToOCLSampler:1081    case CK_MatrixCast:1082      return false;1083 1084    case CK_BooleanToSignedIntegral:1085      llvm_unreachable("OpenCL-specific cast in Objective-C?");1086 1087    case CK_HLSLVectorTruncation:1088    case CK_HLSLElementwiseCast:1089    case CK_HLSLAggregateSplatCast:1090      llvm_unreachable("HLSL-specific cast in Objective-C?");1091      break;1092 1093    case CK_FloatingToFixedPoint:1094    case CK_FixedPointToFloating:1095    case CK_FixedPointCast:1096    case CK_FixedPointToBoolean:1097    case CK_FixedPointToIntegral:1098    case CK_IntegralToFixedPoint:1099      llvm_unreachable("Fixed point types are disabled for Objective-C");1100    }1101  }1102 1103  if (needsCast) {1104    DiagnosticsEngine &Diags = Ctx.getDiagnostics();1105    // FIXME: Use a custom category name to distinguish migration diagnostics.1106    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,1107                       "converting to boxing syntax requires casting %0 to %1");1108    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy1109        << Msg->getSourceRange();1110    return false;1111  }1112 1113  SourceRange ArgRange = OrigArg->getSourceRange();1114  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);1115 1116  if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))1117    commit.insertBefore(ArgRange.getBegin(), "@");1118  else1119    commit.insertWrap("@(", ArgRange, ")");1120 1121  return true;1122}1123 1124//===----------------------------------------------------------------------===//1125// rewriteToStringBoxedExpression.1126//===----------------------------------------------------------------------===//1127 1128static bool doRewriteToUTF8StringBoxedExpressionHelper(1129                                              const ObjCMessageExpr *Msg,1130                                              const NSAPI &NS, Commit &commit) {1131  const Expr *Arg = Msg->getArg(0);1132  if (Arg->isTypeDependent())1133    return false;1134 1135  ASTContext &Ctx = NS.getASTContext();1136 1137  const Expr *OrigArg = Arg->IgnoreImpCasts();1138  QualType OrigTy = OrigArg->getType();1139  if (OrigTy->isArrayType())1140    OrigTy = Ctx.getArrayDecayedType(OrigTy);1141 1142  if (const StringLiteral *1143        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {1144    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());1145    commit.insert(StrE->getBeginLoc(), "@");1146    return true;1147  }1148 1149  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {1150    QualType PointeeType = PT->getPointeeType();1151    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {1152      SourceRange ArgRange = OrigArg->getSourceRange();1153      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);1154 1155      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))1156        commit.insertBefore(ArgRange.getBegin(), "@");1157      else1158        commit.insertWrap("@(", ArgRange, ")");1159 1160      return true;1161    }1162  }1163 1164  return false;1165}1166 1167static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,1168                                           const NSAPI &NS, Commit &commit) {1169  Selector Sel = Msg->getSelector();1170 1171  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||1172      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||1173      Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {1174    if (Msg->getNumArgs() != 1)1175      return false;1176    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);1177  }1178 1179  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {1180    if (Msg->getNumArgs() != 2)1181      return false;1182 1183    const Expr *encodingArg = Msg->getArg(1);1184    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||1185        NS.isNSASCIIStringEncodingConstant(encodingArg))1186      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);1187  }1188 1189  return false;1190}1191