lok: getSlideShowInfo: interactions: check that properties are available
[LibreOffice.git] / compilerplugins / clang / fieldcast.cxx
blob0807aef9b3c3f0d65851e2c5c10b64e093fc7cb9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #if !defined _WIN32 //TODO, #include <sys/file.h>
12 #include <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <unordered_set>
17 #include <vector>
18 #include <algorithm>
19 #include <sys/file.h>
20 #include <unistd.h>
22 #include "config_clang.h"
24 #include "plugin.hxx"
25 #include "compat.hxx"
26 #include "check.hxx"
28 #include "clang/AST/ParentMapContext.h"
30 /**
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
35 unique_ptr
36 shared_ptr
39 namespace
41 struct MyFieldInfo
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>
54 public:
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*);
66 private:
67 MyFieldInfo niceName(const FieldDecl*);
68 void checkCast(const CXXNamedCastExpr*);
71 void FieldCast::run()
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
81 std::string output;
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() //
91 + "\n";
93 std::ofstream myfile;
94 myfile.open(WORKDIR "/loplugin.fieldcast.log", std::ios::app | std::ios::out);
95 myfile << output;
96 myfile.close();
98 else
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)
108 MyFieldInfo aInfo;
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();
118 else
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);
135 aInfo.sourceLocation
136 = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
137 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
138 loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
140 return aInfo;
143 bool FieldCast::VisitCXXDynamicCastExpr(const CXXDynamicCastExpr* expr)
145 checkCast(expr);
146 return true;
149 bool FieldCast::VisitCXXStaticCastExpr(const CXXStaticCastExpr* expr)
151 checkCast(expr);
152 return true;
155 bool FieldCast::VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr* expr)
157 checkCast(expr);
158 return true;
161 void FieldCast::checkCast(const CXXNamedCastExpr* expr)
163 if (ignoreLocation(expr))
164 return;
165 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(expr->getBeginLoc())))
166 return;
167 auto castToType = expr->getTypeAsWritten()->getPointeeCXXRecordDecl();
168 if (!castToType)
169 return;
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")
181 return;
182 const MemberExpr* memberExpr = dyn_cast_or_null<MemberExpr>(
183 memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit());
184 if (!memberExpr)
185 return;
186 fieldDecl = dyn_cast_or_null<FieldDecl>(memberExpr->getMemberDecl());
188 if (!fieldDecl)
189 return;
190 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getBeginLoc())))
191 return;
193 // ignore casting to a less specific type
194 auto castFromType = subExpr->getType()->getPointeeCXXRecordDecl();
195 if (castFromType && castFromType->isDerivedFrom(castToType))
196 return;
198 castMap.emplace(fieldDecl, castToType);
201 loplugin::Plugin::Registration<FieldCast> X("fieldcast", false);
204 #endif
206 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */