Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -42,19 +42,30 @@ const CallEvent &Call) { SVal ThisVal; bool AlwaysReturnsLValue; + const CXXRecordDecl *ThisRD = nullptr; if (const CXXConstructorCall *Ctor = dyn_cast(&Call)) { assert(Ctor->getDecl()->isTrivial()); assert(Ctor->getDecl()->isCopyOrMoveConstructor()); ThisVal = Ctor->getCXXThisVal(); + ThisRD = Ctor->getDecl()->getParent(); AlwaysReturnsLValue = false; } else { assert(cast(Call.getDecl())->isTrivial()); assert(cast(Call.getDecl())->getOverloadedOperator() == OO_Equal); ThisVal = cast(Call).getCXXThisVal(); + ThisRD = cast(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: test/Analysis/ctor.mm =================================================================== --- test/Analysis/ctor.mm +++ test/Analysis/ctor.mm @@ -729,3 +729,23 @@ 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}} +} +}