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