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