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