xref: /freebsd/contrib/llvm-project/clang/lib/Edit/RewriteObjCFoundationAPI.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Rewrites legacy method calls to modern syntax.
10 //
11 //===----------------------------------------------------------------------===//
12 
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"
21 #include <optional>
22 
23 using namespace clang;
24 using namespace edit;
25 
checkForLiteralCreation(const ObjCMessageExpr * Msg,IdentifierInfo * & ClassId,const LangOptions & LangOpts)26 static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
27                                     IdentifierInfo *&ClassId,
28                                     const LangOptions &LangOpts) {
29   if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
30     return false;
31 
32   const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
33   if (!Receiver)
34     return false;
35   ClassId = Receiver->getIdentifier();
36 
37   if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
38     return true;
39 
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)
47           return true;
48       }
49     }
50   }
51 
52   return false;
53 }
54 
55 //===----------------------------------------------------------------------===//
56 // rewriteObjCRedundantCallWithLiteral.
57 //===----------------------------------------------------------------------===//
58 
rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)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()))
63     return false;
64   if (Msg->getNumArgs() != 1)
65     return false;
66 
67   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
68   Selector Sel = Msg->getSelector();
69 
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))   ||
74 
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))     ||
79 
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))) {
85 
86     commit.replaceWithInner(Msg->getSourceRange(),
87                            Msg->getArg(0)->getSourceRange());
88     return true;
89   }
90 
91   return false;
92 }
93 
94 //===----------------------------------------------------------------------===//
95 // rewriteToObjCSubscriptSyntax.
96 //===----------------------------------------------------------------------===//
97 
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.
101 ///
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
105 /// of the receiver.
106 ///
107 /// FIXME: Remove this when these classes start using 'instancetype'.
108 static const ObjCInterfaceDecl *
maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl * IFace,const Expr * Receiver,ASTContext & Ctx)109 maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
110                                          const Expr *Receiver,
111                                          ASTContext &Ctx) {
112   assert(IFace && Receiver);
113 
114   // If the receiver has type 'id'...
115   if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
116     return IFace;
117 
118   const ObjCMessageExpr *
119     InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
120   if (!InnerMsg)
121     return IFace;
122 
123   QualType ClassRec;
124   switch (InnerMsg->getReceiverKind()) {
125   case ObjCMessageExpr::Instance:
126   case ObjCMessageExpr::SuperInstance:
127     return IFace;
128 
129   case ObjCMessageExpr::Class:
130     ClassRec = InnerMsg->getClassReceiver();
131     break;
132   case ObjCMessageExpr::SuperClass:
133     ClassRec = InnerMsg->getSuperType();
134     break;
135   }
136 
137   if (ClassRec.isNull())
138     return IFace;
139 
140   // ...and it is the result of a class message...
141 
142   const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
143   if (!ObjTy)
144     return IFace;
145   const ObjCInterfaceDecl *OID = ObjTy->getInterface();
146 
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")
151     return OID;
152 
153   return IFace;
154 }
155 
canRewriteToSubscriptSyntax(const ObjCInterfaceDecl * & IFace,const ObjCMessageExpr * Msg,ASTContext & Ctx,Selector subscriptSel)156 static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
157                                         const ObjCMessageExpr *Msg,
158                                         ASTContext &Ctx,
159                                         Selector subscriptSel) {
160   const Expr *Rec = Msg->getInstanceReceiver();
161   if (!Rec)
162     return false;
163   IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
164 
165   if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
166     if (!MD->isUnavailable())
167       return true;
168   }
169   return false;
170 }
171 
172 static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173 
maybePutParensOnReceiver(const Expr * Receiver,Commit & commit)174 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
175   if (subscriptOperatorNeedsParens(Receiver)) {
176     SourceRange RecRange = Receiver->getSourceRange();
177     commit.insertWrap("(", RecRange, ")");
178   }
179 }
180 
rewriteToSubscriptGetCommon(const ObjCMessageExpr * Msg,Commit & commit)181 static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182                                         Commit &commit) {
183   if (Msg->getNumArgs() != 1)
184     return false;
185   const Expr *Rec = Msg->getInstanceReceiver();
186   if (!Rec)
187     return false;
188 
189   SourceRange MsgRange = Msg->getSourceRange();
190   SourceRange RecRange = Rec->getSourceRange();
191   SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
192 
193   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
194                                                        ArgRange.getBegin()),
195                          CharSourceRange::getTokenRange(RecRange));
196   commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
197                          ArgRange);
198   commit.insertWrap("[", ArgRange, "]");
199   maybePutParensOnReceiver(Rec, commit);
200   return true;
201 }
202 
rewriteToArraySubscriptGet(const ObjCInterfaceDecl * IFace,const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)203 static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204                                        const ObjCMessageExpr *Msg,
205                                        const NSAPI &NS,
206                                        Commit &commit) {
207   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
208                                    NS.getObjectAtIndexedSubscriptSelector()))
209     return false;
210   return rewriteToSubscriptGetCommon(Msg, commit);
211 }
212 
rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl * IFace,const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)213 static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
214                                             const ObjCMessageExpr *Msg,
215                                             const NSAPI &NS,
216                                             Commit &commit) {
217   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
218                                   NS.getObjectForKeyedSubscriptSelector()))
219     return false;
220   return rewriteToSubscriptGetCommon(Msg, commit);
221 }
222 
rewriteToArraySubscriptSet(const ObjCInterfaceDecl * IFace,const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)223 static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
224                                        const ObjCMessageExpr *Msg,
225                                        const NSAPI &NS,
226                                        Commit &commit) {
227   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
228                                    NS.getSetObjectAtIndexedSubscriptSelector()))
229     return false;
230 
231   if (Msg->getNumArgs() != 2)
232     return false;
233   const Expr *Rec = Msg->getInstanceReceiver();
234   if (!Rec)
235     return false;
236 
237   SourceRange MsgRange = Msg->getSourceRange();
238   SourceRange RecRange = Rec->getSourceRange();
239   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
240   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
241 
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()),
249                          Arg1Range);
250   commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
251                                                        Arg1Range.getBegin()),
252                     "] = ");
253   maybePutParensOnReceiver(Rec, commit);
254   return true;
255 }
256 
rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl * IFace,const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)257 static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258                                             const ObjCMessageExpr *Msg,
259                                             const NSAPI &NS,
260                                             Commit &commit) {
261   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
262                                    NS.getSetObjectForKeyedSubscriptSelector()))
263     return false;
264 
265   if (Msg->getNumArgs() != 2)
266     return false;
267   const Expr *Rec = Msg->getInstanceReceiver();
268   if (!Rec)
269     return false;
270 
271   SourceRange MsgRange = Msg->getSourceRange();
272   SourceRange RecRange = Rec->getSourceRange();
273   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
274   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
275 
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()),
285                          Arg0Range);
286   maybePutParensOnReceiver(Rec, commit);
287   return true;
288 }
289 
rewriteToObjCSubscriptSyntax(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)290 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
291                                         const NSAPI &NS, Commit &commit) {
292   if (!Msg || Msg->isImplicit() ||
293       Msg->getReceiverKind() != ObjCMessageExpr::Instance)
294     return false;
295   const ObjCMethodDecl *Method = Msg->getMethodDecl();
296   if (!Method)
297     return false;
298 
299   const ObjCInterfaceDecl *IFace =
300       NS.getASTContext().getObjContainingInterface(Method);
301   if (!IFace)
302     return false;
303   Selector Sel = Msg->getSelector();
304 
305   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
306     return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
307 
308   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
309     return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
310 
311   if (Msg->getNumArgs() != 2)
312     return false;
313 
314   if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
315     return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
316 
317   if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
318     return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
319 
320   return false;
321 }
322 
323 //===----------------------------------------------------------------------===//
324 // rewriteToObjCLiteralSyntax.
325 //===----------------------------------------------------------------------===//
326 
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);
338 
rewriteToObjCLiteralSyntax(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit,const ParentMap * PMap)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()))
344     return false;
345 
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);
354 
355   return false;
356 }
357 
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.
361 /// \code
362 ///   [NSDictionary dictionaryWithObjects:
363 ///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
364 ///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
365 /// \endcode
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,
369                                                  const NSAPI &NS);
370 
371 //===----------------------------------------------------------------------===//
372 // rewriteToArrayLiteral.
373 //===----------------------------------------------------------------------===//
374 
375 /// Adds an explicit cast to 'id' if the type is not objc object.
376 static void objectifyExpr(const Expr *E, Commit &commit);
377 
rewriteToArrayLiteral(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit,const ParentMap * PMap)378 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
379                                   const NSAPI &NS, Commit &commit,
380                                   const ParentMap *PMap) {
381   if (PMap) {
382     const ObjCMessageExpr *ParentMsg =
383         dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
384     if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
385       return false;
386   }
387 
388   Selector Sel = Msg->getSelector();
389   SourceRange MsgRange = Msg->getSourceRange();
390 
391   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
392     if (Msg->getNumArgs() != 0)
393       return false;
394     commit.replace(MsgRange, "@[]");
395     return true;
396   }
397 
398   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
399     if (Msg->getNumArgs() != 1)
400       return false;
401     objectifyExpr(Msg->getArg(0), commit);
402     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
403     commit.replaceWithInner(MsgRange, ArgRange);
404     commit.insertWrap("@[", ArgRange, "]");
405     return true;
406   }
407 
408   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
409       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
410     if (Msg->getNumArgs() == 0)
411       return false;
412     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
413     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
414       return false;
415 
416     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
417       objectifyExpr(Msg->getArg(i), commit);
418 
419     if (Msg->getNumArgs() == 1) {
420       commit.replace(MsgRange, "@[]");
421       return true;
422     }
423     SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
424                          Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
425     commit.replaceWithInner(MsgRange, ArgRange);
426     commit.insertWrap("@[", ArgRange, "]");
427     return true;
428   }
429 
430   return false;
431 }
432 
433 //===----------------------------------------------------------------------===//
434 // rewriteToDictionaryLiteral.
435 //===----------------------------------------------------------------------===//
436 
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.
getNSArrayObjects(const Expr * E,const NSAPI & NS,SmallVectorImpl<const Expr * > & Objs)440 static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
441                               SmallVectorImpl<const Expr *> &Objs) {
442   if (!E)
443     return false;
444 
445   E = E->IgnoreParenCasts();
446   if (!E)
447     return false;
448 
449   if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
450     IdentifierInfo *Cls = nullptr;
451     if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
452       return false;
453 
454     if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
455       return false;
456 
457     Selector Sel = Msg->getSelector();
458     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
459       return true; // empty array.
460 
461     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
462       if (Msg->getNumArgs() != 1)
463         return false;
464       Objs.push_back(Msg->getArg(0));
465       return true;
466     }
467 
468     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
469         Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
470       if (Msg->getNumArgs() == 0)
471         return false;
472       const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
473       if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
474         return false;
475 
476       for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
477         Objs.push_back(Msg->getArg(i));
478       return true;
479     }
480 
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));
484     return true;
485   }
486 
487   return false;
488 }
489 
rewriteToDictionaryLiteral(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)490 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
491                                        const NSAPI &NS, Commit &commit) {
492   Selector Sel = Msg->getSelector();
493   SourceRange MsgRange = Msg->getSourceRange();
494 
495   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
496     if (Msg->getNumArgs() != 0)
497       return false;
498     commit.replace(MsgRange, "@{}");
499     return true;
500   }
501 
502   if (Sel == NS.getNSDictionarySelector(
503                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
504     if (Msg->getNumArgs() != 2)
505       return false;
506 
507     objectifyExpr(Msg->getArg(0), commit);
508     objectifyExpr(Msg->getArg(1), commit);
509 
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);
520     return true;
521   }
522 
523   if (Sel == NS.getNSDictionarySelector(
524                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
525       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
526     if (Msg->getNumArgs() % 2 != 1)
527       return false;
528     unsigned SentinelIdx = Msg->getNumArgs() - 1;
529     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
530     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
531       return false;
532 
533     if (Msg->getNumArgs() == 1) {
534       commit.replace(MsgRange, "@{}");
535       return true;
536     }
537 
538     for (unsigned i = 0; i < SentinelIdx; i += 2) {
539       objectifyExpr(Msg->getArg(i), commit);
540       objectifyExpr(Msg->getArg(i+1), commit);
541 
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()));
549     }
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
552     // key.
553     SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
554                          Msg->getArg(SentinelIdx - 1)->getEndLoc());
555     commit.insertWrap("@{", ArgRange, "}");
556     commit.replaceWithInner(MsgRange, ArgRange);
557     return true;
558   }
559 
560   if (Sel == NS.getNSDictionarySelector(
561                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
562       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
563     if (Msg->getNumArgs() != 2)
564       return false;
565 
566     SmallVector<const Expr *, 8> Vals;
567     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
568       return false;
569 
570     SmallVector<const Expr *, 8> Keys;
571     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
572       return false;
573 
574     if (Vals.size() != Keys.size())
575       return false;
576 
577     if (Vals.empty()) {
578       commit.replace(MsgRange, "@{}");
579       return true;
580     }
581 
582     for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
583       objectifyExpr(Vals[i], commit);
584       objectifyExpr(Keys[i], commit);
585 
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);
591     }
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);
597     return true;
598   }
599 
600   return false;
601 }
602 
shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr * Msg,const NSAPI & NS)603 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
604                                                  const NSAPI &NS) {
605   if (!Msg)
606     return false;
607 
608   IdentifierInfo *II = nullptr;
609   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
610     return false;
611 
612   if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
613     return false;
614 
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)
620       return false;
621 
622     SmallVector<const Expr *, 8> Vals;
623     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
624       return false;
625 
626     SmallVector<const Expr *, 8> Keys;
627     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
628       return false;
629 
630     if (Vals.size() != Keys.size())
631       return false;
632 
633     return true;
634   }
635 
636   return false;
637 }
638 
639 //===----------------------------------------------------------------------===//
640 // rewriteToNumberLiteral.
641 //===----------------------------------------------------------------------===//
642 
rewriteToCharLiteral(const ObjCMessageExpr * Msg,const CharacterLiteral * Arg,const NSAPI & NS,Commit & commit)643 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
644                                    const CharacterLiteral *Arg,
645                                    const NSAPI &NS, Commit &commit) {
646   if (Arg->getKind() != CharacterLiteralKind::Ascii)
647     return false;
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(), "@");
653     return true;
654   }
655 
656   return rewriteToNumericBoxedExpression(Msg, NS, commit);
657 }
658 
rewriteToBoolLiteral(const ObjCMessageExpr * Msg,const Expr * Arg,const NSAPI & NS,Commit & commit)659 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
660                                    const Expr *Arg,
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(), "@");
667     return true;
668   }
669 
670   return rewriteToNumericBoxedExpression(Msg, NS, commit);
671 }
672 
673 namespace {
674 
675 struct LiteralInfo {
676   bool Hex, Octal;
677   StringRef U, F, L, LL;
678   CharSourceRange WithoutSuffRange;
679 };
680 
681 }
682 
getLiteralInfo(SourceRange literalRange,bool isFloat,bool isIntZero,ASTContext & Ctx,LiteralInfo & Info)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())
688     return false;
689   StringRef text = Lexer::getSourceText(
690                                   CharSourceRange::getTokenRange(literalRange),
691                                   Ctx.getSourceManager(), Ctx.getLangOpts());
692   if (text.empty())
693     return false;
694 
695   std::optional<bool> UpperU, UpperL;
696   bool UpperF = false;
697 
698   struct Suff {
699     static bool has(StringRef suff, StringRef &text) {
700       return text.consume_back(suff);
701     }
702   };
703 
704   while (true) {
705     if (Suff::has("u", text)) {
706       UpperU = false;
707     } else if (Suff::has("U", text)) {
708       UpperU = true;
709     } else if (Suff::has("ll", text)) {
710       UpperL = false;
711     } else if (Suff::has("LL", text)) {
712       UpperL = true;
713     } else if (Suff::has("l", text)) {
714       UpperL = false;
715     } else if (Suff::has("L", text)) {
716       UpperL = true;
717     } else if (isFloat && Suff::has("f", text)) {
718       UpperF = false;
719     } else if (isFloat && Suff::has("F", text)) {
720       UpperF = true;
721     } else
722       break;
723   }
724 
725   if (!UpperU && !UpperL)
726     UpperU = UpperL = true;
727   else if (UpperU && !UpperL)
728     UpperL = UpperU;
729   else if (UpperL && !UpperU)
730     UpperU = UpperL;
731 
732   Info.U = *UpperU ? "U" : "u";
733   Info.L = *UpperL ? "L" : "l";
734   Info.LL = *UpperL ? "LL" : "ll";
735   Info.F = UpperF ? "F" : "f";
736 
737   Info.Hex = Info.Octal = false;
738   if (text.starts_with("0x"))
739     Info.Hex = true;
740   else if (!isFloat && !isIntZero && text.starts_with("0"))
741     Info.Octal = true;
742 
743   SourceLocation B = literalRange.getBegin();
744   Info.WithoutSuffRange =
745       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
746   return true;
747 }
748 
rewriteToNumberLiteral(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)749 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
750                                    const NSAPI &NS, Commit &commit) {
751   if (Msg->getNumArgs() != 1)
752     return false;
753 
754   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
755   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
756     return rewriteToCharLiteral(Msg, CharE, NS, commit);
757   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
758     return rewriteToBoolLiteral(Msg, BE, NS, commit);
759   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
760     return rewriteToBoolLiteral(Msg, BE, NS, commit);
761 
762   const Expr *literalE = Arg;
763   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
764     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
765       literalE = UOE->getSubExpr();
766   }
767 
768   // Only integer and floating literals, otherwise try to rewrite to boxed
769   // expression.
770   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
771     return rewriteToNumericBoxedExpression(Msg, NS, commit);
772 
773   ASTContext &Ctx = NS.getASTContext();
774   Selector Sel = Msg->getSelector();
775   std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =
776       NS.getNSNumberLiteralMethodKind(Sel);
777   if (!MKOpt)
778     return false;
779   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
780 
781   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
782   bool CallIsFloating = false, CallIsDouble = false;
783 
784   switch (MK) {
785   // We cannot have these calls with int/float literals.
786   case NSAPI::NSNumberWithChar:
787   case NSAPI::NSNumberWithUnsignedChar:
788   case NSAPI::NSNumberWithShort:
789   case NSAPI::NSNumberWithUnsignedShort:
790   case NSAPI::NSNumberWithBool:
791     return rewriteToNumericBoxedExpression(Msg, NS, commit);
792 
793   case NSAPI::NSNumberWithUnsignedInt:
794   case NSAPI::NSNumberWithUnsignedInteger:
795     CallIsUnsigned = true;
796     [[fallthrough]];
797   case NSAPI::NSNumberWithInt:
798   case NSAPI::NSNumberWithInteger:
799     break;
800 
801   case NSAPI::NSNumberWithUnsignedLong:
802     CallIsUnsigned = true;
803     [[fallthrough]];
804   case NSAPI::NSNumberWithLong:
805     CallIsLong = true;
806     break;
807 
808   case NSAPI::NSNumberWithUnsignedLongLong:
809     CallIsUnsigned = true;
810     [[fallthrough]];
811   case NSAPI::NSNumberWithLongLong:
812     CallIsLongLong = true;
813     break;
814 
815   case NSAPI::NSNumberWithDouble:
816     CallIsDouble = true;
817     [[fallthrough]];
818   case NSAPI::NSNumberWithFloat:
819     CallIsFloating = true;
820     break;
821   }
822 
823   SourceRange ArgRange = Arg->getSourceRange();
824   QualType ArgTy = Arg->getType();
825   QualType CallTy = Msg->getArg(0)->getType();
826 
827   // Check for the easy case, the literal maps directly to the call.
828   if (Ctx.hasSameType(ArgTy, CallTy)) {
829     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
830     commit.insert(ArgRange.getBegin(), "@");
831     return true;
832   }
833 
834   // We will need to modify the literal suffix to get the same type as the call.
835   // Try with boxed expression if it came from a macro.
836   if (ArgRange.getBegin().isMacroID())
837     return rewriteToNumericBoxedExpression(Msg, NS, commit);
838 
839   bool LitIsFloat = ArgTy->isFloatingType();
840   // For a float passed to integer call, don't try rewriting to objc literal.
841   // It is difficult and a very uncommon case anyway.
842   // But try with boxed expression.
843   if (LitIsFloat && !CallIsFloating)
844     return rewriteToNumericBoxedExpression(Msg, NS, commit);
845 
846   // Try to modify the literal make it the same type as the method call.
847   // -Modify the suffix, and/or
848   // -Change integer to float
849 
850   LiteralInfo LitInfo;
851   bool isIntZero = false;
852   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
853     isIntZero = !IntE->getValue().getBoolValue();
854   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
855     return rewriteToNumericBoxedExpression(Msg, NS, commit);
856 
857   // Not easy to do int -> float with hex/octal and uncommon anyway.
858   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
859     return rewriteToNumericBoxedExpression(Msg, NS, commit);
860 
861   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
862   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
863 
864   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
865                          LitInfo.WithoutSuffRange);
866   commit.insert(LitB, "@");
867 
868   if (!LitIsFloat && CallIsFloating)
869     commit.insert(LitE, ".0");
870 
871   if (CallIsFloating) {
872     if (!CallIsDouble)
873       commit.insert(LitE, LitInfo.F);
874   } else {
875     if (CallIsUnsigned)
876       commit.insert(LitE, LitInfo.U);
877 
878     if (CallIsLong)
879       commit.insert(LitE, LitInfo.L);
880     else if (CallIsLongLong)
881       commit.insert(LitE, LitInfo.LL);
882   }
883   return true;
884 }
885 
886 // FIXME: Make determination of operator precedence more general and
887 // make it broadly available.
subscriptOperatorNeedsParens(const Expr * FullExpr)888 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
889   const Expr* Expr = FullExpr->IgnoreImpCasts();
890   if (isa<ArraySubscriptExpr>(Expr) ||
891       isa<CallExpr>(Expr) ||
892       isa<DeclRefExpr>(Expr) ||
893       isa<CXXNamedCastExpr>(Expr) ||
894       isa<CXXConstructExpr>(Expr) ||
895       isa<CXXThisExpr>(Expr) ||
896       isa<CXXTypeidExpr>(Expr) ||
897       isa<CXXUnresolvedConstructExpr>(Expr) ||
898       isa<ObjCMessageExpr>(Expr) ||
899       isa<ObjCPropertyRefExpr>(Expr) ||
900       isa<ObjCProtocolExpr>(Expr) ||
901       isa<MemberExpr>(Expr) ||
902       isa<ObjCIvarRefExpr>(Expr) ||
903       isa<ParenExpr>(FullExpr) ||
904       isa<ParenListExpr>(Expr) ||
905       isa<SizeOfPackExpr>(Expr))
906     return false;
907 
908   return true;
909 }
castOperatorNeedsParens(const Expr * FullExpr)910 static bool castOperatorNeedsParens(const Expr *FullExpr) {
911   const Expr* Expr = FullExpr->IgnoreImpCasts();
912   if (isa<ArraySubscriptExpr>(Expr) ||
913       isa<CallExpr>(Expr) ||
914       isa<DeclRefExpr>(Expr) ||
915       isa<CastExpr>(Expr) ||
916       isa<CXXNewExpr>(Expr) ||
917       isa<CXXConstructExpr>(Expr) ||
918       isa<CXXDeleteExpr>(Expr) ||
919       isa<CXXNoexceptExpr>(Expr) ||
920       isa<CXXPseudoDestructorExpr>(Expr) ||
921       isa<CXXScalarValueInitExpr>(Expr) ||
922       isa<CXXThisExpr>(Expr) ||
923       isa<CXXTypeidExpr>(Expr) ||
924       isa<CXXUnresolvedConstructExpr>(Expr) ||
925       isa<ObjCMessageExpr>(Expr) ||
926       isa<ObjCPropertyRefExpr>(Expr) ||
927       isa<ObjCProtocolExpr>(Expr) ||
928       isa<MemberExpr>(Expr) ||
929       isa<ObjCIvarRefExpr>(Expr) ||
930       isa<ParenExpr>(FullExpr) ||
931       isa<ParenListExpr>(Expr) ||
932       isa<SizeOfPackExpr>(Expr) ||
933       isa<UnaryOperator>(Expr))
934     return false;
935 
936   return true;
937 }
938 
objectifyExpr(const Expr * E,Commit & commit)939 static void objectifyExpr(const Expr *E, Commit &commit) {
940   if (!E) return;
941 
942   QualType T = E->getType();
943   if (T->isObjCObjectPointerType()) {
944     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
945       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
946         return;
947     } else {
948       return;
949     }
950   } else if (!T->isPointerType()) {
951     return;
952   }
953 
954   SourceRange Range = E->getSourceRange();
955   if (castOperatorNeedsParens(E))
956     commit.insertWrap("(", Range, ")");
957   commit.insertBefore(Range.getBegin(), "(id)");
958 }
959 
960 //===----------------------------------------------------------------------===//
961 // rewriteToNumericBoxedExpression.
962 //===----------------------------------------------------------------------===//
963 
isEnumConstant(const Expr * E)964 static bool isEnumConstant(const Expr *E) {
965   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
966     if (const ValueDecl *VD = DRE->getDecl())
967       return isa<EnumConstantDecl>(VD);
968 
969   return false;
970 }
971 
rewriteToNumericBoxedExpression(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)972 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
973                                             const NSAPI &NS, Commit &commit) {
974   if (Msg->getNumArgs() != 1)
975     return false;
976 
977   const Expr *Arg = Msg->getArg(0);
978   if (Arg->isTypeDependent())
979     return false;
980 
981   ASTContext &Ctx = NS.getASTContext();
982   Selector Sel = Msg->getSelector();
983   std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =
984       NS.getNSNumberLiteralMethodKind(Sel);
985   if (!MKOpt)
986     return false;
987   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
988 
989   const Expr *OrigArg = Arg->IgnoreImpCasts();
990   QualType FinalTy = Arg->getType();
991   QualType OrigTy = OrigArg->getType();
992   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
993   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
994 
995   bool isTruncated = FinalTySize < OrigTySize;
996   bool needsCast = false;
997 
998   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
999     switch (ICE->getCastKind()) {
1000     case CK_LValueToRValue:
1001     case CK_NoOp:
1002     case CK_UserDefinedConversion:
1003     case CK_HLSLArrayRValue:
1004       break;
1005 
1006     case CK_IntegralCast: {
1007       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1008         break;
1009       // Be more liberal with Integer/UnsignedInteger which are very commonly
1010       // used.
1011       if ((MK == NSAPI::NSNumberWithInteger ||
1012            MK == NSAPI::NSNumberWithUnsignedInteger) &&
1013           !isTruncated) {
1014         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1015           break;
1016         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1017             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1018           break;
1019       }
1020 
1021       needsCast = true;
1022       break;
1023     }
1024 
1025     case CK_PointerToBoolean:
1026     case CK_IntegralToBoolean:
1027     case CK_IntegralToFloating:
1028     case CK_FloatingToIntegral:
1029     case CK_FloatingToBoolean:
1030     case CK_FloatingCast:
1031     case CK_FloatingComplexToReal:
1032     case CK_FloatingComplexToBoolean:
1033     case CK_IntegralComplexToReal:
1034     case CK_IntegralComplexToBoolean:
1035     case CK_AtomicToNonAtomic:
1036     case CK_AddressSpaceConversion:
1037       needsCast = true;
1038       break;
1039 
1040     case CK_Dependent:
1041     case CK_BitCast:
1042     case CK_LValueBitCast:
1043     case CK_LValueToRValueBitCast:
1044     case CK_BaseToDerived:
1045     case CK_DerivedToBase:
1046     case CK_UncheckedDerivedToBase:
1047     case CK_Dynamic:
1048     case CK_ToUnion:
1049     case CK_ArrayToPointerDecay:
1050     case CK_FunctionToPointerDecay:
1051     case CK_NullToPointer:
1052     case CK_NullToMemberPointer:
1053     case CK_BaseToDerivedMemberPointer:
1054     case CK_DerivedToBaseMemberPointer:
1055     case CK_MemberPointerToBoolean:
1056     case CK_ReinterpretMemberPointer:
1057     case CK_ConstructorConversion:
1058     case CK_IntegralToPointer:
1059     case CK_PointerToIntegral:
1060     case CK_ToVoid:
1061     case CK_VectorSplat:
1062     case CK_CPointerToObjCPointerCast:
1063     case CK_BlockPointerToObjCPointerCast:
1064     case CK_AnyPointerToBlockPointerCast:
1065     case CK_ObjCObjectLValueCast:
1066     case CK_FloatingRealToComplex:
1067     case CK_FloatingComplexCast:
1068     case CK_FloatingComplexToIntegralComplex:
1069     case CK_IntegralRealToComplex:
1070     case CK_IntegralComplexCast:
1071     case CK_IntegralComplexToFloatingComplex:
1072     case CK_ARCProduceObject:
1073     case CK_ARCConsumeObject:
1074     case CK_ARCReclaimReturnedObject:
1075     case CK_ARCExtendBlockObject:
1076     case CK_NonAtomicToAtomic:
1077     case CK_CopyAndAutoreleaseBlockObject:
1078     case CK_BuiltinFnToFnPtr:
1079     case CK_ZeroToOCLOpaqueType:
1080     case CK_IntToOCLSampler:
1081     case CK_MatrixCast:
1082       return false;
1083 
1084     case CK_BooleanToSignedIntegral:
1085       llvm_unreachable("OpenCL-specific cast in Objective-C?");
1086 
1087     case CK_HLSLVectorTruncation:
1088       llvm_unreachable("HLSL-specific cast in Objective-C?");
1089       break;
1090 
1091     case CK_FloatingToFixedPoint:
1092     case CK_FixedPointToFloating:
1093     case CK_FixedPointCast:
1094     case CK_FixedPointToBoolean:
1095     case CK_FixedPointToIntegral:
1096     case CK_IntegralToFixedPoint:
1097       llvm_unreachable("Fixed point types are disabled for Objective-C");
1098     }
1099   }
1100 
1101   if (needsCast) {
1102     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1103     // FIXME: Use a custom category name to distinguish migration diagnostics.
1104     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1105                        "converting to boxing syntax requires casting %0 to %1");
1106     Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1107         << Msg->getSourceRange();
1108     return false;
1109   }
1110 
1111   SourceRange ArgRange = OrigArg->getSourceRange();
1112   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1113 
1114   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1115     commit.insertBefore(ArgRange.getBegin(), "@");
1116   else
1117     commit.insertWrap("@(", ArgRange, ")");
1118 
1119   return true;
1120 }
1121 
1122 //===----------------------------------------------------------------------===//
1123 // rewriteToStringBoxedExpression.
1124 //===----------------------------------------------------------------------===//
1125 
doRewriteToUTF8StringBoxedExpressionHelper(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)1126 static bool doRewriteToUTF8StringBoxedExpressionHelper(
1127                                               const ObjCMessageExpr *Msg,
1128                                               const NSAPI &NS, Commit &commit) {
1129   const Expr *Arg = Msg->getArg(0);
1130   if (Arg->isTypeDependent())
1131     return false;
1132 
1133   ASTContext &Ctx = NS.getASTContext();
1134 
1135   const Expr *OrigArg = Arg->IgnoreImpCasts();
1136   QualType OrigTy = OrigArg->getType();
1137   if (OrigTy->isArrayType())
1138     OrigTy = Ctx.getArrayDecayedType(OrigTy);
1139 
1140   if (const StringLiteral *
1141         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1142     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1143     commit.insert(StrE->getBeginLoc(), "@");
1144     return true;
1145   }
1146 
1147   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1148     QualType PointeeType = PT->getPointeeType();
1149     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1150       SourceRange ArgRange = OrigArg->getSourceRange();
1151       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1152 
1153       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1154         commit.insertBefore(ArgRange.getBegin(), "@");
1155       else
1156         commit.insertWrap("@(", ArgRange, ")");
1157 
1158       return true;
1159     }
1160   }
1161 
1162   return false;
1163 }
1164 
rewriteToStringBoxedExpression(const ObjCMessageExpr * Msg,const NSAPI & NS,Commit & commit)1165 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1166                                            const NSAPI &NS, Commit &commit) {
1167   Selector Sel = Msg->getSelector();
1168 
1169   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1170       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
1171       Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
1172     if (Msg->getNumArgs() != 1)
1173       return false;
1174     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1175   }
1176 
1177   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1178     if (Msg->getNumArgs() != 2)
1179       return false;
1180 
1181     const Expr *encodingArg = Msg->getArg(1);
1182     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1183         NS.isNSASCIIStringEncodingConstant(encodingArg))
1184       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1185   }
1186 
1187   return false;
1188 }
1189