10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
19 return isa<IfStmt, SwitchStmt, ForStmt, WhileStmt, DoStmt, ReturnStmt,
20 GotoStmt, CXXTryStmt, CXXThrowExpr>(S);
24 const auto *Call = dyn_cast<CallExpr>(S);
28 const FunctionDecl *Func = Call->getDirectCallee();
32 return Func->isNoReturn();
36 return isa<StringLiteral, CharacterLiteral, IntegerLiteral, FloatingLiteral,
37 CXXBoolLiteralExpr, CXXNullPtrLiteralExpr>(
E);
41 if (
const auto *UnOp = dyn_cast<UnaryOperator>(
E))
50 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Value))
51 return isa<EnumConstantDecl>(DRE->getDecl());
58 return Node.getFieldIndex() >= Index;
68 const CXXConstructorDecl *Context) {
71 memberExpr(hasObjectExpression(cxxThisExpr()),
72 member(fieldDecl(indexNotLessThan(
Field->getFieldIndex()))));
74 auto DeclMatcher = declRefExpr(
75 to(varDecl(unless(parmVarDecl()), hasDeclContext(equalsNode(Context)))));
77 return match(expr(anyOf(MemberMatcher, DeclMatcher,
78 hasDescendant(MemberMatcher),
79 hasDescendant(DeclMatcher))),
80 *Init,
Field->getASTContext())
84static std::pair<const FieldDecl *, const Expr *>
86 const CXXConstructorDecl *Ctor) {
87 if (
const auto *BO = dyn_cast<BinaryOperator>(S)) {
88 if (BO->getOpcode() != BO_Assign)
89 return std::make_pair(
nullptr,
nullptr);
91 const auto *ME = dyn_cast<MemberExpr>(BO->getLHS()->IgnoreParenImpCasts());
93 return std::make_pair(
nullptr,
nullptr);
95 const auto *
Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
97 return std::make_pair(
nullptr,
nullptr);
99 if (!isa<CXXThisExpr>(ME->getBase()))
100 return std::make_pair(
nullptr,
nullptr);
101 const Expr *Init = BO->getRHS()->IgnoreParenImpCasts();
103 return std::make_pair(
Field, Init);
104 }
else if (
const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
105 if (COCE->getOperator() != OO_Equal)
106 return std::make_pair(
nullptr,
nullptr);
109 dyn_cast<MemberExpr>(COCE->getArg(0)->IgnoreParenImpCasts());
111 return std::make_pair(
nullptr,
nullptr);
113 const auto *
Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
115 return std::make_pair(
nullptr,
nullptr);
117 if (!isa<CXXThisExpr>(ME->getBase()))
118 return std::make_pair(
nullptr,
nullptr);
119 const Expr *Init = COCE->getArg(1)->IgnoreParenImpCasts();
121 return std::make_pair(
Field, Init);
124 return std::make_pair(
nullptr,
nullptr);
130 IsUseDefaultMemberInitEnabled(
131 Context->isCheckEnabled(
"modernize-use-default-member-init")),
133 Options.get(
"UseAssignment",
135 Context->getOptions().CheckOptions, Context)
136 .get(
"UseAssignment", false))) {}
144 Finder->addMatcher(cxxConstructorDecl(hasBody(compoundStmt()),
145 unless(isInstantiated()),
146 unless(isDelegatingConstructor()))
152 const MatchFinder::MatchResult &Result) {
153 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
154 const auto *Body = cast<CompoundStmt>(Ctor->getBody());
156 const CXXRecordDecl *Class = Ctor->getParent();
157 bool FirstToCtorInits =
true;
159 for (
const Stmt *S : Body->body()) {
160 if (S->getBeginLoc().isMacroID()) {
161 StringRef
MacroName = Lexer::getImmediateMacroName(
162 S->getBeginLoc(), *Result.SourceManager,
getLangOpts());
163 if (
MacroName.contains_insensitive(
"assert"))
172 if (
const auto *CondOp = dyn_cast<ConditionalOperator>(S)) {
178 const FieldDecl *
Field =
nullptr;
179 const Expr *InitValue =
nullptr;
183 Ctor->isDefaultConstructor() &&
185 !
Field->hasInClassInitializer() &&
186 (!isa<RecordDecl>(Class->getDeclContext()) ||
187 !cast<RecordDecl>(Class->getDeclContext())->isUnion()) &&
190 bool InvalidFix =
false;
191 SourceLocation FieldEnd =
192 Lexer::getLocForEndOfToken(
Field->getSourceRange().getEnd(), 0,
194 InvalidFix |= FieldEnd.isInvalid() || FieldEnd.isMacroID();
195 SourceLocation SemiColonEnd;
196 if (
auto NextToken = Lexer::findNextToken(
197 S->getEndLoc(), *Result.SourceManager,
getLangOpts()))
198 SemiColonEnd = NextToken->getEndLoc();
202 diag(S->getBeginLoc(),
"%0 should be initialized in an in-class"
203 " default member initializer")
207 CharSourceRange StmtRange =
208 CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd);
210 SmallString<128> Insertion(
212 Lexer::getSourceText(
213 CharSourceRange(InitValue->getSourceRange(),
true),
217 Diag << FixItHint::CreateInsertion(FieldEnd, Insertion)
218 << FixItHint::CreateRemoval(StmtRange);
221 StringRef InsertPrefix =
"";
222 bool HasInitAlready =
false;
223 SourceLocation InsertPos;
224 SourceRange ReplaceRange;
225 bool AddComma =
false;
226 bool InvalidFix =
false;
227 unsigned Index =
Field->getFieldIndex();
228 const CXXCtorInitializer *LastInListInit =
nullptr;
229 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
230 if (!Init->isWritten() || Init->isInClassMemberInitializer())
232 if (Init->getMember() ==
Field) {
233 HasInitAlready =
true;
234 if (isa<ImplicitValueInitExpr>(Init->getInit()))
235 InsertPos = Init->getRParenLoc();
237 ReplaceRange = Init->getInit()->getSourceRange();
241 if (Init->isMemberInitializer() &&
242 Index < Init->getMember()->getFieldIndex()) {
243 InsertPos = Init->getSourceLocation();
249 LastInListInit = Init;
251 if (HasInitAlready) {
252 if (InsertPos.isValid())
253 InvalidFix |= InsertPos.isMacroID();
255 InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
256 ReplaceRange.getEnd().isMacroID();
258 if (InsertPos.isInvalid()) {
259 if (LastInListInit) {
260 InsertPos = Lexer::getLocForEndOfToken(
261 LastInListInit->getRParenLoc(), 0, *Result.SourceManager,
267 InsertPos = Lexer::getLocForEndOfToken(
268 Ctor->getTypeSourceInfo()
270 .getAs<clang::FunctionTypeLoc>()
278 InsertPrefix = FirstToCtorInits ?
" : " :
", ";
281 InvalidFix |= InsertPos.isMacroID();
284 SourceLocation SemiColonEnd;
285 if (
auto NextToken = Lexer::findNextToken(
286 S->getEndLoc(), *Result.SourceManager,
getLangOpts()))
287 SemiColonEnd = NextToken->getEndLoc();
292 diag(S->getBeginLoc(),
"%0 should be initialized in a member"
293 " initializer of the constructor")
297 StringRef NewInit = Lexer::getSourceText(
298 CharSourceRange(InitValue->getSourceRange(),
true),
300 if (HasInitAlready) {
301 if (InsertPos.isValid())
302 Diag << FixItHint::CreateInsertion(InsertPos, NewInit);
304 Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit);
306 SmallString<128> Insertion({InsertPrefix,
Field->getName(),
"(",
307 NewInit, AddComma ?
"), " :
")"});
308 Diag << FixItHint::CreateInsertion(InsertPos, Insertion,
312 Diag << FixItHint::CreateRemoval(
313 CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd));
::clang::DynTypedNode Node
Provides access to the ClangTidyCheck options via check-local names.
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.
bool areDiagsSelfContained() const
Returns true when the check is run in a use case when only 1 fix will be applied at a time.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
PreferMemberInitializerCheck(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.
const bool IsUseDefaultMemberInitEnabled
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_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
static bool isUnaryExprOfLiteral(const Expr *E)
static bool shouldBeDefaultMemberInitializer(const Expr *Value)
static std::pair< const FieldDecl *, const Expr * > isAssignmentToMemberOf(const CXXRecordDecl *Rec, const Stmt *S, const CXXConstructorDecl *Ctor)
static bool isNoReturnCallStatement(const Stmt *S)
static bool isLiteral(const Expr *E)
static bool isControlStatement(const Stmt *S)
static bool isSafeAssignment(const FieldDecl *Field, const Expr *Init, const CXXConstructorDecl *Context)
llvm::StringMap< ClangTidyValue > OptionMap