blob: 4920a3c928fb80c3cb26ce82f5f5cd05af3c4c07 [file] [edit]
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugLifetimeModeling \
// RUN: -analyzer-config cfg-lifetime=true -verify %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugLifetimeModeling \
// RUN: -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -verify %s
struct A {};
void clang_analyzer_dumpLifetimeOriginsOf(int*);
void clang_analyzer_dumpLifetimeOriginsOf(int&);
void clang_analyzer_dumpLifetimeOriginsOf(A*);
void clang_analyzer_dumpLifetimeOriginsOf(A&);
// These are the cases when the result of function calls are MemRegions.
// Ref type parameter annotated case.
struct X {
int &choose(int &a [[clang::lifetimebound]]) { return a; }
};
void caller() {
int v = 0;
X obj;
int &r = obj.choose(v);
clang_analyzer_dumpLifetimeOriginsOf(r); // expected-warning {{Origin &v bound to v}}
}
// Obj ref type function return annotated case.
struct Y {
A a;
A &getA() [[clang::lifetimebound]] { return a; }
};
void caller_two() {
// Return statement is annotated case.
Y y;
A &f = y.getA();
clang_analyzer_dumpLifetimeOriginsOf(f); // expected-warning {{Origin &y.a bound to y}}
}
// Obj ptr type function return annotated case.
struct Z {
A a;
A *getA() [[clang::lifetimebound]] { return &a; }
};
void caller_three() {
Z z;
A *func = z.getA();
clang_analyzer_dumpLifetimeOriginsOf(func); // expected-warning {{Origin &z.a bound to z}}
}
// Free function with annotated param and ref return.
int &foo(int &num [[clang::lifetimebound]]) { return num; }
void caller_four() {
int num = 5;
int &s = foo(num);
clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &num bound to num}}
}
// Free function with annotated param and ptr return.
int *boo(int *num [[clang::lifetimebound]]) { return num; }
void caller_five() {
int n = 55;
int *n_ptr = &n;
int *s = boo(n_ptr);
clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &n bound to n}}
}
// Free function with both annotated and non-annotated parameters.
int &fn(int &f, int &s [[clang::lifetimebound]]) { return s; }
void caller_six() {
int even = 50;
int odd = 55;
int &s = fn(even, odd);
clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &odd bound to odd}}
}
// These are the cases when the result of function calls are SymbolRefs.
// Function returns ptr and has an annotated parameter.
int *foo(int *n [[clang::lifetimebound]]);
void caller_seven() {
int y = 15;
int *y_ptr = &y;
auto *bind = foo(y_ptr);
clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin &SymRegion{{.*}} bound to y}}
}
// Function returns a reference and has an annotated parameter.
int &func(int &some_number [[clang::lifetimebound]]);
void caller_eight() {
int f = 15;
auto &bind = func(f);
clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin &SymRegion{{.*}} bound to f}}
}
// Function returns a reference and has two annotated parameters.
int &f(int &a [[clang::lifetimebound]], int &b [[clang::lifetimebound]]);
void caller_nine() {
int first_num = 1;
int second_num = 2;
int &numbers = f(first_num, second_num);
clang_analyzer_dumpLifetimeOriginsOf(numbers); // expected-warning-re {{Origin &SymRegion{{.*}} bound to first_num, second_num}}
}
struct View {
int *p;
};
View makeView(int &x [[clang::lifetimebound]]);
void clang_analyzer_dumpLifetimeOriginsOf(View);
void caller_view() {
int v = 42;
View w = makeView(v);
// FIXME: Currently none of the maps cover LazyCompoundVal.
clang_analyzer_dumpLifetimeOriginsOf(w); // no-warning
}
// These are the test cases for testing the correctness of the emitted warning from the UseAfterLifetimeEnd checker.
// Return value bound to annotated param cases.
int *test_func(int *p [[clang::lifetimebound]]);
int *direct_return() {
int i = 5;
return test_func(&i);
// expected-warning@-1 {{Returning value bound to 'i' that will go out of scope}}
// expected-warning@-2 {{address of stack memory associated with local variable 'i' returned}}
}
int *variable_return() {
int y = 5;
int *p = test_func(&y);
return p; // expected-warning {{Returning value bound to 'y' that will go out of scope}}
}
int *borrow_from_caller(int *b [[clang::lifetimebound]]) {
return test_func(b); // no-warning
}
void no_return() {
int i = 5;
int *p = test_func(&i);
(void)p; // no-warning
}
int *g() {
int i = 5;
int *p = test_func(&i);
(void)p;
return nullptr; // no-warning
}
int &multi_param_test_ref(int &a [[clang::lifetimebound]], int &b [[clang::lifetimebound]]);
// Return value bound to annotated parameters (two dangling sources).
int &dangling_sources_ref() {
int x = 1, y = 2;
return multi_param_test_ref(x, y);
// expected-warning@-1 {{Returning value bound to 'x' that will go out of scope}}
// expected-warning@-2 {{Returning value bound to 'y' that will go out of scope}}
// expected-warning@-3 {{reference to stack memory associated with local variable 'x' returned}}
// expected-warning@-4 {{reference to stack memory associated with local variable 'y' returned}}
}
// Return value bound to annotated parameters (no dangling sources).
int &no_dangling_sources_ref(int &a [[clang::lifetimebound]], int &b [[clang::lifetimebound]]) {
return multi_param_test_ref(a, b); // no-warning
}
// Return value bound to annotated parameters (one dangling source).
int &one_dangling_source_ref(int &a [[clang::lifetimebound]]) {
int x = 1;
return multi_param_test_ref(a, x);
// expected-warning@-1 {{Returning value bound to 'x' that will go out of scope}}
// expected-warning@-2 {{reference to stack memory associated with local variable 'x' returned}}
}
int *multi_param_test_ptr(int *a [[clang::lifetimebound]], int *b [[clang::lifetimebound]]);
// Return value bound to annotated parameters (two dangling sources).
int *dangling_sources_ptr() {
int x = 1, y = 2;
int *x_ptr = &x;
int *y_ptr = &y;
return multi_param_test_ptr(x_ptr, y_ptr);
// expected-warning@-1 {{Returning value bound to 'x' that will go out of scope}}
// expected-warning@-2 {{Returning value bound to 'y' that will go out of scope}}
}
// Return value bound to annotated parameters (no dangling sources).
int *no_dangling_sources_ptr(int *a [[clang::lifetimebound]], int *b [[clang::lifetimebound]]) {
return multi_param_test_ptr(a, b); // no-warning
}
// Return value bound to annotated parameters (one dangling source).
int *one_dangling_source_ptr(int *a [[clang::lifetimebound]]) {
int x = 1;
int *x_ptr = &x;
return multi_param_test_ptr(a, x_ptr); // expected-warning {{Returning value bound to 'x' that will go out of scope}}
}
struct S {
int x;
int* get() [[clang::lifetimebound]] { return &x; }
S() = default;
S(S&& other) { (void)other.get(); }
~S() { get(); }
};
S make() { return S(); }
S passThrough(S param) { return param; }
void outer() {
auto f = passThrough(make());
(void)f; // no-warning
}
int *danglingLocal() {
S s;
return s.get();
// expected-warning@-1 {{Returning value bound to 's' that will go out of scope}}
// expected-warning@-2 {{Address of stack memory associated with local variable 's' returned}}
// expected-warning@-3 {{address of stack memory associated with local variable 's' returned}}
}
int *danglingParam(S param) {
return param.get();
// expected-warning@-1 {{Returning value bound to 'param' that will go out of scope}}
// expected-warning@-2 {{Address of stack memory associated with local variable 'param' returned}}
// expected-warning@-3 {{address of stack memory associated with parameter 'param' returned}}
}