Skip to content

Commit

Permalink
[analyzer] Bind return value for assigment and copies of trivial empt…
Browse files Browse the repository at this point in the history
…y classes

We now properly bind return value of the trivial copy constructor
and assignments of the empty objects. Such operations do not
perform any loads from the source, however they preserve identity
of the assigned object:
```
Empty e;
auto& x = (e = Empty());
clang_analyzer_dump(x); // &e, was Unknown
```

Reviewed By: xazax.hun

Differential Revision: https://reviews.llvm.org/D155442
  • Loading branch information
Tomasz Kamiński committed Jul 18, 2023
1 parent ef7d537 commit 61760bb
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 18 deletions.
36 changes: 18 additions & 18 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Loc> L = V.getAs<Loc>())
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<Loc> L = V.getAs<Loc>())
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) {
Expand Down
34 changes: 34 additions & 0 deletions clang/test/Analysis/ctor-trivial-copy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=constructors -verify %s


template<typename T>
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
}

0 comments on commit 61760bb

Please sign in to comment.