Skip to content
3 changes: 3 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ class FindUninitializedField {
const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
for (const auto *I : RD->fields()) {
if (I->isUnnamedBitField()) {
continue;
}
const FieldRegion *FR = MrMgr.getFieldRegion(I, R);
FieldChain.push_back(I);
T = I->getType();
Expand Down
15 changes: 14 additions & 1 deletion clang/lib/StaticAnalyzer/Core/RegionStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2122,8 +2122,21 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;

// If the containing record was initialized, try to get its constant value.
// UnnamedBitField is always Undefined unless using memory operation such
// as 'memset'.
// For example, for code
// typedef struct {
// int i :2;
// int :30; // unnamed bit-field
// } A;
// A a = {1};
// The bits of the unnamed bit-field in local variable a can be anything.
const FieldDecl *FD = R->getDecl();
if (FD->isUnnamedBitField()) {
return UndefinedVal();
}

// If the containing record was initialized, try to get its constant value.
QualType Ty = FD->getType();
const MemRegion* superR = R->getSuperRegion();
if (const auto *VR = dyn_cast<VarRegion>(superR)) {
Expand Down
27 changes: 26 additions & 1 deletion clang/test/Analysis/call-and-message.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
// RUN: %clang_analyze_cc1 %s -verify \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true \
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
// RUN: -analyzer-output=plist -o %t.plist
// RUN: cat %t.plist | FileCheck %s

// RUN: %clang_analyze_cc1 %s -verify=no-pointee \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false

// RUN: %clang_analyze_cc1 %s -verify=arg-init \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=true

// no-pointee-no-diagnostics

Expand All @@ -22,3 +29,21 @@ void pointee_uninit(void) {
// checker, as described in the CallAndMessage comments!
// CHECK: <key>issue_hash_content_of_line_in_context</key>
// CHECK-SAME: <string>97a74322d64dca40aa57303842c745a1</string>

typedef struct {
int i :2;
int :30; // unnamed bit-field
} B;

extern void consume_B(B);

void bitfield_B_init(void) {
B b1;
b1.i = 1; // b1 is initialized
consume_B(b1);
}

void bitfield_B_uninit(void) {
B b2;
consume_B(b2); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}}
}
16 changes: 16 additions & 0 deletions clang/test/Analysis/call-and-message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,20 @@ void record_uninit() {
// CHECK-SAME: <string>a46bb5c1ee44d4611ffeb13f7f499605</string>
// CHECK: <key>issue_hash_content_of_line_in_context</key>
// CHECK-SAME: <string>e0e0d30ea5a7b2e3a71e1931fa0768a5</string>

struct B{
int i :2;
int :30; // unnamed bit-field
};

void bitfield_B_init(void) {
B b1;
b1.i = 1; // b1 is initialized
consume(b1);
}

void bitfield_B_uninit(void) {
B b2;
consume(b2); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}}
}
} // namespace uninit_arg
Loading