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"
15239462Sdim#include "clang/AST/ASTContext.h"
16249423Sdim#include "clang/AST/ExprCXX.h"
17234287Sdim#include "clang/AST/ExprObjC.h"
18234287Sdim#include "clang/AST/NSAPI.h"
19249423Sdim#include "clang/AST/ParentMap.h"
20249423Sdim#include "clang/Edit/Commit.h"
21249423Sdim#include "clang/Lex/Lexer.h"
22234287Sdim
23234287Sdimusing namespace clang;
24234287Sdimusing namespace edit;
25234287Sdim
26234287Sdimstatic bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
27239462Sdim                                    IdentifierInfo *&ClassId,
28239462Sdim                                    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
40239462Sdim  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
41239462Sdim  // since the change from +1 to +0 will be handled fine by ARC.
42239462Sdim  if (LangOpts.ObjCAutoRefCount) {
43239462Sdim    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
44239462Sdim      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
45239462Sdim                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
46239462Sdim        if (Rec->getMethodFamily() == OMF_alloc)
47239462Sdim          return true;
48239462Sdim      }
49239462Sdim    }
50239462Sdim  }
51239462Sdim
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;
62239462Sdim  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 &&
72239462Sdim       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
73239462Sdim        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
74234287Sdim
75234287Sdim      (isa<ObjCArrayLiteral>(Arg) &&
76234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
77239462Sdim       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
78239462Sdim        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
79234287Sdim
80234287Sdim      (isa<ObjCDictionaryLiteral>(Arg) &&
81234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
82239462Sdim       (NS.getNSDictionarySelector(
83239462Sdim                              NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
84239462Sdim        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
98239462Sdim/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
99239462Sdim/// that the migrator handles) but return their instances as 'id', resulting
100239462Sdim/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
101239462Sdim///
102239462Sdim/// When checking if we can convert to subscripting syntax, check whether
103239462Sdim/// the receiver is a result of a class method from a hardcoded list of
104239462Sdim/// such classes. In such a case return the specific class as the interface
105239462Sdim/// of the receiver.
106239462Sdim///
107239462Sdim/// FIXME: Remove this when these classes start using 'instancetype'.
108239462Sdimstatic const ObjCInterfaceDecl *
109239462SdimmaybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
110239462Sdim                                         const Expr *Receiver,
111239462Sdim                                         ASTContext &Ctx) {
112239462Sdim  assert(IFace && Receiver);
113239462Sdim
114239462Sdim  // If the receiver has type 'id'...
115239462Sdim  if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
116239462Sdim    return IFace;
117239462Sdim
118239462Sdim  const ObjCMessageExpr *
119239462Sdim    InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
120239462Sdim  if (!InnerMsg)
121239462Sdim    return IFace;
122239462Sdim
123239462Sdim  QualType ClassRec;
124239462Sdim  switch (InnerMsg->getReceiverKind()) {
125239462Sdim  case ObjCMessageExpr::Instance:
126239462Sdim  case ObjCMessageExpr::SuperInstance:
127239462Sdim    return IFace;
128239462Sdim
129239462Sdim  case ObjCMessageExpr::Class:
130239462Sdim    ClassRec = InnerMsg->getClassReceiver();
131239462Sdim    break;
132239462Sdim  case ObjCMessageExpr::SuperClass:
133239462Sdim    ClassRec = InnerMsg->getSuperType();
134239462Sdim    break;
135239462Sdim  }
136239462Sdim
137239462Sdim  if (ClassRec.isNull())
138239462Sdim    return IFace;
139239462Sdim
140239462Sdim  // ...and it is the result of a class message...
141239462Sdim
142239462Sdim  const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
143239462Sdim  if (!ObjTy)
144239462Sdim    return IFace;
145239462Sdim  const ObjCInterfaceDecl *OID = ObjTy->getInterface();
146239462Sdim
147239462Sdim  // ...and the receiving class is NSMapTable or NSLocale, return that
148239462Sdim  // class as the receiving interface.
149239462Sdim  if (OID->getName() == "NSMapTable" ||
150239462Sdim      OID->getName() == "NSLocale")
151239462Sdim    return OID;
152239462Sdim
153239462Sdim  return IFace;
154239462Sdim}
155239462Sdim
156239462Sdimstatic bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
157239462Sdim                                        const ObjCMessageExpr *Msg,
158239462Sdim                                        ASTContext &Ctx,
159239462Sdim                                        Selector subscriptSel) {
160239462Sdim  const Expr *Rec = Msg->getInstanceReceiver();
161239462Sdim  if (!Rec)
162239462Sdim    return false;
163239462Sdim  IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
164239462Sdim
165239462Sdim  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
166239462Sdim    if (!MD->isUnavailable())
167239462Sdim      return true;
168239462Sdim  }
169239462Sdim  return false;
170239462Sdim}
171239462Sdim
172239462Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173239462Sdim
174234287Sdimstatic void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
175239462Sdim  if (subscriptOperatorNeedsParens(Receiver)) {
176234287Sdim    SourceRange RecRange = Receiver->getSourceRange();
177234287Sdim    commit.insertWrap("(", RecRange, ")");
178234287Sdim  }
179234287Sdim}
180234287Sdim
181239462Sdimstatic bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182239462Sdim                                        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
203239462Sdimstatic bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204239462Sdim                                       const ObjCMessageExpr *Msg,
205239462Sdim                                       const NSAPI &NS,
206234287Sdim                                       Commit &commit) {
207239462Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
208239462Sdim                                   NS.getObjectAtIndexedSubscriptSelector()))
209239462Sdim    return false;
210239462Sdim  return rewriteToSubscriptGetCommon(Msg, commit);
211239462Sdim}
212239462Sdim
213239462Sdimstatic bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
214239462Sdim                                            const ObjCMessageExpr *Msg,
215239462Sdim                                            const NSAPI &NS,
216239462Sdim                                            Commit &commit) {
217239462Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
218239462Sdim                                  NS.getObjectForKeyedSubscriptSelector()))
219239462Sdim    return false;
220239462Sdim  return rewriteToSubscriptGetCommon(Msg, commit);
221239462Sdim}
222239462Sdim
223239462Sdimstatic bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
224239462Sdim                                       const ObjCMessageExpr *Msg,
225239462Sdim                                       const NSAPI &NS,
226239462Sdim                                       Commit &commit) {
227239462Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
228239462Sdim                                   NS.getSetObjectAtIndexedSubscriptSelector()))
229239462Sdim    return false;
230239462Sdim
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
257239462Sdimstatic bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258239462Sdim                                            const ObjCMessageExpr *Msg,
259239462Sdim                                            const NSAPI &NS,
260234287Sdim                                            Commit &commit) {
261239462Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
262239462Sdim                                   NS.getSetObjectForKeyedSubscriptSelector()))
263239462Sdim    return false;
264239462Sdim
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,
291239462Sdim                                        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
299249423Sdim  const ObjCInterfaceDecl *IFace =
300249423Sdim      NS.getASTContext().getObjContainingInterface(Method);
301234287Sdim  if (!IFace)
302234287Sdim    return false;
303234287Sdim  Selector Sel = Msg->getSelector();
304234287Sdim
305239462Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
306239462Sdim    return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
307234287Sdim
308239462Sdim  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
309239462Sdim    return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
310239462Sdim
311234287Sdim  if (Msg->getNumArgs() != 2)
312234287Sdim    return false;
313234287Sdim
314239462Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
315239462Sdim    return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
316234287Sdim
317239462Sdim  if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
318239462Sdim    return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
319234287Sdim
320234287Sdim  return false;
321234287Sdim}
322234287Sdim
323234287Sdim//===----------------------------------------------------------------------===//
324234287Sdim// rewriteToObjCLiteralSyntax.
325234287Sdim//===----------------------------------------------------------------------===//
326234287Sdim
327234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
328249423Sdim                                  const NSAPI &NS, Commit &commit,
329249423Sdim                                  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);
334239462Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
335239462Sdim                                            const NSAPI &NS, Commit &commit);
336239462Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
337239462Sdim                                           const NSAPI &NS, Commit &commit);
338234287Sdim
339234287Sdimbool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
340249423Sdim                                      const NSAPI &NS, Commit &commit,
341249423Sdim                                      const ParentMap *PMap) {
342234287Sdim  IdentifierInfo *II = 0;
343239462Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
344234287Sdim    return false;
345234287Sdim
346234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
347249423Sdim    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);
352239462Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
353239462Sdim    return rewriteToStringBoxedExpression(Msg, NS, commit);
354234287Sdim
355234287Sdim  return false;
356234287Sdim}
357234287Sdim
358249423Sdim/// \brief Returns true if the immediate message arguments of \c Msg should not
359249423Sdim/// be rewritten because it will interfere with the rewrite of the parent
360249423Sdim/// message expression. e.g.
361249423Sdim/// \code
362249423Sdim///   [NSDictionary dictionaryWithObjects:
363249423Sdim///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
364249423Sdim///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
365249423Sdim/// \endcode
366249423Sdim/// It will return true for this because we are going to rewrite this directly
367249423Sdim/// to a dictionary literal without any array literals.
368249423Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
369249423Sdim                                                 const NSAPI &NS);
370249423Sdim
371234287Sdim//===----------------------------------------------------------------------===//
372234287Sdim// rewriteToArrayLiteral.
373234287Sdim//===----------------------------------------------------------------------===//
374234287Sdim
375239462Sdim/// \brief Adds an explicit cast to 'id' if the type is not objc object.
376239462Sdimstatic void objectifyExpr(const Expr *E, Commit &commit);
377239462Sdim
378234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
379249423Sdim                                  const NSAPI &NS, Commit &commit,
380249423Sdim                                  const ParentMap *PMap) {
381249423Sdim  if (PMap) {
382249423Sdim    const ObjCMessageExpr *ParentMsg =
383249423Sdim        dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
384249423Sdim    if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
385249423Sdim      return false;
386249423Sdim  }
387249423Sdim
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;
401239462Sdim    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
408239462Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
409239462Sdim      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
416239462Sdim    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
417239462Sdim      objectifyExpr(Msg->getArg(i), commit);
418239462Sdim
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
437249423Sdim/// \brief If \c Msg is an NSArray creation message or literal, this gets the
438249423Sdim/// objects that were used to create it.
439249423Sdim/// \returns true if it is an NSArray and we got objects, or false otherwise.
440249423Sdimstatic bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
441249423Sdim                              SmallVectorImpl<const Expr *> &Objs) {
442249423Sdim  if (!E)
443249423Sdim    return false;
444249423Sdim
445249423Sdim  E = E->IgnoreParenCasts();
446249423Sdim  if (!E)
447249423Sdim    return false;
448249423Sdim
449249423Sdim  if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
450249423Sdim    IdentifierInfo *Cls = 0;
451249423Sdim    if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
452249423Sdim      return false;
453249423Sdim
454249423Sdim    if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
455249423Sdim      return false;
456249423Sdim
457249423Sdim    Selector Sel = Msg->getSelector();
458249423Sdim    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
459249423Sdim      return true; // empty array.
460249423Sdim
461249423Sdim    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
462249423Sdim      if (Msg->getNumArgs() != 1)
463249423Sdim        return false;
464249423Sdim      Objs.push_back(Msg->getArg(0));
465249423Sdim      return true;
466249423Sdim    }
467249423Sdim
468249423Sdim    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
469249423Sdim        Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
470249423Sdim      if (Msg->getNumArgs() == 0)
471249423Sdim        return false;
472249423Sdim      const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
473249423Sdim      if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
474249423Sdim        return false;
475249423Sdim
476249423Sdim      for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
477249423Sdim        Objs.push_back(Msg->getArg(i));
478249423Sdim      return true;
479249423Sdim    }
480249423Sdim
481249423Sdim  } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
482249423Sdim    for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
483249423Sdim      Objs.push_back(ArrLit->getElement(i));
484249423Sdim    return true;
485249423Sdim  }
486249423Sdim
487249423Sdim  return false;
488249423Sdim}
489249423Sdim
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;
506239462Sdim
507239462Sdim    objectifyExpr(Msg->getArg(0), commit);
508239462Sdim    objectifyExpr(Msg->getArg(1), commit);
509239462Sdim
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(
524239462Sdim                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
525239462Sdim      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) {
539239462Sdim      objectifyExpr(Msg->getArg(i), commit);
540239462Sdim      objectifyExpr(Msg->getArg(i+1), commit);
541239462Sdim
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
560249423Sdim  if (Sel == NS.getNSDictionarySelector(
561249423Sdim                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
562249423Sdim      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
563249423Sdim    if (Msg->getNumArgs() != 2)
564249423Sdim      return false;
565249423Sdim
566249423Sdim    SmallVector<const Expr *, 8> Vals;
567249423Sdim    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
568249423Sdim      return false;
569249423Sdim
570249423Sdim    SmallVector<const Expr *, 8> Keys;
571249423Sdim    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
572249423Sdim      return false;
573249423Sdim
574249423Sdim    if (Vals.size() != Keys.size())
575249423Sdim      return false;
576249423Sdim
577249423Sdim    if (Vals.empty()) {
578249423Sdim      commit.replace(MsgRange, "@{}");
579249423Sdim      return true;
580249423Sdim    }
581249423Sdim
582249423Sdim    for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
583249423Sdim      objectifyExpr(Vals[i], commit);
584249423Sdim      objectifyExpr(Keys[i], commit);
585249423Sdim
586249423Sdim      SourceRange ValRange = Vals[i]->getSourceRange();
587249423Sdim      SourceRange KeyRange = Keys[i]->getSourceRange();
588249423Sdim      // Insert value after key.
589249423Sdim      commit.insertAfterToken(KeyRange.getEnd(), ": ");
590249423Sdim      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
591249423Sdim    }
592249423Sdim    // Range of arguments up until and including the last key.
593249423Sdim    // The first value is cut off, the value will move after the key.
594249423Sdim    SourceRange ArgRange(Keys.front()->getLocStart(),
595249423Sdim                         Keys.back()->getLocEnd());
596249423Sdim    commit.insertWrap("@{", ArgRange, "}");
597249423Sdim    commit.replaceWithInner(MsgRange, ArgRange);
598249423Sdim    return true;
599249423Sdim  }
600249423Sdim
601234287Sdim  return false;
602234287Sdim}
603234287Sdim
604249423Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
605249423Sdim                                                 const NSAPI &NS) {
606249423Sdim  if (!Msg)
607249423Sdim    return false;
608249423Sdim
609249423Sdim  IdentifierInfo *II = 0;
610249423Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
611249423Sdim    return false;
612249423Sdim
613249423Sdim  if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
614249423Sdim    return false;
615249423Sdim
616249423Sdim  Selector Sel = Msg->getSelector();
617249423Sdim  if (Sel == NS.getNSDictionarySelector(
618249423Sdim                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
619249423Sdim      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
620249423Sdim    if (Msg->getNumArgs() != 2)
621249423Sdim      return false;
622249423Sdim
623249423Sdim    SmallVector<const Expr *, 8> Vals;
624249423Sdim    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
625249423Sdim      return false;
626249423Sdim
627249423Sdim    SmallVector<const Expr *, 8> Keys;
628249423Sdim    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
629249423Sdim      return false;
630249423Sdim
631249423Sdim    if (Vals.size() != Keys.size())
632249423Sdim      return false;
633249423Sdim
634249423Sdim    return true;
635249423Sdim  }
636249423Sdim
637249423Sdim  return false;
638249423Sdim}
639249423Sdim
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
657239462Sdim  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
671239462Sdim  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
696249423Sdim  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
773239462Sdim  // Only integer and floating literals, otherwise try to rewrite to boxed
774239462Sdim  // expression.
775234287Sdim  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
776239462Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
777234287Sdim
778234287Sdim  ASTContext &Ctx = NS.getASTContext();
779234287Sdim  Selector Sel = Msg->getSelector();
780249423Sdim  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:
796239462Sdim    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.
836239462Sdim  // Try with boxed expression if it came from a macro.
837234287Sdim  if (ArgRange.getBegin().isMacroID())
838239462Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
839234287Sdim
840234287Sdim  bool LitIsFloat = ArgTy->isFloatingType();
841239462Sdim  // For a float passed to integer call, don't try rewriting to objc literal.
842239462Sdim  // It is difficult and a very uncommon case anyway.
843239462Sdim  // But try with boxed expression.
844234287Sdim  if (LitIsFloat && !CallIsFloating)
845239462Sdim    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))
856239462Sdim    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))
860239462Sdim    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}
886239462Sdim
887239462Sdim// FIXME: Make determination of operator precedence more general and
888239462Sdim// make it broadly available.
889239462Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
890239462Sdim  const Expr* Expr = FullExpr->IgnoreImpCasts();
891239462Sdim  if (isa<ArraySubscriptExpr>(Expr) ||
892239462Sdim      isa<CallExpr>(Expr) ||
893239462Sdim      isa<DeclRefExpr>(Expr) ||
894239462Sdim      isa<CXXNamedCastExpr>(Expr) ||
895239462Sdim      isa<CXXConstructExpr>(Expr) ||
896239462Sdim      isa<CXXThisExpr>(Expr) ||
897239462Sdim      isa<CXXTypeidExpr>(Expr) ||
898239462Sdim      isa<CXXUnresolvedConstructExpr>(Expr) ||
899239462Sdim      isa<ObjCMessageExpr>(Expr) ||
900239462Sdim      isa<ObjCPropertyRefExpr>(Expr) ||
901239462Sdim      isa<ObjCProtocolExpr>(Expr) ||
902239462Sdim      isa<MemberExpr>(Expr) ||
903239462Sdim      isa<ObjCIvarRefExpr>(Expr) ||
904239462Sdim      isa<ParenExpr>(FullExpr) ||
905239462Sdim      isa<ParenListExpr>(Expr) ||
906239462Sdim      isa<SizeOfPackExpr>(Expr))
907239462Sdim    return false;
908239462Sdim
909239462Sdim  return true;
910239462Sdim}
911239462Sdimstatic bool castOperatorNeedsParens(const Expr *FullExpr) {
912239462Sdim  const Expr* Expr = FullExpr->IgnoreImpCasts();
913239462Sdim  if (isa<ArraySubscriptExpr>(Expr) ||
914239462Sdim      isa<CallExpr>(Expr) ||
915239462Sdim      isa<DeclRefExpr>(Expr) ||
916239462Sdim      isa<CastExpr>(Expr) ||
917239462Sdim      isa<CXXNewExpr>(Expr) ||
918239462Sdim      isa<CXXConstructExpr>(Expr) ||
919239462Sdim      isa<CXXDeleteExpr>(Expr) ||
920239462Sdim      isa<CXXNoexceptExpr>(Expr) ||
921239462Sdim      isa<CXXPseudoDestructorExpr>(Expr) ||
922239462Sdim      isa<CXXScalarValueInitExpr>(Expr) ||
923239462Sdim      isa<CXXThisExpr>(Expr) ||
924239462Sdim      isa<CXXTypeidExpr>(Expr) ||
925239462Sdim      isa<CXXUnresolvedConstructExpr>(Expr) ||
926239462Sdim      isa<ObjCMessageExpr>(Expr) ||
927239462Sdim      isa<ObjCPropertyRefExpr>(Expr) ||
928239462Sdim      isa<ObjCProtocolExpr>(Expr) ||
929239462Sdim      isa<MemberExpr>(Expr) ||
930239462Sdim      isa<ObjCIvarRefExpr>(Expr) ||
931239462Sdim      isa<ParenExpr>(FullExpr) ||
932239462Sdim      isa<ParenListExpr>(Expr) ||
933239462Sdim      isa<SizeOfPackExpr>(Expr) ||
934239462Sdim      isa<UnaryOperator>(Expr))
935239462Sdim    return false;
936239462Sdim
937239462Sdim  return true;
938239462Sdim}
939239462Sdim
940239462Sdimstatic void objectifyExpr(const Expr *E, Commit &commit) {
941239462Sdim  if (!E) return;
942239462Sdim
943239462Sdim  QualType T = E->getType();
944239462Sdim  if (T->isObjCObjectPointerType()) {
945239462Sdim    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
946239462Sdim      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
947239462Sdim        return;
948239462Sdim    } else {
949239462Sdim      return;
950239462Sdim    }
951239462Sdim  } else if (!T->isPointerType()) {
952239462Sdim    return;
953239462Sdim  }
954239462Sdim
955239462Sdim  SourceRange Range = E->getSourceRange();
956239462Sdim  if (castOperatorNeedsParens(E))
957239462Sdim    commit.insertWrap("(", Range, ")");
958239462Sdim  commit.insertBefore(Range.getBegin(), "(id)");
959239462Sdim}
960239462Sdim
961239462Sdim//===----------------------------------------------------------------------===//
962239462Sdim// rewriteToNumericBoxedExpression.
963239462Sdim//===----------------------------------------------------------------------===//
964239462Sdim
965239462Sdimstatic bool isEnumConstant(const Expr *E) {
966239462Sdim  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
967239462Sdim    if (const ValueDecl *VD = DRE->getDecl())
968239462Sdim      return isa<EnumConstantDecl>(VD);
969239462Sdim
970239462Sdim  return false;
971239462Sdim}
972239462Sdim
973239462Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
974239462Sdim                                            const NSAPI &NS, Commit &commit) {
975239462Sdim  if (Msg->getNumArgs() != 1)
976239462Sdim    return false;
977239462Sdim
978239462Sdim  const Expr *Arg = Msg->getArg(0);
979239462Sdim  if (Arg->isTypeDependent())
980239462Sdim    return false;
981239462Sdim
982239462Sdim  ASTContext &Ctx = NS.getASTContext();
983239462Sdim  Selector Sel = Msg->getSelector();
984249423Sdim  Optional<NSAPI::NSNumberLiteralMethodKind>
985239462Sdim    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
986239462Sdim  if (!MKOpt)
987239462Sdim    return false;
988239462Sdim  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
989239462Sdim
990239462Sdim  const Expr *OrigArg = Arg->IgnoreImpCasts();
991239462Sdim  QualType FinalTy = Arg->getType();
992239462Sdim  QualType OrigTy = OrigArg->getType();
993239462Sdim  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
994239462Sdim  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
995239462Sdim
996239462Sdim  bool isTruncated = FinalTySize < OrigTySize;
997239462Sdim  bool needsCast = false;
998239462Sdim
999239462Sdim  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1000239462Sdim    switch (ICE->getCastKind()) {
1001239462Sdim    case CK_LValueToRValue:
1002239462Sdim    case CK_NoOp:
1003239462Sdim    case CK_UserDefinedConversion:
1004239462Sdim      break;
1005239462Sdim
1006239462Sdim    case CK_IntegralCast: {
1007239462Sdim      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1008239462Sdim        break;
1009239462Sdim      // Be more liberal with Integer/UnsignedInteger which are very commonly
1010239462Sdim      // used.
1011239462Sdim      if ((MK == NSAPI::NSNumberWithInteger ||
1012239462Sdim           MK == NSAPI::NSNumberWithUnsignedInteger) &&
1013239462Sdim          !isTruncated) {
1014239462Sdim        if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1015239462Sdim          break;
1016239462Sdim        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1017239462Sdim            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1018239462Sdim          break;
1019239462Sdim      }
1020239462Sdim
1021239462Sdim      needsCast = true;
1022239462Sdim      break;
1023239462Sdim    }
1024239462Sdim
1025239462Sdim    case CK_PointerToBoolean:
1026239462Sdim    case CK_IntegralToBoolean:
1027239462Sdim    case CK_IntegralToFloating:
1028239462Sdim    case CK_FloatingToIntegral:
1029239462Sdim    case CK_FloatingToBoolean:
1030239462Sdim    case CK_FloatingCast:
1031239462Sdim    case CK_FloatingComplexToReal:
1032239462Sdim    case CK_FloatingComplexToBoolean:
1033239462Sdim    case CK_IntegralComplexToReal:
1034239462Sdim    case CK_IntegralComplexToBoolean:
1035239462Sdim    case CK_AtomicToNonAtomic:
1036239462Sdim      needsCast = true;
1037239462Sdim      break;
1038239462Sdim
1039239462Sdim    case CK_Dependent:
1040239462Sdim    case CK_BitCast:
1041239462Sdim    case CK_LValueBitCast:
1042239462Sdim    case CK_BaseToDerived:
1043239462Sdim    case CK_DerivedToBase:
1044239462Sdim    case CK_UncheckedDerivedToBase:
1045239462Sdim    case CK_Dynamic:
1046239462Sdim    case CK_ToUnion:
1047239462Sdim    case CK_ArrayToPointerDecay:
1048239462Sdim    case CK_FunctionToPointerDecay:
1049239462Sdim    case CK_NullToPointer:
1050239462Sdim    case CK_NullToMemberPointer:
1051239462Sdim    case CK_BaseToDerivedMemberPointer:
1052239462Sdim    case CK_DerivedToBaseMemberPointer:
1053239462Sdim    case CK_MemberPointerToBoolean:
1054239462Sdim    case CK_ReinterpretMemberPointer:
1055239462Sdim    case CK_ConstructorConversion:
1056239462Sdim    case CK_IntegralToPointer:
1057239462Sdim    case CK_PointerToIntegral:
1058239462Sdim    case CK_ToVoid:
1059239462Sdim    case CK_VectorSplat:
1060239462Sdim    case CK_CPointerToObjCPointerCast:
1061239462Sdim    case CK_BlockPointerToObjCPointerCast:
1062239462Sdim    case CK_AnyPointerToBlockPointerCast:
1063239462Sdim    case CK_ObjCObjectLValueCast:
1064239462Sdim    case CK_FloatingRealToComplex:
1065239462Sdim    case CK_FloatingComplexCast:
1066239462Sdim    case CK_FloatingComplexToIntegralComplex:
1067239462Sdim    case CK_IntegralRealToComplex:
1068239462Sdim    case CK_IntegralComplexCast:
1069239462Sdim    case CK_IntegralComplexToFloatingComplex:
1070239462Sdim    case CK_ARCProduceObject:
1071239462Sdim    case CK_ARCConsumeObject:
1072239462Sdim    case CK_ARCReclaimReturnedObject:
1073239462Sdim    case CK_ARCExtendBlockObject:
1074239462Sdim    case CK_NonAtomicToAtomic:
1075239462Sdim    case CK_CopyAndAutoreleaseBlockObject:
1076243830Sdim    case CK_BuiltinFnToFnPtr:
1077249423Sdim    case CK_ZeroToOCLEvent:
1078239462Sdim      return false;
1079239462Sdim    }
1080239462Sdim  }
1081239462Sdim
1082239462Sdim  if (needsCast) {
1083239462Sdim    DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1084239462Sdim    // FIXME: Use a custom category name to distinguish migration diagnostics.
1085239462Sdim    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1086239462Sdim                       "converting to boxing syntax requires casting %0 to %1");
1087239462Sdim    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1088239462Sdim        << Msg->getSourceRange();
1089239462Sdim    return false;
1090239462Sdim  }
1091239462Sdim
1092239462Sdim  SourceRange ArgRange = OrigArg->getSourceRange();
1093239462Sdim  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1094239462Sdim
1095239462Sdim  if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1096239462Sdim    commit.insertBefore(ArgRange.getBegin(), "@");
1097239462Sdim  else
1098239462Sdim    commit.insertWrap("@(", ArgRange, ")");
1099239462Sdim
1100239462Sdim  return true;
1101239462Sdim}
1102239462Sdim
1103239462Sdim//===----------------------------------------------------------------------===//
1104239462Sdim// rewriteToStringBoxedExpression.
1105239462Sdim//===----------------------------------------------------------------------===//
1106239462Sdim
1107239462Sdimstatic bool doRewriteToUTF8StringBoxedExpressionHelper(
1108239462Sdim                                              const ObjCMessageExpr *Msg,
1109239462Sdim                                              const NSAPI &NS, Commit &commit) {
1110239462Sdim  const Expr *Arg = Msg->getArg(0);
1111239462Sdim  if (Arg->isTypeDependent())
1112239462Sdim    return false;
1113239462Sdim
1114239462Sdim  ASTContext &Ctx = NS.getASTContext();
1115239462Sdim
1116239462Sdim  const Expr *OrigArg = Arg->IgnoreImpCasts();
1117239462Sdim  QualType OrigTy = OrigArg->getType();
1118239462Sdim  if (OrigTy->isArrayType())
1119239462Sdim    OrigTy = Ctx.getArrayDecayedType(OrigTy);
1120239462Sdim
1121239462Sdim  if (const StringLiteral *
1122239462Sdim        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1123239462Sdim    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1124239462Sdim    commit.insert(StrE->getLocStart(), "@");
1125239462Sdim    return true;
1126239462Sdim  }
1127239462Sdim
1128239462Sdim  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1129239462Sdim    QualType PointeeType = PT->getPointeeType();
1130239462Sdim    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1131239462Sdim      SourceRange ArgRange = OrigArg->getSourceRange();
1132239462Sdim      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1133239462Sdim
1134239462Sdim      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1135239462Sdim        commit.insertBefore(ArgRange.getBegin(), "@");
1136239462Sdim      else
1137239462Sdim        commit.insertWrap("@(", ArgRange, ")");
1138239462Sdim
1139239462Sdim      return true;
1140239462Sdim    }
1141239462Sdim  }
1142239462Sdim
1143239462Sdim  return false;
1144239462Sdim}
1145239462Sdim
1146239462Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1147239462Sdim                                           const NSAPI &NS, Commit &commit) {
1148239462Sdim  Selector Sel = Msg->getSelector();
1149239462Sdim
1150239462Sdim  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1151239462Sdim      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
1152239462Sdim    if (Msg->getNumArgs() != 1)
1153239462Sdim      return false;
1154239462Sdim    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1155239462Sdim  }
1156239462Sdim
1157239462Sdim  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1158239462Sdim    if (Msg->getNumArgs() != 2)
1159239462Sdim      return false;
1160239462Sdim
1161239462Sdim    const Expr *encodingArg = Msg->getArg(1);
1162239462Sdim    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1163239462Sdim        NS.isNSASCIIStringEncodingConstant(encodingArg))
1164239462Sdim      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1165239462Sdim  }
1166239462Sdim
1167239462Sdim  return false;
1168239462Sdim}
1169