1 | //===--- TriviallyDestructibleCheck.cpp - clang-tidy ----------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "TriviallyDestructibleCheck.h" |
10 | #include "../utils/LexerUtils.h" |
11 | #include "../utils/Matchers.h" |
12 | #include "clang/AST/ASTContext.h" |
13 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
14 | |
15 | using namespace clang::ast_matchers; |
16 | using namespace clang::ast_matchers::internal; |
17 | using namespace clang::tidy::matchers; |
18 | |
19 | namespace clang::tidy::performance { |
20 | |
21 | namespace { |
22 | |
23 | AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); } |
24 | |
25 | AST_MATCHER_P(CXXRecordDecl, hasBase, Matcher<QualType>, InnerMatcher) { |
26 | for (const CXXBaseSpecifier &BaseSpec : Node.bases()) { |
27 | QualType BaseType = BaseSpec.getType(); |
28 | if (InnerMatcher.matches(Node: BaseType, Finder, Builder)) |
29 | return true; |
30 | } |
31 | return false; |
32 | } |
33 | |
34 | } // namespace |
35 | |
36 | void TriviallyDestructibleCheck::registerMatchers(MatchFinder *Finder) { |
37 | Finder->addMatcher( |
38 | NodeMatch: cxxDestructorDecl( |
39 | isDefaulted(), |
40 | unless(anyOf(isFirstDecl(), isVirtual(), |
41 | ofClass(InnerMatcher: cxxRecordDecl( |
42 | anyOf(hasBase(InnerMatcher: unless(isTriviallyDestructible())), |
43 | has(fieldDecl(unless( |
44 | hasType(InnerMatcher: isTriviallyDestructible())))))))))) |
45 | .bind(ID: "decl" ), |
46 | Action: this); |
47 | } |
48 | |
49 | void TriviallyDestructibleCheck::check(const MatchFinder::MatchResult &Result) { |
50 | const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXDestructorDecl>(ID: "decl" ); |
51 | |
52 | // Get locations of both first and out-of-line declarations. |
53 | SourceManager &SM = *Result.SourceManager; |
54 | const auto *FirstDecl = cast<CXXMethodDecl>(MatchedDecl->getFirstDecl()); |
55 | const SourceLocation FirstDeclEnd = utils::lexer::findNextTerminator( |
56 | Start: FirstDecl->getEndLoc(), SM, LangOpts: getLangOpts()); |
57 | const CharSourceRange SecondDeclRange = CharSourceRange::getTokenRange( |
58 | MatchedDecl->getBeginLoc(), |
59 | utils::lexer::findNextTerminator(Start: MatchedDecl->getEndLoc(), SM, |
60 | LangOpts: getLangOpts())); |
61 | if (FirstDeclEnd.isInvalid() || SecondDeclRange.isInvalid()) |
62 | return; |
63 | |
64 | // Report diagnostic. |
65 | diag(FirstDecl->getLocation(), |
66 | "class %0 can be made trivially destructible by defaulting the " |
67 | "destructor on its first declaration" ) |
68 | << FirstDecl->getParent() |
69 | << FixItHint::CreateInsertion(InsertionLoc: FirstDeclEnd, Code: " = default" ) |
70 | << FixItHint::CreateRemoval(RemoveRange: SecondDeclRange); |
71 | diag(MatchedDecl->getLocation(), "destructor definition is here" , |
72 | DiagnosticIDs::Note); |
73 | } |
74 | |
75 | } // namespace clang::tidy::performance |
76 | |