1234287Sdim//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2234287Sdim//
3234287Sdim//                     The LLVM Compiler Infrastructure
4234287Sdim//
5234287Sdim// This file is distributed under the University of Illinois Open Source
6234287Sdim// License. See LICENSE.TXT for details.
7234287Sdim//
8234287Sdim//===----------------------------------------------------------------------===//
9234287Sdim//
10234287Sdim// Rewrites legacy method calls to modern syntax.
11234287Sdim//
12234287Sdim//===----------------------------------------------------------------------===//
13234287Sdim
14234287Sdim#include "clang/Edit/Rewriters.h"
15245431Sdim#include "clang/AST/ASTContext.h"
16252723Sdim#include "clang/AST/ExprCXX.h"
17234287Sdim#include "clang/AST/ExprObjC.h"
18234287Sdim#include "clang/AST/NSAPI.h"
19252723Sdim#include "clang/AST/ParentMap.h"
20252723Sdim#include "clang/Edit/Commit.h"
21252723Sdim#include "clang/Lex/Lexer.h"
22234287Sdim
23234287Sdimusing namespace clang;
24234287Sdimusing namespace edit;
25234287Sdim
26234287Sdimstatic bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
27245431Sdim                                    IdentifierInfo *&ClassId,
28245431Sdim                                    const LangOptions &LangOpts) {
29234287Sdim  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
30234287Sdim    return false;
31234287Sdim
32234287Sdim  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
33234287Sdim  if (!Receiver)
34234287Sdim    return false;
35234287Sdim  ClassId = Receiver->getIdentifier();
36234287Sdim
37234287Sdim  if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
38234287Sdim    return true;
39234287Sdim
40245431Sdim  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
41245431Sdim  // since the change from +1 to +0 will be handled fine by ARC.
42245431Sdim  if (LangOpts.ObjCAutoRefCount) {
43245431Sdim    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
44245431Sdim      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
45245431Sdim                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
46245431Sdim        if (Rec->getMethodFamily() == OMF_alloc)
47245431Sdim          return true;
48245431Sdim      }
49245431Sdim    }
50245431Sdim  }
51245431Sdim
52234287Sdim  return false;
53234287Sdim}
54234287Sdim
55234287Sdim//===----------------------------------------------------------------------===//
56234287Sdim// rewriteObjCRedundantCallWithLiteral.
57234287Sdim//===----------------------------------------------------------------------===//
58234287Sdim
59234287Sdimbool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
60234287Sdim                                              const NSAPI &NS, Commit &commit) {
61234287Sdim  IdentifierInfo *II = 0;
62245431Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
63234287Sdim    return false;
64234287Sdim  if (Msg->getNumArgs() != 1)
65234287Sdim    return false;
66234287Sdim
67234287Sdim  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
68234287Sdim  Selector Sel = Msg->getSelector();
69234287Sdim
70234287Sdim  if ((isa<ObjCStringLiteral>(Arg) &&
71234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
72245431Sdim       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
73245431Sdim        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
74234287Sdim
75234287Sdim      (isa<ObjCArrayLiteral>(Arg) &&
76234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
77245431Sdim       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
78245431Sdim        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
79234287Sdim
80234287Sdim      (isa<ObjCDictionaryLiteral>(Arg) &&
81234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
82245431Sdim       (NS.getNSDictionarySelector(
83245431Sdim                              NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
84245431Sdim        NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
85234287Sdim
86234287Sdim    commit.replaceWithInner(Msg->getSourceRange(),
87234287Sdim                           Msg->getArg(0)->getSourceRange());
88234287Sdim    return true;
89234287Sdim  }
90234287Sdim
91234287Sdim  return false;
92234287Sdim}
93234287Sdim
94234287Sdim//===----------------------------------------------------------------------===//
95234287Sdim// rewriteToObjCSubscriptSyntax.
96234287Sdim//===----------------------------------------------------------------------===//
97234287Sdim
98245431Sdim/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
99245431Sdim/// that the migrator handles) but return their instances as 'id', resulting
100245431Sdim/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
101245431Sdim///
102245431Sdim/// When checking if we can convert to subscripting syntax, check whether
103245431Sdim/// the receiver is a result of a class method from a hardcoded list of
104245431Sdim/// such classes. In such a case return the specific class as the interface
105245431Sdim/// of the receiver.
106245431Sdim///
107245431Sdim/// FIXME: Remove this when these classes start using 'instancetype'.
108245431Sdimstatic const ObjCInterfaceDecl *
109245431SdimmaybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
110245431Sdim                                         const Expr *Receiver,
111245431Sdim                                         ASTContext &Ctx) {
112245431Sdim  assert(IFace && Receiver);
113245431Sdim
114245431Sdim  // If the receiver has type 'id'...
115245431Sdim  if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
116245431Sdim    return IFace;
117245431Sdim
118245431Sdim  const ObjCMessageExpr *
119245431Sdim    InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
120245431Sdim  if (!InnerMsg)
121245431Sdim    return IFace;
122245431Sdim
123245431Sdim  QualType ClassRec;
124245431Sdim  switch (InnerMsg->getReceiverKind()) {
125245431Sdim  case ObjCMessageExpr::Instance:
126245431Sdim  case ObjCMessageExpr::SuperInstance:
127245431Sdim    return IFace;
128245431Sdim
129245431Sdim  case ObjCMessageExpr::Class:
130245431Sdim    ClassRec = InnerMsg->getClassReceiver();
131245431Sdim    break;
132245431Sdim  case ObjCMessageExpr::SuperClass:
133245431Sdim    ClassRec = InnerMsg->getSuperType();
134245431Sdim    break;
135245431Sdim  }
136245431Sdim
137245431Sdim  if (ClassRec.isNull())
138245431Sdim    return IFace;
139245431Sdim
140245431Sdim  // ...and it is the result of a class message...
141245431Sdim
142245431Sdim  const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
143245431Sdim  if (!ObjTy)
144245431Sdim    return IFace;
145245431Sdim  const ObjCInterfaceDecl *OID = ObjTy->getInterface();
146245431Sdim
147245431Sdim  // ...and the receiving class is NSMapTable or NSLocale, return that
148245431Sdim  // class as the receiving interface.
149245431Sdim  if (OID->getName() == "NSMapTable" ||
150245431Sdim      OID->getName() == "NSLocale")
151245431Sdim    return OID;
152245431Sdim
153245431Sdim  return IFace;
154245431Sdim}
155245431Sdim
156245431Sdimstatic bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
157245431Sdim                                        const ObjCMessageExpr *Msg,
158245431Sdim                                        ASTContext &Ctx,
159245431Sdim                                        Selector subscriptSel) {
160245431Sdim  const Expr *Rec = Msg->getInstanceReceiver();
161245431Sdim  if (!Rec)
162245431Sdim    return false;
163245431Sdim  IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
164245431Sdim
165245431Sdim  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
166245431Sdim    if (!MD->isUnavailable())
167245431Sdim      return true;
168245431Sdim  }
169245431Sdim  return false;
170245431Sdim}
171245431Sdim
172245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173245431Sdim
174234287Sdimstatic void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
175245431Sdim  if (subscriptOperatorNeedsParens(Receiver)) {
176234287Sdim    SourceRange RecRange = Receiver->getSourceRange();
177234287Sdim    commit.insertWrap("(", RecRange, ")");
178234287Sdim  }
179234287Sdim}
180234287Sdim
181245431Sdimstatic bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182245431Sdim                                        Commit &commit) {
183234287Sdim  if (Msg->getNumArgs() != 1)
184234287Sdim    return false;
185234287Sdim  const Expr *Rec = Msg->getInstanceReceiver();
186234287Sdim  if (!Rec)
187234287Sdim    return false;
188234287Sdim
189234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
190234287Sdim  SourceRange RecRange = Rec->getSourceRange();
191234287Sdim  SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
192234287Sdim
193234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
194234287Sdim                                                       ArgRange.getBegin()),
195234287Sdim                         CharSourceRange::getTokenRange(RecRange));
196234287Sdim  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
197234287Sdim                         ArgRange);
198234287Sdim  commit.insertWrap("[", ArgRange, "]");
199234287Sdim  maybePutParensOnReceiver(Rec, commit);
200234287Sdim  return true;
201234287Sdim}
202234287Sdim
203245431Sdimstatic bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204245431Sdim                                       const ObjCMessageExpr *Msg,
205245431Sdim                                       const NSAPI &NS,
206234287Sdim                                       Commit &commit) {
207245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
208245431Sdim                                   NS.getObjectAtIndexedSubscriptSelector()))
209245431Sdim    return false;
210245431Sdim  return rewriteToSubscriptGetCommon(Msg, commit);
211245431Sdim}
212245431Sdim
213245431Sdimstatic bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
214245431Sdim                                            const ObjCMessageExpr *Msg,
215245431Sdim                                            const NSAPI &NS,
216245431Sdim                                            Commit &commit) {
217245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
218245431Sdim                                  NS.getObjectForKeyedSubscriptSelector()))
219245431Sdim    return false;
220245431Sdim  return rewriteToSubscriptGetCommon(Msg, commit);
221245431Sdim}
222245431Sdim
223245431Sdimstatic bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
224245431Sdim                                       const ObjCMessageExpr *Msg,
225245431Sdim                                       const NSAPI &NS,
226245431Sdim                                       Commit &commit) {
227245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
228245431Sdim                                   NS.getSetObjectAtIndexedSubscriptSelector()))
229245431Sdim    return false;
230245431Sdim
231234287Sdim  if (Msg->getNumArgs() != 2)
232234287Sdim    return false;
233234287Sdim  const Expr *Rec = Msg->getInstanceReceiver();
234234287Sdim  if (!Rec)
235234287Sdim    return false;
236234287Sdim
237234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
238234287Sdim  SourceRange RecRange = Rec->getSourceRange();
239234287Sdim  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
240234287Sdim  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
241234287Sdim
242234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
243234287Sdim                                                       Arg0Range.getBegin()),
244234287Sdim                         CharSourceRange::getTokenRange(RecRange));
245234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
246234287Sdim                                                       Arg1Range.getBegin()),
247234287Sdim                         CharSourceRange::getTokenRange(Arg0Range));
248234287Sdim  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
249234287Sdim                         Arg1Range);
250234287Sdim  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
251234287Sdim                                                       Arg1Range.getBegin()),
252234287Sdim                    "] = ");
253234287Sdim  maybePutParensOnReceiver(Rec, commit);
254234287Sdim  return true;
255234287Sdim}
256234287Sdim
257245431Sdimstatic bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258245431Sdim                                            const ObjCMessageExpr *Msg,
259245431Sdim                                            const NSAPI &NS,
260234287Sdim                                            Commit &commit) {
261245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
262245431Sdim                                   NS.getSetObjectForKeyedSubscriptSelector()))
263245431Sdim    return false;
264245431Sdim
265234287Sdim  if (Msg->getNumArgs() != 2)
266234287Sdim    return false;
267234287Sdim  const Expr *Rec = Msg->getInstanceReceiver();
268234287Sdim  if (!Rec)
269234287Sdim    return false;
270234287Sdim
271234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
272234287Sdim  SourceRange RecRange = Rec->getSourceRange();
273234287Sdim  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
274234287Sdim  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
275234287Sdim
276234287Sdim  SourceLocation LocBeforeVal = Arg0Range.getBegin();
277234287Sdim  commit.insertBefore(LocBeforeVal, "] = ");
278234287Sdim  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
279234287Sdim                         /*beforePreviousInsertions=*/true);
280234287Sdim  commit.insertBefore(LocBeforeVal, "[");
281234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
282234287Sdim                                                       Arg0Range.getBegin()),
283234287Sdim                         CharSourceRange::getTokenRange(RecRange));
284234287Sdim  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
285234287Sdim                         Arg0Range);
286234287Sdim  maybePutParensOnReceiver(Rec, commit);
287234287Sdim  return true;
288234287Sdim}
289234287Sdim
290234287Sdimbool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
291245431Sdim                                        const NSAPI &NS, Commit &commit) {
292234287Sdim  if (!Msg || Msg->isImplicit() ||
293234287Sdim      Msg->getReceiverKind() != ObjCMessageExpr::Instance)
294234287Sdim    return false;
295234287Sdim  const ObjCMethodDecl *Method = Msg->getMethodDecl();
296234287Sdim  if (!Method)
297234287Sdim    return false;
298234287Sdim
299252723Sdim  const ObjCInterfaceDecl *IFace =
300252723Sdim      NS.getASTContext().getObjContainingInterface(Method);
301234287Sdim  if (!IFace)
302234287Sdim    return false;
303234287Sdim  Selector Sel = Msg->getSelector();
304234287Sdim
305245431Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
306245431Sdim    return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
307234287Sdim
308245431Sdim  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
309245431Sdim    return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
310245431Sdim
311234287Sdim  if (Msg->getNumArgs() != 2)
312234287Sdim    return false;
313234287Sdim
314245431Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
315245431Sdim    return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
316234287Sdim
317245431Sdim  if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
318245431Sdim    return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
319234287Sdim
320234287Sdim  return false;
321234287Sdim}
322234287Sdim
323234287Sdim//===----------------------------------------------------------------------===//
324234287Sdim// rewriteToObjCLiteralSyntax.
325234287Sdim//===----------------------------------------------------------------------===//
326234287Sdim
327234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
328252723Sdim                                  const NSAPI &NS, Commit &commit,
329252723Sdim                                  const ParentMap *PMap);
330234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
331234287Sdim                                  const NSAPI &NS, Commit &commit);
332234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
333234287Sdim                                  const NSAPI &NS, Commit &commit);
334245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
335245431Sdim                                            const NSAPI &NS, Commit &commit);
336245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
337245431Sdim                                           const NSAPI &NS, Commit &commit);
338234287Sdim
339234287Sdimbool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
340252723Sdim                                      const NSAPI &NS, Commit &commit,
341252723Sdim                                      const ParentMap *PMap) {
342234287Sdim  IdentifierInfo *II = 0;
343245431Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
344234287Sdim    return false;
345234287Sdim
346234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
347252723Sdim    return rewriteToArrayLiteral(Msg, NS, commit, PMap);
348234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
349234287Sdim    return rewriteToDictionaryLiteral(Msg, NS, commit);
350234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
351234287Sdim    return rewriteToNumberLiteral(Msg, NS, commit);
352245431Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
353245431Sdim    return rewriteToStringBoxedExpression(Msg, NS, commit);
354234287Sdim
355234287Sdim  return false;
356234287Sdim}
357234287Sdim
358252723Sdim/// \brief Returns true if the immediate message arguments of \c Msg should not
359252723Sdim/// be rewritten because it will interfere with the rewrite of the parent
360252723Sdim/// message expression. e.g.
361252723Sdim/// \code
362252723Sdim///   [NSDictionary dictionaryWithObjects:
363252723Sdim///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
364252723Sdim///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
365252723Sdim/// \endcode
366252723Sdim/// It will return true for this because we are going to rewrite this directly
367252723Sdim/// to a dictionary literal without any array literals.
368252723Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
369252723Sdim                                                 const NSAPI &NS);
370252723Sdim
371234287Sdim//===----------------------------------------------------------------------===//
372234287Sdim// rewriteToArrayLiteral.
373234287Sdim//===----------------------------------------------------------------------===//
374234287Sdim
375245431Sdim/// \brief Adds an explicit cast to 'id' if the type is not objc object.
376245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit);
377245431Sdim
378234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
379252723Sdim                                  const NSAPI &NS, Commit &commit,
380252723Sdim                                  const ParentMap *PMap) {
381252723Sdim  if (PMap) {
382252723Sdim    const ObjCMessageExpr *ParentMsg =
383252723Sdim        dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
384252723Sdim    if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
385252723Sdim      return false;
386252723Sdim  }
387252723Sdim
388234287Sdim  Selector Sel = Msg->getSelector();
389234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
390234287Sdim
391234287Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
392234287Sdim    if (Msg->getNumArgs() != 0)
393234287Sdim      return false;
394234287Sdim    commit.replace(MsgRange, "@[]");
395234287Sdim    return true;
396234287Sdim  }
397234287Sdim
398234287Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
399234287Sdim    if (Msg->getNumArgs() != 1)
400234287Sdim      return false;
401245431Sdim    objectifyExpr(Msg->getArg(0), commit);
402234287Sdim    SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
403234287Sdim    commit.replaceWithInner(MsgRange, ArgRange);
404234287Sdim    commit.insertWrap("@[", ArgRange, "]");
405234287Sdim    return true;
406234287Sdim  }
407234287Sdim
408245431Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
409245431Sdim      Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
410234287Sdim    if (Msg->getNumArgs() == 0)
411234287Sdim      return false;
412234287Sdim    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
413234287Sdim    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
414234287Sdim      return false;
415234287Sdim
416245431Sdim    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
417245431Sdim      objectifyExpr(Msg->getArg(i), commit);
418245431Sdim
419234287Sdim    if (Msg->getNumArgs() == 1) {
420234287Sdim      commit.replace(MsgRange, "@[]");
421234287Sdim      return true;
422234287Sdim    }
423234287Sdim    SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
424234287Sdim                         Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
425234287Sdim    commit.replaceWithInner(MsgRange, ArgRange);
426234287Sdim    commit.insertWrap("@[", ArgRange, "]");
427234287Sdim    return true;
428234287Sdim  }
429234287Sdim
430234287Sdim  return false;
431234287Sdim}
432234287Sdim
433234287Sdim//===----------------------------------------------------------------------===//
434234287Sdim// rewriteToDictionaryLiteral.
435234287Sdim//===----------------------------------------------------------------------===//
436234287Sdim
437252723Sdim/// \brief If \c Msg is an NSArray creation message or literal, this gets the
438252723Sdim/// objects that were used to create it.
439252723Sdim/// \returns true if it is an NSArray and we got objects, or false otherwise.
440252723Sdimstatic bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
441252723Sdim                              SmallVectorImpl<const Expr *> &Objs) {
442252723Sdim  if (!E)
443252723Sdim    return false;
444252723Sdim
445252723Sdim  E = E->IgnoreParenCasts();
446252723Sdim  if (!E)
447252723Sdim    return false;
448252723Sdim
449252723Sdim  if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
450252723Sdim    IdentifierInfo *Cls = 0;
451252723Sdim    if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
452252723Sdim      return false;
453252723Sdim
454252723Sdim    if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
455252723Sdim      return false;
456252723Sdim
457252723Sdim    Selector Sel = Msg->getSelector();
458252723Sdim    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
459252723Sdim      return true; // empty array.
460252723Sdim
461252723Sdim    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
462252723Sdim      if (Msg->getNumArgs() != 1)
463252723Sdim        return false;
464252723Sdim      Objs.push_back(Msg->getArg(0));
465252723Sdim      return true;
466252723Sdim    }
467252723Sdim
468252723Sdim    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
469252723Sdim        Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
470252723Sdim      if (Msg->getNumArgs() == 0)
471252723Sdim        return false;
472252723Sdim      const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
473252723Sdim      if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
474252723Sdim        return false;
475252723Sdim
476252723Sdim      for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
477252723Sdim        Objs.push_back(Msg->getArg(i));
478252723Sdim      return true;
479252723Sdim    }
480252723Sdim
481252723Sdim  } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
482252723Sdim    for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
483252723Sdim      Objs.push_back(ArrLit->getElement(i));
484252723Sdim    return true;
485252723Sdim  }
486252723Sdim
487252723Sdim  return false;
488252723Sdim}
489252723Sdim
490234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
491234287Sdim                                       const NSAPI &NS, Commit &commit) {
492234287Sdim  Selector Sel = Msg->getSelector();
493234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
494234287Sdim
495234287Sdim  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
496234287Sdim    if (Msg->getNumArgs() != 0)
497234287Sdim      return false;
498234287Sdim    commit.replace(MsgRange, "@{}");
499234287Sdim    return true;
500234287Sdim  }
501234287Sdim
502234287Sdim  if (Sel == NS.getNSDictionarySelector(
503234287Sdim                                    NSAPI::NSDict_dictionaryWithObjectForKey)) {
504234287Sdim    if (Msg->getNumArgs() != 2)
505234287Sdim      return false;
506245431Sdim
507245431Sdim    objectifyExpr(Msg->getArg(0), commit);
508245431Sdim    objectifyExpr(Msg->getArg(1), commit);
509245431Sdim
510234287Sdim    SourceRange ValRange = Msg->getArg(0)->getSourceRange();
511234287Sdim    SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
512234287Sdim    // Insert key before the value.
513234287Sdim    commit.insertBefore(ValRange.getBegin(), ": ");
514234287Sdim    commit.insertFromRange(ValRange.getBegin(),
515234287Sdim                           CharSourceRange::getTokenRange(KeyRange),
516234287Sdim                       /*afterToken=*/false, /*beforePreviousInsertions=*/true);
517234287Sdim    commit.insertBefore(ValRange.getBegin(), "@{");
518234287Sdim    commit.insertAfterToken(ValRange.getEnd(), "}");
519234287Sdim    commit.replaceWithInner(MsgRange, ValRange);
520234287Sdim    return true;
521234287Sdim  }
522234287Sdim
523234287Sdim  if (Sel == NS.getNSDictionarySelector(
524245431Sdim                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
525245431Sdim      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
526234287Sdim    if (Msg->getNumArgs() % 2 != 1)
527234287Sdim      return false;
528234287Sdim    unsigned SentinelIdx = Msg->getNumArgs() - 1;
529234287Sdim    const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
530234287Sdim    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
531234287Sdim      return false;
532234287Sdim
533234287Sdim    if (Msg->getNumArgs() == 1) {
534234287Sdim      commit.replace(MsgRange, "@{}");
535234287Sdim      return true;
536234287Sdim    }
537234287Sdim
538234287Sdim    for (unsigned i = 0; i < SentinelIdx; i += 2) {
539245431Sdim      objectifyExpr(Msg->getArg(i), commit);
540245431Sdim      objectifyExpr(Msg->getArg(i+1), commit);
541245431Sdim
542234287Sdim      SourceRange ValRange = Msg->getArg(i)->getSourceRange();
543234287Sdim      SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
544234287Sdim      // Insert value after key.
545234287Sdim      commit.insertAfterToken(KeyRange.getEnd(), ": ");
546234287Sdim      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
547234287Sdim      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
548234287Sdim                                                  KeyRange.getBegin()));
549234287Sdim    }
550234287Sdim    // Range of arguments up until and including the last key.
551234287Sdim    // The sentinel and first value are cut off, the value will move after the
552234287Sdim    // key.
553234287Sdim    SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
554234287Sdim                         Msg->getArg(SentinelIdx-1)->getLocEnd());
555234287Sdim    commit.insertWrap("@{", ArgRange, "}");
556234287Sdim    commit.replaceWithInner(MsgRange, ArgRange);
557234287Sdim    return true;
558234287Sdim  }
559234287Sdim
560252723Sdim  if (Sel == NS.getNSDictionarySelector(
561252723Sdim                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
562252723Sdim      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
563252723Sdim    if (Msg->getNumArgs() != 2)
564252723Sdim      return false;
565252723Sdim
566252723Sdim    SmallVector<const Expr *, 8> Vals;
567252723Sdim    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
568252723Sdim      return false;
569252723Sdim
570252723Sdim    SmallVector<const Expr *, 8> Keys;
571252723Sdim    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
572252723Sdim      return false;
573252723Sdim
574252723Sdim    if (Vals.size() != Keys.size())
575252723Sdim      return false;
576252723Sdim
577252723Sdim    if (Vals.empty()) {
578252723Sdim      commit.replace(MsgRange, "@{}");
579252723Sdim      return true;
580252723Sdim    }
581252723Sdim
582252723Sdim    for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
583252723Sdim      objectifyExpr(Vals[i], commit);
584252723Sdim      objectifyExpr(Keys[i], commit);
585252723Sdim
586252723Sdim      SourceRange ValRange = Vals[i]->getSourceRange();
587252723Sdim      SourceRange KeyRange = Keys[i]->getSourceRange();
588252723Sdim      // Insert value after key.
589252723Sdim      commit.insertAfterToken(KeyRange.getEnd(), ": ");
590252723Sdim      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
591252723Sdim    }
592252723Sdim    // Range of arguments up until and including the last key.
593252723Sdim    // The first value is cut off, the value will move after the key.
594252723Sdim    SourceRange ArgRange(Keys.front()->getLocStart(),
595252723Sdim                         Keys.back()->getLocEnd());
596252723Sdim    commit.insertWrap("@{", ArgRange, "}");
597252723Sdim    commit.replaceWithInner(MsgRange, ArgRange);
598252723Sdim    return true;
599252723Sdim  }
600252723Sdim
601234287Sdim  return false;
602234287Sdim}
603234287Sdim
604252723Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
605252723Sdim                                                 const NSAPI &NS) {
606252723Sdim  if (!Msg)
607252723Sdim    return false;
608252723Sdim
609252723Sdim  IdentifierInfo *II = 0;
610252723Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
611252723Sdim    return false;
612252723Sdim
613252723Sdim  if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
614252723Sdim    return false;
615252723Sdim
616252723Sdim  Selector Sel = Msg->getSelector();
617252723Sdim  if (Sel == NS.getNSDictionarySelector(
618252723Sdim                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
619252723Sdim      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
620252723Sdim    if (Msg->getNumArgs() != 2)
621252723Sdim      return false;
622252723Sdim
623252723Sdim    SmallVector<const Expr *, 8> Vals;
624252723Sdim    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
625252723Sdim      return false;
626252723Sdim
627252723Sdim    SmallVector<const Expr *, 8> Keys;
628252723Sdim    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
629252723Sdim      return false;
630252723Sdim
631252723Sdim    if (Vals.size() != Keys.size())
632252723Sdim      return false;
633252723Sdim
634252723Sdim    return true;
635252723Sdim  }
636252723Sdim
637252723Sdim  return false;
638252723Sdim}
639252723Sdim
640234287Sdim//===----------------------------------------------------------------------===//
641234287Sdim// rewriteToNumberLiteral.
642234287Sdim//===----------------------------------------------------------------------===//
643234287Sdim
644234287Sdimstatic bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
645234287Sdim                                   const CharacterLiteral *Arg,
646234287Sdim                                   const NSAPI &NS, Commit &commit) {
647234287Sdim  if (Arg->getKind() != CharacterLiteral::Ascii)
648234287Sdim    return false;
649234287Sdim  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
650234287Sdim                                   Msg->getSelector())) {
651234287Sdim    SourceRange ArgRange = Arg->getSourceRange();
652234287Sdim    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
653234287Sdim    commit.insert(ArgRange.getBegin(), "@");
654234287Sdim    return true;
655234287Sdim  }
656234287Sdim
657245431Sdim  return rewriteToNumericBoxedExpression(Msg, NS, commit);
658234287Sdim}
659234287Sdim
660234287Sdimstatic bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
661234287Sdim                                   const Expr *Arg,
662234287Sdim                                   const NSAPI &NS, Commit &commit) {
663234287Sdim  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
664234287Sdim                                   Msg->getSelector())) {
665234287Sdim    SourceRange ArgRange = Arg->getSourceRange();
666234287Sdim    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
667234287Sdim    commit.insert(ArgRange.getBegin(), "@");
668234287Sdim    return true;
669234287Sdim  }
670234287Sdim
671245431Sdim  return rewriteToNumericBoxedExpression(Msg, NS, commit);
672234287Sdim}
673234287Sdim
674234287Sdimnamespace {
675234287Sdim
676234287Sdimstruct LiteralInfo {
677234287Sdim  bool Hex, Octal;
678234287Sdim  StringRef U, F, L, LL;
679234287Sdim  CharSourceRange WithoutSuffRange;
680234287Sdim};
681234287Sdim
682234287Sdim}
683234287Sdim
684234287Sdimstatic bool getLiteralInfo(SourceRange literalRange,
685234287Sdim                           bool isFloat, bool isIntZero,
686234287Sdim                          ASTContext &Ctx, LiteralInfo &Info) {
687234287Sdim  if (literalRange.getBegin().isMacroID() ||
688234287Sdim      literalRange.getEnd().isMacroID())
689234287Sdim    return false;
690234287Sdim  StringRef text = Lexer::getSourceText(
691234287Sdim                                  CharSourceRange::getTokenRange(literalRange),
692234287Sdim                                  Ctx.getSourceManager(), Ctx.getLangOpts());
693234287Sdim  if (text.empty())
694234287Sdim    return false;
695234287Sdim
696252723Sdim  Optional<bool> UpperU, UpperL;
697234287Sdim  bool UpperF = false;
698234287Sdim
699234287Sdim  struct Suff {
700234287Sdim    static bool has(StringRef suff, StringRef &text) {
701234287Sdim      if (text.endswith(suff)) {
702234287Sdim        text = text.substr(0, text.size()-suff.size());
703234287Sdim        return true;
704234287Sdim      }
705234287Sdim      return false;
706234287Sdim    }
707234287Sdim  };
708234287Sdim
709234287Sdim  while (1) {
710234287Sdim    if (Suff::has("u", text)) {
711234287Sdim      UpperU = false;
712234287Sdim    } else if (Suff::has("U", text)) {
713234287Sdim      UpperU = true;
714234287Sdim    } else if (Suff::has("ll", text)) {
715234287Sdim      UpperL = false;
716234287Sdim    } else if (Suff::has("LL", text)) {
717234287Sdim      UpperL = true;
718234287Sdim    } else if (Suff::has("l", text)) {
719234287Sdim      UpperL = false;
720234287Sdim    } else if (Suff::has("L", text)) {
721234287Sdim      UpperL = true;
722234287Sdim    } else if (isFloat && Suff::has("f", text)) {
723234287Sdim      UpperF = false;
724234287Sdim    } else if (isFloat && Suff::has("F", text)) {
725234287Sdim      UpperF = true;
726234287Sdim    } else
727234287Sdim      break;
728234287Sdim  }
729234287Sdim
730234287Sdim  if (!UpperU.hasValue() && !UpperL.hasValue())
731234287Sdim    UpperU = UpperL = true;
732234287Sdim  else if (UpperU.hasValue() && !UpperL.hasValue())
733234287Sdim    UpperL = UpperU;
734234287Sdim  else if (UpperL.hasValue() && !UpperU.hasValue())
735234287Sdim    UpperU = UpperL;
736234287Sdim
737234287Sdim  Info.U = *UpperU ? "U" : "u";
738234287Sdim  Info.L = *UpperL ? "L" : "l";
739234287Sdim  Info.LL = *UpperL ? "LL" : "ll";
740234287Sdim  Info.F = UpperF ? "F" : "f";
741234287Sdim
742234287Sdim  Info.Hex = Info.Octal = false;
743234287Sdim  if (text.startswith("0x"))
744234287Sdim    Info.Hex = true;
745234287Sdim  else if (!isFloat && !isIntZero && text.startswith("0"))
746234287Sdim    Info.Octal = true;
747234287Sdim
748234287Sdim  SourceLocation B = literalRange.getBegin();
749234287Sdim  Info.WithoutSuffRange =
750234287Sdim      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
751234287Sdim  return true;
752234287Sdim}
753234287Sdim
754234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
755234287Sdim                                   const NSAPI &NS, Commit &commit) {
756234287Sdim  if (Msg->getNumArgs() != 1)
757234287Sdim    return false;
758234287Sdim
759234287Sdim  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
760234287Sdim  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
761234287Sdim    return rewriteToCharLiteral(Msg, CharE, NS, commit);
762234287Sdim  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
763234287Sdim    return rewriteToBoolLiteral(Msg, BE, NS, commit);
764234287Sdim  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
765234287Sdim    return rewriteToBoolLiteral(Msg, BE, NS, commit);
766234287Sdim
767234287Sdim  const Expr *literalE = Arg;
768234287Sdim  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
769234287Sdim    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
770234287Sdim      literalE = UOE->getSubExpr();
771234287Sdim  }
772234287Sdim
773245431Sdim  // Only integer and floating literals, otherwise try to rewrite to boxed
774245431Sdim  // expression.
775234287Sdim  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
776245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
777234287Sdim
778234287Sdim  ASTContext &Ctx = NS.getASTContext();
779234287Sdim  Selector Sel = Msg->getSelector();
780252723Sdim  Optional<NSAPI::NSNumberLiteralMethodKind>
781234287Sdim    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
782234287Sdim  if (!MKOpt)
783234287Sdim    return false;
784234287Sdim  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
785234287Sdim
786234287Sdim  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
787234287Sdim  bool CallIsFloating = false, CallIsDouble = false;
788234287Sdim
789234287Sdim  switch (MK) {
790234287Sdim  // We cannot have these calls with int/float literals.
791234287Sdim  case NSAPI::NSNumberWithChar:
792234287Sdim  case NSAPI::NSNumberWithUnsignedChar:
793234287Sdim  case NSAPI::NSNumberWithShort:
794234287Sdim  case NSAPI::NSNumberWithUnsignedShort:
795234287Sdim  case NSAPI::NSNumberWithBool:
796245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
797234287Sdim
798234287Sdim  case NSAPI::NSNumberWithUnsignedInt:
799234287Sdim  case NSAPI::NSNumberWithUnsignedInteger:
800234287Sdim    CallIsUnsigned = true;
801234287Sdim  case NSAPI::NSNumberWithInt:
802234287Sdim  case NSAPI::NSNumberWithInteger:
803234287Sdim    break;
804234287Sdim
805234287Sdim  case NSAPI::NSNumberWithUnsignedLong:
806234287Sdim    CallIsUnsigned = true;
807234287Sdim  case NSAPI::NSNumberWithLong:
808234287Sdim    CallIsLong = true;
809234287Sdim    break;
810234287Sdim
811234287Sdim  case NSAPI::NSNumberWithUnsignedLongLong:
812234287Sdim    CallIsUnsigned = true;
813234287Sdim  case NSAPI::NSNumberWithLongLong:
814234287Sdim    CallIsLongLong = true;
815234287Sdim    break;
816234287Sdim
817234287Sdim  case NSAPI::NSNumberWithDouble:
818234287Sdim    CallIsDouble = true;
819234287Sdim  case NSAPI::NSNumberWithFloat:
820234287Sdim    CallIsFloating = true;
821234287Sdim    break;
822234287Sdim  }
823234287Sdim
824234287Sdim  SourceRange ArgRange = Arg->getSourceRange();
825234287Sdim  QualType ArgTy = Arg->getType();
826234287Sdim  QualType CallTy = Msg->getArg(0)->getType();
827234287Sdim
828234287Sdim  // Check for the easy case, the literal maps directly to the call.
829234287Sdim  if (Ctx.hasSameType(ArgTy, CallTy)) {
830234287Sdim    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
831234287Sdim    commit.insert(ArgRange.getBegin(), "@");
832234287Sdim    return true;
833234287Sdim  }
834234287Sdim
835234287Sdim  // We will need to modify the literal suffix to get the same type as the call.
836245431Sdim  // Try with boxed expression if it came from a macro.
837234287Sdim  if (ArgRange.getBegin().isMacroID())
838245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
839234287Sdim
840234287Sdim  bool LitIsFloat = ArgTy->isFloatingType();
841245431Sdim  // For a float passed to integer call, don't try rewriting to objc literal.
842245431Sdim  // It is difficult and a very uncommon case anyway.
843245431Sdim  // But try with boxed expression.
844234287Sdim  if (LitIsFloat && !CallIsFloating)
845245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
846234287Sdim
847234287Sdim  // Try to modify the literal make it the same type as the method call.
848234287Sdim  // -Modify the suffix, and/or
849234287Sdim  // -Change integer to float
850234287Sdim
851234287Sdim  LiteralInfo LitInfo;
852234287Sdim  bool isIntZero = false;
853234287Sdim  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
854234287Sdim    isIntZero = !IntE->getValue().getBoolValue();
855234287Sdim  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
856245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
857234287Sdim
858234287Sdim  // Not easy to do int -> float with hex/octal and uncommon anyway.
859234287Sdim  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
860245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
861234287Sdim
862234287Sdim  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
863234287Sdim  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
864234287Sdim
865234287Sdim  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
866234287Sdim                         LitInfo.WithoutSuffRange);
867234287Sdim  commit.insert(LitB, "@");
868234287Sdim
869234287Sdim  if (!LitIsFloat && CallIsFloating)
870234287Sdim    commit.insert(LitE, ".0");
871234287Sdim
872234287Sdim  if (CallIsFloating) {
873234287Sdim    if (!CallIsDouble)
874234287Sdim      commit.insert(LitE, LitInfo.F);
875234287Sdim  } else {
876234287Sdim    if (CallIsUnsigned)
877234287Sdim      commit.insert(LitE, LitInfo.U);
878234287Sdim
879234287Sdim    if (CallIsLong)
880234287Sdim      commit.insert(LitE, LitInfo.L);
881234287Sdim    else if (CallIsLongLong)
882234287Sdim      commit.insert(LitE, LitInfo.LL);
883234287Sdim  }
884234287Sdim  return true;
885234287Sdim}
886245431Sdim
887245431Sdim// FIXME: Make determination of operator precedence more general and
888245431Sdim// make it broadly available.
889245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
890245431Sdim  const Expr* Expr = FullExpr->IgnoreImpCasts();
891245431Sdim  if (isa<ArraySubscriptExpr>(Expr) ||
892245431Sdim      isa<CallExpr>(Expr) ||
893245431Sdim      isa<DeclRefExpr>(Expr) ||
894245431Sdim      isa<CXXNamedCastExpr>(Expr) ||
895245431Sdim      isa<CXXConstructExpr>(Expr) ||
896245431Sdim      isa<CXXThisExpr>(Expr) ||
897245431Sdim      isa<CXXTypeidExpr>(Expr) ||
898245431Sdim      isa<CXXUnresolvedConstructExpr>(Expr) ||
899245431Sdim      isa<ObjCMessageExpr>(Expr) ||
900245431Sdim      isa<ObjCPropertyRefExpr>(Expr) ||
901245431Sdim      isa<ObjCProtocolExpr>(Expr) ||
902245431Sdim      isa<MemberExpr>(Expr) ||
903245431Sdim      isa<ObjCIvarRefExpr>(Expr) ||
904245431Sdim      isa<ParenExpr>(FullExpr) ||
905245431Sdim      isa<ParenListExpr>(Expr) ||
906245431Sdim      isa<SizeOfPackExpr>(Expr))
907245431Sdim    return false;
908245431Sdim
909245431Sdim  return true;
910245431Sdim}
911245431Sdimstatic bool castOperatorNeedsParens(const Expr *FullExpr) {
912245431Sdim  const Expr* Expr = FullExpr->IgnoreImpCasts();
913245431Sdim  if (isa<ArraySubscriptExpr>(Expr) ||
914245431Sdim      isa<CallExpr>(Expr) ||
915245431Sdim      isa<DeclRefExpr>(Expr) ||
916245431Sdim      isa<CastExpr>(Expr) ||
917245431Sdim      isa<CXXNewExpr>(Expr) ||
918245431Sdim      isa<CXXConstructExpr>(Expr) ||
919245431Sdim      isa<CXXDeleteExpr>(Expr) ||
920245431Sdim      isa<CXXNoexceptExpr>(Expr) ||
921245431Sdim      isa<CXXPseudoDestructorExpr>(Expr) ||
922245431Sdim      isa<CXXScalarValueInitExpr>(Expr) ||
923245431Sdim      isa<CXXThisExpr>(Expr) ||
924245431Sdim      isa<CXXTypeidExpr>(Expr) ||
925245431Sdim      isa<CXXUnresolvedConstructExpr>(Expr) ||
926245431Sdim      isa<ObjCMessageExpr>(Expr) ||
927245431Sdim      isa<ObjCPropertyRefExpr>(Expr) ||
928245431Sdim      isa<ObjCProtocolExpr>(Expr) ||
929245431Sdim      isa<MemberExpr>(Expr) ||
930245431Sdim      isa<ObjCIvarRefExpr>(Expr) ||
931245431Sdim      isa<ParenExpr>(FullExpr) ||
932245431Sdim      isa<ParenListExpr>(Expr) ||
933245431Sdim      isa<SizeOfPackExpr>(Expr) ||
934245431Sdim      isa<UnaryOperator>(Expr))
935245431Sdim    return false;
936245431Sdim
937245431Sdim  return true;
938245431Sdim}
939245431Sdim
940245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit) {
941245431Sdim  if (!E) return;
942245431Sdim
943245431Sdim  QualType T = E->getType();
944245431Sdim  if (T->isObjCObjectPointerType()) {
945245431Sdim    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
946245431Sdim      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
947245431Sdim        return;
948245431Sdim    } else {
949245431Sdim      return;
950245431Sdim    }
951245431Sdim  } else if (!T->isPointerType()) {
952245431Sdim    return;
953245431Sdim  }
954245431Sdim
955245431Sdim  SourceRange Range = E->getSourceRange();
956245431Sdim  if (castOperatorNeedsParens(E))
957245431Sdim    commit.insertWrap("(", Range, ")");
958245431Sdim  commit.insertBefore(Range.getBegin(), "(id)");
959245431Sdim}
960245431Sdim
961245431Sdim//===----------------------------------------------------------------------===//
962245431Sdim// rewriteToNumericBoxedExpression.
963245431Sdim//===----------------------------------------------------------------------===//
964245431Sdim
965245431Sdimstatic bool isEnumConstant(const Expr *E) {
966245431Sdim  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
967245431Sdim    if (const ValueDecl *VD = DRE->getDecl())
968245431Sdim      return isa<EnumConstantDecl>(VD);
969245431Sdim
970245431Sdim  return false;
971245431Sdim}
972245431Sdim
973245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
974245431Sdim                                            const NSAPI &NS, Commit &commit) {
975245431Sdim  if (Msg->getNumArgs() != 1)
976245431Sdim    return false;
977245431Sdim
978245431Sdim  const Expr *Arg = Msg->getArg(0);
979245431Sdim  if (Arg->isTypeDependent())
980245431Sdim    return false;
981245431Sdim
982245431Sdim  ASTContext &Ctx = NS.getASTContext();
983245431Sdim  Selector Sel = Msg->getSelector();
984252723Sdim  Optional<NSAPI::NSNumberLiteralMethodKind>
985245431Sdim    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
986245431Sdim  if (!MKOpt)
987245431Sdim    return false;
988245431Sdim  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
989245431Sdim
990245431Sdim  const Expr *OrigArg = Arg->IgnoreImpCasts();
991245431Sdim  QualType FinalTy = Arg->getType();
992245431Sdim  QualType OrigTy = OrigArg->getType();
993245431Sdim  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
994245431Sdim  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
995245431Sdim
996245431Sdim  bool isTruncated = FinalTySize < OrigTySize;
997245431Sdim  bool needsCast = false;
998245431Sdim
999245431Sdim  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1000245431Sdim    switch (ICE->getCastKind()) {
1001245431Sdim    case CK_LValueToRValue:
1002245431Sdim    case CK_NoOp:
1003245431Sdim    case CK_UserDefinedConversion:
1004245431Sdim      break;
1005245431Sdim
1006245431Sdim    case CK_IntegralCast: {
1007245431Sdim      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1008245431Sdim        break;
1009245431Sdim      // Be more liberal with Integer/UnsignedInteger which are very commonly
1010245431Sdim      // used.
1011245431Sdim      if ((MK == NSAPI::NSNumberWithInteger ||
1012245431Sdim           MK == NSAPI::NSNumberWithUnsignedInteger) &&
1013245431Sdim          !isTruncated) {
1014245431Sdim        if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1015245431Sdim          break;
1016245431Sdim        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1017245431Sdim            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1018245431Sdim          break;
1019245431Sdim      }
1020245431Sdim
1021245431Sdim      needsCast = true;
1022245431Sdim      break;
1023245431Sdim    }
1024245431Sdim
1025245431Sdim    case CK_PointerToBoolean:
1026245431Sdim    case CK_IntegralToBoolean:
1027245431Sdim    case CK_IntegralToFloating:
1028245431Sdim    case CK_FloatingToIntegral:
1029245431Sdim    case CK_FloatingToBoolean:
1030245431Sdim    case CK_FloatingCast:
1031245431Sdim    case CK_FloatingComplexToReal:
1032245431Sdim    case CK_FloatingComplexToBoolean:
1033245431Sdim    case CK_IntegralComplexToReal:
1034245431Sdim    case CK_IntegralComplexToBoolean:
1035245431Sdim    case CK_AtomicToNonAtomic:
1036245431Sdim      needsCast = true;
1037245431Sdim      break;
1038245431Sdim
1039245431Sdim    case CK_Dependent:
1040245431Sdim    case CK_BitCast:
1041245431Sdim    case CK_LValueBitCast:
1042245431Sdim    case CK_BaseToDerived:
1043245431Sdim    case CK_DerivedToBase:
1044245431Sdim    case CK_UncheckedDerivedToBase:
1045245431Sdim    case CK_Dynamic:
1046245431Sdim    case CK_ToUnion:
1047245431Sdim    case CK_ArrayToPointerDecay:
1048245431Sdim    case CK_FunctionToPointerDecay:
1049245431Sdim    case CK_NullToPointer:
1050245431Sdim    case CK_NullToMemberPointer:
1051245431Sdim    case CK_BaseToDerivedMemberPointer:
1052245431Sdim    case CK_DerivedToBaseMemberPointer:
1053245431Sdim    case CK_MemberPointerToBoolean:
1054245431Sdim    case CK_ReinterpretMemberPointer:
1055245431Sdim    case CK_ConstructorConversion:
1056245431Sdim    case CK_IntegralToPointer:
1057245431Sdim    case CK_PointerToIntegral:
1058245431Sdim    case CK_ToVoid:
1059245431Sdim    case CK_VectorSplat:
1060245431Sdim    case CK_CPointerToObjCPointerCast:
1061245431Sdim    case CK_BlockPointerToObjCPointerCast:
1062245431Sdim    case CK_AnyPointerToBlockPointerCast:
1063245431Sdim    case CK_ObjCObjectLValueCast:
1064245431Sdim    case CK_FloatingRealToComplex:
1065245431Sdim    case CK_FloatingComplexCast:
1066245431Sdim    case CK_FloatingComplexToIntegralComplex:
1067245431Sdim    case CK_IntegralRealToComplex:
1068245431Sdim    case CK_IntegralComplexCast:
1069245431Sdim    case CK_IntegralComplexToFloatingComplex:
1070245431Sdim    case CK_ARCProduceObject:
1071245431Sdim    case CK_ARCConsumeObject:
1072245431Sdim    case CK_ARCReclaimReturnedObject:
1073245431Sdim    case CK_ARCExtendBlockObject:
1074245431Sdim    case CK_NonAtomicToAtomic:
1075245431Sdim    case CK_CopyAndAutoreleaseBlockObject:
1076245431Sdim    case CK_BuiltinFnToFnPtr:
1077252723Sdim    case CK_ZeroToOCLEvent:
1078245431Sdim      return false;
1079245431Sdim    }
1080245431Sdim  }
1081245431Sdim
1082245431Sdim  if (needsCast) {
1083245431Sdim    DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1084245431Sdim    // FIXME: Use a custom category name to distinguish migration diagnostics.
1085245431Sdim    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1086245431Sdim                       "converting to boxing syntax requires casting %0 to %1");
1087245431Sdim    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1088245431Sdim        << Msg->getSourceRange();
1089245431Sdim    return false;
1090245431Sdim  }
1091245431Sdim
1092245431Sdim  SourceRange ArgRange = OrigArg->getSourceRange();
1093245431Sdim  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1094245431Sdim
1095245431Sdim  if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1096245431Sdim    commit.insertBefore(ArgRange.getBegin(), "@");
1097245431Sdim  else
1098245431Sdim    commit.insertWrap("@(", ArgRange, ")");
1099245431Sdim
1100245431Sdim  return true;
1101245431Sdim}
1102245431Sdim
1103245431Sdim//===----------------------------------------------------------------------===//
1104245431Sdim// rewriteToStringBoxedExpression.
1105245431Sdim//===----------------------------------------------------------------------===//
1106245431Sdim
1107245431Sdimstatic bool doRewriteToUTF8StringBoxedExpressionHelper(
1108245431Sdim                                              const ObjCMessageExpr *Msg,
1109245431Sdim                                              const NSAPI &NS, Commit &commit) {
1110245431Sdim  const Expr *Arg = Msg->getArg(0);
1111245431Sdim  if (Arg->isTypeDependent())
1112245431Sdim    return false;
1113245431Sdim
1114245431Sdim  ASTContext &Ctx = NS.getASTContext();
1115245431Sdim
1116245431Sdim  const Expr *OrigArg = Arg->IgnoreImpCasts();
1117245431Sdim  QualType OrigTy = OrigArg->getType();
1118245431Sdim  if (OrigTy->isArrayType())
1119245431Sdim    OrigTy = Ctx.getArrayDecayedType(OrigTy);
1120245431Sdim
1121245431Sdim  if (const StringLiteral *
1122245431Sdim        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1123245431Sdim    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1124245431Sdim    commit.insert(StrE->getLocStart(), "@");
1125245431Sdim    return true;
1126245431Sdim  }
1127245431Sdim
1128245431Sdim  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1129245431Sdim    QualType PointeeType = PT->getPointeeType();
1130245431Sdim    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1131245431Sdim      SourceRange ArgRange = OrigArg->getSourceRange();
1132245431Sdim      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1133245431Sdim
1134245431Sdim      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1135245431Sdim        commit.insertBefore(ArgRange.getBegin(), "@");
1136245431Sdim      else
1137245431Sdim        commit.insertWrap("@(", ArgRange, ")");
1138245431Sdim
1139245431Sdim      return true;
1140245431Sdim    }
1141245431Sdim  }
1142245431Sdim
1143245431Sdim  return false;
1144245431Sdim}
1145245431Sdim
1146245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1147245431Sdim                                           const NSAPI &NS, Commit &commit) {
1148245431Sdim  Selector Sel = Msg->getSelector();
1149245431Sdim
1150245431Sdim  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1151245431Sdim      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
1152245431Sdim    if (Msg->getNumArgs() != 1)
1153245431Sdim      return false;
1154245431Sdim    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1155245431Sdim  }
1156245431Sdim
1157245431Sdim  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1158245431Sdim    if (Msg->getNumArgs() != 2)
1159245431Sdim      return false;
1160245431Sdim
1161245431Sdim    const Expr *encodingArg = Msg->getArg(1);
1162245431Sdim    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1163245431Sdim        NS.isNSASCIIStringEncodingConstant(encodingArg))
1164245431Sdim      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1165245431Sdim  }
1166245431Sdim
1167245431Sdim  return false;
1168245431Sdim}
1169