annotate clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp @ 204:e348f3e5c8b2

ReadFromString worked.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 05 Jun 2021 15:35:13 +0900
parents 0572611fdcc8
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===--- UseEqualsDefaultCheck.cpp - clang-tidy----------------------------===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "UseEqualsDefaultCheck.h"
anatofuz
parents:
diff changeset
10 #include "clang/AST/ASTContext.h"
anatofuz
parents:
diff changeset
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
anatofuz
parents:
diff changeset
12 #include "clang/Lex/Lexer.h"
anatofuz
parents:
diff changeset
13 #include "../utils/LexerUtils.h"
anatofuz
parents:
diff changeset
14
anatofuz
parents:
diff changeset
15 using namespace clang::ast_matchers;
anatofuz
parents:
diff changeset
16
anatofuz
parents:
diff changeset
17 namespace clang {
anatofuz
parents:
diff changeset
18 namespace tidy {
anatofuz
parents:
diff changeset
19 namespace modernize {
anatofuz
parents:
diff changeset
20
anatofuz
parents:
diff changeset
21 static const char SpecialFunction[] = "SpecialFunction";
anatofuz
parents:
diff changeset
22
anatofuz
parents:
diff changeset
23 /// Finds all the named non-static fields of \p Record.
anatofuz
parents:
diff changeset
24 static std::set<const FieldDecl *>
anatofuz
parents:
diff changeset
25 getAllNamedFields(const CXXRecordDecl *Record) {
anatofuz
parents:
diff changeset
26 std::set<const FieldDecl *> Result;
anatofuz
parents:
diff changeset
27 for (const auto *Field : Record->fields()) {
anatofuz
parents:
diff changeset
28 // Static data members are not in this range.
anatofuz
parents:
diff changeset
29 if (Field->isUnnamedBitfield())
anatofuz
parents:
diff changeset
30 continue;
anatofuz
parents:
diff changeset
31 Result.insert(Field);
anatofuz
parents:
diff changeset
32 }
anatofuz
parents:
diff changeset
33 return Result;
anatofuz
parents:
diff changeset
34 }
anatofuz
parents:
diff changeset
35
anatofuz
parents:
diff changeset
36 /// Returns the names of the direct bases of \p Record, both virtual and
anatofuz
parents:
diff changeset
37 /// non-virtual.
anatofuz
parents:
diff changeset
38 static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
anatofuz
parents:
diff changeset
39 std::set<const Type *> Result;
anatofuz
parents:
diff changeset
40 for (auto Base : Record->bases()) {
anatofuz
parents:
diff changeset
41 // CXXBaseSpecifier.
anatofuz
parents:
diff changeset
42 const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
anatofuz
parents:
diff changeset
43 Result.insert(BaseType);
anatofuz
parents:
diff changeset
44 }
anatofuz
parents:
diff changeset
45 return Result;
anatofuz
parents:
diff changeset
46 }
anatofuz
parents:
diff changeset
47
anatofuz
parents:
diff changeset
48 /// Returns a matcher that matches member expressions where the base is
anatofuz
parents:
diff changeset
49 /// the variable declared as \p Var and the accessed member is the one declared
anatofuz
parents:
diff changeset
50 /// as \p Field.
anatofuz
parents:
diff changeset
51 internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
anatofuz
parents:
diff changeset
52 const ValueDecl *Var) {
anatofuz
parents:
diff changeset
53 return ignoringImpCasts(
anatofuz
parents:
diff changeset
54 memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
anatofuz
parents:
diff changeset
55 member(fieldDecl(equalsNode(Field)))));
anatofuz
parents:
diff changeset
56 }
anatofuz
parents:
diff changeset
57
anatofuz
parents:
diff changeset
58 /// Check that the given constructor has copy signature and that it
anatofuz
parents:
diff changeset
59 /// copy-initializes all its bases and members.
anatofuz
parents:
diff changeset
60 static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
anatofuz
parents:
diff changeset
61 const CXXConstructorDecl *Ctor) {
anatofuz
parents:
diff changeset
62 // An explicitly-defaulted constructor cannot have default arguments.
anatofuz
parents:
diff changeset
63 if (Ctor->getMinRequiredArguments() != 1)
anatofuz
parents:
diff changeset
64 return false;
anatofuz
parents:
diff changeset
65
anatofuz
parents:
diff changeset
66 const auto *Record = Ctor->getParent();
anatofuz
parents:
diff changeset
67 const auto *Param = Ctor->getParamDecl(0);
anatofuz
parents:
diff changeset
68
anatofuz
parents:
diff changeset
69 // Base classes and members that have to be copied.
anatofuz
parents:
diff changeset
70 auto BasesToInit = getAllDirectBases(Record);
anatofuz
parents:
diff changeset
71 auto FieldsToInit = getAllNamedFields(Record);
anatofuz
parents:
diff changeset
72
anatofuz
parents:
diff changeset
73 // Ensure that all the bases are copied.
anatofuz
parents:
diff changeset
74 for (const auto *Base : BasesToInit) {
anatofuz
parents:
diff changeset
75 // The initialization of a base class should be a call to a copy
anatofuz
parents:
diff changeset
76 // constructor of the base.
anatofuz
parents:
diff changeset
77 if (match(
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
78 traverse(ast_type_traits::TK_AsIs,
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
79 cxxConstructorDecl(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
80 forEachConstructorInitializer(cxxCtorInitializer(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
81 isBaseInitializer(),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
82 withInitializer(cxxConstructExpr(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
83 hasType(equalsNode(Base)),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
84 hasDeclaration(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
85 cxxConstructorDecl(isCopyConstructor())),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
86 argumentCountIs(1),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
87 hasArgument(0, declRefExpr(to(varDecl(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
88 equalsNode(Param))))))))))),
150
anatofuz
parents:
diff changeset
89 *Ctor, *Context)
anatofuz
parents:
diff changeset
90 .empty())
anatofuz
parents:
diff changeset
91 return false;
anatofuz
parents:
diff changeset
92 }
anatofuz
parents:
diff changeset
93
anatofuz
parents:
diff changeset
94 // Ensure that all the members are copied.
anatofuz
parents:
diff changeset
95 for (const auto *Field : FieldsToInit) {
anatofuz
parents:
diff changeset
96 auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
anatofuz
parents:
diff changeset
97 // The initialization is a CXXConstructExpr for class types.
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
98 if (match(traverse(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
99 ast_type_traits::TK_AsIs,
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
100 cxxConstructorDecl(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
101 forEachConstructorInitializer(cxxCtorInitializer(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
102 isMemberInitializer(), forField(equalsNode(Field)),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
103 withInitializer(anyOf(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
104 AccessToFieldInParam,
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
105 initListExpr(has(AccessToFieldInParam)),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
106 cxxConstructExpr(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
107 hasDeclaration(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
108 cxxConstructorDecl(isCopyConstructor())),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
109 argumentCountIs(1),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
110 hasArgument(0, AccessToFieldInParam)))))))),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
111 *Ctor, *Context)
150
anatofuz
parents:
diff changeset
112 .empty())
anatofuz
parents:
diff changeset
113 return false;
anatofuz
parents:
diff changeset
114 }
anatofuz
parents:
diff changeset
115
anatofuz
parents:
diff changeset
116 // Ensure that we don't do anything else, like initializing an indirect base.
anatofuz
parents:
diff changeset
117 return Ctor->getNumCtorInitializers() ==
anatofuz
parents:
diff changeset
118 BasesToInit.size() + FieldsToInit.size();
anatofuz
parents:
diff changeset
119 }
anatofuz
parents:
diff changeset
120
anatofuz
parents:
diff changeset
121 /// Checks that the given method is an overloading of the assignment
anatofuz
parents:
diff changeset
122 /// operator, has copy signature, returns a reference to "*this" and copies
anatofuz
parents:
diff changeset
123 /// all its members and subobjects.
anatofuz
parents:
diff changeset
124 static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
anatofuz
parents:
diff changeset
125 const CXXMethodDecl *Operator) {
anatofuz
parents:
diff changeset
126 const auto *Record = Operator->getParent();
anatofuz
parents:
diff changeset
127 const auto *Param = Operator->getParamDecl(0);
anatofuz
parents:
diff changeset
128
anatofuz
parents:
diff changeset
129 // Base classes and members that have to be copied.
anatofuz
parents:
diff changeset
130 auto BasesToInit = getAllDirectBases(Record);
anatofuz
parents:
diff changeset
131 auto FieldsToInit = getAllNamedFields(Record);
anatofuz
parents:
diff changeset
132
anatofuz
parents:
diff changeset
133 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
anatofuz
parents:
diff changeset
134
anatofuz
parents:
diff changeset
135 // The assignment operator definition has to end with the following return
anatofuz
parents:
diff changeset
136 // statement:
anatofuz
parents:
diff changeset
137 // return *this;
anatofuz
parents:
diff changeset
138 if (Compound->body_empty() ||
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
139 match(traverse(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
140 ast_type_traits::TK_AsIs,
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
141 returnStmt(has(ignoringParenImpCasts(unaryOperator(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
142 hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())))))),
150
anatofuz
parents:
diff changeset
143 *Compound->body_back(), *Context)
anatofuz
parents:
diff changeset
144 .empty())
anatofuz
parents:
diff changeset
145 return false;
anatofuz
parents:
diff changeset
146
anatofuz
parents:
diff changeset
147 // Ensure that all the bases are copied.
anatofuz
parents:
diff changeset
148 for (const auto *Base : BasesToInit) {
anatofuz
parents:
diff changeset
149 // Assignment operator of a base class:
anatofuz
parents:
diff changeset
150 // Base::operator=(Other);
anatofuz
parents:
diff changeset
151 //
anatofuz
parents:
diff changeset
152 // Clang translates this into:
anatofuz
parents:
diff changeset
153 // ((Base*)this)->operator=((Base)Other);
anatofuz
parents:
diff changeset
154 //
anatofuz
parents:
diff changeset
155 // So we are looking for a member call that fulfills:
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
156 if (match(traverse(ast_type_traits::TK_AsIs,
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
157 compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
158 // - The object is an implicit cast of 'this' to a
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
159 // pointer to
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
160 // a base class.
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
161 onImplicitObjectArgument(implicitCastExpr(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
162 hasImplicitDestinationType(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
163 pointsTo(type(equalsNode(Base)))),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
164 hasSourceExpression(cxxThisExpr()))),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
165 // - The called method is the operator=.
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
166 callee(cxxMethodDecl(isCopyAssignmentOperator())),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
167 // - The argument is (an implicit cast to a Base of)
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
168 // the argument taken by "Operator".
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
169 argumentCountIs(1),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
170 hasArgument(0, declRefExpr(to(varDecl(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
171 equalsNode(Param)))))))))),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
172 *Compound, *Context)
150
anatofuz
parents:
diff changeset
173 .empty())
anatofuz
parents:
diff changeset
174 return false;
anatofuz
parents:
diff changeset
175 }
anatofuz
parents:
diff changeset
176
anatofuz
parents:
diff changeset
177 // Ensure that all the members are copied.
anatofuz
parents:
diff changeset
178 for (const auto *Field : FieldsToInit) {
anatofuz
parents:
diff changeset
179 // The assignment of data members:
anatofuz
parents:
diff changeset
180 // Field = Other.Field;
anatofuz
parents:
diff changeset
181 // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
anatofuz
parents:
diff changeset
182 // otherwise.
anatofuz
parents:
diff changeset
183 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
anatofuz
parents:
diff changeset
184 member(fieldDecl(equalsNode(Field))));
anatofuz
parents:
diff changeset
185 auto RHS = accessToFieldInVar(Field, Param);
anatofuz
parents:
diff changeset
186 if (match(
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
187 traverse(ast_type_traits::TK_AsIs,
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
188 compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
189 binaryOperator(hasOperatorName("="), hasLHS(LHS),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
190 hasRHS(RHS)),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
191 cxxOperatorCallExpr(
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
192 hasOverloadedOperatorName("="), argumentCountIs(2),
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
193 hasArgument(0, LHS), hasArgument(1, RHS)))))))),
150
anatofuz
parents:
diff changeset
194 *Compound, *Context)
anatofuz
parents:
diff changeset
195 .empty())
anatofuz
parents:
diff changeset
196 return false;
anatofuz
parents:
diff changeset
197 }
anatofuz
parents:
diff changeset
198
anatofuz
parents:
diff changeset
199 // Ensure that we don't do anything else.
anatofuz
parents:
diff changeset
200 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
anatofuz
parents:
diff changeset
201 }
anatofuz
parents:
diff changeset
202
anatofuz
parents:
diff changeset
203 /// Returns false if the body has any non-whitespace character.
anatofuz
parents:
diff changeset
204 static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
anatofuz
parents:
diff changeset
205 bool Invalid = false;
anatofuz
parents:
diff changeset
206 StringRef Text = Lexer::getSourceText(
anatofuz
parents:
diff changeset
207 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
anatofuz
parents:
diff changeset
208 Body->getRBracLoc()),
anatofuz
parents:
diff changeset
209 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
anatofuz
parents:
diff changeset
210 return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
anatofuz
parents:
diff changeset
211 }
anatofuz
parents:
diff changeset
212
anatofuz
parents:
diff changeset
213 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name,
anatofuz
parents:
diff changeset
214 ClangTidyContext *Context)
anatofuz
parents:
diff changeset
215 : ClangTidyCheck(Name, Context),
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
216 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
150
anatofuz
parents:
diff changeset
217
anatofuz
parents:
diff changeset
218 void UseEqualsDefaultCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
anatofuz
parents:
diff changeset
219 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
anatofuz
parents:
diff changeset
220 }
anatofuz
parents:
diff changeset
221
anatofuz
parents:
diff changeset
222 void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
anatofuz
parents:
diff changeset
223 // Destructor.
anatofuz
parents:
diff changeset
224 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
anatofuz
parents:
diff changeset
225 this);
anatofuz
parents:
diff changeset
226 Finder->addMatcher(
anatofuz
parents:
diff changeset
227 cxxConstructorDecl(
anatofuz
parents:
diff changeset
228 isDefinition(),
anatofuz
parents:
diff changeset
229 anyOf(
anatofuz
parents:
diff changeset
230 // Default constructor.
anatofuz
parents:
diff changeset
231 allOf(unless(hasAnyConstructorInitializer(isWritten())),
anatofuz
parents:
diff changeset
232 parameterCountIs(0)),
anatofuz
parents:
diff changeset
233 // Copy constructor.
anatofuz
parents:
diff changeset
234 allOf(isCopyConstructor(),
anatofuz
parents:
diff changeset
235 // Discard constructors that can be used as a copy
anatofuz
parents:
diff changeset
236 // constructor because all the other arguments have
anatofuz
parents:
diff changeset
237 // default values.
anatofuz
parents:
diff changeset
238 parameterCountIs(1))))
anatofuz
parents:
diff changeset
239 .bind(SpecialFunction),
anatofuz
parents:
diff changeset
240 this);
anatofuz
parents:
diff changeset
241 // Copy-assignment operator.
anatofuz
parents:
diff changeset
242 Finder->addMatcher(
anatofuz
parents:
diff changeset
243 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
anatofuz
parents:
diff changeset
244 // isCopyAssignmentOperator() allows the parameter to be
anatofuz
parents:
diff changeset
245 // passed by value, and in this case it cannot be
anatofuz
parents:
diff changeset
246 // defaulted.
anatofuz
parents:
diff changeset
247 hasParameter(0, hasType(lValueReferenceType())))
anatofuz
parents:
diff changeset
248 .bind(SpecialFunction),
anatofuz
parents:
diff changeset
249 this);
anatofuz
parents:
diff changeset
250 }
anatofuz
parents:
diff changeset
251
anatofuz
parents:
diff changeset
252 void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
anatofuz
parents:
diff changeset
253 std::string SpecialFunctionName;
anatofuz
parents:
diff changeset
254
anatofuz
parents:
diff changeset
255 // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
anatofuz
parents:
diff changeset
256 const auto *SpecialFunctionDecl =
anatofuz
parents:
diff changeset
257 Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
anatofuz
parents:
diff changeset
258
anatofuz
parents:
diff changeset
259 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
anatofuz
parents:
diff changeset
260 return;
anatofuz
parents:
diff changeset
261
anatofuz
parents:
diff changeset
262 // Discard explicitly deleted/defaulted special member functions and those
anatofuz
parents:
diff changeset
263 // that are not user-provided (automatically generated).
anatofuz
parents:
diff changeset
264 if (SpecialFunctionDecl->isDeleted() ||
anatofuz
parents:
diff changeset
265 SpecialFunctionDecl->isExplicitlyDefaulted() ||
anatofuz
parents:
diff changeset
266 SpecialFunctionDecl->isLateTemplateParsed() ||
anatofuz
parents:
diff changeset
267 SpecialFunctionDecl->isTemplateInstantiation() ||
anatofuz
parents:
diff changeset
268 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
anatofuz
parents:
diff changeset
269 return;
anatofuz
parents:
diff changeset
270
anatofuz
parents:
diff changeset
271 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
anatofuz
parents:
diff changeset
272 if (!Body)
anatofuz
parents:
diff changeset
273 return;
anatofuz
parents:
diff changeset
274
anatofuz
parents:
diff changeset
275 // If there is code inside the body, don't warn.
anatofuz
parents:
diff changeset
276 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
anatofuz
parents:
diff changeset
277 return;
anatofuz
parents:
diff changeset
278
anatofuz
parents:
diff changeset
279 // If there are comments inside the body, don't do the change.
anatofuz
parents:
diff changeset
280 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
anatofuz
parents:
diff changeset
281 bodyEmpty(Result.Context, Body);
anatofuz
parents:
diff changeset
282
anatofuz
parents:
diff changeset
283 std::vector<FixItHint> RemoveInitializers;
anatofuz
parents:
diff changeset
284
anatofuz
parents:
diff changeset
285 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
anatofuz
parents:
diff changeset
286 if (Ctor->getNumParams() == 0) {
anatofuz
parents:
diff changeset
287 SpecialFunctionName = "default constructor";
anatofuz
parents:
diff changeset
288 } else {
anatofuz
parents:
diff changeset
289 if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
anatofuz
parents:
diff changeset
290 return;
anatofuz
parents:
diff changeset
291 SpecialFunctionName = "copy constructor";
anatofuz
parents:
diff changeset
292 // If there are constructor initializers, they must be removed.
anatofuz
parents:
diff changeset
293 for (const auto *Init : Ctor->inits()) {
anatofuz
parents:
diff changeset
294 RemoveInitializers.emplace_back(
anatofuz
parents:
diff changeset
295 FixItHint::CreateRemoval(Init->getSourceRange()));
anatofuz
parents:
diff changeset
296 }
anatofuz
parents:
diff changeset
297 }
anatofuz
parents:
diff changeset
298 } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
anatofuz
parents:
diff changeset
299 SpecialFunctionName = "destructor";
anatofuz
parents:
diff changeset
300 } else {
anatofuz
parents:
diff changeset
301 if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
anatofuz
parents:
diff changeset
302 return;
anatofuz
parents:
diff changeset
303 SpecialFunctionName = "copy-assignment operator";
anatofuz
parents:
diff changeset
304 }
anatofuz
parents:
diff changeset
305
anatofuz
parents:
diff changeset
306 // The location of the body is more useful inside a macro as spelling and
anatofuz
parents:
diff changeset
307 // expansion locations are reported.
anatofuz
parents:
diff changeset
308 SourceLocation Location = SpecialFunctionDecl->getLocation();
anatofuz
parents:
diff changeset
309 if (Location.isMacroID())
anatofuz
parents:
diff changeset
310 Location = Body->getBeginLoc();
anatofuz
parents:
diff changeset
311
anatofuz
parents:
diff changeset
312 auto Diag = diag(Location, "use '= default' to define a trivial " +
anatofuz
parents:
diff changeset
313 SpecialFunctionName);
anatofuz
parents:
diff changeset
314
anatofuz
parents:
diff changeset
315 if (ApplyFix) {
anatofuz
parents:
diff changeset
316 // Skipping comments, check for a semicolon after Body->getSourceRange()
anatofuz
parents:
diff changeset
317 Optional<Token> Token = utils::lexer::findNextTokenSkippingComments(
anatofuz
parents:
diff changeset
318 Body->getSourceRange().getEnd().getLocWithOffset(1),
anatofuz
parents:
diff changeset
319 Result.Context->getSourceManager(), Result.Context->getLangOpts());
anatofuz
parents:
diff changeset
320 StringRef Replacement =
anatofuz
parents:
diff changeset
321 Token && Token->is(tok::semi) ? "= default" : "= default;";
anatofuz
parents:
diff changeset
322 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
anatofuz
parents:
diff changeset
323 << RemoveInitializers;
anatofuz
parents:
diff changeset
324 }
anatofuz
parents:
diff changeset
325 }
anatofuz
parents:
diff changeset
326
anatofuz
parents:
diff changeset
327 } // namespace modernize
anatofuz
parents:
diff changeset
328 } // namespace tidy
anatofuz
parents:
diff changeset
329 } // namespace clang