blob: 68ead15e2240e416d4893ee7f3daa51858e4367e [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() ||
163 match(returnStmt(has(unaryOperator(hasOperatorName("*"),
164 hasUnaryOperand(cxxThisExpr())))),
165 *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:
178 if (match(
179 compoundStmt(has(cxxMemberCallExpr(allOf(
180 // - The object is an implicit cast of 'this' to a pointer to
181 // a base class.
182 onImplicitObjectArgument(
183 implicitCastExpr(hasImplicitDestinationType(
184 pointsTo(type(equalsNode(Base)))),
185 hasSourceExpression(cxxThisExpr()))),
186 // - The called method is the operator=.
187 callee(cxxMethodDecl(isCopyAssignmentOperator())),
188 // - The argument is (an implicit cast to a Base of) the
189 // argument taken by "Operator".
190 argumentCountIs(1),
191 hasArgument(0, declRefExpr(to(varDecl(equalsNode(Param))))))))),
192 *Compound, *Context)
193 .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(
207 compoundStmt(has(stmt(anyOf(
208 binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
209 cxxOperatorCallExpr(hasOverloadedOperatorName("="),
210 argumentCountIs(2), hasArgument(0, LHS),
211 hasArgument(1, RHS)))))),
212 *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.
241 allOf(unless(hasAnyConstructorInitializer(anything())),
242 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() ||
275 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000276 return;
277
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000278 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
279 if (!Body)
280 return;
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000281
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000282 // Default locations.
283 StartLoc = Body->getLBracLoc();
284 EndLoc = Body->getRBracLoc();
285
286 // If there are comments inside the body, don't do the change.
287 if (!SpecialFunctionDecl->isCopyAssignmentOperator() &&
288 !bodyEmpty(Result.Context, Body))
289 return;
290
291 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
292 if (Ctor->getNumParams() == 0) {
293 SpecialFunctionName = "default constructor";
294 } else {
295 if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
296 return;
297 SpecialFunctionName = "copy constructor";
298 }
299 // 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 }
Craig Topper506dad82015-11-18 07:08:11 +0000305 } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000306 SpecialFunctionName = "destructor";
307 } else {
308 if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
309 return;
310 SpecialFunctionName = "copy-assignment operator";
311 }
312
313 diag(SpecialFunctionDecl->getLocStart(),
314 "use '= default' to define a trivial " + SpecialFunctionName)
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000315 << FixItHint::CreateReplacement(
Angel Garcia Gomezf2bc2f02015-11-02 10:34:19 +0000316 CharSourceRange::getTokenRange(StartLoc, EndLoc), "= default;");
Angel Garcia Gomez8dedeb02015-10-21 12:58:15 +0000317}
318
319} // namespace modernize
320} // namespace tidy
321} // namespace clang