aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaInit.cpp
diff options
context:
space:
mode:
authorhigher-performance <higher.performance.github@gmail.com>2025-01-14 13:31:12 -0500
committerGitHub <noreply@github.com>2025-01-14 13:31:12 -0500
commit1594413d5edf6a47d4100cb6a2bc613cfbb92beb (patch)
treefc9743f336e09c4e40794ca00570b403ee73f718 /clang/lib/Sema/SemaInit.cpp
parent576b53801fc3d721602ae0d8377af9950f356000 (diff)
downloadllvm-1594413d5edf6a47d4100cb6a2bc613cfbb92beb.zip
llvm-1594413d5edf6a47d4100cb6a2bc613cfbb92beb.tar.gz
llvm-1594413d5edf6a47d4100cb6a2bc613cfbb92beb.tar.bz2
Add Clang attribute to ensure that fields are initialized explicitly (#102040)
This is a new Clang-specific attribute to ensure that field initializations are performed explicitly. For example, if we have ``` struct B { [[clang::explicit]] int f1; }; ``` then the diagnostic would trigger if we do `B b{};`: ``` field 'f1' is left uninitialized, but was marked as requiring initialization ``` This prevents callers from accidentally forgetting to initialize fields, particularly when new fields are added to the class.
Diffstat (limited to 'clang/lib/Sema/SemaInit.cpp')
-rw-r--r--clang/lib/Sema/SemaInit.cpp58
1 files changed, 58 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 0dd5f46..b95cbbf 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -264,6 +264,13 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
updateStringLiteralType(Str, DeclT);
}
+void emitUninitializedExplicitInitFields(Sema &S, const RecordDecl *R) {
+ for (const FieldDecl *Field : R->fields()) {
+ if (Field->hasAttr<ExplicitInitAttr>())
+ S.Diag(Field->getLocation(), diag::note_entity_declared_at) << Field;
+ }
+}
+
//===----------------------------------------------------------------------===//
// Semantic checking for initializer lists.
//===----------------------------------------------------------------------===//
@@ -738,6 +745,14 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
ILE->updateInit(SemaRef.Context, Init, Filler);
return;
}
+
+ if (!VerifyOnly && Field->hasAttr<ExplicitInitAttr>()) {
+ SemaRef.Diag(ILE->getExprLoc(), diag::warn_field_requires_explicit_init)
+ << /* Var-in-Record */ 0 << Field;
+ SemaRef.Diag(Field->getLocation(), diag::note_entity_declared_at)
+ << Field;
+ }
+
// C++1y [dcl.init.aggr]p7:
// If there are fewer initializer-clauses in the list than there are
// members in the aggregate, then each member not explicitly initialized
@@ -4558,6 +4573,14 @@ static void TryConstructorInitialization(Sema &S,
CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function);
if (Result != OR_Deleted) {
+ if (!IsListInit && Kind.getKind() == InitializationKind::IK_Default &&
+ DestRecordDecl != nullptr && DestRecordDecl->isAggregate() &&
+ DestRecordDecl->hasUninitializedExplicitInitFields()) {
+ S.Diag(Kind.getLocation(), diag::warn_field_requires_explicit_init)
+ << /* Var-in-Record */ 1 << DestRecordDecl;
+ emitUninitializedExplicitInitFields(S, DestRecordDecl);
+ }
+
// C++11 [dcl.init]p6:
// If a program calls for the default initialization of an object
// of a const-qualified type T, T shall be a class type with a
@@ -5852,6 +5875,12 @@ static void TryOrBuildParenListInitialization(
} else {
// We've processed all of the args, but there are still members that
// have to be initialized.
+ if (!VerifyOnly && FD->hasAttr<ExplicitInitAttr>()) {
+ S.Diag(Kind.getLocation(), diag::warn_field_requires_explicit_init)
+ << /* Var-in-Record */ 0 << FD;
+ S.Diag(FD->getLocation(), diag::note_entity_declared_at) << FD;
+ }
+
if (FD->hasInClassInitializer()) {
if (!VerifyOnly) {
// C++ [dcl.init]p16.6.2.2
@@ -6457,6 +6486,19 @@ void InitializationSequence::InitializeFrom(Sema &S,
}
}
+ if (!S.getLangOpts().CPlusPlus &&
+ Kind.getKind() == InitializationKind::IK_Default) {
+ RecordDecl *Rec = DestType->getAsRecordDecl();
+ if (Rec && Rec->hasUninitializedExplicitInitFields()) {
+ VarDecl *Var = dyn_cast_or_null<VarDecl>(Entity.getDecl());
+ if (Var && !Initializer) {
+ S.Diag(Var->getLocation(), diag::warn_field_requires_explicit_init)
+ << /* Var-in-Record */ 1 << Rec;
+ emitUninitializedExplicitInitFields(S, Rec);
+ }
+ }
+ }
+
// - If the destination type is a reference type, see 8.5.3.
if (DestType->isReferenceType()) {
// C++0x [dcl.init.ref]p1:
@@ -7310,6 +7352,22 @@ PerformConstructorInitialization(Sema &S,
if (S.DiagnoseUseOfDecl(Step.Function.FoundDecl, Loc))
return ExprError();
+ if (Kind.getKind() == InitializationKind::IK_Value &&
+ Constructor->isImplicit()) {
+ auto *RD = Step.Type.getCanonicalType()->getAsCXXRecordDecl();
+ if (RD && RD->isAggregate() && RD->hasUninitializedExplicitInitFields()) {
+ unsigned I = 0;
+ for (const FieldDecl *FD : RD->fields()) {
+ if (I >= ConstructorArgs.size() && FD->hasAttr<ExplicitInitAttr>()) {
+ S.Diag(Loc, diag::warn_field_requires_explicit_init)
+ << /* Var-in-Record */ 0 << FD;
+ S.Diag(FD->getLocation(), diag::note_entity_declared_at) << FD;
+ }
+ ++I;
+ }
+ }
+ }
+
TypeSourceInfo *TSInfo = Entity.getTypeSourceInfo();
if (!TSInfo)
TSInfo = S.Context.getTrivialTypeSourceInfo(Entity.getType(), Loc);