10#include "../utils/LexerUtils.h"
11#include "../utils/Matchers.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
24static 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) {
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) {
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() ||
141 returnStmt(has(ignoringParenImpCasts(unaryOperator(
142 hasOperatorName(
"*"), hasUnaryOperand(cxxThisExpr())))))),
143 *Compound->body_back(), *Context)
148 for (
const auto *Base : BasesToInit) {
158 compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(
162 onImplicitObjectArgument(implicitCastExpr(
163 hasImplicitDestinationType(hasCanonicalType(pointsTo(
164 type(equalsNode(Base->getCanonicalTypeInternal()
166 hasSourceExpression(cxxThisExpr()))),
168 callee(cxxMethodDecl(isCopyAssignmentOperator())),
173 0, declRefExpr(to(varDecl(equalsNode(Param)))))))))),
180 for (
const auto *
Field : FieldsToInit) {
185 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
186 member(fieldDecl(equalsNode(
Field))));
188 if (match(traverse(TK_AsIs,
189 compoundStmt(has(ignoringParenImpCasts(binaryOperation(
190 hasOperatorName(
"="), hasLHS(LHS), hasRHS(RHS)))))),
197 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
201static bool bodyEmpty(
const ASTContext *Context,
const CompoundStmt *Body) {
202 bool Invalid =
false;
203 StringRef
Text = Lexer::getSourceText(
204 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
205 Body->getRBracLoc()),
206 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
207 return !Invalid && std::strspn(
Text.data(),
" \t\r\n") ==
Text.size();
213 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true)) {}
226 auto IsUnionLikeClass = recordDecl(
228 has(fieldDecl(isImplicit(), hasType(cxxRecordDecl(isUnion()))))));
231 auto IsPublicOrOutOfLineUntilCPP20 =
233 ? cxxConstructorDecl()
234 : cxxConstructorDecl(anyOf(isOutOfLine(), isPublic()));
238 cxxDestructorDecl(isDefinition(), unless(ofClass(IsUnionLikeClass)))
244 isDefinition(), unless(ofClass(IsUnionLikeClass)),
245 unless(hasParent(functionTemplateDecl())),
248 allOf(parameterCountIs(0),
249 unless(hasAnyConstructorInitializer(isWritten())),
250 unless(isVariadic()), IsPublicOrOutOfLineUntilCPP20),
252 allOf(isCopyConstructor(),
256 parameterCountIs(1))))
261 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
262 unless(ofClass(IsUnionLikeClass)),
263 unless(hasParent(functionTemplateDecl())),
267 hasParameter(0, hasType(lValueReferenceType())),
270 returns(qualType(hasCanonicalType(
271 allOf(lValueReferenceType(pointee(type())),
272 unless(matchers::isReferenceToConst()))))))
279 const auto *SpecialFunctionDecl =
282 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
287 if (SpecialFunctionDecl->isDeleted() ||
288 SpecialFunctionDecl->isExplicitlyDefaulted() ||
289 SpecialFunctionDecl->isLateTemplateParsed() ||
290 SpecialFunctionDecl->isTemplateInstantiation() ||
291 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
294 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
299 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
304 Body->getSourceRange(), *Result.SourceManager,
305 Result.Context->getLangOpts()))
309 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
312 std::vector<FixItHint> RemoveInitializers;
313 unsigned MemberType = 0;
314 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
315 if (Ctor->getNumParams() == 0) {
322 for (
const auto *Init : Ctor->inits()) {
323 RemoveInitializers.emplace_back(
324 FixItHint::CreateRemoval(Init->getSourceRange()));
327 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
337 SourceLocation
Location = SpecialFunctionDecl->getLocation();
343 "use '= default' to define a trivial %select{default constructor|copy "
344 "constructor|destructor|copy-assignment operator}0");
349 *Body, Result.Context->getSourceManager(),
350 Result.Context->getLangOpts());
353 UnifiedEnd, Result.Context->getSourceManager(),
354 Result.Context->getLangOpts());
355 StringRef Replacement =
356 Token && Token->is(tok::semi) ?
"= default" :
"= default;";
357 Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement)
358 << RemoveInitializers;
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
static bool empty(SourceRange Range)
static std::set< const Type * > getAllDirectBases(const CXXRecordDecl *Record)
Returns the names of the direct bases of Record, both virtual and non-virtual.
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 ...
static std::set< const FieldDecl * > getAllNamedFields(const CXXRecordDecl *Record)
Finds all the named non-static fields of Record.
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 const char SpecialFunction[]
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