xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp (revision 43465bf3fd6cca715187ee7286c881cb210fc3c4)
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(allOf(
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(allOf(
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(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
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,
161                               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 
206 void UseEqualsDefaultCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
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
314