diff options
author | higher-performance <higher.performance.github@gmail.com> | 2025-01-14 13:31:12 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-01-14 13:31:12 -0500 |
commit | 1594413d5edf6a47d4100cb6a2bc613cfbb92beb (patch) | |
tree | fc9743f336e09c4e40794ca00570b403ee73f718 /clang/lib/Sema/SemaInit.cpp | |
parent | 576b53801fc3d721602ae0d8377af9950f356000 (diff) | |
download | llvm-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.cpp | 58 |
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); |