1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 #ifndef LO_CLANG_SHARED_PLUGINS
10 
11 #include <algorithm>
12 
13 #include "clang/AST/Attr.h"
14 #include "clang/AST/CXXInheritance.h"
15 
16 #include "plugin.hxx"
17 
18 namespace {
19 
20 // cf. Clang's clang::AST::CXXDynamicCastExpr::isAlwaysNull
21 // (lib/AST/ExprCXX.cpp):
isAlwaysNull(CXXDynamicCastExpr const * expr)22 bool isAlwaysNull(CXXDynamicCastExpr const * expr) {
23   QualType SrcType = expr->getSubExpr()->getType();
24   QualType DestType = expr->getType();
25 
26   if (const clang::PointerType *SrcPTy = SrcType->getAs<clang::PointerType>()) {
27     SrcType = SrcPTy->getPointeeType();
28 #if 0
29     DestType = DestType->castAs<PointerType>()->getPointeeType();
30 #else
31     auto DstPTy = DestType->getAs<clang::PointerType>();
32     if (!DstPTy)
33       return false;
34     DestType = DstPTy->getPointeeType();
35 #endif
36   }
37 
38   if (DestType->isVoidType())
39     return false;
40 
41 #if 0
42   const CXXRecordDecl *SrcRD =
43     cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl());
44 #else
45   auto SrcRT = SrcType->getAs<RecordType>();
46   if (!SrcRT)
47     return false;
48   const CXXRecordDecl *SrcRD = cast<CXXRecordDecl>(SrcRT->getDecl());
49 #endif
50 
51 #if 0
52   if (!SrcRD->hasAttr<FinalAttr>())
53     return false;
54 #endif
55 
56 #if 0
57   const CXXRecordDecl *DestRD =
58     cast<CXXRecordDecl>(DestType->castAs<RecordType>()->getDecl());
59 #else
60   auto DestRT = DestType->getAs<RecordType>();
61   if (!DestRT)
62     return false;
63   const CXXRecordDecl *DestRD = cast<CXXRecordDecl>(DestRT->getDecl());
64 #endif
65 
66 #if 1
67   if (!(SrcRD && DestRD))
68     return false;
69 
70   if (DestRD->hasAttr<FinalAttr>()) {
71     CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
72                        /*DetectVirtual=*/false);
73     if (DestRD->isDerivedFrom(SrcRD, Paths) &&
74         std::all_of(Paths.begin(), Paths.end(),
75                     [](CXXBasePath const & Path) {
76                         return Path.Access != AS_public; }))
77       return true;
78   }
79 
80   if (!SrcRD->hasAttr<FinalAttr>())
81     return false;
82 #endif
83 
84 #if 0
85   return !DestRD->isDerivedFrom(SrcRD);
86 #else
87   return !(DestRD->isDerivedFrom(SrcRD)
88            || SrcRD->isDerivedFrom(DestRD)
89            || SrcRD == DestRD);
90 #endif
91 }
92 
93 class FailedDynCast:
94     public loplugin::FilteringPlugin<FailedDynCast>
95 {
96 public:
FailedDynCast(loplugin::InstantiationData const & data)97     explicit FailedDynCast(loplugin::InstantiationData const & data):
98         FilteringPlugin(data) {}
99 
shouldVisitTemplateInstantiations() const100     bool shouldVisitTemplateInstantiations() const { return true; }
101 
102     bool preRun() override;
103     void run() override;
104 
105     bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr);
106 };
107 
preRun()108 bool FailedDynCast::preRun() {
109     return compiler.getLangOpts().CPlusPlus;
110 }
111 
run()112 void FailedDynCast::run() {
113     if (preRun()) {
114         TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
115     }
116 }
117 
VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr)118 bool FailedDynCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
119     if (ignoreLocation(expr)) {
120         return true;
121     }
122     if (isAlwaysNull(expr)) {
123         report(
124             DiagnosticsEngine::Warning,
125             "dynamic_cast from %0 to %1 always fails", compat::getBeginLoc(expr))
126             << expr->getSubExpr()->getType() << expr->getType()
127             << expr->getSourceRange();
128     }
129     return true;
130 }
131 
132 loplugin::Plugin::Registration<FailedDynCast> faileddyncast("faileddyncast");
133 
134 } // namespace
135 
136 #endif // LO_CLANG_SHARED_PLUGINS
137 
138 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
139