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