diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2022-03-10 14:41:47 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2022-03-11 09:06:06 +0100 |
commit | 7002caa97e10d29353d3490b4fbb782d436575b3 (patch) | |
tree | 98c254c9f94272e145417034f5cdc85b942edf74 /compilerplugins | |
parent | a370e7ff7e8225b8343678401eca5a1721b2b9bb (diff) |
new loplugin:trivialdestructor
look for potentially trivial destructors that can then be elided
Change-Id: I435c251bd4291b5864c20d68f88676faac7c43fb
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131318
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/trivialdestructor.cxx | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/compilerplugins/clang/trivialdestructor.cxx b/compilerplugins/clang/trivialdestructor.cxx new file mode 100644 index 000000000000..208e4931a152 --- /dev/null +++ b/compilerplugins/clang/trivialdestructor.cxx @@ -0,0 +1,133 @@ +/* -*- 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 explicit destructors that can be trivial (and therefore don't need to be declared) + +namespace +{ +class TrivialDestructor : public loplugin::FilteringPlugin<TrivialDestructor> +{ +public: + explicit TrivialDestructor(loplugin::InstantiationData const& data) + : FilteringPlugin(data) + { + } + + virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } + + bool VisitCXXDestructorDecl(CXXDestructorDecl const*); + +private: + bool HasTrivialDestructorBody(const CXXRecordDecl* BaseClassDecl, + const CXXRecordDecl* MostDerivedClassDecl); + bool FieldHasTrivialDestructorBody(const FieldDecl* Field); +}; + +bool TrivialDestructor::VisitCXXDestructorDecl(CXXDestructorDecl const* destructorDecl) +{ + if (ignoreLocation(destructorDecl)) + return true; + if (!destructorDecl->hasTrivialBody()) + return true; + if (destructorDecl->isVirtual()) + return true; + if (destructorDecl->getExceptionSpecType() != EST_None) + return true; + if (destructorDecl->getAccess() != AS_public) + return true; + if (isInUnoIncludeFile( + compiler.getSourceManager().getSpellingLoc(destructorDecl->getLocation()))) + return true; + if (!HasTrivialDestructorBody(destructorDecl->getParent(), destructorDecl->getParent())) + return true; + + report(DiagnosticsEngine::Warning, "no need for explicit destructor decl", + destructorDecl->getLocation()) + << destructorDecl->getSourceRange(); + if (destructorDecl->getCanonicalDecl() != destructorDecl) + { + destructorDecl = destructorDecl->getCanonicalDecl(); + report(DiagnosticsEngine::Warning, "no need for explicit destructor decl", + destructorDecl->getLocation()) + << destructorDecl->getSourceRange(); + } + return true; +} + +bool TrivialDestructor::HasTrivialDestructorBody(const CXXRecordDecl* BaseClassDecl, + const CXXRecordDecl* MostDerivedClassDecl) +{ + if (BaseClassDecl != MostDerivedClassDecl && !BaseClassDecl->hasTrivialDestructor()) + return false; + + // Check fields. + for (const auto* field : BaseClassDecl->fields()) + if (!FieldHasTrivialDestructorBody(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 && !HasTrivialDestructorBody(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 && !HasTrivialDestructorBody(VirtualBase, MostDerivedClassDecl)) + return false; + } + } + return true; +} + +bool TrivialDestructor::FieldHasTrivialDestructorBody(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 destructor for an implicit anonymous union member is never invoked. + if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) + return false; + + return FieldClassDecl->hasTrivialDestructor(); +} + +loplugin::Plugin::Registration<TrivialDestructor> X("trivialdestructor", true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |