From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- compilerplugins/clang/cstylecast.cxx | 707 +++++++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 compilerplugins/clang/cstylecast.cxx (limited to 'compilerplugins/clang/cstylecast.cxx') diff --git a/compilerplugins/clang/cstylecast.cxx b/compilerplugins/clang/cstylecast.cxx new file mode 100644 index 000000000..35292ecd8 --- /dev/null +++ b/compilerplugins/clang/cstylecast.cxx @@ -0,0 +1,707 @@ +/* -*- 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 +#include +#include +#include +#include + +#include "compat.hxx" +#include "plugin.hxx" + +// +// We don't like using C-style casts in C++ code. Similarly, warn about function-style casts (which +// are semantically equivalent to C-style casts) that are not semantically equivalent to static_cast +// and should rather be written as const_cast or reinterpret_cast. +// + +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(); + auto t2a = t2->getAs(); + t1 = t1a->getPointeeType().getTypePtr(); + t2 = t2a->getPointeeType().getTypePtr(); + } else if (t1->isMemberPointerType()) { + if (!t2->isMemberPointerType()) { + return false; + } + auto t1a = t1->getAs(); + auto t2a = t2->getAs(); + 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( + t1->getAsArrayTypeUnsafe()); + auto t2a = static_cast( + 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( + t1->getAsArrayTypeUnsafe()); + auto t2a = static_cast( + 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()->getPointeeType(); + } + return type; +} + +bool isLiteralLike(Expr const * expr) { + expr = expr->IgnoreParenImpCasts(); + if (isa(expr) || isa(expr) || isa(expr) + || isa(expr) || isa(expr) + || isa(expr) || isa(expr)) + { + return true; + } + if (auto const e = dyn_cast(expr)) { + auto const d = e->getDecl(); + if (isa(d)) { + return true; + } + if (auto const v = dyn_cast(d)) { + if (d->getType().isConstQualified()) { + if (auto const init = v->getAnyInitializer()) { + return isLiteralLike(init); + } + } + } + return false; + } + if (auto const e = dyn_cast(expr)) { + auto const k = e->getKind(); + return k == UETT_SizeOf || k == UETT_AlignOf; + } + if (auto const e = dyn_cast(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(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(expr)) { + auto const t = e->getTypeAsWritten(); + return (t->isArithmeticType() || t->isEnumeralType()) + && isLiteralLike(e->getSubExprAsWritten()); + } + return false; +} + +bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) { + // Must be or , lets approximate that here: + assert(info != nullptr); + auto const type = info->getType(); + if (type.hasLocalQualifiers()) { + return false; + } + if (auto const t = dyn_cast(type)) { + if (!(t->isInteger() || t->isFloatingPoint())) { + return false; + } + auto const loc = info->getTypeLoc().castAs(); + return + (int(loc.hasWrittenSignSpec()) + int(loc.hasWrittenWidthSpec()) + + int(loc.hasWrittenTypeSpec())) + == 1; + } + if (isa(type) || isa(type) || isa(type) + || isa(type) || isa(type)) + { + return true; + } + if (auto const t = dyn_cast(type)) { + return t->getKeyword() == ETK_None; + } + return false; +} + +class CStyleCast: + public loplugin::FilteringRewritePlugin +{ +public: + explicit CStyleCast(loplugin::InstantiationData const & data): FilteringRewritePlugin(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); + + bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * 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); + + void reportCast(ExplicitCastExpr const * expr, char const * performsHint); + + unsigned int externCContexts_ = 0; + std::set 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 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::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->getBeginLoc())) { + 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"; + } + } + reportCast(expr, perf); + return true; +} + +bool CStyleCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) { + if (ignoreLocation(expr)) { + return true; + } + char const * perf = nullptr; + switch (expr->getCastKind()) { + case CK_ConstructorConversion: + case CK_Dependent: //TODO: really filter out all of these? + case CK_IntegralCast: + case CK_IntegralToBoolean: + case CK_ToVoid: + return true; + case CK_NoOp: + if (isConstCast( + expr->getSubExprAsWritten()->getType(), + expr->getTypeAsWritten())) + { + perf = "const_cast"; + break; + } + return true; //TODO: really filter out all of these? + default: + break; + } + reportCast(expr, perf); + return true; +} + +bool CStyleCast::isConstCast(QualType from, QualType to) { + if (to->isReferenceType() + && to->getAs()->getPointeeType()->isObjectType()) + { + if (!from->isObjectType()) { + return false; + } + from = compiler.getASTContext().getPointerType(from); + to = compiler.getASTContext().getPointerType( + to->getAs()->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(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->getBeginLoc(); + auto fourth = sub->getEndLoc(); + bool macro = false; + // Ensure that + // + // #define FOO(x) (int)x + // FOO(y) + // + // is changed to + // + // #define FOO(x) static_cast(x) + // FOO(y) + // + // instead of + // + // #define FOO(x) static_castx + // 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(sub)) { + // Ensure that with + // + // #define FOO (x) + // + // a cast like + // + // (int) FOO + // + // is changed to + // + // static_cast(FOO) + // + // instead of + // + // static_castFOO + 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(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(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_.insert(firstBegin).second) { + 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_.insert(third).second) { + 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; +} + +void CStyleCast::reportCast(ExplicitCastExpr const * expr, char const * performsHint) { + 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 (performsHint == nullptr) { + performsHint = recommendedFix(expr->getCastKind()); + } + std::string performs; + if (performsHint != nullptr) { + performs = std::string(" (performs: ") + performsHint + ")"; + } + report( + DiagnosticsEngine::Warning, "%select{C|Function}0-style cast from %1%2 to %3%4%5 (%6)", + expr->getSourceRange().getBegin()) + << isa(expr) + << incompFrom << expr->getSubExprAsWritten()->getType() + << incompTo << expr->getTypeAsWritten() << performs + << expr->getCastKindName() + << expr->getSourceRange(); +} + +loplugin::Plugin::Registration< CStyleCast > X("cstylecast", true); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3