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/.
16 #include <clang/AST/CXXInheritance.h>
21 look for methods where all they do is call their superclass method
26 class UnnecessaryOverride
:
27 public RecursiveASTVisitor
<UnnecessaryOverride
>, public loplugin::Plugin
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")
39 if (fn
== SRCDIR
"/forms/source/component/Date.cxx")
41 if (fn
== SRCDIR
"/forms/source/component/Time.cxx")
43 if (fn
== SRCDIR
"/svx/source/dialog/hyperdlg.cxx")
45 if (fn
== SRCDIR
"/svx/source/dialog/rubydialog.cxx")
47 if (fn
.startswith(SRCDIR
"/canvas"))
49 if (fn
== SRCDIR
"/sc/source/ui/view/spelldialog.cxx")
51 if (fn
== SRCDIR
"/sd/source/ui/dlg/SpellDialogChildWindow.cxx")
53 // HAVE_ODBC_ADMINISTRATION
54 if (fn
== SRCDIR
"/dbaccess/source/ui/dlg/dsselect.cxx")
57 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
60 bool VisitCXXMethodDecl(const CXXMethodDecl
*);
63 const CXXMethodDecl
* findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl
*);
64 bool BaseCheckCallback(
65 const CXXRecordDecl
*BaseDefinition
66 #if CLANG_VERSION < 30800
72 bool UnnecessaryOverride::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
74 if (ignoreLocation(methodDecl
->getCanonicalDecl()) || !methodDecl
->doesThisDeclarationHaveABody()) {
77 if (isa
<CXXConstructorDecl
>(methodDecl
) || isa
<CXXDestructorDecl
>(methodDecl
)) {
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)
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")
92 // not sure what is happening here
93 if (aFileName
== SRCDIR
"/extensions/source/bibliography/datman.cxx")
95 // some very creative method hiding going on here
96 if (aFileName
== SRCDIR
"/svx/source/dialog/checklbx.cxx")
98 // entertaining template magic
99 if (aFileName
== SRCDIR
"/sc/source/ui/vba/vbaformatcondition.cxx")
101 // not sure what is going on here, but removing the override causes a crash
102 if (methodDecl
->getQualifiedNameAsString() == "SwXTextDocument::queryAdapter")
106 const CXXMethodDecl
* overriddenMethodDecl
= findOverriddenOrSimilarMethodInSuperclasses(methodDecl
);
107 if (!overriddenMethodDecl
) {
111 if (compat::getReturnType(*methodDecl
).getCanonicalType()
112 != compat::getReturnType(*overriddenMethodDecl
).getCanonicalType())
117 //TODO: check for identical exception specifications
119 const CompoundStmt
* compoundStmt
= dyn_cast
<CompoundStmt
>(methodDecl
->getBody());
120 if (!compoundStmt
|| compoundStmt
->size() != 1)
123 const CXXMemberCallExpr
* callExpr
;
124 if (compat::getReturnType(*methodDecl
).getCanonicalType()->isVoidType())
126 callExpr
= dyn_cast
<CXXMemberCallExpr
>(*compoundStmt
->body_begin());
130 auto returnStmt
= dyn_cast
<ReturnStmt
>(*compoundStmt
->body_begin());
131 if (returnStmt
== nullptr) {
134 auto returnExpr
= returnStmt
->getRetValue();
135 if (returnExpr
== nullptr) {
138 returnExpr
= returnExpr
->IgnoreImplicit();
142 // Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery(
143 // const rtl::OUString& sql)
144 // throw(SQLException, RuntimeException, std::exception)
146 // return OCommonStatement::executeQuery( sql );
149 // look down through all the
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
)
182 const Expr
* expr1
= callExpr
->getImplicitObjectArgument()->IgnoreImpCasts();
185 const CXXThisExpr
* expr2
= dyn_cast_or_null
<CXXThisExpr
>(expr1
);
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
))
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();
205 DiagnosticsEngine::Note
,
206 "method declaration here",
207 pOther
->getLocStart())
208 << pOther
->getSourceRange();
213 const CXXMethodDecl
* UnnecessaryOverride::findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl
* methodDecl
)
215 if (methodDecl
->isVirtual()) {
216 return *methodDecl
->begin_overridden_methods();
218 if (!methodDecl
->getDeclName().isIdentifier()) {
222 #if CLANG_VERSION < 30800
225 std::vector
<const CXXMethodDecl
*> maSimilarMethods
;
227 auto BaseMatchesCallback
= [&](const CXXBaseSpecifier
*cxxBaseSpecifier
, CXXBasePath
& )
229 if (cxxBaseSpecifier
->getAccessSpecifier() != AS_public
&& cxxBaseSpecifier
->getAccessSpecifier() != AS_protected
)
231 if (!cxxBaseSpecifier
->getType().getTypePtr())
233 const CXXRecordDecl
* baseCXXRecordDecl
= cxxBaseSpecifier
->getType()->getAsCXXRecordDecl();
234 if (!baseCXXRecordDecl
)
236 if (baseCXXRecordDecl
->isInvalidDecl())
238 for (const CXXMethodDecl
* baseMethod
: baseCXXRecordDecl
->methods())
240 if (!baseMethod
->getDeclName().isIdentifier() || methodDecl
->getName() != baseMethod
->getName()) {
243 if (compat::getReturnType(*methodDecl
).getCanonicalType()
244 != compat::getReturnType(*baseMethod
).getCanonicalType())
248 if (methodDecl
->param_size() != baseMethod
->param_size())
250 if (methodDecl
->getNumParams() != baseMethod
->getNumParams())
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;
262 maSimilarMethods
.push_back(baseMethod
);
268 methodDecl
->getParent()->lookupInBases(BaseMatchesCallback
, aPaths
);
270 if (maSimilarMethods
.size() == 1) {
271 return maSimilarMethods
[0];
278 loplugin::Plugin::Registration
< UnnecessaryOverride
> X("unnecessaryoverride", true);
282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */