clang-tools  8.0.0
UseEqualsDefaultCheck.cpp
Go to the documentation of this file.
1 //===--- UseEqualsDefaultCheck.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 "UseEqualsDefaultCheck.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 all the named non-static fields of \p Record.
24 static std::set<const FieldDecl *>
25 getAllNamedFields(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.
38 static 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.
51 internal::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.
60 static 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(
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  initListExpr(has(AccessToFieldInParam)),
101  cxxConstructExpr(
102  hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
103  argumentCountIs(1),
104  hasArgument(0, AccessToFieldInParam))))))),
105  *Ctor, *Context)
106  .empty())
107  return false;
108  }
109 
110  // Ensure that we don't do anything else, like initializing an indirect base.
111  return Ctor->getNumCtorInitializers() ==
112  BasesToInit.size() + FieldsToInit.size();
113 }
114 
115 /// \brief Checks that the given method is an overloading of the assignment
116 /// operator, has copy signature, returns a reference to "*this" and copies
117 /// all its members and subobjects.
118 static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
119  const CXXMethodDecl *Operator) {
120  const auto *Record = Operator->getParent();
121  const auto *Param = Operator->getParamDecl(0);
122 
123  // Base classes and members that have to be copied.
124  auto BasesToInit = getAllDirectBases(Record);
125  auto FieldsToInit = getAllNamedFields(Record);
126 
127  const auto *Compound = cast<CompoundStmt>(Operator->getBody());
128 
129  // The assignment operator definition has to end with the following return
130  // statement:
131  // return *this;
132  if (Compound->body_empty() ||
133  match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
134  hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))),
135  *Compound->body_back(), *Context)
136  .empty())
137  return false;
138 
139  // Ensure that all the bases are copied.
140  for (const auto *Base : BasesToInit) {
141  // Assignment operator of a base class:
142  // Base::operator=(Other);
143  //
144  // Clang translates this into:
145  // ((Base*)this)->operator=((Base)Other);
146  //
147  // So we are looking for a member call that fulfills:
148  if (match(
149  compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
150  // - The object is an implicit cast of 'this' to a pointer to
151  // a base class.
152  onImplicitObjectArgument(
153  implicitCastExpr(hasImplicitDestinationType(
154  pointsTo(type(equalsNode(Base)))),
155  hasSourceExpression(cxxThisExpr()))),
156  // - The called method is the operator=.
157  callee(cxxMethodDecl(isCopyAssignmentOperator())),
158  // - The argument is (an implicit cast to a Base of) the
159  // argument taken by "Operator".
160  argumentCountIs(1),
161  hasArgument(0, declRefExpr(to(varDecl(equalsNode(Param))))))))),
162  *Compound, *Context)
163  .empty())
164  return false;
165  }
166 
167  // Ensure that all the members are copied.
168  for (const auto *Field : FieldsToInit) {
169  // The assignment of data members:
170  // Field = Other.Field;
171  // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
172  // otherwise.
173  auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
174  member(fieldDecl(equalsNode(Field))));
175  auto RHS = accessToFieldInVar(Field, Param);
176  if (match(
177  compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
178  binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
179  cxxOperatorCallExpr(hasOverloadedOperatorName("="),
180  argumentCountIs(2), hasArgument(0, LHS),
181  hasArgument(1, RHS))))))),
182  *Compound, *Context)
183  .empty())
184  return false;
185  }
186 
187  // Ensure that we don't do anything else.
188  return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
189 }
190 
191 /// \brief Returns false if the body has any non-whitespace character.
192 static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
193  bool Invalid = false;
194  StringRef Text = Lexer::getSourceText(
195  CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
196  Body->getRBracLoc()),
197  Context->getSourceManager(), Context->getLangOpts(), &Invalid);
198  return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
199 }
200 
201 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name,
202  ClangTidyContext *Context)
203  : ClangTidyCheck(Name, Context),
204  IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true) != 0) {}
205 
207  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
208 }
209 
210 void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
211  if (!getLangOpts().CPlusPlus)
212  return;
213 
214  // Destructor.
215  Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
216  this);
217  Finder->addMatcher(
218  cxxConstructorDecl(
219  isDefinition(),
220  anyOf(
221  // Default constructor.
222  allOf(unless(hasAnyConstructorInitializer(isWritten())),
223  parameterCountIs(0)),
224  // Copy constructor.
225  allOf(isCopyConstructor(),
226  // Discard constructors that can be used as a copy
227  // constructor because all the other arguments have
228  // default values.
229  parameterCountIs(1))))
230  .bind(SpecialFunction),
231  this);
232  // Copy-assignment operator.
233  Finder->addMatcher(
234  cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
235  // isCopyAssignmentOperator() allows the parameter to be
236  // passed by value, and in this case it cannot be
237  // defaulted.
238  hasParameter(0, hasType(lValueReferenceType())))
239  .bind(SpecialFunction),
240  this);
241 }
242 
243 void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
244  std::string SpecialFunctionName;
245 
246  // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
247  const auto *SpecialFunctionDecl =
248  Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
249 
250  if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
251  return;
252 
253  // Discard explicitly deleted/defaulted special member functions and those
254  // that are not user-provided (automatically generated).
255  if (SpecialFunctionDecl->isDeleted() ||
256  SpecialFunctionDecl->isExplicitlyDefaulted() ||
257  SpecialFunctionDecl->isLateTemplateParsed() ||
258  SpecialFunctionDecl->isTemplateInstantiation() ||
259  !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
260  return;
261 
262  const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
263  if (!Body)
264  return;
265 
266  // If there is code inside the body, don't warn.
267  if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
268  return;
269 
270  // If there are comments inside the body, don't do the change.
271  bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
272  bodyEmpty(Result.Context, Body);
273 
274  std::vector<FixItHint> RemoveInitializers;
275 
276  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
277  if (Ctor->getNumParams() == 0) {
278  SpecialFunctionName = "default constructor";
279  } else {
280  if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
281  return;
282  SpecialFunctionName = "copy constructor";
283  // If there are constructor initializers, they must be removed.
284  for (const auto *Init : Ctor->inits()) {
285  RemoveInitializers.emplace_back(
286  FixItHint::CreateRemoval(Init->getSourceRange()));
287  }
288  }
289  } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
290  SpecialFunctionName = "destructor";
291  } else {
292  if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
293  return;
294  SpecialFunctionName = "copy-assignment operator";
295  }
296 
297  // The location of the body is more useful inside a macro as spelling and
298  // expansion locations are reported.
299  SourceLocation Location = SpecialFunctionDecl->getLocation();
300  if (Location.isMacroID())
301  Location = Body->getBeginLoc();
302 
303  auto Diag = diag(Location, "use '= default' to define a trivial " +
304  SpecialFunctionName);
305 
306  if (ApplyFix)
307  Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;")
308  << RemoveInitializers;
309 }
310 
311 } // namespace modernize
312 } // namespace tidy
313 } // 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[]
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:473
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
llvm::SmallVector< uint64_t, 1024 > Record
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body)
Returns false if the body has any non-whitespace character.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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 ...
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.