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;
105 const CXXConstructorDecl *Ctor) {
106 if (
const auto *BO = dyn_cast<BinaryOperator>(S)) {
107 if (BO->getOpcode() != BO_Assign)
110 const auto *ME = dyn_cast<MemberExpr>(BO->getLHS()->IgnoreParenImpCasts());
114 const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
118 if (!isa<CXXThisExpr>(ME->getBase()))
120 const Expr *Init = BO->getRHS()->IgnoreParenImpCasts();
123 if (
const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
124 if (COCE->getOperator() != OO_Equal)
128 dyn_cast<MemberExpr>(COCE->getArg(0)->IgnoreParenImpCasts());
132 const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
136 if (!isa<CXXThisExpr>(ME->getBase()))
138 const Expr *Init = COCE->getArg(1)->IgnoreParenImpCasts();
157 const MatchFinder::MatchResult &Result) {
158 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
159 const auto *Body = cast<CompoundStmt>(Ctor->getBody());
161 const CXXRecordDecl *Class = Ctor->getParent();
162 bool FirstToCtorInits =
true;
164 llvm::DenseMap<const FieldDecl *, AssignedLevel> AssignedFields{};
166 for (
const CXXCtorInitializer *Init : Ctor->inits())
167 if (FieldDecl *Field = Init->getMember())
170 for (
const Stmt *S : Body->body()) {
171 if (S->getBeginLoc().isMacroID()) {
172 StringRef MacroName = Lexer::getImmediateMacroName(
173 S->getBeginLoc(), *Result.SourceManager, getLangOpts());
174 if (MacroName.contains_insensitive(
"assert"))
183 if (
const auto *CondOp = dyn_cast<ConditionalOperator>(S)) {
189 std::optional<AssignmentPair> AssignmentToMember =
191 if (!AssignmentToMember)
193 const FieldDecl *Field = AssignmentToMember->Field;
195 if (Field->getParent() != Class)
197 const Expr *InitValue = AssignmentToMember->Init;
202 StringRef InsertPrefix =
"";
203 bool HasInitAlready =
false;
204 SourceLocation InsertPos;
205 SourceRange ReplaceRange;
206 bool AddComma =
false;
207 bool AddBrace =
false;
208 bool InvalidFix =
false;
209 unsigned Index = Field->getFieldIndex();
210 const CXXCtorInitializer *LastInListInit =
nullptr;
211 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
212 if (!Init->isWritten() || Init->isInClassMemberInitializer())
214 if (Init->getMember() == Field) {
215 HasInitAlready =
true;
216 if (isa<ImplicitValueInitExpr>(Init->getInit()))
217 InsertPos = Init->getRParenLoc();
219 ReplaceRange = Init->getInit()->getSourceRange();
220 AddBrace = isa<InitListExpr>(Init->getInit());
224 if (Init->isMemberInitializer() &&
225 Index < Init->getMember()->getFieldIndex()) {
226 InsertPos = Init->getSourceLocation();
232 LastInListInit = Init;
234 if (HasInitAlready) {
235 if (InsertPos.isValid())
236 InvalidFix |= InsertPos.isMacroID();
238 InvalidFix |= ReplaceRange.getBegin().isMacroID() ||
239 ReplaceRange.getEnd().isMacroID();
241 if (InsertPos.isInvalid()) {
242 if (LastInListInit) {
244 Lexer::getLocForEndOfToken(LastInListInit->getRParenLoc(), 0,
245 *Result.SourceManager, getLangOpts());
250 InsertPos = Lexer::getLocForEndOfToken(
251 Ctor->getTypeSourceInfo()
253 .getAs<clang::FunctionTypeLoc>()
255 0, *Result.SourceManager, getLangOpts());
261 InsertPrefix = FirstToCtorInits ?
" : " :
", ";
264 InvalidFix |= InsertPos.isMacroID();
267 SourceLocation SemiColonEnd;
268 if (
auto NextToken = Lexer::findNextToken(
269 S->getEndLoc(), *Result.SourceManager, getLangOpts()))
270 SemiColonEnd = NextToken->getEndLoc();
274 auto Diag = diag(S->getBeginLoc(),
"%0 should be initialized in a member"
275 " initializer of the constructor")
279 StringRef NewInit = Lexer::getSourceText(
280 Result.SourceManager->getExpansionRange(InitValue->getSourceRange()),
281 *Result.SourceManager, getLangOpts());
282 if (HasInitAlready) {
283 if (InsertPos.isValid())
284 Diag << FixItHint::CreateInsertion(InsertPos, NewInit);
286 Diag << FixItHint::CreateReplacement(ReplaceRange,
287 (
"{" + NewInit +
"}").str());
289 Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit);
291 SmallString<128> Insertion({InsertPrefix, Field->getName(),
"(", NewInit,
292 AddComma ?
"), " :
")"});
293 Diag << FixItHint::CreateInsertion(InsertPos, Insertion,
295 FirstToCtorInits = areDiagsSelfContained();
297 Diag << FixItHint::CreateRemoval(
298 CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.