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