[analyzer] mark returns of functions where the region passed as parameter was not initialized
In the wild, many cases of null pointer dereference, or uninitialized
value read occur because the value was meant to be initialized by the
inlined function, but did not, most often due to error condition in the
inlined function.
This change highlights the return branch taken by the inlined function,
in order to help user understand the error report and see why the value
was uninitialized.
rdar://36287652
Differential Revision: https://reviews.llvm.org/D41848
llvm-svn: 325976
diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp b/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp
new file mode 100644
index 0000000..a704c14
--- /dev/null
+++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp
@@ -0,0 +1,147 @@
+// RUN: %clang_analyze_cc1 -x c++ -analyzer-checker=core -analyzer-output=text -verify %s
+
+int initializer1(int &p, int x) {
+ if (x) { // expected-note{{Taking false branch}}
+ p = 1;
+ return 0;
+ } else {
+ return 1; // expected-note {{Returning without writing to 'p'}}
+ }
+}
+
+int param_not_initialized_by_func() {
+ int p; // expected-note {{'p' declared without an initial value}}
+ int out = initializer1(p, 0); // expected-note{{Calling 'initializer1'}}
+ // expected-note@-1{{Returning from 'initializer1'}}
+ return p; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct S {
+ int initialize(int *p, int param) {
+ if (param) { //expected-note{{Taking false branch}}
+ *p = 1;
+ return 1;
+ }
+ return 0; // expected-note{{Returning without writing to '*p'}}
+ }
+};
+
+int use(S *s) {
+ int p; //expected-note{{'p' declared without an initial value}}
+ s->initialize(&p, 0); //expected-note{{Calling 'S::initialize'}}
+ //expected-note@-1{{Returning from 'S::initialize'}}
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+int initializer2(const int &p) {
+ return 0;
+}
+
+int no_msg_const_ref() {
+ int p; //expected-note{{'p' declared without an initial value}}
+ initializer2(p);
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+void nested() {}
+void init_in_nested_func(int **x) {
+ *x = 0; // expected-note{{Null pointer value stored to 'y'}}
+ nested();
+} // no-note
+
+int call_init_nested() {
+ int x = 0;
+ int *y = &x;
+ init_in_nested_func(&y); // expected-note{{Calling 'init_in_nested_func'}}
+ // expected-note@-1{{Returning from 'init_in_nested_func'}}
+ return *y; //expected-warning{{Dereference of null pointer (loaded from variable 'y')}}
+ //expected-note@-1{{Dereference of null pointer (loaded from variable 'y')}}
+}
+
+struct A {
+ int x;
+ int y;
+};
+
+void partial_init_by_reference(A &a) {
+ a.x = 0;
+} // expected-note {{Returning without writing to 'a.y'}}
+
+int use_partial_init_by_reference() {
+ A a;
+ partial_init_by_reference(a); // expected-note{{Calling 'partial_init_by_reference'}}
+ // expected-note@-1{{Returning from 'partial_init_by_reference'}}
+ return a.y; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct B : A {
+};
+
+void partially_init_inherited_struct(B *b) {
+ b->x = 0;
+} // expected-note{{Returning without writing to 'b->y'}}
+
+int use_partially_init_inherited_struct() {
+ B b;
+ partially_init_inherited_struct(&b); // expected-note{{Calling 'partially_init_inherited_struct'}}
+ // expected-note@-1{{Returning from 'partially_init_inherited_struct'}}
+ return b.y; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct C {
+ int x;
+ int y;
+ C(int pX, int pY) : x(pX) {} // expected-note{{Returning without writing to 'this->y'}}
+};
+
+int use_constructor() {
+ C c(0, 0); // expected-note{{Calling constructor for 'C'}}
+ // expected-note@-1{{Returning from constructor for 'C'}}
+ return c.y; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct D {
+ void initialize(int *);
+};
+
+void D::initialize(int *p) {
+
+} // expected-note{{Returning without writing to '*p'}}
+
+int use_d_initializer(D* d) {
+ int p; // expected-note {{'p' declared without an initial value}}
+ d->initialize(&p); // expected-note{{Calling 'D::initialize'}}
+ // expected-note@-1{{Returning from 'D::initialize'}}
+ return p; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+int coin();
+
+struct S2 {
+ int x;
+};
+
+int pointerreference(S2* &s) {
+ if (coin()) // expected-note{{Assuming the condition is true}}
+ // expected-note@-1{{Taking true branch}}
+ return 1; // expected-note{{Returning without writing to 's->x'}}
+
+ s->x = 0;
+ return 0;
+}
+
+int usepointerreference() {
+ S2 s;
+ S2* p = &s;
+ pointerreference(p); //expected-note{{Calling 'pointerreference'}}
+ //expected-note@-1{{Returning from 'pointerreference'}}
+ return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}