//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Rewrites legacy method calls to modern syntax.
//
//===----------------------------------------------------------------------===//
#include "clang/Edit/Rewriters.h"
#include "clang/Edit/Commit.h"
#include "clang/Lex/Lexer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NSAPI.h"
using namespace clang;
using namespace edit;
static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
IdentifierInfo *&ClassId,
const LangOptions &LangOpts) {
if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
return false;
const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
if (!Receiver)
return false;
ClassId = Receiver->getIdentifier();
if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
return true;
// When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
// since the change from +1 to +0 will be handled fine by ARC.
if (LangOpts.ObjCAutoRefCount) {
if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
if (Rec->getMethodFamily() == OMF_alloc)
return true;
}
}
}
return false;
}
//===----------------------------------------------------------------------===//
// rewriteObjCRedundantCallWithLiteral.
//===----------------------------------------------------------------------===//
bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
const NSAPI &NS, Commit &commit) {
IdentifierInfo *II = 0;
if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
return false;
if (Msg->getNumArgs() != 1)
return false;
const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
Selector Sel = Msg->getSelector();
if ((isa<ObjCStringLiteral>(Arg) &&
NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
(NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
(isa<ObjCArrayLiteral>(Arg) &&
NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
(NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
(isa<ObjCDictionaryLiteral>(Arg) &&
NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
(NS.getNSDictionarySelector(
NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
commit.replaceWithInner(Msg->getSourceRange(),
Msg->getArg(0)->getSourceRange());
return true;
}
return false;
}
//===----------------------------------------------------------------------===//
// rewriteToObjCSubscriptSyntax.
//===----------------------------------------------------------------------===//
/// \brief Check for classes that accept 'objectForKey:' (or the other selectors
/// that the migrator handles) but return their instances as 'id', resulting
/// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
///
/// When checking if we can convert to subscripting syntax, check whether
/// the receiver is a result of a class method from a hardcoded list of
/// such classes. In such a case return the specific class as the interface
/// of the receiver.
///
/// FIXME: Remove this when these classes start using 'instancetype'.
static const ObjCInterfaceDecl *
maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
const Expr *Receiver,
ASTContext &Ctx) {
assert(IFace && Receiver);
// If the receiver has type 'id'...
if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
return IFace;
const ObjCMessageExpr *
InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
if (!InnerMsg)
return IFace;
QualType ClassRec;
switch (InnerMsg->getReceiverKind()) {
case ObjCMessageExpr::Instance:
case ObjCMessageExpr::SuperInstance:
return IFace;
case ObjCMessageExpr::Class:
ClassRec = InnerMsg->getClassReceiver();
break;
case ObjCMessageExpr::SuperClass:
ClassRec = InnerMsg->getSuperType();
break;
}
if (