]> granicus.if.org Git - clang/commitdiff
[analyzer] Fix trivial copy for empty objects.
authorArtem Dergachev <artem.dergachev@gmail.com>
Tue, 27 Feb 2018 21:10:08 +0000 (21:10 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Tue, 27 Feb 2018 21:10:08 +0000 (21:10 +0000)
The SVal for any empty C++ object is an UnknownVal. Because RegionStore does
not have binding extents, binding an empty object to an UnknownVal may
potentially overwrite existing bindings at the same offset.

Therefore, when performing a trivial copy of an empty object, don't try to
take the value of the object and bind it to the copy. Doing nothing is accurate
enough, and it doesn't screw any existing bindings.

Differential Revision: https://reviews.llvm.org/D43714

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@326247 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
test/Analysis/ctor.mm

index 6502af74a33775f1df81f045d4338109b5f18f07..f4aa56f2750dec4c5a857b915a002a20ac12f9af 100644 (file)
@@ -42,19 +42,30 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
                                     const CallEvent &Call) {
   SVal ThisVal;
   bool AlwaysReturnsLValue;
+  const CXXRecordDecl *ThisRD = nullptr;
   if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
     assert(Ctor->getDecl()->isTrivial());
     assert(Ctor->getDecl()->isCopyOrMoveConstructor());
     ThisVal = Ctor->getCXXThisVal();
+    ThisRD = Ctor->getDecl()->getParent();
     AlwaysReturnsLValue = false;
   } else {
     assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial());
     assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() ==
            OO_Equal);
     ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal();
+    ThisRD = cast<CXXMethodDecl>(Call.getDecl())->getParent();
     AlwaysReturnsLValue = true;
   }
 
+  assert(ThisRD);
+  if (ThisRD->isEmpty()) {
+    // Do nothing for empty classes. Otherwise it'd retrieve an UnknownVal
+    // and bind it and RegionStore would think that the actual value
+    // in this region at this offset is unknown.
+    return;
+  }
+
   const LocationContext *LCtx = Pred->getLocationContext();
 
   ExplodedNodeSet Dst;
index 5ef2beea4966f12fa47fb523a26d6457c2f7f5f2..cfae8ed4ed030f0b6cce7490132f6e76b0b7bba6 100644 (file)
@@ -729,3 +729,23 @@ namespace NoCrashOnEmptyBaseOptimization {
     S s;
   }
 }
+
+namespace EmptyBaseAssign {
+struct B1 {};
+struct B2 { int x; };
+struct D: public B1, public B2 {
+const D &operator=(const D &d) {
+  *((B2 *)this) = d;
+  *((B1 *)this) = d;
+  return *this;
+}
+};
+
+void test() {
+  D d1;
+  d1.x = 1;
+  D d2;
+  d2 = d1;
+  clang_analyzer_eval(d2.x == 1); // expected-warning{{TRUE}}
+}
+}