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