]> granicus.if.org Git - clang/commitdiff
[analyzer] Fix handling of "empty" structs with base classes
authorPavel Labath <labath@google.com>
Thu, 29 Aug 2013 16:06:04 +0000 (16:06 +0000)
committerPavel Labath <labath@google.com>
Thu, 29 Aug 2013 16:06:04 +0000 (16:06 +0000)
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

lib/StaticAnalyzer/Core/RegionStore.cpp
test/Analysis/array-struct-region.cpp

index 2a3319bc0424e79de625fb85885c049deb1597a0..8bc16bd41910755a714cd13d5c0af9bd628a6264 100644 (file)
@@ -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<CXXRecordDecl>(RD))
+    return CRD->getNumBases() == 0;
+  return true;
+}
+
 SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B,
                                              const TypedValueRegion *R) {
   const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl();
-  if (RD->field_empty())
+  if (isRecordEmpty(RD))
     return UnknownVal();
 
   return createLazyBinding(B, R);
index 12ae5d3eba6ec26168463ddcc9a95bb1349cd1f7..a776d7da50bb8589d84c6bd42b8bc18213831a79 100644 (file)
@@ -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