xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp (revision 2946cd701067404b99c39fb29dc9c74bd7193eb3)
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 
9 #include "UseEqualsDefaultCheck.h"
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 
205 void UseEqualsDefaultCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
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
313