68 const FieldDecl *Field,
const Expr *Init,
const CXXConstructorDecl *Ctor,
69 llvm::DenseMap<const FieldDecl *, AssignedLevel> &AssignedFields) {
70 auto It = AssignedFields.try_emplace(Field, AssignedLevel::None).first;
76 if (Field->getType().getCanonicalType()->isReferenceType()) {
78 It->second = AssignedLevel::HasSideEffect;
83 memberExpr(hasObjectExpression(cxxThisExpr()),
84 member(fieldDecl(indexNotLessThan(Field->getFieldIndex()))));
85 auto DeclMatcher = declRefExpr(
86 to(valueDecl(unless(parmVarDecl()), hasDeclContext(equalsNode(Ctor)))));
87 const bool HasDependence = !match(expr(anyOf(MemberMatcher, DeclMatcher,
88 hasDescendant(MemberMatcher),
89 hasDescendant(DeclMatcher))),
90 *Init, Field->getASTContext())
93 It->second = AssignedLevel::HasDependence;
109 const CXXConstructorDecl *Ctor) {
110 if (
const auto *BO = dyn_cast<BinaryOperator>(S)) {
111 if (BO->getOpcode() != BO_Assign)
114 const auto *ME = dyn_cast<MemberExpr>(BO->getLHS()->IgnoreParenImpCasts());
118 const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
122 if (!isa<CXXThisExpr>(ME->getBase()))
124 const Expr *Init = BO->getRHS()->IgnoreParenImpCasts();
125 return AssignmentPair{Field, Init};
127 if (
const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
128 if (COCE->getOperator() != OO_Equal)
132 dyn_cast<MemberExpr>(COCE->getArg(0)->IgnoreParenImpCasts());
136 const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
140 if (!isa<CXXThisExpr>(ME->getBase()))
142 const Expr *Init = COCE->getArg(1)->IgnoreParenImpCasts();
143 return AssignmentPair{Field, Init};
161 const MatchFinder::MatchResult &Result) {
162 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
163 const auto *Body = cast<CompoundStmt>(Ctor->getBody());
165 const CXXRecordDecl *Class = Ctor->getParent();
166 bool FirstToCtorInits =
true;
168 llvm::DenseMap<const FieldDecl *, AssignedLevel> AssignedFields{};
170 for (
const CXXCtorInitializer *Init : Ctor->inits())
171 if (
const FieldDecl *Field = Init->getMember())
174 for (
const Stmt *S : Body->body()) {
175 if (S->getBeginLoc().isMacroID()) {
176 const StringRef MacroName = Lexer::getImmediateMacroName(
177 S->getBeginLoc(), *Result.SourceManager, getLangOpts());
178 if (MacroName.contains_insensitive(
"assert"))
187 if (
const auto *CondOp = dyn_cast<ConditionalOperator>(S)) {
193 std::optional<AssignmentPair> AssignmentToMember =
195 if (!AssignmentToMember)
197 const FieldDecl *Field = AssignmentToMember->Field;
199 if (Field->getParent() != Class)
201 const Expr *InitValue = AssignmentToMember->Init;
206 StringRef InsertPrefix =
"";
207 bool HasInitAlready =
false;
208 SourceLocation InsertPos;
209 SourceRange ReplaceRange;
210 bool AddComma =
false;
211 bool AddBrace =
false;
212 bool InvalidFix =
false;
213 const unsigned Index = Field->getFieldIndex();
214 const CXXCtorInitializer *LastInListInit =
nullptr;
215 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
216 if (!Init->isWritten() || Init->isInClassMemberInitializer())
218 if (Init->getMember() == Field) {
219 HasInitAlready =
true;
220 if (isa<ImplicitValueInitExpr>(Init->getInit()))
221 InsertPos = Init->getRParenLoc();
223 ReplaceRange = Init->getInit()->getSourceRange();
224 AddBrace = isa<InitListExpr>(Init->getInit());
228 if (Init->isMemberInitializer() &&
229 Index < Init->getMember()->getFieldIndex()) {
230 InsertPos = Init->getSourceLocation();
236 LastInListInit = Init;
238 if (HasInitAlready) {
239 if (InsertPos.isValid())
240 InvalidFix |= InsertPos.isMacroID();
242 InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
243 ReplaceRange.getEnd().isMacroID();
245 if (InsertPos.isInvalid()) {
246 if (LastInListInit) {
248 Lexer::getLocForEndOfToken(LastInListInit->getRParenLoc(), 0,
249 *Result.SourceManager, getLangOpts());
254 InsertPos = Lexer::getLocForEndOfToken(
255 Ctor->getTypeSourceInfo()
257 .getAs<clang::FunctionTypeLoc>()
259 0, *Result.SourceManager, getLangOpts());
265 InsertPrefix = FirstToCtorInits ?
" : " :
", ";
268 InvalidFix |= InsertPos.isMacroID();
271 SourceLocation SemiColonEnd;
273 S->getEndLoc(), *Result.SourceManager, getLangOpts()))
274 SemiColonEnd = NextToken->getEndLoc();
278 auto Diag = diag(S->getBeginLoc(),
"%0 should be initialized in a member"
279 " initializer of the constructor")
283 const StringRef NewInit = Lexer::getSourceText(
284 Result.SourceManager->getExpansionRange(InitValue->getSourceRange()),
285 *Result.SourceManager, getLangOpts());
286 if (HasInitAlready) {
287 if (InsertPos.isValid())
288 Diag << FixItHint::CreateInsertion(InsertPos, NewInit);
290 Diag << FixItHint::CreateReplacement(ReplaceRange,
291 (
"{" + NewInit +
"}").str());
293 Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit);
295 const SmallString<128> Insertion({InsertPrefix, Field->getName(),
"(",
296 NewInit, AddComma ?
"), " :
")"});
297 Diag << FixItHint::CreateInsertion(InsertPos, Insertion,
299 FirstToCtorInits = areDiagsSelfContained();
301 Diag << FixItHint::CreateRemoval(
302 CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.