68 const FieldDecl *Field,
const Expr *Init,
const CXXConstructorDecl *Ctor,
69 llvm::DenseMap<const FieldDecl *, AssignedLevel> &AssignedFields) {
70 const 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);
192 std::optional<AssignmentPair> AssignmentToMember =
194 if (!AssignmentToMember)
196 const FieldDecl *Field = AssignmentToMember->Field;
198 if (Field->getParent() != Class)
200 const Expr *InitValue = AssignmentToMember->Init;
205 StringRef InsertPrefix =
"";
206 bool HasInitAlready =
false;
207 SourceLocation InsertPos;
208 SourceRange ReplaceRange;
209 bool AddComma =
false;
210 bool AddBrace =
false;
211 bool InvalidFix =
false;
212 const unsigned Index = Field->getFieldIndex();
213 const CXXCtorInitializer *LastInListInit =
nullptr;
214 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
215 if (!Init->isWritten() || Init->isInClassMemberInitializer())
217 if (Init->getMember() == Field) {
218 HasInitAlready =
true;
219 if (isa<ImplicitValueInitExpr>(Init->getInit())) {
220 InsertPos = Init->getRParenLoc();
222 ReplaceRange = Init->getInit()->getSourceRange();
223 AddBrace = isa<InitListExpr>(Init->getInit());
227 if (Init->isMemberInitializer() &&
228 Index < Init->getMember()->getFieldIndex()) {
229 InsertPos = Init->getSourceLocation();
235 LastInListInit = Init;
237 if (HasInitAlready) {
238 if (InsertPos.isValid())
239 InvalidFix |= InsertPos.isMacroID();
241 InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
242 ReplaceRange.getEnd().isMacroID();
244 if (InsertPos.isInvalid()) {
245 if (LastInListInit) {
247 Lexer::getLocForEndOfToken(LastInListInit->getRParenLoc(), 0,
248 *Result.SourceManager, getLangOpts());
253 InsertPos = Lexer::getLocForEndOfToken(Ctor->getTypeSourceInfo()
255 .getAs<FunctionTypeLoc>()
257 0, *Result.SourceManager,
264 InsertPrefix = FirstToCtorInits ?
" : " :
", ";
267 InvalidFix |= InsertPos.isMacroID();
270 SourceLocation SemiColonEnd;
272 S->getEndLoc(), *Result.SourceManager, getLangOpts()))
273 SemiColonEnd = NextToken->getEndLoc();
278 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.