diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index d1894d4447a2e..7ee7c1394a670 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -61,30 +61,30 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, 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(); + const Expr *CallExpr = Call.getOriginExpr(); ExplodedNodeSet Dst; Bldr.takeNodes(Pred); - SVal V = Call.getArgSVal(0); - - // If the value being copied is not unknown, load from its location to get - // an aggregate rvalue. - if (std::optional L = V.getAs()) - V = Pred->getState()->getSVal(*L); - else - assert(V.isUnknownOrUndef()); + assert(ThisRD); + if (!ThisRD->isEmpty()) { + // Load the source value only for non-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. + SVal V = Call.getArgSVal(0); - const Expr *CallExpr = Call.getOriginExpr(); - evalBind(Dst, CallExpr, Pred, ThisVal, V, true); + // If the value being copied is not unknown, load from its location to get + // an aggregate rvalue. + if (std::optional L = V.getAs()) + V = Pred->getState()->getSVal(*L); + else + assert(V.isUnknownOrUndef()); + evalBind(Dst, CallExpr, Pred, ThisVal, V, true); + } else { + Dst.Add(Pred); + } PostStmt PS(CallExpr, LCtx); for (ExplodedNode *N : Dst) { diff --git a/clang/test/Analysis/ctor-trivial-copy.cpp b/clang/test/Analysis/ctor-trivial-copy.cpp new file mode 100644 index 0000000000000..5ed188aa8f1ea --- /dev/null +++ b/clang/test/Analysis/ctor-trivial-copy.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=constructors -verify %s + + +template +void clang_analyzer_dump(T&); + +struct aggr { + int x; + int y; +}; + +struct empty { +}; + +void test_copy_return() { + aggr s1 = {1, 2}; + aggr const& cr1 = aggr(s1); + clang_analyzer_dump(cr1); // expected-warning-re {{&lifetime_extended_object{aggr, cr1, S{{[0-9]+}}} }} + + empty s2; + empty const& cr2 = empty{s2}; + clang_analyzer_dump(cr2); // expected-warning-re {{&lifetime_extended_object{empty, cr2, S{{[0-9]+}}} }} +} + +void test_assign_return() { + aggr s1 = {1, 2}; + aggr d1; + clang_analyzer_dump(d1 = s1); // expected-warning {{&d1 }} + + empty s2; + empty d2; + clang_analyzer_dump(d2 = s2); // expected-warning {{&d2 }} was Unknown +} +