cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / unnecessaryoverride.cxx
blob64239a62746f8e767b8ed898e0091316f9be9c4d
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 #include <cassert>
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <set>
16 #include <clang/AST/CXXInheritance.h>
17 #include "compat.hxx"
18 #include "plugin.hxx"
20 /**
21 look for methods where all they do is call their superclass method
24 namespace {
26 class UnnecessaryOverride:
27 public RecursiveASTVisitor<UnnecessaryOverride>, public loplugin::Plugin
29 public:
30 explicit UnnecessaryOverride(InstantiationData const & data): Plugin(data) {}
32 virtual void run() override
34 // ignore some files with problematic macros
35 StringRef fn( compiler.getSourceManager().getFileEntryForID(
36 compiler.getSourceManager().getMainFileID())->getName() );
37 if (fn == SRCDIR "/sd/source/ui/framework/factories/ChildWindowPane.cxx")
38 return;
39 if (fn == SRCDIR "/forms/source/component/Date.cxx")
40 return;
41 if (fn == SRCDIR "/forms/source/component/Time.cxx")
42 return;
43 if (fn == SRCDIR "/svx/source/dialog/hyperdlg.cxx")
44 return;
45 if (fn == SRCDIR "/svx/source/dialog/rubydialog.cxx")
46 return;
47 if (fn.startswith(SRCDIR "/canvas"))
48 return;
49 if (fn == SRCDIR "/sc/source/ui/view/spelldialog.cxx")
50 return;
51 if (fn == SRCDIR "/sd/source/ui/dlg/SpellDialogChildWindow.cxx")
52 return;
53 // HAVE_ODBC_ADMINISTRATION
54 if (fn == SRCDIR "/dbaccess/source/ui/dlg/dsselect.cxx")
55 return;
57 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
60 bool VisitCXXMethodDecl(const CXXMethodDecl *);
62 private:
63 const CXXMethodDecl * findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl *);
64 bool BaseCheckCallback(
65 const CXXRecordDecl *BaseDefinition
66 #if CLANG_VERSION < 30800
67 , void *
68 #endif
72 bool UnnecessaryOverride::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
74 if (ignoreLocation(methodDecl->getCanonicalDecl()) || !methodDecl->doesThisDeclarationHaveABody()) {
75 return true;
77 if (isa<CXXConstructorDecl>(methodDecl) || isa<CXXDestructorDecl>(methodDecl)) {
78 return true;
81 // if we are overriding more than one method, then this is a disambiguating override
82 if (methodDecl->isVirtual()) {
83 if (methodDecl->size_overridden_methods() != 1)
85 return true;
88 // sometimes the disambiguation happens in a base class
89 StringRef aFileName = compiler.getSourceManager().getFilename(compiler.getSourceManager().getSpellingLoc(methodDecl->getLocStart()));
90 if (aFileName == SRCDIR "/comphelper/source/property/propertycontainer.cxx")
91 return true;
92 // not sure what is happening here
93 if (aFileName == SRCDIR "/extensions/source/bibliography/datman.cxx")
94 return true;
95 // some very creative method hiding going on here
96 if (aFileName == SRCDIR "/svx/source/dialog/checklbx.cxx")
97 return true;
98 // entertaining template magic
99 if (aFileName == SRCDIR "/sc/source/ui/vba/vbaformatcondition.cxx")
100 return true;
101 // not sure what is going on here, but removing the override causes a crash
102 if (methodDecl->getQualifiedNameAsString() == "SwXTextDocument::queryAdapter")
103 return true;
106 const CXXMethodDecl* overriddenMethodDecl = findOverriddenOrSimilarMethodInSuperclasses(methodDecl);
107 if (!overriddenMethodDecl) {
108 return true;
111 if (compat::getReturnType(*methodDecl).getCanonicalType()
112 != compat::getReturnType(*overriddenMethodDecl).getCanonicalType())
114 return true;
117 //TODO: check for identical exception specifications
119 const CompoundStmt* compoundStmt = dyn_cast<CompoundStmt>(methodDecl->getBody());
120 if (!compoundStmt || compoundStmt->size() != 1)
121 return true;
123 const CXXMemberCallExpr* callExpr;
124 if (compat::getReturnType(*methodDecl).getCanonicalType()->isVoidType())
126 callExpr = dyn_cast<CXXMemberCallExpr>(*compoundStmt->body_begin());
128 else
130 auto returnStmt = dyn_cast<ReturnStmt>(*compoundStmt->body_begin());
131 if (returnStmt == nullptr) {
132 return true;
134 auto returnExpr = returnStmt->getRetValue();
135 if (returnExpr == nullptr) {
136 return true;
138 returnExpr = returnExpr->IgnoreImplicit();
140 // In something like
142 // Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery(
143 // const rtl::OUString& sql)
144 // throw(SQLException, RuntimeException, std::exception)
145 // {
146 // return OCommonStatement::executeQuery( sql );
147 // }
149 // look down through all the
151 // ReturnStmt
152 // `-ExprWithCleanups
153 // `-CXXConstructExpr
154 // `-MaterializeTemporaryExpr
155 // `-ImplicitCastExpr
156 // `-CXXBindTemporaryExpr
157 // `-CXXMemberCallExpr
159 // where the fact that the overriding and overridden function have identical
160 // return types makes us confident that all we need to check here is whether
161 // there's an (arbitrary, one-argument) CXXConstructorExpr and
162 // CXXBindTemporaryExpr in between:
163 if (auto ctorExpr = dyn_cast<CXXConstructExpr>(returnExpr)) {
164 if (ctorExpr->getNumArgs() == 1) {
165 auto tempExpr1 = ctorExpr->getArg(0)->IgnoreImplicit();
166 if (auto tempExpr2 = dyn_cast<CXXBindTemporaryExpr>(tempExpr1))
168 returnExpr = tempExpr2->getSubExpr();
170 else if (auto tempExpr2 = dyn_cast<CXXMemberCallExpr>(tempExpr1))
172 returnExpr = tempExpr2;
177 callExpr = dyn_cast<CXXMemberCallExpr>(returnExpr->IgnoreParenImpCasts());
180 if (!callExpr || callExpr->getMethodDecl() != overriddenMethodDecl)
181 return true;
182 const Expr* expr1 = callExpr->getImplicitObjectArgument()->IgnoreImpCasts();
183 if (!expr1)
184 return true;
185 const CXXThisExpr* expr2 = dyn_cast_or_null<CXXThisExpr>(expr1);
186 if (!expr2)
187 return true;
188 for (unsigned i = 0; i<callExpr->getNumArgs(); ++i) {
189 // ignore ImplicitCastExpr
190 const DeclRefExpr * declRefExpr = dyn_cast<DeclRefExpr>(callExpr->getArg(i)->IgnoreImplicit());
191 if (!declRefExpr || declRefExpr->getDecl() != methodDecl->getParamDecl(i))
192 return true;
195 report(
196 DiagnosticsEngine::Warning, "%0%1 function just calls %2 parent",
197 methodDecl->getSourceRange().getBegin())
198 << methodDecl->getAccess()
199 << (methodDecl->isVirtual() ? " virtual" : "")
200 << overriddenMethodDecl->getAccess()
201 << methodDecl->getSourceRange();
202 if (methodDecl->getCanonicalDecl()->getLocation() != methodDecl->getLocation()) {
203 const CXXMethodDecl* pOther = methodDecl->getCanonicalDecl();
204 report(
205 DiagnosticsEngine::Note,
206 "method declaration here",
207 pOther->getLocStart())
208 << pOther->getSourceRange();
210 return true;
213 const CXXMethodDecl* UnnecessaryOverride::findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl* methodDecl)
215 if (methodDecl->isVirtual()) {
216 return *methodDecl->begin_overridden_methods();
218 if (!methodDecl->getDeclName().isIdentifier()) {
219 return nullptr;
222 #if CLANG_VERSION < 30800
223 return nullptr;
224 #else
225 std::vector<const CXXMethodDecl*> maSimilarMethods;
227 auto BaseMatchesCallback = [&](const CXXBaseSpecifier *cxxBaseSpecifier, CXXBasePath& )
229 if (cxxBaseSpecifier->getAccessSpecifier() != AS_public && cxxBaseSpecifier->getAccessSpecifier() != AS_protected)
230 return false;
231 if (!cxxBaseSpecifier->getType().getTypePtr())
232 return false;
233 const CXXRecordDecl* baseCXXRecordDecl = cxxBaseSpecifier->getType()->getAsCXXRecordDecl();
234 if (!baseCXXRecordDecl)
235 return false;
236 if (baseCXXRecordDecl->isInvalidDecl())
237 return false;
238 for (const CXXMethodDecl* baseMethod : baseCXXRecordDecl->methods())
240 if (!baseMethod->getDeclName().isIdentifier() || methodDecl->getName() != baseMethod->getName()) {
241 continue;
243 if (compat::getReturnType(*methodDecl).getCanonicalType()
244 != compat::getReturnType(*baseMethod).getCanonicalType())
246 continue;
248 if (methodDecl->param_size() != baseMethod->param_size())
249 continue;
250 if (methodDecl->getNumParams() != baseMethod->getNumParams())
251 continue;
252 bool bParamsMatch = true;
253 for (unsigned i=0; i<methodDecl->param_size(); ++i)
255 if (methodDecl->parameters()[i]->getType() != baseMethod->parameters()[i]->getType())
257 bParamsMatch = false;
258 break;
261 if (bParamsMatch)
262 maSimilarMethods.push_back(baseMethod);
264 return false;
267 CXXBasePaths aPaths;
268 methodDecl->getParent()->lookupInBases(BaseMatchesCallback, aPaths);
270 if (maSimilarMethods.size() == 1) {
271 return maSimilarMethods[0];
273 return nullptr;
274 #endif
278 loplugin::Plugin::Registration< UnnecessaryOverride > X("unnecessaryoverride", true);
282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */