RewriteObjCFoundationAPI.cpp revision 245431
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"
15234287Sdim#include "clang/Edit/Commit.h"
16234287Sdim#include "clang/Lex/Lexer.h"
17245431Sdim#include "clang/AST/ASTContext.h"
18234287Sdim#include "clang/AST/ExprObjC.h"
19234287Sdim#include "clang/AST/ExprCXX.h"
20234287Sdim#include "clang/AST/NSAPI.h"
21234287Sdim
22234287Sdimusing namespace clang;
23234287Sdimusing namespace edit;
24234287Sdim
25234287Sdimstatic bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
26245431Sdim                                    IdentifierInfo *&ClassId,
27245431Sdim                                    const LangOptions &LangOpts) {
28234287Sdim  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
29234287Sdim    return false;
30234287Sdim
31234287Sdim  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
32234287Sdim  if (!Receiver)
33234287Sdim    return false;
34234287Sdim  ClassId = Receiver->getIdentifier();
35234287Sdim
36234287Sdim  if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
37234287Sdim    return true;
38234287Sdim
39245431Sdim  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
40245431Sdim  // since the change from +1 to +0 will be handled fine by ARC.
41245431Sdim  if (LangOpts.ObjCAutoRefCount) {
42245431Sdim    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
43245431Sdim      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
44245431Sdim                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
45245431Sdim        if (Rec->getMethodFamily() == OMF_alloc)
46245431Sdim          return true;
47245431Sdim      }
48245431Sdim    }
49245431Sdim  }
50245431Sdim
51234287Sdim  return false;
52234287Sdim}
53234287Sdim
54234287Sdim//===----------------------------------------------------------------------===//
55234287Sdim// rewriteObjCRedundantCallWithLiteral.
56234287Sdim//===----------------------------------------------------------------------===//
57234287Sdim
58234287Sdimbool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
59234287Sdim                                              const NSAPI &NS, Commit &commit) {
60234287Sdim  IdentifierInfo *II = 0;
61245431Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
62234287Sdim    return false;
63234287Sdim  if (Msg->getNumArgs() != 1)
64234287Sdim    return false;
65234287Sdim
66234287Sdim  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
67234287Sdim  Selector Sel = Msg->getSelector();
68234287Sdim
69234287Sdim  if ((isa<ObjCStringLiteral>(Arg) &&
70234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
71245431Sdim       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72245431Sdim        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
73234287Sdim
74234287Sdim      (isa<ObjCArrayLiteral>(Arg) &&
75234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
76245431Sdim       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77245431Sdim        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
78234287Sdim
79234287Sdim      (isa<ObjCDictionaryLiteral>(Arg) &&
80234287Sdim       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
81245431Sdim       (NS.getNSDictionarySelector(
82245431Sdim                              NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83245431Sdim        NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
84234287Sdim
85234287Sdim    commit.replaceWithInner(Msg->getSourceRange(),
86234287Sdim                           Msg->getArg(0)->getSourceRange());
87234287Sdim    return true;
88234287Sdim  }
89234287Sdim
90234287Sdim  return false;
91234287Sdim}
92234287Sdim
93234287Sdim//===----------------------------------------------------------------------===//
94234287Sdim// rewriteToObjCSubscriptSyntax.
95234287Sdim//===----------------------------------------------------------------------===//
96234287Sdim
97245431Sdim/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
98245431Sdim/// that the migrator handles) but return their instances as 'id', resulting
99245431Sdim/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
100245431Sdim///
101245431Sdim/// When checking if we can convert to subscripting syntax, check whether
102245431Sdim/// the receiver is a result of a class method from a hardcoded list of
103245431Sdim/// such classes. In such a case return the specific class as the interface
104245431Sdim/// of the receiver.
105245431Sdim///
106245431Sdim/// FIXME: Remove this when these classes start using 'instancetype'.
107245431Sdimstatic const ObjCInterfaceDecl *
108245431SdimmaybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
109245431Sdim                                         const Expr *Receiver,
110245431Sdim                                         ASTContext &Ctx) {
111245431Sdim  assert(IFace && Receiver);
112245431Sdim
113245431Sdim  // If the receiver has type 'id'...
114245431Sdim  if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
115245431Sdim    return IFace;
116245431Sdim
117245431Sdim  const ObjCMessageExpr *
118245431Sdim    InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
119245431Sdim  if (!InnerMsg)
120245431Sdim    return IFace;
121245431Sdim
122245431Sdim  QualType ClassRec;
123245431Sdim  switch (InnerMsg->getReceiverKind()) {
124245431Sdim  case ObjCMessageExpr::Instance:
125245431Sdim  case ObjCMessageExpr::SuperInstance:
126245431Sdim    return IFace;
127245431Sdim
128245431Sdim  case ObjCMessageExpr::Class:
129245431Sdim    ClassRec = InnerMsg->getClassReceiver();
130245431Sdim    break;
131245431Sdim  case ObjCMessageExpr::SuperClass:
132245431Sdim    ClassRec = InnerMsg->getSuperType();
133245431Sdim    break;
134245431Sdim  }
135245431Sdim
136245431Sdim  if (ClassRec.isNull())
137245431Sdim    return IFace;
138245431Sdim
139245431Sdim  // ...and it is the result of a class message...
140245431Sdim
141245431Sdim  const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
142245431Sdim  if (!ObjTy)
143245431Sdim    return IFace;
144245431Sdim  const ObjCInterfaceDecl *OID = ObjTy->getInterface();
145245431Sdim
146245431Sdim  // ...and the receiving class is NSMapTable or NSLocale, return that
147245431Sdim  // class as the receiving interface.
148245431Sdim  if (OID->getName() == "NSMapTable" ||
149245431Sdim      OID->getName() == "NSLocale")
150245431Sdim    return OID;
151245431Sdim
152245431Sdim  return IFace;
153245431Sdim}
154245431Sdim
155245431Sdimstatic bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
156245431Sdim                                        const ObjCMessageExpr *Msg,
157245431Sdim                                        ASTContext &Ctx,
158245431Sdim                                        Selector subscriptSel) {
159245431Sdim  const Expr *Rec = Msg->getInstanceReceiver();
160245431Sdim  if (!Rec)
161245431Sdim    return false;
162245431Sdim  IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
163245431Sdim
164245431Sdim  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
165245431Sdim    if (!MD->isUnavailable())
166245431Sdim      return true;
167245431Sdim  }
168245431Sdim  return false;
169245431Sdim}
170245431Sdim
171245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr);
172245431Sdim
173234287Sdimstatic void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
174245431Sdim  if (subscriptOperatorNeedsParens(Receiver)) {
175234287Sdim    SourceRange RecRange = Receiver->getSourceRange();
176234287Sdim    commit.insertWrap("(", RecRange, ")");
177234287Sdim  }
178234287Sdim}
179234287Sdim
180245431Sdimstatic bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
181245431Sdim                                        Commit &commit) {
182234287Sdim  if (Msg->getNumArgs() != 1)
183234287Sdim    return false;
184234287Sdim  const Expr *Rec = Msg->getInstanceReceiver();
185234287Sdim  if (!Rec)
186234287Sdim    return false;
187234287Sdim
188234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
189234287Sdim  SourceRange RecRange = Rec->getSourceRange();
190234287Sdim  SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
191234287Sdim
192234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
193234287Sdim                                                       ArgRange.getBegin()),
194234287Sdim                         CharSourceRange::getTokenRange(RecRange));
195234287Sdim  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
196234287Sdim                         ArgRange);
197234287Sdim  commit.insertWrap("[", ArgRange, "]");
198234287Sdim  maybePutParensOnReceiver(Rec, commit);
199234287Sdim  return true;
200234287Sdim}
201234287Sdim
202245431Sdimstatic bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
203245431Sdim                                       const ObjCMessageExpr *Msg,
204245431Sdim                                       const NSAPI &NS,
205234287Sdim                                       Commit &commit) {
206245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
207245431Sdim                                   NS.getObjectAtIndexedSubscriptSelector()))
208245431Sdim    return false;
209245431Sdim  return rewriteToSubscriptGetCommon(Msg, commit);
210245431Sdim}
211245431Sdim
212245431Sdimstatic bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
213245431Sdim                                            const ObjCMessageExpr *Msg,
214245431Sdim                                            const NSAPI &NS,
215245431Sdim                                            Commit &commit) {
216245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
217245431Sdim                                  NS.getObjectForKeyedSubscriptSelector()))
218245431Sdim    return false;
219245431Sdim  return rewriteToSubscriptGetCommon(Msg, commit);
220245431Sdim}
221245431Sdim
222245431Sdimstatic bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
223245431Sdim                                       const ObjCMessageExpr *Msg,
224245431Sdim                                       const NSAPI &NS,
225245431Sdim                                       Commit &commit) {
226245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
227245431Sdim                                   NS.getSetObjectAtIndexedSubscriptSelector()))
228245431Sdim    return false;
229245431Sdim
230234287Sdim  if (Msg->getNumArgs() != 2)
231234287Sdim    return false;
232234287Sdim  const Expr *Rec = Msg->getInstanceReceiver();
233234287Sdim  if (!Rec)
234234287Sdim    return false;
235234287Sdim
236234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
237234287Sdim  SourceRange RecRange = Rec->getSourceRange();
238234287Sdim  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
239234287Sdim  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
240234287Sdim
241234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
242234287Sdim                                                       Arg0Range.getBegin()),
243234287Sdim                         CharSourceRange::getTokenRange(RecRange));
244234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
245234287Sdim                                                       Arg1Range.getBegin()),
246234287Sdim                         CharSourceRange::getTokenRange(Arg0Range));
247234287Sdim  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
248234287Sdim                         Arg1Range);
249234287Sdim  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
250234287Sdim                                                       Arg1Range.getBegin()),
251234287Sdim                    "] = ");
252234287Sdim  maybePutParensOnReceiver(Rec, commit);
253234287Sdim  return true;
254234287Sdim}
255234287Sdim
256245431Sdimstatic bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
257245431Sdim                                            const ObjCMessageExpr *Msg,
258245431Sdim                                            const NSAPI &NS,
259234287Sdim                                            Commit &commit) {
260245431Sdim  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
261245431Sdim                                   NS.getSetObjectForKeyedSubscriptSelector()))
262245431Sdim    return false;
263245431Sdim
264234287Sdim  if (Msg->getNumArgs() != 2)
265234287Sdim    return false;
266234287Sdim  const Expr *Rec = Msg->getInstanceReceiver();
267234287Sdim  if (!Rec)
268234287Sdim    return false;
269234287Sdim
270234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
271234287Sdim  SourceRange RecRange = Rec->getSourceRange();
272234287Sdim  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
273234287Sdim  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
274234287Sdim
275234287Sdim  SourceLocation LocBeforeVal = Arg0Range.getBegin();
276234287Sdim  commit.insertBefore(LocBeforeVal, "] = ");
277234287Sdim  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
278234287Sdim                         /*beforePreviousInsertions=*/true);
279234287Sdim  commit.insertBefore(LocBeforeVal, "[");
280234287Sdim  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
281234287Sdim                                                       Arg0Range.getBegin()),
282234287Sdim                         CharSourceRange::getTokenRange(RecRange));
283234287Sdim  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
284234287Sdim                         Arg0Range);
285234287Sdim  maybePutParensOnReceiver(Rec, commit);
286234287Sdim  return true;
287234287Sdim}
288234287Sdim
289234287Sdimbool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
290245431Sdim                                        const NSAPI &NS, Commit &commit) {
291234287Sdim  if (!Msg || Msg->isImplicit() ||
292234287Sdim      Msg->getReceiverKind() != ObjCMessageExpr::Instance)
293234287Sdim    return false;
294234287Sdim  const ObjCMethodDecl *Method = Msg->getMethodDecl();
295234287Sdim  if (!Method)
296234287Sdim    return false;
297234287Sdim
298234287Sdim  const ObjCInterfaceDecl *
299234287Sdim    IFace = NS.getASTContext().getObjContainingInterface(
300234287Sdim                                          const_cast<ObjCMethodDecl *>(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,
328234287Sdim                                  const NSAPI &NS, Commit &commit);
329234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
330234287Sdim                                  const NSAPI &NS, Commit &commit);
331234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
332234287Sdim                                  const NSAPI &NS, Commit &commit);
333245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
334245431Sdim                                            const NSAPI &NS, Commit &commit);
335245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
336245431Sdim                                           const NSAPI &NS, Commit &commit);
337234287Sdim
338234287Sdimbool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
339234287Sdim                                      const NSAPI &NS, Commit &commit) {
340234287Sdim  IdentifierInfo *II = 0;
341245431Sdim  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
342234287Sdim    return false;
343234287Sdim
344234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
345234287Sdim    return rewriteToArrayLiteral(Msg, NS, commit);
346234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
347234287Sdim    return rewriteToDictionaryLiteral(Msg, NS, commit);
348234287Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
349234287Sdim    return rewriteToNumberLiteral(Msg, NS, commit);
350245431Sdim  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
351245431Sdim    return rewriteToStringBoxedExpression(Msg, NS, commit);
352234287Sdim
353234287Sdim  return false;
354234287Sdim}
355234287Sdim
356234287Sdim//===----------------------------------------------------------------------===//
357234287Sdim// rewriteToArrayLiteral.
358234287Sdim//===----------------------------------------------------------------------===//
359234287Sdim
360245431Sdim/// \brief Adds an explicit cast to 'id' if the type is not objc object.
361245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit);
362245431Sdim
363234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
364234287Sdim                                  const NSAPI &NS, Commit &commit) {
365234287Sdim  Selector Sel = Msg->getSelector();
366234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
367234287Sdim
368234287Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
369234287Sdim    if (Msg->getNumArgs() != 0)
370234287Sdim      return false;
371234287Sdim    commit.replace(MsgRange, "@[]");
372234287Sdim    return true;
373234287Sdim  }
374234287Sdim
375234287Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
376234287Sdim    if (Msg->getNumArgs() != 1)
377234287Sdim      return false;
378245431Sdim    objectifyExpr(Msg->getArg(0), commit);
379234287Sdim    SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
380234287Sdim    commit.replaceWithInner(MsgRange, ArgRange);
381234287Sdim    commit.insertWrap("@[", ArgRange, "]");
382234287Sdim    return true;
383234287Sdim  }
384234287Sdim
385245431Sdim  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
386245431Sdim      Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
387234287Sdim    if (Msg->getNumArgs() == 0)
388234287Sdim      return false;
389234287Sdim    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
390234287Sdim    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
391234287Sdim      return false;
392234287Sdim
393245431Sdim    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
394245431Sdim      objectifyExpr(Msg->getArg(i), commit);
395245431Sdim
396234287Sdim    if (Msg->getNumArgs() == 1) {
397234287Sdim      commit.replace(MsgRange, "@[]");
398234287Sdim      return true;
399234287Sdim    }
400234287Sdim    SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
401234287Sdim                         Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
402234287Sdim    commit.replaceWithInner(MsgRange, ArgRange);
403234287Sdim    commit.insertWrap("@[", ArgRange, "]");
404234287Sdim    return true;
405234287Sdim  }
406234287Sdim
407234287Sdim  return false;
408234287Sdim}
409234287Sdim
410234287Sdim//===----------------------------------------------------------------------===//
411234287Sdim// rewriteToDictionaryLiteral.
412234287Sdim//===----------------------------------------------------------------------===//
413234287Sdim
414234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
415234287Sdim                                       const NSAPI &NS, Commit &commit) {
416234287Sdim  Selector Sel = Msg->getSelector();
417234287Sdim  SourceRange MsgRange = Msg->getSourceRange();
418234287Sdim
419234287Sdim  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
420234287Sdim    if (Msg->getNumArgs() != 0)
421234287Sdim      return false;
422234287Sdim    commit.replace(MsgRange, "@{}");
423234287Sdim    return true;
424234287Sdim  }
425234287Sdim
426234287Sdim  if (Sel == NS.getNSDictionarySelector(
427234287Sdim                                    NSAPI::NSDict_dictionaryWithObjectForKey)) {
428234287Sdim    if (Msg->getNumArgs() != 2)
429234287Sdim      return false;
430245431Sdim
431245431Sdim    objectifyExpr(Msg->getArg(0), commit);
432245431Sdim    objectifyExpr(Msg->getArg(1), commit);
433245431Sdim
434234287Sdim    SourceRange ValRange = Msg->getArg(0)->getSourceRange();
435234287Sdim    SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
436234287Sdim    // Insert key before the value.
437234287Sdim    commit.insertBefore(ValRange.getBegin(), ": ");
438234287Sdim    commit.insertFromRange(ValRange.getBegin(),
439234287Sdim                           CharSourceRange::getTokenRange(KeyRange),
440234287Sdim                       /*afterToken=*/false, /*beforePreviousInsertions=*/true);
441234287Sdim    commit.insertBefore(ValRange.getBegin(), "@{");
442234287Sdim    commit.insertAfterToken(ValRange.getEnd(), "}");
443234287Sdim    commit.replaceWithInner(MsgRange, ValRange);
444234287Sdim    return true;
445234287Sdim  }
446234287Sdim
447234287Sdim  if (Sel == NS.getNSDictionarySelector(
448245431Sdim                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
449245431Sdim      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
450234287Sdim    if (Msg->getNumArgs() % 2 != 1)
451234287Sdim      return false;
452234287Sdim    unsigned SentinelIdx = Msg->getNumArgs() - 1;
453234287Sdim    const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
454234287Sdim    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
455234287Sdim      return false;
456234287Sdim
457234287Sdim    if (Msg->getNumArgs() == 1) {
458234287Sdim      commit.replace(MsgRange, "@{}");
459234287Sdim      return true;
460234287Sdim    }
461234287Sdim
462234287Sdim    for (unsigned i = 0; i < SentinelIdx; i += 2) {
463245431Sdim      objectifyExpr(Msg->getArg(i), commit);
464245431Sdim      objectifyExpr(Msg->getArg(i+1), commit);
465245431Sdim
466234287Sdim      SourceRange ValRange = Msg->getArg(i)->getSourceRange();
467234287Sdim      SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
468234287Sdim      // Insert value after key.
469234287Sdim      commit.insertAfterToken(KeyRange.getEnd(), ": ");
470234287Sdim      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
471234287Sdim      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
472234287Sdim                                                  KeyRange.getBegin()));
473234287Sdim    }
474234287Sdim    // Range of arguments up until and including the last key.
475234287Sdim    // The sentinel and first value are cut off, the value will move after the
476234287Sdim    // key.
477234287Sdim    SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
478234287Sdim                         Msg->getArg(SentinelIdx-1)->getLocEnd());
479234287Sdim    commit.insertWrap("@{", ArgRange, "}");
480234287Sdim    commit.replaceWithInner(MsgRange, ArgRange);
481234287Sdim    return true;
482234287Sdim  }
483234287Sdim
484234287Sdim  return false;
485234287Sdim}
486234287Sdim
487234287Sdim//===----------------------------------------------------------------------===//
488234287Sdim// rewriteToNumberLiteral.
489234287Sdim//===----------------------------------------------------------------------===//
490234287Sdim
491234287Sdimstatic bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
492234287Sdim                                   const CharacterLiteral *Arg,
493234287Sdim                                   const NSAPI &NS, Commit &commit) {
494234287Sdim  if (Arg->getKind() != CharacterLiteral::Ascii)
495234287Sdim    return false;
496234287Sdim  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
497234287Sdim                                   Msg->getSelector())) {
498234287Sdim    SourceRange ArgRange = Arg->getSourceRange();
499234287Sdim    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
500234287Sdim    commit.insert(ArgRange.getBegin(), "@");
501234287Sdim    return true;
502234287Sdim  }
503234287Sdim
504245431Sdim  return rewriteToNumericBoxedExpression(Msg, NS, commit);
505234287Sdim}
506234287Sdim
507234287Sdimstatic bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
508234287Sdim                                   const Expr *Arg,
509234287Sdim                                   const NSAPI &NS, Commit &commit) {
510234287Sdim  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
511234287Sdim                                   Msg->getSelector())) {
512234287Sdim    SourceRange ArgRange = Arg->getSourceRange();
513234287Sdim    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
514234287Sdim    commit.insert(ArgRange.getBegin(), "@");
515234287Sdim    return true;
516234287Sdim  }
517234287Sdim
518245431Sdim  return rewriteToNumericBoxedExpression(Msg, NS, commit);
519234287Sdim}
520234287Sdim
521234287Sdimnamespace {
522234287Sdim
523234287Sdimstruct LiteralInfo {
524234287Sdim  bool Hex, Octal;
525234287Sdim  StringRef U, F, L, LL;
526234287Sdim  CharSourceRange WithoutSuffRange;
527234287Sdim};
528234287Sdim
529234287Sdim}
530234287Sdim
531234287Sdimstatic bool getLiteralInfo(SourceRange literalRange,
532234287Sdim                           bool isFloat, bool isIntZero,
533234287Sdim                          ASTContext &Ctx, LiteralInfo &Info) {
534234287Sdim  if (literalRange.getBegin().isMacroID() ||
535234287Sdim      literalRange.getEnd().isMacroID())
536234287Sdim    return false;
537234287Sdim  StringRef text = Lexer::getSourceText(
538234287Sdim                                  CharSourceRange::getTokenRange(literalRange),
539234287Sdim                                  Ctx.getSourceManager(), Ctx.getLangOpts());
540234287Sdim  if (text.empty())
541234287Sdim    return false;
542234287Sdim
543234287Sdim  llvm::Optional<bool> UpperU, UpperL;
544234287Sdim  bool UpperF = false;
545234287Sdim
546234287Sdim  struct Suff {
547234287Sdim    static bool has(StringRef suff, StringRef &text) {
548234287Sdim      if (text.endswith(suff)) {
549234287Sdim        text = text.substr(0, text.size()-suff.size());
550234287Sdim        return true;
551234287Sdim      }
552234287Sdim      return false;
553234287Sdim    }
554234287Sdim  };
555234287Sdim
556234287Sdim  while (1) {
557234287Sdim    if (Suff::has("u", text)) {
558234287Sdim      UpperU = false;
559234287Sdim    } else if (Suff::has("U", text)) {
560234287Sdim      UpperU = true;
561234287Sdim    } else if (Suff::has("ll", text)) {
562234287Sdim      UpperL = false;
563234287Sdim    } else if (Suff::has("LL", text)) {
564234287Sdim      UpperL = true;
565234287Sdim    } else if (Suff::has("l", text)) {
566234287Sdim      UpperL = false;
567234287Sdim    } else if (Suff::has("L", text)) {
568234287Sdim      UpperL = true;
569234287Sdim    } else if (isFloat && Suff::has("f", text)) {
570234287Sdim      UpperF = false;
571234287Sdim    } else if (isFloat && Suff::has("F", text)) {
572234287Sdim      UpperF = true;
573234287Sdim    } else
574234287Sdim      break;
575234287Sdim  }
576234287Sdim
577234287Sdim  if (!UpperU.hasValue() && !UpperL.hasValue())
578234287Sdim    UpperU = UpperL = true;
579234287Sdim  else if (UpperU.hasValue() && !UpperL.hasValue())
580234287Sdim    UpperL = UpperU;
581234287Sdim  else if (UpperL.hasValue() && !UpperU.hasValue())
582234287Sdim    UpperU = UpperL;
583234287Sdim
584234287Sdim  Info.U = *UpperU ? "U" : "u";
585234287Sdim  Info.L = *UpperL ? "L" : "l";
586234287Sdim  Info.LL = *UpperL ? "LL" : "ll";
587234287Sdim  Info.F = UpperF ? "F" : "f";
588234287Sdim
589234287Sdim  Info.Hex = Info.Octal = false;
590234287Sdim  if (text.startswith("0x"))
591234287Sdim    Info.Hex = true;
592234287Sdim  else if (!isFloat && !isIntZero && text.startswith("0"))
593234287Sdim    Info.Octal = true;
594234287Sdim
595234287Sdim  SourceLocation B = literalRange.getBegin();
596234287Sdim  Info.WithoutSuffRange =
597234287Sdim      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
598234287Sdim  return true;
599234287Sdim}
600234287Sdim
601234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
602234287Sdim                                   const NSAPI &NS, Commit &commit) {
603234287Sdim  if (Msg->getNumArgs() != 1)
604234287Sdim    return false;
605234287Sdim
606234287Sdim  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
607234287Sdim  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
608234287Sdim    return rewriteToCharLiteral(Msg, CharE, NS, commit);
609234287Sdim  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
610234287Sdim    return rewriteToBoolLiteral(Msg, BE, NS, commit);
611234287Sdim  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
612234287Sdim    return rewriteToBoolLiteral(Msg, BE, NS, commit);
613234287Sdim
614234287Sdim  const Expr *literalE = Arg;
615234287Sdim  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
616234287Sdim    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
617234287Sdim      literalE = UOE->getSubExpr();
618234287Sdim  }
619234287Sdim
620245431Sdim  // Only integer and floating literals, otherwise try to rewrite to boxed
621245431Sdim  // expression.
622234287Sdim  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
623245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
624234287Sdim
625234287Sdim  ASTContext &Ctx = NS.getASTContext();
626234287Sdim  Selector Sel = Msg->getSelector();
627234287Sdim  llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
628234287Sdim    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
629234287Sdim  if (!MKOpt)
630234287Sdim    return false;
631234287Sdim  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
632234287Sdim
633234287Sdim  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
634234287Sdim  bool CallIsFloating = false, CallIsDouble = false;
635234287Sdim
636234287Sdim  switch (MK) {
637234287Sdim  // We cannot have these calls with int/float literals.
638234287Sdim  case NSAPI::NSNumberWithChar:
639234287Sdim  case NSAPI::NSNumberWithUnsignedChar:
640234287Sdim  case NSAPI::NSNumberWithShort:
641234287Sdim  case NSAPI::NSNumberWithUnsignedShort:
642234287Sdim  case NSAPI::NSNumberWithBool:
643245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
644234287Sdim
645234287Sdim  case NSAPI::NSNumberWithUnsignedInt:
646234287Sdim  case NSAPI::NSNumberWithUnsignedInteger:
647234287Sdim    CallIsUnsigned = true;
648234287Sdim  case NSAPI::NSNumberWithInt:
649234287Sdim  case NSAPI::NSNumberWithInteger:
650234287Sdim    break;
651234287Sdim
652234287Sdim  case NSAPI::NSNumberWithUnsignedLong:
653234287Sdim    CallIsUnsigned = true;
654234287Sdim  case NSAPI::NSNumberWithLong:
655234287Sdim    CallIsLong = true;
656234287Sdim    break;
657234287Sdim
658234287Sdim  case NSAPI::NSNumberWithUnsignedLongLong:
659234287Sdim    CallIsUnsigned = true;
660234287Sdim  case NSAPI::NSNumberWithLongLong:
661234287Sdim    CallIsLongLong = true;
662234287Sdim    break;
663234287Sdim
664234287Sdim  case NSAPI::NSNumberWithDouble:
665234287Sdim    CallIsDouble = true;
666234287Sdim  case NSAPI::NSNumberWithFloat:
667234287Sdim    CallIsFloating = true;
668234287Sdim    break;
669234287Sdim  }
670234287Sdim
671234287Sdim  SourceRange ArgRange = Arg->getSourceRange();
672234287Sdim  QualType ArgTy = Arg->getType();
673234287Sdim  QualType CallTy = Msg->getArg(0)->getType();
674234287Sdim
675234287Sdim  // Check for the easy case, the literal maps directly to the call.
676234287Sdim  if (Ctx.hasSameType(ArgTy, CallTy)) {
677234287Sdim    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
678234287Sdim    commit.insert(ArgRange.getBegin(), "@");
679234287Sdim    return true;
680234287Sdim  }
681234287Sdim
682234287Sdim  // We will need to modify the literal suffix to get the same type as the call.
683245431Sdim  // Try with boxed expression if it came from a macro.
684234287Sdim  if (ArgRange.getBegin().isMacroID())
685245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
686234287Sdim
687234287Sdim  bool LitIsFloat = ArgTy->isFloatingType();
688245431Sdim  // For a float passed to integer call, don't try rewriting to objc literal.
689245431Sdim  // It is difficult and a very uncommon case anyway.
690245431Sdim  // But try with boxed expression.
691234287Sdim  if (LitIsFloat && !CallIsFloating)
692245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
693234287Sdim
694234287Sdim  // Try to modify the literal make it the same type as the method call.
695234287Sdim  // -Modify the suffix, and/or
696234287Sdim  // -Change integer to float
697234287Sdim
698234287Sdim  LiteralInfo LitInfo;
699234287Sdim  bool isIntZero = false;
700234287Sdim  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
701234287Sdim    isIntZero = !IntE->getValue().getBoolValue();
702234287Sdim  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
703245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
704234287Sdim
705234287Sdim  // Not easy to do int -> float with hex/octal and uncommon anyway.
706234287Sdim  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
707245431Sdim    return rewriteToNumericBoxedExpression(Msg, NS, commit);
708234287Sdim
709234287Sdim  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
710234287Sdim  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
711234287Sdim
712234287Sdim  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
713234287Sdim                         LitInfo.WithoutSuffRange);
714234287Sdim  commit.insert(LitB, "@");
715234287Sdim
716234287Sdim  if (!LitIsFloat && CallIsFloating)
717234287Sdim    commit.insert(LitE, ".0");
718234287Sdim
719234287Sdim  if (CallIsFloating) {
720234287Sdim    if (!CallIsDouble)
721234287Sdim      commit.insert(LitE, LitInfo.F);
722234287Sdim  } else {
723234287Sdim    if (CallIsUnsigned)
724234287Sdim      commit.insert(LitE, LitInfo.U);
725234287Sdim
726234287Sdim    if (CallIsLong)
727234287Sdim      commit.insert(LitE, LitInfo.L);
728234287Sdim    else if (CallIsLongLong)
729234287Sdim      commit.insert(LitE, LitInfo.LL);
730234287Sdim  }
731234287Sdim  return true;
732234287Sdim}
733245431Sdim
734245431Sdim// FIXME: Make determination of operator precedence more general and
735245431Sdim// make it broadly available.
736245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
737245431Sdim  const Expr* Expr = FullExpr->IgnoreImpCasts();
738245431Sdim  if (isa<ArraySubscriptExpr>(Expr) ||
739245431Sdim      isa<CallExpr>(Expr) ||
740245431Sdim      isa<DeclRefExpr>(Expr) ||
741245431Sdim      isa<CXXNamedCastExpr>(Expr) ||
742245431Sdim      isa<CXXConstructExpr>(Expr) ||
743245431Sdim      isa<CXXThisExpr>(Expr) ||
744245431Sdim      isa<CXXTypeidExpr>(Expr) ||
745245431Sdim      isa<CXXUnresolvedConstructExpr>(Expr) ||
746245431Sdim      isa<ObjCMessageExpr>(Expr) ||
747245431Sdim      isa<ObjCPropertyRefExpr>(Expr) ||
748245431Sdim      isa<ObjCProtocolExpr>(Expr) ||
749245431Sdim      isa<MemberExpr>(Expr) ||
750245431Sdim      isa<ObjCIvarRefExpr>(Expr) ||
751245431Sdim      isa<ParenExpr>(FullExpr) ||
752245431Sdim      isa<ParenListExpr>(Expr) ||
753245431Sdim      isa<SizeOfPackExpr>(Expr))
754245431Sdim    return false;
755245431Sdim
756245431Sdim  return true;
757245431Sdim}
758245431Sdimstatic bool castOperatorNeedsParens(const Expr *FullExpr) {
759245431Sdim  const Expr* Expr = FullExpr->IgnoreImpCasts();
760245431Sdim  if (isa<ArraySubscriptExpr>(Expr) ||
761245431Sdim      isa<CallExpr>(Expr) ||
762245431Sdim      isa<DeclRefExpr>(Expr) ||
763245431Sdim      isa<CastExpr>(Expr) ||
764245431Sdim      isa<CXXNewExpr>(Expr) ||
765245431Sdim      isa<CXXConstructExpr>(Expr) ||
766245431Sdim      isa<CXXDeleteExpr>(Expr) ||
767245431Sdim      isa<CXXNoexceptExpr>(Expr) ||
768245431Sdim      isa<CXXPseudoDestructorExpr>(Expr) ||
769245431Sdim      isa<CXXScalarValueInitExpr>(Expr) ||
770245431Sdim      isa<CXXThisExpr>(Expr) ||
771245431Sdim      isa<CXXTypeidExpr>(Expr) ||
772245431Sdim      isa<CXXUnresolvedConstructExpr>(Expr) ||
773245431Sdim      isa<ObjCMessageExpr>(Expr) ||
774245431Sdim      isa<ObjCPropertyRefExpr>(Expr) ||
775245431Sdim      isa<ObjCProtocolExpr>(Expr) ||
776245431Sdim      isa<MemberExpr>(Expr) ||
777245431Sdim      isa<ObjCIvarRefExpr>(Expr) ||
778245431Sdim      isa<ParenExpr>(FullExpr) ||
779245431Sdim      isa<ParenListExpr>(Expr) ||
780245431Sdim      isa<SizeOfPackExpr>(Expr) ||
781245431Sdim      isa<UnaryOperator>(Expr))
782245431Sdim    return false;
783245431Sdim
784245431Sdim  return true;
785245431Sdim}
786245431Sdim
787245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit) {
788245431Sdim  if (!E) return;
789245431Sdim
790245431Sdim  QualType T = E->getType();
791245431Sdim  if (T->isObjCObjectPointerType()) {
792245431Sdim    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
793245431Sdim      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
794245431Sdim        return;
795245431Sdim    } else {
796245431Sdim      return;
797245431Sdim    }
798245431Sdim  } else if (!T->isPointerType()) {
799245431Sdim    return;
800245431Sdim  }
801245431Sdim
802245431Sdim  SourceRange Range = E->getSourceRange();
803245431Sdim  if (castOperatorNeedsParens(E))
804245431Sdim    commit.insertWrap("(", Range, ")");
805245431Sdim  commit.insertBefore(Range.getBegin(), "(id)");
806245431Sdim}
807245431Sdim
808245431Sdim//===----------------------------------------------------------------------===//
809245431Sdim// rewriteToNumericBoxedExpression.
810245431Sdim//===----------------------------------------------------------------------===//
811245431Sdim
812245431Sdimstatic bool isEnumConstant(const Expr *E) {
813245431Sdim  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
814245431Sdim    if (const ValueDecl *VD = DRE->getDecl())
815245431Sdim      return isa<EnumConstantDecl>(VD);
816245431Sdim
817245431Sdim  return false;
818245431Sdim}
819245431Sdim
820245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
821245431Sdim                                            const NSAPI &NS, Commit &commit) {
822245431Sdim  if (Msg->getNumArgs() != 1)
823245431Sdim    return false;
824245431Sdim
825245431Sdim  const Expr *Arg = Msg->getArg(0);
826245431Sdim  if (Arg->isTypeDependent())
827245431Sdim    return false;
828245431Sdim
829245431Sdim  ASTContext &Ctx = NS.getASTContext();
830245431Sdim  Selector Sel = Msg->getSelector();
831245431Sdim  llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
832245431Sdim    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
833245431Sdim  if (!MKOpt)
834245431Sdim    return false;
835245431Sdim  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
836245431Sdim
837245431Sdim  const Expr *OrigArg = Arg->IgnoreImpCasts();
838245431Sdim  QualType FinalTy = Arg->getType();
839245431Sdim  QualType OrigTy = OrigArg->getType();
840245431Sdim  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
841245431Sdim  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
842245431Sdim
843245431Sdim  bool isTruncated = FinalTySize < OrigTySize;
844245431Sdim  bool needsCast = false;
845245431Sdim
846245431Sdim  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
847245431Sdim    switch (ICE->getCastKind()) {
848245431Sdim    case CK_LValueToRValue:
849245431Sdim    case CK_NoOp:
850245431Sdim    case CK_UserDefinedConversion:
851245431Sdim      break;
852245431Sdim
853245431Sdim    case CK_IntegralCast: {
854245431Sdim      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
855245431Sdim        break;
856245431Sdim      // Be more liberal with Integer/UnsignedInteger which are very commonly
857245431Sdim      // used.
858245431Sdim      if ((MK == NSAPI::NSNumberWithInteger ||
859245431Sdim           MK == NSAPI::NSNumberWithUnsignedInteger) &&
860245431Sdim          !isTruncated) {
861245431Sdim        if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
862245431Sdim          break;
863245431Sdim        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
864245431Sdim            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
865245431Sdim          break;
866245431Sdim      }
867245431Sdim
868245431Sdim      needsCast = true;
869245431Sdim      break;
870245431Sdim    }
871245431Sdim
872245431Sdim    case CK_PointerToBoolean:
873245431Sdim    case CK_IntegralToBoolean:
874245431Sdim    case CK_IntegralToFloating:
875245431Sdim    case CK_FloatingToIntegral:
876245431Sdim    case CK_FloatingToBoolean:
877245431Sdim    case CK_FloatingCast:
878245431Sdim    case CK_FloatingComplexToReal:
879245431Sdim    case CK_FloatingComplexToBoolean:
880245431Sdim    case CK_IntegralComplexToReal:
881245431Sdim    case CK_IntegralComplexToBoolean:
882245431Sdim    case CK_AtomicToNonAtomic:
883245431Sdim      needsCast = true;
884245431Sdim      break;
885245431Sdim
886245431Sdim    case CK_Dependent:
887245431Sdim    case CK_BitCast:
888245431Sdim    case CK_LValueBitCast:
889245431Sdim    case CK_BaseToDerived:
890245431Sdim    case CK_DerivedToBase:
891245431Sdim    case CK_UncheckedDerivedToBase:
892245431Sdim    case CK_Dynamic:
893245431Sdim    case CK_ToUnion:
894245431Sdim    case CK_ArrayToPointerDecay:
895245431Sdim    case CK_FunctionToPointerDecay:
896245431Sdim    case CK_NullToPointer:
897245431Sdim    case CK_NullToMemberPointer:
898245431Sdim    case CK_BaseToDerivedMemberPointer:
899245431Sdim    case CK_DerivedToBaseMemberPointer:
900245431Sdim    case CK_MemberPointerToBoolean:
901245431Sdim    case CK_ReinterpretMemberPointer:
902245431Sdim    case CK_ConstructorConversion:
903245431Sdim    case CK_IntegralToPointer:
904245431Sdim    case CK_PointerToIntegral:
905245431Sdim    case CK_ToVoid:
906245431Sdim    case CK_VectorSplat:
907245431Sdim    case CK_CPointerToObjCPointerCast:
908245431Sdim    case CK_BlockPointerToObjCPointerCast:
909245431Sdim    case CK_AnyPointerToBlockPointerCast:
910245431Sdim    case CK_ObjCObjectLValueCast:
911245431Sdim    case CK_FloatingRealToComplex:
912245431Sdim    case CK_FloatingComplexCast:
913245431Sdim    case CK_FloatingComplexToIntegralComplex:
914245431Sdim    case CK_IntegralRealToComplex:
915245431Sdim    case CK_IntegralComplexCast:
916245431Sdim    case CK_IntegralComplexToFloatingComplex:
917245431Sdim    case CK_ARCProduceObject:
918245431Sdim    case CK_ARCConsumeObject:
919245431Sdim    case CK_ARCReclaimReturnedObject:
920245431Sdim    case CK_ARCExtendBlockObject:
921245431Sdim    case CK_NonAtomicToAtomic:
922245431Sdim    case CK_CopyAndAutoreleaseBlockObject:
923245431Sdim    case CK_BuiltinFnToFnPtr:
924245431Sdim      return false;
925245431Sdim    }
926245431Sdim  }
927245431Sdim
928245431Sdim  if (needsCast) {
929245431Sdim    DiagnosticsEngine &Diags = Ctx.getDiagnostics();
930245431Sdim    // FIXME: Use a custom category name to distinguish migration diagnostics.
931245431Sdim    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
932245431Sdim                       "converting to boxing syntax requires casting %0 to %1");
933245431Sdim    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
934245431Sdim        << Msg->getSourceRange();
935245431Sdim    return false;
936245431Sdim  }
937245431Sdim
938245431Sdim  SourceRange ArgRange = OrigArg->getSourceRange();
939245431Sdim  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
940245431Sdim
941245431Sdim  if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
942245431Sdim    commit.insertBefore(ArgRange.getBegin(), "@");
943245431Sdim  else
944245431Sdim    commit.insertWrap("@(", ArgRange, ")");
945245431Sdim
946245431Sdim  return true;
947245431Sdim}
948245431Sdim
949245431Sdim//===----------------------------------------------------------------------===//
950245431Sdim// rewriteToStringBoxedExpression.
951245431Sdim//===----------------------------------------------------------------------===//
952245431Sdim
953245431Sdimstatic bool doRewriteToUTF8StringBoxedExpressionHelper(
954245431Sdim                                              const ObjCMessageExpr *Msg,
955245431Sdim                                              const NSAPI &NS, Commit &commit) {
956245431Sdim  const Expr *Arg = Msg->getArg(0);
957245431Sdim  if (Arg->isTypeDependent())
958245431Sdim    return false;
959245431Sdim
960245431Sdim  ASTContext &Ctx = NS.getASTContext();
961245431Sdim
962245431Sdim  const Expr *OrigArg = Arg->IgnoreImpCasts();
963245431Sdim  QualType OrigTy = OrigArg->getType();
964245431Sdim  if (OrigTy->isArrayType())
965245431Sdim    OrigTy = Ctx.getArrayDecayedType(OrigTy);
966245431Sdim
967245431Sdim  if (const StringLiteral *
968245431Sdim        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
969245431Sdim    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
970245431Sdim    commit.insert(StrE->getLocStart(), "@");
971245431Sdim    return true;
972245431Sdim  }
973245431Sdim
974245431Sdim  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
975245431Sdim    QualType PointeeType = PT->getPointeeType();
976245431Sdim    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
977245431Sdim      SourceRange ArgRange = OrigArg->getSourceRange();
978245431Sdim      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
979245431Sdim
980245431Sdim      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
981245431Sdim        commit.insertBefore(ArgRange.getBegin(), "@");
982245431Sdim      else
983245431Sdim        commit.insertWrap("@(", ArgRange, ")");
984245431Sdim
985245431Sdim      return true;
986245431Sdim    }
987245431Sdim  }
988245431Sdim
989245431Sdim  return false;
990245431Sdim}
991245431Sdim
992245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
993245431Sdim                                           const NSAPI &NS, Commit &commit) {
994245431Sdim  Selector Sel = Msg->getSelector();
995245431Sdim
996245431Sdim  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
997245431Sdim      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
998245431Sdim    if (Msg->getNumArgs() != 1)
999245431Sdim      return false;
1000245431Sdim    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1001245431Sdim  }
1002245431Sdim
1003245431Sdim  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1004245431Sdim    if (Msg->getNumArgs() != 2)
1005245431Sdim      return false;
1006245431Sdim
1007245431Sdim    const Expr *encodingArg = Msg->getArg(1);
1008245431Sdim    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1009245431Sdim        NS.isNSASCIIStringEncodingConstant(encodingArg))
1010245431Sdim      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1011245431Sdim  }
1012245431Sdim
1013245431Sdim  return false;
1014245431Sdim}
1015