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