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 (
const 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 const 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 const 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(
252 unless(withInitializer(cxxConstructExpr(
254 unless(requiresZeroInitialization()))))))),
255 unless(isVariadic()), IsPublicOrOutOfLineUntilCPP20),
257 allOf(isCopyConstructor(),
261 parameterCountIs(1))))
266 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
267 unless(ofClass(IsUnionLikeClass)),
268 unless(hasParent(functionTemplateDecl())),
272 hasParameter(0, hasType(lValueReferenceType())),
275 returns(qualType(hasCanonicalType(
276 allOf(lValueReferenceType(pointee(type())),
277 unless(matchers::isReferenceToConst()))))))
284 const auto *SpecialFunctionDecl =
287 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
292 if (SpecialFunctionDecl->isDeleted() ||
293 SpecialFunctionDecl->isExplicitlyDefaulted() ||
294 SpecialFunctionDecl->isLateTemplateParsed() ||
295 SpecialFunctionDecl->isTemplateInstantiation() ||
296 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
299 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
304 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
309 Body->getSourceRange(), *Result.SourceManager,
310 Result.Context->getLangOpts()))
314 const bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
317 std::vector<FixItHint> RemoveInitializers;
318 unsigned MemberType = 0;
319 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
320 if (Ctor->getNumParams() == 0) {
328 for (
const auto *Init : Ctor->inits()) {
329 if (!Init->isWritten())
331 RemoveInitializers.emplace_back(
332 FixItHint::CreateRemoval(Init->getSourceRange()));
334 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
344 SourceLocation
Location = SpecialFunctionDecl->getLocation();
348 const auto Diag = diag(
350 "use '= default' to define a trivial %select{default constructor|copy "
351 "constructor|destructor|copy-assignment operator}0");
356 *Body, Result.Context->getSourceManager(),
357 Result.Context->getLangOpts());
360 UnifiedEnd, Result.Context->getSourceManager(),
361 Result.Context->getLangOpts());
362 const StringRef Replacement =
363 Token && Token->is(tok::semi) ?
"= default" :
"= default;";
364 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
365 << 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