Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// Rewrites legacy method calls to modern syntax.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "clang/Edit/Rewriters.h"
14
#include "clang/AST/ASTContext.h"
15
#include "clang/AST/ExprCXX.h"
16
#include "clang/AST/ExprObjC.h"
17
#include "clang/AST/NSAPI.h"
18
#include "clang/AST/ParentMap.h"
19
#include "clang/Edit/Commit.h"
20
#include "clang/Lex/Lexer.h"
21
#include <optional>
22
23
using namespace clang;
24
using namespace edit;
25
26
static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
27
                                    IdentifierInfo *&ClassId,
28
0
                                    const LangOptions &LangOpts) {
29
0
  if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
30
0
    return false;
31
32
0
  const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
33
0
  if (!Receiver)
34
0
    return false;
35
0
  ClassId = Receiver->getIdentifier();
36
37
0
  if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
38
0
    return true;
39
40
  // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
41
  // since the change from +1 to +0 will be handled fine by ARC.
42
0
  if (LangOpts.ObjCAutoRefCount) {
43
0
    if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
44
0
      if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
45
0
                           Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
46
0
        if (Rec->getMethodFamily() == OMF_alloc)
47
0
          return true;
48
0
      }
49
0
    }
50
0
  }
51
52
0
  return false;
53
0
}
54
55
//===----------------------------------------------------------------------===//
56
// rewriteObjCRedundantCallWithLiteral.
57
//===----------------------------------------------------------------------===//
58
59
bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
60
0
                                              const NSAPI &NS, Commit &commit) {
61
0
  IdentifierInfo *II = nullptr;
62
0
  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
63
0
    return false;
64
0
  if (Msg->getNumArgs() != 1)
65
0
    return false;
66
67
0
  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
68
0
  Selector Sel = Msg->getSelector();
69
70
0
  if ((isa<ObjCStringLiteral>(Arg) &&
71
0
       NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
72
0
       (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
73
0
        NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
74
75
0
      (isa<ObjCArrayLiteral>(Arg) &&
76
0
       NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
77
0
       (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
78
0
        NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
79
80
0
      (isa<ObjCDictionaryLiteral>(Arg) &&
81
0
       NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
82
0
       (NS.getNSDictionarySelector(
83
0
                              NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
84
0
        NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
85
86
0
    commit.replaceWithInner(Msg->getSourceRange(),
87
0
                           Msg->getArg(0)->getSourceRange());
88
0
    return true;
89
0
  }
90
91
0
  return false;
92
0
}
93
94
//===----------------------------------------------------------------------===//
95
// rewriteToObjCSubscriptSyntax.
96
//===----------------------------------------------------------------------===//
97
98
/// Check for classes that accept 'objectForKey:' (or the other selectors
99
/// that the migrator handles) but return their instances as 'id', resulting
100
/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
101
///
102
/// When checking if we can convert to subscripting syntax, check whether
103
/// the receiver is a result of a class method from a hardcoded list of
104
/// such classes. In such a case return the specific class as the interface
105
/// of the receiver.
106
///
107
/// FIXME: Remove this when these classes start using 'instancetype'.
108
static const ObjCInterfaceDecl *
109
maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
110
                                         const Expr *Receiver,
111
0
                                         ASTContext &Ctx) {
112
0
  assert(IFace && Receiver);
113
114
  // If the receiver has type 'id'...
115
0
  if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
116
0
    return IFace;
117
118
0
  const ObjCMessageExpr *
119
0
    InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
120
0
  if (!InnerMsg)
121
0
    return IFace;
122
123
0
  QualType ClassRec;
124
0
  switch (InnerMsg->getReceiverKind()) {
125
0
  case ObjCMessageExpr::Instance:
126
0
  case ObjCMessageExpr::SuperInstance:
127
0
    return IFace;
128
129
0
  case ObjCMessageExpr::Class:
130
0
    ClassRec = InnerMsg->getClassReceiver();
131
0
    break;
132
0
  case ObjCMessageExpr::SuperClass:
133
0
    ClassRec = InnerMsg->getSuperType();
134
0
    break;
135
0
  }
136
137
0
  if (ClassRec.isNull())
138
0
    return IFace;
139
140
  // ...and it is the result of a class message...
141
142
0
  const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
143
0
  if (!ObjTy)
144
0
    return IFace;
145
0
  const ObjCInterfaceDecl *OID = ObjTy->getInterface();
146
147
  // ...and the receiving class is NSMapTable or NSLocale, return that
148
  // class as the receiving interface.
149
0
  if (OID->getName() == "NSMapTable" ||
150
0
      OID->getName() == "NSLocale")
151
0
    return OID;
152
153
0
  return IFace;
154
0
}
155
156
static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
157
                                        const ObjCMessageExpr *Msg,
158
                                        ASTContext &Ctx,
159
0
                                        Selector subscriptSel) {
160
0
  const Expr *Rec = Msg->getInstanceReceiver();
161
0
  if (!Rec)
162
0
    return false;
163
0
  IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
164
165
0
  if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
166
0
    if (!MD->isUnavailable())
167
0
      return true;
168
0
  }
169
0
  return false;
170
0
}
171
172
static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
173
174
0
static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
175
0
  if (subscriptOperatorNeedsParens(Receiver)) {
176
0
    SourceRange RecRange = Receiver->getSourceRange();
177
0
    commit.insertWrap("(", RecRange, ")");
178
0
  }
179
0
}
180
181
static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
182
0
                                        Commit &commit) {
183
0
  if (Msg->getNumArgs() != 1)
184
0
    return false;
185
0
  const Expr *Rec = Msg->getInstanceReceiver();
186
0
  if (!Rec)
187
0
    return false;
188
189
0
  SourceRange MsgRange = Msg->getSourceRange();
190
0
  SourceRange RecRange = Rec->getSourceRange();
191
0
  SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
192
193
0
  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
194
0
                                                       ArgRange.getBegin()),
195
0
                         CharSourceRange::getTokenRange(RecRange));
196
0
  commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
197
0
                         ArgRange);
198
0
  commit.insertWrap("[", ArgRange, "]");
199
0
  maybePutParensOnReceiver(Rec, commit);
200
0
  return true;
201
0
}
202
203
static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
204
                                       const ObjCMessageExpr *Msg,
205
                                       const NSAPI &NS,
206
0
                                       Commit &commit) {
207
0
  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
208
0
                                   NS.getObjectAtIndexedSubscriptSelector()))
209
0
    return false;
210
0
  return rewriteToSubscriptGetCommon(Msg, commit);
211
0
}
212
213
static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
214
                                            const ObjCMessageExpr *Msg,
215
                                            const NSAPI &NS,
216
0
                                            Commit &commit) {
217
0
  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
218
0
                                  NS.getObjectForKeyedSubscriptSelector()))
219
0
    return false;
220
0
  return rewriteToSubscriptGetCommon(Msg, commit);
221
0
}
222
223
static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
224
                                       const ObjCMessageExpr *Msg,
225
                                       const NSAPI &NS,
226
0
                                       Commit &commit) {
227
0
  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
228
0
                                   NS.getSetObjectAtIndexedSubscriptSelector()))
229
0
    return false;
230
231
0
  if (Msg->getNumArgs() != 2)
232
0
    return false;
233
0
  const Expr *Rec = Msg->getInstanceReceiver();
234
0
  if (!Rec)
235
0
    return false;
236
237
0
  SourceRange MsgRange = Msg->getSourceRange();
238
0
  SourceRange RecRange = Rec->getSourceRange();
239
0
  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
240
0
  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
241
242
0
  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
243
0
                                                       Arg0Range.getBegin()),
244
0
                         CharSourceRange::getTokenRange(RecRange));
245
0
  commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
246
0
                                                       Arg1Range.getBegin()),
247
0
                         CharSourceRange::getTokenRange(Arg0Range));
248
0
  commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
249
0
                         Arg1Range);
250
0
  commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
251
0
                                                       Arg1Range.getBegin()),
252
0
                    "] = ");
253
0
  maybePutParensOnReceiver(Rec, commit);
254
0
  return true;
255
0
}
256
257
static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
258
                                            const ObjCMessageExpr *Msg,
259
                                            const NSAPI &NS,
260
0
                                            Commit &commit) {
261
0
  if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
262
0
                                   NS.getSetObjectForKeyedSubscriptSelector()))
263
0
    return false;
264
265
0
  if (Msg->getNumArgs() != 2)
266
0
    return false;
267
0
  const Expr *Rec = Msg->getInstanceReceiver();
268
0
  if (!Rec)
269
0
    return false;
270
271
0
  SourceRange MsgRange = Msg->getSourceRange();
272
0
  SourceRange RecRange = Rec->getSourceRange();
273
0
  SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
274
0
  SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
275
276
0
  SourceLocation LocBeforeVal = Arg0Range.getBegin();
277
0
  commit.insertBefore(LocBeforeVal, "] = ");
278
0
  commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
279
0
                         /*beforePreviousInsertions=*/true);
280
0
  commit.insertBefore(LocBeforeVal, "[");
281
0
  commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
282
0
                                                       Arg0Range.getBegin()),
283
0
                         CharSourceRange::getTokenRange(RecRange));
284
0
  commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
285
0
                         Arg0Range);
286
0
  maybePutParensOnReceiver(Rec, commit);
287
0
  return true;
288
0
}
289
290
bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
291
0
                                        const NSAPI &NS, Commit &commit) {
292
0
  if (!Msg || Msg->isImplicit() ||
293
0
      Msg->getReceiverKind() != ObjCMessageExpr::Instance)
294
0
    return false;
295
0
  const ObjCMethodDecl *Method = Msg->getMethodDecl();
296
0
  if (!Method)
297
0
    return false;
298
299
0
  const ObjCInterfaceDecl *IFace =
300
0
      NS.getASTContext().getObjContainingInterface(Method);
301
0
  if (!IFace)
302
0
    return false;
303
0
  Selector Sel = Msg->getSelector();
304
305
0
  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
306
0
    return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
307
308
0
  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
309
0
    return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
310
311
0
  if (Msg->getNumArgs() != 2)
312
0
    return false;
313
314
0
  if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
315
0
    return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
316
317
0
  if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
318
0
    return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
319
320
0
  return false;
321
0
}
322
323
//===----------------------------------------------------------------------===//
324
// rewriteToObjCLiteralSyntax.
325
//===----------------------------------------------------------------------===//
326
327
static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
328
                                  const NSAPI &NS, Commit &commit,
329
                                  const ParentMap *PMap);
330
static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
331
                                  const NSAPI &NS, Commit &commit);
332
static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
333
                                  const NSAPI &NS, Commit &commit);
334
static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
335
                                            const NSAPI &NS, Commit &commit);
336
static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
337
                                           const NSAPI &NS, Commit &commit);
338
339
bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
340
                                      const NSAPI &NS, Commit &commit,
341
0
                                      const ParentMap *PMap) {
342
0
  IdentifierInfo *II = nullptr;
343
0
  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
344
0
    return false;
345
346
0
  if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
347
0
    return rewriteToArrayLiteral(Msg, NS, commit, PMap);
348
0
  if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
349
0
    return rewriteToDictionaryLiteral(Msg, NS, commit);
350
0
  if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
351
0
    return rewriteToNumberLiteral(Msg, NS, commit);
352
0
  if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
353
0
    return rewriteToStringBoxedExpression(Msg, NS, commit);
354
355
0
  return false;
356
0
}
357
358
/// Returns true if the immediate message arguments of \c Msg should not
359
/// be rewritten because it will interfere with the rewrite of the parent
360
/// message expression. e.g.
361
/// \code
362
///   [NSDictionary dictionaryWithObjects:
363
///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
364
///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
365
/// \endcode
366
/// It will return true for this because we are going to rewrite this directly
367
/// to a dictionary literal without any array literals.
368
static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
369
                                                 const NSAPI &NS);
370
371
//===----------------------------------------------------------------------===//
372
// rewriteToArrayLiteral.
373
//===----------------------------------------------------------------------===//
374
375
/// Adds an explicit cast to 'id' if the type is not objc object.
376
static void objectifyExpr(const Expr *E, Commit &commit);
377
378
static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
379
                                  const NSAPI &NS, Commit &commit,
380
0
                                  const ParentMap *PMap) {
381
0
  if (PMap) {
382
0
    const ObjCMessageExpr *ParentMsg =
383
0
        dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
384
0
    if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
385
0
      return false;
386
0
  }
387
388
0
  Selector Sel = Msg->getSelector();
389
0
  SourceRange MsgRange = Msg->getSourceRange();
390
391
0
  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
392
0
    if (Msg->getNumArgs() != 0)
393
0
      return false;
394
0
    commit.replace(MsgRange, "@[]");
395
0
    return true;
396
0
  }
397
398
0
  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
399
0
    if (Msg->getNumArgs() != 1)
400
0
      return false;
401
0
    objectifyExpr(Msg->getArg(0), commit);
402
0
    SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
403
0
    commit.replaceWithInner(MsgRange, ArgRange);
404
0
    commit.insertWrap("@[", ArgRange, "]");
405
0
    return true;
406
0
  }
407
408
0
  if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
409
0
      Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
410
0
    if (Msg->getNumArgs() == 0)
411
0
      return false;
412
0
    const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
413
0
    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
414
0
      return false;
415
416
0
    for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
417
0
      objectifyExpr(Msg->getArg(i), commit);
418
419
0
    if (Msg->getNumArgs() == 1) {
420
0
      commit.replace(MsgRange, "@[]");
421
0
      return true;
422
0
    }
423
0
    SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
424
0
                         Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
425
0
    commit.replaceWithInner(MsgRange, ArgRange);
426
0
    commit.insertWrap("@[", ArgRange, "]");
427
0
    return true;
428
0
  }
429
430
0
  return false;
431
0
}
432
433
//===----------------------------------------------------------------------===//
434
// rewriteToDictionaryLiteral.
435
//===----------------------------------------------------------------------===//
436
437
/// If \c Msg is an NSArray creation message or literal, this gets the
438
/// objects that were used to create it.
439
/// \returns true if it is an NSArray and we got objects, or false otherwise.
440
static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
441
0
                              SmallVectorImpl<const Expr *> &Objs) {
442
0
  if (!E)
443
0
    return false;
444
445
0
  E = E->IgnoreParenCasts();
446
0
  if (!E)
447
0
    return false;
448
449
0
  if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
450
0
    IdentifierInfo *Cls = nullptr;
451
0
    if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
452
0
      return false;
453
454
0
    if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
455
0
      return false;
456
457
0
    Selector Sel = Msg->getSelector();
458
0
    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
459
0
      return true; // empty array.
460
461
0
    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
462
0
      if (Msg->getNumArgs() != 1)
463
0
        return false;
464
0
      Objs.push_back(Msg->getArg(0));
465
0
      return true;
466
0
    }
467
468
0
    if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
469
0
        Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
470
0
      if (Msg->getNumArgs() == 0)
471
0
        return false;
472
0
      const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
473
0
      if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
474
0
        return false;
475
476
0
      for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
477
0
        Objs.push_back(Msg->getArg(i));
478
0
      return true;
479
0
    }
480
481
0
  } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
482
0
    for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
483
0
      Objs.push_back(ArrLit->getElement(i));
484
0
    return true;
485
0
  }
486
487
0
  return false;
488
0
}
489
490
static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
491
0
                                       const NSAPI &NS, Commit &commit) {
492
0
  Selector Sel = Msg->getSelector();
493
0
  SourceRange MsgRange = Msg->getSourceRange();
494
495
0
  if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
496
0
    if (Msg->getNumArgs() != 0)
497
0
      return false;
498
0
    commit.replace(MsgRange, "@{}");
499
0
    return true;
500
0
  }
501
502
0
  if (Sel == NS.getNSDictionarySelector(
503
0
                                    NSAPI::NSDict_dictionaryWithObjectForKey)) {
504
0
    if (Msg->getNumArgs() != 2)
505
0
      return false;
506
507
0
    objectifyExpr(Msg->getArg(0), commit);
508
0
    objectifyExpr(Msg->getArg(1), commit);
509
510
0
    SourceRange ValRange = Msg->getArg(0)->getSourceRange();
511
0
    SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
512
    // Insert key before the value.
513
0
    commit.insertBefore(ValRange.getBegin(), ": ");
514
0
    commit.insertFromRange(ValRange.getBegin(),
515
0
                           CharSourceRange::getTokenRange(KeyRange),
516
0
                       /*afterToken=*/false, /*beforePreviousInsertions=*/true);
517
0
    commit.insertBefore(ValRange.getBegin(), "@{");
518
0
    commit.insertAfterToken(ValRange.getEnd(), "}");
519
0
    commit.replaceWithInner(MsgRange, ValRange);
520
0
    return true;
521
0
  }
522
523
0
  if (Sel == NS.getNSDictionarySelector(
524
0
                                  NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
525
0
      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
526
0
    if (Msg->getNumArgs() % 2 != 1)
527
0
      return false;
528
0
    unsigned SentinelIdx = Msg->getNumArgs() - 1;
529
0
    const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
530
0
    if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
531
0
      return false;
532
533
0
    if (Msg->getNumArgs() == 1) {
534
0
      commit.replace(MsgRange, "@{}");
535
0
      return true;
536
0
    }
537
538
0
    for (unsigned i = 0; i < SentinelIdx; i += 2) {
539
0
      objectifyExpr(Msg->getArg(i), commit);
540
0
      objectifyExpr(Msg->getArg(i+1), commit);
541
542
0
      SourceRange ValRange = Msg->getArg(i)->getSourceRange();
543
0
      SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
544
      // Insert value after key.
545
0
      commit.insertAfterToken(KeyRange.getEnd(), ": ");
546
0
      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
547
0
      commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
548
0
                                                  KeyRange.getBegin()));
549
0
    }
550
    // Range of arguments up until and including the last key.
551
    // The sentinel and first value are cut off, the value will move after the
552
    // key.
553
0
    SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
554
0
                         Msg->getArg(SentinelIdx - 1)->getEndLoc());
555
0
    commit.insertWrap("@{", ArgRange, "}");
556
0
    commit.replaceWithInner(MsgRange, ArgRange);
557
0
    return true;
558
0
  }
559
560
0
  if (Sel == NS.getNSDictionarySelector(
561
0
                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
562
0
      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
563
0
    if (Msg->getNumArgs() != 2)
564
0
      return false;
565
566
0
    SmallVector<const Expr *, 8> Vals;
567
0
    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
568
0
      return false;
569
570
0
    SmallVector<const Expr *, 8> Keys;
571
0
    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
572
0
      return false;
573
574
0
    if (Vals.size() != Keys.size())
575
0
      return false;
576
577
0
    if (Vals.empty()) {
578
0
      commit.replace(MsgRange, "@{}");
579
0
      return true;
580
0
    }
581
582
0
    for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
583
0
      objectifyExpr(Vals[i], commit);
584
0
      objectifyExpr(Keys[i], commit);
585
586
0
      SourceRange ValRange = Vals[i]->getSourceRange();
587
0
      SourceRange KeyRange = Keys[i]->getSourceRange();
588
      // Insert value after key.
589
0
      commit.insertAfterToken(KeyRange.getEnd(), ": ");
590
0
      commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
591
0
    }
592
    // Range of arguments up until and including the last key.
593
    // The first value is cut off, the value will move after the key.
594
0
    SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());
595
0
    commit.insertWrap("@{", ArgRange, "}");
596
0
    commit.replaceWithInner(MsgRange, ArgRange);
597
0
    return true;
598
0
  }
599
600
0
  return false;
601
0
}
602
603
static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
604
0
                                                 const NSAPI &NS) {
605
0
  if (!Msg)
606
0
    return false;
607
608
0
  IdentifierInfo *II = nullptr;
609
0
  if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
610
0
    return false;
611
612
0
  if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
613
0
    return false;
614
615
0
  Selector Sel = Msg->getSelector();
616
0
  if (Sel == NS.getNSDictionarySelector(
617
0
                                  NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
618
0
      Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
619
0
    if (Msg->getNumArgs() != 2)
620
0
      return false;
621
622
0
    SmallVector<const Expr *, 8> Vals;
623
0
    if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
624
0
      return false;
625
626
0
    SmallVector<const Expr *, 8> Keys;
627
0
    if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
628
0
      return false;
629
630
0
    if (Vals.size() != Keys.size())
631
0
      return false;
632
633
0
    return true;
634
0
  }
635
636
0
  return false;
637
0
}
638
639
//===----------------------------------------------------------------------===//
640
// rewriteToNumberLiteral.
641
//===----------------------------------------------------------------------===//
642
643
static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
644
                                   const CharacterLiteral *Arg,
645
0
                                   const NSAPI &NS, Commit &commit) {
646
0
  if (Arg->getKind() != CharacterLiteralKind::Ascii)
647
0
    return false;
648
0
  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
649
0
                                   Msg->getSelector())) {
650
0
    SourceRange ArgRange = Arg->getSourceRange();
651
0
    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
652
0
    commit.insert(ArgRange.getBegin(), "@");
653
0
    return true;
654
0
  }
655
656
0
  return rewriteToNumericBoxedExpression(Msg, NS, commit);
657
0
}
658
659
static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
660
                                   const Expr *Arg,
661
0
                                   const NSAPI &NS, Commit &commit) {
662
0
  if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
663
0
                                   Msg->getSelector())) {
664
0
    SourceRange ArgRange = Arg->getSourceRange();
665
0
    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
666
0
    commit.insert(ArgRange.getBegin(), "@");
667
0
    return true;
668
0
  }
669
670
0
  return rewriteToNumericBoxedExpression(Msg, NS, commit);
671
0
}
672
673
namespace {
674
675
struct LiteralInfo {
676
  bool Hex, Octal;
677
  StringRef U, F, L, LL;
678
  CharSourceRange WithoutSuffRange;
679
};
680
681
}
682
683
static bool getLiteralInfo(SourceRange literalRange,
684
                           bool isFloat, bool isIntZero,
685
0
                          ASTContext &Ctx, LiteralInfo &Info) {
686
0
  if (literalRange.getBegin().isMacroID() ||
687
0
      literalRange.getEnd().isMacroID())
688
0
    return false;
689
0
  StringRef text = Lexer::getSourceText(
690
0
                                  CharSourceRange::getTokenRange(literalRange),
691
0
                                  Ctx.getSourceManager(), Ctx.getLangOpts());
692
0
  if (text.empty())
693
0
    return false;
694
695
0
  std::optional<bool> UpperU, UpperL;
696
0
  bool UpperF = false;
697
698
0
  struct Suff {
699
0
    static bool has(StringRef suff, StringRef &text) {
700
0
      if (text.ends_with(suff)) {
701
0
        text = text.substr(0, text.size()-suff.size());
702
0
        return true;
703
0
      }
704
0
      return false;
705
0
    }
706
0
  };
707
708
0
  while (true) {
709
0
    if (Suff::has("u", text)) {
710
0
      UpperU = false;
711
0
    } else if (Suff::has("U", text)) {
712
0
      UpperU = true;
713
0
    } else if (Suff::has("ll", text)) {
714
0
      UpperL = false;
715
0
    } else if (Suff::has("LL", text)) {
716
0
      UpperL = true;
717
0
    } else if (Suff::has("l", text)) {
718
0
      UpperL = false;
719
0
    } else if (Suff::has("L", text)) {
720
0
      UpperL = true;
721
0
    } else if (isFloat && Suff::has("f", text)) {
722
0
      UpperF = false;
723
0
    } else if (isFloat && Suff::has("F", text)) {
724
0
      UpperF = true;
725
0
    } else
726
0
      break;
727
0
  }
728
729
0
  if (!UpperU && !UpperL)
730
0
    UpperU = UpperL = true;
731
0
  else if (UpperU && !UpperL)
732
0
    UpperL = UpperU;
733
0
  else if (UpperL && !UpperU)
734
0
    UpperU = UpperL;
735
736
0
  Info.U = *UpperU ? "U" : "u";
737
0
  Info.L = *UpperL ? "L" : "l";
738
0
  Info.LL = *UpperL ? "LL" : "ll";
739
0
  Info.F = UpperF ? "F" : "f";
740
741
0
  Info.Hex = Info.Octal = false;
742
0
  if (text.starts_with("0x"))
743
0
    Info.Hex = true;
744
0
  else if (!isFloat && !isIntZero && text.starts_with("0"))
745
0
    Info.Octal = true;
746
747
0
  SourceLocation B = literalRange.getBegin();
748
0
  Info.WithoutSuffRange =
749
0
      CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
750
0
  return true;
751
0
}
752
753
static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
754
0
                                   const NSAPI &NS, Commit &commit) {
755
0
  if (Msg->getNumArgs() != 1)
756
0
    return false;
757
758
0
  const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
759
0
  if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
760
0
    return rewriteToCharLiteral(Msg, CharE, NS, commit);
761
0
  if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
762
0
    return rewriteToBoolLiteral(Msg, BE, NS, commit);
763
0
  if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
764
0
    return rewriteToBoolLiteral(Msg, BE, NS, commit);
765
766
0
  const Expr *literalE = Arg;
767
0
  if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
768
0
    if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
769
0
      literalE = UOE->getSubExpr();
770
0
  }
771
772
  // Only integer and floating literals, otherwise try to rewrite to boxed
773
  // expression.
774
0
  if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
775
0
    return rewriteToNumericBoxedExpression(Msg, NS, commit);
776
777
0
  ASTContext &Ctx = NS.getASTContext();
778
0
  Selector Sel = Msg->getSelector();
779
0
  std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =
780
0
      NS.getNSNumberLiteralMethodKind(Sel);
781
0
  if (!MKOpt)
782
0
    return false;
783
0
  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
784
785
0
  bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
786
0
  bool CallIsFloating = false, CallIsDouble = false;
787
788
0
  switch (MK) {
789
  // We cannot have these calls with int/float literals.
790
0
  case NSAPI::NSNumberWithChar:
791
0
  case NSAPI::NSNumberWithUnsignedChar:
792
0
  case NSAPI::NSNumberWithShort:
793
0
  case NSAPI::NSNumberWithUnsignedShort:
794
0
  case NSAPI::NSNumberWithBool:
795
0
    return rewriteToNumericBoxedExpression(Msg, NS, commit);
796
797
0
  case NSAPI::NSNumberWithUnsignedInt:
798
0
  case NSAPI::NSNumberWithUnsignedInteger:
799
0
    CallIsUnsigned = true;
800
0
    [[fallthrough]];
801
0
  case NSAPI::NSNumberWithInt:
802
0
  case NSAPI::NSNumberWithInteger:
803
0
    break;
804
805
0
  case NSAPI::NSNumberWithUnsignedLong:
806
0
    CallIsUnsigned = true;
807
0
    [[fallthrough]];
808
0
  case NSAPI::NSNumberWithLong:
809
0
    CallIsLong = true;
810
0
    break;
811
812
0
  case NSAPI::NSNumberWithUnsignedLongLong:
813
0
    CallIsUnsigned = true;
814
0
    [[fallthrough]];
815
0
  case NSAPI::NSNumberWithLongLong:
816
0
    CallIsLongLong = true;
817
0
    break;
818
819
0
  case NSAPI::NSNumberWithDouble:
820
0
    CallIsDouble = true;
821
0
    [[fallthrough]];
822
0
  case NSAPI::NSNumberWithFloat:
823
0
    CallIsFloating = true;
824
0
    break;
825
0
  }
826
827
0
  SourceRange ArgRange = Arg->getSourceRange();
828
0
  QualType ArgTy = Arg->getType();
829
0
  QualType CallTy = Msg->getArg(0)->getType();
830
831
  // Check for the easy case, the literal maps directly to the call.
832
0
  if (Ctx.hasSameType(ArgTy, CallTy)) {
833
0
    commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
834
0
    commit.insert(ArgRange.getBegin(), "@");
835
0
    return true;
836
0
  }
837
838
  // We will need to modify the literal suffix to get the same type as the call.
839
  // Try with boxed expression if it came from a macro.
840
0
  if (ArgRange.getBegin().isMacroID())
841
0
    return rewriteToNumericBoxedExpression(Msg, NS, commit);
842
843
0
  bool LitIsFloat = ArgTy->isFloatingType();
844
  // For a float passed to integer call, don't try rewriting to objc literal.
845
  // It is difficult and a very uncommon case anyway.
846
  // But try with boxed expression.
847
0
  if (LitIsFloat && !CallIsFloating)
848
0
    return rewriteToNumericBoxedExpression(Msg, NS, commit);
849
850
  // Try to modify the literal make it the same type as the method call.
851
  // -Modify the suffix, and/or
852
  // -Change integer to float
853
854
0
  LiteralInfo LitInfo;
855
0
  bool isIntZero = false;
856
0
  if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
857
0
    isIntZero = !IntE->getValue().getBoolValue();
858
0
  if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
859
0
    return rewriteToNumericBoxedExpression(Msg, NS, commit);
860
861
  // Not easy to do int -> float with hex/octal and uncommon anyway.
862
0
  if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
863
0
    return rewriteToNumericBoxedExpression(Msg, NS, commit);
864
865
0
  SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
866
0
  SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
867
868
0
  commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
869
0
                         LitInfo.WithoutSuffRange);
870
0
  commit.insert(LitB, "@");
871
872
0
  if (!LitIsFloat && CallIsFloating)
873
0
    commit.insert(LitE, ".0");
874
875
0
  if (CallIsFloating) {
876
0
    if (!CallIsDouble)
877
0
      commit.insert(LitE, LitInfo.F);
878
0
  } else {
879
0
    if (CallIsUnsigned)
880
0
      commit.insert(LitE, LitInfo.U);
881
882
0
    if (CallIsLong)
883
0
      commit.insert(LitE, LitInfo.L);
884
0
    else if (CallIsLongLong)
885
0
      commit.insert(LitE, LitInfo.LL);
886
0
  }
887
0
  return true;
888
0
}
889
890
// FIXME: Make determination of operator precedence more general and
891
// make it broadly available.
892
0
static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
893
0
  const Expr* Expr = FullExpr->IgnoreImpCasts();
894
0
  if (isa<ArraySubscriptExpr>(Expr) ||
895
0
      isa<CallExpr>(Expr) ||
896
0
      isa<DeclRefExpr>(Expr) ||
897
0
      isa<CXXNamedCastExpr>(Expr) ||
898
0
      isa<CXXConstructExpr>(Expr) ||
899
0
      isa<CXXThisExpr>(Expr) ||
900
0
      isa<CXXTypeidExpr>(Expr) ||
901
0
      isa<CXXUnresolvedConstructExpr>(Expr) ||
902
0
      isa<ObjCMessageExpr>(Expr) ||
903
0
      isa<ObjCPropertyRefExpr>(Expr) ||
904
0
      isa<ObjCProtocolExpr>(Expr) ||
905
0
      isa<MemberExpr>(Expr) ||
906
0
      isa<ObjCIvarRefExpr>(Expr) ||
907
0
      isa<ParenExpr>(FullExpr) ||
908
0
      isa<ParenListExpr>(Expr) ||
909
0
      isa<SizeOfPackExpr>(Expr))
910
0
    return false;
911
912
0
  return true;
913
0
}
914
0
static bool castOperatorNeedsParens(const Expr *FullExpr) {
915
0
  const Expr* Expr = FullExpr->IgnoreImpCasts();
916
0
  if (isa<ArraySubscriptExpr>(Expr) ||
917
0
      isa<CallExpr>(Expr) ||
918
0
      isa<DeclRefExpr>(Expr) ||
919
0
      isa<CastExpr>(Expr) ||
920
0
      isa<CXXNewExpr>(Expr) ||
921
0
      isa<CXXConstructExpr>(Expr) ||
922
0
      isa<CXXDeleteExpr>(Expr) ||
923
0
      isa<CXXNoexceptExpr>(Expr) ||
924
0
      isa<CXXPseudoDestructorExpr>(Expr) ||
925
0
      isa<CXXScalarValueInitExpr>(Expr) ||
926
0
      isa<CXXThisExpr>(Expr) ||
927
0
      isa<CXXTypeidExpr>(Expr) ||
928
0
      isa<CXXUnresolvedConstructExpr>(Expr) ||
929
0
      isa<ObjCMessageExpr>(Expr) ||
930
0
      isa<ObjCPropertyRefExpr>(Expr) ||
931
0
      isa<ObjCProtocolExpr>(Expr) ||
932
0
      isa<MemberExpr>(Expr) ||
933
0
      isa<ObjCIvarRefExpr>(Expr) ||
934
0
      isa<ParenExpr>(FullExpr) ||
935
0
      isa<ParenListExpr>(Expr) ||
936
0
      isa<SizeOfPackExpr>(Expr) ||
937
0
      isa<UnaryOperator>(Expr))
938
0
    return false;
939
940
0
  return true;
941
0
}
942
943
0
static void objectifyExpr(const Expr *E, Commit &commit) {
944
0
  if (!E) return;
945
946
0
  QualType T = E->getType();
947
0
  if (T->isObjCObjectPointerType()) {
948
0
    if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
949
0
      if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
950
0
        return;
951
0
    } else {
952
0
      return;
953
0
    }
954
0
  } else if (!T->isPointerType()) {
955
0
    return;
956
0
  }
957
958
0
  SourceRange Range = E->getSourceRange();
959
0
  if (castOperatorNeedsParens(E))
960
0
    commit.insertWrap("(", Range, ")");
961
0
  commit.insertBefore(Range.getBegin(), "(id)");
962
0
}
963
964
//===----------------------------------------------------------------------===//
965
// rewriteToNumericBoxedExpression.
966
//===----------------------------------------------------------------------===//
967
968
0
static bool isEnumConstant(const Expr *E) {
969
0
  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
970
0
    if (const ValueDecl *VD = DRE->getDecl())
971
0
      return isa<EnumConstantDecl>(VD);
972
973
0
  return false;
974
0
}
975
976
static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
977
0
                                            const NSAPI &NS, Commit &commit) {
978
0
  if (Msg->getNumArgs() != 1)
979
0
    return false;
980
981
0
  const Expr *Arg = Msg->getArg(0);
982
0
  if (Arg->isTypeDependent())
983
0
    return false;
984
985
0
  ASTContext &Ctx = NS.getASTContext();
986
0
  Selector Sel = Msg->getSelector();
987
0
  std::optional<NSAPI::NSNumberLiteralMethodKind> MKOpt =
988
0
      NS.getNSNumberLiteralMethodKind(Sel);
989
0
  if (!MKOpt)
990
0
    return false;
991
0
  NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
992
993
0
  const Expr *OrigArg = Arg->IgnoreImpCasts();
994
0
  QualType FinalTy = Arg->getType();
995
0
  QualType OrigTy = OrigArg->getType();
996
0
  uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
997
0
  uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
998
999
0
  bool isTruncated = FinalTySize < OrigTySize;
1000
0
  bool needsCast = false;
1001
1002
0
  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
1003
0
    switch (ICE->getCastKind()) {
1004
0
    case CK_LValueToRValue:
1005
0
    case CK_NoOp:
1006
0
    case CK_UserDefinedConversion:
1007
0
      break;
1008
1009
0
    case CK_IntegralCast: {
1010
0
      if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
1011
0
        break;
1012
      // Be more liberal with Integer/UnsignedInteger which are very commonly
1013
      // used.
1014
0
      if ((MK == NSAPI::NSNumberWithInteger ||
1015
0
           MK == NSAPI::NSNumberWithUnsignedInteger) &&
1016
0
          !isTruncated) {
1017
0
        if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
1018
0
          break;
1019
0
        if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
1020
0
            OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
1021
0
          break;
1022
0
      }
1023
1024
0
      needsCast = true;
1025
0
      break;
1026
0
    }
1027
1028
0
    case CK_PointerToBoolean:
1029
0
    case CK_IntegralToBoolean:
1030
0
    case CK_IntegralToFloating:
1031
0
    case CK_FloatingToIntegral:
1032
0
    case CK_FloatingToBoolean:
1033
0
    case CK_FloatingCast:
1034
0
    case CK_FloatingComplexToReal:
1035
0
    case CK_FloatingComplexToBoolean:
1036
0
    case CK_IntegralComplexToReal:
1037
0
    case CK_IntegralComplexToBoolean:
1038
0
    case CK_AtomicToNonAtomic:
1039
0
    case CK_AddressSpaceConversion:
1040
0
      needsCast = true;
1041
0
      break;
1042
1043
0
    case CK_Dependent:
1044
0
    case CK_BitCast:
1045
0
    case CK_LValueBitCast:
1046
0
    case CK_LValueToRValueBitCast:
1047
0
    case CK_BaseToDerived:
1048
0
    case CK_DerivedToBase:
1049
0
    case CK_UncheckedDerivedToBase:
1050
0
    case CK_Dynamic:
1051
0
    case CK_ToUnion:
1052
0
    case CK_ArrayToPointerDecay:
1053
0
    case CK_FunctionToPointerDecay:
1054
0
    case CK_NullToPointer:
1055
0
    case CK_NullToMemberPointer:
1056
0
    case CK_BaseToDerivedMemberPointer:
1057
0
    case CK_DerivedToBaseMemberPointer:
1058
0
    case CK_MemberPointerToBoolean:
1059
0
    case CK_ReinterpretMemberPointer:
1060
0
    case CK_ConstructorConversion:
1061
0
    case CK_IntegralToPointer:
1062
0
    case CK_PointerToIntegral:
1063
0
    case CK_ToVoid:
1064
0
    case CK_VectorSplat:
1065
0
    case CK_CPointerToObjCPointerCast:
1066
0
    case CK_BlockPointerToObjCPointerCast:
1067
0
    case CK_AnyPointerToBlockPointerCast:
1068
0
    case CK_ObjCObjectLValueCast:
1069
0
    case CK_FloatingRealToComplex:
1070
0
    case CK_FloatingComplexCast:
1071
0
    case CK_FloatingComplexToIntegralComplex:
1072
0
    case CK_IntegralRealToComplex:
1073
0
    case CK_IntegralComplexCast:
1074
0
    case CK_IntegralComplexToFloatingComplex:
1075
0
    case CK_ARCProduceObject:
1076
0
    case CK_ARCConsumeObject:
1077
0
    case CK_ARCReclaimReturnedObject:
1078
0
    case CK_ARCExtendBlockObject:
1079
0
    case CK_NonAtomicToAtomic:
1080
0
    case CK_CopyAndAutoreleaseBlockObject:
1081
0
    case CK_BuiltinFnToFnPtr:
1082
0
    case CK_ZeroToOCLOpaqueType:
1083
0
    case CK_IntToOCLSampler:
1084
0
    case CK_MatrixCast:
1085
0
      return false;
1086
1087
0
    case CK_BooleanToSignedIntegral:
1088
0
      llvm_unreachable("OpenCL-specific cast in Objective-C?");
1089
1090
0
    case CK_FloatingToFixedPoint:
1091
0
    case CK_FixedPointToFloating:
1092
0
    case CK_FixedPointCast:
1093
0
    case CK_FixedPointToBoolean:
1094
0
    case CK_FixedPointToIntegral:
1095
0
    case CK_IntegralToFixedPoint:
1096
0
      llvm_unreachable("Fixed point types are disabled for Objective-C");
1097
0
    }
1098
0
  }
1099
1100
0
  if (needsCast) {
1101
0
    DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1102
    // FIXME: Use a custom category name to distinguish migration diagnostics.
1103
0
    unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
1104
0
                       "converting to boxing syntax requires casting %0 to %1");
1105
0
    Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
1106
0
        << Msg->getSourceRange();
1107
0
    return false;
1108
0
  }
1109
1110
0
  SourceRange ArgRange = OrigArg->getSourceRange();
1111
0
  commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1112
1113
0
  if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1114
0
    commit.insertBefore(ArgRange.getBegin(), "@");
1115
0
  else
1116
0
    commit.insertWrap("@(", ArgRange, ")");
1117
1118
0
  return true;
1119
0
}
1120
1121
//===----------------------------------------------------------------------===//
1122
// rewriteToStringBoxedExpression.
1123
//===----------------------------------------------------------------------===//
1124
1125
static bool doRewriteToUTF8StringBoxedExpressionHelper(
1126
                                              const ObjCMessageExpr *Msg,
1127
0
                                              const NSAPI &NS, Commit &commit) {
1128
0
  const Expr *Arg = Msg->getArg(0);
1129
0
  if (Arg->isTypeDependent())
1130
0
    return false;
1131
1132
0
  ASTContext &Ctx = NS.getASTContext();
1133
1134
0
  const Expr *OrigArg = Arg->IgnoreImpCasts();
1135
0
  QualType OrigTy = OrigArg->getType();
1136
0
  if (OrigTy->isArrayType())
1137
0
    OrigTy = Ctx.getArrayDecayedType(OrigTy);
1138
1139
0
  if (const StringLiteral *
1140
0
        StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
1141
0
    commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
1142
0
    commit.insert(StrE->getBeginLoc(), "@");
1143
0
    return true;
1144
0
  }
1145
1146
0
  if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
1147
0
    QualType PointeeType = PT->getPointeeType();
1148
0
    if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
1149
0
      SourceRange ArgRange = OrigArg->getSourceRange();
1150
0
      commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
1151
1152
0
      if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
1153
0
        commit.insertBefore(ArgRange.getBegin(), "@");
1154
0
      else
1155
0
        commit.insertWrap("@(", ArgRange, ")");
1156
1157
0
      return true;
1158
0
    }
1159
0
  }
1160
1161
0
  return false;
1162
0
}
1163
1164
static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
1165
0
                                           const NSAPI &NS, Commit &commit) {
1166
0
  Selector Sel = Msg->getSelector();
1167
1168
0
  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
1169
0
      Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
1170
0
      Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
1171
0
    if (Msg->getNumArgs() != 1)
1172
0
      return false;
1173
0
    return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1174
0
  }
1175
1176
0
  if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
1177
0
    if (Msg->getNumArgs() != 2)
1178
0
      return false;
1179
1180
0
    const Expr *encodingArg = Msg->getArg(1);
1181
0
    if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
1182
0
        NS.isNSASCIIStringEncodingConstant(encodingArg))
1183
0
      return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
1184
0
  }
1185
1186
0
  return false;
1187
0
}