11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers;
26 const CXXConstructorDecl *Ctor) {
30 SourceLocation FirstInit = (*Ctor->init_begin())->getSourceLocation();
31 SourceLocation LastArg =
32 Ctor->getParamDecl(Ctor->getNumParams() - 1)->getLocEnd();
35 StringRef Text = Lexer::getSourceText(
36 CharSourceRange::getCharRange(LastArg, FirstInit),
37 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
39 return SourceLocation();
41 size_t ColonPos = Text.rfind(
':');
42 if (ColonPos == StringRef::npos)
43 return SourceLocation();
45 Text = Text.drop_front(ColonPos + 1);
46 if (std::strspn(Text.data(),
" \t\r\n") != Text.size()) {
48 return SourceLocation();
51 return LastArg.getLocWithOffset(ColonPos);
55 static std::set<const FieldDecl *>
57 std::set<const FieldDecl *>
Result;
58 for (
const auto *Field : Record->fields()) {
60 if (Field->isUnnamedBitfield())
70 std::set<const Type *>
Result;
71 for (
auto Base : Record->bases()) {
73 const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
74 Result.insert(BaseType);
83 const ValueDecl *Var) {
84 return ignoringImpCasts(
85 memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
86 member(fieldDecl(equalsNode(Field)))));
92 const CXXConstructorDecl *Ctor) {
94 if (Ctor->getMinRequiredArguments() != 1)
97 const auto *Record = Ctor->getParent();
98 const auto *Param = Ctor->getParamDecl(0);
105 for (
const auto *Base : BasesToInit) {
109 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
111 withInitializer(cxxConstructExpr(allOf(
112 hasType(equalsNode(Base)),
113 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
116 0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
123 for (
const auto *Field : FieldsToInit) {
127 cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
128 isMemberInitializer(), forField(equalsNode(Field)),
129 withInitializer(anyOf(
130 AccessToFieldInParam,
131 cxxConstructExpr(allOf(
132 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
134 hasArgument(0, AccessToFieldInParam)))))))),
141 return Ctor->getNumCtorInitializers() ==
142 BasesToInit.size() + FieldsToInit.size();
149 const CXXMethodDecl *Operator) {
150 const auto *Record = Operator->getParent();
151 const auto *Param = Operator->getParamDecl(0);
157 const auto *Compound = cast<CompoundStmt>(Operator->getBody());
162 if (Compound->body_empty() ||
163 match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
164 hasOperatorName(
"*"), hasUnaryOperand(cxxThisExpr()))))),
165 *Compound->body_back(), *
Context)
170 for (
const auto *Base : BasesToInit) {
178 if (match(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
181 onImplicitObjectArgument(
182 implicitCastExpr(hasImplicitDestinationType(
183 pointsTo(type(equalsNode(Base)))),
184 hasSourceExpression(cxxThisExpr()))),
186 callee(cxxMethodDecl(isCopyAssignmentOperator())),
191 declRefExpr(to(varDecl(equalsNode(Param)))))))))),
198 for (
const auto *Field : FieldsToInit) {
203 auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
204 member(fieldDecl(equalsNode(Field))));
207 compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
208 binaryOperator(hasOperatorName(
"="), hasLHS(LHS), hasRHS(RHS)),
209 cxxOperatorCallExpr(hasOverloadedOperatorName(
"="),
210 argumentCountIs(2), hasArgument(0, LHS),
211 hasArgument(1, RHS))))))),
218 return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
223 bool Invalid =
false;
224 StringRef Text = Lexer::getSourceText(
225 CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
226 Body->getRBracLoc()),
227 Context->getSourceManager(), Context->getLangOpts(), &Invalid);
228 return !Invalid && std::strspn(Text.data(),
" \t\r\n") == Text.size();
231 void UseDefaultCheck::registerMatchers(MatchFinder *
Finder) {
232 if (getLangOpts().CPlusPlus) {
234 Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(
SpecialFunction),
241 allOf(unless(hasAnyConstructorInitializer(anything())),
242 parameterCountIs(0)),
244 allOf(isCopyConstructor(),
248 parameterCountIs(1))))
253 cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
257 hasParameter(0, hasType(lValueReferenceType())))
263 void UseDefaultCheck::check(
const MatchFinder::MatchResult &
Result) {
264 std::string SpecialFunctionName;
265 SourceLocation StartLoc, EndLoc;
268 const auto *SpecialFunctionDecl =
273 if (SpecialFunctionDecl->isDeleted() ||
274 SpecialFunctionDecl->isExplicitlyDefaulted() ||
275 SpecialFunctionDecl->isLateTemplateParsed() ||
276 !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
279 const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
284 StartLoc = Body->getLBracLoc();
285 EndLoc = Body->getRBracLoc();
288 if (!SpecialFunctionDecl->isCopyAssignmentOperator() &&
292 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
293 if (Ctor->getNumParams() == 0) {
294 SpecialFunctionName =
"default constructor";
298 SpecialFunctionName =
"copy constructor";
301 if (Ctor->getNumCtorInitializers() != 0) {
303 if (!StartLoc.isValid())
306 }
else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
307 SpecialFunctionName =
"destructor";
311 SpecialFunctionName =
"copy-assignment operator";
314 diag(SpecialFunctionDecl->getLocStart(),
315 "use '= default' to define a trivial " + SpecialFunctionName)
316 << FixItHint::CreateReplacement(
317 CharSourceRange::getTokenRange(StartLoc, EndLoc),
"= default;");
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.
static SourceLocation getColonLoc(const ASTContext *Context, const CXXConstructorDecl *Ctor)
Finds the SourceLocation of the colon ':' before the initialization list in the definition of a const...