diff options
Diffstat (limited to 'compilerplugins/clang/trivialconstructor.cxx')
-rw-r--r-- | compilerplugins/clang/trivialconstructor.cxx | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/compilerplugins/clang/trivialconstructor.cxx b/compilerplugins/clang/trivialconstructor.cxx new file mode 100644 index 000000000..c991d71ae --- /dev/null +++ b/compilerplugins/clang/trivialconstructor.cxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <cassert> +#include <string> +#include <iostream> +#include <unordered_map> +#include <unordered_set> + +#include "plugin.hxx" +#include "check.hxx" +#include "config_clang.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/StmtVisitor.h" + +// Look for declared constructors that can be trivial (and therefore don't need to be declared) + +namespace +{ +class TrivialConstructor : public loplugin::FilteringPlugin<TrivialConstructor> +{ +public: + explicit TrivialConstructor(loplugin::InstantiationData const& data) + : FilteringPlugin(data) + { + } + + virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } + + bool VisitCXXConstructorDecl(CXXConstructorDecl const*); + +private: + bool HasTrivialConstructorBody(const CXXRecordDecl* BaseClassDecl, + const CXXRecordDecl* MostDerivedClassDecl); + bool FieldHasTrivialConstructorBody(const FieldDecl* Field); +}; + +bool TrivialConstructor::VisitCXXConstructorDecl(CXXConstructorDecl const* constructorDecl) +{ + if (ignoreLocation(constructorDecl)) + return true; + if (!constructorDecl->hasTrivialBody()) + return true; + if (constructorDecl->isExplicit()) + return true; + if (!constructorDecl->isDefaultConstructor()) + return true; + if (constructorDecl->getNumParams() != 0) + return true; + if (!constructorDecl->inits().empty()) + return true; + if (constructorDecl->getExceptionSpecType() != EST_None) + return true; + if (constructorDecl->getAccess() != AS_public) + return true; + if (!constructorDecl->isThisDeclarationADefinition()) + return true; + if (isInUnoIncludeFile( + compiler.getSourceManager().getSpellingLoc(constructorDecl->getLocation()))) + return true; + const CXXRecordDecl* recordDecl = constructorDecl->getParent(); + if (std::distance(recordDecl->ctor_begin(), recordDecl->ctor_end()) != 1) + return true; + // Constructor templates are not included in ctor_begin()..ctor_end() above, so also do a slow + // check across all decls(): + for (auto d : recordDecl->decls()) + { + if (auto const d2 = dyn_cast<FunctionTemplateDecl>(d)) + { + if (isa<CXXConstructorDecl>(d2->getTemplatedDecl())) + { + return true; + } + } + } + if (!HasTrivialConstructorBody(recordDecl, recordDecl)) + return true; + + report(DiagnosticsEngine::Warning, "no need for explicit constructor decl", + constructorDecl->getLocation()) + << constructorDecl->getSourceRange(); + if (constructorDecl->getCanonicalDecl() != constructorDecl) + { + constructorDecl = constructorDecl->getCanonicalDecl(); + report(DiagnosticsEngine::Warning, "no need for explicit constructor decl", + constructorDecl->getLocation()) + << constructorDecl->getSourceRange(); + } + return true; +} + +bool TrivialConstructor::HasTrivialConstructorBody(const CXXRecordDecl* BaseClassDecl, + const CXXRecordDecl* MostDerivedClassDecl) +{ + if (BaseClassDecl != MostDerivedClassDecl && !BaseClassDecl->hasTrivialDefaultConstructor()) + return false; + + // Check fields. + for (const auto* field : BaseClassDecl->fields()) + if (!FieldHasTrivialConstructorBody(field)) + return false; + + // Check non-virtual bases. + for (const auto& I : BaseClassDecl->bases()) + { + if (I.isVirtual()) + continue; + if (!I.getType()->isRecordType()) + continue; + const CXXRecordDecl* NonVirtualBase = I.getType()->getAsCXXRecordDecl(); + if (NonVirtualBase && !HasTrivialConstructorBody(NonVirtualBase, MostDerivedClassDecl)) + return false; + } + + if (BaseClassDecl == MostDerivedClassDecl) + { + // Check virtual bases. + for (const auto& I : BaseClassDecl->vbases()) + { + if (!I.getType()->isRecordType()) + continue; + const CXXRecordDecl* VirtualBase = I.getType()->getAsCXXRecordDecl(); + if (VirtualBase && !HasTrivialConstructorBody(VirtualBase, MostDerivedClassDecl)) + return false; + } + } + return true; +} + +bool TrivialConstructor::FieldHasTrivialConstructorBody(const FieldDecl* Field) +{ + QualType FieldBaseElementType = compiler.getASTContext().getBaseElementType(Field->getType()); + + const RecordType* RT = FieldBaseElementType->getAs<RecordType>(); + if (!RT) + return true; + + CXXRecordDecl* FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl()); + + // The constructor for an implicit anonymous union member is never invoked. + if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) + return false; + + return FieldClassDecl->hasTrivialDefaultConstructor(); +} + +loplugin::Plugin::Registration<TrivialConstructor> X("trivialconstructor", true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |