10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 #include "../utils/LexerUtils.h"
24 static std::set<const FieldDecl *>
26 std::set<const FieldDecl *> Result;
27 for (
const auto *Field :
Record->fields()) {
29 if (Field->isUnnamedBitfield())
39 std::set<const Type *> Result;
42 const auto *BaseType =
Base.getTypeSourceInfo()->getType().getTypePtr();
43 Result.insert(BaseType);
52 const ValueDecl *Var) {
53 return ignoringImpCasts(
54 memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
55 member(fieldDecl(equalsNode(Field)))));
61 const CXXConstructorDecl *Ctor) {
63 if (Ctor->getMinRequiredArguments() != 1)
66 const auto *
Record = Ctor->getParent();
67 const auto *Param = Ctor->getParamDecl(0);
74 for (
const auto *
Base : BasesToInit) {
78 traverse(ast_type_traits::TK_AsIs,
80 forEachConstructorInitializer(cxxCtorInitializer(
82 withInitializer(cxxConstructExpr(
83 hasType(equalsNode(
Base)),
85 cxxConstructorDecl(isCopyConstructor())),
87 hasArgument(0, declRefExpr(to(varDecl(
88 equalsNode(Param))))))))))),
95 for (
const auto *Field : FieldsToInit) {
99 ast_type_traits::TK_AsIs,
101 forEachConstructorInitializer(cxxCtorInitializer(
102 isMemberInitializer(), forField(equalsNode(Field)),
103 withInitializer(anyOf(
104 AccessToFieldInParam,
105 initListExpr(has(AccessToFieldInParam)),
108 cxxConstructorDecl(isCopyConstructor())),
110 hasArgument(0, AccessToFieldInParam)))))))),
117 return Ctor->getNumCtorInitializers() ==
118 BasesToInit.size() + FieldsToInit.size();
125 const CXXMethodDecl *Operator) {
126 const auto *
Record = Operator->getParent();
127 const auto *Param = Operator->getParamDecl(0);
133 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
138 if (Compound->body_empty() ||
140 ast_type_traits::TK_AsIs,
141 returnStmt(has(ignoringParenImpCasts(unaryOperator(
142 hasOperatorName(
"*"), hasUnaryOperand(cxxThisExpr())))))),
143 *Compound->body_back(), *Context)
148 for (
const auto *
Base : BasesToInit) {
156 if (
match(traverse(ast_type_traits::TK_AsIs,
157 compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
161 onImplicitObjectArgument(implicitCastExpr(
162 hasImplicitDestinationType(
163 pointsTo(type(equalsNode(
Base)))),
164 hasSourceExpression(cxxThisExpr()))),
166 callee(cxxMethodDecl(isCopyAssignmentOperator())),
170 hasArgument(0, declRefExpr(to(varDecl(
171 equalsNode(Param)))))))))),
178 for (
const auto *Field : FieldsToInit) {
183 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
184 member(fieldDecl(equalsNode(Field))));
187 traverse(ast_type_traits::TK_AsIs,
188 compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
189 binaryOperator(hasOperatorName(
"="), hasLHS(LHS),
192 hasOverloadedOperatorName(
"="), argumentCountIs(2),
193 hasArgument(0, LHS), hasArgument(1, RHS)))))))),
200 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
204 static bool bodyEmpty(
const ASTContext *Context,
const CompoundStmt *Body) {
205 bool Invalid =
false;
206 StringRef
Text = Lexer::getSourceText(
207 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
208 Body->getRBracLoc()),
209 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
210 return !Invalid && std::strspn(
Text.data(),
" \t\r\n") ==
Text.size();
213 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef
Name,
216 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true)) {}
224 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(
SpecialFunction),
231 allOf(unless(hasAnyConstructorInitializer(isWritten())),
232 parameterCountIs(0)),
234 allOf(isCopyConstructor(),
238 parameterCountIs(1))))
243 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
247 hasParameter(0, hasType(lValueReferenceType())))
253 std::string SpecialFunctionName;
256 const auto *SpecialFunctionDecl =
259 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
264 if (SpecialFunctionDecl->isDeleted() ||
265 SpecialFunctionDecl->isExplicitlyDefaulted() ||
266 SpecialFunctionDecl->isLateTemplateParsed() ||
267 SpecialFunctionDecl->isTemplateInstantiation() ||
268 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
271 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
276 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
280 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
283 std::vector<FixItHint> RemoveInitializers;
285 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
286 if (Ctor->getNumParams() == 0) {
287 SpecialFunctionName =
"default constructor";
291 SpecialFunctionName =
"copy constructor";
293 for (
const auto *Init : Ctor->inits()) {
294 RemoveInitializers.emplace_back(
295 FixItHint::CreateRemoval(Init->getSourceRange()));
298 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
299 SpecialFunctionName =
"destructor";
303 SpecialFunctionName =
"copy-assignment operator";
308 SourceLocation
Location = SpecialFunctionDecl->getLocation();
312 auto Diag =
diag(
Location,
"use '= default' to define a trivial " +
313 SpecialFunctionName);
318 Body->getSourceRange().getEnd().getLocWithOffset(1),
319 Result.Context->getSourceManager(), Result.Context->getLangOpts());
320 StringRef Replacement =
321 Token && Token->is(tok::semi) ?
"= default" :
"= default;";
322 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
323 << RemoveInitializers;