llvm/clang/test/Analysis/lambdas.cpp

// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -analyzer-config inline-lambdas=false -DNO_INLINING=1 -verify %s
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
// RUN: FileCheck --input-file=%t %s

#include "Inputs/system-header-simulator-cxx.h"

void clang_analyzer_warnIfReached();
void clang_analyzer_eval(int);

#ifdef NO_INLINING

// expected-no-diagnostics

int& invalidate_static_on_unknown_lambda() {
  static int* z;
  auto f = [] {
    z = nullptr;
  }; // should invalidate "z" when inlining is disabled.
  f();
  return *z; // no-warning
}

#else

struct X { X(const X&); };
void f(X x) { (void) [x]{}; }


// Lambda semantics tests.

void basicCapture() {
  int i = 5;
  [i]() mutable {
    // clang_analyzer_eval does nothing in inlined functions.
    if (i != 5)
      clang_analyzer_warnIfReached();
    ++i;
  }();
  [&i] {
    if (i != 5)
      clang_analyzer_warnIfReached();
  }();
  [&i] {
    if (i != 5)
      clang_analyzer_warnIfReached();
    i++;
  }();
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

void deferredLambdaCall() {
  int i = 5;
  auto l1 = [i]() mutable {
    if (i != 5)
      clang_analyzer_warnIfReached();
    ++i;
  };
  auto l2 = [&i] {
    if (i != 5)
      clang_analyzer_warnIfReached();
  };
  auto l3 = [&i] {
    if (i != 5)
      clang_analyzer_warnIfReached();
    i++;
  };
  l1();
  l2();
  l3();
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

void multipleCaptures() {
  int i = 5, j = 5;
  [i, &j]() mutable {
    if (i != 5 && j != 5)
      clang_analyzer_warnIfReached();
    ++i;
    ++j;
  }();
  clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
  clang_analyzer_eval(j == 6); // expected-warning{{TRUE}}
  [=]() mutable {
    if (i != 5 && j != 6)
      clang_analyzer_warnIfReached();
    ++i;
    ++j;
  }();
  clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
  clang_analyzer_eval(j == 6); // expected-warning{{TRUE}}
  [&]() mutable {
    if (i != 5 && j != 6)
      clang_analyzer_warnIfReached();
    ++i;
    ++j;
  }();
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
  clang_analyzer_eval(j == 7); // expected-warning{{TRUE}}
}

void testReturnValue() {
  int i = 5;
  auto l = [i] (int a) {
    return i + a;
  };
  int b = l(3);
  clang_analyzer_eval(b == 8); // expected-warning{{TRUE}}
}

void testAliasingBetweenParameterAndCapture() {
  int i = 5;

  auto l = [&i](int &p) {
    i++;
    p++;
  };
  l(i);
  clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
}

// Nested lambdas.

void testNestedLambdas() {
  int i = 5;
  auto l = [i]() mutable {
    [&i]() {
      ++i;
    }();
    if (i != 6)
      clang_analyzer_warnIfReached();
  };
  l();
  clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
}

// Captured this.

class RandomClass {
  int i;

  void captureFields() {
    i = 5;
    [this]() {
      // clang_analyzer_eval does nothing in inlined functions.
      if (i != 5)
        clang_analyzer_warnIfReached();
      ++i;
    }();
    clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
  }
};


// Nested this capture.

class RandomClass2 {
  int i;

  void captureFields() {
    i = 5;
    [this]() {
      // clang_analyzer_eval does nothing in inlined functions.
      if (i != 5)
        clang_analyzer_warnIfReached();
      ++i;
      [this]() {
        // clang_analyzer_eval does nothing in inlined functions.
        if (i != 6)
          clang_analyzer_warnIfReached();
        ++i;
      }();
    }();
    clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
  }
};


// Captured function pointers.

void inc(int &x) {
  ++x;
}

void testFunctionPointerCapture() {
  void (*func)(int &) = inc;
  int i = 5;
  [&i, func] {
    func(i);
  }();
  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
}

// Captured variable-length array.

void testVariableLengthArrayCaptured() {
  int n = 2;     // expected-note {{declared here}}
  int array[n];  // expected-warning {{variable length arrays in C++ are a Clang extension}} \
                    expected-note {{read of non-const variable 'n' is not allowed in a constant expression}}
  array[0] = 7;

  int i = [&]{
    return array[0];
  }();

  clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
}

// Test inline defensive checks
int getNum();

void inlineDefensiveChecks() {
  int i = getNum();
  [=]() {
    if (i == 0)
      ;
  }();
  int p = 5/i;
  (void)p;
}


template<typename T>
void callLambda(T t) {
  t();
}

struct DontCrash {
  int x;
  void f() {
    callLambda([&](){ ++x; });
    callLambdaFromStatic([&](){ ++x; });
  }

  template<typename T>
  static void callLambdaFromStatic(T t) {
    t();
  }
};


// Capture constants

void captureConstants() {
  const int i = 5;
  [=]() {
    if (i != 5)
      clang_analyzer_warnIfReached();
  }();
  [&] {
    if (i != 5)
      clang_analyzer_warnIfReached();
  }();
}

void captureReferenceByCopy(int &p) {
  int v = 7;
  p = 8;

  // p is a reference captured by copy
  [&v,p]() mutable {
    v = p;
    p = 22;
  }();

  clang_analyzer_eval(v == 8); // expected-warning{{TRUE}}
  clang_analyzer_eval(p == 8); // expected-warning{{TRUE}}
}

void captureReferenceByReference(int &p) {
  int v = 7;
  p = 8;

  // p is a reference captured by reference
  [&v,&p]() {
    v = p;
    p = 22;
  }();

  clang_analyzer_eval(v == 8); // expected-warning{{TRUE}}
  clang_analyzer_eval(p == 22); // expected-warning{{TRUE}}
}

void callMutableLambdaMultipleTimes(int &p) {
  int v = 0;
  p = 8;

  auto l = [&v, p]() mutable {
    v = p;
    p++;
  };

  l();

  clang_analyzer_eval(v == 8); // expected-warning{{TRUE}}
  clang_analyzer_eval(p == 8); // expected-warning{{TRUE}}

  l();

  clang_analyzer_eval(v == 9); // expected-warning{{TRUE}}
  clang_analyzer_eval(p == 8); // expected-warning{{TRUE}}
}

// PR 24914
struct StructPR24914{
  int x;
};

void takesConstStructArgument(const StructPR24914&);
void captureStructReference(const StructPR24914& s) {
  [s]() {
    takesConstStructArgument(s);
  }();
}

// Lambda capture counts as use for dead-store checking.

int returnsValue();

void captureByCopyCausesUse() {
  int local1 = returnsValue(); // no-warning
  int local2 = returnsValue(); // no-warning
  int local3 = returnsValue(); // expected-warning{{Value stored to 'local3' during its initialization is never read}}

  (void)[local1, local2]() { }; // Explicit capture by copy counts as use.

  int local4 = returnsValue(); // no-warning
  int local5 = returnsValue(); // expected-warning{{Value stored to 'local5' during its initialization is never read}}

  (void)[=]() {
    (void)local4; // Implicit capture by copy counts as use
  };
}

void captureByReference() {
  int local1 = returnsValue(); // no-warning

  auto lambda1 = [&local1]() { // Explicit capture by reference
    local1++;
  };

  // Don't treat as a dead store because local1 was captured by reference.
  local1 = 7; // no-warning

  lambda1();

  int local2 = returnsValue(); // no-warning

  auto lambda2 = [&]() {
    local2++; // Implicit capture by reference
  };

  // Don't treat as a dead store because local2 was captured by reference.
  local2 = 7; // no-warning

  lambda2();
}

void testCapturedConstExprFloat() {
  constexpr float localConstant = 4.0;
  auto lambda = []{
    // Don't treat localConstant as containing a garbage value
    float copy = localConstant; // no-warning
    (void)copy;
  };

  lambda();
}

void escape(void*);

int& invalidate_static_on_unknown_lambda() {
  static int* z;
  auto lambda = [] {
    static float zz;
    z = new int(120);
  };
  escape(&lambda);
  return *z; // no-warning
}


static int b = 0;

int f() {
  b = 0;
  auto &bm = b;
  [&] {
    bm++;
    bm++;
  }();
  if (bm != 2) {
    int *y = 0;
    return *y; // no-warning
  }
  return 0;
}

#endif

// CHECK: [B2 (ENTRY)]
// CHECK:   Succs (1): B1
// CHECK: [B1]
// CHECK:   1: x
// CHECK:   2: [B1.1] (ImplicitCastExpr, NoOp, const X)
// CHECK:   3: [B1.2] (CXXConstructExpr[B1.4]+0, X)
// CHECK:   4: [x]     {
// CHECK:    }
// CHECK:   5: (void)[B1.4] (CStyleCastExpr, ToVoid, void)
// CHECK:   Preds (1): B2
// CHECK:   Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK:   Preds (1): B1