From: Pavel Labath Date: Thu, 29 Aug 2013 16:06:04 +0000 (+0000) Subject: [analyzer] Fix handling of "empty" structs with base classes X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c114f704a882f6923d6107f22aab89ba3d0a6b5;p=clang [analyzer] Fix handling of "empty" structs with base classes Summary: RegionStoreManager had an optimization which replaces references to empty structs with UnknownVal. Unfortunately, this check didn't take into account possible field members in base classes. To address this, I changed this test to "is empty and has no base classes". I don't consider it worth the trouble to go through base classes and check if all of them are empty. Reviewers: jordan_rose CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D1547 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189590 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 2a3319bc04..8bc16bd419 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1832,10 +1832,18 @@ NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B, return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R); } +static bool isRecordEmpty(const RecordDecl *RD) { + if (!RD->field_empty()) + return false; + if (const CXXRecordDecl *CRD = dyn_cast(RD)) + return CRD->getNumBases() == 0; + return true; +} + SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B, const TypedValueRegion *R) { const RecordDecl *RD = R->getValueType()->castAs()->getDecl(); - if (RD->field_empty()) + if (isRecordEmpty(RD)) return UnknownVal(); return createLazyBinding(B, R); diff --git a/test/Analysis/array-struct-region.cpp b/test/Analysis/array-struct-region.cpp index 12ae5d3eba..a776d7da50 100644 --- a/test/Analysis/array-struct-region.cpp +++ b/test/Analysis/array-struct-region.cpp @@ -173,4 +173,27 @@ void testImmediateUseOp() { clang_analyzer_eval(getConstrainedFieldRefOp(getS()) == 42); // expected-warning{{TRUE}} } +namespace EmptyClass { + struct Base { + int& x; + + Base(int& x) : x(x) {} + }; + + struct Derived : public Base { + Derived(int& x) : Base(x) {} + + void operator=(int a) { x = a; } + }; + + Derived ref(int& a) { return Derived(a); } + + // There used to be a warning here, because analyzer treated Derived as empty. + int test() { + int a; + ref(a) = 42; + return a; // no warning + } +} + #endif