Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -147,6 +147,8 @@ /// Compute the offset within the top level memory object. RegionOffset getAsOffset() const; + uint64_t getExtent() const; + /// Get a string representation of a region for debug use. std::string getString() const; Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1602,6 +1602,31 @@ return *cachedOffset; } +uint64_t MemRegion::getExtent() const { + const MemRegion *R = this; + + // Prior to this patch we assumed the + // extent of all region is infinite, so in case + // we fail to find the extent, we default to + // "infinity". + uint64_t Extent = UINT64_MAX; + + // FIXME: Handle other cases as well as bitsets. + // For 'unsigned b : 1' we still return 32, however + // this might be incorrect. + if (const auto *TVR = dyn_cast(R)) { + const auto Ty = TVR->getDesugaredValueType(getContext()); + if (!Ty->isIncompleteType()) + Extent = getContext().getTypeSize(Ty); + } else if (const auto *SR = dyn_cast(R)) { + const auto Ty = SR->getSymbol()->getType().getDesugaredType(getContext()); + if (!Ty->isIncompleteType()) + Extent = getContext().getTypeSize(Ty); + } + + return Extent; +} + //===----------------------------------------------------------------------===// // BlockDataRegion //===----------------------------------------------------------------------===// Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -48,24 +48,29 @@ llvm::PointerIntPair P; uint64_t Data; + uint64_t Extent; /// Create a key for a binding to region \p r, which has a symbolic offset /// from region \p Base. - explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k) - : P(r, k | Symbolic), Data(reinterpret_cast(Base)) { + explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k, + uint64_t extent) + : P(r, k | Symbolic), Data(reinterpret_cast(Base)), + Extent(extent) { assert(r && Base && "Must have known regions."); assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); } /// Create a key for a binding at \p offset from base region \p r. - explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) - : P(r, k), Data(offset) { + explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k, + uint64_t extent) + : P(r, k), Data(offset), Extent(extent) { assert(r && "Must have known regions."); assert(getOffset() == offset && "Failed to store offset"); assert((r == r->getBaseRegion() || isa(r)) && "Not a base"); } + public: bool isDirect() const { return P.getInt() & Direct; } @@ -77,6 +82,8 @@ return Data; } + uint64_t getExtent() const { return Extent; } + const SubRegion *getConcreteOffsetRegion() const { assert(hasSymbolicOffset()); return reinterpret_cast(static_cast(Data)); @@ -91,6 +98,7 @@ void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddPointer(P.getOpaqueValue()); ID.AddInteger(Data); + ID.AddInteger(Extent); } static BindingKey Make(const MemRegion *R, Kind k); @@ -100,14 +108,39 @@ return true; if (P.getOpaqueValue() > X.P.getOpaqueValue()) return false; - return Data < X.Data; + + if (Data < X.Data) + return true; + if (Data > X.Data) + return false; + + return Extent < X.Extent; } - bool operator==(const BindingKey &X) const { + bool isInsideOrEqual(const BindingKey &X) const { + if (hasSymbolicOffset() || X.hasSymbolicOffset()) + return false; + + if (P.getOpaqueValue() != X.P.getOpaqueValue()) + return false; + + return getOffset() >= X.getOffset() && + getOffset() <= + X.getOffset() + + X.getExtent() && // The offset can be negative and in that + // case we might hit an overflow. + getOffset() + getExtent() <= X.getOffset() + X.getExtent(); + } + + bool isSameWithoutExtent(const BindingKey &X) const { return P.getOpaqueValue() == X.P.getOpaqueValue() && Data == X.Data; } + bool operator==(const BindingKey &X) const { + return isSameWithoutExtent(X) && Extent == X.Extent; + } + LLVM_DUMP_METHOD void dump() const; }; } // end anonymous namespace @@ -115,9 +148,10 @@ BindingKey BindingKey::Make(const MemRegion *R, Kind k) { const RegionOffset &RO = R->getAsOffset(); if (RO.hasSymbolicOffset()) - return BindingKey(cast(R), cast(RO.getRegion()), k); + return BindingKey(cast(R), cast(RO.getRegion()), k, + R->getExtent()); - return BindingKey(RO.getRegion(), RO.getOffset(), k); + return BindingKey(RO.getRegion(), RO.getOffset(), k, R->getExtent()); } namespace llvm { @@ -130,6 +164,9 @@ else Out << "null"; + Out << ", \"extent\": "; + Out << K.getExtent(); + return Out; } @@ -143,7 +180,60 @@ // Actual Store type. //===----------------------------------------------------------------------===// -typedef llvm::ImmutableMap ClusterBindings; +class ClusterBindings : public llvm::ImmutableMap { +public: + ClusterBindings(const llvm::ImmutableMap &m) + : llvm::ImmutableMap(m) {} + ClusterBindings(llvm::ImmutableMap &&m) + : llvm::ImmutableMap(m) {} + +public: + const SVal *lookup(const BindingKey &K) const { + const auto lookupInBase = [this](const BindingKey &K) { + return llvm::ImmutableMap::lookup(K); + }; + + // Try to lookup the value by comparing everything. + const auto *ResVal = lookupInBase(K); + + // We might have failed to lookup the value because + // the extent of the current region is smaller than the extent + // of the stored region. In this case we try to find the + // smallest sub-region that contains the region we are searching + // for. + if (!ResVal) { + uint64_t lowerBound = 0; + uint64_t upperBound = UINT64_MAX; + for (auto &&B : *this) { + if (K.isInsideOrEqual(B.first) && B.first.getOffset() >= lowerBound && + B.first.getOffset() + B.first.getExtent() <= upperBound) { + // FIXME: Handle other overlapping regions as well. + if (isa(B.second)) { + upperBound = B.first.getOffset() + B.first.getExtent(); + lowerBound = B.first.getOffset(); + + ResVal = &B.second; + } + } + } + } + + // If we still can't get a result, we fall back to our original + // lookup strategy, when only the region and the offset is compared + // and the first result is returned. + if (!ResVal) { + for (auto &&B : *this) { + if (K.isSameWithoutExtent(B.first)) { + ResVal = &B.second; + break; + } + } + } + + return ResVal; + } +}; + typedef llvm::ImmutableMapRef ClusterBindingsRef; typedef std::pair BindingPair; @@ -275,7 +365,9 @@ const ClusterBindings *ExistingCluster = lookup(Base); ClusterBindings Cluster = - (ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap()); + (ExistingCluster + ? *ExistingCluster + : static_cast(CBFactory->getEmptyMap())); ClusterBindings NewCluster = CBFactory->add(Cluster, K, V); return add(Base, NewCluster); @@ -292,7 +384,11 @@ const ClusterBindings *Cluster = lookup(K.getBaseRegion()); if (!Cluster) return nullptr; - return Cluster->lookup(K); + + // Try looking up the value. + const auto *ResVal = Cluster->lookup(K); + + return ResVal; } const SVal *RegionBindingsRef::lookup(const MemRegion *R, @@ -2055,23 +2151,6 @@ Result = getBindingForField(LazyBinding, cast(LazyBindingRegion)); - // FIXME: This is a hack to deal with RegionStore's inability to distinguish a - // default value for /part/ of an aggregate from a default value for the - // /entire/ aggregate. The most common case of this is when struct Outer - // has as its first member a struct Inner, which is copied in from a stack - // variable. In this case, even if the Outer's default value is symbolic, 0, - // or unknown, it gets overridden by the Inner's default value of undefined. - // - // This is a general problem -- if the Inner is zero-initialized, the Outer - // will now look zero-initialized. The proper way to solve this is with a - // new version of RegionStore that tracks the extent of a binding as well - // as the offset. - // - // This hack only takes care of the undefined case because that can very - // quickly result in a warning. - if (Result.isUndef()) - Result = UnknownVal(); - return Result; } Index: clang/test/Analysis/array-init-loop.cpp =================================================================== --- clang/test/Analysis/array-init-loop.cpp +++ clang/test/Analysis/array-init-loop.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -std=c++17 -verify %s void clang_analyzer_eval(bool); @@ -19,7 +19,11 @@ auto [a, b, c, d, e] = arr; - int x = e; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(d); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(e); // expected-warning{{UNDEFINED}} } void lambda_init() { @@ -44,21 +48,20 @@ void lambda_uninit() { int arr[5]; - // FIXME: These should be Undefined, but we fail to read Undefined from a lazyCompoundVal - int l = [arr] { return arr[0]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + int l = [arr] { return arr[0]; }(); + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[1]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[2]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[3]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[4]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} } struct S { @@ -86,14 +89,11 @@ S copy = orig; - // FIXME: These should be Undefined, but we fail to read Undefined from a lazyCompoundVal. - // If the struct is not considered a small struct, instead of a copy, we store a lazy compound value. - // As the struct has an array data member, it is not considered small. - clang_analyzer_eval(copy.arr[0]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[1]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[2]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[3]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(copy.arr[0]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[1]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[2]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[3]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[4]); // expected-warning{{UNDEFINED}} } void move_ctor_init() { @@ -118,12 +118,11 @@ S moved = (S &&) orig; - // FIXME: These should be Undefined, but we fail to read Undefined from a lazyCompoundVal. - clang_analyzer_eval(moved.arr[0]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[1]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[2]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[3]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(moved.arr[0]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[1]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[2]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[3]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[4]); // expected-warning{{UNDEFINED}} } // The struct has a user defined copy and move ctor, which allow us to @@ -168,7 +167,8 @@ void array_uninit_non_pod() { S3 arr[1]; - auto [a] = arr; // expected-warning@159{{ in implicit constructor is garbage or undefined }} + auto [a] = arr; + clang_analyzer_eval(a.i); // expected-warning{{UNDEFINED}} } void lambda_init_non_pod() { @@ -191,7 +191,8 @@ void lambda_uninit_non_pod() { S3_duplicate arr[4]; - int l = [arr] { return arr[3].i; }(); // expected-warning@164{{ in implicit constructor is garbage or undefined }} + int l = [arr] { return arr[3].i; }(); + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} } // If this struct is being copy/move constructed by the implicit ctors, ArrayInitLoopExpr Index: clang/test/Analysis/copy-elision.cpp =================================================================== --- clang/test/Analysis/copy-elision.cpp +++ clang/test/Analysis/copy-elision.cpp @@ -57,13 +57,8 @@ C() : t(T(4)) { S s = {1, 2, 3}; t.s = s; - // FIXME: Should be TRUE regardless of copy elision. clang_analyzer_eval(t.w == 4); -#ifdef ELIDE - // expected-warning@-2{{TRUE}} -#else - // expected-warning@-4{{UNKNOWN}} -#endif + // expected-warning@-1{{TRUE}} } }; Index: clang/test/Analysis/cxx-uninitialized-object.cpp =================================================================== --- clang/test/Analysis/cxx-uninitialized-object.cpp +++ clang/test/Analysis/cxx-uninitialized-object.cpp @@ -871,12 +871,13 @@ Callable functor; int dontGetFilteredByNonPedanticMode = 0; - MultipleLambdaCapturesTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}} + MultipleLambdaCapturesTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized field}} }; void fMultipleLambdaCapturesTest2() { int b1, b2 = 3, b3; auto equals = [b1, &b2, &b3](int a) { return a == b1 == b2 == b3; }; // expected-note{{uninitialized pointee 'this->functor./*captured variable*/b3'}} + // expected-note@-1{{uninitialized field 'this->functor./*captured variable*/b1'}} MultipleLambdaCapturesTest2(equals, int()); } Index: clang/test/Analysis/dump_egraph.cpp =================================================================== --- clang/test/Analysis/dump_egraph.cpp +++ clang/test/Analysis/dump_egraph.cpp @@ -21,7 +21,7 @@ // CHECK: \"location_context\": \"#0 Call\", \"calling\": \"T::T\", \"location\": \{ \"line\": 15, \"column\": 5, \"file\": \"{{.*}}dump_egraph.cpp\" \}, \"items\": [\l        \{ \"init_id\": {{[0-9]+}}, \"kind\": \"construct into member variable\", \"argument_index\": null, \"pretty\": \"s\", \"value\": \"&t.s\" -// CHECK: \"cluster\": \"t\", \"pointer\": \"{{0x[0-9a-f]+}}\", \"items\": [\l        \{ \"kind\": \"Default\", \"offset\": 0, \"value\": \"conj_$2\{int, LC5, no stmt, #1\}\" +// CHECK: \"cluster\": \"t\", \"pointer\": \"{{0x[0-9a-f]+}}\", \"items\": [\l        \{ \"kind\": \"Default\", \"offset\": 0, \"extent\": {{[0-9]+}}, \"value\": \"conj_$2\{int, LC5, no stmt, #1\}\" // CHECK: \"dynamic_types\": [\l      \{ \"region\": \"HeapSymRegion\{conj_$1\{S *, LC1, S{{[0-9]+}}, #1\}\}\", \"dyn_type\": \"S\", \"sub_classable\": false \}\l Index: clang/test/Analysis/expr-inspection.c =================================================================== --- clang/test/Analysis/expr-inspection.c +++ clang/test/Analysis/expr-inspection.c @@ -31,7 +31,7 @@ // CHECK: "program_state": { // CHECK-NEXT: "store": { "pointer": "{{0x[0-9a-f]+}}", "items": [ // CHECK-NEXT: { "cluster": "y", "pointer": "{{0x[0-9a-f]+}}", "items": [ -// CHECK-NEXT: { "kind": "Direct", "offset": {{[0-9]+}}, "value": "2 S32b" } +// CHECK-NEXT: { "kind": "Direct", "offset": {{[0-9]+}}, "extent": {{[0-9]+}}, "value": "2 S32b" } // CHECK-NEXT: ]} // CHECK-NEXT: ]}, // CHECK-NEXT: "environment": { "pointer": "{{0x[0-9a-f]+}}", "items": [ Index: clang/test/Analysis/store-extent-rework.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/store-extent-rework.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection,alpha.unix.cstring -verify %s + +void clang_analyzer_eval(bool); + +typedef __typeof__(sizeof(int)) size_t; +void *memset(void *dest, int ch, size_t count); + +struct Point +{ + int x; + int y; + int z; +}; + +struct Circle +{ + Point origin; + int size; +}; + +Point makePoint(int x, int y) +{ + Point result; + result.x = x; + result.y = y; + result.z = 3; + return result; +} + +void multipleDefaultOnStack() { + Circle testObj; + memset(&testObj, 0, sizeof(testObj)); + + clang_analyzer_eval(testObj.size == 0); //expected-warning{{TRUE}} + testObj.origin = makePoint(1, 2); + clang_analyzer_eval(testObj.size == 0); //expected-warning{{TRUE}} + + clang_analyzer_eval(testObj.origin.x == 1); //expected-warning{{TRUE}} + clang_analyzer_eval(testObj.origin.y == 2); //expected-warning{{TRUE}} + clang_analyzer_eval(testObj.origin.z == 3); //expected-warning{{TRUE}} +} Index: clang/test/Analysis/uninit-structured-binding-array.cpp =================================================================== --- clang/test/Analysis/uninit-structured-binding-array.cpp +++ clang/test/Analysis/uninit-structured-binding-array.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -std=c++17 -verify %s void clang_analyzer_eval(bool); @@ -7,7 +7,7 @@ auto [a, b] = arr; arr[0] = 0; - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} } void array_value_b(void) { @@ -16,8 +16,6 @@ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} - - int x = a; // no-warning } void array_value_c(void) { @@ -28,9 +26,7 @@ auto [a, b, c] = arr; clang_analyzer_eval(b == arr[1]); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} } void array_value_d(void) { @@ -41,9 +37,7 @@ auto [a, b, c] = arr; clang_analyzer_eval(b == arr[1]); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = c; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} } void array_value_e(void) { @@ -56,9 +50,6 @@ clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // no-warning } void array_value_f(void) { @@ -70,15 +61,15 @@ auto [i, j] = uninit; clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void array_lref_a(void) { int arr[2]; auto &[a, b] = arr; - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_lref_b(void) { @@ -87,8 +78,6 @@ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} - - int x = a; // no-warning } void array_lref_c(void) { @@ -98,9 +87,7 @@ arr[0] = 1; clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} - - int x = a; // no-warning - int y = b; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_lref_d(void) { @@ -111,9 +98,7 @@ auto &[a, b, c] = arr; clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} } void array_lref_e(void) { @@ -124,9 +109,7 @@ auto &[a, b, c] = arr; clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = c; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} } void array_lref_f(void) { @@ -139,9 +122,6 @@ clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // no-warning } void array_lref_g(void) { @@ -153,15 +133,14 @@ auto &[i, j] = uninit; clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void array_rref_a(void) { int arr[2]; auto &&[a, b] = arr; - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_rref_b(void) { @@ -181,9 +160,7 @@ arr[0] = 1; clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} - - int x = a; // no-warning - int y = b; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_rref_d(void) { @@ -194,22 +171,8 @@ auto &&[a, b, c] = arr; clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = a; // expected-warning{{Assigned value is garbage or undefined}} -} - -void array_rref_e(void) { - int arr[3]; - - arr[1] = 1; - - auto &&[a, b, c] = arr; - - clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = c; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} } void array_rref_f(void) { @@ -222,9 +185,6 @@ clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // no-warning } void array_rref_g(void) { @@ -236,9 +196,7 @@ auto &&[i, j] = uninit; clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void array_change_a(void) { @@ -276,7 +234,11 @@ auto [a, b, c, d, e] = arr; - int x = e; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(d); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(e); // expected-warning{{UNDEFINED}} } void array_big_a(void) { @@ -284,13 +246,12 @@ auto [a, b, c, d, e, f] = arr; - // FIXME: These will be Undefined when we handle reading Undefined values from lazyCompoundVal. - clang_analyzer_eval(a == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c == 3); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(d == 4); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(e == 5); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(f == 6); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(d); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(e); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(f); // expected-warning{{UNDEFINED}} } struct S { Index: clang/test/Analysis/uninit-structured-binding-struct.cpp =================================================================== --- clang/test/Analysis/uninit-structured-binding-struct.cpp +++ clang/test/Analysis/uninit-structured-binding-struct.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -std=c++17 -verify %s void clang_analyzer_eval(bool); @@ -12,7 +12,8 @@ auto [i, j] = tst; - int x = i; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(i); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void b(void) { @@ -22,7 +23,7 @@ auto [i, j] = tst; clang_analyzer_eval(i == 1); // expected-warning{{TRUE}} - int y = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void c(void) { @@ -30,7 +31,8 @@ auto &[i, j] = tst; - int x = i; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(i); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void d(void) { @@ -43,7 +45,7 @@ i = 2; clang_analyzer_eval(tst.a == 2); // expected-warning{{TRUE}} - int y = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void e(void) { @@ -63,7 +65,8 @@ auto &&[i, j] = tst; - int x = i; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(i); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void g(void) { @@ -73,7 +76,7 @@ auto &&[i, j] = tst; clang_analyzer_eval(i == 1); // expected-warning{{TRUE}} - int y = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } struct s2 { @@ -93,10 +96,8 @@ auto [i, j] = tst; - // FIXME: These should be undefined, but we have to fix - // reading undefined from lazy compound values first. - clang_analyzer_eval(i.a); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i.b); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i.a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(i.b); // expected-warning{{UNDEFINED}} clang_analyzer_eval(j.a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(j.b == 2); // expected-warning{{TRUE}} Index: clang/test/Analysis/uninit-vals.m =================================================================== --- clang/test/Analysis/uninit-vals.m +++ clang/test/Analysis/uninit-vals.m @@ -164,15 +164,11 @@ // expected-note@-1{{TRUE}} testObj->origin = makePoint(0.0, 0.0); - if (testObj->size > 0) { ; } // expected-note{{Assuming field 'size' is <= 0}} + if (testObj->size > 0) { ; } // expected-note{{Field 'size' is <= 0}} // expected-note@-1{{Taking false branch}} - // FIXME: Assigning to 'testObj->origin' kills the default binding for the - // whole region, meaning that we've forgotten that testObj->size should also - // default to 0. Tracked by . - // This should be TRUE. - clang_analyzer_eval(testObj->size == 0); // expected-warning{{UNKNOWN}} - // expected-note@-1{{UNKNOWN}} + clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} free(testObj); } @@ -219,21 +215,17 @@ // expected-note@-1{{TRUE}} testObj->origin = makeIntPoint(1, 2); - if (testObj->size > 0) { ; } // expected-note{{Assuming field 'size' is <= 0}} + if (testObj->size > 0) { ; } // expected-note{{Field 'size' is <= 0}} // expected-note@-1{{Taking false branch}} - // expected-note@-2{{Assuming field 'size' is <= 0}} + // expected-note@-2{{Field 'size' is <= 0}} // expected-note@-3{{Taking false branch}} - // expected-note@-4{{Assuming field 'size' is <= 0}} + // expected-note@-4{{Field 'size' is <= 0}} // expected-note@-5{{Taking false branch}} - // expected-note@-6{{Assuming field 'size' is <= 0}} + // expected-note@-6{{Field 'size' is <= 0}} // expected-note@-7{{Taking false branch}} - // FIXME: Assigning to 'testObj->origin' kills the default binding for the - // whole region, meaning that we've forgotten that testObj->size should also - // default to 0. Tracked by . - // This should be TRUE. - clang_analyzer_eval(testObj->size == 0); // expected-warning{{UNKNOWN}} - // expected-note@-1{{UNKNOWN}} + clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} clang_analyzer_eval(testObj->origin.x == 1); // expected-warning{{TRUE}} // expected-note@-1{{TRUE}} clang_analyzer_eval(testObj->origin.y == 2); // expected-warning{{TRUE}} @@ -307,7 +299,8 @@ IntPoint b = a; extern void useInt(int); useInt(b.x); // no-warning - useInt(b.y); // no-warning + useInt(b.y); // expected-warning{{uninitialized}} + // expected-note@-1{{uninitialized}} } void testSmallStructInLargerStruct(void) { Index: clang/utils/analyzer/exploded-graph-rewriter.py =================================================================== --- clang/utils/analyzer/exploded-graph-rewriter.py +++ clang/utils/analyzer/exploded-graph-rewriter.py @@ -180,9 +180,10 @@ def __init__(self, json_sk): self.kind = json_sk['kind'] self.offset = json_sk['offset'] + self.extent = json_sk['extent'] if 'extent' in json_sk else None def _key(self): - return (self.kind, self.offset) + return (self.kind, self.offset, self.extent) def __eq__(self, other): return self._key() == other._key()