/* -*- 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/. */ #ifndef LO_CLANG_SHARED_PLUGINS #include #include #include #include #include #include #include "compat.hxx" #include "check.hxx" #include "plugin.hxx" namespace { class StaticDynamic : public loplugin::FilteringPlugin { public: explicit StaticDynamic(loplugin::InstantiationData const& data) : FilteringPlugin(data) { } bool preRun() override { return compiler.getLangOpts().CPlusPlus; } void postRun() override {} virtual void run() override { if (preRun()) TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr const*); bool VisitCXXStaticCastExpr(CXXStaticCastExpr const*); bool PreTraverseCompoundStmt(CompoundStmt*); bool PostTraverseCompoundStmt(CompoundStmt*, bool); bool TraverseCompoundStmt(CompoundStmt*); private: // the key is the pair of VarDecl and the type being cast to. struct BlockState { std::map, SourceLocation> staticCastVars; std::map, SourceLocation> dynamicCastVars; }; // only maintain state inside a single basic block, we're not trying to analyse // cross-block interactions. std::vector blockStack; BlockState blockState; }; bool StaticDynamic::PreTraverseCompoundStmt(CompoundStmt*) { blockStack.push_back(std::move(blockState)); return true; } bool StaticDynamic::PostTraverseCompoundStmt(CompoundStmt*, bool) { blockState = std::move(blockStack.back()); blockStack.pop_back(); return true; } bool StaticDynamic::TraverseCompoundStmt(CompoundStmt* compoundStmt) { bool ret = true; if (PreTraverseCompoundStmt(compoundStmt)) { ret = FilteringPlugin::TraverseCompoundStmt(compoundStmt); PostTraverseCompoundStmt(compoundStmt, ret); } return ret; } const clang::Type* strip(QualType qt) { const clang::Type* varType = qt->getUnqualifiedDesugaredType(); if (varType->isPointerType()) varType = varType->getPointeeType()->getUnqualifiedDesugaredType(); if (varType->isReferenceType()) varType = varType->getAs() ->getPointeeType() ->getUnqualifiedDesugaredType(); return varType; } bool StaticDynamic::VisitCXXStaticCastExpr(CXXStaticCastExpr const* staticCastExpr) { if (ignoreLocation(staticCastExpr)) return true; auto subExprDecl = dyn_cast(staticCastExpr->getSubExpr()->IgnoreParenImpCasts()); if (!subExprDecl) return true; auto varDecl = dyn_cast_or_null(subExprDecl->getDecl()); if (!varDecl) return true; auto varType = strip(staticCastExpr->getType()); auto it = blockState.dynamicCastVars.find({ varDecl, varType }); if (it != blockState.dynamicCastVars.end()) { StringRef fn = getFilenameOfLocation( compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(staticCastExpr))); // loop if (loplugin::isSamePathname(fn, SRCDIR "/basctl/source/basicide/basobj3.cxx")) return true; if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/doc/swserv.cxx")) return true; if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/text/txtfly.cxx")) return true; report(DiagnosticsEngine::Warning, "static_cast after dynamic_cast", compat::getBeginLoc(staticCastExpr)) << staticCastExpr->getSourceRange(); report(DiagnosticsEngine::Note, "dynamic_cast here", it->second); return true; } blockState.staticCastVars.insert({ { varDecl, varType }, compat::getBeginLoc(staticCastExpr) }); return true; } bool StaticDynamic::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const* dynamicCastExpr) { if (ignoreLocation(dynamicCastExpr)) return true; auto subExprDecl = dyn_cast(dynamicCastExpr->getSubExpr()->IgnoreParenImpCasts()); if (!subExprDecl) return true; auto varDecl = dyn_cast_or_null(subExprDecl->getDecl()); if (!varDecl) return true; auto varType = strip(dynamicCastExpr->getTypeAsWritten()); auto it = blockState.staticCastVars.find({ varDecl, varType }); if (it != blockState.staticCastVars.end()) { report(DiagnosticsEngine::Warning, "dynamic_cast after static_cast", compat::getBeginLoc(dynamicCastExpr)) << dynamicCastExpr->getSourceRange(); report(DiagnosticsEngine::Note, "static_cast here", it->second); return true; } auto loc = compat::getBeginLoc(dynamicCastExpr); if (compiler.getSourceManager().isMacroArgExpansion(loc) && (Lexer::getImmediateMacroNameForDiagnostics(loc, compiler.getSourceManager(), compiler.getLangOpts()) == "assert")) { return true; } blockState.dynamicCastVars.insert( { { varDecl, varType }, compat::getBeginLoc(dynamicCastExpr) }); return true; } loplugin::Plugin::Registration staticdynamic("staticdynamic"); } #endif // LO_CLANG_SHARED_PLUGINS /* vim:set shiftwidth=4 softtabstop=4 expandtab: */