RewriteObjCFoundationAPI.cpp revision c2abbe0b98ec21c0c0e844c20d037639ef284664
1//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Rewrites legacy method calls to modern syntax.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Edit/Rewriters.h"
15#include "clang/Edit/Commit.h"
16#include "clang/Lex/Lexer.h"
17#include "clang/AST/ASTContext.h"
18#include "clang/AST/ExprObjC.h"
19#include "clang/AST/ExprCXX.h"
20#include "clang/AST/NSAPI.h"
21
22using namespace clang;
23using namespace edit;
24
25static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
26                                    IdentifierInfo *&ClassId,
27                                    const LangOptions &LangOpts) {
28  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
29    return false;
30
31  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
32  if (!Receiver)
33    return false;
34  ClassId = Receiver->getIdentifier();
35
36  if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
37    return true;
38
39  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
40  // since the change from +1 to +0 will be handled fine by ARC.
41  if (LangOpts.ObjCAutoRefCount) {
42    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
43      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
44                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
45        if (Rec->getMethodFamily() == OMF_alloc)
46          return true;
47      }
48    }
49  }
50
51  return false;
52}
53
54//===----------------------------------------------------------------------===//
55// rewriteObjCRedundantCallWithLiteral.
56//===----------------------------------------------------------------------===//
57
58bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
59                                              const NSAPI &NS, Commit &commit) {
60  IdentifierInfo *II = 0;
61  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
62    return false;
63  if (Msg->getNumArgs() != 1)
64    return false;
65
66  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
67  Selector Sel = Msg->getSelector();
68
69  if ((isa<ObjCStringLiteral>(Arg) &&
70       NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
71       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
72        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
73
74      (isa<ObjCArrayLiteral>(Arg) &&
75       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
76       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
77        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
78
79      (isa<ObjCDictionaryLiteral>(Arg) &&
80       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
81       (NS.getNSDictionarySelector(
82                              NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
83        NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
84
85    commit.replaceWithInner(Msg->getSourceRange(),
86                           Msg->getArg(0)->getSourceRange());
87    return true;
88  }
89
90  return false;
91}
92
93//===----------------------------------------------------------------------===//
94// rewriteToObjCSubscriptSyntax.
95//===----------------------------------------------------------------------===//
96
97static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *IFace,
98                                        Selector subscriptSel) {
99  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
100    if (!MD->isUnavailable())
101      return true;
102  }
103  return false;
104}
105
106static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
107
108static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
109  if (subscriptOperatorNeedsParens(Receiver)) {
110    SourceRange RecRange = Receiver->getSourceRange();
111    commit.insertWrap("(", RecRange, ")");
112  }
113}
114
115static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
116                                        Commit &commit) {
117  if (Msg->getNumArgs() != 1)
118    return false;
119  const Expr *Rec = Msg->getInstanceReceiver();
120  if (!Rec)
121    return false;
122
123  SourceRange MsgRange = Msg->getSourceRange();
124  SourceRange RecRange = Rec->getSourceRange();
125  SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
126
127  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
128                                                       ArgRange.getBegin()),
129                         CharSourceRange::getTokenRange(RecRange));
130  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
131                         ArgRange);
132  commit.insertWrap("[", ArgRange, "]");
133  maybePutParensOnReceiver(Rec, commit);
134  return true;
135}
136
137static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
138                                       const ObjCMessageExpr *Msg,
139                                       const NSAPI &NS,
140                                       Commit &commit) {
141  if (!canRewriteToSubscriptSyntax(IFace,
142                                   NS.getObjectAtIndexedSubscriptSelector()))
143    return false;
144  return rewriteToSubscriptGetCommon(Msg, commit);
145}
146
147static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
148                                            const ObjCMessageExpr *Msg,
149                                            const NSAPI &NS,
150                                            Commit &commit) {
151  if (!canRewriteToSubscriptSyntax(IFace,
152                                  NS.getObjectForKeyedSubscriptSelector()))
153    return false;
154  return rewriteToSubscriptGetCommon(Msg, commit);
155}
156
157static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
158                                       const ObjCMessageExpr *Msg,
159                                       const NSAPI &NS,
160                                       Commit &commit) {
161  if (!canRewriteToSubscriptSyntax(IFace,
162                                   NS.getSetObjectAtIndexedSubscriptSelector()))
163    return false;
164
165  if (Msg->getNumArgs() != 2)
166    return false;
167  const Expr *Rec = Msg->getInstanceReceiver();
168  if (!Rec)
169    return false;
170
171  SourceRange MsgRange = Msg->getSourceRange();
172  SourceRange RecRange = Rec->getSourceRange();
173  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
174  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
175
176  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
177                                                       Arg0Range.getBegin()),
178                         CharSourceRange::getTokenRange(RecRange));
179  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
180                                                       Arg1Range.getBegin()),
181                         CharSourceRange::getTokenRange(Arg0Range));
182  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
183                         Arg1Range);
184  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
185                                                       Arg1Range.getBegin()),
186                    "] = ");
187  maybePutParensOnReceiver(Rec, commit);
188  return true;
189}
190
191static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
192                                            const ObjCMessageExpr *Msg,
193                                            const NSAPI &NS,
194                                            Commit &commit) {
195  if (!canRewriteToSubscriptSyntax(IFace,
196                                   NS.getSetObjectForKeyedSubscriptSelector()))
197    return false;
198
199  if (Msg->getNumArgs() != 2)
200    return false;
201  const Expr *Rec = Msg->getInstanceReceiver();
202  if (!Rec)
203    return false;
204
205  SourceRange MsgRange = Msg->getSourceRange();
206  SourceRange RecRange = Rec->getSourceRange();
207  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
208  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
209
210  SourceLocation LocBeforeVal = Arg0Range.getBegin();
211  commit.insertBefore(LocBeforeVal, "] = ");
212  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
213                         /*beforePreviousInsertions=*/true);
214  commit.insertBefore(LocBeforeVal, "[");
215  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
216                                                       Arg0Range.getBegin()),
217                         CharSourceRange::getTokenRange(RecRange));
218  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
219                         Arg0Range);
220  maybePutParensOnReceiver(Rec, commit);
221  return true;
222}
223
224bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
225                                        const NSAPI &NS, Commit &commit) {
226  if (!Msg || Msg->isImplicit() ||
227      Msg->getReceiverKind() != ObjCMessageExpr::Instance)
228    return false;
229  const ObjCMethodDecl *Method = Msg->getMethodDecl();
230  if (!Method)
231    return false;
232
233  const ObjCInterfaceDecl *
234    IFace = NS.getASTContext().getObjContainingInterface(
235                                          const_cast<ObjCMethodDecl *>(Method));
236  if (!IFace)
237    return false;
238  Selector Sel = Msg->getSelector();
239
240  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
241    return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
242
243  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
244    return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
245
246  if (Msg->getNumArgs() != 2)
247    return false;
248
249  if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
250    return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
251
252  if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
253    return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
254
255  return false;
256}
257
258//===----------------------------------------------------------------------===//
259// rewriteToObjCLiteralSyntax.
260//===----------------------------------------------------------------------===//
261
262static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
263                                  const NSAPI &NS, Commit &commit);
264static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
265                                  const NSAPI &NS, Commit &commit);
266static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
267                                  const NSAPI &NS, Commit &commit);
268static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
269                                            const NSAPI &NS, Commit &commit);
270static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
271                                           const NSAPI &NS, Commit &commit);
272
273bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
274                                      const NSAPI &NS, Commit &commit) {
275  IdentifierInfo *II = 0;
276  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
277    return false;
278
279  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
280    return rewriteToArrayLiteral(Msg, NS, commit);
281  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
282    return rewriteToDictionaryLiteral(Msg, NS, commit);
283  if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
284    return rewriteToNumberLiteral(Msg, NS, commit);
285  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
286    return rewriteToStringBoxedExpression(Msg, NS, commit);
287
288  return false;
289}
290
291//===----------------------------------------------------------------------===//
292// rewriteToArrayLiteral.
293//===----------------------------------------------------------------------===//
294
295/// \brief Adds an explicit cast to 'id' if the type is not objc object.
296static void objectifyExpr(const Expr *E, Commit &commit);
297
298static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
299                                  const NSAPI &NS, Commit &commit) {
300  Selector Sel = Msg->getSelector();
301  SourceRange MsgRange = Msg->getSourceRange();
302
303  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
304    if (Msg->getNumArgs() != 0)
305      return false;
306    commit.replace(MsgRange, "@[]");
307    return true;
308  }
309
310  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
311    if (Msg->getNumArgs() != 1)
312      return false;
313    objectifyExpr(Msg->getArg(0), commit);
314    SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
315    commit.replaceWithInner(MsgRange, ArgRange);
316    commit.insertWrap("@[", ArgRange, "]");
317    return true;
318  }
319
320  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
321      Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
322    if (Msg->getNumArgs() == 0)
323      return false;
324    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
325    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
326      return false;
327
328    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
329      objectifyExpr(Msg->getArg(i), commit);
330
331    if (Msg->getNumArgs() == 1) {
332      commit.replace(MsgRange, "@[]");
333      return true;
334    }
335    SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
336                         Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
337    commit.replaceWithInner(MsgRange, ArgRange);
338    commit.insertWrap("@[", ArgRange, "]");
339    return true;
340  }
341
342  return false;
343}
344
345//===----------------------------------------------------------------------===//
346// rewriteToDictionaryLiteral.
347//===----------------------------------------------------------------------===//
348
349static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
350                                       const NSAPI &NS, Commit &commit) {
351  Selector Sel = Msg->getSelector();
352  SourceRange MsgRange = Msg->getSourceRange();
353
354  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
355    if (Msg->getNumArgs() != 0)
356      return false;
357    commit.replace(MsgRange, "@{}");
358    return true;
359  }
360
361  if (Sel == NS.getNSDictionarySelector(
362                                    NSAPI::NSDict_dictionaryWithObjectForKey)) {
363    if (Msg->getNumArgs() != 2)
364      return false;
365
366    objectifyExpr(Msg->getArg(0), commit);
367    objectifyExpr(Msg->getArg(1), commit);
368
369    SourceRange ValRange = Msg->getArg(0)->getSourceRange();
370    SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
371    // Insert key before the value.
372    commit.insertBefore(ValRange.getBegin(), ": ");
373    commit.insertFromRange(ValRange.getBegin(),
374                           CharSourceRange::getTokenRange(KeyRange),
375                       /*afterToken=*/false, /*beforePreviousInsertions=*/true);
376    commit.insertBefore(ValRange.getBegin(), "@{");
377    commit.insertAfterToken(ValRange.getEnd(), "}");
378    commit.replaceWithInner(MsgRange, ValRange);
379    return true;
380  }
381
382  if (Sel == NS.getNSDictionarySelector(
383                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
384      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
385    if (Msg->getNumArgs() % 2 != 1)
386      return false;
387    unsigned SentinelIdx = Msg->getNumArgs() - 1;
388    const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
389    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
390      return false;
391
392    if (Msg->getNumArgs() == 1) {
393      commit.replace(MsgRange, "@{}");
394      return true;
395    }
396
397    for (unsigned i = 0; i < SentinelIdx; i += 2) {
398      objectifyExpr(Msg->getArg(i), commit);
399      objectifyExpr(Msg->getArg(i+1), commit);
400
401      SourceRange ValRange = Msg->getArg(i)->getSourceRange();
402      SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
403      // Insert value after key.
404      commit.insertAfterToken(KeyRange.getEnd(), ": ");
405      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
406      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
407                                                  KeyRange.getBegin()));
408    }
409    // Range of arguments up until and including the last key.
410    // The sentinel and first value are cut off, the value will move after the
411    // key.
412    SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
413                         Msg->getArg(SentinelIdx-1)->getLocEnd());
414    commit.insertWrap("@{", ArgRange, "}");
415    commit.replaceWithInner(MsgRange, ArgRange);
416    return true;
417  }
418
419  return false;
420}
421
422//===----------------------------------------------------------------------===//
423// rewriteToNumberLiteral.
424//===----------------------------------------------------------------------===//
425
426static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
427                                   const CharacterLiteral *Arg,
428                                   const NSAPI &NS, Commit &commit) {
429  if (Arg->getKind() != CharacterLiteral::Ascii)
430    return false;
431  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
432                                   Msg->getSelector())) {
433    SourceRange ArgRange = Arg->getSourceRange();
434    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
435    commit.insert(ArgRange.getBegin(), "@");
436    return true;
437  }
438
439  return rewriteToNumericBoxedExpression(Msg, NS, commit);
440}
441
442static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
443                                   const Expr *Arg,
444                                   const NSAPI &NS, Commit &commit) {
445  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
446                                   Msg->getSelector())) {
447    SourceRange ArgRange = Arg->getSourceRange();
448    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
449    commit.insert(ArgRange.getBegin(), "@");
450    return true;
451  }
452
453  return rewriteToNumericBoxedExpression(Msg, NS, commit);
454}
455
456namespace {
457
458struct LiteralInfo {
459  bool Hex, Octal;
460  StringRef U, F, L, LL;
461  CharSourceRange WithoutSuffRange;
462};
463
464}
465
466static bool getLiteralInfo(SourceRange literalRange,
467                           bool isFloat, bool isIntZero,
468                          ASTContext &Ctx, LiteralInfo &Info) {
469  if (literalRange.getBegin().isMacroID() ||
470      literalRange.getEnd().isMacroID())
471    return false;
472  StringRef text = Lexer::getSourceText(
473                                  CharSourceRange::getTokenRange(literalRange),
474                                  Ctx.getSourceManager(), Ctx.getLangOpts());
475  if (text.empty())
476    return false;
477
478  llvm::Optional<bool> UpperU, UpperL;
479  bool UpperF = false;
480
481  struct Suff {
482    static bool has(StringRef suff, StringRef &text) {
483      if (text.endswith(suff)) {
484        text = text.substr(0, text.size()-suff.size());
485        return true;
486      }
487      return false;
488    }
489  };
490
491  while (1) {
492    if (Suff::has("u", text)) {
493      UpperU = false;
494    } else if (Suff::has("U", text)) {
495      UpperU = true;
496    } else if (Suff::has("ll", text)) {
497      UpperL = false;
498    } else if (Suff::has("LL", text)) {
499      UpperL = true;
500    } else if (Suff::has("l", text)) {
501      UpperL = false;
502    } else if (Suff::has("L", text)) {
503      UpperL = true;
504    } else if (isFloat && Suff::has("f", text)) {
505      UpperF = false;
506    } else if (isFloat && Suff::has("F", text)) {
507      UpperF = true;
508    } else
509      break;
510  }
511
512  if (!UpperU.hasValue() && !UpperL.hasValue())
513    UpperU = UpperL = true;
514  else if (UpperU.hasValue() && !UpperL.hasValue())
515    UpperL = UpperU;
516  else if (UpperL.hasValue() && !UpperU.hasValue())
517    UpperU = UpperL;
518
519  Info.U = *UpperU ? "U" : "u";
520  Info.L = *UpperL ? "L" : "l";
521  Info.LL = *UpperL ? "LL" : "ll";
522  Info.F = UpperF ? "F" : "f";
523
524  Info.Hex = Info.Octal = false;
525  if (text.startswith("0x"))
526    Info.Hex = true;
527  else if (!isFloat && !isIntZero && text.startswith("0"))
528    Info.Octal = true;
529
530  SourceLocation B = literalRange.getBegin();
531  Info.WithoutSuffRange =
532      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
533  return true;
534}
535
536static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
537                                   const NSAPI &NS, Commit &commit) {
538  if (Msg->getNumArgs() != 1)
539    return false;
540
541  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
542  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
543    return rewriteToCharLiteral(Msg, CharE, NS, commit);
544  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
545    return rewriteToBoolLiteral(Msg, BE, NS, commit);
546  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
547    return rewriteToBoolLiteral(Msg, BE, NS, commit);
548
549  const Expr *literalE = Arg;
550  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
551    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
552      literalE = UOE->getSubExpr();
553  }
554
555  // Only integer and floating literals, otherwise try to rewrite to boxed
556  // expression.
557  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
558    return rewriteToNumericBoxedExpression(Msg, NS, commit);
559
560  ASTContext &Ctx = NS.getASTContext();
561  Selector Sel = Msg->getSelector();
562  llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
563    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
564  if (!MKOpt)
565    return false;
566  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
567
568  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
569  bool CallIsFloating = false, CallIsDouble = false;
570
571  switch (MK) {
572  // We cannot have these calls with int/float literals.
573  case NSAPI::NSNumberWithChar:
574  case NSAPI::NSNumberWithUnsignedChar:
575  case NSAPI::NSNumberWithShort:
576  case NSAPI::NSNumberWithUnsignedShort:
577  case NSAPI::NSNumberWithBool:
578    return rewriteToNumericBoxedExpression(Msg, NS, commit);
579
580  case NSAPI::NSNumberWithUnsignedInt:
581  case NSAPI::NSNumberWithUnsignedInteger:
582    CallIsUnsigned = true;
583  case NSAPI::NSNumberWithInt:
584  case NSAPI::NSNumberWithInteger:
585    break;
586
587  case NSAPI::NSNumberWithUnsignedLong:
588    CallIsUnsigned = true;
589  case NSAPI::NSNumberWithLong:
590    CallIsLong = true;
591    break;
592
593  case NSAPI::NSNumberWithUnsignedLongLong:
594    CallIsUnsigned = true;
595  case NSAPI::NSNumberWithLongLong:
596    CallIsLongLong = true;
597    break;
598
599  case NSAPI::NSNumberWithDouble:
600    CallIsDouble = true;
601  case NSAPI::NSNumberWithFloat:
602    CallIsFloating = true;
603    break;
604  }
605
606  SourceRange ArgRange = Arg->getSourceRange();
607  QualType ArgTy = Arg->getType();
608  QualType CallTy = Msg->getArg(0)->getType();
609
610  // Check for the easy case, the literal maps directly to the call.
611  if (Ctx.hasSameType(ArgTy, CallTy)) {
612    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
613    commit.insert(ArgRange.getBegin(), "@");
614    return true;
615  }
616
617  // We will need to modify the literal suffix to get the same type as the call.
618  // Try with boxed expression if it came from a macro.
619  if (ArgRange.getBegin().isMacroID())
620    return rewriteToNumericBoxedExpression(Msg, NS, commit);
621
622  bool LitIsFloat = ArgTy->isFloatingType();
623  // For a float passed to integer call, don't try rewriting to objc literal.
624  // It is difficult and a very uncommon case anyway.
625  // But try with boxed expression.
626  if (LitIsFloat && !CallIsFloating)
627    return rewriteToNumericBoxedExpression(Msg, NS, commit);
628
629  // Try to modify the literal make it the same type as the method call.
630  // -Modify the suffix, and/or
631  // -Change integer to float
632
633  LiteralInfo LitInfo;
634  bool isIntZero = false;
635  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
636    isIntZero = !IntE->getValue().getBoolValue();
637  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
638    return rewriteToNumericBoxedExpression(Msg, NS, commit);
639
640  // Not easy to do int -> float with hex/octal and uncommon anyway.
641  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
642    return rewriteToNumericBoxedExpression(Msg, NS, commit);
643
644  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
645  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
646
647  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
648                         LitInfo.WithoutSuffRange);
649  commit.insert(LitB, "@");
650
651  if (!LitIsFloat && CallIsFloating)
652    commit.insert(LitE, ".0");
653
654  if (CallIsFloating) {
655    if (!CallIsDouble)
656      commit.insert(LitE, LitInfo.F);
657  } else {
658    if (CallIsUnsigned)
659      commit.insert(LitE, LitInfo.U);
660
661    if (CallIsLong)
662      commit.insert(LitE, LitInfo.L);
663    else if (CallIsLongLong)
664      commit.insert(LitE, LitInfo.LL);
665  }
666  return true;
667}
668
669// FIXME: Make determination of operator precedence more general and
670// make it broadly available.
671static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
672  const Expr* Expr = FullExpr->IgnoreImpCasts();
673  if (isa<ArraySubscriptExpr>(Expr) ||
674      isa<CallExpr>(Expr) ||
675      isa<DeclRefExpr>(Expr) ||
676      isa<CXXNamedCastExpr>(Expr) ||
677      isa<CXXConstructExpr>(Expr) ||
678      isa<CXXThisExpr>(Expr) ||
679      isa<CXXTypeidExpr>(Expr) ||
680      isa<CXXUnresolvedConstructExpr>(Expr) ||
681      isa<ObjCMessageExpr>(Expr) ||
682      isa<ObjCPropertyRefExpr>(Expr) ||
683      isa<ObjCProtocolExpr>(Expr) ||
684      isa<MemberExpr>(Expr) ||
685      isa<ObjCIvarRefExpr>(Expr) ||
686      isa<ParenExpr>(FullExpr) ||
687      isa<ParenListExpr>(Expr) ||
688      isa<SizeOfPackExpr>(Expr))
689    return false;
690
691  return true;
692}
693static bool castOperatorNeedsParens(const Expr *FullExpr) {
694  const Expr* Expr = FullExpr->IgnoreImpCasts();
695  if (isa<ArraySubscriptExpr>(Expr) ||
696      isa<CallExpr>(Expr) ||
697      isa<DeclRefExpr>(Expr) ||
698      isa<CastExpr>(Expr) ||
699      isa<CXXNewExpr>(Expr) ||
700      isa<CXXConstructExpr>(Expr) ||
701      isa<CXXDeleteExpr>(Expr) ||
702      isa<CXXNoexceptExpr>(Expr) ||
703      isa<CXXPseudoDestructorExpr>(Expr) ||
704      isa<CXXScalarValueInitExpr>(Expr) ||
705      isa<CXXThisExpr>(Expr) ||
706      isa<CXXTypeidExpr>(Expr) ||
707      isa<CXXUnresolvedConstructExpr>(Expr) ||
708      isa<ObjCMessageExpr>(Expr) ||
709      isa<ObjCPropertyRefExpr>(Expr) ||
710      isa<ObjCProtocolExpr>(Expr) ||
711      isa<MemberExpr>(Expr) ||
712      isa<ObjCIvarRefExpr>(Expr) ||
713      isa<ParenExpr>(FullExpr) ||
714      isa<ParenListExpr>(Expr) ||
715      isa<SizeOfPackExpr>(Expr) ||
716      isa<UnaryOperator>(Expr))
717    return false;
718
719  return true;
720}
721
722static void objectifyExpr(const Expr *E, Commit &commit) {
723  if (!E) return;
724
725  QualType T = E->getType();
726  if (T->isObjCObjectPointerType()) {
727    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
728      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
729        return;
730    } else {
731      return;
732    }
733  } else if (!T->isPointerType()) {
734    return;
735  }
736
737  SourceRange Range = E->getSourceRange();
738  if (castOperatorNeedsParens(E))
739    commit.insertWrap("(", Range, ")");
740  commit.insertBefore(Range.getBegin(), "(id)");
741}
742
743//===----------------------------------------------------------------------===//
744// rewriteToNumericBoxedExpression.
745//===----------------------------------------------------------------------===//
746
747static bool isEnumConstant(const Expr *E) {
748  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
749    if (const ValueDecl *VD = DRE->getDecl())
750      return isa<EnumConstantDecl>(VD);
751
752  return false;
753}
754
755static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
756                                            const NSAPI &NS, Commit &commit) {
757  if (Msg->getNumArgs() != 1)
758    return false;
759
760  const Expr *Arg = Msg->getArg(0);
761  if (Arg->isTypeDependent())
762    return false;
763
764  ASTContext &Ctx = NS.getASTContext();
765  Selector Sel = Msg->getSelector();
766  llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
767    MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
768  if (!MKOpt)
769    return false;
770  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
771
772  const Expr *OrigArg = Arg->IgnoreImpCasts();
773  QualType FinalTy = Arg->getType();
774  QualType OrigTy = OrigArg->getType();
775  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
776  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
777
778  bool isTruncated = FinalTySize < OrigTySize;
779  bool needsCast = false;
780
781  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
782    switch (ICE->getCastKind()) {
783    case CK_LValueToRValue:
784    case CK_NoOp:
785    case CK_UserDefinedConversion:
786      break;
787
788    case CK_IntegralCast: {
789      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
790        break;
791      // Be more liberal with Integer/UnsignedInteger which are very commonly
792      // used.
793      if ((MK == NSAPI::NSNumberWithInteger ||
794           MK == NSAPI::NSNumberWithUnsignedInteger) &&
795          !isTruncated) {
796        if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
797          break;
798        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
799            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
800          break;
801      }
802
803      needsCast = true;
804      break;
805    }
806
807    case CK_PointerToBoolean:
808    case CK_IntegralToBoolean:
809    case CK_IntegralToFloating:
810    case CK_FloatingToIntegral:
811    case CK_FloatingToBoolean:
812    case CK_FloatingCast:
813    case CK_FloatingComplexToReal:
814    case CK_FloatingComplexToBoolean:
815    case CK_IntegralComplexToReal:
816    case CK_IntegralComplexToBoolean:
817    case CK_AtomicToNonAtomic:
818      needsCast = true;
819      break;
820
821    case CK_Dependent:
822    case CK_BitCast:
823    case CK_LValueBitCast:
824    case CK_BaseToDerived:
825    case CK_DerivedToBase:
826    case CK_UncheckedDerivedToBase:
827    case CK_Dynamic:
828    case CK_ToUnion:
829    case CK_ArrayToPointerDecay:
830    case CK_FunctionToPointerDecay:
831    case CK_NullToPointer:
832    case CK_NullToMemberPointer:
833    case CK_BaseToDerivedMemberPointer:
834    case CK_DerivedToBaseMemberPointer:
835    case CK_MemberPointerToBoolean:
836    case CK_ReinterpretMemberPointer:
837    case CK_ConstructorConversion:
838    case CK_IntegralToPointer:
839    case CK_PointerToIntegral:
840    case CK_ToVoid:
841    case CK_VectorSplat:
842    case CK_CPointerToObjCPointerCast:
843    case CK_BlockPointerToObjCPointerCast:
844    case CK_AnyPointerToBlockPointerCast:
845    case CK_ObjCObjectLValueCast:
846    case CK_FloatingRealToComplex:
847    case CK_FloatingComplexCast:
848    case CK_FloatingComplexToIntegralComplex:
849    case CK_IntegralRealToComplex:
850    case CK_IntegralComplexCast:
851    case CK_IntegralComplexToFloatingComplex:
852    case CK_ARCProduceObject:
853    case CK_ARCConsumeObject:
854    case CK_ARCReclaimReturnedObject:
855    case CK_ARCExtendBlockObject:
856    case CK_NonAtomicToAtomic:
857    case CK_CopyAndAutoreleaseBlockObject:
858      return false;
859    }
860  }
861
862  if (needsCast) {
863    DiagnosticsEngine &Diags = Ctx.getDiagnostics();
864    // FIXME: Use a custom category name to distinguish migration diagnostics.
865    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
866                       "converting to boxing syntax requires casting %0 to %1");
867    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
868        << Msg->getSourceRange();
869    return false;
870  }
871
872  SourceRange ArgRange = OrigArg->getSourceRange();
873  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
874
875  if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
876    commit.insertBefore(ArgRange.getBegin(), "@");
877  else
878    commit.insertWrap("@(", ArgRange, ")");
879
880  return true;
881}
882
883//===----------------------------------------------------------------------===//
884// rewriteToStringBoxedExpression.
885//===----------------------------------------------------------------------===//
886
887static bool doRewriteToUTF8StringBoxedExpressionHelper(
888                                              const ObjCMessageExpr *Msg,
889                                              const NSAPI &NS, Commit &commit) {
890  const Expr *Arg = Msg->getArg(0);
891  if (Arg->isTypeDependent())
892    return false;
893
894  ASTContext &Ctx = NS.getASTContext();
895
896  const Expr *OrigArg = Arg->IgnoreImpCasts();
897  QualType OrigTy = OrigArg->getType();
898  if (OrigTy->isArrayType())
899    OrigTy = Ctx.getArrayDecayedType(OrigTy);
900
901  if (const StringLiteral *
902        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
903    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
904    commit.insert(StrE->getLocStart(), "@");
905    return true;
906  }
907
908  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
909    QualType PointeeType = PT->getPointeeType();
910    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
911      SourceRange ArgRange = OrigArg->getSourceRange();
912      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
913
914      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
915        commit.insertBefore(ArgRange.getBegin(), "@");
916      else
917        commit.insertWrap("@(", ArgRange, ")");
918
919      return true;
920    }
921  }
922
923  return false;
924}
925
926static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
927                                           const NSAPI &NS, Commit &commit) {
928  Selector Sel = Msg->getSelector();
929
930  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
931      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
932    if (Msg->getNumArgs() != 1)
933      return false;
934    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
935  }
936
937  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
938    if (Msg->getNumArgs() != 2)
939      return false;
940
941    const Expr *encodingArg = Msg->getArg(1);
942    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
943        NS.isNSASCIIStringEncodingConstant(encodingArg))
944      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
945  }
946
947  return false;
948}
949