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 "../utils/LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.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 *>
getAllNamedFields(const CXXRecordDecl * Record)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.
getAllDirectBases(const CXXRecordDecl * Record)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.
accessToFieldInVar(const FieldDecl * Field,const ValueDecl * Var)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.
isCopyConstructorAndCanBeDefaulted(ASTContext * Context,const CXXConstructorDecl * Ctor)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(
78 traverse(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))))))))))),
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.
98 if (match(traverse(
99 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)
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.
isCopyAssignmentAndCanBeDefaulted(ASTContext * Context,const CXXMethodDecl * Operator)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() ||
139 match(traverse(
140 TK_AsIs,
141 returnStmt(has(ignoringParenImpCasts(unaryOperator(
142 hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())))))),
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:
156 if (match(traverse(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)
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(traverse(TK_AsIs,
187 compoundStmt(has(ignoringParenImpCasts(binaryOperation(
188 hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)))))),
189 *Compound, *Context)
190 .empty())
191 return false;
192 }
193
194 // Ensure that we don't do anything else.
195 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
196 }
197
198 /// Returns false if the body has any non-whitespace character.
bodyEmpty(const ASTContext * Context,const CompoundStmt * Body)199 static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
200 bool Invalid = false;
201 StringRef Text = Lexer::getSourceText(
202 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
203 Body->getRBracLoc()),
204 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
205 return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
206 }
207
UseEqualsDefaultCheck(StringRef Name,ClangTidyContext * Context)208 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name,
209 ClangTidyContext *Context)
210 : ClangTidyCheck(Name, Context),
211 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
212
storeOptions(ClangTidyOptions::OptionMap & Opts)213 void UseEqualsDefaultCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
214 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
215 }
216
registerMatchers(MatchFinder * Finder)217 void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
218 // Destructor.
219 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
220 this);
221 Finder->addMatcher(
222 cxxConstructorDecl(
223 isDefinition(),
224 anyOf(
225 // Default constructor.
226 allOf(unless(hasAnyConstructorInitializer(isWritten())),
227 parameterCountIs(0)),
228 // Copy constructor.
229 allOf(isCopyConstructor(),
230 // Discard constructors that can be used as a copy
231 // constructor because all the other arguments have
232 // default values.
233 parameterCountIs(1))))
234 .bind(SpecialFunction),
235 this);
236 // Copy-assignment operator.
237 Finder->addMatcher(
238 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
239 // isCopyAssignmentOperator() allows the parameter to be
240 // passed by value, and in this case it cannot be
241 // defaulted.
242 hasParameter(0, hasType(lValueReferenceType())))
243 .bind(SpecialFunction),
244 this);
245 }
246
check(const MatchFinder::MatchResult & Result)247 void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
248 // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
249 const auto *SpecialFunctionDecl =
250 Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
251
252 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
253 return;
254
255 // Discard explicitly deleted/defaulted special member functions and those
256 // that are not user-provided (automatically generated).
257 if (SpecialFunctionDecl->isDeleted() ||
258 SpecialFunctionDecl->isExplicitlyDefaulted() ||
259 SpecialFunctionDecl->isLateTemplateParsed() ||
260 SpecialFunctionDecl->isTemplateInstantiation() ||
261 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
262 return;
263
264 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
265 if (!Body)
266 return;
267
268 // If there is code inside the body, don't warn.
269 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
270 return;
271
272 // If there are comments inside the body, don't do the change.
273 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
274 bodyEmpty(Result.Context, Body);
275
276 std::vector<FixItHint> RemoveInitializers;
277 unsigned MemberType;
278 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
279 if (Ctor->getNumParams() == 0) {
280 MemberType = 0;
281 } else {
282 if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
283 return;
284 MemberType = 1;
285 // If there are constructor initializers, they must be removed.
286 for (const auto *Init : Ctor->inits()) {
287 RemoveInitializers.emplace_back(
288 FixItHint::CreateRemoval(Init->getSourceRange()));
289 }
290 }
291 } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
292 MemberType = 2;
293 } else {
294 if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
295 return;
296 MemberType = 3;
297 }
298
299 // The location of the body is more useful inside a macro as spelling and
300 // expansion locations are reported.
301 SourceLocation Location = SpecialFunctionDecl->getLocation();
302 if (Location.isMacroID())
303 Location = Body->getBeginLoc();
304
305 auto Diag = diag(
306 Location,
307 "use '= default' to define a trivial %select{default constructor|copy "
308 "constructor|destructor|copy-assignment operator}0");
309 Diag << MemberType;
310
311 if (ApplyFix) {
312 // Skipping comments, check for a semicolon after Body->getSourceRange()
313 Optional<Token> Token = utils::lexer::findNextTokenSkippingComments(
314 Body->getSourceRange().getEnd().getLocWithOffset(1),
315 Result.Context->getSourceManager(), Result.Context->getLangOpts());
316 StringRef Replacement =
317 Token && Token->is(tok::semi) ? "= default" : "= default;";
318 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
319 << RemoveInitializers;
320 }
321 }
322
323 } // namespace modernize
324 } // namespace tidy
325 } // namespace clang
326