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/Edit/Commit.h"
16 #include "clang/Lex/Lexer.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/ExprObjC.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/NSAPI.h"
22 using namespace clang;
25 static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
26 IdentifierInfo *&ClassId,
27 const LangOptions &LangOpts) {
28 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
31 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
34 ClassId = Receiver->getIdentifier();
36 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
39 // 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.
41 if (LangOpts.ObjCAutoRefCount) {
42 if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
43 if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
44 Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
45 if (Rec->getMethodFamily() == OMF_alloc)
54 //===----------------------------------------------------------------------===//
55 // rewriteObjCRedundantCallWithLiteral.
56 //===----------------------------------------------------------------------===//
58 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
59 const NSAPI &NS, Commit &commit) {
60 IdentifierInfo *II = 0;
61 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
63 if (Msg->getNumArgs() != 1)
66 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
67 Selector Sel = Msg->getSelector();
69 if ((isa<ObjCStringLiteral>(Arg) &&
70 NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
71 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
74 (isa<ObjCArrayLiteral>(Arg) &&
75 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
76 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
79 (isa<ObjCDictionaryLiteral>(Arg) &&
80 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
81 (NS.getNSDictionarySelector(
82 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
85 commit.replaceWithInner(Msg->getSourceRange(),
86 Msg->getArg(0)->getSourceRange());
93 //===----------------------------------------------------------------------===//
94 // rewriteToObjCSubscriptSyntax.
95 //===----------------------------------------------------------------------===//
97 /// \brief Check for classes that accept 'objectForKey:' (or the other selectors
98 /// that the migrator handles) but return their instances as 'id', resulting
99 /// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
101 /// When checking if we can convert to subscripting syntax, check whether
102 /// the receiver is a result of a class method from a hardcoded list of
103 /// such classes. In such a case return the specific class as the interface
106 /// FIXME: Remove this when these classes start using 'instancetype'.
107 static const ObjCInterfaceDecl *
108 maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
109 const Expr *Receiver,
111 assert(IFace && Receiver);
113 // If the receiver has type 'id'...
114 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
117 const ObjCMessageExpr *
118 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
123 switch (InnerMsg->getReceiverKind()) {
124 case ObjCMessageExpr::Instance:
125 case ObjCMessageExpr::SuperInstance:
128 case ObjCMessageExpr::Class:
129 ClassRec = InnerMsg->getClassReceiver();
131 case ObjCMessageExpr::SuperClass:
132 ClassRec = InnerMsg->getSuperType();
136 if (ClassRec.isNull())
139 // ...and it is the result of a class message...
141 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
144 const ObjCInterfaceDecl *OID = ObjTy->getInterface();
146 // ...and the receiving class is NSMapTable or NSLocale, return that
147 // class as the receiving interface.
148 if (OID->getName() == "NSMapTable" ||
149 OID->getName() == "NSLocale")
155 static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
156 const ObjCMessageExpr *Msg,
158 Selector subscriptSel) {
159 const Expr *Rec = Msg->getInstanceReceiver();
162 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
164 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
165 if (!MD->isUnavailable())
171 static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
174 if (subscriptOperatorNeedsParens(Receiver)) {
175 SourceRange RecRange = Receiver->getSourceRange();
176 commit.insertWrap("(", RecRange, ")");
180 static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182 if (Msg->getNumArgs() != 1)
184 const Expr *Rec = Msg->getInstanceReceiver();
188 SourceRange MsgRange = Msg->getSourceRange();
189 SourceRange RecRange = Rec->getSourceRange();
190 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
192 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
193 ArgRange.getBegin()),
194 CharSourceRange::getTokenRange(RecRange));
195 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
197 commit.insertWrap("[", ArgRange, "]");
198 maybePutParensOnReceiver(Rec, commit);
202 static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
203 const ObjCMessageExpr *Msg,
206 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
207 NS.getObjectAtIndexedSubscriptSelector()))
209 return rewriteToSubscriptGetCommon(Msg, commit);
212 static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
213 const ObjCMessageExpr *Msg,
216 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
217 NS.getObjectForKeyedSubscriptSelector()))
219 return rewriteToSubscriptGetCommon(Msg, commit);
222 static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
223 const ObjCMessageExpr *Msg,
226 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
227 NS.getSetObjectAtIndexedSubscriptSelector()))
230 if (Msg->getNumArgs() != 2)
232 const Expr *Rec = Msg->getInstanceReceiver();
236 SourceRange MsgRange = Msg->getSourceRange();
237 SourceRange RecRange = Rec->getSourceRange();
238 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
239 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
241 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
242 Arg0Range.getBegin()),
243 CharSourceRange::getTokenRange(RecRange));
244 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
245 Arg1Range.getBegin()),
246 CharSourceRange::getTokenRange(Arg0Range));
247 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
249 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
250 Arg1Range.getBegin()),
252 maybePutParensOnReceiver(Rec, commit);
256 static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
257 const ObjCMessageExpr *Msg,
260 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
261 NS.getSetObjectForKeyedSubscriptSelector()))
264 if (Msg->getNumArgs() != 2)
266 const Expr *Rec = Msg->getInstanceReceiver();
270 SourceRange MsgRange = Msg->getSourceRange();
271 SourceRange RecRange = Rec->getSourceRange();
272 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
273 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
275 SourceLocation LocBeforeVal = Arg0Range.getBegin();
276 commit.insertBefore(LocBeforeVal, "] = ");
277 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
278 /*beforePreviousInsertions=*/true);
279 commit.insertBefore(LocBeforeVal, "[");
280 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
281 Arg0Range.getBegin()),
282 CharSourceRange::getTokenRange(RecRange));
283 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
285 maybePutParensOnReceiver(Rec, commit);
289 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
290 const NSAPI &NS, Commit &commit) {
291 if (!Msg || Msg->isImplicit() ||
292 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
294 const ObjCMethodDecl *Method = Msg->getMethodDecl();
298 const ObjCInterfaceDecl *
299 IFace = NS.getASTContext().getObjContainingInterface(
300 const_cast<ObjCMethodDecl *>(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 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
330 const NSAPI &NS, Commit &commit);
331 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
332 const NSAPI &NS, Commit &commit);
333 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
334 const NSAPI &NS, Commit &commit);
335 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
336 const NSAPI &NS, Commit &commit);
338 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
339 const NSAPI &NS, Commit &commit) {
340 IdentifierInfo *II = 0;
341 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
344 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
345 return rewriteToArrayLiteral(Msg, NS, commit);
346 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
347 return rewriteToDictionaryLiteral(Msg, NS, commit);
348 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
349 return rewriteToNumberLiteral(Msg, NS, commit);
350 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
351 return rewriteToStringBoxedExpression(Msg, NS, commit);
356 //===----------------------------------------------------------------------===//
357 // rewriteToArrayLiteral.
358 //===----------------------------------------------------------------------===//
360 /// \brief Adds an explicit cast to 'id' if the type is not objc object.
361 static void objectifyExpr(const Expr *E, Commit &commit);
363 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
364 const NSAPI &NS, Commit &commit) {
365 Selector Sel = Msg->getSelector();
366 SourceRange MsgRange = Msg->getSourceRange();
368 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
369 if (Msg->getNumArgs() != 0)
371 commit.replace(MsgRange, "@[]");
375 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
376 if (Msg->getNumArgs() != 1)
378 objectifyExpr(Msg->getArg(0), commit);
379 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
380 commit.replaceWithInner(MsgRange, ArgRange);
381 commit.insertWrap("@[", ArgRange, "]");
385 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
386 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
387 if (Msg->getNumArgs() == 0)
389 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
390 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
393 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
394 objectifyExpr(Msg->getArg(i), commit);
396 if (Msg->getNumArgs() == 1) {
397 commit.replace(MsgRange, "@[]");
400 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
401 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
402 commit.replaceWithInner(MsgRange, ArgRange);
403 commit.insertWrap("@[", ArgRange, "]");
410 //===----------------------------------------------------------------------===//
411 // rewriteToDictionaryLiteral.
412 //===----------------------------------------------------------------------===//
414 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
415 const NSAPI &NS, Commit &commit) {
416 Selector Sel = Msg->getSelector();
417 SourceRange MsgRange = Msg->getSourceRange();
419 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
420 if (Msg->getNumArgs() != 0)
422 commit.replace(MsgRange, "@{}");
426 if (Sel == NS.getNSDictionarySelector(
427 NSAPI::NSDict_dictionaryWithObjectForKey)) {
428 if (Msg->getNumArgs() != 2)
431 objectifyExpr(Msg->getArg(0), commit);
432 objectifyExpr(Msg->getArg(1), commit);
434 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
435 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
436 // Insert key before the value.
437 commit.insertBefore(ValRange.getBegin(), ": ");
438 commit.insertFromRange(ValRange.getBegin(),
439 CharSourceRange::getTokenRange(KeyRange),
440 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
441 commit.insertBefore(ValRange.getBegin(), "@{");
442 commit.insertAfterToken(ValRange.getEnd(), "}");
443 commit.replaceWithInner(MsgRange, ValRange);
447 if (Sel == NS.getNSDictionarySelector(
448 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
449 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
450 if (Msg->getNumArgs() % 2 != 1)
452 unsigned SentinelIdx = Msg->getNumArgs() - 1;
453 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
454 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
457 if (Msg->getNumArgs() == 1) {
458 commit.replace(MsgRange, "@{}");
462 for (unsigned i = 0; i < SentinelIdx; i += 2) {
463 objectifyExpr(Msg->getArg(i), commit);
464 objectifyExpr(Msg->getArg(i+1), commit);
466 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
467 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
468 // Insert value after key.
469 commit.insertAfterToken(KeyRange.getEnd(), ": ");
470 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
471 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
472 KeyRange.getBegin()));
474 // Range of arguments up until and including the last key.
475 // The sentinel and first value are cut off, the value will move after the
477 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
478 Msg->getArg(SentinelIdx-1)->getLocEnd());
479 commit.insertWrap("@{", ArgRange, "}");
480 commit.replaceWithInner(MsgRange, ArgRange);
487 //===----------------------------------------------------------------------===//
488 // rewriteToNumberLiteral.
489 //===----------------------------------------------------------------------===//
491 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
492 const CharacterLiteral *Arg,
493 const NSAPI &NS, Commit &commit) {
494 if (Arg->getKind() != CharacterLiteral::Ascii)
496 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
497 Msg->getSelector())) {
498 SourceRange ArgRange = Arg->getSourceRange();
499 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
500 commit.insert(ArgRange.getBegin(), "@");
504 return rewriteToNumericBoxedExpression(Msg, NS, commit);
507 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
509 const NSAPI &NS, Commit &commit) {
510 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
511 Msg->getSelector())) {
512 SourceRange ArgRange = Arg->getSourceRange();
513 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
514 commit.insert(ArgRange.getBegin(), "@");
518 return rewriteToNumericBoxedExpression(Msg, NS, commit);
525 StringRef U, F, L, LL;
526 CharSourceRange WithoutSuffRange;
531 static bool getLiteralInfo(SourceRange literalRange,
532 bool isFloat, bool isIntZero,
533 ASTContext &Ctx, LiteralInfo &Info) {
534 if (literalRange.getBegin().isMacroID() ||
535 literalRange.getEnd().isMacroID())
537 StringRef text = Lexer::getSourceText(
538 CharSourceRange::getTokenRange(literalRange),
539 Ctx.getSourceManager(), Ctx.getLangOpts());
543 llvm::Optional<bool> UpperU, UpperL;
547 static bool has(StringRef suff, StringRef &text) {
548 if (text.endswith(suff)) {
549 text = text.substr(0, text.size()-suff.size());
557 if (Suff::has("u", text)) {
559 } else if (Suff::has("U", text)) {
561 } else if (Suff::has("ll", text)) {
563 } else if (Suff::has("LL", text)) {
565 } else if (Suff::has("l", text)) {
567 } else if (Suff::has("L", text)) {
569 } else if (isFloat && Suff::has("f", text)) {
571 } else if (isFloat && Suff::has("F", text)) {
577 if (!UpperU.hasValue() && !UpperL.hasValue())
578 UpperU = UpperL = true;
579 else if (UpperU.hasValue() && !UpperL.hasValue())
581 else if (UpperL.hasValue() && !UpperU.hasValue())
584 Info.U = *UpperU ? "U" : "u";
585 Info.L = *UpperL ? "L" : "l";
586 Info.LL = *UpperL ? "LL" : "ll";
587 Info.F = UpperF ? "F" : "f";
589 Info.Hex = Info.Octal = false;
590 if (text.startswith("0x"))
592 else if (!isFloat && !isIntZero && text.startswith("0"))
595 SourceLocation B = literalRange.getBegin();
596 Info.WithoutSuffRange =
597 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
601 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
602 const NSAPI &NS, Commit &commit) {
603 if (Msg->getNumArgs() != 1)
606 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
607 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
608 return rewriteToCharLiteral(Msg, CharE, NS, commit);
609 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
610 return rewriteToBoolLiteral(Msg, BE, NS, commit);
611 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
612 return rewriteToBoolLiteral(Msg, BE, NS, commit);
614 const Expr *literalE = Arg;
615 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
616 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
617 literalE = UOE->getSubExpr();
620 // Only integer and floating literals, otherwise try to rewrite to boxed
622 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
623 return rewriteToNumericBoxedExpression(Msg, NS, commit);
625 ASTContext &Ctx = NS.getASTContext();
626 Selector Sel = Msg->getSelector();
627 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
628 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
631 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
633 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
634 bool CallIsFloating = false, CallIsDouble = false;
637 // We cannot have these calls with int/float literals.
638 case NSAPI::NSNumberWithChar:
639 case NSAPI::NSNumberWithUnsignedChar:
640 case NSAPI::NSNumberWithShort:
641 case NSAPI::NSNumberWithUnsignedShort:
642 case NSAPI::NSNumberWithBool:
643 return rewriteToNumericBoxedExpression(Msg, NS, commit);
645 case NSAPI::NSNumberWithUnsignedInt:
646 case NSAPI::NSNumberWithUnsignedInteger:
647 CallIsUnsigned = true;
648 case NSAPI::NSNumberWithInt:
649 case NSAPI::NSNumberWithInteger:
652 case NSAPI::NSNumberWithUnsignedLong:
653 CallIsUnsigned = true;
654 case NSAPI::NSNumberWithLong:
658 case NSAPI::NSNumberWithUnsignedLongLong:
659 CallIsUnsigned = true;
660 case NSAPI::NSNumberWithLongLong:
661 CallIsLongLong = true;
664 case NSAPI::NSNumberWithDouble:
666 case NSAPI::NSNumberWithFloat:
667 CallIsFloating = true;
671 SourceRange ArgRange = Arg->getSourceRange();
672 QualType ArgTy = Arg->getType();
673 QualType CallTy = Msg->getArg(0)->getType();
675 // Check for the easy case, the literal maps directly to the call.
676 if (Ctx.hasSameType(ArgTy, CallTy)) {
677 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
678 commit.insert(ArgRange.getBegin(), "@");
682 // We will need to modify the literal suffix to get the same type as the call.
683 // Try with boxed expression if it came from a macro.
684 if (ArgRange.getBegin().isMacroID())
685 return rewriteToNumericBoxedExpression(Msg, NS, commit);
687 bool LitIsFloat = ArgTy->isFloatingType();
688 // For a float passed to integer call, don't try rewriting to objc literal.
689 // It is difficult and a very uncommon case anyway.
690 // But try with boxed expression.
691 if (LitIsFloat && !CallIsFloating)
692 return rewriteToNumericBoxedExpression(Msg, NS, commit);
694 // Try to modify the literal make it the same type as the method call.
695 // -Modify the suffix, and/or
696 // -Change integer to float
699 bool isIntZero = false;
700 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
701 isIntZero = !IntE->getValue().getBoolValue();
702 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
703 return rewriteToNumericBoxedExpression(Msg, NS, commit);
705 // Not easy to do int -> float with hex/octal and uncommon anyway.
706 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
707 return rewriteToNumericBoxedExpression(Msg, NS, commit);
709 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
710 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
712 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
713 LitInfo.WithoutSuffRange);
714 commit.insert(LitB, "@");
716 if (!LitIsFloat && CallIsFloating)
717 commit.insert(LitE, ".0");
719 if (CallIsFloating) {
721 commit.insert(LitE, LitInfo.F);
724 commit.insert(LitE, LitInfo.U);
727 commit.insert(LitE, LitInfo.L);
728 else if (CallIsLongLong)
729 commit.insert(LitE, LitInfo.LL);
734 // FIXME: Make determination of operator precedence more general and
735 // make it broadly available.
736 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
737 const Expr* Expr = FullExpr->IgnoreImpCasts();
738 if (isa<ArraySubscriptExpr>(Expr) ||
739 isa<CallExpr>(Expr) ||
740 isa<DeclRefExpr>(Expr) ||
741 isa<CXXNamedCastExpr>(Expr) ||
742 isa<CXXConstructExpr>(Expr) ||
743 isa<CXXThisExpr>(Expr) ||
744 isa<CXXTypeidExpr>(Expr) ||
745 isa<CXXUnresolvedConstructExpr>(Expr) ||
746 isa<ObjCMessageExpr>(Expr) ||
747 isa<ObjCPropertyRefExpr>(Expr) ||
748 isa<ObjCProtocolExpr>(Expr) ||
749 isa<MemberExpr>(Expr) ||
750 isa<ObjCIvarRefExpr>(Expr) ||
751 isa<ParenExpr>(FullExpr) ||
752 isa<ParenListExpr>(Expr) ||
753 isa<SizeOfPackExpr>(Expr))
758 static bool castOperatorNeedsParens(const Expr *FullExpr) {
759 const Expr* Expr = FullExpr->IgnoreImpCasts();
760 if (isa<ArraySubscriptExpr>(Expr) ||
761 isa<CallExpr>(Expr) ||
762 isa<DeclRefExpr>(Expr) ||
763 isa<CastExpr>(Expr) ||
764 isa<CXXNewExpr>(Expr) ||
765 isa<CXXConstructExpr>(Expr) ||
766 isa<CXXDeleteExpr>(Expr) ||
767 isa<CXXNoexceptExpr>(Expr) ||
768 isa<CXXPseudoDestructorExpr>(Expr) ||
769 isa<CXXScalarValueInitExpr>(Expr) ||
770 isa<CXXThisExpr>(Expr) ||
771 isa<CXXTypeidExpr>(Expr) ||
772 isa<CXXUnresolvedConstructExpr>(Expr) ||
773 isa<ObjCMessageExpr>(Expr) ||
774 isa<ObjCPropertyRefExpr>(Expr) ||
775 isa<ObjCProtocolExpr>(Expr) ||
776 isa<MemberExpr>(Expr) ||
777 isa<ObjCIvarRefExpr>(Expr) ||
778 isa<ParenExpr>(FullExpr) ||
779 isa<ParenListExpr>(Expr) ||
780 isa<SizeOfPackExpr>(Expr) ||
781 isa<UnaryOperator>(Expr))
787 static void objectifyExpr(const Expr *E, Commit &commit) {
790 QualType T = E->getType();
791 if (T->isObjCObjectPointerType()) {
792 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
793 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
798 } else if (!T->isPointerType()) {
802 SourceRange Range = E->getSourceRange();
803 if (castOperatorNeedsParens(E))
804 commit.insertWrap("(", Range, ")");
805 commit.insertBefore(Range.getBegin(), "(id)");
808 //===----------------------------------------------------------------------===//
809 // rewriteToNumericBoxedExpression.
810 //===----------------------------------------------------------------------===//
812 static bool isEnumConstant(const Expr *E) {
813 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
814 if (const ValueDecl *VD = DRE->getDecl())
815 return isa<EnumConstantDecl>(VD);
820 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
821 const NSAPI &NS, Commit &commit) {
822 if (Msg->getNumArgs() != 1)
825 const Expr *Arg = Msg->getArg(0);
826 if (Arg->isTypeDependent())
829 ASTContext &Ctx = NS.getASTContext();
830 Selector Sel = Msg->getSelector();
831 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
832 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
835 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
837 const Expr *OrigArg = Arg->IgnoreImpCasts();
838 QualType FinalTy = Arg->getType();
839 QualType OrigTy = OrigArg->getType();
840 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
841 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
843 bool isTruncated = FinalTySize < OrigTySize;
844 bool needsCast = false;
846 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
847 switch (ICE->getCastKind()) {
848 case CK_LValueToRValue:
850 case CK_UserDefinedConversion:
853 case CK_IntegralCast: {
854 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
856 // Be more liberal with Integer/UnsignedInteger which are very commonly
858 if ((MK == NSAPI::NSNumberWithInteger ||
859 MK == NSAPI::NSNumberWithUnsignedInteger) &&
861 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
863 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
864 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
872 case CK_PointerToBoolean:
873 case CK_IntegralToBoolean:
874 case CK_IntegralToFloating:
875 case CK_FloatingToIntegral:
876 case CK_FloatingToBoolean:
877 case CK_FloatingCast:
878 case CK_FloatingComplexToReal:
879 case CK_FloatingComplexToBoolean:
880 case CK_IntegralComplexToReal:
881 case CK_IntegralComplexToBoolean:
882 case CK_AtomicToNonAtomic:
888 case CK_LValueBitCast:
889 case CK_BaseToDerived:
890 case CK_DerivedToBase:
891 case CK_UncheckedDerivedToBase:
894 case CK_ArrayToPointerDecay:
895 case CK_FunctionToPointerDecay:
896 case CK_NullToPointer:
897 case CK_NullToMemberPointer:
898 case CK_BaseToDerivedMemberPointer:
899 case CK_DerivedToBaseMemberPointer:
900 case CK_MemberPointerToBoolean:
901 case CK_ReinterpretMemberPointer:
902 case CK_ConstructorConversion:
903 case CK_IntegralToPointer:
904 case CK_PointerToIntegral:
907 case CK_CPointerToObjCPointerCast:
908 case CK_BlockPointerToObjCPointerCast:
909 case CK_AnyPointerToBlockPointerCast:
910 case CK_ObjCObjectLValueCast:
911 case CK_FloatingRealToComplex:
912 case CK_FloatingComplexCast:
913 case CK_FloatingComplexToIntegralComplex:
914 case CK_IntegralRealToComplex:
915 case CK_IntegralComplexCast:
916 case CK_IntegralComplexToFloatingComplex:
917 case CK_ARCProduceObject:
918 case CK_ARCConsumeObject:
919 case CK_ARCReclaimReturnedObject:
920 case CK_ARCExtendBlockObject:
921 case CK_NonAtomicToAtomic:
922 case CK_CopyAndAutoreleaseBlockObject:
923 case CK_BuiltinFnToFnPtr:
929 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
930 // FIXME: Use a custom category name to distinguish migration diagnostics.
931 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
932 "converting to boxing syntax requires casting %0 to %1");
933 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
934 << Msg->getSourceRange();
938 SourceRange ArgRange = OrigArg->getSourceRange();
939 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
941 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
942 commit.insertBefore(ArgRange.getBegin(), "@");
944 commit.insertWrap("@(", ArgRange, ")");
949 //===----------------------------------------------------------------------===//
950 // rewriteToStringBoxedExpression.
951 //===----------------------------------------------------------------------===//
953 static bool doRewriteToUTF8StringBoxedExpressionHelper(
954 const ObjCMessageExpr *Msg,
955 const NSAPI &NS, Commit &commit) {
956 const Expr *Arg = Msg->getArg(0);
957 if (Arg->isTypeDependent())
960 ASTContext &Ctx = NS.getASTContext();
962 const Expr *OrigArg = Arg->IgnoreImpCasts();
963 QualType OrigTy = OrigArg->getType();
964 if (OrigTy->isArrayType())
965 OrigTy = Ctx.getArrayDecayedType(OrigTy);
967 if (const StringLiteral *
968 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
969 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
970 commit.insert(StrE->getLocStart(), "@");
974 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
975 QualType PointeeType = PT->getPointeeType();
976 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
977 SourceRange ArgRange = OrigArg->getSourceRange();
978 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
980 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
981 commit.insertBefore(ArgRange.getBegin(), "@");
983 commit.insertWrap("@(", ArgRange, ")");
992 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
993 const NSAPI &NS, Commit &commit) {
994 Selector Sel = Msg->getSelector();
996 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
997 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
998 if (Msg->getNumArgs() != 1)
1000 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1003 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1004 if (Msg->getNumArgs() != 2)
1007 const Expr *encodingArg = Msg->getArg(1);
1008 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1009 NS.isNSASCIIStringEncodingConstant(encodingArg))
1010 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);