clang-tools  16.0.0git
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 "../utils/LexerUtils.h"
11 #include "../utils/Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace modernize {
21 
22 static const char SpecialFunction[] = "SpecialFunction";
23 
24 /// Finds all the named non-static fields of \p Record.
25 static std::set<const FieldDecl *>
26 getAllNamedFields(const CXXRecordDecl *Record) {
27  std::set<const FieldDecl *> Result;
28  for (const auto *Field : Record->fields()) {
29  // Static data members are not in this range.
30  if (Field->isUnnamedBitfield())
31  continue;
32  Result.insert(Field);
33  }
34  return Result;
35 }
36 
37 /// Returns the names of the direct bases of \p Record, both virtual and
38 /// non-virtual.
39 static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
40  std::set<const Type *> Result;
41  for (auto Base : Record->bases()) {
42  // CXXBaseSpecifier.
43  const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
44  Result.insert(BaseType);
45  }
46  return Result;
47 }
48 
49 /// Returns a matcher that matches member expressions where the base is
50 /// the variable declared as \p Var and the accessed member is the one declared
51 /// as \p Field.
52 internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
53  const ValueDecl *Var) {
54  return ignoringImpCasts(
55  memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
56  member(fieldDecl(equalsNode(Field)))));
57 }
58 
59 /// Check that the given constructor has copy signature and that it
60 /// copy-initializes all its bases and members.
61 static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
62  const CXXConstructorDecl *Ctor) {
63  // An explicitly-defaulted constructor cannot have default arguments.
64  if (Ctor->getMinRequiredArguments() != 1)
65  return false;
66 
67  const auto *Record = Ctor->getParent();
68  const auto *Param = Ctor->getParamDecl(0);
69 
70  // Base classes and members that have to be copied.
71  auto BasesToInit = getAllDirectBases(Record);
72  auto FieldsToInit = getAllNamedFields(Record);
73 
74  // Ensure that all the bases are copied.
75  for (const auto *Base : BasesToInit) {
76  // The initialization of a base class should be a call to a copy
77  // constructor of the base.
78  if (match(
79  traverse(TK_AsIs,
80  cxxConstructorDecl(
81  forEachConstructorInitializer(cxxCtorInitializer(
82  isBaseInitializer(),
83  withInitializer(cxxConstructExpr(
84  hasType(equalsNode(Base)),
85  hasDeclaration(
86  cxxConstructorDecl(isCopyConstructor())),
87  argumentCountIs(1),
88  hasArgument(0, declRefExpr(to(varDecl(
89  equalsNode(Param))))))))))),
90  *Ctor, *Context)
91  .empty())
92  return false;
93  }
94 
95  // Ensure that all the members are copied.
96  for (const auto *Field : FieldsToInit) {
97  auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
98  // The initialization is a CXXConstructExpr for class types.
99  if (match(traverse(
100  TK_AsIs,
101  cxxConstructorDecl(
102  forEachConstructorInitializer(cxxCtorInitializer(
103  isMemberInitializer(), forField(equalsNode(Field)),
104  withInitializer(anyOf(
105  AccessToFieldInParam,
106  initListExpr(has(AccessToFieldInParam)),
107  cxxConstructExpr(
108  hasDeclaration(
109  cxxConstructorDecl(isCopyConstructor())),
110  argumentCountIs(1),
111  hasArgument(0, AccessToFieldInParam)))))))),
112  *Ctor, *Context)
113  .empty())
114  return false;
115  }
116 
117  // Ensure that we don't do anything else, like initializing an indirect base.
118  return Ctor->getNumCtorInitializers() ==
119  BasesToInit.size() + FieldsToInit.size();
120 }
121 
122 /// Checks that the given method is an overloading of the assignment
123 /// operator, has copy signature, returns a reference to "*this" and copies
124 /// all its members and subobjects.
125 static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
126  const CXXMethodDecl *Operator) {
127  const auto *Record = Operator->getParent();
128  const auto *Param = Operator->getParamDecl(0);
129 
130  // Base classes and members that have to be copied.
131  auto BasesToInit = getAllDirectBases(Record);
132  auto FieldsToInit = getAllNamedFields(Record);
133 
134  const auto *Compound = cast<CompoundStmt>(Operator->getBody());
135 
136  // The assignment operator definition has to end with the following return
137  // statement:
138  // return *this;
139  if (Compound->body_empty() ||
140  match(traverse(
141  TK_AsIs,
142  returnStmt(has(ignoringParenImpCasts(unaryOperator(
143  hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())))))),
144  *Compound->body_back(), *Context)
145  .empty())
146  return false;
147 
148  // Ensure that all the bases are copied.
149  for (const auto *Base : BasesToInit) {
150  // Assignment operator of a base class:
151  // Base::operator=(Other);
152  //
153  // Clang translates this into:
154  // ((Base*)this)->operator=((Base)Other);
155  //
156  // So we are looking for a member call that fulfills:
157  if (match(traverse(
158  TK_AsIs,
159  compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
160  // - The object is an implicit cast of 'this' to a
161  // pointer to
162  // a base class.
163  onImplicitObjectArgument(implicitCastExpr(
164  hasImplicitDestinationType(hasCanonicalType(pointsTo(
165  type(equalsNode(Base->getCanonicalTypeInternal()
166  .getTypePtr()))))),
167  hasSourceExpression(cxxThisExpr()))),
168  // - The called method is the operator=.
169  callee(cxxMethodDecl(isCopyAssignmentOperator())),
170  // - The argument is (an implicit cast to a Base of)
171  // the argument taken by "Operator".
172  argumentCountIs(1),
173  hasArgument(
174  0, declRefExpr(to(varDecl(equalsNode(Param)))))))))),
175  *Compound, *Context)
176  .empty())
177  return false;
178  }
179 
180  // Ensure that all the members are copied.
181  for (const auto *Field : FieldsToInit) {
182  // The assignment of data members:
183  // Field = Other.Field;
184  // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
185  // otherwise.
186  auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
187  member(fieldDecl(equalsNode(Field))));
188  auto RHS = accessToFieldInVar(Field, Param);
189  if (match(traverse(TK_AsIs,
190  compoundStmt(has(ignoringParenImpCasts(binaryOperation(
191  hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)))))),
192  *Compound, *Context)
193  .empty())
194  return false;
195  }
196 
197  // Ensure that we don't do anything else.
198  return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
199 }
200 
201 /// Returns false if the body has any non-whitespace character.
202 static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
203  bool Invalid = false;
204  StringRef Text = Lexer::getSourceText(
205  CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
206  Body->getRBracLoc()),
207  Context->getSourceManager(), Context->getLangOpts(), &Invalid);
208  return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
209 }
210 
211 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef Name,
212  ClangTidyContext *Context)
213  : ClangTidyCheck(Name, Context),
214  IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
215 
217  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
218 }
219 
220 namespace {
221 AST_MATCHER(CXXMethodDecl, isOutOfLine) { return Node.isOutOfLine(); }
222 } // namespace
223 
224 void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
225  // Skip unions/union-like classes since their constructors behave differently
226  // when defaulted vs. empty.
227  auto IsUnionLikeClass = recordDecl(
228  anyOf(isUnion(),
229  has(fieldDecl(isImplicit(), hasType(cxxRecordDecl(isUnion()))))));
230 
231  const LangOptions &LangOpts = getLangOpts();
232  auto IsPublicOrOutOfLineUntilCPP20 =
233  LangOpts.CPlusPlus20
234  ? cxxConstructorDecl()
235  : cxxConstructorDecl(anyOf(isOutOfLine(), isPublic()));
236 
237  // Destructor.
238  Finder->addMatcher(
239  cxxDestructorDecl(unless(hasParent(IsUnionLikeClass)), isDefinition())
240  .bind(SpecialFunction),
241  this);
242  Finder->addMatcher(
243  cxxConstructorDecl(
244  unless(
245  hasParent(decl(anyOf(IsUnionLikeClass, functionTemplateDecl())))),
246  isDefinition(),
247  anyOf(
248  // Default constructor.
249  allOf(unless(hasAnyConstructorInitializer(isWritten())),
250  unless(isVariadic()), parameterCountIs(0),
251  IsPublicOrOutOfLineUntilCPP20),
252  // Copy constructor.
253  allOf(isCopyConstructor(),
254  // Discard constructors that can be used as a copy
255  // constructor because all the other arguments have
256  // default values.
257  parameterCountIs(1))))
258  .bind(SpecialFunction),
259  this);
260  // Copy-assignment operator.
261  Finder->addMatcher(
262  cxxMethodDecl(unless(hasParent(
263  decl(anyOf(IsUnionLikeClass, functionTemplateDecl())))),
264  isDefinition(), isCopyAssignmentOperator(),
265  // isCopyAssignmentOperator() allows the parameter to be
266  // passed by value, and in this case it cannot be
267  // defaulted.
268  hasParameter(0, hasType(lValueReferenceType())),
269  // isCopyAssignmentOperator() allows non lvalue reference
270  // return types, and in this case it cannot be defaulted.
271  returns(qualType(hasCanonicalType(
272  allOf(lValueReferenceType(pointee(type())),
273  unless(matchers::isReferenceToConst()))))))
274  .bind(SpecialFunction),
275  this);
276 }
277 
278 void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
279  // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
280  const auto *SpecialFunctionDecl =
281  Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
282 
283  if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
284  return;
285 
286  // Discard explicitly deleted/defaulted special member functions and those
287  // that are not user-provided (automatically generated).
288  if (SpecialFunctionDecl->isDeleted() ||
289  SpecialFunctionDecl->isExplicitlyDefaulted() ||
290  SpecialFunctionDecl->isLateTemplateParsed() ||
291  SpecialFunctionDecl->isTemplateInstantiation() ||
292  !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
293  return;
294 
295  const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
296  if (!Body)
297  return;
298 
299  // If there is code inside the body, don't warn.
300  if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
301  return;
302 
303  // If there are comments inside the body, don't do the change.
304  bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
305  bodyEmpty(Result.Context, Body);
306 
307  std::vector<FixItHint> RemoveInitializers;
308  unsigned MemberType;
309  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
310  if (Ctor->getNumParams() == 0) {
311  MemberType = 0;
312  } else {
313  if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
314  return;
315  MemberType = 1;
316  // If there are constructor initializers, they must be removed.
317  for (const auto *Init : Ctor->inits()) {
318  RemoveInitializers.emplace_back(
319  FixItHint::CreateRemoval(Init->getSourceRange()));
320  }
321  }
322  } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
323  MemberType = 2;
324  } else {
325  if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
326  return;
327  MemberType = 3;
328  }
329 
330  // The location of the body is more useful inside a macro as spelling and
331  // expansion locations are reported.
332  SourceLocation Location = SpecialFunctionDecl->getLocation();
333  if (Location.isMacroID())
334  Location = Body->getBeginLoc();
335 
336  auto Diag = diag(
337  Location,
338  "use '= default' to define a trivial %select{default constructor|copy "
339  "constructor|destructor|copy-assignment operator}0");
340  Diag << MemberType;
341 
342  if (ApplyFix) {
343  SourceLocation UnifiedEnd = utils::lexer::getUnifiedEndLoc(
344  *Body, Result.Context->getSourceManager(),
345  Result.Context->getLangOpts());
346  // Skipping comments, check for a semicolon after Body->getSourceRange()
347  Optional<Token> Token = utils::lexer::findNextTokenSkippingComments(
348  UnifiedEnd, Result.Context->getSourceManager(),
349  Result.Context->getLangOpts());
350  StringRef Replacement =
351  Token && Token->is(tok::semi) ? "= default" : "= default;";
352  Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
353  << RemoveInitializers;
354  }
355 }
356 
357 } // namespace modernize
358 } // namespace tidy
359 } // namespace clang
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:85
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
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:125
clang::tidy::abseil::AST_MATCHER
AST_MATCHER(Type, isCharType)
Definition: StringFindStrContainsCheck.cpp:35
Location
Definition: Modularize.cpp:381
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:115
Text
std::string Text
Definition: HTMLGenerator.cpp:80
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:53
clang::tidy::modernize::SpecialFunction
static const char SpecialFunction[]
Definition: UseEqualsDefaultCheck.cpp:22
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:39
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:419
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:216
clang::clangd::match
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:133
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:26
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
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:202
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:67
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:25
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
clang::doc::serialize::isPublic
static bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link)
Definition: Serialize.cpp:256
clang::tidy::utils::lexer::getUnifiedEndLoc
SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM, const LangOptions &LangOpts)
Stmt->getEndLoc does not always behave the same way depending on Token type.
Definition: LexerUtils.cpp:199
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:61
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::modernize::empty
static bool empty(SourceRange Range)
Definition: MacroToEnumCheck.cpp:538
LangOpts
const LangOptions * LangOpts
Definition: ExtractFunction.cpp:375
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:278
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:129
clang::tidy::modernize::UseEqualsDefaultCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UseEqualsDefaultCheck.cpp:224
Field
const FieldDecl * Field
Definition: MemberwiseConstructor.cpp:260
clang::tidy::utils::lexer::findNextTokenSkippingComments
Optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition: LexerUtils.cpp:80
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:52