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>
23 look for methods where all they do is call their superclass method
28 bool hasMultipleBaseInstances_(
29 CXXRecordDecl
const * derived
, CXXRecordDecl
const * canonicBase
,
30 bool & hasAsNonVirtualBase
, bool & hasAsVirtualBase
)
32 for (auto i
= derived
->bases_begin(); i
!= derived
->bases_end(); ++i
) {
33 auto const cls
= i
->getType()->getAsCXXRecordDecl();
35 assert(i
->getType()->isDependentType());
36 // Conservatively assume "yes" for dependent bases:
39 if (cls
->getCanonicalDecl() == canonicBase
) {
41 if (hasAsNonVirtualBase
) {
44 hasAsVirtualBase
= true;
46 if (hasAsNonVirtualBase
|| hasAsVirtualBase
) {
49 hasAsNonVirtualBase
= true;
51 } else if (hasMultipleBaseInstances_(
52 cls
, canonicBase
, hasAsNonVirtualBase
, hasAsVirtualBase
))
60 bool hasMultipleBaseInstances(
61 CXXRecordDecl
const * derived
, CXXRecordDecl
const * base
)
65 return hasMultipleBaseInstances_(
66 derived
, base
->getCanonicalDecl(), nonVirt
, virt
);
69 class UnnecessaryOverride
:
70 public loplugin::FilteringPlugin
<UnnecessaryOverride
>
73 explicit UnnecessaryOverride(loplugin::InstantiationData
const & data
): FilteringPlugin(data
) {}
75 virtual bool preRun() override
77 // ignore some files with problematic macros
78 StringRef
fn(handler
.getMainFileName());
79 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/framework/factories/ChildWindowPane.cxx"))
81 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/Date.cxx"))
83 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/Time.cxx"))
85 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/dialog/hyperdlg.cxx"))
87 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/dialog/rubydialog.cxx"))
89 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/canvas/"))
91 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/view/spelldialog.cxx"))
93 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/dlg/SpellDialogChildWindow.cxx"))
95 // HAVE_ODBC_ADMINISTRATION
96 if (loplugin::isSamePathname(fn
, SRCDIR
"/dbaccess/source/ui/dlg/dsselect.cxx"))
98 if (loplugin::isSamePathname(fn
, SRCDIR
"/unotools/source/streaming/streamhelper.cxx"))
103 virtual void run() override
106 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
109 bool VisitCXXMethodDecl(const CXXMethodDecl
*);
112 const CXXMethodDecl
* findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl
*);
113 bool BaseCheckCallback(const CXXRecordDecl
*BaseDefinition
);
114 CXXMemberCallExpr
const * extractCallExpr(Expr
const *);
117 bool UnnecessaryOverride::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
119 if (ignoreLocation(methodDecl
->getCanonicalDecl())) {
122 if (isa
<CXXConstructorDecl
>(methodDecl
)) {
126 StringRef aFileName
= getFilenameOfLocation(
127 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(methodDecl
)));
129 if (isa
<CXXDestructorDecl
>(methodDecl
)
130 && !isInUnoIncludeFile(methodDecl
))
132 // the code is this method is only __compiled__ if OSL_DEBUG_LEVEL > 1
133 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/tools/source/stream/strmunx.cxx"))
136 // Warn about unnecessarily user-declared destructors.
137 // A destructor is deemed unnecessary if:
139 // * its class is only defined in the .cxx file (i.e., the virtual
140 // destructor is neither used to control the place of vtable
141 // emission, nor is its definition depending on types that may still
144 // the destructor is inline, the class definition is complete,
145 // and the class has no superclasses
146 // * it either does not have an explicit exception specification, or has
147 // a non-dependent explicit exception specification that is compatible
148 // with a non-dependent exception specification the destructor would
149 // have if it did not have an explicit one (TODO);
150 // * it is either defined as defaulted or with an empty body.
151 // Removing the user-declared destructor may cause the class to get an
152 // implicitly declared move constructor and/or move assignment operator;
153 // that is considered acceptable: If any subobject cannot be moved, the
154 // implicitly declared function will be defined as deleted (which is in
155 // practice not much different from not having it declared), and
156 // otherwise offering movability is likely even an improvement over not
157 // offering it due to a "pointless" user-declared destructor.
158 // Similarly, removing the user-declared destructor may cause the
159 // implicit definition of a copy constructor and/or copy assignment
160 // operator to change from being an obsolete feature to being a standard
161 // feature. That difference is not taken into account here.
162 auto cls
= methodDecl
->getParent();
163 if (methodDecl
->getAccess() != AS_public
)
167 if (!compiler
.getSourceManager().isInMainFile(
168 methodDecl
->getCanonicalDecl()->getLocation())
169 && !( methodDecl
->isInlined()))
173 // if it's virtual, but it has a base-class with a non-virtual destructor
174 if (methodDecl
->isVirtual())
176 bool baseWithVirtualDtor
= false;
177 for (auto baseSpecifier
= cls
->bases_begin(); baseSpecifier
!= cls
->bases_end(); ++baseSpecifier
)
179 const RecordType
* baseRecordType
= baseSpecifier
->getType()->getAs
<RecordType
>();
182 const CXXRecordDecl
* baseRecordDecl
= dyn_cast
<CXXRecordDecl
>(baseRecordType
->getDecl());
183 if (baseRecordDecl
&& baseRecordDecl
->getDestructor()
184 && baseRecordDecl
->getDestructor()->isVirtual())
186 baseWithVirtualDtor
= true;
191 if (!baseWithVirtualDtor
)
196 if (methodDecl
->isExplicitlyDefaulted()) {
197 if (methodDecl
->getPreviousDecl() != nullptr) {
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 if (bodyIt
== compoundStmt
->body_end()) {
328 auto declStmt
= dyn_cast
<DeclStmt
>(*bodyIt
);
329 if (!declStmt
|| !declStmt
->isSingleDecl())
331 auto varDecl
= dyn_cast
<VarDecl
>(declStmt
->getSingleDecl());
333 auto returnStmt
= dyn_cast
<ReturnStmt
>(*bodyIt
);
334 if (!varDecl
|| !returnStmt
)
336 Expr
const * retValue
= returnStmt
->getRetValue()->IgnoreParenImpCasts();
337 if (auto exprWithCleanups
= dyn_cast
<ExprWithCleanups
>(retValue
))
338 retValue
= exprWithCleanups
->getSubExpr()->IgnoreParenImpCasts();
339 if (auto constructExpr
= dyn_cast
<CXXConstructExpr
>(retValue
)) {
340 if (constructExpr
->getNumArgs() == 1)
341 retValue
= constructExpr
->getArg(0)->IgnoreParenImpCasts();
343 if (!isa
<DeclRefExpr
>(retValue
))
345 callExpr
= extractCallExpr(varDecl
->getInit());
348 if (!callExpr
|| callExpr
->getMethodDecl() != overriddenMethodDecl
)
350 const Expr
* expr1
= callExpr
->getImplicitObjectArgument()->IgnoreImpCasts();
353 const CXXThisExpr
* expr2
= dyn_cast_or_null
<CXXThisExpr
>(expr1
);
356 for (unsigned i
= 0; i
<callExpr
->getNumArgs(); ++i
) {
357 auto e
= callExpr
->getArg(i
)->IgnoreImplicit();
358 if (auto const e1
= dyn_cast
<CXXConstructExpr
>(e
)) {
359 if (e1
->getConstructor()->isCopyOrMoveConstructor() && e1
->getNumArgs() == 1) {
360 e
= e1
->getArg(0)->IgnoreImpCasts();
363 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(e
);
364 if (!declRefExpr
|| declRefExpr
->getDecl() != methodDecl
->getParamDecl(i
))
368 const CXXMethodDecl
* pOther
= nullptr;
369 if (methodDecl
->getCanonicalDecl()->getLocation() != methodDecl
->getLocation())
370 pOther
= methodDecl
->getCanonicalDecl();
373 StringRef aFileName
= getFilenameOfLocation(
374 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(pOther
)));
375 // SFX_DECL_CHILDWINDOW_WITHID macro
376 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/sfx2/childwin.hxx"))
380 if (containsPreprocessingConditionalInclusion(methodDecl
->getBody()->getSourceRange())) {
385 DiagnosticsEngine::Warning
, "%0%1 function just calls %2 parent",
386 methodDecl
->getLocation())
387 << methodDecl
->getAccess()
388 << (methodDecl
->isVirtual() ? " virtual" : "")
389 << overriddenMethodDecl
->getAccess()
390 << methodDecl
->getSourceRange();
391 if (methodDecl
->getCanonicalDecl()->getLocation() != methodDecl
->getLocation()) {
392 const CXXMethodDecl
* pOther
= methodDecl
->getCanonicalDecl();
394 DiagnosticsEngine::Note
,
395 "method declaration here",
396 pOther
->getLocation())
397 << pOther
->getSourceRange();
402 const CXXMethodDecl
* UnnecessaryOverride::findOverriddenOrSimilarMethodInSuperclasses(const CXXMethodDecl
* methodDecl
)
404 if (methodDecl
->isVirtual()) {
405 return *methodDecl
->begin_overridden_methods();
407 if (!methodDecl
->getDeclName().isIdentifier()) {
411 const CXXMethodDecl
* similarMethod
= nullptr;
412 CXXBasePath similarBasePath
;
414 auto BaseMatchesCallback
= [&](const CXXBaseSpecifier
*cxxBaseSpecifier
, CXXBasePath
& path
)
416 if (cxxBaseSpecifier
->getAccessSpecifier() != AS_public
&& cxxBaseSpecifier
->getAccessSpecifier() != AS_protected
)
418 if (!cxxBaseSpecifier
->getType().getTypePtr())
420 const CXXRecordDecl
* baseCXXRecordDecl
= cxxBaseSpecifier
->getType()->getAsCXXRecordDecl();
421 if (!baseCXXRecordDecl
)
423 if (baseCXXRecordDecl
->isInvalidDecl())
425 for (const CXXMethodDecl
* baseMethod
: baseCXXRecordDecl
->methods())
427 auto effectiveBaseMethodAccess
= baseMethod
->getAccess();
428 if (effectiveBaseMethodAccess
== AS_public
&& path
.Access
== AS_protected
)
429 effectiveBaseMethodAccess
= AS_protected
;
430 if (effectiveBaseMethodAccess
!= methodDecl
->getAccess())
432 if (!baseMethod
->getDeclName().isIdentifier() || methodDecl
->getName() != baseMethod
->getName()) {
435 if (methodDecl
->isStatic() != baseMethod
->isStatic()
436 || methodDecl
->isConst() != baseMethod
->isConst()
437 || methodDecl
->isVolatile() != baseMethod
->isVolatile()
438 || (methodDecl
->getRefQualifier()
439 != baseMethod
->getRefQualifier())
440 || methodDecl
->isVariadic() != baseMethod
->isVariadic())
444 if (methodDecl
->getReturnType().getCanonicalType()
445 != baseMethod
->getReturnType().getCanonicalType())
449 if (methodDecl
->getNumParams() != baseMethod
->getNumParams())
451 bool bParamsMatch
= true;
452 for (unsigned i
=0; i
<methodDecl
->param_size(); ++i
)
454 if (methodDecl
->parameters()[i
]->getType() != baseMethod
->parameters()[i
]->getType())
456 bParamsMatch
= false;
462 // if we have already found a method directly below us in the inheritance hierarchy, just ignore this one
463 auto Compare
= [&](CXXBasePathElement
const & lhs
, CXXBasePathElement
const & rhs
)
465 return lhs
.Class
== rhs
.Class
;
468 && similarBasePath
.size() < path
.size()
469 && std::equal(similarBasePath
.begin(), similarBasePath
.end(),
470 path
.begin(), Compare
))
473 return true; // short circuit the process
474 similarMethod
= baseMethod
;
475 similarBasePath
= path
;
482 if (methodDecl
->getParent()->lookupInBases(BaseMatchesCallback
, aPaths
))
485 return similarMethod
;
488 CXXMemberCallExpr
const * UnnecessaryOverride::extractCallExpr(Expr
const *returnExpr
)
490 returnExpr
= returnExpr
->IgnoreImplicit();
494 // Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery(
495 // const rtl::OUString& sql)
496 // throw(SQLException, RuntimeException, std::exception)
498 // return OCommonStatement::executeQuery( sql );
501 // look down through all the
504 // `-ExprWithCleanups
505 // `-CXXConstructExpr
506 // `-MaterializeTemporaryExpr
507 // `-ImplicitCastExpr
508 // `-CXXBindTemporaryExpr
509 // `-CXXMemberCallExpr
511 // where the fact that the overriding and overridden function have identical
512 // return types makes us confident that all we need to check here is whether
513 // there's an (arbitrary, one-argument) CXXConstructorExpr and
514 // CXXBindTemporaryExpr in between:
515 if (auto ctorExpr
= dyn_cast
<CXXConstructExpr
>(returnExpr
)) {
516 if (ctorExpr
->getNumArgs() == 1) {
517 auto tempExpr1
= ctorExpr
->getArg(0)->IgnoreImplicit();
518 if (auto tempExpr2
= dyn_cast
<CXXBindTemporaryExpr
>(tempExpr1
))
520 returnExpr
= tempExpr2
->getSubExpr();
522 else if (auto tempExpr2
= dyn_cast
<CXXMemberCallExpr
>(tempExpr1
))
524 returnExpr
= tempExpr2
;
529 return dyn_cast
<CXXMemberCallExpr
>(returnExpr
->IgnoreParenImpCasts());
532 loplugin::Plugin::Registration
< UnnecessaryOverride
> unnecessaryoverride("unnecessaryoverride", true);
536 #endif // LO_CLANG_SHARED_PLUGINS
538 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */