//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "TriviallyDestructibleCheck.h" #include "../utils/LexerUtils.h" #include "../utils/Matchers.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; using namespace clang::tidy::matchers; namespace clang::tidy::performance { namespace { // We use isOutOfLine() to find out-of-line defaulted destructor definitions. // This is more robust than !isFirstDecl() because with C++20 modules, when a // class is visible through both a header include and a module import, its // declarations may appear multiple times in the redeclaration chain. AST_MATCHER(CXXMethodDecl, isOutOfLine) { return Node.isOutOfLine(); } AST_MATCHER_P(CXXRecordDecl, hasBase, Matcher, InnerMatcher) { return llvm::any_of(Node.bases(), [&](const CXXBaseSpecifier &BaseSpec) { return InnerMatcher.matches(BaseSpec.getType(), Finder, Builder); }); } } // namespace void TriviallyDestructibleCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( cxxDestructorDecl( isDefaulted(), isOutOfLine(), unless(anyOf(isVirtual(), ofClass(cxxRecordDecl( anyOf(hasBase(unless(isTriviallyDestructible())), has(fieldDecl(unless( hasType(isTriviallyDestructible())))))))))) .bind("decl"), this); } void TriviallyDestructibleCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedDecl = Result.Nodes.getNodeAs("decl"); // Get locations of both first and out-of-line declarations. const SourceManager &SM = *Result.SourceManager; const auto *FirstDecl = cast(MatchedDecl->getFirstDecl()); const SourceLocation FirstDeclEnd = utils::lexer::findNextTerminator( FirstDecl->getEndLoc(), SM, getLangOpts()); const CharSourceRange SecondDeclRange = CharSourceRange::getTokenRange( MatchedDecl->getBeginLoc(), utils::lexer::findNextTerminator(MatchedDecl->getEndLoc(), SM, getLangOpts())); if (FirstDeclEnd.isInvalid() || SecondDeclRange.isInvalid()) return; // Report diagnostic. diag(FirstDecl->getLocation(), "class %0 can be made trivially destructible by defaulting the " "destructor on its first declaration") << FirstDecl->getParent() << FixItHint::CreateInsertion(FirstDeclEnd, " = default") << FixItHint::CreateRemoval(SecondDeclRange); diag(MatchedDecl->getLocation(), "destructor definition is here", DiagnosticIDs::Note); } } // namespace clang::tidy::performance