clang-tools  9.0.0
UseEqualsDefaultCheck.cpp
Go to the documentation of this file.
1 //===--- UseEqualsDefaultCheck.cpp - clang-tidy----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace modernize {
19 
20 static const char SpecialFunction[] = "SpecialFunction";
21 
22 /// \brief Finds all the named non-static fields of \p Record.
23 static std::set<const FieldDecl *>
24 getAllNamedFields(const CXXRecordDecl *Record) {
25  std::set<const FieldDecl *> Result;
26  for (const auto *Field : Record->fields()) {
27  // Static data members are not in this range.
28  if (Field->isUnnamedBitfield())
29  continue;
30  Result.insert(Field);
31  }
32  return Result;
33 }
34 
35 /// \brief Returns the names of the direct bases of \p Record, both virtual and
36 /// non-virtual.
37 static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
38  std::set<const Type *> Result;
39  for (auto Base : Record->bases()) {
40  // CXXBaseSpecifier.
41  const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
42  Result.insert(BaseType);
43  }
44  return Result;
45 }
46 
47 /// \brief Returns a matcher that matches member expressions where the base is
48 /// the variable declared as \p Var and the accessed member is the one declared
49 /// as \p Field.
50 internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
51  const ValueDecl *Var) {
52  return ignoringImpCasts(
53  memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
54  member(fieldDecl(equalsNode(Field)))));
55 }
56 
57 /// \brief Check that the given constructor has copy signature and that it
58 /// copy-initializes all its bases and members.
59 static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
60  const CXXConstructorDecl *Ctor) {
61  // An explicitly-defaulted constructor cannot have default arguments.
62  if (Ctor->getMinRequiredArguments() != 1)
63  return false;
64 
65  const auto *Record = Ctor->getParent();
66  const auto *Param = Ctor->getParamDecl(0);
67 
68  // Base classes and members that have to be copied.
69  auto BasesToInit = getAllDirectBases(Record);
70  auto FieldsToInit = getAllNamedFields(Record);
71 
72  // Ensure that all the bases are copied.
73  for (const auto *Base : BasesToInit) {
74  // The initialization of a base class should be a call to a copy
75  // constructor of the base.
76  if (match(
77  cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
78  isBaseInitializer(),
79  withInitializer(cxxConstructExpr(
80  hasType(equalsNode(Base)),
81  hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
82  argumentCountIs(1),
83  hasArgument(
84  0, declRefExpr(to(varDecl(equalsNode(Param)))))))))),
85  *Ctor, *Context)
86  .empty())
87  return false;
88  }
89 
90  // Ensure that all the members are copied.
91  for (const auto *Field : FieldsToInit) {
92  auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
93  // The initialization is a CXXConstructExpr for class types.
94  if (match(
95  cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
96  isMemberInitializer(), forField(equalsNode(Field)),
97  withInitializer(anyOf(
98  AccessToFieldInParam,
99  initListExpr(has(AccessToFieldInParam)),
100  cxxConstructExpr(
101  hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
102  argumentCountIs(1),
103  hasArgument(0, AccessToFieldInParam))))))),
104  *Ctor, *Context)
105  .empty())
106  return false;
107  }
108 
109  // Ensure that we don't do anything else, like initializing an indirect base.
110  return Ctor->getNumCtorInitializers() ==
111  BasesToInit.size() + FieldsToInit.size();
112 }
113 
114 /// \brief Checks that the given method is an overloading of the assignment
115 /// operator, has copy signature, returns a reference to "*this" and copies
116 /// all its members and subobjects.
117 static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
118  const CXXMethodDecl *Operator) {
119  const auto *Record = Operator->getParent();
120  const auto *Param = Operator->getParamDecl(0);
121 
122  // Base classes and members that have to be copied.
123  auto BasesToInit = getAllDirectBases(Record);
124  auto FieldsToInit = getAllNamedFields(Record);
125 
126  const auto *Compound = cast<CompoundStmt>(Operator->getBody());
127 
128  // The assignment operator definition has to end with the following return
129  // statement:
130  // return *this;
131  if (Compound->body_empty() ||
132  match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
133  hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))),
134  *Compound->body_back(), *Context)
135  .empty())
136  return false;
137 
138  // Ensure that all the bases are copied.
139  for (const auto *Base : BasesToInit) {
140  // Assignment operator of a base class:
141  // Base::operator=(Other);
142  //
143  // Clang translates this into:
144  // ((Base*)this)->operator=((Base)Other);
145  //
146  // So we are looking for a member call that fulfills:
147  if (match(
148  compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
149  // - The object is an implicit cast of 'this' to a pointer to
150  // a base class.
151  onImplicitObjectArgument(
152  implicitCastExpr(hasImplicitDestinationType(
153  pointsTo(type(equalsNode(Base)))),
154  hasSourceExpression(cxxThisExpr()))),
155  // - The called method is the operator=.
156  callee(cxxMethodDecl(isCopyAssignmentOperator())),
157  // - The argument is (an implicit cast to a Base of) the
158  // argument taken by "Operator".
159  argumentCountIs(1),
160  hasArgument(0, declRefExpr(to(varDecl(equalsNode(Param))))))))),
161  *Compound, *Context)
162  .empty())
163  return false;
164  }
165 
166  // Ensure that all the members are copied.
167  for (const auto *Field : FieldsToInit) {
168  // The assignment of data members:
169  // Field = Other.Field;
170  // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
171  // otherwise.
172  auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
173  member(fieldDecl(equalsNode(Field))));
174  auto RHS = accessToFieldInVar(Field, Param);
175  if (match(
176  compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
177  binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
178  cxxOperatorCallExpr(hasOverloadedOperatorName("="),
179  argumentCountIs(2), hasArgument(0, LHS),
180  hasArgument(1, RHS))))))),
181  *Compound, *Context)
182  .empty())
183  return false;
184  }
185 
186  // Ensure that we don't do anything else.
187  return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
188 }
189 
190 /// \brief Returns false if the body has any non-whitespace character.
191 static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
192  bool Invalid = false;
193  StringRef Text = Lexer::getSourceText(
194  CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
195  Body->getRBracLoc()),
196  Context->getSourceManager(), Context->getLangOpts(), &Invalid);
197  return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
198 }
199 
200 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name,
201  ClangTidyContext *Context)
202  : ClangTidyCheck(Name, Context),
203  IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true) != 0) {}
204 
206  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
207 }
208 
209 void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
210  if (!getLangOpts().CPlusPlus)
211  return;
212 
213  // Destructor.
214  Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
215  this);
216  Finder->addMatcher(
217  cxxConstructorDecl(
218  isDefinition(),
219  anyOf(
220  // Default constructor.
221  allOf(unless(hasAnyConstructorInitializer(isWritten())),
222  parameterCountIs(0)),
223  // Copy constructor.
224  allOf(isCopyConstructor(),
225  // Discard constructors that can be used as a copy
226  // constructor because all the other arguments have
227  // default values.
228  parameterCountIs(1))))
229  .bind(SpecialFunction),
230  this);
231  // Copy-assignment operator.
232  Finder->addMatcher(
233  cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
234  // isCopyAssignmentOperator() allows the parameter to be
235  // passed by value, and in this case it cannot be
236  // defaulted.
237  hasParameter(0, hasType(lValueReferenceType())))
238  .bind(SpecialFunction),
239  this);
240 }
241 
242 void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
243  std::string SpecialFunctionName;
244 
245  // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
246  const auto *SpecialFunctionDecl =
247  Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
248 
249  if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
250  return;
251 
252  // Discard explicitly deleted/defaulted special member functions and those
253  // that are not user-provided (automatically generated).
254  if (SpecialFunctionDecl->isDeleted() ||
255  SpecialFunctionDecl->isExplicitlyDefaulted() ||
256  SpecialFunctionDecl->isLateTemplateParsed() ||
257  SpecialFunctionDecl->isTemplateInstantiation() ||
258  !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
259  return;
260 
261  const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
262  if (!Body)
263  return;
264 
265  // If there is code inside the body, don't warn.
266  if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
267  return;
268 
269  // If there are comments inside the body, don't do the change.
270  bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
271  bodyEmpty(Result.Context, Body);
272 
273  std::vector<FixItHint> RemoveInitializers;
274 
275  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
276  if (Ctor->getNumParams() == 0) {
277  SpecialFunctionName = "default constructor";
278  } else {
279  if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
280  return;
281  SpecialFunctionName = "copy constructor";
282  // If there are constructor initializers, they must be removed.
283  for (const auto *Init : Ctor->inits()) {
284  RemoveInitializers.emplace_back(
285  FixItHint::CreateRemoval(Init->getSourceRange()));
286  }
287  }
288  } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
289  SpecialFunctionName = "destructor";
290  } else {
291  if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
292  return;
293  SpecialFunctionName = "copy-assignment operator";
294  }
295 
296  // The location of the body is more useful inside a macro as spelling and
297  // expansion locations are reported.
298  SourceLocation Location = SpecialFunctionDecl->getLocation();
299  if (Location.isMacroID())
300  Location = Body->getBeginLoc();
301 
302  auto Diag = diag(Location, "use '= default' to define a trivial " +
303  SpecialFunctionName);
304 
305  if (ApplyFix)
306  Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;")
307  << RemoveInitializers;
308 }
309 
310 } // namespace modernize
311 } // namespace tidy
312 } // 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::string Text
llvm::SmallVector< uint64_t, 1024 > Record
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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.
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:94
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
===– 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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
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.
std::unique_ptr< GlobalCompilationDatabase > Base
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.