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 #ifndef LO_CLANG_SHARED_PLUGINS
18 #include <clang/AST/CXXInheritance.h>
22 look for methods where all they do is call their superclass method
27 bool hasMultipleBaseInstances_(
28 CXXRecordDecl
const * derived
, CXXRecordDecl
const * canonicBase
,
29 bool & hasAsNonVirtualBase
, bool & hasAsVirtualBase
)
31 for (auto i
= derived
->bases_begin(); i
!= derived
->bases_end(); ++i
) {
32 auto const cls
= i
->getType()->getAsCXXRecordDecl();
34 assert(i
->getType()->isDependentType());
35 // Conservatively assume "yes" for dependent bases:
38 if (cls
->getCanonicalDecl() == canonicBase
) {
40 if (hasAsNonVirtualBase
) {
43 hasAsVirtualBase
= true;
45 if (hasAsNonVirtualBase
|| hasAsVirtualBase
) {
48 hasAsNonVirtualBase
= true;
50 } else if (hasMultipleBaseInstances_(
51 cls
, canonicBase
, hasAsNonVirtualBase
, hasAsVirtualBase
))
59 bool hasMultipleBaseInstances(
60 CXXRecordDecl
const * derived
, CXXRecordDecl
const * base
)
64 return hasMultipleBaseInstances_(
65 derived
, base
->getCanonicalDecl(), nonVirt
, virt
);
68 class UnnecessaryOverride
:
69 public loplugin::FilteringPlugin
<UnnecessaryOverride
>
72 explicit UnnecessaryOverride(loplugin::InstantiationData
const & data
): FilteringPlugin(data
) {}
74 virtual bool preRun() override
76 // ignore some files with problematic macros
77 StringRef
fn(handler
.getMainFileName());
78 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/framework/factories/ChildWindowPane.cxx"))
80 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/Date.cxx"))
82 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/Time.cxx"))
84 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/dialog/hyperdlg.cxx"))
86 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/dialog/rubydialog.cxx"))
88 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/canvas/"))
90 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/view/spelldialog.cxx"))
92 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/dlg/SpellDialogChildWindow.cxx"))
94 // HAVE_ODBC_ADMINISTRATION
95 if (loplugin::isSamePathname(fn
, SRCDIR
"/dbaccess/source/ui/dlg/dsselect.cxx"))
100 virtual void run() override
103 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
106 bool VisitCXXMethodDecl(const CXXMethodDecl
*);
109 const CXXMethodDecl
* findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl
*);
110 bool BaseCheckCallback(const CXXRecordDecl
*BaseDefinition
);
111 CXXMemberCallExpr
const * extractCallExpr(Expr
const *);
114 bool UnnecessaryOverride::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
116 if (ignoreLocation(methodDecl
->getCanonicalDecl())) {
119 if (isa
<CXXConstructorDecl
>(methodDecl
)) {
123 StringRef aFileName
= getFileNameOfSpellingLoc(
124 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(methodDecl
)));
126 if (isa
<CXXDestructorDecl
>(methodDecl
)
127 && !isInUnoIncludeFile(methodDecl
))
129 // the code is this method is only __compiled__ if OSL_DEBUG_LEVEL > 1
130 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/tools/source/stream/strmunx.cxx"))
133 // Warn about unnecessarily user-declared destructors.
134 // A destructor is deemed unnecessary if:
136 // * its class is only defined in the .cxx file (i.e., the virtual
137 // destructor is neither used to control the place of vtable
138 // emission, nor is its definition depending on types that may still
141 // the destructor is inline, the class definition is complete,
142 // and the class has no superclasses
143 // * it either does not have an explicit exception specification, or has
144 // a non-dependent explicit exception specification that is compatible
145 // with a non-dependent exception specification the destructor would
146 // have if it did not have an explicit one (TODO);
147 // * it is either defined as defaulted or with an empty body.
148 // Removing the user-declared destructor may cause the class to get an
149 // implicitly declared move constructor and/or move assignment operator;
150 // that is considered acceptable: If any subobject cannot be moved, the
151 // implicitly declared function will be defined as deleted (which is in
152 // practice not much different from not having it declared), and
153 // otherwise offering movability is likely even an improvement over not
154 // offering it due to a "pointless" user-declared destructor.
155 // Similarly, removing the user-declared destructor may cause the
156 // implicit definition of a copy constructor and/or copy assignment
157 // operator to change from being an obsolete feature to being a standard
158 // feature. That difference is not taken into account here.
159 auto cls
= methodDecl
->getParent();
160 if (methodDecl
->getAccess() != AS_public
)
164 if (!compiler
.getSourceManager().isInMainFile(
165 methodDecl
->getCanonicalDecl()->getLocation())
166 && !( methodDecl
->isInlined()))
170 // if it's virtual, but it has a base-class with a non-virtual destructor
171 if (methodDecl
->isVirtual())
173 bool baseWithVirtualDtor
= false;
174 for (auto baseSpecifier
= cls
->bases_begin(); baseSpecifier
!= cls
->bases_end(); ++baseSpecifier
)
176 const RecordType
* baseRecordType
= baseSpecifier
->getType()->getAs
<RecordType
>();
179 const CXXRecordDecl
* baseRecordDecl
= dyn_cast
<CXXRecordDecl
>(baseRecordType
->getDecl());
180 if (baseRecordDecl
&& baseRecordDecl
->getDestructor()
181 && baseRecordDecl
->getDestructor()->isVirtual())
183 baseWithVirtualDtor
= true;
188 if (!baseWithVirtualDtor
)
194 if (methodDecl
->isInlined()
195 && compiler
.getSourceManager().isInMainFile(methodDecl
->getLocation())
196 && !compiler
.getSourceManager().isInMainFile(methodDecl
->getCanonicalDecl()->getLocation()))
200 if (!methodDecl
->isExplicitlyDefaulted()) {
201 if (!methodDecl
->doesThisDeclarationHaveABody()
202 || methodDecl
->isLateTemplateParsed())
206 auto stmt
= dyn_cast
<CompoundStmt
>(methodDecl
->getBody());
207 if (stmt
== nullptr || stmt
->size() != 0) {
211 //TODO: exception specification
212 if (!(cls
->hasUserDeclaredCopyConstructor()
213 || cls
->hasUserDeclaredCopyAssignment()
214 || cls
->hasUserDeclaredMoveConstructor()
215 || cls
->hasUserDeclaredMoveAssignment()))
218 if ((cls
->needsImplicitMoveConstructor()
219 && !(cls
->hasUserDeclaredCopyConstructor()
220 || cls
->hasUserDeclaredCopyAssignment()
221 || cls
->hasUserDeclaredMoveAssignment()))
222 || (cls
->needsImplicitMoveAssignment()
223 && !(cls
->hasUserDeclaredCopyConstructor()
224 || cls
->hasUserDeclaredCopyAssignment()
225 || cls
->hasUserDeclaredMoveConstructor())))
227 report(DiagnosticsEngine::Fatal
, "TODO", methodDecl
->getLocation());
231 DiagnosticsEngine::Warning
, "unnecessary user-declared destructor",
232 methodDecl
->getLocation())
233 << methodDecl
->getSourceRange();
234 auto cd
= methodDecl
->getCanonicalDecl();
235 if (cd
->getLocation() != methodDecl
->getLocation()) {
236 report(DiagnosticsEngine::Note
, "declared here", cd
->getLocation())
237 << cd
->getSourceRange();
242 if (!methodDecl
->doesThisDeclarationHaveABody()
243 || methodDecl
->isLateTemplateParsed())
248 // If overriding more than one base member function, or one base member
249 // function that is available in multiple (non-virtual) base class
250 // instances, then this is a disambiguating override:
251 if (methodDecl
->isVirtual()) {
252 if (methodDecl
->size_overridden_methods() != 1)
256 if (hasMultipleBaseInstances(
257 methodDecl
->getParent(),
258 (*methodDecl
->begin_overridden_methods())->getParent()))
264 const CXXMethodDecl
* overriddenMethodDecl
= findOverriddenOrSimilarMethodInSuperclasses(methodDecl
);
265 if (!overriddenMethodDecl
) {
269 // Check for differences in default parameters:
270 unsigned const numParams
= methodDecl
->getNumParams();
271 assert(overriddenMethodDecl
->getNumParams() == numParams
);
272 for (unsigned i
= 0; i
!= numParams
; ++i
) {
273 if (checkIdenticalDefaultArguments(
274 methodDecl
->getParamDecl(i
)->getDefaultArg(),
275 overriddenMethodDecl
->getParamDecl(i
)->getDefaultArg())
276 != IdenticalDefaultArgumentsResult::Yes
)
282 if (methodDecl
->getReturnType().getCanonicalType()
283 != overriddenMethodDecl
->getReturnType().getCanonicalType())
288 //TODO: check for identical exception specifications
290 const CompoundStmt
* compoundStmt
= dyn_cast
<CompoundStmt
>(methodDecl
->getBody());
291 if (!compoundStmt
|| compoundStmt
->size() > 2)
294 const CXXMemberCallExpr
* callExpr
= nullptr;
295 if (compoundStmt
->size() == 1)
297 if (methodDecl
->getReturnType().getCanonicalType()->isVoidType())
299 if (auto const e
= dyn_cast
<Expr
>(*compoundStmt
->body_begin())) {
300 callExpr
= dyn_cast
<CXXMemberCallExpr
>(e
->IgnoreImplicit()->IgnoreParens());
305 auto returnStmt
= dyn_cast
<ReturnStmt
>(*compoundStmt
->body_begin());
306 if (returnStmt
== nullptr) {
309 auto returnExpr
= returnStmt
->getRetValue();
310 if (returnExpr
== nullptr) {
313 callExpr
= extractCallExpr(returnExpr
);
316 else if (!methodDecl
->getReturnType().getCanonicalType()->isVoidType())
318 /** handle constructions like
320 bool ret = Base::foo();
324 auto bodyIt
= compoundStmt
->body_begin();
325 auto declStmt
= dyn_cast
<DeclStmt
>(*bodyIt
);
326 if (!declStmt
|| !declStmt
->isSingleDecl())
328 auto varDecl
= dyn_cast
<VarDecl
>(declStmt
->getSingleDecl());
330 auto returnStmt
= dyn_cast
<ReturnStmt
>(*bodyIt
);
331 if (!varDecl
|| !returnStmt
)
333 Expr
const * retValue
= returnStmt
->getRetValue()->IgnoreParenImpCasts();
334 if (auto exprWithCleanups
= dyn_cast
<ExprWithCleanups
>(retValue
))
335 retValue
= exprWithCleanups
->getSubExpr()->IgnoreParenImpCasts();
336 if (auto constructExpr
= dyn_cast
<CXXConstructExpr
>(retValue
)) {
337 if (constructExpr
->getNumArgs() == 1)
338 retValue
= constructExpr
->getArg(0)->IgnoreParenImpCasts();
340 if (!isa
<DeclRefExpr
>(retValue
))
342 callExpr
= extractCallExpr(varDecl
->getInit());
345 if (!callExpr
|| callExpr
->getMethodDecl() != overriddenMethodDecl
)
347 const Expr
* expr1
= callExpr
->getImplicitObjectArgument()->IgnoreImpCasts();
350 const CXXThisExpr
* expr2
= dyn_cast_or_null
<CXXThisExpr
>(expr1
);
353 for (unsigned i
= 0; i
<callExpr
->getNumArgs(); ++i
) {
354 auto e
= callExpr
->getArg(i
)->IgnoreImplicit();
355 if (auto const e1
= dyn_cast
<CXXConstructExpr
>(e
)) {
356 if (e1
->getConstructor()->isCopyOrMoveConstructor() && e1
->getNumArgs() == 1) {
357 e
= e1
->getArg(0)->IgnoreImpCasts();
360 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(e
);
361 if (!declRefExpr
|| declRefExpr
->getDecl() != methodDecl
->getParamDecl(i
))
365 const CXXMethodDecl
* pOther
= nullptr;
366 if (methodDecl
->getCanonicalDecl()->getLocation() != methodDecl
->getLocation())
367 pOther
= methodDecl
->getCanonicalDecl();
370 StringRef aFileName
= getFileNameOfSpellingLoc(
371 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(pOther
)));
372 // SFX_DECL_CHILDWINDOW_WITHID macro
373 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/sfx2/childwin.hxx"))
378 DiagnosticsEngine::Warning
, "%0%1 function just calls %2 parent",
379 methodDecl
->getLocation())
380 << methodDecl
->getAccess()
381 << (methodDecl
->isVirtual() ? " virtual" : "")
382 << overriddenMethodDecl
->getAccess()
383 << methodDecl
->getSourceRange();
384 if (methodDecl
->getCanonicalDecl()->getLocation() != methodDecl
->getLocation()) {
385 const CXXMethodDecl
* pOther
= methodDecl
->getCanonicalDecl();
387 DiagnosticsEngine::Note
,
388 "method declaration here",
389 pOther
->getLocation())
390 << pOther
->getSourceRange();
395 const CXXMethodDecl
* UnnecessaryOverride::findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl
* methodDecl
)
397 if (methodDecl
->isVirtual()) {
398 return *methodDecl
->begin_overridden_methods();
400 if (!methodDecl
->getDeclName().isIdentifier()) {
404 const CXXMethodDecl
* similarMethod
= nullptr;
405 CXXBasePath similarBasePath
;
407 auto BaseMatchesCallback
= [&](const CXXBaseSpecifier
*cxxBaseSpecifier
, CXXBasePath
& path
)
409 if (cxxBaseSpecifier
->getAccessSpecifier() != AS_public
&& cxxBaseSpecifier
->getAccessSpecifier() != AS_protected
)
411 if (!cxxBaseSpecifier
->getType().getTypePtr())
413 const CXXRecordDecl
* baseCXXRecordDecl
= cxxBaseSpecifier
->getType()->getAsCXXRecordDecl();
414 if (!baseCXXRecordDecl
)
416 if (baseCXXRecordDecl
->isInvalidDecl())
418 for (const CXXMethodDecl
* baseMethod
: baseCXXRecordDecl
->methods())
420 auto effectiveBaseMethodAccess
= baseMethod
->getAccess();
421 if (effectiveBaseMethodAccess
== AS_public
&& path
.Access
== AS_protected
)
422 effectiveBaseMethodAccess
= AS_protected
;
423 if (effectiveBaseMethodAccess
!= methodDecl
->getAccess())
425 if (!baseMethod
->getDeclName().isIdentifier() || methodDecl
->getName() != baseMethod
->getName()) {
428 if (methodDecl
->isStatic() != baseMethod
->isStatic()
429 || methodDecl
->isConst() != baseMethod
->isConst()
430 || methodDecl
->isVolatile() != baseMethod
->isVolatile()
431 || (methodDecl
->getRefQualifier()
432 != baseMethod
->getRefQualifier())
433 || methodDecl
->isVariadic() != baseMethod
->isVariadic())
437 if (methodDecl
->getReturnType().getCanonicalType()
438 != baseMethod
->getReturnType().getCanonicalType())
442 if (methodDecl
->getNumParams() != baseMethod
->getNumParams())
444 bool bParamsMatch
= true;
445 for (unsigned i
=0; i
<methodDecl
->param_size(); ++i
)
447 if (methodDecl
->parameters()[i
]->getType() != baseMethod
->parameters()[i
]->getType())
449 bParamsMatch
= false;
455 // if we have already found a method directly below us in the inheritance hierarchy, just ignore this one
456 auto Compare
= [&](CXXBasePathElement
const & lhs
, CXXBasePathElement
const & rhs
)
458 return lhs
.Class
== rhs
.Class
;
461 && similarBasePath
.size() < path
.size()
462 && std::equal(similarBasePath
.begin(), similarBasePath
.end(),
463 path
.begin(), Compare
))
466 return true; // short circuit the process
467 similarMethod
= baseMethod
;
468 similarBasePath
= path
;
475 if (methodDecl
->getParent()->lookupInBases(BaseMatchesCallback
, aPaths
))
478 return similarMethod
;
481 CXXMemberCallExpr
const * UnnecessaryOverride::extractCallExpr(Expr
const *returnExpr
)
483 returnExpr
= returnExpr
->IgnoreImplicit();
487 // Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery(
488 // const rtl::OUString& sql)
489 // throw(SQLException, RuntimeException, std::exception)
491 // return OCommonStatement::executeQuery( sql );
494 // look down through all the
497 // `-ExprWithCleanups
498 // `-CXXConstructExpr
499 // `-MaterializeTemporaryExpr
500 // `-ImplicitCastExpr
501 // `-CXXBindTemporaryExpr
502 // `-CXXMemberCallExpr
504 // where the fact that the overriding and overridden function have identical
505 // return types makes us confident that all we need to check here is whether
506 // there's an (arbitrary, one-argument) CXXConstructorExpr and
507 // CXXBindTemporaryExpr in between:
508 if (auto ctorExpr
= dyn_cast
<CXXConstructExpr
>(returnExpr
)) {
509 if (ctorExpr
->getNumArgs() == 1) {
510 auto tempExpr1
= ctorExpr
->getArg(0)->IgnoreImplicit();
511 if (auto tempExpr2
= dyn_cast
<CXXBindTemporaryExpr
>(tempExpr1
))
513 returnExpr
= tempExpr2
->getSubExpr();
515 else if (auto tempExpr2
= dyn_cast
<CXXMemberCallExpr
>(tempExpr1
))
517 returnExpr
= tempExpr2
;
522 return dyn_cast
<CXXMemberCallExpr
>(returnExpr
->IgnoreParenImpCasts());
525 loplugin::Plugin::Registration
< UnnecessaryOverride
> unnecessaryoverride("unnecessaryoverride", true);
529 #endif // LO_CLANG_SHARED_PLUGINS
531 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */