1 //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Rewrites legacy method calls to modern syntax.
12 //===----------------------------------------------------------------------===//
14 #include "clang/Edit/Rewriters.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/ExprCXX.h"
17 #include "clang/AST/ExprObjC.h"
18 #include "clang/AST/NSAPI.h"
19 #include "clang/AST/ParentMap.h"
20 #include "clang/Edit/Commit.h"
21 #include "clang/Lex/Lexer.h"
23 using namespace clang;
26 static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
27 IdentifierInfo *&ClassId,
28 const LangOptions &LangOpts) {
29 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
32 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
35 ClassId = Receiver->getIdentifier();
37 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
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)
55 //===----------------------------------------------------------------------===//
56 // rewriteObjCRedundantCallWithLiteral.
57 //===----------------------------------------------------------------------===//
59 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
60 const NSAPI &NS, Commit &commit) {
61 IdentifierInfo *II = nullptr;
62 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
64 if (Msg->getNumArgs() != 1)
67 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
68 Selector Sel = Msg->getSelector();
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)) ||
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)) ||
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))) {
86 commit.replaceWithInner(Msg->getSourceRange(),
87 Msg->getArg(0)->getSourceRange());
94 //===----------------------------------------------------------------------===//
95 // rewriteToObjCSubscriptSyntax.
96 //===----------------------------------------------------------------------===//
98 /// Check for classes that accept 'objectForKey:' (or the other selectors
99 /// that the migrator handles) but return their instances as 'id', resulting
100 /// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
102 /// When checking if we can convert to subscripting syntax, check whether
103 /// the receiver is a result of a class method from a hardcoded list of
104 /// such classes. In such a case return the specific class as the interface
107 /// FIXME: Remove this when these classes start using 'instancetype'.
108 static const ObjCInterfaceDecl *
109 maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
110 const Expr *Receiver,
112 assert(IFace && Receiver);
114 // If the receiver has type 'id'...
115 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
118 const ObjCMessageExpr *
119 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
124 switch (InnerMsg->getReceiverKind()) {
125 case ObjCMessageExpr::Instance:
126 case ObjCMessageExpr::SuperInstance:
129 case ObjCMessageExpr::Class:
130 ClassRec = InnerMsg->getClassReceiver();
132 case ObjCMessageExpr::SuperClass:
133 ClassRec = InnerMsg->getSuperType();
137 if (ClassRec.isNull())
140 // ...and it is the result of a class message...
142 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
145 const ObjCInterfaceDecl *OID = ObjTy->getInterface();
147 // ...and the receiving class is NSMapTable or NSLocale, return that
148 // class as the receiving interface.
149 if (OID->getName() == "NSMapTable" ||
150 OID->getName() == "NSLocale")
156 static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
157 const ObjCMessageExpr *Msg,
159 Selector subscriptSel) {
160 const Expr *Rec = Msg->getInstanceReceiver();
163 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
165 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
166 if (!MD->isUnavailable())
172 static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
174 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
175 if (subscriptOperatorNeedsParens(Receiver)) {
176 SourceRange RecRange = Receiver->getSourceRange();
177 commit.insertWrap("(", RecRange, ")");
181 static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
183 if (Msg->getNumArgs() != 1)
185 const Expr *Rec = Msg->getInstanceReceiver();
189 SourceRange MsgRange = Msg->getSourceRange();
190 SourceRange RecRange = Rec->getSourceRange();
191 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
193 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
194 ArgRange.getBegin()),
195 CharSourceRange::getTokenRange(RecRange));
196 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
198 commit.insertWrap("[", ArgRange, "]");
199 maybePutParensOnReceiver(Rec, commit);
203 static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204 const ObjCMessageExpr *Msg,
207 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
208 NS.getObjectAtIndexedSubscriptSelector()))
210 return rewriteToSubscriptGetCommon(Msg, commit);
213 static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
214 const ObjCMessageExpr *Msg,
217 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
218 NS.getObjectForKeyedSubscriptSelector()))
220 return rewriteToSubscriptGetCommon(Msg, commit);
223 static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
224 const ObjCMessageExpr *Msg,
227 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
228 NS.getSetObjectAtIndexedSubscriptSelector()))
231 if (Msg->getNumArgs() != 2)
233 const Expr *Rec = Msg->getInstanceReceiver();
237 SourceRange MsgRange = Msg->getSourceRange();
238 SourceRange RecRange = Rec->getSourceRange();
239 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
240 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
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()),
250 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
251 Arg1Range.getBegin()),
253 maybePutParensOnReceiver(Rec, commit);
257 static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258 const ObjCMessageExpr *Msg,
261 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
262 NS.getSetObjectForKeyedSubscriptSelector()))
265 if (Msg->getNumArgs() != 2)
267 const Expr *Rec = Msg->getInstanceReceiver();
271 SourceRange MsgRange = Msg->getSourceRange();
272 SourceRange RecRange = Rec->getSourceRange();
273 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
274 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
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()),
286 maybePutParensOnReceiver(Rec, commit);
290 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
291 const NSAPI &NS, Commit &commit) {
292 if (!Msg || Msg->isImplicit() ||
293 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
295 const ObjCMethodDecl *Method = Msg->getMethodDecl();
299 const ObjCInterfaceDecl *IFace =
300 NS.getASTContext().getObjContainingInterface(Method);
303 Selector Sel = Msg->getSelector();
305 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
306 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
308 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
309 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
311 if (Msg->getNumArgs() != 2)
314 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
315 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
317 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
318 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
323 //===----------------------------------------------------------------------===//
324 // rewriteToObjCLiteralSyntax.
325 //===----------------------------------------------------------------------===//
327 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
328 const NSAPI &NS, Commit &commit,
329 const ParentMap *PMap);
330 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
331 const NSAPI &NS, Commit &commit);
332 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
333 const NSAPI &NS, Commit &commit);
334 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
335 const NSAPI &NS, Commit &commit);
336 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
337 const NSAPI &NS, Commit &commit);
339 bool 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()))
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);
358 /// Returns true if the immediate message arguments of \c Msg should not
359 /// be rewritten because it will interfere with the rewrite of the parent
360 /// message expression. e.g.
362 /// [NSDictionary dictionaryWithObjects:
363 /// [NSArray arrayWithObjects:@"1", @"2", nil]
364 /// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
366 /// It will return true for this because we are going to rewrite this directly
367 /// to a dictionary literal without any array literals.
368 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
371 //===----------------------------------------------------------------------===//
372 // rewriteToArrayLiteral.
373 //===----------------------------------------------------------------------===//
375 /// Adds an explicit cast to 'id' if the type is not objc object.
376 static void objectifyExpr(const Expr *E, Commit &commit);
378 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
379 const NSAPI &NS, Commit &commit,
380 const ParentMap *PMap) {
382 const ObjCMessageExpr *ParentMsg =
383 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
384 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
388 Selector Sel = Msg->getSelector();
389 SourceRange MsgRange = Msg->getSourceRange();
391 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
392 if (Msg->getNumArgs() != 0)
394 commit.replace(MsgRange, "@[]");
398 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
399 if (Msg->getNumArgs() != 1)
401 objectifyExpr(Msg->getArg(0), commit);
402 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
403 commit.replaceWithInner(MsgRange, ArgRange);
404 commit.insertWrap("@[", ArgRange, "]");
408 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
409 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
410 if (Msg->getNumArgs() == 0)
412 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
413 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
416 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
417 objectifyExpr(Msg->getArg(i), commit);
419 if (Msg->getNumArgs() == 1) {
420 commit.replace(MsgRange, "@[]");
423 SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
424 Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
425 commit.replaceWithInner(MsgRange, ArgRange);
426 commit.insertWrap("@[", ArgRange, "]");
433 //===----------------------------------------------------------------------===//
434 // rewriteToDictionaryLiteral.
435 //===----------------------------------------------------------------------===//
437 /// If \c Msg is an NSArray creation message or literal, this gets the
438 /// objects that were used to create it.
439 /// \returns true if it is an NSArray and we got objects, or false otherwise.
440 static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
441 SmallVectorImpl<const Expr *> &Objs) {
445 E = E->IgnoreParenCasts();
449 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
450 IdentifierInfo *Cls = nullptr;
451 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
454 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
457 Selector Sel = Msg->getSelector();
458 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
459 return true; // empty array.
461 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
462 if (Msg->getNumArgs() != 1)
464 Objs.push_back(Msg->getArg(0));
468 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
469 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
470 if (Msg->getNumArgs() == 0)
472 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
473 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
476 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
477 Objs.push_back(Msg->getArg(i));
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));
490 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
491 const NSAPI &NS, Commit &commit) {
492 Selector Sel = Msg->getSelector();
493 SourceRange MsgRange = Msg->getSourceRange();
495 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
496 if (Msg->getNumArgs() != 0)
498 commit.replace(MsgRange, "@{}");
502 if (Sel == NS.getNSDictionarySelector(
503 NSAPI::NSDict_dictionaryWithObjectForKey)) {
504 if (Msg->getNumArgs() != 2)
507 objectifyExpr(Msg->getArg(0), commit);
508 objectifyExpr(Msg->getArg(1), commit);
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);
523 if (Sel == NS.getNSDictionarySelector(
524 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
525 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
526 if (Msg->getNumArgs() % 2 != 1)
528 unsigned SentinelIdx = Msg->getNumArgs() - 1;
529 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
530 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
533 if (Msg->getNumArgs() == 1) {
534 commit.replace(MsgRange, "@{}");
538 for (unsigned i = 0; i < SentinelIdx; i += 2) {
539 objectifyExpr(Msg->getArg(i), commit);
540 objectifyExpr(Msg->getArg(i+1), commit);
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()));
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 the
553 SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
554 Msg->getArg(SentinelIdx - 1)->getEndLoc());
555 commit.insertWrap("@{", ArgRange, "}");
556 commit.replaceWithInner(MsgRange, ArgRange);
560 if (Sel == NS.getNSDictionarySelector(
561 NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
562 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
563 if (Msg->getNumArgs() != 2)
566 SmallVector<const Expr *, 8> Vals;
567 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
570 SmallVector<const Expr *, 8> Keys;
571 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
574 if (Vals.size() != Keys.size())
578 commit.replace(MsgRange, "@{}");
582 for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
583 objectifyExpr(Vals[i], commit);
584 objectifyExpr(Keys[i], commit);
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);
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);
603 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
608 IdentifierInfo *II = nullptr;
609 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
612 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
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)
622 SmallVector<const Expr *, 8> Vals;
623 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
626 SmallVector<const Expr *, 8> Keys;
627 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
630 if (Vals.size() != Keys.size())
639 //===----------------------------------------------------------------------===//
640 // rewriteToNumberLiteral.
641 //===----------------------------------------------------------------------===//
643 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
644 const CharacterLiteral *Arg,
645 const NSAPI &NS, Commit &commit) {
646 if (Arg->getKind() != CharacterLiteral::Ascii)
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(), "@");
656 return rewriteToNumericBoxedExpression(Msg, NS, commit);
659 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
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(), "@");
670 return rewriteToNumericBoxedExpression(Msg, NS, commit);
677 StringRef U, F, L, LL;
678 CharSourceRange WithoutSuffRange;
683 static bool getLiteralInfo(SourceRange literalRange,
684 bool isFloat, bool isIntZero,
685 ASTContext &Ctx, LiteralInfo &Info) {
686 if (literalRange.getBegin().isMacroID() ||
687 literalRange.getEnd().isMacroID())
689 StringRef text = Lexer::getSourceText(
690 CharSourceRange::getTokenRange(literalRange),
691 Ctx.getSourceManager(), Ctx.getLangOpts());
695 Optional<bool> UpperU, UpperL;
699 static bool has(StringRef suff, StringRef &text) {
700 if (text.endswith(suff)) {
701 text = text.substr(0, text.size()-suff.size());
709 if (Suff::has("u", text)) {
711 } else if (Suff::has("U", text)) {
713 } else if (Suff::has("ll", text)) {
715 } else if (Suff::has("LL", text)) {
717 } else if (Suff::has("l", text)) {
719 } else if (Suff::has("L", text)) {
721 } else if (isFloat && Suff::has("f", text)) {
723 } else if (isFloat && Suff::has("F", text)) {
729 if (!UpperU.hasValue() && !UpperL.hasValue())
730 UpperU = UpperL = true;
731 else if (UpperU.hasValue() && !UpperL.hasValue())
733 else if (UpperL.hasValue() && !UpperU.hasValue())
736 Info.U = *UpperU ? "U" : "u";
737 Info.L = *UpperL ? "L" : "l";
738 Info.LL = *UpperL ? "LL" : "ll";
739 Info.F = UpperF ? "F" : "f";
741 Info.Hex = Info.Octal = false;
742 if (text.startswith("0x"))
744 else if (!isFloat && !isIntZero && text.startswith("0"))
747 SourceLocation B = literalRange.getBegin();
748 Info.WithoutSuffRange =
749 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
753 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
754 const NSAPI &NS, Commit &commit) {
755 if (Msg->getNumArgs() != 1)
758 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
759 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
760 return rewriteToCharLiteral(Msg, CharE, NS, commit);
761 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
762 return rewriteToBoolLiteral(Msg, BE, NS, commit);
763 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
764 return rewriteToBoolLiteral(Msg, BE, NS, commit);
766 const Expr *literalE = Arg;
767 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
768 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
769 literalE = UOE->getSubExpr();
772 // Only integer and floating literals, otherwise try to rewrite to boxed
774 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
775 return rewriteToNumericBoxedExpression(Msg, NS, commit);
777 ASTContext &Ctx = NS.getASTContext();
778 Selector Sel = Msg->getSelector();
779 Optional<NSAPI::NSNumberLiteralMethodKind>
780 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
783 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
785 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
786 bool CallIsFloating = false, CallIsDouble = false;
789 // We cannot have these calls with int/float literals.
790 case NSAPI::NSNumberWithChar:
791 case NSAPI::NSNumberWithUnsignedChar:
792 case NSAPI::NSNumberWithShort:
793 case NSAPI::NSNumberWithUnsignedShort:
794 case NSAPI::NSNumberWithBool:
795 return rewriteToNumericBoxedExpression(Msg, NS, commit);
797 case NSAPI::NSNumberWithUnsignedInt:
798 case NSAPI::NSNumberWithUnsignedInteger:
799 CallIsUnsigned = true;
801 case NSAPI::NSNumberWithInt:
802 case NSAPI::NSNumberWithInteger:
805 case NSAPI::NSNumberWithUnsignedLong:
806 CallIsUnsigned = true;
808 case NSAPI::NSNumberWithLong:
812 case NSAPI::NSNumberWithUnsignedLongLong:
813 CallIsUnsigned = true;
815 case NSAPI::NSNumberWithLongLong:
816 CallIsLongLong = true;
819 case NSAPI::NSNumberWithDouble:
822 case NSAPI::NSNumberWithFloat:
823 CallIsFloating = true;
827 SourceRange ArgRange = Arg->getSourceRange();
828 QualType ArgTy = Arg->getType();
829 QualType CallTy = Msg->getArg(0)->getType();
831 // Check for the easy case, the literal maps directly to the call.
832 if (Ctx.hasSameType(ArgTy, CallTy)) {
833 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
834 commit.insert(ArgRange.getBegin(), "@");
838 // We will need to modify the literal suffix to get the same type as the call.
839 // Try with boxed expression if it came from a macro.
840 if (ArgRange.getBegin().isMacroID())
841 return rewriteToNumericBoxedExpression(Msg, NS, commit);
843 bool LitIsFloat = ArgTy->isFloatingType();
844 // For a float passed to integer call, don't try rewriting to objc literal.
845 // It is difficult and a very uncommon case anyway.
846 // But try with boxed expression.
847 if (LitIsFloat && !CallIsFloating)
848 return rewriteToNumericBoxedExpression(Msg, NS, commit);
850 // Try to modify the literal make it the same type as the method call.
851 // -Modify the suffix, and/or
852 // -Change integer to float
855 bool isIntZero = false;
856 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
857 isIntZero = !IntE->getValue().getBoolValue();
858 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
859 return rewriteToNumericBoxedExpression(Msg, NS, commit);
861 // Not easy to do int -> float with hex/octal and uncommon anyway.
862 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
863 return rewriteToNumericBoxedExpression(Msg, NS, commit);
865 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
866 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
868 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
869 LitInfo.WithoutSuffRange);
870 commit.insert(LitB, "@");
872 if (!LitIsFloat && CallIsFloating)
873 commit.insert(LitE, ".0");
875 if (CallIsFloating) {
877 commit.insert(LitE, LitInfo.F);
880 commit.insert(LitE, LitInfo.U);
883 commit.insert(LitE, LitInfo.L);
884 else if (CallIsLongLong)
885 commit.insert(LitE, LitInfo.LL);
890 // FIXME: Make determination of operator precedence more general and
891 // make it broadly available.
892 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
893 const Expr* Expr = FullExpr->IgnoreImpCasts();
894 if (isa<ArraySubscriptExpr>(Expr) ||
895 isa<CallExpr>(Expr) ||
896 isa<DeclRefExpr>(Expr) ||
897 isa<CXXNamedCastExpr>(Expr) ||
898 isa<CXXConstructExpr>(Expr) ||
899 isa<CXXThisExpr>(Expr) ||
900 isa<CXXTypeidExpr>(Expr) ||
901 isa<CXXUnresolvedConstructExpr>(Expr) ||
902 isa<ObjCMessageExpr>(Expr) ||
903 isa<ObjCPropertyRefExpr>(Expr) ||
904 isa<ObjCProtocolExpr>(Expr) ||
905 isa<MemberExpr>(Expr) ||
906 isa<ObjCIvarRefExpr>(Expr) ||
907 isa<ParenExpr>(FullExpr) ||
908 isa<ParenListExpr>(Expr) ||
909 isa<SizeOfPackExpr>(Expr))
914 static bool castOperatorNeedsParens(const Expr *FullExpr) {
915 const Expr* Expr = FullExpr->IgnoreImpCasts();
916 if (isa<ArraySubscriptExpr>(Expr) ||
917 isa<CallExpr>(Expr) ||
918 isa<DeclRefExpr>(Expr) ||
919 isa<CastExpr>(Expr) ||
920 isa<CXXNewExpr>(Expr) ||
921 isa<CXXConstructExpr>(Expr) ||
922 isa<CXXDeleteExpr>(Expr) ||
923 isa<CXXNoexceptExpr>(Expr) ||
924 isa<CXXPseudoDestructorExpr>(Expr) ||
925 isa<CXXScalarValueInitExpr>(Expr) ||
926 isa<CXXThisExpr>(Expr) ||
927 isa<CXXTypeidExpr>(Expr) ||
928 isa<CXXUnresolvedConstructExpr>(Expr) ||
929 isa<ObjCMessageExpr>(Expr) ||
930 isa<ObjCPropertyRefExpr>(Expr) ||
931 isa<ObjCProtocolExpr>(Expr) ||
932 isa<MemberExpr>(Expr) ||
933 isa<ObjCIvarRefExpr>(Expr) ||
934 isa<ParenExpr>(FullExpr) ||
935 isa<ParenListExpr>(Expr) ||
936 isa<SizeOfPackExpr>(Expr) ||
937 isa<UnaryOperator>(Expr))
943 static void objectifyExpr(const Expr *E, Commit &commit) {
946 QualType T = E->getType();
947 if (T->isObjCObjectPointerType()) {
948 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
949 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
954 } else if (!T->isPointerType()) {
958 SourceRange Range = E->getSourceRange();
959 if (castOperatorNeedsParens(E))
960 commit.insertWrap("(", Range, ")");
961 commit.insertBefore(Range.getBegin(), "(id)");
964 //===----------------------------------------------------------------------===//
965 // rewriteToNumericBoxedExpression.
966 //===----------------------------------------------------------------------===//
968 static bool isEnumConstant(const Expr *E) {
969 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
970 if (const ValueDecl *VD = DRE->getDecl())
971 return isa<EnumConstantDecl>(VD);
976 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
977 const NSAPI &NS, Commit &commit) {
978 if (Msg->getNumArgs() != 1)
981 const Expr *Arg = Msg->getArg(0);
982 if (Arg->isTypeDependent())
985 ASTContext &Ctx = NS.getASTContext();
986 Selector Sel = Msg->getSelector();
987 Optional<NSAPI::NSNumberLiteralMethodKind>
988 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
991 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
993 const Expr *OrigArg = Arg->IgnoreImpCasts();
994 QualType FinalTy = Arg->getType();
995 QualType OrigTy = OrigArg->getType();
996 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
997 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
999 bool isTruncated = FinalTySize < OrigTySize;
1000 bool needsCast = false;
1002 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1003 switch (ICE->getCastKind()) {
1004 case CK_LValueToRValue:
1006 case CK_UserDefinedConversion:
1009 case CK_IntegralCast: {
1010 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1012 // Be more liberal with Integer/UnsignedInteger which are very commonly
1014 if ((MK == NSAPI::NSNumberWithInteger ||
1015 MK == NSAPI::NSNumberWithUnsignedInteger) &&
1017 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1019 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1020 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1028 case CK_PointerToBoolean:
1029 case CK_IntegralToBoolean:
1030 case CK_IntegralToFloating:
1031 case CK_FloatingToIntegral:
1032 case CK_FloatingToBoolean:
1033 case CK_FloatingCast:
1034 case CK_FloatingComplexToReal:
1035 case CK_FloatingComplexToBoolean:
1036 case CK_IntegralComplexToReal:
1037 case CK_IntegralComplexToBoolean:
1038 case CK_AtomicToNonAtomic:
1039 case CK_AddressSpaceConversion:
1045 case CK_LValueBitCast:
1046 case CK_BaseToDerived:
1047 case CK_DerivedToBase:
1048 case CK_UncheckedDerivedToBase:
1051 case CK_ArrayToPointerDecay:
1052 case CK_FunctionToPointerDecay:
1053 case CK_NullToPointer:
1054 case CK_NullToMemberPointer:
1055 case CK_BaseToDerivedMemberPointer:
1056 case CK_DerivedToBaseMemberPointer:
1057 case CK_MemberPointerToBoolean:
1058 case CK_ReinterpretMemberPointer:
1059 case CK_ConstructorConversion:
1060 case CK_IntegralToPointer:
1061 case CK_PointerToIntegral:
1063 case CK_VectorSplat:
1064 case CK_CPointerToObjCPointerCast:
1065 case CK_BlockPointerToObjCPointerCast:
1066 case CK_AnyPointerToBlockPointerCast:
1067 case CK_ObjCObjectLValueCast:
1068 case CK_FloatingRealToComplex:
1069 case CK_FloatingComplexCast:
1070 case CK_FloatingComplexToIntegralComplex:
1071 case CK_IntegralRealToComplex:
1072 case CK_IntegralComplexCast:
1073 case CK_IntegralComplexToFloatingComplex:
1074 case CK_ARCProduceObject:
1075 case CK_ARCConsumeObject:
1076 case CK_ARCReclaimReturnedObject:
1077 case CK_ARCExtendBlockObject:
1078 case CK_NonAtomicToAtomic:
1079 case CK_CopyAndAutoreleaseBlockObject:
1080 case CK_BuiltinFnToFnPtr:
1081 case CK_ZeroToOCLOpaqueType:
1082 case CK_IntToOCLSampler:
1085 case CK_BooleanToSignedIntegral:
1086 llvm_unreachable("OpenCL-specific cast in Objective-C?");
1088 case CK_FixedPointCast:
1089 case CK_FixedPointToBoolean:
1090 llvm_unreachable("Fixed point types are disabled for Objective-C");
1095 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1096 // FIXME: Use a custom category name to distinguish migration diagnostics.
1097 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1098 "converting to boxing syntax requires casting %0 to %1");
1099 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1100 << Msg->getSourceRange();
1104 SourceRange ArgRange = OrigArg->getSourceRange();
1105 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1107 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1108 commit.insertBefore(ArgRange.getBegin(), "@");
1110 commit.insertWrap("@(", ArgRange, ")");
1115 //===----------------------------------------------------------------------===//
1116 // rewriteToStringBoxedExpression.
1117 //===----------------------------------------------------------------------===//
1119 static bool doRewriteToUTF8StringBoxedExpressionHelper(
1120 const ObjCMessageExpr *Msg,
1121 const NSAPI &NS, Commit &commit) {
1122 const Expr *Arg = Msg->getArg(0);
1123 if (Arg->isTypeDependent())
1126 ASTContext &Ctx = NS.getASTContext();
1128 const Expr *OrigArg = Arg->IgnoreImpCasts();
1129 QualType OrigTy = OrigArg->getType();
1130 if (OrigTy->isArrayType())
1131 OrigTy = Ctx.getArrayDecayedType(OrigTy);
1133 if (const StringLiteral *
1134 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1135 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1136 commit.insert(StrE->getBeginLoc(), "@");
1140 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1141 QualType PointeeType = PT->getPointeeType();
1142 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1143 SourceRange ArgRange = OrigArg->getSourceRange();
1144 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1146 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1147 commit.insertBefore(ArgRange.getBegin(), "@");
1149 commit.insertWrap("@(", ArgRange, ")");
1158 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1159 const NSAPI &NS, Commit &commit) {
1160 Selector Sel = Msg->getSelector();
1162 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1163 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
1164 Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
1165 if (Msg->getNumArgs() != 1)
1167 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1170 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1171 if (Msg->getNumArgs() != 2)
1174 const Expr *encodingArg = Msg->getArg(1);
1175 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1176 NS.isNSASCIIStringEncodingConstant(encodingArg))
1177 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);