11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/Lex/Lexer.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;
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;
191 static bool bodyEmpty(
const ASTContext *Context,
const CompoundStmt *Body) {
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 UseEqualsDefaultCheck::UseEqualsDefaultCheck(StringRef
Name,
203 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true) != 0) {}
214 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
221 allOf(unless(hasAnyConstructorInitializer(isWritten())),
222 parameterCountIs(0)),
224 allOf(isCopyConstructor(),
228 parameterCountIs(1))))
229 .bind(SpecialFunction),
233 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
237 hasParameter(0, hasType(lValueReferenceType())))
238 .bind(SpecialFunction),
243 std::string SpecialFunctionName;
246 const auto *SpecialFunctionDecl =
249 if (IgnoreMacros && SpecialFunctionDecl->getLocation().isMacroID())
254 if (SpecialFunctionDecl->isDeleted() ||
255 SpecialFunctionDecl->isExplicitlyDefaulted() ||
256 SpecialFunctionDecl->isLateTemplateParsed() ||
257 SpecialFunctionDecl->isTemplateInstantiation() ||
258 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
261 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
266 if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
270 bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
273 std::vector<FixItHint> RemoveInitializers;
275 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
276 if (Ctor->getNumParams() == 0) {
277 SpecialFunctionName =
"default constructor";
281 SpecialFunctionName =
"copy constructor";
283 for (
const auto *Init : Ctor->inits()) {
284 RemoveInitializers.emplace_back(
285 FixItHint::CreateRemoval(Init->getSourceRange()));
288 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
289 SpecialFunctionName =
"destructor";
293 SpecialFunctionName =
"copy-assignment operator";
298 SourceLocation
Location = SpecialFunctionDecl->getLocation();
299 if (Location.isMacroID())
300 Location = Body->getLocStart();
302 auto Diag =
diag(Location,
"use '= default' to define a trivial " +
303 SpecialFunctionName);
306 Diag << FixItHint::CreateReplacement(Body->getSourceRange(),
"= default;")
307 << 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[]
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.
LangOptions getLangOpts() const
Returns the language options from the context.
Base class for all clang-tidy checks.
static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body)
Returns false if the body has any non-whitespace character.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
std::map< std::string, std::string > OptionMap
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 ...
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.