blob: 93be79463aa2147b12aa7c4601435b4263dda4dd [file] [edit]
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++17 %s 2>&1 | FileCheck %s
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++11 %s 2>&1 | FileCheck %s
// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core,cplusplus.Move \
// RUN: -analyzer-output=text -verify %s
#include "Inputs/system-header-simulator-cxx.h"
int *getPtr(int);
int getVal(int);
// Compound assignment operators sequence the RHS before the LHS, exactly like
// simple assignment ([expr.ass]/1). The RHS (getVal) must therefore appear
// before the LHS (getPtr) in the CFG.
void test_builtin_compound(int a, int b) {
*getPtr(a) += getVal(b);
}
// CHECK-LABEL: void test_builtin_compound(int a, int b)
// CHECK: 1: getVal
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(int))
// CHECK-NEXT: 3: b
// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, int)
// CHECK-NEXT: 5: [B1.2]([B1.4])
// CHECK-NEXT: 6: getPtr
// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, FunctionToPointerDecay, int *(*)(int))
// CHECK-NEXT: 8: a
// CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, LValueToRValue, int)
// CHECK-NEXT: 10: [B1.7]([B1.9])
// CHECK-NEXT: 11: *[B1.10]
// CHECK-NEXT: 12: [B1.11] += [B1.5]
struct A { A(); ~A(); };
struct B { B(); ~B(); };
int &getLHS(const A &);
int getRHS(const B &);
// The RHS temporary (B) is constructed before the LHS temporary (A), because
// the RHS is fully sequenced before the LHS. Temporaries are destroyed in
// reverse order of construction, so ~A() runs before ~B().
void test_temp_dtor_order() {
getLHS(A()) += getRHS(B());
}
// CHECK-LABEL: void test_temp_dtor_order()
// CHECK: ~A() (Temporary object destructor)
// CHECK-NEXT: ~B() (Temporary object destructor)
struct S {
int use(S &) const { return 1; }
};
int consume(S);
// Because the RHS is evaluated before the LHS, the object 's' is moved from
// (in the RHS) before it is used (in the LHS index computation).
void test_move_then_use() {
S s;
int arr[10] = {};
arr[s.use(s)] += consume(std::move(s));
// expected-warning@-1 {{Method called on moved-from object 's'}}
// expected-note@-2 {{Method called on moved-from object 's'}}
// expected-note@-3 {{Object 's' is moved}}
(void)arr;
}