355 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
// 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,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
 | 
						|
// RUN: FileCheck --input-file=%t %s
 | 
						|
 | 
						|
void clang_analyzer_warnIfReached();
 | 
						|
void clang_analyzer_eval(int);
 | 
						|
 | 
						|
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;
 | 
						|
  int array[n];
 | 
						|
  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 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 was captured by reference.
 | 
						|
  local2 = 7; // no-warning
 | 
						|
 | 
						|
  lambda2();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// CHECK: [B2 (ENTRY)]
 | 
						|
// CHECK:   Succs (1): B1
 | 
						|
// CHECK: [B1]
 | 
						|
// CHECK:   1: x
 | 
						|
// CHECK:   2: [B1.1] (ImplicitCastExpr, NoOp, const struct X)
 | 
						|
// CHECK:   3: [B1.2] (CXXConstructExpr, struct 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
 | 
						|
 |