clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

UseDefaultCheck.cpp
Go to the documentation of this file.
1 //===--- 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"
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 /// \brief Finds the SourceLocation of the colon ':' before the initialization
24 /// list in the definition of a constructor.
25 static 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.
55 static std::set<const FieldDecl *>
56 getAllNamedFields(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.
69 static 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.
82 internal::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.
91 static 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.
148 static 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.
222 static 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 }
230
231 void UseDefaultCheck::registerMatchers(MatchFinder *Finder) {
232 if (getLangOpts().CPlusPlus) {
233 // Destructor.
234 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
235 this);
236 Finder->addMatcher(
237 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),
250 this);
251 // 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);
260 }
261 }
262
263 void UseDefaultCheck::check(const MatchFinder::MatchResult &Result) {
264 std::string SpecialFunctionName;
265 SourceLocation StartLoc, EndLoc;
266
267 // 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->isLateTemplateParsed() ||
276 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
277 return;
278
279 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
280 if (!Body)
281 return;
282
283 // 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";
299 }
300 // If there are constructor initializers, they must be removed.
301 if (Ctor->getNumCtorInitializers() != 0) {
302 StartLoc = getColonLoc(Result.Context, Ctor);
303 if (!StartLoc.isValid())
304 return;
305 }
306 } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
307 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)
316 << FixItHint::CreateReplacement(
317 CharSourceRange::getTokenRange(StartLoc, EndLoc), "= default;");
318 }
319
320 } // namespace modernize
321 } // namespace tidy
322 } // namespace clang
clang::tidy::modernize::isCopyAssignmentAndCanBeDefaulted
static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context, const CXXMethodDecl *Operator)
Checks that the given method is an overloading of the assignment operator, has copy signature...
Definition: UseDefaultCheck.cpp:148
clang::tidy::modernize::SpecialFunction
static const char SpecialFunction[]
Definition: UseDefaultCheck.cpp:21
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
UseDefaultCheck.h
clang::tidy::modernize::bodyEmpty
static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body)
Returns false if the body has any non-whitespace character.
Definition: UseDefaultCheck.cpp:222
clang::tidy::modernize::accessToFieldInVar
internal::Matcher< Expr > accessToFieldInVar(const FieldDecl *Field, const ValueDecl *Var)
Returns a matcher that matches member expressions where the base is the variable declared as Var and ...
Definition: UseDefaultCheck.cpp:82
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
clang::tidy::modernize::isCopyConstructorAndCanBeDefaulted
static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context, const CXXConstructorDecl *Ctor)
Check that the given constructor has copy signature and that it copy-initializes all its bases and me...
Definition: UseDefaultCheck.cpp:91
clang::tidy::modernize::getAllDirectBases
static std::set< const Type * > getAllDirectBases(const CXXRecordDecl *Record)
Returns the names of the direct bases of Record, both virtual and non-virtual.
Definition: UseDefaultCheck.cpp:69
clang::tidy::modernize::getAllNamedFields
static std::set< const FieldDecl * > getAllNamedFields(const CXXRecordDecl *Record)
Finds all the named non-static fields of Record.
Definition: UseDefaultCheck.cpp:56
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121
clang::tidy::modernize::getColonLoc
static SourceLocation getColonLoc(const ASTContext *Context, const CXXConstructorDecl *Ctor)
Finds the SourceLocation of the colon ':' before the initialization list in the definition of a const...
Definition: UseDefaultCheck.cpp:25