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