Files
libreoffice/compilerplugins/clang/cstylecast.cxx
Stephan Bergmann b72a31b37f Adapt to Clang trunk SourceManager::getImmediateExpansionRange changes
...of <http://llvm.org/viewvc/llvm-project?view=revision&revision=331155>
"PR37189 Fix incorrect end source location and spelling for a split '>>' token",
changing (among others) the return type of getImmediateExpansionRange from a
std::pair of token locations to CharSourceRange (which will typically also
represent token locations, but might also represent char locations).

For now, map the return value of getImmediateExpansionRange back to a std::pair
(as expected by our compilerplugins code in its current form), and mark the
char location case with a TODO (which will need to be addressed if any of our
plugins starts to produce wrong results due to not handling that char location
case).  In the long run, we should instead adapt our code to use the new return
type of getImmediateExpansionRange directly.

Change-Id: Idc2f5dc43830af4798b55bf605976c4ab146c522
Reviewed-on: https://gerrit.libreoffice.org/53817
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2018-05-04 13:56:51 +02:00

671 lines
26 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <algorithm>
#include <cassert>
#include <limits>
#include <set>
#include <string>
#include "compat.hxx"
#include "plugin.hxx"
//
// We don't like using C-style casts in C++ code
//
namespace {
bool areSimilar(QualType type1, QualType type2) {
auto t1 = type1.getCanonicalType().getTypePtr();
auto t2 = type2.getCanonicalType().getTypePtr();
for (;;) {
if (t1->isPointerType()) {
if (!t2->isPointerType()) {
return false;
}
auto t1a = t1->getAs<clang::PointerType>();
auto t2a = t2->getAs<clang::PointerType>();
t1 = t1a->getPointeeType().getTypePtr();
t2 = t2a->getPointeeType().getTypePtr();
} else if (t1->isMemberPointerType()) {
if (!t2->isMemberPointerType()) {
return false;
}
auto t1a = t1->getAs<MemberPointerType>();
auto t2a = t2->getAs<MemberPointerType>();
if (t1a->getClass()->getCanonicalTypeInternal()
!= t2a->getClass()->getCanonicalTypeInternal())
{
return false;
}
t1 = t1a->getPointeeType().getTypePtr();
t2 = t2a->getPointeeType().getTypePtr();
} else if (t1->isConstantArrayType()) {
if (!t2->isConstantArrayType()) {
return false;
}
auto t1a = static_cast<ConstantArrayType const *>(
t1->getAsArrayTypeUnsafe());
auto t2a = static_cast<ConstantArrayType const *>(
t2->getAsArrayTypeUnsafe());
if (t1a->getSize() != t2a->getSize()) {
return false;
}
t1 = t1a->getElementType().getTypePtr();
t2 = t2a->getElementType().getTypePtr();
} else if (t1->isIncompleteArrayType()) {
if (!t2->isIncompleteArrayType()) {
return false;
}
auto t1a = static_cast<IncompleteArrayType const *>(
t1->getAsArrayTypeUnsafe());
auto t2a = static_cast<IncompleteArrayType const *>(
t2->getAsArrayTypeUnsafe());
t1 = t1a->getElementType().getTypePtr();
t2 = t2a->getElementType().getTypePtr();
} else {
return false;
}
if (t1 == t2) {
return true;
}
}
}
QualType resolvePointers(QualType type) {
while (type->isPointerType()) {
type = type->getAs<clang::PointerType>()->getPointeeType();
}
return type;
}
bool isLiteralLike(Expr const * expr) {
expr = expr->IgnoreParenImpCasts();
if (isa<IntegerLiteral>(expr) || isa<CharacterLiteral>(expr) || isa<FloatingLiteral>(expr)
|| isa<ImaginaryLiteral>(expr) || isa<CXXBoolLiteralExpr>(expr)
|| isa<CXXNullPtrLiteralExpr>(expr) || isa<ObjCBoolLiteralExpr>(expr))
{
return true;
}
if (auto const e = dyn_cast<DeclRefExpr>(expr)) {
auto const d = e->getDecl();
if (isa<EnumConstantDecl>(d)) {
return true;
}
if (auto const v = dyn_cast<VarDecl>(d)) {
if (d->getType().isConstQualified()) {
if (auto const init = v->getAnyInitializer()) {
return isLiteralLike(init);
}
}
}
return false;
}
if (auto const e = dyn_cast<UnaryExprOrTypeTraitExpr>(expr)) {
auto const k = e->getKind();
return k == UETT_SizeOf || k == UETT_AlignOf;
}
if (auto const e = dyn_cast<UnaryOperator>(expr)) {
auto const k = e->getOpcode();
if (k == UO_Plus || k == UO_Minus || k == UO_Not || k == UO_LNot) {
return isLiteralLike(e->getSubExpr());
}
return false;
}
if (auto const e = dyn_cast<BinaryOperator>(expr)) {
auto const k = e->getOpcode();
if (k == BO_Mul || k == BO_Div || k == BO_Rem || k == BO_Add || k == BO_Sub || k == BO_Shl
|| k == BO_Shr || k == BO_And || k == BO_Xor || k == BO_Or)
{
return isLiteralLike(e->getLHS()) && isLiteralLike(e->getRHS());
}
return false;
}
if (auto const e = dyn_cast<ExplicitCastExpr>(expr)) {
auto const t = e->getTypeAsWritten();
return (t->isArithmeticType() || t->isEnumeralType())
&& isLiteralLike(e->getSubExprAsWritten());
}
return false;
}
bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) {
// Must be <simple-type-specifier> or <typename-specifier>, lets approximate that here:
assert(info != nullptr);
auto const type = info->getType();
if (type.hasLocalQualifiers()) {
return false;
}
if (auto const t = dyn_cast<BuiltinType>(type)) {
if (!(t->isInteger() || t->isFloatingPoint())) {
return false;
}
auto const loc = info->getTypeLoc().castAs<BuiltinTypeLoc>();
return
(int(loc.hasWrittenSignSpec()) + int(loc.hasWrittenWidthSpec())
+ int(loc.hasWrittenTypeSpec()))
== 1;
}
if (isa<TagType>(type) || isa<TemplateTypeParmType>(type) || isa<AutoType>(type)
|| isa<DecltypeType>(type) || isa<TypedefType>(type))
{
return true;
}
if (auto const t = dyn_cast<ElaboratedType>(type)) {
return t->getKeyword() == ETK_None;
}
return false;
}
class CStyleCast:
public RecursiveASTVisitor<CStyleCast>, public loplugin::RewritePlugin
{
public:
explicit CStyleCast(loplugin::InstantiationData const & data): RewritePlugin(data)
{}
virtual void run() override {
if (compiler.getLangOpts().CPlusPlus) {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
}
bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr) {
return WalkUpFromInitListExpr(expr)
&& TraverseSynOrSemInitListExpr(
expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
}
bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
bool VisitCStyleCastExpr(const CStyleCastExpr * expr);
private:
bool isConstCast(QualType from, QualType to);
bool isFromCIncludeFile(SourceLocation spellingLocation) const;
bool isSharedCAndCppCode(SourceLocation location) const;
bool isLastTokenOfImmediateMacroBodyExpansion(
SourceLocation loc, SourceLocation * macroEnd = nullptr) const;
bool rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement);
unsigned int externCContexts_ = 0;
std::set<SourceLocation> rewritten_;
// needed when rewriting in macros, in general to avoid "double code replacement, possible
// plugin error" warnings, and in particular to avoid adding multiple sets of parens around
// sub-exprs
std::set<CStyleCastExpr const *> rewrittenSubExprs_;
};
const char * recommendedFix(clang::CastKind ck) {
switch(ck) {
case CK_IntegralToPointer: return "reinterpret_cast";
case CK_PointerToIntegral: return "reinterpret_cast";
case CK_BaseToDerived: return "static_cast";
default: return nullptr;
}
}
bool CStyleCast::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
++externCContexts_;
bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
assert(externCContexts_ != 0);
--externCContexts_;
return ret;
}
bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr * expr) {
if (ignoreLocation(expr)) {
return true;
}
// casting to void is typically used when a parameter or field is only used in
// debug mode, and we want to eliminate an "unused" warning
if( expr->getCastKind() == CK_ToVoid ) {
return true;
}
if (isSharedCAndCppCode(expr->getLocStart())) {
return true;
}
char const * perf = nullptr;
if( expr->getCastKind() == CK_IntegralCast ) {
if (rewriteArithmeticCast(expr, &perf)) {
return true;
}
} else if( expr->getCastKind() == CK_NoOp ) {
if (!((expr->getSubExpr()->getType()->isPointerType()
&& expr->getType()->isPointerType())
|| expr->getTypeAsWritten()->isReferenceType()))
{
if (rewriteArithmeticCast(expr, &perf)) {
return true;
}
}
if (isConstCast(
expr->getSubExprAsWritten()->getType(),
expr->getTypeAsWritten()))
{
perf = "const_cast";
}
}
std::string incompFrom;
std::string incompTo;
if( expr->getCastKind() == CK_BitCast ) {
if (resolvePointers(expr->getSubExprAsWritten()->getType())
->isIncompleteType())
{
incompFrom = "incomplete ";
}
if (resolvePointers(expr->getType())->isIncompleteType()) {
incompTo = "incomplete ";
}
}
if (perf == nullptr) {
perf = recommendedFix(expr->getCastKind());
}
std::string performs;
if (perf != nullptr) {
performs = std::string(" (performs: ") + perf + ")";
}
report(
DiagnosticsEngine::Warning, "C-style cast from %0%1 to %2%3%4 (%5)",
expr->getSourceRange().getBegin())
<< incompFrom << expr->getSubExprAsWritten()->getType()
<< incompTo << expr->getTypeAsWritten() << performs
<< expr->getCastKindName()
<< expr->getSourceRange();
return true;
}
bool CStyleCast::isConstCast(QualType from, QualType to) {
if (to->isReferenceType()
&& to->getAs<ReferenceType>()->getPointeeType()->isObjectType())
{
if (!from->isObjectType()) {
return false;
}
from = compiler.getASTContext().getPointerType(from);
to = compiler.getASTContext().getPointerType(
to->getAs<ReferenceType>()->getPointeeType());
} else {
if (from->isArrayType()) {
from = compiler.getASTContext().getPointerType(
from->getAsArrayTypeUnsafe()->getElementType());
} else if (from->isFunctionType()) {
compiler.getASTContext().getPointerType(from);
}
}
return areSimilar(from, to);
}
bool CStyleCast::isFromCIncludeFile(SourceLocation spellingLocation) const {
return !compiler.getSourceManager().isInMainFile(spellingLocation)
&& (StringRef(
compiler.getSourceManager().getPresumedLoc(spellingLocation)
.getFilename())
.endswith(".h"));
}
bool CStyleCast::isSharedCAndCppCode(SourceLocation location) const {
while (compiler.getSourceManager().isMacroArgExpansion(location)) {
location = compiler.getSourceManager().getImmediateMacroCallerLoc(
location);
}
// Assume that code is intended to be shared between C and C++ if it comes
// from an include file ending in .h, and is either in an extern "C" context
// or the body of a macro definition:
return
isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
&& (externCContexts_ != 0
|| compiler.getSourceManager().isMacroBodyExpansion(location));
}
bool CStyleCast::isLastTokenOfImmediateMacroBodyExpansion(
SourceLocation loc, SourceLocation * macroEnd) const
{
assert(compiler.getSourceManager().isMacroBodyExpansion(loc));
auto const spell = compiler.getSourceManager().getSpellingLoc(loc);
auto name = Lexer::getImmediateMacroName(
loc, compiler.getSourceManager(), compiler.getLangOpts());
while (name.startswith("\\\n")) {
name = name.drop_front(2);
while (!name.empty()
&& (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
|| name.front() == '\v' || name.front() == '\f'))
{
name = name.drop_front(1);
}
}
auto const MI
= (compiler.getPreprocessor().getMacroDefinitionAtLoc(
&compiler.getASTContext().Idents.get(name), spell)
.getMacroInfo());
assert(MI != nullptr);
if (spell == MI->getDefinitionEndLoc()) {
if (macroEnd != nullptr) {
*macroEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).second;
}
return true;
}
return false;
}
bool CStyleCast::rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement) {
assert(replacement != nullptr);
auto const sub = expr->getSubExprAsWritten();
auto const functional = isLiteralLike(sub)
&& canBeUsedForFunctionalCast(expr->getTypeInfoAsWritten());
*replacement = functional ? "functional cast" : "static_cast";
if (rewriter == nullptr) {
return false;
}
// Doing modifications for a chain of C-style casts as in
//
// (foo)(bar)(baz)x
//
// leads to unpredictable results, so only rewrite them one at a time, starting with the
// outermost:
if (auto const e = dyn_cast<CStyleCastExpr>(sub)) {
rewrittenSubExprs_.insert(e);
}
if (rewrittenSubExprs_.find(expr) != rewrittenSubExprs_.end()) {
return false;
}
// Two or four ranges to replace:
// First is the CStyleCast's LParen, plus following whitespace, replaced with either "" or
// "static_cast<". (TODO: insert space before "static_cast<" when converting "else(int)...".)
// Second is the CStyleCast's RParen, plus preceding and following whitespace, replaced with
// either "" or ">".
// If the sub expr is not a ParenExpr, third is the sub expr's begin, inserting "(", and fourth
// is the sub expr's end, inserting ")".
// (The reason the second and third are not combined is in case there's a comment between them.)
auto firstBegin = expr->getLParenLoc();
auto secondBegin = expr->getRParenLoc();
while (compiler.getSourceManager().isMacroArgExpansion(firstBegin)
&& compiler.getSourceManager().isMacroArgExpansion(secondBegin)
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), firstBegin)
== compat::getImmediateExpansionRange(compiler.getSourceManager(), secondBegin)))
{
firstBegin = compiler.getSourceManager().getImmediateSpellingLoc(firstBegin);
secondBegin = compiler.getSourceManager().getImmediateSpellingLoc(secondBegin);
}
if (compiler.getSourceManager().isMacroBodyExpansion(firstBegin)
&& compiler.getSourceManager().isMacroBodyExpansion(secondBegin)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(firstBegin)
== compiler.getSourceManager().getImmediateMacroCallerLoc(secondBegin)))
{
firstBegin = compiler.getSourceManager().getSpellingLoc(firstBegin);
secondBegin = compiler.getSourceManager().getSpellingLoc(secondBegin);
}
auto third = sub->getLocStart();
auto fourth = sub->getLocEnd();
bool macro = false;
// Ensure that
//
// #define FOO(x) (int)x
// FOO(y)
//
// is changed to
//
// #define FOO(x) static_cast<int>(x)
// FOO(y)
//
// instead of
//
// #define FOO(x) static_cast<int>x
// FOO((y))
while (compiler.getSourceManager().isMacroArgExpansion(third)
&& compiler.getSourceManager().isMacroArgExpansion(fourth)
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
== compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth))
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third))
//TODO: check fourth is at end of immediate macro expansion, but
// SourceManager::isAtEndOfImmediateMacroExpansion requires a location pointing at the
// character end of the last token
{
auto const range = compat::getImmediateExpansionRange(compiler.getSourceManager(), third);
third = range.first;
fourth = range.second;
macro = true;
assert(third.isValid());
}
while (compiler.getSourceManager().isMacroArgExpansion(third)
&& compiler.getSourceManager().isMacroArgExpansion(fourth)
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
== compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth)))
{
third = compiler.getSourceManager().getImmediateSpellingLoc(third);
fourth = compiler.getSourceManager().getImmediateSpellingLoc(fourth);
}
if (isa<ParenExpr>(sub)) {
// Ensure that with
//
// #define FOO (x)
//
// a cast like
//
// (int) FOO
//
// is changed to
//
// static_cast<int>(FOO)
//
// instead of
//
// static_cast<int>FOO
for (;; macro = true) {
if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)
&& isLastTokenOfImmediateMacroBodyExpansion(fourth)))
{
if (!macro) {
third = fourth = SourceLocation();
}
break;
}
auto const range = compat::getImmediateExpansionRange(
compiler.getSourceManager(), third);
third = range.first;
fourth = range.second;
assert(third.isValid());
}
if (third.isValid() && compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
{
third = compiler.getSourceManager().getSpellingLoc(third);
fourth = compiler.getSourceManager().getSpellingLoc(fourth);
assert(third.isValid());
}
} else {
// Ensure that a cast like
//
// (int)LONG_MAX
//
// (where LONG_MAX expands to __LONG_MAX__, which in turn is a built-in expanding to a value
// like 9223372036854775807L) is changed to
//
// int(LONG_MAX)
//
// instead of trying to add the parentheses to the built-in __LONG_MAX__ definition:
for (;;) {
if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)))
// TODO: check that fourth is at end of immediate macro expansion (but
// SourceManager::isAtEndOfImmediateMacroExpansion wants a location pointing at the
// character end)
{
break;
}
auto const range = compat::getImmediateExpansionRange(
compiler.getSourceManager(), third);
third = range.first;
fourth = range.second;
}
// ...and additionally asymmetrically unwind macros only at the start or end, for code like
//
// (long)ubidi_getVisualIndex(...)
//
// (in editeng/source/editeng/impedit2.cxx) where ubidi_getVisualIndex is an object-like
// macro, or
//
// #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
//
// (in hwpfilter/source/lexer.cxx):
if (!fourth.isMacroID()) {
while (compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third))
{}
} else if (compiler.getSourceManager().isMacroBodyExpansion(fourth)) {
while (compiler.getSourceManager().isMacroArgExpansion(third)
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third)) {}
}
if (!third.isMacroID()) {
while (compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& isLastTokenOfImmediateMacroBodyExpansion(fourth, &fourth))
{}
} else if (compiler.getSourceManager().isMacroBodyExpansion(third)) {
while (compiler.getSourceManager().isMacroArgExpansion(fourth, &fourth)) {}
}
if (compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
{
third = compiler.getSourceManager().getSpellingLoc(third);
fourth = compiler.getSourceManager().getSpellingLoc(fourth);
}
assert(third.isValid());
}
if (firstBegin.isMacroID() || secondBegin.isMacroID() || (third.isValid() && third.isMacroID())
|| (fourth.isValid() && fourth.isMacroID()))
{
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal,
"TODO: cannot rewrite C-style cast in macro, needs investigation",
expr->getExprLoc())
<< expr->getSourceRange();
}
return false;
}
unsigned firstLen = Lexer::MeasureTokenLength(
firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
l = l.getLocWithOffset(1))
{
unsigned n = Lexer::MeasureTokenLength(
l, compiler.getSourceManager(), compiler.getLangOpts());
if (n != 0) {
break;
}
++firstLen;
}
unsigned secondLen = Lexer::MeasureTokenLength(
secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
for (auto l = secondBegin.getLocWithOffset(std::max<unsigned>(secondLen, 1));;
l = l.getLocWithOffset(1))
{
unsigned n = Lexer::MeasureTokenLength(
l, compiler.getSourceManager(), compiler.getLangOpts());
if (n != 0) {
break;
}
++secondLen;
}
for (;;) {
auto l = secondBegin.getLocWithOffset(-1);
auto const c = compiler.getSourceManager().getCharacterData(l)[0];
if (c == '\n') {
if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
break;
}
} else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
break;
}
secondBegin = l;
++secondLen;
}
if (rewritten_.find(firstBegin) == rewritten_.end()) {
rewritten_.insert(firstBegin);
if (!replaceText(firstBegin, firstLen, functional ? "" : "static_cast<")) {
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #1, needs investigation",
firstBegin);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
<< expr->getSourceRange();
}
return false;
}
if (!replaceText(secondBegin, secondLen, functional ? "" : ">")) {
//TODO: roll back
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #2, needs investigation",
secondBegin);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
<< expr->getSourceRange();
}
return false;
}
}
if (third.isValid()) {
if (rewritten_.find(third) == rewritten_.end()) {
rewritten_.insert(third);
if (!insertTextBefore(third, "(")) {
//TODO: roll back
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #3, needs investigation",
third);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast",
expr->getExprLoc())
<< expr->getSourceRange();
}
return false;
}
if (!insertTextAfterToken(fourth, ")")) {
//TODO: roll back
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #4, needs investigation",
third);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast",
expr->getExprLoc())
<< expr->getSourceRange();
}
return false;
}
}
}
return true;
}
loplugin::Plugin::Registration< CStyleCast > X("cstylecast", true);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */