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