1 //===--- UseEqualsDefaultCheck.cpp - clang-tidy----------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "UseEqualsDefaultCheck.h"
10 #include "../utils/LexerUtils.h"
11 #include "../utils/Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Lex/Lexer.h"
17 using namespace clang::ast_matchers
;
19 namespace clang::tidy::modernize
{
21 static const char SpecialFunction
[] = "SpecialFunction";
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())
36 /// Returns the names of the direct bases of \p Record, both virtual and
38 static std::set
<const Type
*> getAllDirectBases(const CXXRecordDecl
*Record
) {
39 std::set
<const Type
*> Result
;
40 for (auto Base
: Record
->bases()) {
42 const auto *BaseType
= Base
.getTypeSourceInfo()->getType().getTypePtr();
43 Result
.insert(BaseType
);
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
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
)))));
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)
66 const auto *Record
= Ctor
->getParent();
67 const auto *Param
= Ctor
->getParamDecl(0);
69 // Base classes and members that have to be copied.
70 auto BasesToInit
= getAllDirectBases(Record
);
71 auto FieldsToInit
= getAllNamedFields(Record
);
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.
80 forEachConstructorInitializer(cxxCtorInitializer(
82 withInitializer(cxxConstructExpr(
83 hasType(equalsNode(Base
)),
85 cxxConstructorDecl(isCopyConstructor())),
87 hasArgument(0, declRefExpr(to(varDecl(
88 equalsNode(Param
))))))))))),
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.
101 forEachConstructorInitializer(cxxCtorInitializer(
102 isMemberInitializer(), forField(equalsNode(Field
)),
103 withInitializer(anyOf(
104 AccessToFieldInParam
,
105 initListExpr(has(AccessToFieldInParam
)),
108 cxxConstructorDecl(isCopyConstructor())),
110 hasArgument(0, AccessToFieldInParam
)))))))),
116 // Ensure that we don't do anything else, like initializing an indirect base.
117 return Ctor
->getNumCtorInitializers() ==
118 BasesToInit
.size() + FieldsToInit
.size();
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);
129 // Base classes and members that have to be copied.
130 auto BasesToInit
= getAllDirectBases(Record
);
131 auto FieldsToInit
= getAllNamedFields(Record
);
133 const auto *Compound
= cast
<CompoundStmt
>(Operator
->getBody());
135 // The assignment operator definition has to end with the following return
138 if (Compound
->body_empty() ||
141 returnStmt(has(ignoringParenImpCasts(unaryOperator(
142 hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())))))),
143 *Compound
->body_back(), *Context
)
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);
152 // Clang translates this into:
153 // ((Base*)this)->operator=((Base)Other);
155 // So we are looking for a member call that fulfills:
158 compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
159 // - The object is an implicit cast of 'this' to a
162 onImplicitObjectArgument(implicitCastExpr(
163 hasImplicitDestinationType(hasCanonicalType(pointsTo(
164 type(equalsNode(Base
->getCanonicalTypeInternal()
166 hasSourceExpression(cxxThisExpr()))),
167 // - The called method is the operator=.
168 callee(cxxMethodDecl(isCopyAssignmentOperator())),
169 // - The argument is (an implicit cast to a Base of)
170 // the argument taken by "Operator".
173 0, declRefExpr(to(varDecl(equalsNode(Param
)))))))))),
179 // Ensure that all the members are copied.
180 for (const auto *Field
: FieldsToInit
) {
181 // The assignment of data members:
182 // Field = Other.Field;
183 // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
185 auto LHS
= memberExpr(hasObjectExpression(cxxThisExpr()),
186 member(fieldDecl(equalsNode(Field
))));
187 auto RHS
= accessToFieldInVar(Field
, Param
);
188 if (match(traverse(TK_AsIs
,
189 compoundStmt(has(ignoringParenImpCasts(binaryOperation(
190 hasOperatorName("="), hasLHS(LHS
), hasRHS(RHS
)))))),
196 // Ensure that we don't do anything else.
197 return Compound
->size() == BasesToInit
.size() + FieldsToInit
.size() + 1;
200 /// Returns false if the body has any non-whitespace character.
201 static bool bodyEmpty(const ASTContext
*Context
, const CompoundStmt
*Body
) {
202 bool Invalid
= false;
203 StringRef Text
= Lexer::getSourceText(
204 CharSourceRange::getCharRange(Body
->getLBracLoc().getLocWithOffset(1),
205 Body
->getRBracLoc()),
206 Context
->getSourceManager(), Context
->getLangOpts(), &Invalid
);
207 return !Invalid
&& std::strspn(Text
.data(), " \t\r\n") == Text
.size();
210 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name
,
211 ClangTidyContext
*Context
)
212 : ClangTidyCheck(Name
, Context
),
213 IgnoreMacros(Options
.getLocalOrGlobal("IgnoreMacros", true)) {}
215 void UseEqualsDefaultCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
216 Options
.store(Opts
, "IgnoreMacros", IgnoreMacros
);
220 AST_MATCHER(CXXMethodDecl
, isOutOfLine
) { return Node
.isOutOfLine(); }
223 void UseEqualsDefaultCheck::registerMatchers(MatchFinder
*Finder
) {
224 // Skip unions/union-like classes since their constructors behave differently
225 // when defaulted vs. empty.
226 auto IsUnionLikeClass
= recordDecl(
228 has(fieldDecl(isImplicit(), hasType(cxxRecordDecl(isUnion()))))));
230 const LangOptions
&LangOpts
= getLangOpts();
231 auto IsPublicOrOutOfLineUntilCPP20
=
233 ? cxxConstructorDecl()
234 : cxxConstructorDecl(anyOf(isOutOfLine(), isPublic()));
238 cxxDestructorDecl(isDefinition(), unless(ofClass(IsUnionLikeClass
)))
239 .bind(SpecialFunction
),
244 isDefinition(), unless(ofClass(IsUnionLikeClass
)),
245 unless(hasParent(functionTemplateDecl())),
247 // Default constructor.
248 allOf(parameterCountIs(0),
249 unless(hasAnyConstructorInitializer(isWritten())),
250 unless(isVariadic()), IsPublicOrOutOfLineUntilCPP20
),
252 allOf(isCopyConstructor(),
253 // Discard constructors that can be used as a copy
254 // constructor because all the other arguments have
256 parameterCountIs(1))))
257 .bind(SpecialFunction
),
259 // Copy-assignment operator.
261 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
262 unless(ofClass(IsUnionLikeClass
)),
263 unless(hasParent(functionTemplateDecl())),
264 // isCopyAssignmentOperator() allows the parameter to be
265 // passed by value, and in this case it cannot be
267 hasParameter(0, hasType(lValueReferenceType())),
268 // isCopyAssignmentOperator() allows non lvalue reference
269 // return types, and in this case it cannot be defaulted.
270 returns(qualType(hasCanonicalType(
271 allOf(lValueReferenceType(pointee(type())),
272 unless(matchers::isReferenceToConst()))))))
273 .bind(SpecialFunction
),
277 void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult
&Result
) {
278 // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
279 const auto *SpecialFunctionDecl
=
280 Result
.Nodes
.getNodeAs
<CXXMethodDecl
>(SpecialFunction
);
282 if (IgnoreMacros
&& SpecialFunctionDecl
->getLocation().isMacroID())
285 // Discard explicitly deleted/defaulted special member functions and those
286 // that are not user-provided (automatically generated).
287 if (SpecialFunctionDecl
->isDeleted() ||
288 SpecialFunctionDecl
->isExplicitlyDefaulted() ||
289 SpecialFunctionDecl
->isLateTemplateParsed() ||
290 SpecialFunctionDecl
->isTemplateInstantiation() ||
291 !SpecialFunctionDecl
->isUserProvided() || !SpecialFunctionDecl
->hasBody())
294 const auto *Body
= dyn_cast
<CompoundStmt
>(SpecialFunctionDecl
->getBody());
298 // If there is code inside the body, don't warn.
299 if (!SpecialFunctionDecl
->isCopyAssignmentOperator() && !Body
->body_empty())
302 // If body contain any preprocesor derictives, don't warn.
303 if (IgnoreMacros
&& utils::lexer::rangeContainsExpansionsOrDirectives(
304 Body
->getSourceRange(), *Result
.SourceManager
,
305 Result
.Context
->getLangOpts()))
308 // If there are comments inside the body, don't do the change.
309 bool ApplyFix
= SpecialFunctionDecl
->isCopyAssignmentOperator() ||
310 bodyEmpty(Result
.Context
, Body
);
312 std::vector
<FixItHint
> RemoveInitializers
;
313 unsigned MemberType
= 0;
314 if (const auto *Ctor
= dyn_cast
<CXXConstructorDecl
>(SpecialFunctionDecl
)) {
315 if (Ctor
->getNumParams() == 0) {
318 if (!isCopyConstructorAndCanBeDefaulted(Result
.Context
, Ctor
))
321 // If there are constructor initializers, they must be removed.
322 for (const auto *Init
: Ctor
->inits()) {
323 RemoveInitializers
.emplace_back(
324 FixItHint::CreateRemoval(Init
->getSourceRange()));
327 } else if (isa
<CXXDestructorDecl
>(SpecialFunctionDecl
)) {
330 if (!isCopyAssignmentAndCanBeDefaulted(Result
.Context
, SpecialFunctionDecl
))
335 // The location of the body is more useful inside a macro as spelling and
336 // expansion locations are reported.
337 SourceLocation Location
= SpecialFunctionDecl
->getLocation();
338 if (Location
.isMacroID())
339 Location
= Body
->getBeginLoc();
343 "use '= default' to define a trivial %select{default constructor|copy "
344 "constructor|destructor|copy-assignment operator}0");
348 SourceLocation UnifiedEnd
= utils::lexer::getUnifiedEndLoc(
349 *Body
, Result
.Context
->getSourceManager(),
350 Result
.Context
->getLangOpts());
351 // Skipping comments, check for a semicolon after Body->getSourceRange()
352 std::optional
<Token
> Token
= utils::lexer::findNextTokenSkippingComments(
353 UnifiedEnd
, Result
.Context
->getSourceManager(),
354 Result
.Context
->getLangOpts());
355 StringRef Replacement
=
356 Token
&& Token
->is(tok::semi
) ? "= default" : "= default;";
357 Diag
<< FixItHint::CreateReplacement(Body
->getSourceRange(), Replacement
)
358 << RemoveInitializers
;
362 } // namespace clang::tidy::modernize