11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers;
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;
40 for (
auto Base : Record->bases()) {
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 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
80 withInitializer(cxxConstructExpr(allOf(
81 hasType(equalsNode(Base)),
82 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
85 0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
92 for (
const auto *Field : FieldsToInit) {
96 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
97 isMemberInitializer(), forField(equalsNode(Field)),
98 withInitializer(anyOf(
100 cxxConstructExpr(allOf(
101 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
103 hasArgument(0, AccessToFieldInParam)))))))),
110 return Ctor->getNumCtorInitializers() ==
111 BasesToInit.size() + FieldsToInit.size();
118 const CXXMethodDecl *Operator) {
119 const auto *Record = Operator->getParent();
120 const auto *Param = Operator->getParamDecl(0);
126 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
131 if (Compound->body_empty() ||
132 match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
133 hasOperatorName(
"*"), hasUnaryOperand(cxxThisExpr()))))),
134 *Compound->body_back(), *
Context)
139 for (
const auto *Base : BasesToInit) {
147 if (match(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
150 onImplicitObjectArgument(
151 implicitCastExpr(hasImplicitDestinationType(
152 pointsTo(type(equalsNode(Base)))),
153 hasSourceExpression(cxxThisExpr()))),
155 callee(cxxMethodDecl(isCopyAssignmentOperator())),
160 declRefExpr(to(varDecl(equalsNode(Param)))))))))),
167 for (
const auto *Field : FieldsToInit) {
172 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
173 member(fieldDecl(equalsNode(Field))));
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))))))),
187 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
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();
200 void UseEqualsDefaultCheck::registerMatchers(MatchFinder *
Finder) {
201 if (getLangOpts().CPlusPlus) {
203 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(
SpecialFunction),
210 allOf(unless(hasAnyConstructorInitializer(isWritten())),
211 parameterCountIs(0)),
213 allOf(isCopyConstructor(),
217 parameterCountIs(1))))
222 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
226 hasParameter(0, hasType(lValueReferenceType())))
232 void UseEqualsDefaultCheck::check(
const MatchFinder::MatchResult &
Result) {
233 std::string SpecialFunctionName;
236 const auto *SpecialFunctionDecl =
241 if (SpecialFunctionDecl->isDeleted() ||
242 SpecialFunctionDecl->isExplicitlyDefaulted() ||
243 SpecialFunctionDecl->isLateTemplateParsed() ||
244 SpecialFunctionDecl->isTemplateInstantiation() ||
245 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
248 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
253 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
257 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
260 std::vector<FixItHint> RemoveInitializers;
262 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
263 if (Ctor->getNumParams() == 0) {
264 SpecialFunctionName =
"default constructor";
268 SpecialFunctionName =
"copy constructor";
270 for (
const auto *Init : Ctor->inits()) {
271 RemoveInitializers.emplace_back(
272 FixItHint::CreateRemoval(Init->getSourceRange()));
275 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
276 SpecialFunctionName =
"destructor";
280 SpecialFunctionName =
"copy-assignment operator";
285 SourceLocation
Location = SpecialFunctionDecl->getLocation();
286 if (Location.isMacroID())
287 Location = Body->getLocStart();
289 auto Diag = diag(Location,
"use '= default' to define a trivial " +
290 SpecialFunctionName);
293 Diag << FixItHint::CreateReplacement(Body->getSourceRange(),
"= default;")
294 << RemoveInitializers;
static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context, const CXXMethodDecl *Operator)
Checks that the given method is an overloading of the assignment operator, has copy signature...
static const char SpecialFunction[]
std::unique_ptr< ast_matchers::MatchFinder > Finder
static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body)
Returns false if the body has any non-whitespace character.
internal::Matcher< Expr > accessToFieldInVar(const FieldDecl *Field, const ValueDecl *Var)
Returns a matcher that matches member expressions where the base is the variable declared as Var and ...
ClangTidyContext & Context
static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context, const CXXConstructorDecl *Ctor)
Check that the given constructor has copy signature and that it copy-initializes all its bases and me...
static std::set< const Type * > getAllDirectBases(const CXXRecordDecl *Record)
Returns the names of the direct bases of Record, both virtual and non-virtual.
static std::set< const FieldDecl * > getAllNamedFields(const CXXRecordDecl *Record)
Finds all the named non-static fields of Record.