clang-tools  3.9.0
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.
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.
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(ignoringParenImpCasts(unaryOperator(
164  hasOperatorName("*"), 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(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)
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(ignoringParenImpCasts(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
static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context, const CXXMethodDecl *Operator)
Checks that the given method is an overloading of the assignment operator, has copy signature...
static const char SpecialFunction[]
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body)
Returns false if the body has any non-whitespace character.
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 ...
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
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...
static std::set< const Type * > getAllDirectBases(const CXXRecordDecl *Record)
Returns the names of the direct bases of Record, both virtual and non-virtual.
static std::set< const FieldDecl * > getAllNamedFields(const CXXRecordDecl *Record)
Finds all the named non-static fields of Record.
const NamedDecl * Result
Definition: USRFinder.cpp:137
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...