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