12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
24static llvm::SmallPtrSet<const FieldDecl *, 0>
26 llvm::SmallPtrSet<const FieldDecl *, 0> Result;
27 for (
const auto *Field : Record->fields()) {
29 if (Field->isUnnamedBitField())
38static llvm::SmallPtrSet<const Type *, 0>
40 llvm::SmallPtrSet<const Type *, 0> Result;
41 for (
auto Base : Record->bases()) {
43 const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
44 Result.insert(BaseType);
53 const ValueDecl *Var) {
54 return ignoringImpCasts(
55 memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
56 member(fieldDecl(equalsNode(Field)))));
62 const CXXConstructorDecl *Ctor) {
64 if (Ctor->getMinRequiredArguments() != 1)
67 const auto *Record = Ctor->getParent();
68 const auto *Param = Ctor->getParamDecl(0);
75 for (
const auto *Base : BasesToInit) {
81 forEachConstructorInitializer(cxxCtorInitializer(
83 withInitializer(cxxConstructExpr(
84 hasType(equalsNode(Base)),
86 cxxConstructorDecl(isCopyConstructor())),
88 hasArgument(0, declRefExpr(to(varDecl(
89 equalsNode(Param))))))))))),
96 for (
const auto *Field : FieldsToInit) {
102 forEachConstructorInitializer(cxxCtorInitializer(
103 isMemberInitializer(), forField(equalsNode(Field)),
104 withInitializer(anyOf(
105 AccessToFieldInParam,
106 initListExpr(has(AccessToFieldInParam)),
109 cxxConstructorDecl(isCopyConstructor())),
111 hasArgument(0, AccessToFieldInParam)))))))),
118 return Ctor->getNumCtorInitializers() ==
119 BasesToInit.size() + FieldsToInit.size();
126 const CXXMethodDecl *Operator) {
127 const auto *Record = Operator->getParent();
128 const auto *Param = Operator->getParamDecl(0);
134 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
139 if (Compound->body_empty() ||
142 returnStmt(has(ignoringParenImpCasts(unaryOperator(
143 hasOperatorName(
"*"), hasUnaryOperand(cxxThisExpr())))))),
144 *Compound->body_back(), *Context)
149 for (
const auto *Base : BasesToInit) {
159 compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
163 onImplicitObjectArgument(implicitCastExpr(
164 hasImplicitDestinationType(hasCanonicalType(pointsTo(
165 type(equalsNode(Base->getCanonicalTypeInternal()
167 hasSourceExpression(cxxThisExpr()))),
169 callee(cxxMethodDecl(isCopyAssignmentOperator())),
174 0, declRefExpr(to(varDecl(equalsNode(Param)))))))))),
181 for (
const auto *Field : FieldsToInit) {
186 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
187 member(fieldDecl(equalsNode(Field))));
189 if (match(traverse(TK_AsIs,
190 compoundStmt(has(ignoringParenImpCasts(binaryOperation(
191 hasOperatorName(
"="), hasLHS(LHS), hasRHS(RHS)))))),
198 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
202static bool bodyEmpty(
const ASTContext *Context,
const CompoundStmt *Body) {
203 bool Invalid =
false;
204 const StringRef Text = Lexer::getSourceText(
205 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
206 Body->getRBracLoc()),
207 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
208 return !Invalid && Text.ltrim(
" \t\r\n").empty();
214 IgnoreMacros(Options.get(
"IgnoreMacros", true)) {}
217 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
221AST_MATCHER(CXXMethodDecl, isOutOfLine) {
return Node.isOutOfLine(); }
227 auto IsUnionLikeClass = recordDecl(
229 has(fieldDecl(isImplicit(), hasType(cxxRecordDecl(isUnion()))))));
231 const LangOptions &LangOpts = getLangOpts();
232 auto IsPublicOrOutOfLineUntilCPP20 =
234 ? cxxConstructorDecl()
235 : cxxConstructorDecl(anyOf(isOutOfLine(), isPublic()));
239 cxxDestructorDecl(isDefinition(), unless(ofClass(IsUnionLikeClass)))
245 isDefinition(), unless(ofClass(IsUnionLikeClass)),
246 unless(hasParent(functionTemplateDecl())),
249 allOf(parameterCountIs(0),
250 unless(hasAnyConstructorInitializer(isWritten())),
251 unless(isVariadic()), IsPublicOrOutOfLineUntilCPP20),
253 allOf(isCopyConstructor(),
257 parameterCountIs(1))))
262 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
263 unless(ofClass(IsUnionLikeClass)),
264 unless(hasParent(functionTemplateDecl())),
268 hasParameter(0, hasType(lValueReferenceType())),
271 returns(qualType(hasCanonicalType(
272 allOf(lValueReferenceType(pointee(type())),
273 unless(matchers::isReferenceToConst()))))))
280 const auto *SpecialFunctionDecl =
283 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
288 if (SpecialFunctionDecl->isDeleted() ||
289 SpecialFunctionDecl->isExplicitlyDefaulted() ||
290 SpecialFunctionDecl->isLateTemplateParsed() ||
291 SpecialFunctionDecl->isTemplateInstantiation() ||
292 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
295 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
300 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
305 Body->getSourceRange(), *Result.SourceManager,
306 Result.Context->getLangOpts()))
310 const bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
313 std::vector<FixItHint> RemoveInitializers;
314 unsigned MemberType = 0;
315 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
316 if (Ctor->getNumParams() == 0) {
323 for (
const auto *Init : Ctor->inits()) {
324 RemoveInitializers.emplace_back(
325 FixItHint::CreateRemoval(Init->getSourceRange()));
328 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
338 SourceLocation
Location = SpecialFunctionDecl->getLocation();
344 "use '= default' to define a trivial %select{default constructor|copy "
345 "constructor|destructor|copy-assignment operator}0");
350 *Body, Result.Context->getSourceManager(),
351 Result.Context->getLangOpts());
354 UnifiedEnd, Result.Context->getSourceManager(),
355 Result.Context->getLangOpts());
356 const StringRef Replacement =
357 Token && Token->is(tok::semi) ?
"= default" :
"= default;";
358 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
359 << RemoveInitializers;
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseEqualsDefaultCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static bool empty(SourceRange Range)
static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body)
Returns false if the body has any non-whitespace character.
static 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 ...
static constexpr char SpecialFunction[]
static llvm::SmallPtrSet< const Type *, 0 > getAllDirectBases(const CXXRecordDecl *Record)
Returns the names of the direct bases of Record, both virtual and non-virtual.
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 bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context, const CXXMethodDecl *Operator)
Checks that the given method is an overloading of the assignment operator, has copy signature,...
static llvm::SmallPtrSet< const FieldDecl *, 0 > getAllNamedFields(const CXXRecordDecl *Record)
Finds all the named non-static fields of Record.
SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM, const LangOptions &LangOpts)
Stmt->getEndLoc does not always behave the same way depending on Token type.
bool rangeContainsExpansionsOrDirectives(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Re-lex the provide Range and return false if either a macro spans multiple tokens,...
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
llvm::StringMap< ClangTidyValue > OptionMap