[Libreoffice-commits] core.git: compilerplugins/clang

Stephan Bergmann sbergman at redhat.com
Tue Jan 12 06:08:46 PST 2016


 compilerplugins/clang/faileddyncast.cxx |  122 ++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

New commits:
commit 75f0f9d5538577e0d4923b4d46ba6f88b6199814
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Tue Jan 12 15:00:33 2016 +0100

    New loplugin:faileddyncast
    
    Offline discussion about tdf#96067 "Crash on undo row inserts" brought up the
    idea to warn about cases where uses of dynamic_cast are statically knwon to
    always fail.  Clang's clang::AST::CXXDynamicCastExpr::isAlwaysNull already
    implements such a check, reporting true if the casted-from class is final, but
    has two issues:
    
    For one, it does not work for template code, when one of the involved types is a
    template parameter type (so e.g., DestType->castAs<PointerType>() can crash).
    
    For another, it misses the opportunity to report true if the casted-to type is
    final and only derives from the casted-from type non-publicly.  My hope was that
    this, after the "final" decorations in 548c43238d02b34cf73e7c2ca1a912ee4fe82544
    "Mark some classes as final," might turn up the culprit of tdf#96067 (with a
    scenario similar to the failed dynamic_cast on private derivation in
    63b67ab5cab8cf7576a68cabe5fb1a42c6ad800c "Use public derivation, and remove
    then-unnecessary downcasts")---but not so.
    
    Change-Id: I962ee19820758f9c601f4a292da7f37fa9dff5ce

diff --git a/compilerplugins/clang/faileddyncast.cxx b/compilerplugins/clang/faileddyncast.cxx
new file mode 100644
index 0000000..6fef227
--- /dev/null
+++ b/compilerplugins/clang/faileddyncast.cxx
@@ -0,0 +1,122 @@
+/* -*- 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 "clang/AST/CXXInheritance.h"
+
+#include "plugin.hxx"
+
+namespace {
+
+// cf. Clang's clang::AST::CXXDynamicCastExpr::isAlwaysNull
+// (lib/AST/ExprCXX.cpp):
+bool isAlwaysNull(CXXDynamicCastExpr const * expr) {
+  QualType SrcType = expr->getSubExpr()->getType();
+  QualType DestType = expr->getType();
+
+  if (const PointerType *SrcPTy = SrcType->getAs<PointerType>()) {
+    SrcType = SrcPTy->getPointeeType();
+#if 0
+    DestType = DestType->castAs<PointerType>()->getPointeeType();
+#else
+    auto DstPTy = DestType->getAs<PointerType>();
+    if (!DstPTy)
+      return false;
+    DestType = DstPTy->getPointeeType();
+#endif
+  }
+
+  if (DestType->isVoidType())
+    return false;
+
+#if 0
+  const CXXRecordDecl *SrcRD =
+    cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl());
+#else
+  auto SrcRT = SrcType->getAs<RecordType>();
+  if (!SrcRT)
+    return false;
+  const CXXRecordDecl *SrcRD = cast<CXXRecordDecl>(SrcRT->getDecl());
+#endif
+
+#if 0
+  if (!SrcRD->hasAttr<FinalAttr>())
+    return false;
+#endif
+
+#if 0
+  const CXXRecordDecl *DestRD =
+    cast<CXXRecordDecl>(DestType->castAs<RecordType>()->getDecl());
+#else
+  auto DestRT = DestType->getAs<RecordType>();
+  if (!DestRT)
+    return false;
+  const CXXRecordDecl *DestRD = cast<CXXRecordDecl>(DestRT->getDecl());
+#endif
+
+#if 1
+  if (!(SrcRD && DestRD))
+    return false;
+
+  if (DestRD->hasAttr<FinalAttr>()) {
+    CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
+                       /*DetectVirtual=*/false);
+    if (DestRD->isDerivedFrom(SrcRD, Paths) &&
+        std::all_of(Paths.begin(), Paths.end(),
+                    [](CXXBasePath const & Path) {
+                        return Path.Access != AS_public; }))
+      return true;
+  }
+
+  if (!SrcRD->hasAttr<FinalAttr>())
+    return false;
+#endif
+
+  return !DestRD->isDerivedFrom(SrcRD);
+}
+
+class FailedDynCast:
+    public RecursiveASTVisitor<FailedDynCast>, public loplugin::Plugin
+{
+public:
+    explicit FailedDynCast(InstantiationData const & data): Plugin(data) {}
+
+    bool shouldVisitTemplateInstantiations() const { return true; }
+
+    void run() override;
+
+    bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr);
+};
+
+void FailedDynCast::run() {
+    if (compiler.getLangOpts().CPlusPlus) {
+        TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+    }
+}
+
+bool FailedDynCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
+    if (ignoreLocation(expr)) {
+        return true;
+    }
+    if (isAlwaysNull(expr)) {
+        report(
+            DiagnosticsEngine::Warning,
+            "dynamic_cast from %0 to %1 always fails", expr->getLocStart())
+            << expr->getSubExpr()->getType() << expr->getType()
+            << expr->getSourceRange();
+    }
+    return true;
+}
+
+loplugin::Plugin::Registration<FailedDynCast> X("faileddyncast");
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list