10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14#include "llvm/ADT/DenseMap.h"
21 return isa<IfStmt, SwitchStmt, ForStmt, WhileStmt, DoStmt, ReturnStmt,
22 GotoStmt, CXXTryStmt, CXXThrowExpr>(S);
26 const auto *Call = dyn_cast<CallExpr>(S);
30 const FunctionDecl *Func = Call->getDirectCallee();
34 return Func->isNoReturn();
40 return Node.getFieldIndex() >= Index;
43enum class AssignedLevel {
59 return Level == AssignedLevel::None || Level == AssignedLevel::Default;
68 const FieldDecl *Field,
const Expr *Init,
const CXXConstructorDecl *Ctor,
69 llvm::DenseMap<const FieldDecl *, AssignedLevel> &AssignedFields) {
70 auto It = AssignedFields.find(
Field);
71 if (It == AssignedFields.end())
72 It = AssignedFields.insert({
Field, AssignedLevel::None}).first;
78 if (
Field->getType().getCanonicalType()->isReferenceType()) {
80 It->second = AssignedLevel::HasSideEffect;
85 memberExpr(hasObjectExpression(cxxThisExpr()),
86 member(fieldDecl(indexNotLessThan(
Field->getFieldIndex()))));
87 auto DeclMatcher = declRefExpr(
88 to(varDecl(unless(parmVarDecl()), hasDeclContext(equalsNode(Ctor)))));
89 const bool HasDependence = !match(expr(anyOf(MemberMatcher, DeclMatcher,
90 hasDescendant(MemberMatcher),
91 hasDescendant(DeclMatcher))),
92 *Init,
Field->getASTContext())
95 It->second = AssignedLevel::HasDependence;
105static std::optional<AssignmentPair>
107 const CXXConstructorDecl *Ctor) {
108 if (
const auto *BO = dyn_cast<BinaryOperator>(S)) {
109 if (BO->getOpcode() != BO_Assign)
112 const auto *ME = dyn_cast<MemberExpr>(BO->getLHS()->IgnoreParenImpCasts());
116 const auto *
Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
120 if (!isa<CXXThisExpr>(ME->getBase()))
122 const Expr *Init = BO->getRHS()->IgnoreParenImpCasts();
125 if (
const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
126 if (COCE->getOperator() != OO_Equal)
130 dyn_cast<MemberExpr>(COCE->getArg(0)->IgnoreParenImpCasts());
134 const auto *
Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
138 if (!isa<CXXThisExpr>(ME->getBase()))
140 const Expr *Init = COCE->getArg(1)->IgnoreParenImpCasts();
151 Finder->addMatcher(cxxConstructorDecl(hasBody(compoundStmt()),
152 unless(isInstantiated()),
153 unless(isDelegatingConstructor()))
159 const MatchFinder::MatchResult &Result) {
160 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
161 const auto *Body = cast<CompoundStmt>(Ctor->getBody());
163 const CXXRecordDecl *Class = Ctor->getParent();
164 bool FirstToCtorInits =
true;
166 llvm::DenseMap<const FieldDecl *, AssignedLevel> AssignedFields{};
168 for (
const CXXCtorInitializer *Init : Ctor->inits())
169 if (FieldDecl *
Field = Init->getMember())
172 for (
const Stmt *S : Body->body()) {
173 if (S->getBeginLoc().isMacroID()) {
174 StringRef
MacroName = Lexer::getImmediateMacroName(
175 S->getBeginLoc(), *Result.SourceManager,
getLangOpts());
176 if (
MacroName.contains_insensitive(
"assert"))
185 if (
const auto *CondOp = dyn_cast<ConditionalOperator>(S)) {
191 std::optional<AssignmentPair> AssignmentToMember =
193 if (!AssignmentToMember)
195 const FieldDecl *
Field = AssignmentToMember->Field;
196 const Expr *InitValue = AssignmentToMember->Init;
201 StringRef InsertPrefix =
"";
202 bool HasInitAlready =
false;
203 SourceLocation InsertPos;
204 SourceRange ReplaceRange;
205 bool AddComma =
false;
206 bool AddBrace =
false;
207 bool InvalidFix =
false;
208 unsigned Index =
Field->getFieldIndex();
209 const CXXCtorInitializer *LastInListInit =
nullptr;
210 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
211 if (!Init->isWritten() || Init->isInClassMemberInitializer())
213 if (Init->getMember() ==
Field) {
214 HasInitAlready =
true;
215 if (isa<ImplicitValueInitExpr>(Init->getInit()))
216 InsertPos = Init->getRParenLoc();
218 ReplaceRange = Init->getInit()->getSourceRange();
219 AddBrace = isa<InitListExpr>(Init->getInit());
223 if (Init->isMemberInitializer() &&
224 Index < Init->getMember()->getFieldIndex()) {
225 InsertPos = Init->getSourceLocation();
231 LastInListInit = Init;
233 if (HasInitAlready) {
234 if (InsertPos.isValid())
235 InvalidFix |= InsertPos.isMacroID();
237 InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
238 ReplaceRange.getEnd().isMacroID();
240 if (InsertPos.isInvalid()) {
241 if (LastInListInit) {
243 Lexer::getLocForEndOfToken(LastInListInit->getRParenLoc(), 0,
249 InsertPos = Lexer::getLocForEndOfToken(
250 Ctor->getTypeSourceInfo()
252 .getAs<clang::FunctionTypeLoc>()
260 InsertPrefix = FirstToCtorInits ?
" : " :
", ";
263 InvalidFix |= InsertPos.isMacroID();
266 SourceLocation SemiColonEnd;
267 if (
auto NextToken = Lexer::findNextToken(
268 S->getEndLoc(), *Result.SourceManager,
getLangOpts()))
269 SemiColonEnd = NextToken->getEndLoc();
273 auto Diag =
diag(S->getBeginLoc(),
"%0 should be initialized in a member"
274 " initializer of the constructor")
278 StringRef NewInit = Lexer::getSourceText(
279 Result.SourceManager->getExpansionRange(InitValue->getSourceRange()),
281 if (HasInitAlready) {
282 if (InsertPos.isValid())
283 Diag << FixItHint::CreateInsertion(InsertPos, NewInit);
285 Diag << FixItHint::CreateReplacement(ReplaceRange,
286 (
"{" + NewInit +
"}").str());
288 Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit);
290 SmallString<128> Insertion({InsertPrefix,
Field->getName(),
"(", NewInit,
291 AddComma ?
"), " :
")"});
292 Diag << FixItHint::CreateInsertion(InsertPos, Insertion,
296 Diag << FixItHint::CreateRemoval(
297 CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd));
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
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.
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
static void updateAssignmentLevel(const FieldDecl *Field, const Expr *Init, const CXXConstructorDecl *Ctor, llvm::DenseMap< const FieldDecl *, AssignedLevel > &AssignedFields)
static bool canAdvanceAssignment(AssignedLevel Level)
static bool isNoReturnCallStatement(const Stmt *S)
static bool isControlStatement(const Stmt *S)
static std::optional< AssignmentPair > isAssignmentToMemberOf(const CXXRecordDecl *Rec, const Stmt *S, const CXXConstructorDecl *Ctor)