summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/staticdynamic.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/staticdynamic.cxx')
-rw-r--r--compilerplugins/clang/staticdynamic.cxx168
1 files changed, 168 insertions, 0 deletions
diff --git a/compilerplugins/clang/staticdynamic.cxx b/compilerplugins/clang/staticdynamic.cxx
new file mode 100644
index 000000000..6dc351546
--- /dev/null
+++ b/compilerplugins/clang/staticdynamic.cxx
@@ -0,0 +1,168 @@
+/* -*- 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 <cassert>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <vector>
+
+#include "config_clang.h"
+
+#include "check.hxx"
+#include "plugin.hxx"
+
+namespace
+{
+class StaticDynamic : public loplugin::FilteringPlugin<StaticDynamic>
+{
+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<std::pair<VarDecl const*, clang::Type const*>, SourceLocation> staticCastVars;
+ std::map<std::pair<VarDecl const*, clang::Type const*>, SourceLocation> dynamicCastVars;
+ };
+ // only maintain state inside a single basic block, we're not trying to analyse
+ // cross-block interactions.
+ std::vector<BlockState> 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<clang::ReferenceType>()
+ ->getPointeeType()
+ ->getUnqualifiedDesugaredType();
+ return varType;
+}
+
+bool StaticDynamic::VisitCXXStaticCastExpr(CXXStaticCastExpr const* staticCastExpr)
+{
+ if (ignoreLocation(staticCastExpr))
+ return true;
+ auto subExprDecl = dyn_cast<DeclRefExpr>(staticCastExpr->getSubExpr()->IgnoreParenImpCasts());
+ if (!subExprDecl)
+ return true;
+ auto varDecl = dyn_cast_or_null<VarDecl>(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(staticCastExpr->getBeginLoc()));
+ // 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",
+ staticCastExpr->getBeginLoc())
+ << staticCastExpr->getSourceRange();
+ report(DiagnosticsEngine::Note, "dynamic_cast here", it->second);
+ return true;
+ }
+ blockState.staticCastVars.insert({ { varDecl, varType }, staticCastExpr->getBeginLoc() });
+ return true;
+}
+
+bool StaticDynamic::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const* dynamicCastExpr)
+{
+ if (ignoreLocation(dynamicCastExpr))
+ return true;
+
+ auto subExprDecl = dyn_cast<DeclRefExpr>(dynamicCastExpr->getSubExpr()->IgnoreParenImpCasts());
+ if (!subExprDecl)
+ return true;
+ auto varDecl = dyn_cast_or_null<VarDecl>(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",
+ dynamicCastExpr->getBeginLoc())
+ << dynamicCastExpr->getSourceRange();
+ report(DiagnosticsEngine::Note, "static_cast here", it->second);
+ return true;
+ }
+ auto loc = dynamicCastExpr->getBeginLoc();
+ if (compiler.getSourceManager().isMacroArgExpansion(loc)
+ && (Lexer::getImmediateMacroNameForDiagnostics(loc, compiler.getSourceManager(),
+ compiler.getLangOpts())
+ == "assert"))
+ {
+ return true;
+ }
+ blockState.dynamicCastVars.insert({ { varDecl, varType }, dynamicCastExpr->getBeginLoc() });
+ return true;
+}
+
+loplugin::Plugin::Registration<StaticDynamic> staticdynamic("staticdynamic");
+}
+
+#endif // LO_CLANG_SHARED_PLUGINS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */