1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 // Find uses of dynamic_cast that cast between unrelated classes, which is suspicious and might
11 // indicate a bug. The heuristic used to consider two classes unrelated is that neither derives
12 // from the other (directly or indirectly) and they do not both virtually derive (directly or
13 // indirectly) from a common third class. Additionally, class definitions can be attributed with
14 // SAL_LOPLUGIN_ANNOTATE("crosscast") (from sal/types.h) to suppress false warnings about known-good
15 // cases casting from or to such a class.
17 #ifndef LO_CLANG_SHARED_PLUGINS
27 void computeVirtualBases(CXXRecordDecl
const* decl
, std::set
<CXXRecordDecl
const*>* vbases
)
29 assert(vbases
!= nullptr);
30 for (auto const& i
: decl
->bases())
32 auto const d
= i
.getType()->getAsCXXRecordDecl();
39 if (!vbases
->insert(d
->getCanonicalDecl()).second
)
41 // As we track the already computed virtual bases in vbases anyway, we can cheaply
42 // optimize the case where we see a virtual base again, even though we don't bother
43 // to optimize the case where we see a non-virtual base multiple times:
47 computeVirtualBases(d
, vbases
);
51 bool compareVirtualBases(CXXRecordDecl
const* decl
, std::set
<CXXRecordDecl
const*>& vbases
)
53 for (auto const& i
: decl
->bases())
55 auto const d
= i
.getType()->getAsCXXRecordDecl();
60 if (i
.isVirtual() && vbases
.count(d
->getCanonicalDecl()) == 1)
64 if (compareVirtualBases(d
, vbases
))
72 bool haveCommonVirtualBase(CXXRecordDecl
const* decl1
, CXXRecordDecl
const* decl2
)
74 std::set
<CXXRecordDecl
const*> vbases
;
75 computeVirtualBases(decl1
, &vbases
);
76 return compareVirtualBases(decl2
, vbases
);
79 bool isAllowedInCrossCasts(CXXRecordDecl
const* decl
)
81 auto const def
= decl
->getDefinition();
86 for (auto const attr
: def
->specific_attrs
<AnnotateAttr
>())
88 if (attr
->getAnnotation() == "loplugin:crosscast")
96 class CrossCast final
: public loplugin::FilteringPlugin
<CrossCast
>
99 explicit CrossCast(loplugin::InstantiationData
const& data
)
100 : FilteringPlugin(data
)
104 bool preRun() override
{ return compiler
.getLangOpts().CPlusPlus
; }
110 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
114 bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr
const* expr
)
116 if (ignoreLocation(expr
))
120 auto const t2
= expr
->getTypeAsWritten();
121 if (t2
->isVoidPointerType())
125 auto const d2
= t2
->getPointeeCXXRecordDecl();
130 auto const t1
= compat::getSubExprAsWritten(expr
)->getType();
132 if (auto const t
= t1a
->getAs
<clang::PointerType
>())
134 t1a
= t
->getPointeeType();
136 auto const d1
= t1a
->getAsCXXRecordDecl();
141 if (d1
->getCanonicalDecl() == d2
->getCanonicalDecl() || d1
->isDerivedFrom(d2
)
142 || d2
->isDerivedFrom(d1
) || haveCommonVirtualBase(d1
, d2
))
146 if (isAllowedInCrossCasts(d1
) || isAllowedInCrossCasts(d2
))
150 if (suppressWarningAt(expr
->getBeginLoc()))
154 report(DiagnosticsEngine::Warning
, "suspicious dynamic cross cast from %0 to %1",
156 << t1
<< t2
<< expr
->getSourceRange();
161 loplugin::Plugin::Registration
<CrossCast
> crosscast("crosscast");
166 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */