1 //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Rewrites legacy method calls to modern syntax.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Edit/Rewriters.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/ExprCXX.h"
17 #include "clang/AST/ExprObjC.h"
18 #include "clang/AST/NSAPI.h"
19 #include "clang/AST/ParentMap.h"
20 #include "clang/Edit/Commit.h"
21 #include "clang/Lex/Lexer.h"
22 
23 using namespace clang;
24 using namespace edit;
25 
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 
59 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
60                                               const NSAPI &NS, Commit &commit) {
61   IdentifierInfo *II = 0;
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 /// \brief 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 *
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 
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 
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 
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 
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 
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 
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 
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 
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 *
300     IFace = NS.getASTContext().getObjContainingInterface(
301                                           const_cast<ObjCMethodDecl *>(Method));
302   if (!IFace)
303     return false;
304   Selector Sel = Msg->getSelector();
305 
306   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
307     return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
308 
309   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
310     return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
311 
312   if (Msg->getNumArgs() != 2)
313     return false;
314 
315   if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
316     return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
317 
318   if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
319     return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
320 
321   return false;
322 }
323 
324 //===----------------------------------------------------------------------===//
325 // rewriteToObjCLiteralSyntax.
326 //===----------------------------------------------------------------------===//
327 
328 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
329                                   const NSAPI &NS, Commit &commit,
330                                   const ParentMap *PMap);
331 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
332                                   const NSAPI &NS, Commit &commit);
333 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
334                                   const NSAPI &NS, Commit &commit);
335 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
336                                             const NSAPI &NS, Commit &commit);
337 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
338                                            const NSAPI &NS, Commit &commit);
339 
340 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
341                                       const NSAPI &NS, Commit &commit,
342                                       const ParentMap *PMap) {
343   IdentifierInfo *II = 0;
344   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
345     return false;
346 
347   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
348     return rewriteToArrayLiteral(Msg, NS, commit, PMap);
349   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
350     return rewriteToDictionaryLiteral(Msg, NS, commit);
351   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
352     return rewriteToNumberLiteral(Msg, NS, commit);
353   if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
354     return rewriteToStringBoxedExpression(Msg, NS, commit);
355 
356   return false;
357 }
358 
359 /// \brief Returns true if the immediate message arguments of \c Msg should not
360 /// be rewritten because it will interfere with the rewrite of the parent
361 /// message expression. e.g.
362 /// \code
363 ///   [NSDictionary dictionaryWithObjects:
364 ///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
365 ///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
366 /// \endcode
367 /// It will return true for this because we are going to rewrite this directly
368 /// to a dictionary literal without any array literals.
369 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
370                                                  const NSAPI &NS);
371 
372 //===----------------------------------------------------------------------===//
373 // rewriteToArrayLiteral.
374 //===----------------------------------------------------------------------===//
375 
376 /// \brief Adds an explicit cast to 'id' if the type is not objc object.
377 static void objectifyExpr(const Expr *E, Commit &commit);
378 
379 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
380                                   const NSAPI &NS, Commit &commit,
381                                   const ParentMap *PMap) {
382   if (PMap) {
383     const ObjCMessageExpr *ParentMsg =
384         dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
385     if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
386       return false;
387   }
388 
389   Selector Sel = Msg->getSelector();
390   SourceRange MsgRange = Msg->getSourceRange();
391 
392   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
393     if (Msg->getNumArgs() != 0)
394       return false;
395     commit.replace(MsgRange, "@[]");
396     return true;
397   }
398 
399   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
400     if (Msg->getNumArgs() != 1)
401       return false;
402     objectifyExpr(Msg->getArg(0), commit);
403     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
404     commit.replaceWithInner(MsgRange, ArgRange);
405     commit.insertWrap("@[", ArgRange, "]");
406     return true;
407   }
408 
409   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
410       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
411     if (Msg->getNumArgs() == 0)
412       return false;
413     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
414     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
415       return false;
416 
417     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
418       objectifyExpr(Msg->getArg(i), commit);
419 
420     if (Msg->getNumArgs() == 1) {
421       commit.replace(MsgRange, "@[]");
422       return true;
423     }
424     SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
425                          Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
426     commit.replaceWithInner(MsgRange, ArgRange);
427     commit.insertWrap("@[", ArgRange, "]");
428     return true;
429   }
430 
431   return false;
432 }
433 
434 //===----------------------------------------------------------------------===//
435 // rewriteToDictionaryLiteral.
436 //===----------------------------------------------------------------------===//
437 
438 /// \brief If \c Msg is an NSArray creation message or literal, this gets the
439 /// objects that were used to create it.
440 /// \returns true if it is an NSArray and we got objects, or false otherwise.
441 static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
442                               SmallVectorImpl<const Expr *> &Objs) {
443   if (!E)
444     return false;
445 
446   E = E->IgnoreParenCasts();
447   if (!E)
448     return false;
449 
450   if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
451     IdentifierInfo *Cls = 0;
452     if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
453       return false;
454 
455     if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
456       return false;
457 
458     Selector Sel = Msg->getSelector();
459     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
460       return true; // empty array.
461 
462     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
463       if (Msg->getNumArgs() != 1)
464         return false;
465       Objs.push_back(Msg->getArg(0));
466       return true;
467     }
468 
469     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
470         Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
471       if (Msg->getNumArgs() == 0)
472         return false;
473       const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
474       if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
475         return false;
476 
477       for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
478         Objs.push_back(Msg->getArg(i));
479       return true;
480     }
481 
482   } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
483     for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
484       Objs.push_back(ArrLit->getElement(i));
485     return true;
486   }
487 
488   return false;
489 }
490 
491 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
492                                        const NSAPI &NS, Commit &commit) {
493   Selector Sel = Msg->getSelector();
494   SourceRange MsgRange = Msg->getSourceRange();
495 
496   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
497     if (Msg->getNumArgs() != 0)
498       return false;
499     commit.replace(MsgRange, "@{}");
500     return true;
501   }
502 
503   if (Sel == NS.getNSDictionarySelector(
504                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
505     if (Msg->getNumArgs() != 2)
506       return false;
507 
508     objectifyExpr(Msg->getArg(0), commit);
509     objectifyExpr(Msg->getArg(1), commit);
510 
511     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
512     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
513     // Insert key before the value.
514     commit.insertBefore(ValRange.getBegin(), ": ");
515     commit.insertFromRange(ValRange.getBegin(),
516                            CharSourceRange::getTokenRange(KeyRange),
517                        /*afterToken=*/false, /*beforePreviousInsertions=*/true);
518     commit.insertBefore(ValRange.getBegin(), "@{");
519     commit.insertAfterToken(ValRange.getEnd(), "}");
520     commit.replaceWithInner(MsgRange, ValRange);
521     return true;
522   }
523 
524   if (Sel == NS.getNSDictionarySelector(
525                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
526       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
527     if (Msg->getNumArgs() % 2 != 1)
528       return false;
529     unsigned SentinelIdx = Msg->getNumArgs() - 1;
530     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
531     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
532       return false;
533 
534     if (Msg->getNumArgs() == 1) {
535       commit.replace(MsgRange, "@{}");
536       return true;
537     }
538 
539     for (unsigned i = 0; i < SentinelIdx; i += 2) {
540       objectifyExpr(Msg->getArg(i), commit);
541       objectifyExpr(Msg->getArg(i+1), commit);
542 
543       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
544       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
545       // Insert value after key.
546       commit.insertAfterToken(KeyRange.getEnd(), ": ");
547       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
548       commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
549                                                   KeyRange.getBegin()));
550     }
551     // Range of arguments up until and including the last key.
552     // The sentinel and first value are cut off, the value will move after the
553     // key.
554     SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
555                          Msg->getArg(SentinelIdx-1)->getLocEnd());
556     commit.insertWrap("@{", ArgRange, "}");
557     commit.replaceWithInner(MsgRange, ArgRange);
558     return true;
559   }
560 
561   if (Sel == NS.getNSDictionarySelector(
562                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
563       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
564     if (Msg->getNumArgs() != 2)
565       return false;
566 
567     SmallVector<const Expr *, 8> Vals;
568     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
569       return false;
570 
571     SmallVector<const Expr *, 8> Keys;
572     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
573       return false;
574 
575     if (Vals.size() != Keys.size())
576       return false;
577 
578     if (Vals.empty()) {
579       commit.replace(MsgRange, "@{}");
580       return true;
581     }
582 
583     for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
584       objectifyExpr(Vals[i], commit);
585       objectifyExpr(Keys[i], commit);
586 
587       SourceRange ValRange = Vals[i]->getSourceRange();
588       SourceRange KeyRange = Keys[i]->getSourceRange();
589       // Insert value after key.
590       commit.insertAfterToken(KeyRange.getEnd(), ": ");
591       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
592     }
593     // Range of arguments up until and including the last key.
594     // The first value is cut off, the value will move after the key.
595     SourceRange ArgRange(Keys.front()->getLocStart(),
596                          Keys.back()->getLocEnd());
597     commit.insertWrap("@{", ArgRange, "}");
598     commit.replaceWithInner(MsgRange, ArgRange);
599     return true;
600   }
601 
602   return false;
603 }
604 
605 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
606                                                  const NSAPI &NS) {
607   if (!Msg)
608     return false;
609 
610   IdentifierInfo *II = 0;
611   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
612     return false;
613 
614   if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
615     return false;
616 
617   Selector Sel = Msg->getSelector();
618   if (Sel == NS.getNSDictionarySelector(
619                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
620       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
621     if (Msg->getNumArgs() != 2)
622       return false;
623 
624     SmallVector<const Expr *, 8> Vals;
625     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
626       return false;
627 
628     SmallVector<const Expr *, 8> Keys;
629     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
630       return false;
631 
632     if (Vals.size() != Keys.size())
633       return false;
634 
635     return true;
636   }
637 
638   return false;
639 }
640 
641 //===----------------------------------------------------------------------===//
642 // rewriteToNumberLiteral.
643 //===----------------------------------------------------------------------===//
644 
645 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
646                                    const CharacterLiteral *Arg,
647                                    const NSAPI &NS, Commit &commit) {
648   if (Arg->getKind() != CharacterLiteral::Ascii)
649     return false;
650   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
651                                    Msg->getSelector())) {
652     SourceRange ArgRange = Arg->getSourceRange();
653     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
654     commit.insert(ArgRange.getBegin(), "@");
655     return true;
656   }
657 
658   return rewriteToNumericBoxedExpression(Msg, NS, commit);
659 }
660 
661 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
662                                    const Expr *Arg,
663                                    const NSAPI &NS, Commit &commit) {
664   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
665                                    Msg->getSelector())) {
666     SourceRange ArgRange = Arg->getSourceRange();
667     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
668     commit.insert(ArgRange.getBegin(), "@");
669     return true;
670   }
671 
672   return rewriteToNumericBoxedExpression(Msg, NS, commit);
673 }
674 
675 namespace {
676 
677 struct LiteralInfo {
678   bool Hex, Octal;
679   StringRef U, F, L, LL;
680   CharSourceRange WithoutSuffRange;
681 };
682 
683 }
684 
685 static bool getLiteralInfo(SourceRange literalRange,
686                            bool isFloat, bool isIntZero,
687                           ASTContext &Ctx, LiteralInfo &Info) {
688   if (literalRange.getBegin().isMacroID() ||
689       literalRange.getEnd().isMacroID())
690     return false;
691   StringRef text = Lexer::getSourceText(
692                                   CharSourceRange::getTokenRange(literalRange),
693                                   Ctx.getSourceManager(), Ctx.getLangOpts());
694   if (text.empty())
695     return false;
696 
697   llvm::Optional<bool> UpperU, UpperL;
698   bool UpperF = false;
699 
700   struct Suff {
701     static bool has(StringRef suff, StringRef &text) {
702       if (text.endswith(suff)) {
703         text = text.substr(0, text.size()-suff.size());
704         return true;
705       }
706       return false;
707     }
708   };
709 
710   while (1) {
711     if (Suff::has("u", text)) {
712       UpperU = false;
713     } else if (Suff::has("U", text)) {
714       UpperU = true;
715     } else if (Suff::has("ll", text)) {
716       UpperL = false;
717     } else if (Suff::has("LL", text)) {
718       UpperL = true;
719     } else if (Suff::has("l", text)) {
720       UpperL = false;
721     } else if (Suff::has("L", text)) {
722       UpperL = true;
723     } else if (isFloat && Suff::has("f", text)) {
724       UpperF = false;
725     } else if (isFloat && Suff::has("F", text)) {
726       UpperF = true;
727     } else
728       break;
729   }
730 
731   if (!UpperU.hasValue() && !UpperL.hasValue())
732     UpperU = UpperL = true;
733   else if (UpperU.hasValue() && !UpperL.hasValue())
734     UpperL = UpperU;
735   else if (UpperL.hasValue() && !UpperU.hasValue())
736     UpperU = UpperL;
737 
738   Info.U = *UpperU ? "U" : "u";
739   Info.L = *UpperL ? "L" : "l";
740   Info.LL = *UpperL ? "LL" : "ll";
741   Info.F = UpperF ? "F" : "f";
742 
743   Info.Hex = Info.Octal = false;
744   if (text.startswith("0x"))
745     Info.Hex = true;
746   else if (!isFloat && !isIntZero && text.startswith("0"))
747     Info.Octal = true;
748 
749   SourceLocation B = literalRange.getBegin();
750   Info.WithoutSuffRange =
751       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
752   return true;
753 }
754 
755 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
756                                    const NSAPI &NS, Commit &commit) {
757   if (Msg->getNumArgs() != 1)
758     return false;
759 
760   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
761   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
762     return rewriteToCharLiteral(Msg, CharE, NS, commit);
763   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
764     return rewriteToBoolLiteral(Msg, BE, NS, commit);
765   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
766     return rewriteToBoolLiteral(Msg, BE, NS, commit);
767 
768   const Expr *literalE = Arg;
769   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
770     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
771       literalE = UOE->getSubExpr();
772   }
773 
774   // Only integer and floating literals, otherwise try to rewrite to boxed
775   // expression.
776   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
777     return rewriteToNumericBoxedExpression(Msg, NS, commit);
778 
779   ASTContext &Ctx = NS.getASTContext();
780   Selector Sel = Msg->getSelector();
781   llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
782     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
783   if (!MKOpt)
784     return false;
785   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
786 
787   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
788   bool CallIsFloating = false, CallIsDouble = false;
789 
790   switch (MK) {
791   // We cannot have these calls with int/float literals.
792   case NSAPI::NSNumberWithChar:
793   case NSAPI::NSNumberWithUnsignedChar:
794   case NSAPI::NSNumberWithShort:
795   case NSAPI::NSNumberWithUnsignedShort:
796   case NSAPI::NSNumberWithBool:
797     return rewriteToNumericBoxedExpression(Msg, NS, commit);
798 
799   case NSAPI::NSNumberWithUnsignedInt:
800   case NSAPI::NSNumberWithUnsignedInteger:
801     CallIsUnsigned = true;
802   case NSAPI::NSNumberWithInt:
803   case NSAPI::NSNumberWithInteger:
804     break;
805 
806   case NSAPI::NSNumberWithUnsignedLong:
807     CallIsUnsigned = true;
808   case NSAPI::NSNumberWithLong:
809     CallIsLong = true;
810     break;
811 
812   case NSAPI::NSNumberWithUnsignedLongLong:
813     CallIsUnsigned = true;
814   case NSAPI::NSNumberWithLongLong:
815     CallIsLongLong = true;
816     break;
817 
818   case NSAPI::NSNumberWithDouble:
819     CallIsDouble = true;
820   case NSAPI::NSNumberWithFloat:
821     CallIsFloating = true;
822     break;
823   }
824 
825   SourceRange ArgRange = Arg->getSourceRange();
826   QualType ArgTy = Arg->getType();
827   QualType CallTy = Msg->getArg(0)->getType();
828 
829   // Check for the easy case, the literal maps directly to the call.
830   if (Ctx.hasSameType(ArgTy, CallTy)) {
831     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
832     commit.insert(ArgRange.getBegin(), "@");
833     return true;
834   }
835 
836   // We will need to modify the literal suffix to get the same type as the call.
837   // Try with boxed expression if it came from a macro.
838   if (ArgRange.getBegin().isMacroID())
839     return rewriteToNumericBoxedExpression(Msg, NS, commit);
840 
841   bool LitIsFloat = ArgTy->isFloatingType();
842   // For a float passed to integer call, don't try rewriting to objc literal.
843   // It is difficult and a very uncommon case anyway.
844   // But try with boxed expression.
845   if (LitIsFloat && !CallIsFloating)
846     return rewriteToNumericBoxedExpression(Msg, NS, commit);
847 
848   // Try to modify the literal make it the same type as the method call.
849   // -Modify the suffix, and/or
850   // -Change integer to float
851 
852   LiteralInfo LitInfo;
853   bool isIntZero = false;
854   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
855     isIntZero = !IntE->getValue().getBoolValue();
856   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
857     return rewriteToNumericBoxedExpression(Msg, NS, commit);
858 
859   // Not easy to do int -> float with hex/octal and uncommon anyway.
860   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
861     return rewriteToNumericBoxedExpression(Msg, NS, commit);
862 
863   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
864   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
865 
866   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
867                          LitInfo.WithoutSuffRange);
868   commit.insert(LitB, "@");
869 
870   if (!LitIsFloat && CallIsFloating)
871     commit.insert(LitE, ".0");
872 
873   if (CallIsFloating) {
874     if (!CallIsDouble)
875       commit.insert(LitE, LitInfo.F);
876   } else {
877     if (CallIsUnsigned)
878       commit.insert(LitE, LitInfo.U);
879 
880     if (CallIsLong)
881       commit.insert(LitE, LitInfo.L);
882     else if (CallIsLongLong)
883       commit.insert(LitE, LitInfo.LL);
884   }
885   return true;
886 }
887 
888 // FIXME: Make determination of operator precedence more general and
889 // make it broadly available.
890 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
891   const Expr* Expr = FullExpr->IgnoreImpCasts();
892   if (isa<ArraySubscriptExpr>(Expr) ||
893       isa<CallExpr>(Expr) ||
894       isa<DeclRefExpr>(Expr) ||
895       isa<CXXNamedCastExpr>(Expr) ||
896       isa<CXXConstructExpr>(Expr) ||
897       isa<CXXThisExpr>(Expr) ||
898       isa<CXXTypeidExpr>(Expr) ||
899       isa<CXXUnresolvedConstructExpr>(Expr) ||
900       isa<ObjCMessageExpr>(Expr) ||
901       isa<ObjCPropertyRefExpr>(Expr) ||
902       isa<ObjCProtocolExpr>(Expr) ||
903       isa<MemberExpr>(Expr) ||
904       isa<ObjCIvarRefExpr>(Expr) ||
905       isa<ParenExpr>(FullExpr) ||
906       isa<ParenListExpr>(Expr) ||
907       isa<SizeOfPackExpr>(Expr))
908     return false;
909 
910   return true;
911 }
912 static bool castOperatorNeedsParens(const Expr *FullExpr) {
913   const Expr* Expr = FullExpr->IgnoreImpCasts();
914   if (isa<ArraySubscriptExpr>(Expr) ||
915       isa<CallExpr>(Expr) ||
916       isa<DeclRefExpr>(Expr) ||
917       isa<CastExpr>(Expr) ||
918       isa<CXXNewExpr>(Expr) ||
919       isa<CXXConstructExpr>(Expr) ||
920       isa<CXXDeleteExpr>(Expr) ||
921       isa<CXXNoexceptExpr>(Expr) ||
922       isa<CXXPseudoDestructorExpr>(Expr) ||
923       isa<CXXScalarValueInitExpr>(Expr) ||
924       isa<CXXThisExpr>(Expr) ||
925       isa<CXXTypeidExpr>(Expr) ||
926       isa<CXXUnresolvedConstructExpr>(Expr) ||
927       isa<ObjCMessageExpr>(Expr) ||
928       isa<ObjCPropertyRefExpr>(Expr) ||
929       isa<ObjCProtocolExpr>(Expr) ||
930       isa<MemberExpr>(Expr) ||
931       isa<ObjCIvarRefExpr>(Expr) ||
932       isa<ParenExpr>(FullExpr) ||
933       isa<ParenListExpr>(Expr) ||
934       isa<SizeOfPackExpr>(Expr) ||
935       isa<UnaryOperator>(Expr))
936     return false;
937 
938   return true;
939 }
940 
941 static void objectifyExpr(const Expr *E, Commit &commit) {
942   if (!E) return;
943 
944   QualType T = E->getType();
945   if (T->isObjCObjectPointerType()) {
946     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
947       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
948         return;
949     } else {
950       return;
951     }
952   } else if (!T->isPointerType()) {
953     return;
954   }
955 
956   SourceRange Range = E->getSourceRange();
957   if (castOperatorNeedsParens(E))
958     commit.insertWrap("(", Range, ")");
959   commit.insertBefore(Range.getBegin(), "(id)");
960 }
961 
962 //===----------------------------------------------------------------------===//
963 // rewriteToNumericBoxedExpression.
964 //===----------------------------------------------------------------------===//
965 
966 static bool isEnumConstant(const Expr *E) {
967   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
968     if (const ValueDecl *VD = DRE->getDecl())
969       return isa<EnumConstantDecl>(VD);
970 
971   return false;
972 }
973 
974 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
975                                             const NSAPI &NS, Commit &commit) {
976   if (Msg->getNumArgs() != 1)
977     return false;
978 
979   const Expr *Arg = Msg->getArg(0);
980   if (Arg->isTypeDependent())
981     return false;
982 
983   ASTContext &Ctx = NS.getASTContext();
984   Selector Sel = Msg->getSelector();
985   llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
986     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
987   if (!MKOpt)
988     return false;
989   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
990 
991   const Expr *OrigArg = Arg->IgnoreImpCasts();
992   QualType FinalTy = Arg->getType();
993   QualType OrigTy = OrigArg->getType();
994   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
995   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
996 
997   bool isTruncated = FinalTySize < OrigTySize;
998   bool needsCast = false;
999 
1000   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1001     switch (ICE->getCastKind()) {
1002     case CK_LValueToRValue:
1003     case CK_NoOp:
1004     case CK_UserDefinedConversion:
1005       break;
1006 
1007     case CK_IntegralCast: {
1008       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1009         break;
1010       // Be more liberal with Integer/UnsignedInteger which are very commonly
1011       // used.
1012       if ((MK == NSAPI::NSNumberWithInteger ||
1013            MK == NSAPI::NSNumberWithUnsignedInteger) &&
1014           !isTruncated) {
1015         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1016           break;
1017         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1018             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1019           break;
1020       }
1021 
1022       needsCast = true;
1023       break;
1024     }
1025 
1026     case CK_PointerToBoolean:
1027     case CK_IntegralToBoolean:
1028     case CK_IntegralToFloating:
1029     case CK_FloatingToIntegral:
1030     case CK_FloatingToBoolean:
1031     case CK_FloatingCast:
1032     case CK_FloatingComplexToReal:
1033     case CK_FloatingComplexToBoolean:
1034     case CK_IntegralComplexToReal:
1035     case CK_IntegralComplexToBoolean:
1036     case CK_AtomicToNonAtomic:
1037       needsCast = true;
1038       break;
1039 
1040     case CK_Dependent:
1041     case CK_BitCast:
1042     case CK_LValueBitCast:
1043     case CK_BaseToDerived:
1044     case CK_DerivedToBase:
1045     case CK_UncheckedDerivedToBase:
1046     case CK_Dynamic:
1047     case CK_ToUnion:
1048     case CK_ArrayToPointerDecay:
1049     case CK_FunctionToPointerDecay:
1050     case CK_NullToPointer:
1051     case CK_NullToMemberPointer:
1052     case CK_BaseToDerivedMemberPointer:
1053     case CK_DerivedToBaseMemberPointer:
1054     case CK_MemberPointerToBoolean:
1055     case CK_ReinterpretMemberPointer:
1056     case CK_ConstructorConversion:
1057     case CK_IntegralToPointer:
1058     case CK_PointerToIntegral:
1059     case CK_ToVoid:
1060     case CK_VectorSplat:
1061     case CK_CPointerToObjCPointerCast:
1062     case CK_BlockPointerToObjCPointerCast:
1063     case CK_AnyPointerToBlockPointerCast:
1064     case CK_ObjCObjectLValueCast:
1065     case CK_FloatingRealToComplex:
1066     case CK_FloatingComplexCast:
1067     case CK_FloatingComplexToIntegralComplex:
1068     case CK_IntegralRealToComplex:
1069     case CK_IntegralComplexCast:
1070     case CK_IntegralComplexToFloatingComplex:
1071     case CK_ARCProduceObject:
1072     case CK_ARCConsumeObject:
1073     case CK_ARCReclaimReturnedObject:
1074     case CK_ARCExtendBlockObject:
1075     case CK_NonAtomicToAtomic:
1076     case CK_CopyAndAutoreleaseBlockObject:
1077     case CK_BuiltinFnToFnPtr:
1078     case CK_ZeroToOCLEvent:
1079       return false;
1080     }
1081   }
1082 
1083   if (needsCast) {
1084     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1085     // FIXME: Use a custom category name to distinguish migration diagnostics.
1086     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1087                        "converting to boxing syntax requires casting %0 to %1");
1088     Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1089         << Msg->getSourceRange();
1090     return false;
1091   }
1092 
1093   SourceRange ArgRange = OrigArg->getSourceRange();
1094   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1095 
1096   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1097     commit.insertBefore(ArgRange.getBegin(), "@");
1098   else
1099     commit.insertWrap("@(", ArgRange, ")");
1100 
1101   return true;
1102 }
1103 
1104 //===----------------------------------------------------------------------===//
1105 // rewriteToStringBoxedExpression.
1106 //===----------------------------------------------------------------------===//
1107 
1108 static bool doRewriteToUTF8StringBoxedExpressionHelper(
1109                                               const ObjCMessageExpr *Msg,
1110                                               const NSAPI &NS, Commit &commit) {
1111   const Expr *Arg = Msg->getArg(0);
1112   if (Arg->isTypeDependent())
1113     return false;
1114 
1115   ASTContext &Ctx = NS.getASTContext();
1116 
1117   const Expr *OrigArg = Arg->IgnoreImpCasts();
1118   QualType OrigTy = OrigArg->getType();
1119   if (OrigTy->isArrayType())
1120     OrigTy = Ctx.getArrayDecayedType(OrigTy);
1121 
1122   if (const StringLiteral *
1123         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1124     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1125     commit.insert(StrE->getLocStart(), "@");
1126     return true;
1127   }
1128 
1129   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1130     QualType PointeeType = PT->getPointeeType();
1131     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1132       SourceRange ArgRange = OrigArg->getSourceRange();
1133       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1134 
1135       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1136         commit.insertBefore(ArgRange.getBegin(), "@");
1137       else
1138         commit.insertWrap("@(", ArgRange, ")");
1139 
1140       return true;
1141     }
1142   }
1143 
1144   return false;
1145 }
1146 
1147 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1148                                            const NSAPI &NS, Commit &commit) {
1149   Selector Sel = Msg->getSelector();
1150 
1151   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1152       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
1153     if (Msg->getNumArgs() != 1)
1154       return false;
1155     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1156   }
1157 
1158   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1159     if (Msg->getNumArgs() != 2)
1160       return false;
1161 
1162     const Expr *encodingArg = Msg->getArg(1);
1163     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1164         NS.isNSASCIIStringEncodingConstant(encodingArg))
1165       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1166   }
1167 
1168   return false;
1169 }
1170