summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/cstylecast.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /compilerplugins/clang/cstylecast.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compilerplugins/clang/cstylecast.cxx')
-rw-r--r--compilerplugins/clang/cstylecast.cxx707
1 files changed, 707 insertions, 0 deletions
diff --git a/compilerplugins/clang/cstylecast.cxx b/compilerplugins/clang/cstylecast.cxx
new file mode 100644
index 000000000..7fa2ce0ae
--- /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 <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. 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<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 loplugin::FilteringRewritePlugin<CStyleCast>
+{
+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<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(compat::getBeginLoc(expr))) {
+ 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<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 = compat::getBeginLoc(sub);
+ auto fourth = compat::getEndLoc(sub);
+ 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_.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<CXXFunctionalCastExpr>(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: */