1 //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Rewrites legacy method calls to modern syntax.
11 //===----------------------------------------------------------------------===//
13 #include "clang/Edit/Rewriters.h"
14 #include "clang/AST/ASTContext.h"
15 #include "clang/AST/ExprCXX.h"
16 #include "clang/AST/ExprObjC.h"
17 #include "clang/AST/NSAPI.h"
18 #include "clang/AST/ParentMap.h"
19 #include "clang/Edit/Commit.h"
20 #include "clang/Lex/Lexer.h"
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 std::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
&& !UpperL
)
730 UpperU
= UpperL
= true;
731 else if (UpperU
&& !UpperL
)
733 else if (UpperL
&& !UpperU
)
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 std::optional
<NSAPI::NSNumberLiteralMethodKind
> MKOpt
=
780 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 std::optional
<NSAPI::NSNumberLiteralMethodKind
> MKOpt
=
988 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_LValueToRValueBitCast
:
1047 case CK_BaseToDerived
:
1048 case CK_DerivedToBase
:
1049 case CK_UncheckedDerivedToBase
:
1052 case CK_ArrayToPointerDecay
:
1053 case CK_FunctionToPointerDecay
:
1054 case CK_NullToPointer
:
1055 case CK_NullToMemberPointer
:
1056 case CK_BaseToDerivedMemberPointer
:
1057 case CK_DerivedToBaseMemberPointer
:
1058 case CK_MemberPointerToBoolean
:
1059 case CK_ReinterpretMemberPointer
:
1060 case CK_ConstructorConversion
:
1061 case CK_IntegralToPointer
:
1062 case CK_PointerToIntegral
:
1064 case CK_VectorSplat
:
1065 case CK_CPointerToObjCPointerCast
:
1066 case CK_BlockPointerToObjCPointerCast
:
1067 case CK_AnyPointerToBlockPointerCast
:
1068 case CK_ObjCObjectLValueCast
:
1069 case CK_FloatingRealToComplex
:
1070 case CK_FloatingComplexCast
:
1071 case CK_FloatingComplexToIntegralComplex
:
1072 case CK_IntegralRealToComplex
:
1073 case CK_IntegralComplexCast
:
1074 case CK_IntegralComplexToFloatingComplex
:
1075 case CK_ARCProduceObject
:
1076 case CK_ARCConsumeObject
:
1077 case CK_ARCReclaimReturnedObject
:
1078 case CK_ARCExtendBlockObject
:
1079 case CK_NonAtomicToAtomic
:
1080 case CK_CopyAndAutoreleaseBlockObject
:
1081 case CK_BuiltinFnToFnPtr
:
1082 case CK_ZeroToOCLOpaqueType
:
1083 case CK_IntToOCLSampler
:
1087 case CK_BooleanToSignedIntegral
:
1088 llvm_unreachable("OpenCL-specific cast in Objective-C?");
1090 case CK_FloatingToFixedPoint
:
1091 case CK_FixedPointToFloating
:
1092 case CK_FixedPointCast
:
1093 case CK_FixedPointToBoolean
:
1094 case CK_FixedPointToIntegral
:
1095 case CK_IntegralToFixedPoint
:
1096 llvm_unreachable("Fixed point types are disabled for Objective-C");
1101 DiagnosticsEngine
&Diags
= Ctx
.getDiagnostics();
1102 // FIXME: Use a custom category name to distinguish migration diagnostics.
1103 unsigned diagID
= Diags
.getCustomDiagID(DiagnosticsEngine::Warning
,
1104 "converting to boxing syntax requires casting %0 to %1");
1105 Diags
.Report(Msg
->getExprLoc(), diagID
) << OrigTy
<< FinalTy
1106 << Msg
->getSourceRange();
1110 SourceRange ArgRange
= OrigArg
->getSourceRange();
1111 commit
.replaceWithInner(Msg
->getSourceRange(), ArgRange
);
1113 if (isa
<ParenExpr
>(OrigArg
) || isa
<IntegerLiteral
>(OrigArg
))
1114 commit
.insertBefore(ArgRange
.getBegin(), "@");
1116 commit
.insertWrap("@(", ArgRange
, ")");
1121 //===----------------------------------------------------------------------===//
1122 // rewriteToStringBoxedExpression.
1123 //===----------------------------------------------------------------------===//
1125 static bool doRewriteToUTF8StringBoxedExpressionHelper(
1126 const ObjCMessageExpr
*Msg
,
1127 const NSAPI
&NS
, Commit
&commit
) {
1128 const Expr
*Arg
= Msg
->getArg(0);
1129 if (Arg
->isTypeDependent())
1132 ASTContext
&Ctx
= NS
.getASTContext();
1134 const Expr
*OrigArg
= Arg
->IgnoreImpCasts();
1135 QualType OrigTy
= OrigArg
->getType();
1136 if (OrigTy
->isArrayType())
1137 OrigTy
= Ctx
.getArrayDecayedType(OrigTy
);
1139 if (const StringLiteral
*
1140 StrE
= dyn_cast
<StringLiteral
>(OrigArg
->IgnoreParens())) {
1141 commit
.replaceWithInner(Msg
->getSourceRange(), StrE
->getSourceRange());
1142 commit
.insert(StrE
->getBeginLoc(), "@");
1146 if (const PointerType
*PT
= OrigTy
->getAs
<PointerType
>()) {
1147 QualType PointeeType
= PT
->getPointeeType();
1148 if (Ctx
.hasSameUnqualifiedType(PointeeType
, Ctx
.CharTy
)) {
1149 SourceRange ArgRange
= OrigArg
->getSourceRange();
1150 commit
.replaceWithInner(Msg
->getSourceRange(), ArgRange
);
1152 if (isa
<ParenExpr
>(OrigArg
) || isa
<IntegerLiteral
>(OrigArg
))
1153 commit
.insertBefore(ArgRange
.getBegin(), "@");
1155 commit
.insertWrap("@(", ArgRange
, ")");
1164 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr
*Msg
,
1165 const NSAPI
&NS
, Commit
&commit
) {
1166 Selector Sel
= Msg
->getSelector();
1168 if (Sel
== NS
.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String
) ||
1169 Sel
== NS
.getNSStringSelector(NSAPI::NSStr_stringWithCString
) ||
1170 Sel
== NS
.getNSStringSelector(NSAPI::NSStr_initWithUTF8String
)) {
1171 if (Msg
->getNumArgs() != 1)
1173 return doRewriteToUTF8StringBoxedExpressionHelper(Msg
, NS
, commit
);
1176 if (Sel
== NS
.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding
)) {
1177 if (Msg
->getNumArgs() != 2)
1180 const Expr
*encodingArg
= Msg
->getArg(1);
1181 if (NS
.isNSUTF8StringEncodingConstant(encodingArg
) ||
1182 NS
.isNSASCIIStringEncodingConstant(encodingArg
))
1183 return doRewriteToUTF8StringBoxedExpressionHelper(Msg
, NS
, commit
);