|  | //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // 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" | 
|  | #include <optional> | 
|  |  | 
|  | 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)->getBeginLoc(), | 
|  | Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc()); | 
|  | 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)->getBeginLoc(), | 
|  | Msg->getArg(SentinelIdx - 1)->getEndLoc()); | 
|  | 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()->getBeginLoc(), Keys.back()->getEndLoc()); | 
|  | 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() != CharacterLiteralKind::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; | 
|  |  | 
|  | std::optional<bool> UpperU, UpperL; | 
|  | bool UpperF = false; | 
|  |  | 
|  | struct Suff { | 
|  | static bool has(StringRef suff, StringRef &text) { | 
|  | return text.consume_back(suff); | 
|  | } | 
|  | }; | 
|  |  | 
|  | while (true) { | 
|  | 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 && !UpperL) | 
|  | UpperU = UpperL = true; | 
|  | else if (UpperU && !UpperL) | 
|  | UpperL = UpperU; | 
|  | else if (UpperL && !UpperU) | 
|  | 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.starts_with("0x")) | 
|  | Info.Hex = true; | 
|  | else if (!isFloat && !isIntZero && text.starts_with("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(); | 
|  | std::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; | 
|  | [[fallthrough]]; | 
|  | case NSAPI::NSNumberWithInt: | 
|  | case NSAPI::NSNumberWithInteger: | 
|  | break; | 
|  |  | 
|  | case NSAPI::NSNumberWithUnsignedLong: | 
|  | CallIsUnsigned = true; | 
|  | [[fallthrough]]; | 
|  | case NSAPI::NSNumberWithLong: | 
|  | CallIsLong = true; | 
|  | break; | 
|  |  | 
|  | case NSAPI::NSNumberWithUnsignedLongLong: | 
|  | CallIsUnsigned = true; | 
|  | [[fallthrough]]; | 
|  | case NSAPI::NSNumberWithLongLong: | 
|  | CallIsLongLong = true; | 
|  | break; | 
|  |  | 
|  | case NSAPI::NSNumberWithDouble: | 
|  | CallIsDouble = true; | 
|  | [[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(); | 
|  | std::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: | 
|  | case CK_HLSLArrayRValue: | 
|  | 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_LValueToRValueBitCast: | 
|  | 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_ZeroToOCLOpaqueType: | 
|  | case CK_IntToOCLSampler: | 
|  | case CK_MatrixCast: | 
|  | return false; | 
|  |  | 
|  | case CK_BooleanToSignedIntegral: | 
|  | llvm_unreachable("OpenCL-specific cast in Objective-C?"); | 
|  |  | 
|  | case CK_HLSLVectorTruncation: | 
|  | case CK_HLSLElementwiseCast: | 
|  | case CK_HLSLAggregateSplatCast: | 
|  | llvm_unreachable("HLSL-specific cast in Objective-C?"); | 
|  | break; | 
|  |  | 
|  | case CK_FloatingToFixedPoint: | 
|  | case CK_FixedPointToFloating: | 
|  | case CK_FixedPointCast: | 
|  | case CK_FixedPointToBoolean: | 
|  | case CK_FixedPointToIntegral: | 
|  | case CK_IntegralToFixedPoint: | 
|  | llvm_unreachable("Fixed point types are disabled for 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->getBeginLoc(), "@"); | 
|  | 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; | 
|  | } |