1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #if !defined _WIN32 //TODO, #include <sys/file.h>
16 #include <unordered_set>
22 #include "config_clang.h"
28 #include "clang/AST/ParentMapContext.h"
31 Look for class fields that are always cast to some subtype,
32 which indicates that they should probably just be declared to be that subtype.
34 TODO add checking for dynamic_cast/static_cast on
43 std::string parentClass
;
44 std::string fieldName
;
45 std::string fieldType
;
46 std::string sourceLocation
;
49 // try to limit the voluminous output a little
50 static std::unordered_multimap
<const FieldDecl
*, const CXXRecordDecl
*> castMap
;
52 class FieldCast
: public loplugin::FilteringPlugin
<FieldCast
>
55 explicit FieldCast(loplugin::InstantiationData
const& data
)
56 : FilteringPlugin(data
)
60 virtual void run() override
;
62 bool VisitCXXStaticCastExpr(const CXXStaticCastExpr
*);
63 bool VisitCXXDynamicCastExpr(const CXXDynamicCastExpr
*);
64 bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr
*);
67 MyFieldInfo
niceName(const FieldDecl
*);
68 void checkCast(const CXXNamedCastExpr
*);
73 handler
.enableTreeWideAnalysisMode();
75 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
77 if (!isUnitTestMode())
79 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
80 // writing to the same logfile
82 output
.reserve(64 * 1024);
83 for (const auto& pair
: castMap
)
85 MyFieldInfo s
= niceName(pair
.first
);
86 output
+= "cast:\t" + s
.parentClass
//
87 + "\t" + s
.fieldName
//
88 + "\t" + s
.fieldType
//
89 + "\t" + s
.sourceLocation
//
90 + "\t" + pair
.second
->getQualifiedNameAsString() //
94 myfile
.open(WORKDIR
"/loplugin.fieldcast.log", std::ios::app
| std::ios::out
);
100 for (const auto& pair
: castMap
)
101 report(DiagnosticsEngine::Warning
, "cast %0", pair
.first
->getBeginLoc())
102 << pair
.second
->getQualifiedNameAsString();
106 MyFieldInfo
FieldCast::niceName(const FieldDecl
* fieldDecl
)
110 const RecordDecl
* recordDecl
= fieldDecl
->getParent();
112 if (const CXXRecordDecl
* cxxRecordDecl
= dyn_cast
<CXXRecordDecl
>(recordDecl
))
114 if (cxxRecordDecl
->getTemplateInstantiationPattern())
115 cxxRecordDecl
= cxxRecordDecl
->getTemplateInstantiationPattern();
116 aInfo
.parentClass
= cxxRecordDecl
->getQualifiedNameAsString();
120 aInfo
.parentClass
= recordDecl
->getQualifiedNameAsString();
123 aInfo
.fieldName
= fieldDecl
->getNameAsString();
124 // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
125 size_t idx
= aInfo
.fieldName
.find(SRCDIR
);
126 if (idx
!= std::string::npos
)
128 aInfo
.fieldName
= aInfo
.fieldName
.replace(idx
, strlen(SRCDIR
), "");
130 aInfo
.fieldType
= fieldDecl
->getType().getAsString();
132 SourceLocation expansionLoc
133 = compiler
.getSourceManager().getExpansionLoc(fieldDecl
->getLocation());
134 StringRef name
= getFilenameOfLocation(expansionLoc
);
136 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
137 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
138 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
143 bool FieldCast::VisitCXXDynamicCastExpr(const CXXDynamicCastExpr
* expr
)
149 bool FieldCast::VisitCXXStaticCastExpr(const CXXStaticCastExpr
* expr
)
155 bool FieldCast::VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr
* expr
)
161 void FieldCast::checkCast(const CXXNamedCastExpr
* expr
)
163 if (ignoreLocation(expr
))
165 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(expr
->getBeginLoc())))
167 auto castToType
= expr
->getTypeAsWritten()->getPointeeCXXRecordDecl();
170 const Expr
* subExpr
= compat::getSubExprAsWritten(expr
);
171 const FieldDecl
* fieldDecl
= nullptr;
172 if (const MemberExpr
* memberExpr
= dyn_cast_or_null
<MemberExpr
>(subExpr
->IgnoreImplicit()))
174 fieldDecl
= dyn_cast_or_null
<FieldDecl
>(memberExpr
->getMemberDecl());
176 else if (const CXXMemberCallExpr
* memberCallExpr
177 = dyn_cast_or_null
<CXXMemberCallExpr
>(subExpr
->IgnoreImplicit()))
179 if (!memberCallExpr
->getMethodDecl()->getIdentifier()
180 || memberCallExpr
->getMethodDecl()->getName() != "get")
182 const MemberExpr
* memberExpr
= dyn_cast_or_null
<MemberExpr
>(
183 memberCallExpr
->getImplicitObjectArgument()->IgnoreImplicit());
186 fieldDecl
= dyn_cast_or_null
<FieldDecl
>(memberExpr
->getMemberDecl());
190 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getBeginLoc())))
193 // ignore casting to a less specific type
194 auto castFromType
= subExpr
->getType()->getPointeeCXXRecordDecl();
195 if (castFromType
&& castFromType
->isDerivedFrom(castToType
))
198 castMap
.emplace(fieldDecl
, castToType
);
201 loplugin::Plugin::Registration
<FieldCast
> X("fieldcast", false);
206 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */