blob: 38fb5f6ff3ba4bbb4865ff169fe99c40e7b62e08 [file] [log] [blame]
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +00001//===--- UseDefaultCheck.cpp - clang-tidy----------------------------------===//
2//
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
10#include "UseDefaultCheck.h"
11#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
23/// \brief Finds the SourceLocation of the colon ':' before the initialization
24/// list in the definition of a constructor.
25static SourceLocation getColonLoc(const ASTContext *Context,
26 const CXXConstructorDecl *Ctor) {
27 // FIXME: First init is the first initialization that is going to be
28 // performed, no matter what was the real order in the source code. If the
29 // order of the inits is wrong in the code, it may result in a false negative.
30 SourceLocation FirstInit = (*Ctor->init_begin())->getSourceLocation();
31 SourceLocation LastArg =
32 Ctor->getParamDecl(Ctor->getNumParams() - 1)->getLocEnd();
33 // We need to find the colon between the ')' and the first initializer.
34 bool Invalid = false;
35 StringRef Text = Lexer::getSourceText(
36 CharSourceRange::getCharRange(LastArg, FirstInit),
37 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
38 if (Invalid)
39 return SourceLocation();
40
41 size_t ColonPos = Text.rfind(':');
42 if (ColonPos == StringRef::npos)
43 return SourceLocation();
44
45 Text = Text.drop_front(ColonPos + 1);
46 if (std::strspn(Text.data(), " \t\r\n") != Text.size()) {
47 // If there are comments, preprocessor directives or anything, abort.
48 return SourceLocation();
49 }
50 // FIXME: don't remove comments in the middle of the initializers.
51 return LastArg.getLocWithOffset(ColonPos);
52}
53
54/// \brief Finds all the named non-static fields of \p Record.
55static std::set<const FieldDecl *>
56getAllNamedFields(const CXXRecordDecl *Record) {
57 std::set<const FieldDecl *> Result;
58 for (const auto *Field : Record->fields()) {
59 // Static data members are not in this range.
60 if (Field->isUnnamedBitfield())
61 continue;
62 Result.insert(Field);
63 }
64 return Result;
65}
66
67/// \brief Returns the names of the direct bases of \p Record, both virtual and
68/// non-virtual.
69static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
70 std::set<const Type *> Result;
71 for (auto Base : Record->bases()) {
72 // CXXBaseSpecifier.
73 const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
74 Result.insert(BaseType);
75 }
76 return Result;
77}
78
79/// \brief Returns a matcher that matches member expressions where the base is
80/// the variable declared as \p Var and the accessed member is the one declared
81/// as \p Field.
82internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
83 const ValueDecl *Var) {
84 return ignoringImpCasts(
85 memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
86 member(fieldDecl(equalsNode(Field)))));
87}
88
89/// \brief Check that the given constructor has copy signature and that it
90/// copy-initializes all its bases and members.
91static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
92 const CXXConstructorDecl *Ctor) {
93 // An explicitly-defaulted constructor cannot have default arguments.
94 if (Ctor->getMinRequiredArguments() != 1)
95 return false;
96
97 const auto *Record = Ctor->getParent();
98 const auto *Param = Ctor->getParamDecl(0);
99
100 // Base classes and members that have to be copied.
101 auto BasesToInit = getAllDirectBases(Record);
102 auto FieldsToInit = getAllNamedFields(Record);
103
104 // Ensure that all the bases are copied.
105 for (const auto *Base : BasesToInit) {
106 // The initialization of a base class should be a call to a copy
107 // constructor of the base.
108 if (match(
109 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
110 isBaseInitializer(),
111 withInitializer(cxxConstructExpr(allOf(
112 hasType(equalsNode(Base)),
113 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
114 argumentCountIs(1),
115 hasArgument(
116 0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
117 *Ctor, *Context)
118 .empty())
119 return false;
120 }
121
122 // Ensure that all the members are copied.
123 for (const auto *Field : FieldsToInit) {
124 auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
125 // The initialization is a CXXConstructExpr for class types.
126 if (match(
127 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
128 isMemberInitializer(), forField(equalsNode(Field)),
129 withInitializer(anyOf(
130 AccessToFieldInParam,
131 cxxConstructExpr(allOf(
132 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
133 argumentCountIs(1),
134 hasArgument(0, AccessToFieldInParam)))))))),
135 *Ctor, *Context)
136 .empty())
137 return false;
138 }
139
140 // Ensure that we don't do anything else, like initializing an indirect base.
141 return Ctor->getNumCtorInitializers() ==
142 BasesToInit.size() + FieldsToInit.size();
143}
144
145/// \brief Checks that the given method is an overloading of the assignment
146/// operator, has copy signature, returns a reference to "*this" and copies
147/// all its members and subobjects.
148static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
149 const CXXMethodDecl *Operator) {
150 const auto *Record = Operator->getParent();
151 const auto *Param = Operator->getParamDecl(0);
152
153 // Base classes and members that have to be copied.
154 auto BasesToInit = getAllDirectBases(Record);
155 auto FieldsToInit = getAllNamedFields(Record);
156
157 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
158
159 // The assignment operator definition has to end with the following return
160 // statement:
161 // return *this;
162 if (Compound->body_empty() ||
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000163 match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
164 hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))),
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000165 *Compound->body_back(), *Context)
166 .empty())
167 return false;
168
169 // Ensure that all the bases are copied.
170 for (const auto *Base : BasesToInit) {
171 // Assignment operator of a base class:
172 // Base::operator=(Other);
173 //
174 // Clang translates this into:
175 // ((Base*)this)->operator=((Base)Other);
176 //
177 // So we are looking for a member call that fulfills:
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000178 if (match(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
179 // - The object is an implicit cast of 'this' to a pointer to
180 // a base class.
181 onImplicitObjectArgument(
182 implicitCastExpr(hasImplicitDestinationType(
183 pointsTo(type(equalsNode(Base)))),
184 hasSourceExpression(cxxThisExpr()))),
185 // - The called method is the operator=.
186 callee(cxxMethodDecl(isCopyAssignmentOperator())),
187 // - The argument is (an implicit cast to a Base of) the
188 // argument taken by "Operator".
189 argumentCountIs(1),
190 hasArgument(0,
191 declRefExpr(to(varDecl(equalsNode(Param)))))))))),
192 *Compound, *Context)
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000193 .empty())
194 return false;
195 }
196
197 // Ensure that all the members are copied.
198 for (const auto *Field : FieldsToInit) {
199 // The assignment of data members:
200 // Field = Other.Field;
201 // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
202 // otherwise.
203 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
204 member(fieldDecl(equalsNode(Field))));
205 auto RHS = accessToFieldInVar(Field, Param);
206 if (match(
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000207 compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000208 binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
209 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
210 argumentCountIs(2), hasArgument(0, LHS),
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000211 hasArgument(1, RHS))))))),
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000212 *Compound, *Context)
213 .empty())
214 return false;
215 }
216
217 // Ensure that we don't do anything else.
218 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
219}
220
221/// \brief Returns false if the body has any non-whitespace character.
222static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
223 bool Invalid = false;
224 StringRef Text = Lexer::getSourceText(
225 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
226 Body->getRBracLoc()),
227 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
228 return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
229}
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000230
231void UseDefaultCheck::registerMatchers(MatchFinder *Finder) {
232 if (getLangOpts().CPlusPlus) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000233 // Destructor.
234 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
235 this);
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000236 Finder->addMatcher(
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000237 cxxConstructorDecl(
238 isDefinition(),
239 anyOf(
240 // Default constructor.
Malcolm Parsons7a325532016-10-11 10:47:06 +0000241 allOf(unless(hasAnyConstructorInitializer(isWritten())),
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000242 parameterCountIs(0)),
243 // Copy constructor.
244 allOf(isCopyConstructor(),
245 // Discard constructors that can be used as a copy
246 // constructor because all the other arguments have
247 // default values.
248 parameterCountIs(1))))
249 .bind(SpecialFunction),
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000250 this);
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000251 // Copy-assignment operator.
252 Finder->addMatcher(
253 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
254 // isCopyAssignmentOperator() allows the parameter to be
255 // passed by value, and in this case it cannot be
256 // defaulted.
257 hasParameter(0, hasType(lValueReferenceType())))
258 .bind(SpecialFunction),
259 this);
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000260 }
261}
262
263void UseDefaultCheck::check(const MatchFinder::MatchResult &Result) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000264 std::string SpecialFunctionName;
265 SourceLocation StartLoc, EndLoc;
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000266
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000267 // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
268 const auto *SpecialFunctionDecl =
269 Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
270
271 // Discard explicitly deleted/defaulted special member functions and those
272 // that are not user-provided (automatically generated).
273 if (SpecialFunctionDecl->isDeleted() ||
274 SpecialFunctionDecl->isExplicitlyDefaulted() ||
Aaron Ballmanb6182982016-01-20 22:14:10 +0000275 SpecialFunctionDecl->isLateTemplateParsed() ||
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000276 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000277 return;
278
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000279 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
280 if (!Body)
281 return;
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000282
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000283 // Default locations.
284 StartLoc = Body->getLBracLoc();
285 EndLoc = Body->getRBracLoc();
286
287 // If there are comments inside the body, don't do the change.
288 if (!SpecialFunctionDecl->isCopyAssignmentOperator() &&
289 !bodyEmpty(Result.Context, Body))
290 return;
291
292 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
293 if (Ctor->getNumParams() == 0) {
294 SpecialFunctionName = "default constructor";
295 } else {
296 if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
297 return;
298 SpecialFunctionName = "copy constructor";
Malcolm Parsons7a325532016-10-11 10:47:06 +0000299 // If there are constructor initializers, they must be removed.
300 if (Ctor->getNumCtorInitializers() != 0) {
301 StartLoc = getColonLoc(Result.Context, Ctor);
302 if (!StartLoc.isValid())
303 return;
304 }
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000305 }
Craig Topper506dad82015-11-18 07:08:11 +0000306 } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000307 SpecialFunctionName = "destructor";
308 } else {
309 if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
310 return;
311 SpecialFunctionName = "copy-assignment operator";
312 }
313
314 diag(SpecialFunctionDecl->getLocStart(),
315 "use '= default' to define a trivial " + SpecialFunctionName)
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000316 << FixItHint::CreateReplacement(
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000317 CharSourceRange::getTokenRange(StartLoc, EndLoc), "= default;");
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000318}
319
320} // namespace modernize
321} // namespace tidy
322} // namespace clang