Path: blob/main/contrib/llvm-project/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
35262 views
//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Rewrites legacy method calls to modern syntax.9//10//===----------------------------------------------------------------------===//1112#include "clang/Edit/Rewriters.h"13#include "clang/AST/ASTContext.h"14#include "clang/AST/ExprCXX.h"15#include "clang/AST/ExprObjC.h"16#include "clang/AST/NSAPI.h"17#include "clang/AST/ParentMap.h"18#include "clang/Edit/Commit.h"19#include "clang/Lex/Lexer.h"20#include <optional>2122using namespace clang;23using namespace edit;2425static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,26IdentifierInfo *&ClassId,27const LangOptions &LangOpts) {28if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())29return false;3031const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();32if (!Receiver)33return false;34ClassId = Receiver->getIdentifier();3536if (Msg->getReceiverKind() == ObjCMessageExpr::Class)37return true;3839// When in ARC mode we also convert "[[.. alloc] init]" messages to literals,40// since the change from +1 to +0 will be handled fine by ARC.41if (LangOpts.ObjCAutoRefCount) {42if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {43if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(44Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {45if (Rec->getMethodFamily() == OMF_alloc)46return true;47}48}49}5051return false;52}5354//===----------------------------------------------------------------------===//55// rewriteObjCRedundantCallWithLiteral.56//===----------------------------------------------------------------------===//5758bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,59const NSAPI &NS, Commit &commit) {60IdentifierInfo *II = nullptr;61if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))62return false;63if (Msg->getNumArgs() != 1)64return false;6566const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();67Selector Sel = Msg->getSelector();6869if ((isa<ObjCStringLiteral>(Arg) &&70NS.getNSClassId(NSAPI::ClassId_NSString) == II &&71(NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||72NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||7374(isa<ObjCArrayLiteral>(Arg) &&75NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&76(NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||77NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||7879(isa<ObjCDictionaryLiteral>(Arg) &&80NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&81(NS.getNSDictionarySelector(82NSAPI::NSDict_dictionaryWithDictionary) == Sel ||83NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {8485commit.replaceWithInner(Msg->getSourceRange(),86Msg->getArg(0)->getSourceRange());87return true;88}8990return false;91}9293//===----------------------------------------------------------------------===//94// rewriteToObjCSubscriptSyntax.95//===----------------------------------------------------------------------===//9697/// Check for classes that accept 'objectForKey:' (or the other selectors98/// that the migrator handles) but return their instances as 'id', resulting99/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.100///101/// When checking if we can convert to subscripting syntax, check whether102/// the receiver is a result of a class method from a hardcoded list of103/// such classes. In such a case return the specific class as the interface104/// of the receiver.105///106/// FIXME: Remove this when these classes start using 'instancetype'.107static const ObjCInterfaceDecl *108maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,109const Expr *Receiver,110ASTContext &Ctx) {111assert(IFace && Receiver);112113// If the receiver has type 'id'...114if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))115return IFace;116117const ObjCMessageExpr *118InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());119if (!InnerMsg)120return IFace;121122QualType ClassRec;123switch (InnerMsg->getReceiverKind()) {124case ObjCMessageExpr::Instance:125case ObjCMessageExpr::SuperInstance:126return IFace;127128case ObjCMessageExpr::Class:129ClassRec = InnerMsg->getClassReceiver();130break;131case ObjCMessageExpr::SuperClass:132ClassRec = InnerMsg->getSuperType();133break;134}135136if (ClassRec.isNull())137return IFace;138139// ...and it is the result of a class message...140141const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();142if (!ObjTy)143return IFace;144const ObjCInterfaceDecl *OID = ObjTy->getInterface();145146// ...and the receiving class is NSMapTable or NSLocale, return that147// class as the receiving interface.148if (OID->getName() == "NSMapTable" ||149OID->getName() == "NSLocale")150return OID;151152return IFace;153}154155static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,156const ObjCMessageExpr *Msg,157ASTContext &Ctx,158Selector subscriptSel) {159const Expr *Rec = Msg->getInstanceReceiver();160if (!Rec)161return false;162IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);163164if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {165if (!MD->isUnavailable())166return true;167}168return false;169}170171static bool subscriptOperatorNeedsParens(const Expr *FullExpr);172173static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {174if (subscriptOperatorNeedsParens(Receiver)) {175SourceRange RecRange = Receiver->getSourceRange();176commit.insertWrap("(", RecRange, ")");177}178}179180static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,181Commit &commit) {182if (Msg->getNumArgs() != 1)183return false;184const Expr *Rec = Msg->getInstanceReceiver();185if (!Rec)186return false;187188SourceRange MsgRange = Msg->getSourceRange();189SourceRange RecRange = Rec->getSourceRange();190SourceRange ArgRange = Msg->getArg(0)->getSourceRange();191192commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),193ArgRange.getBegin()),194CharSourceRange::getTokenRange(RecRange));195commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),196ArgRange);197commit.insertWrap("[", ArgRange, "]");198maybePutParensOnReceiver(Rec, commit);199return true;200}201202static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,203const ObjCMessageExpr *Msg,204const NSAPI &NS,205Commit &commit) {206if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),207NS.getObjectAtIndexedSubscriptSelector()))208return false;209return rewriteToSubscriptGetCommon(Msg, commit);210}211212static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,213const ObjCMessageExpr *Msg,214const NSAPI &NS,215Commit &commit) {216if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),217NS.getObjectForKeyedSubscriptSelector()))218return false;219return rewriteToSubscriptGetCommon(Msg, commit);220}221222static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,223const ObjCMessageExpr *Msg,224const NSAPI &NS,225Commit &commit) {226if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),227NS.getSetObjectAtIndexedSubscriptSelector()))228return false;229230if (Msg->getNumArgs() != 2)231return false;232const Expr *Rec = Msg->getInstanceReceiver();233if (!Rec)234return false;235236SourceRange MsgRange = Msg->getSourceRange();237SourceRange RecRange = Rec->getSourceRange();238SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();239SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();240241commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),242Arg0Range.getBegin()),243CharSourceRange::getTokenRange(RecRange));244commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),245Arg1Range.getBegin()),246CharSourceRange::getTokenRange(Arg0Range));247commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),248Arg1Range);249commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),250Arg1Range.getBegin()),251"] = ");252maybePutParensOnReceiver(Rec, commit);253return true;254}255256static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,257const ObjCMessageExpr *Msg,258const NSAPI &NS,259Commit &commit) {260if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),261NS.getSetObjectForKeyedSubscriptSelector()))262return false;263264if (Msg->getNumArgs() != 2)265return false;266const Expr *Rec = Msg->getInstanceReceiver();267if (!Rec)268return false;269270SourceRange MsgRange = Msg->getSourceRange();271SourceRange RecRange = Rec->getSourceRange();272SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();273SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();274275SourceLocation LocBeforeVal = Arg0Range.getBegin();276commit.insertBefore(LocBeforeVal, "] = ");277commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,278/*beforePreviousInsertions=*/true);279commit.insertBefore(LocBeforeVal, "[");280commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),281Arg0Range.getBegin()),282CharSourceRange::getTokenRange(RecRange));283commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),284Arg0Range);285maybePutParensOnReceiver(Rec, commit);286return true;287}288289bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,290const NSAPI &NS, Commit &commit) {291if (!Msg || Msg->isImplicit() ||292Msg->getReceiverKind() != ObjCMessageExpr::Instance)293return false;294const ObjCMethodDecl *Method = Msg->getMethodDecl();295if (!Method)296return false;297298const ObjCInterfaceDecl *IFace =299NS.getASTContext().getObjContainingInterface(Method);300if (!IFace)301return false;302Selector Sel = Msg->getSelector();303304if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))305return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);306307if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))308return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);309310if (Msg->getNumArgs() != 2)311return false;312313if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))314return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);315316if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))317return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);318319return false;320}321322//===----------------------------------------------------------------------===//323// rewriteToObjCLiteralSyntax.324//===----------------------------------------------------------------------===//325326static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,327const NSAPI &NS, Commit &commit,328const ParentMap *PMap);329static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,330const NSAPI &NS, Commit &commit);331static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,332const NSAPI &NS, Commit &commit);333static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,334const NSAPI &NS, Commit &commit);335static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,336const NSAPI &NS, Commit &commit);337338bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,339const NSAPI &NS, Commit &commit,340const ParentMap *PMap) {341IdentifierInfo *II = nullptr;342if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))343return false;344345if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))346return rewriteToArrayLiteral(Msg, NS, commit, PMap);347if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))348return rewriteToDictionaryLiteral(Msg, NS, commit);349if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))350return rewriteToNumberLiteral(Msg, NS, commit);351if (II == NS.getNSClassId(NSAPI::ClassId_NSString))352return rewriteToStringBoxedExpression(Msg, NS, commit);353354return false;355}356357/// Returns true if the immediate message arguments of \c Msg should not358/// be rewritten because it will interfere with the rewrite of the parent359/// message expression. e.g.360/// \code361/// [NSDictionary dictionaryWithObjects:362/// [NSArray arrayWithObjects:@"1", @"2", nil]363/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];364/// \endcode365/// It will return true for this because we are going to rewrite this directly366/// to a dictionary literal without any array literals.367static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,368const NSAPI &NS);369370//===----------------------------------------------------------------------===//371// rewriteToArrayLiteral.372//===----------------------------------------------------------------------===//373374/// Adds an explicit cast to 'id' if the type is not objc object.375static void objectifyExpr(const Expr *E, Commit &commit);376377static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,378const NSAPI &NS, Commit &commit,379const ParentMap *PMap) {380if (PMap) {381const ObjCMessageExpr *ParentMsg =382dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));383if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))384return false;385}386387Selector Sel = Msg->getSelector();388SourceRange MsgRange = Msg->getSourceRange();389390if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {391if (Msg->getNumArgs() != 0)392return false;393commit.replace(MsgRange, "@[]");394return true;395}396397if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {398if (Msg->getNumArgs() != 1)399return false;400objectifyExpr(Msg->getArg(0), commit);401SourceRange ArgRange = Msg->getArg(0)->getSourceRange();402commit.replaceWithInner(MsgRange, ArgRange);403commit.insertWrap("@[", ArgRange, "]");404return true;405}406407if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||408Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {409if (Msg->getNumArgs() == 0)410return false;411const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);412if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))413return false;414415for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)416objectifyExpr(Msg->getArg(i), commit);417418if (Msg->getNumArgs() == 1) {419commit.replace(MsgRange, "@[]");420return true;421}422SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),423Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());424commit.replaceWithInner(MsgRange, ArgRange);425commit.insertWrap("@[", ArgRange, "]");426return true;427}428429return false;430}431432//===----------------------------------------------------------------------===//433// rewriteToDictionaryLiteral.434//===----------------------------------------------------------------------===//435436/// If \c Msg is an NSArray creation message or literal, this gets the437/// objects that were used to create it.438/// \returns true if it is an NSArray and we got objects, or false otherwise.439static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,440SmallVectorImpl<const Expr *> &Objs) {441if (!E)442return false;443444E = E->IgnoreParenCasts();445if (!E)446return false;447448if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {449IdentifierInfo *Cls = nullptr;450if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))451return false;452453if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))454return false;455456Selector Sel = Msg->getSelector();457if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))458return true; // empty array.459460if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {461if (Msg->getNumArgs() != 1)462return false;463Objs.push_back(Msg->getArg(0));464return true;465}466467if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||468Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {469if (Msg->getNumArgs() == 0)470return false;471const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);472if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))473return false;474475for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)476Objs.push_back(Msg->getArg(i));477return true;478}479480} else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {481for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)482Objs.push_back(ArrLit->getElement(i));483return true;484}485486return false;487}488489static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,490const NSAPI &NS, Commit &commit) {491Selector Sel = Msg->getSelector();492SourceRange MsgRange = Msg->getSourceRange();493494if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {495if (Msg->getNumArgs() != 0)496return false;497commit.replace(MsgRange, "@{}");498return true;499}500501if (Sel == NS.getNSDictionarySelector(502NSAPI::NSDict_dictionaryWithObjectForKey)) {503if (Msg->getNumArgs() != 2)504return false;505506objectifyExpr(Msg->getArg(0), commit);507objectifyExpr(Msg->getArg(1), commit);508509SourceRange ValRange = Msg->getArg(0)->getSourceRange();510SourceRange KeyRange = Msg->getArg(1)->getSourceRange();511// Insert key before the value.512commit.insertBefore(ValRange.getBegin(), ": ");513commit.insertFromRange(ValRange.getBegin(),514CharSourceRange::getTokenRange(KeyRange),515/*afterToken=*/false, /*beforePreviousInsertions=*/true);516commit.insertBefore(ValRange.getBegin(), "@{");517commit.insertAfterToken(ValRange.getEnd(), "}");518commit.replaceWithInner(MsgRange, ValRange);519return true;520}521522if (Sel == NS.getNSDictionarySelector(523NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||524Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {525if (Msg->getNumArgs() % 2 != 1)526return false;527unsigned SentinelIdx = Msg->getNumArgs() - 1;528const Expr *SentinelExpr = Msg->getArg(SentinelIdx);529if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))530return false;531532if (Msg->getNumArgs() == 1) {533commit.replace(MsgRange, "@{}");534return true;535}536537for (unsigned i = 0; i < SentinelIdx; i += 2) {538objectifyExpr(Msg->getArg(i), commit);539objectifyExpr(Msg->getArg(i+1), commit);540541SourceRange ValRange = Msg->getArg(i)->getSourceRange();542SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();543// Insert value after key.544commit.insertAfterToken(KeyRange.getEnd(), ": ");545commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);546commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),547KeyRange.getBegin()));548}549// Range of arguments up until and including the last key.550// The sentinel and first value are cut off, the value will move after the551// key.552SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),553Msg->getArg(SentinelIdx - 1)->getEndLoc());554commit.insertWrap("@{", ArgRange, "}");555commit.replaceWithInner(MsgRange, ArgRange);556return true;557}558559if (Sel == NS.getNSDictionarySelector(560NSAPI::NSDict_dictionaryWithObjectsForKeys) ||561Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {562if (Msg->getNumArgs() != 2)563return false;564565SmallVector<const Expr *, 8> Vals;566if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))567return false;568569SmallVector<const Expr *, 8> Keys;570if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))571return false;572573if (Vals.size() != Keys.size())574return false;575576if (Vals.empty()) {577commit.replace(MsgRange, "@{}");578return true;579}580581for (unsigned i = 0, n = Vals.size(); i < n; ++i) {582objectifyExpr(Vals[i], commit);583objectifyExpr(Keys[i], commit);584585SourceRange ValRange = Vals[i]->getSourceRange();586SourceRange KeyRange = Keys[i]->getSourceRange();587// Insert value after key.588commit.insertAfterToken(KeyRange.getEnd(), ": ");589commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);590}591// Range of arguments up until and including the last key.592// The first value is cut off, the value will move after the key.593SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());594commit.insertWrap("@{", ArgRange, "}");595commit.replaceWithInner(MsgRange, ArgRange);596return true;597}598599return false;600}601602static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,603const NSAPI &NS) {604if (!Msg)605return false;606607IdentifierInfo *II = nullptr;608if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))609return false;610611if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))612return false;613614Selector Sel = Msg->getSelector();615if (Sel == NS.getNSDictionarySelector(616NSAPI::NSDict_dictionaryWithObjectsForKeys) ||617Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {618if (Msg->getNumArgs() != 2)619return false;620621SmallVector<const Expr *, 8> Vals;622if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))623return false;624625SmallVector<const Expr *, 8> Keys;626if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))627return false;628629if (Vals.size() != Keys.size())630return false;631632return true;633}634635return false;636}637638//===----------------------------------------------------------------------===//639// rewriteToNumberLiteral.640//===----------------------------------------------------------------------===//641642static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,643const CharacterLiteral *Arg,644const NSAPI &NS, Commit &commit) {645if (Arg->getKind() != CharacterLiteralKind::Ascii)646return false;647if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,648Msg->getSelector())) {649SourceRange ArgRange = Arg->getSourceRange();650commit.replaceWithInner(Msg->getSourceRange(), ArgRange);651commit.insert(ArgRange.getBegin(), "@");652return true;653}654655return rewriteToNumericBoxedExpression(Msg, NS, commit);656}657658static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,659const Expr *Arg,660const NSAPI &NS, Commit &commit) {661if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,662Msg->getSelector())) {663SourceRange ArgRange = Arg->getSourceRange();664commit.replaceWithInner(Msg->getSourceRange(), ArgRange);665commit.insert(ArgRange.getBegin(), "@");666return true;667}668669return rewriteToNumericBoxedExpression(Msg, NS, commit);670}671672namespace {673674struct LiteralInfo {675bool Hex, Octal;676StringRef U, F, L, LL;677CharSourceRange WithoutSuffRange;678};679680}681682static bool getLiteralInfo(SourceRange literalRange,683bool isFloat, bool isIntZero,684ASTContext &Ctx, LiteralInfo &Info) {685if (literalRange.getBegin().isMacroID() ||686literalRange.getEnd().isMacroID())687return false;688StringRef text = Lexer::getSourceText(689CharSourceRange::getTokenRange(literalRange),690Ctx.getSourceManager(), Ctx.getLangOpts());691if (text.empty())692return false;693694std::optional<bool> UpperU, UpperL;695bool UpperF = false;696697struct Suff {698static bool has(StringRef suff, StringRef &text) {699return text.consume_back(suff);700}701};702703while (true) {704if (Suff::has("u", text)) {705UpperU = false;706} else if (Suff::has("U", text)) {707UpperU = true;708} else if (Suff::has("ll", text)) {709UpperL = false;710} else if (Suff::has("LL", text)) {711UpperL = true;712} else if (Suff::has("l", text)) {713UpperL = false;714} else if (Suff::has("L", text)) {715UpperL = true;716} else if (isFloat && Suff::has("f", text)) {717UpperF = false;718} else if (isFloat && Suff::has("F", text)) {719UpperF = true;720} else721break;722}723724if (!UpperU && !UpperL)725UpperU = UpperL = true;726else if (UpperU && !UpperL)727UpperL = UpperU;728else if (UpperL && !UpperU)729UpperU = UpperL;730731Info.U = *UpperU ? "U" : "u";732Info.L = *UpperL ? "L" : "l";733Info.LL = *UpperL ? "LL" : "ll";734Info.F = UpperF ? "F" : "f";735736Info.Hex = Info.Octal = false;737if (text.starts_with("0x"))738Info.Hex = true;739else if (!isFloat && !isIntZero && text.starts_with("0"))740Info.Octal = true;741742SourceLocation B = literalRange.getBegin();743Info.WithoutSuffRange =744CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));745return true;746}747748static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,749const NSAPI &NS, Commit &commit) {750if (Msg->getNumArgs() != 1)751return false;752753const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();754if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))755return rewriteToCharLiteral(Msg, CharE, NS, commit);756if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))757return rewriteToBoolLiteral(Msg, BE, NS, commit);758if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))759return rewriteToBoolLiteral(Msg, BE, NS, commit);760761const Expr *literalE = Arg;762if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {763if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)764literalE = UOE->getSubExpr();765}766767// Only integer and floating literals, otherwise try to rewrite to boxed768// expression.769if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))770return rewriteToNumericBoxedExpression(Msg, NS, commit);771772ASTContext &Ctx = NS.getASTContext();773Selector Sel = Msg->getSelector();774std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =775NS.getNSNumberLiteralMethodKind(Sel);776if (!MKOpt)777return false;778NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;779780bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;781bool CallIsFloating = false, CallIsDouble = false;782783switch (MK) {784// We cannot have these calls with int/float literals.785case NSAPI::NSNumberWithChar:786case NSAPI::NSNumberWithUnsignedChar:787case NSAPI::NSNumberWithShort:788case NSAPI::NSNumberWithUnsignedShort:789case NSAPI::NSNumberWithBool:790return rewriteToNumericBoxedExpression(Msg, NS, commit);791792case NSAPI::NSNumberWithUnsignedInt:793case NSAPI::NSNumberWithUnsignedInteger:794CallIsUnsigned = true;795[[fallthrough]];796case NSAPI::NSNumberWithInt:797case NSAPI::NSNumberWithInteger:798break;799800case NSAPI::NSNumberWithUnsignedLong:801CallIsUnsigned = true;802[[fallthrough]];803case NSAPI::NSNumberWithLong:804CallIsLong = true;805break;806807case NSAPI::NSNumberWithUnsignedLongLong:808CallIsUnsigned = true;809[[fallthrough]];810case NSAPI::NSNumberWithLongLong:811CallIsLongLong = true;812break;813814case NSAPI::NSNumberWithDouble:815CallIsDouble = true;816[[fallthrough]];817case NSAPI::NSNumberWithFloat:818CallIsFloating = true;819break;820}821822SourceRange ArgRange = Arg->getSourceRange();823QualType ArgTy = Arg->getType();824QualType CallTy = Msg->getArg(0)->getType();825826// Check for the easy case, the literal maps directly to the call.827if (Ctx.hasSameType(ArgTy, CallTy)) {828commit.replaceWithInner(Msg->getSourceRange(), ArgRange);829commit.insert(ArgRange.getBegin(), "@");830return true;831}832833// We will need to modify the literal suffix to get the same type as the call.834// Try with boxed expression if it came from a macro.835if (ArgRange.getBegin().isMacroID())836return rewriteToNumericBoxedExpression(Msg, NS, commit);837838bool LitIsFloat = ArgTy->isFloatingType();839// For a float passed to integer call, don't try rewriting to objc literal.840// It is difficult and a very uncommon case anyway.841// But try with boxed expression.842if (LitIsFloat && !CallIsFloating)843return rewriteToNumericBoxedExpression(Msg, NS, commit);844845// Try to modify the literal make it the same type as the method call.846// -Modify the suffix, and/or847// -Change integer to float848849LiteralInfo LitInfo;850bool isIntZero = false;851if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))852isIntZero = !IntE->getValue().getBoolValue();853if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))854return rewriteToNumericBoxedExpression(Msg, NS, commit);855856// Not easy to do int -> float with hex/octal and uncommon anyway.857if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))858return rewriteToNumericBoxedExpression(Msg, NS, commit);859860SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();861SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();862863commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),864LitInfo.WithoutSuffRange);865commit.insert(LitB, "@");866867if (!LitIsFloat && CallIsFloating)868commit.insert(LitE, ".0");869870if (CallIsFloating) {871if (!CallIsDouble)872commit.insert(LitE, LitInfo.F);873} else {874if (CallIsUnsigned)875commit.insert(LitE, LitInfo.U);876877if (CallIsLong)878commit.insert(LitE, LitInfo.L);879else if (CallIsLongLong)880commit.insert(LitE, LitInfo.LL);881}882return true;883}884885// FIXME: Make determination of operator precedence more general and886// make it broadly available.887static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {888const Expr* Expr = FullExpr->IgnoreImpCasts();889if (isa<ArraySubscriptExpr>(Expr) ||890isa<CallExpr>(Expr) ||891isa<DeclRefExpr>(Expr) ||892isa<CXXNamedCastExpr>(Expr) ||893isa<CXXConstructExpr>(Expr) ||894isa<CXXThisExpr>(Expr) ||895isa<CXXTypeidExpr>(Expr) ||896isa<CXXUnresolvedConstructExpr>(Expr) ||897isa<ObjCMessageExpr>(Expr) ||898isa<ObjCPropertyRefExpr>(Expr) ||899isa<ObjCProtocolExpr>(Expr) ||900isa<MemberExpr>(Expr) ||901isa<ObjCIvarRefExpr>(Expr) ||902isa<ParenExpr>(FullExpr) ||903isa<ParenListExpr>(Expr) ||904isa<SizeOfPackExpr>(Expr))905return false;906907return true;908}909static bool castOperatorNeedsParens(const Expr *FullExpr) {910const Expr* Expr = FullExpr->IgnoreImpCasts();911if (isa<ArraySubscriptExpr>(Expr) ||912isa<CallExpr>(Expr) ||913isa<DeclRefExpr>(Expr) ||914isa<CastExpr>(Expr) ||915isa<CXXNewExpr>(Expr) ||916isa<CXXConstructExpr>(Expr) ||917isa<CXXDeleteExpr>(Expr) ||918isa<CXXNoexceptExpr>(Expr) ||919isa<CXXPseudoDestructorExpr>(Expr) ||920isa<CXXScalarValueInitExpr>(Expr) ||921isa<CXXThisExpr>(Expr) ||922isa<CXXTypeidExpr>(Expr) ||923isa<CXXUnresolvedConstructExpr>(Expr) ||924isa<ObjCMessageExpr>(Expr) ||925isa<ObjCPropertyRefExpr>(Expr) ||926isa<ObjCProtocolExpr>(Expr) ||927isa<MemberExpr>(Expr) ||928isa<ObjCIvarRefExpr>(Expr) ||929isa<ParenExpr>(FullExpr) ||930isa<ParenListExpr>(Expr) ||931isa<SizeOfPackExpr>(Expr) ||932isa<UnaryOperator>(Expr))933return false;934935return true;936}937938static void objectifyExpr(const Expr *E, Commit &commit) {939if (!E) return;940941QualType T = E->getType();942if (T->isObjCObjectPointerType()) {943if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {944if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)945return;946} else {947return;948}949} else if (!T->isPointerType()) {950return;951}952953SourceRange Range = E->getSourceRange();954if (castOperatorNeedsParens(E))955commit.insertWrap("(", Range, ")");956commit.insertBefore(Range.getBegin(), "(id)");957}958959//===----------------------------------------------------------------------===//960// rewriteToNumericBoxedExpression.961//===----------------------------------------------------------------------===//962963static bool isEnumConstant(const Expr *E) {964if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))965if (const ValueDecl *VD = DRE->getDecl())966return isa<EnumConstantDecl>(VD);967968return false;969}970971static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,972const NSAPI &NS, Commit &commit) {973if (Msg->getNumArgs() != 1)974return false;975976const Expr *Arg = Msg->getArg(0);977if (Arg->isTypeDependent())978return false;979980ASTContext &Ctx = NS.getASTContext();981Selector Sel = Msg->getSelector();982std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =983NS.getNSNumberLiteralMethodKind(Sel);984if (!MKOpt)985return false;986NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;987988const Expr *OrigArg = Arg->IgnoreImpCasts();989QualType FinalTy = Arg->getType();990QualType OrigTy = OrigArg->getType();991uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);992uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);993994bool isTruncated = FinalTySize < OrigTySize;995bool needsCast = false;996997if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {998switch (ICE->getCastKind()) {999case CK_LValueToRValue:1000case CK_NoOp:1001case CK_UserDefinedConversion:1002case CK_HLSLArrayRValue:1003break;10041005case CK_IntegralCast: {1006if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())1007break;1008// Be more liberal with Integer/UnsignedInteger which are very commonly1009// used.1010if ((MK == NSAPI::NSNumberWithInteger ||1011MK == NSAPI::NSNumberWithUnsignedInteger) &&1012!isTruncated) {1013if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))1014break;1015if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&1016OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))1017break;1018}10191020needsCast = true;1021break;1022}10231024case CK_PointerToBoolean:1025case CK_IntegralToBoolean:1026case CK_IntegralToFloating:1027case CK_FloatingToIntegral:1028case CK_FloatingToBoolean:1029case CK_FloatingCast:1030case CK_FloatingComplexToReal:1031case CK_FloatingComplexToBoolean:1032case CK_IntegralComplexToReal:1033case CK_IntegralComplexToBoolean:1034case CK_AtomicToNonAtomic:1035case CK_AddressSpaceConversion:1036needsCast = true;1037break;10381039case CK_Dependent:1040case CK_BitCast:1041case CK_LValueBitCast:1042case CK_LValueToRValueBitCast:1043case CK_BaseToDerived:1044case CK_DerivedToBase:1045case CK_UncheckedDerivedToBase:1046case CK_Dynamic:1047case CK_ToUnion:1048case CK_ArrayToPointerDecay:1049case CK_FunctionToPointerDecay:1050case CK_NullToPointer:1051case CK_NullToMemberPointer:1052case CK_BaseToDerivedMemberPointer:1053case CK_DerivedToBaseMemberPointer:1054case CK_MemberPointerToBoolean:1055case CK_ReinterpretMemberPointer:1056case CK_ConstructorConversion:1057case CK_IntegralToPointer:1058case CK_PointerToIntegral:1059case CK_ToVoid:1060case CK_VectorSplat:1061case CK_CPointerToObjCPointerCast:1062case CK_BlockPointerToObjCPointerCast:1063case CK_AnyPointerToBlockPointerCast:1064case CK_ObjCObjectLValueCast:1065case CK_FloatingRealToComplex:1066case CK_FloatingComplexCast:1067case CK_FloatingComplexToIntegralComplex:1068case CK_IntegralRealToComplex:1069case CK_IntegralComplexCast:1070case CK_IntegralComplexToFloatingComplex:1071case CK_ARCProduceObject:1072case CK_ARCConsumeObject:1073case CK_ARCReclaimReturnedObject:1074case CK_ARCExtendBlockObject:1075case CK_NonAtomicToAtomic:1076case CK_CopyAndAutoreleaseBlockObject:1077case CK_BuiltinFnToFnPtr:1078case CK_ZeroToOCLOpaqueType:1079case CK_IntToOCLSampler:1080case CK_MatrixCast:1081return false;10821083case CK_BooleanToSignedIntegral:1084llvm_unreachable("OpenCL-specific cast in Objective-C?");10851086case CK_HLSLVectorTruncation:1087llvm_unreachable("HLSL-specific cast in Objective-C?");1088break;10891090case CK_FloatingToFixedPoint:1091case CK_FixedPointToFloating:1092case CK_FixedPointCast:1093case CK_FixedPointToBoolean:1094case CK_FixedPointToIntegral:1095case CK_IntegralToFixedPoint:1096llvm_unreachable("Fixed point types are disabled for Objective-C");1097}1098}10991100if (needsCast) {1101DiagnosticsEngine &Diags = Ctx.getDiagnostics();1102// FIXME: Use a custom category name to distinguish migration diagnostics.1103unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,1104"converting to boxing syntax requires casting %0 to %1");1105Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy1106<< Msg->getSourceRange();1107return false;1108}11091110SourceRange ArgRange = OrigArg->getSourceRange();1111commit.replaceWithInner(Msg->getSourceRange(), ArgRange);11121113if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))1114commit.insertBefore(ArgRange.getBegin(), "@");1115else1116commit.insertWrap("@(", ArgRange, ")");11171118return true;1119}11201121//===----------------------------------------------------------------------===//1122// rewriteToStringBoxedExpression.1123//===----------------------------------------------------------------------===//11241125static bool doRewriteToUTF8StringBoxedExpressionHelper(1126const ObjCMessageExpr *Msg,1127const NSAPI &NS, Commit &commit) {1128const Expr *Arg = Msg->getArg(0);1129if (Arg->isTypeDependent())1130return false;11311132ASTContext &Ctx = NS.getASTContext();11331134const Expr *OrigArg = Arg->IgnoreImpCasts();1135QualType OrigTy = OrigArg->getType();1136if (OrigTy->isArrayType())1137OrigTy = Ctx.getArrayDecayedType(OrigTy);11381139if (const StringLiteral *1140StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {1141commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());1142commit.insert(StrE->getBeginLoc(), "@");1143return true;1144}11451146if (const PointerType *PT = OrigTy->getAs<PointerType>()) {1147QualType PointeeType = PT->getPointeeType();1148if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {1149SourceRange ArgRange = OrigArg->getSourceRange();1150commit.replaceWithInner(Msg->getSourceRange(), ArgRange);11511152if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))1153commit.insertBefore(ArgRange.getBegin(), "@");1154else1155commit.insertWrap("@(", ArgRange, ")");11561157return true;1158}1159}11601161return false;1162}11631164static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,1165const NSAPI &NS, Commit &commit) {1166Selector Sel = Msg->getSelector();11671168if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||1169Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||1170Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {1171if (Msg->getNumArgs() != 1)1172return false;1173return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);1174}11751176if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {1177if (Msg->getNumArgs() != 2)1178return false;11791180const Expr *encodingArg = Msg->getArg(1);1181if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||1182NS.isNSASCIIStringEncodingConstant(encodingArg))1183return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);1184}11851186return false;1187}118811891190