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
22using namespace clang;
23using namespace edit;
24
25static 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
58bool 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'.
107static const ObjCInterfaceDecl *
108maybeAdjustInterfaceForSubscriptingCheck(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
155static 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
171static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
172
173static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
174  if (subscriptOperatorNeedsParens(Receiver)) {
175    SourceRange RecRange = Receiver->getSourceRange();
176    commit.insertWrap("(", RecRange, ")");
177  }
178}
179
180static 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
202static 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
212static 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
222static 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
256static 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
289bool 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
326static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
327                                  const NSAPI &NS, Commit &commit,
328                                  const ParentMap *PMap);
329static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
330                                  const NSAPI &NS, Commit &commit);
331static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
332                                  const NSAPI &NS, Commit &commit);
333static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
334                                            const NSAPI &NS, Commit &commit);
335static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
336                                           const NSAPI &NS, Commit &commit);
337
338bool 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.
367static 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.
375static void objectifyExpr(const Expr *E, Commit &commit);
376
377static 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.
439static 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
489static 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
602static 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
642static 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
658static 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
672namespace {
673
674struct LiteralInfo {
675  bool Hex, Octal;
676  StringRef U, F, L, LL;
677  CharSourceRange WithoutSuffRange;
678};
679
680}
681
682static 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 (1) {
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
752static 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.
891static 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}
913static 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
942static 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
967static 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
975static 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
1124static 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
1163static 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