1 | //===--- UseEqualsDeleteCheck.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 "UseEqualsDeleteCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "clang/Lex/Lexer.h" |
13 | |
14 | using namespace clang::ast_matchers; |
15 | |
16 | namespace clang::tidy::modernize { |
17 | |
18 | namespace { |
19 | AST_MATCHER(FunctionDecl, hasAnyDefinition) { |
20 | if (Node.hasBody() || Node.isPureVirtual() || Node.isDefaulted() || |
21 | Node.isDeleted()) |
22 | return true; |
23 | |
24 | if (const FunctionDecl *Definition = Node.getDefinition()) |
25 | if (Definition->hasBody() || Definition->isPureVirtual() || |
26 | Definition->isDefaulted() || Definition->isDeleted()) |
27 | return true; |
28 | |
29 | return false; |
30 | } |
31 | |
32 | AST_MATCHER(Decl, isUsed) { return Node.isUsed(); } |
33 | |
34 | AST_MATCHER(CXXMethodDecl, isSpecialFunction) { |
35 | if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(Val: &Node)) |
36 | return Constructor->isDefaultConstructor() || |
37 | Constructor->isCopyOrMoveConstructor(); |
38 | |
39 | return isa<CXXDestructorDecl>(Val: Node) || Node.isCopyAssignmentOperator() || |
40 | Node.isMoveAssignmentOperator(); |
41 | } |
42 | } // namespace |
43 | |
44 | static const char SpecialFunction[] = "SpecialFunction" ; |
45 | static const char DeletedNotPublic[] = "DeletedNotPublic" ; |
46 | |
47 | UseEqualsDeleteCheck::UseEqualsDeleteCheck(StringRef Name, |
48 | ClangTidyContext *Context) |
49 | : ClangTidyCheck(Name, Context), |
50 | IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros" , Default: true)) {} |
51 | |
52 | void UseEqualsDeleteCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
53 | Options.store(Options&: Opts, LocalName: "IgnoreMacros" , Value: IgnoreMacros); |
54 | } |
55 | |
56 | void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) { |
57 | auto PrivateSpecialFn = cxxMethodDecl(isPrivate(), isSpecialFunction()); |
58 | |
59 | Finder->addMatcher( |
60 | NodeMatch: cxxMethodDecl( |
61 | PrivateSpecialFn, unless(hasAnyDefinition()), unless(isUsed()), |
62 | // Ensure that all methods except private special member functions are |
63 | // defined. |
64 | unless(ofClass(InnerMatcher: hasMethod(InnerMatcher: cxxMethodDecl(unless(PrivateSpecialFn), |
65 | unless(hasAnyDefinition())))))) |
66 | .bind(ID: SpecialFunction), |
67 | Action: this); |
68 | |
69 | Finder->addMatcher( |
70 | NodeMatch: cxxMethodDecl(isDeleted(), unless(isPublic())).bind(ID: DeletedNotPublic), |
71 | Action: this); |
72 | } |
73 | |
74 | void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) { |
75 | if (const auto *Func = |
76 | Result.Nodes.getNodeAs<CXXMethodDecl>(ID: SpecialFunction)) { |
77 | SourceLocation EndLoc = Lexer::getLocForEndOfToken( |
78 | Loc: Func->getEndLoc(), Offset: 0, SM: *Result.SourceManager, LangOpts: getLangOpts()); |
79 | |
80 | if (IgnoreMacros && Func->getLocation().isMacroID()) |
81 | return; |
82 | // FIXME: Improve FixItHint to make the method public. |
83 | diag(Func->getLocation(), |
84 | "use '= delete' to prohibit calling of a special member function" ) |
85 | << FixItHint::CreateInsertion(InsertionLoc: EndLoc, Code: " = delete" ); |
86 | } else if (const auto *Func = |
87 | Result.Nodes.getNodeAs<CXXMethodDecl>(ID: DeletedNotPublic)) { |
88 | // Ignore this warning in macros, since it's extremely noisy in code using |
89 | // DISALLOW_COPY_AND_ASSIGN-style macros and there's no easy way to |
90 | // automatically fix the warning when macros are in play. |
91 | if (IgnoreMacros && Func->getLocation().isMacroID()) |
92 | return; |
93 | // FIXME: Add FixItHint to make the method public. |
94 | diag(Func->getLocation(), "deleted member function should be public" ); |
95 | } |
96 | } |
97 | |
98 | } // namespace clang::tidy::modernize |
99 | |