Skip to content

Commit

Permalink
[analyzer] Re-enable C++17-specific variable and member construction …
Browse files Browse the repository at this point in the history
…contexts.

Not contexts themselves, but rather support for them in the analyzer.

Such construction contexts appear when C++17 mandatory copy elision occurs
during initialization, and presence of a destructor causes a
CXXBindTemporaryExpr to appear in the AST.

Similar C++17-specific constructors for return values are still to be supported.

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

llvm-svn: 334683
  • Loading branch information
haoNoQ committed Jun 14, 2018
1 parent a84374d commit 53b8ce0
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 3 deletions.
6 changes: 3 additions & 3 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
Expand Up @@ -119,8 +119,9 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// current construction context.
if (CC) {
switch (CC->getKind()) {
case ConstructionContext::CXX17ElidedCopyVariableKind:
case ConstructionContext::SimpleVariableKind: {
const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
const auto *DSCC = cast<VariableConstructionContext>(CC);
const auto *DS = DSCC->getDeclStmt();
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
SVal LValue = State->getLValue(Var, LCtx);
Expand All @@ -131,6 +132,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue);
return std::make_pair(State, LValue);
}
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
const auto *Init = ICC->getCXXCtorInitializer();
Expand Down Expand Up @@ -259,9 +261,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
CallOpts.IsTemporaryCtorOrDtor = true;
return std::make_pair(State, V);
}
case ConstructionContext::CXX17ElidedCopyVariableKind:
case ConstructionContext::CXX17ElidedCopyReturnedValueKind:
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
// Not implemented yet.
break;
}
Expand Down
119 changes: 119 additions & 0 deletions clang/test/Analysis/cxx17-mandatory-elision.cpp
Expand Up @@ -49,9 +49,64 @@ class C {
}
};


struct A {
int x;
A(): x(0) {}
~A() {}
};

struct B {
A a;
B() : a(A()) {}
};

void foo() {
B b;
clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}}
}

} // namespace ctor_initializer


namespace elision_on_ternary_op_branches {
class C1 {
int x;
public:
C1(int x): x(x) {}
int getX() const { return x; }
~C1();
};

class C2 {
int x;
int y;
public:
C2(int x, int y): x(x), y(y) {}
int getX() const { return x; }
int getY() const { return y; }
~C2();
};

void foo(int coin) {
C1 c1 = coin ? C1(1) : C1(2);
if (coin) {
clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}
} else {
clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}}
}
C2 c2 = coin ? C2(3, 4) : C2(5, 6);
if (coin) {
clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}}
clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}}
} else {
clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}}
clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}}
}
}
} // namespace elision_on_ternary_op_branches


namespace address_vector_tests {

template <typename T> struct AddressVector {
Expand Down Expand Up @@ -108,4 +163,68 @@ void testMultipleReturns() {
#endif
}

class ClassWithDestructor {
AddressVector<ClassWithDestructor> &v;

public:
ClassWithDestructor(AddressVector<ClassWithDestructor> &v) : v(v) {
v.push(this);
}

ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { v.push(this); }
ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) {
v.push(this);
}

~ClassWithDestructor() { v.push(this); }
};

void testVariable() {
AddressVector<ClassWithDestructor> v;
{
ClassWithDestructor c = ClassWithDestructor(v);
}
#if __cplusplus >= 201703L
// 0. Construct the variable.
// 1. Destroy the variable.
clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
#else
// 0. Construct the temporary.
// 1. Construct the variable.
// 2. Destroy the temporary.
// 3. Destroy the variable.
clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
#endif
}

struct TestCtorInitializer {
ClassWithDestructor c;
TestCtorInitializer(AddressVector<ClassWithDestructor> &v)
: c(ClassWithDestructor(v)) {}
};

void testCtorInitializer() {
AddressVector<ClassWithDestructor> v;
{
TestCtorInitializer t(v);
}
#if __cplusplus >= 201703L
// 0. Construct the member variable.
// 1. Destroy the member variable.
clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
#else
// 0. Construct the temporary.
// 1. Construct the member variable.
// 2. Destroy the temporary.
// 3. Destroy the member variable.
clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
#endif
}

} // namespace address_vector_tests

0 comments on commit 53b8ce0

Please sign in to comment.