llvm-capstone/clang/lib/ARCMigrate/TransAPIUses.cpp
Alp Toker 42aa21222d ARCMigrate: Introduce proper diagnostics for TransformActions
This starts to switch ARCMT to use proper diagnostic messages. The old use was
based on incorrect example code from the documentation.

The logic of the previous report() functions has been retained to support any
external consumers that might be intercepting diagnostic messages through the
old interface.

Note that the change in test/Misc/warning-flags.c isn't a new warning without a
flag, rather one that was previously invisible to the test. Adding a flag might
be a good idea though.

llvm-svn: 200124
2014-01-26 05:07:32 +00:00

109 lines
3.5 KiB
C++

//===--- TransAPIUses.cpp - Transformations to ARC mode -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// checkAPIUses:
//
// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
//
// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
// with __unsafe_unretained objects.
// - Calling -zone gets replaced with 'nil'.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class APIChecker : public RecursiveASTVisitor<APIChecker> {
MigrationPass &Pass;
Selector getReturnValueSel, setReturnValueSel;
Selector getArgumentSel, setArgumentSel;
Selector zoneSel;
public:
APIChecker(MigrationPass &pass) : Pass(pass) {
SelectorTable &sels = Pass.Ctx.Selectors;
IdentifierTable &ids = Pass.Ctx.Idents;
getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
IdentifierInfo *selIds[2];
selIds[0] = &ids.get("getArgument");
selIds[1] = &ids.get("atIndex");
getArgumentSel = sels.getSelector(2, selIds);
selIds[0] = &ids.get("setArgument");
setArgumentSel = sels.getSelector(2, selIds);
zoneSel = sels.getNullarySelector(&ids.get("zone"));
}
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
// NSInvocation.
if (E->isInstanceMessage() &&
E->getReceiverInterface() &&
E->getReceiverInterface()->getName() == "NSInvocation") {
StringRef selName;
if (E->getSelector() == getReturnValueSel)
selName = "getReturnValue";
else if (E->getSelector() == setReturnValueSel)
selName = "setReturnValue";
else if (E->getSelector() == getArgumentSel)
selName = "getArgument";
else if (E->getSelector() == setArgumentSel)
selName = "setArgument";
else
return true;
Expr *parm = E->getArg(0)->IgnoreParenCasts();
QualType pointee = parm->getType()->getPointeeType();
if (pointee.isNull())
return true;
if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone)
Pass.TA.report(parm->getLocStart(),
diag::err_arcmt_nsinvocation_ownership,
parm->getSourceRange())
<< selName;
return true;
}
// -zone.
if (E->isInstanceMessage() &&
E->getInstanceReceiver() &&
E->getSelector() == zoneSel &&
Pass.TA.hasDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
E->getSelectorLoc(0))) {
// Calling -zone is meaningless in ARC, change it to nil.
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
E->getSelectorLoc(0));
Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
}
return true;
}
};
} // anonymous namespace
void trans::checkAPIUses(MigrationPass &pass) {
APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}