blob: 1b677147be22d7b89d094b7b32141124880b0809 [file] [log] [blame]
Malcolm Parsonsbcf23662016-12-01 17:24:42 +00001//===--- UseEqualsDefaultCheck.cpp - clang-tidy----------------------------===//
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Malcolm Parsonsbcf23662016-12-01 17:24:42 +000010#include "UseEqualsDefaultCheck.h"
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +000011#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +000013#include "clang/Lex/Lexer.h"
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +000014
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
19namespace modernize {
20
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +000021static const char SpecialFunction[] = "SpecialFunction";
22
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +000023/// \brief Finds all the named non-static fields of \p Record.
24static std::set<const FieldDecl *>
25getAllNamedFields(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/// \brief Returns the names of the direct bases of \p Record, both virtual and
37/// non-virtual.
38static 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/// \brief 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.
51internal::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/// \brief Check that the given constructor has copy signature and that it
59/// copy-initializes all its bases and members.
60static 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 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
79 isBaseInitializer(),
80 withInitializer(cxxConstructExpr(allOf(
81 hasType(equalsNode(Base)),
82 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
83 argumentCountIs(1),
84 hasArgument(
85 0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
86 *Ctor, *Context)
87 .empty())
88 return false;
89 }
90
91 // Ensure that all the members are copied.
92 for (const auto *Field : FieldsToInit) {
93 auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
94 // The initialization is a CXXConstructExpr for class types.
95 if (match(
96 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
97 isMemberInitializer(), forField(equalsNode(Field)),
98 withInitializer(anyOf(
99 AccessToFieldInParam,
100 cxxConstructExpr(allOf(
101 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
102 argumentCountIs(1),
103 hasArgument(0, AccessToFieldInParam)))))))),
104 *Ctor, *Context)
105 .empty())
106 return false;
107 }
108
109 // Ensure that we don't do anything else, like initializing an indirect base.
110 return Ctor->getNumCtorInitializers() ==
111 BasesToInit.size() + FieldsToInit.size();
112}
113
114/// \brief Checks that the given method is an overloading of the assignment
115/// operator, has copy signature, returns a reference to "*this" and copies
116/// all its members and subobjects.
117static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
118 const CXXMethodDecl *Operator) {
119 const auto *Record = Operator->getParent();
120 const auto *Param = Operator->getParamDecl(0);
121
122 // Base classes and members that have to be copied.
123 auto BasesToInit = getAllDirectBases(Record);
124 auto FieldsToInit = getAllNamedFields(Record);
125
126 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
127
128 // The assignment operator definition has to end with the following return
129 // statement:
130 // return *this;
131 if (Compound->body_empty() ||
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000132 match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
133 hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))),
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000134 *Compound->body_back(), *Context)
135 .empty())
136 return false;
137
138 // Ensure that all the bases are copied.
139 for (const auto *Base : BasesToInit) {
140 // Assignment operator of a base class:
141 // Base::operator=(Other);
142 //
143 // Clang translates this into:
144 // ((Base*)this)->operator=((Base)Other);
145 //
146 // So we are looking for a member call that fulfills:
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000147 if (match(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
148 // - The object is an implicit cast of 'this' to a pointer to
149 // a base class.
150 onImplicitObjectArgument(
151 implicitCastExpr(hasImplicitDestinationType(
152 pointsTo(type(equalsNode(Base)))),
153 hasSourceExpression(cxxThisExpr()))),
154 // - The called method is the operator=.
155 callee(cxxMethodDecl(isCopyAssignmentOperator())),
156 // - The argument is (an implicit cast to a Base of) the
157 // argument taken by "Operator".
158 argumentCountIs(1),
159 hasArgument(0,
160 declRefExpr(to(varDecl(equalsNode(Param)))))))))),
161 *Compound, *Context)
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000162 .empty())
163 return false;
164 }
165
166 // Ensure that all the members are copied.
167 for (const auto *Field : FieldsToInit) {
168 // The assignment of data members:
169 // Field = Other.Field;
170 // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
171 // otherwise.
172 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
173 member(fieldDecl(equalsNode(Field))));
174 auto RHS = accessToFieldInVar(Field, Param);
175 if (match(
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000176 compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000177 binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
178 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
179 argumentCountIs(2), hasArgument(0, LHS),
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000180 hasArgument(1, RHS))))))),
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000181 *Compound, *Context)
182 .empty())
183 return false;
184 }
185
186 // Ensure that we don't do anything else.
187 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
188}
189
190/// \brief Returns false if the body has any non-whitespace character.
191static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
192 bool Invalid = false;
193 StringRef Text = Lexer::getSourceText(
194 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
195 Body->getRBracLoc()),
196 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
197 return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
198}
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000199
Malcolm Parsonsbcf23662016-12-01 17:24:42 +0000200void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000201 if (getLangOpts().CPlusPlus) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000202 // Destructor.
203 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
204 this);
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000205 Finder->addMatcher(
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000206 cxxConstructorDecl(
207 isDefinition(),
208 anyOf(
209 // Default constructor.
Malcolm Parsons7a325532016-10-11 10:47:06 +0000210 allOf(unless(hasAnyConstructorInitializer(isWritten())),
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000211 parameterCountIs(0)),
212 // Copy constructor.
213 allOf(isCopyConstructor(),
214 // Discard constructors that can be used as a copy
215 // constructor because all the other arguments have
216 // default values.
217 parameterCountIs(1))))
218 .bind(SpecialFunction),
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000219 this);
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000220 // Copy-assignment operator.
221 Finder->addMatcher(
222 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
223 // isCopyAssignmentOperator() allows the parameter to be
224 // passed by value, and in this case it cannot be
225 // defaulted.
226 hasParameter(0, hasType(lValueReferenceType())))
227 .bind(SpecialFunction),
228 this);
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000229 }
230}
231
Malcolm Parsonsbcf23662016-12-01 17:24:42 +0000232void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000233 std::string SpecialFunctionName;
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000234
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000235 // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
236 const auto *SpecialFunctionDecl =
237 Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
238
239 // Discard explicitly deleted/defaulted special member functions and those
240 // that are not user-provided (automatically generated).
241 if (SpecialFunctionDecl->isDeleted() ||
242 SpecialFunctionDecl->isExplicitlyDefaulted() ||
Aaron Ballmanb6182982016-01-20 22:14:10 +0000243 SpecialFunctionDecl->isLateTemplateParsed() ||
Malcolm Parsons31055c62016-11-16 09:51:40 +0000244 SpecialFunctionDecl->isTemplateInstantiation() ||
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000245 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000246 return;
247
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000248 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
249 if (!Body)
250 return;
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000251
Malcolm Parsons5be50922016-11-17 09:14:04 +0000252 // If there is code inside the body, don't warn.
253 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000254 return;
255
Malcolm Parsons5be50922016-11-17 09:14:04 +0000256 // If there are comments inside the body, don't do the change.
257 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
258 bodyEmpty(Result.Context, Body);
259
Malcolm Parsonsc2da6312016-10-20 15:31:34 +0000260 std::vector<FixItHint> RemoveInitializers;
261
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000262 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
263 if (Ctor->getNumParams() == 0) {
264 SpecialFunctionName = "default constructor";
265 } else {
266 if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
267 return;
268 SpecialFunctionName = "copy constructor";
Malcolm Parsons7a325532016-10-11 10:47:06 +0000269 // If there are constructor initializers, they must be removed.
Malcolm Parsonse9425b22016-10-20 15:40:34 +0000270 for (const auto *Init : Ctor->inits()) {
Malcolm Parsonsc2da6312016-10-20 15:31:34 +0000271 RemoveInitializers.emplace_back(
272 FixItHint::CreateRemoval(Init->getSourceRange()));
Malcolm Parsons7a325532016-10-11 10:47:06 +0000273 }
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000274 }
Craig Topper506dad82015-11-18 07:08:11 +0000275 } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000276 SpecialFunctionName = "destructor";
277 } else {
278 if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
279 return;
280 SpecialFunctionName = "copy-assignment operator";
281 }
282
Malcolm Parsons5be50922016-11-17 09:14:04 +0000283 // The location of the body is more useful inside a macro as spelling and
284 // expansion locations are reported.
285 SourceLocation Location = SpecialFunctionDecl->getLocation();
286 if (Location.isMacroID())
287 Location = Body->getLocStart();
288
289 auto Diag = diag(Location, "use '= default' to define a trivial " +
290 SpecialFunctionName);
291
292 if (ApplyFix)
293 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;")
294 << RemoveInitializers;
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000295}
296
297} // namespace modernize
298} // namespace tidy
299} // namespace clang