[analyzer] Re-enable C++17-specific variable and member construction contexts.
authorArtem Dergachev <artem.dergachev@gmail.com>
Thu, 14 Jun 2018 01:54:21 +0000 (01:54 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Thu, 14 Jun 2018 01:54:21 +0000 (01:54 +0000)
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

clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
clang/test/Analysis/cxx17-mandatory-elision.cpp

index ada99e1..1dd094b 100644 (file)
@@ -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);
@@ -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();
@@ -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;
     }
index 6a88bba..be786be 100644 (file)
@@ -49,9 +49,64 @@ public:
   }
 };
 
+
+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 {
@@ -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