[analyzer] Extend NoStoreFuncVisitor to follow fields.

rdar://39701823

Differential Revision: https://reviews.llvm.org/D49901

llvm-svn: 338667
diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.c b/clang/test/Analysis/diagnostics/no-store-func-path-notes.c
index a444ab6..2050f62 100644
--- a/clang/test/Analysis/diagnostics/no-store-func-path-notes.c
+++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.c
@@ -224,3 +224,23 @@
   return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
               // expected-note@-1{{Undefined or garbage value returned to caller}}
 }
+
+typedef struct {
+  int *x;
+} D;
+
+void initializeMaybeInStruct(D* pD) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    *pD->x = 120;
+} // expected-note{{Returning without writing to 'pD->x'}}
+
+int useInitializeMaybeInStruct() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  D d;
+  d.x = &z;
+  initializeMaybeInStruct(&d); // expected-note{{Calling 'initializeMaybeInStruct'}}
+                               // expected-note@-1{{Returning from 'initializeMaybeInStruct'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp b/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp
index 17ec96a..3d7003e 100644
--- a/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp
+++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp
@@ -10,10 +10,10 @@
 }
 
 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'}}
+  int outP;                        // expected-note {{'outP' declared without an initial value}}
+  int out = initializer1(outP, 0); // expected-note{{Calling 'initializer1'}}
                                 // expected-note@-1{{Returning from 'initializer1'}}
-  return p;                     // expected-note{{Undefined or garbage value returned to caller}}
+  return outP;                     // expected-note{{Undefined or garbage value returned to caller}}
                                 // expected-warning@-1{{Undefined or garbage value returned to caller}}
 }
 
@@ -175,3 +175,161 @@
                           //expected-note@-1{{}}
     (void)useLocal;
 }
+
+////////
+
+struct HasRef {
+  int &a;
+  HasRef(int &a) : a(a) {}
+};
+
+
+void maybeInitialize(const HasRef &&pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA.a = 120;
+} // expected-note{{Returning without writing to 'pA.a'}}
+
+int useMaybeInitializerWritingIntoField() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  maybeInitialize(HasRef(z)); // expected-note{{Calling constructor for 'HasRef'}}
+                              // expected-note@-1{{Returning from constructor for 'HasRef'}}
+                              // expected-note@-2{{Calling 'maybeInitialize'}}
+                              // expected-note@-3{{Returning from 'maybeInitialize'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasRefToItself {
+  HasRefToItself &Ref; // no infinite loop
+  int &z;
+  HasRefToItself(int &z) : Ref(*this), z(z) {}
+};
+
+void maybeInitialize(const HasRefToItself &&pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA.z = 120;
+} // expected-note{{Returning without writing to 'pA.Ref.z'}}
+
+int useMaybeInitializerWritingIntoFieldWithRefToItself() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  maybeInitialize(HasRefToItself(z)); // expected-note{{Calling constructor for 'HasRefToItself'}}
+                              // expected-note@-1{{Returning from constructor for 'HasRefToItself'}}
+                              // expected-note@-2{{Calling 'maybeInitialize'}}
+                              // expected-note@-3{{Returning from 'maybeInitialize'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+////
+
+void maybeInitialize(const HasRef *pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA->a = 120;
+} // expected-note{{Returning without writing to 'pA->a'}}
+
+int useMaybeInitializerStructByPointer() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  HasRef wrapper(z); // expected-note{{Calling constructor for 'HasRef'}}
+                     // expected-note@-1{{Returning from constructor for 'HasRef'}}
+  maybeInitialize(&wrapper); // expected-note{{Calling 'maybeInitialize'}}
+                             // expected-note@-1{{Returning from 'maybeInitialize'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasParentWithRef : public HasRef {
+  HasParentWithRef(int &a) : HasRef(a) {} // expected-note{{Calling constructor for 'HasRef'}}
+                                          // expected-note@-1{{Returning from constructor for 'HasRef'}}
+};
+
+void maybeInitializeWithParent(const HasParentWithRef &pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA.a = 120;
+} // expected-note{{Returning without writing to 'pA.a'}}
+
+int useMaybeInitializerWritingIntoParentField() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  maybeInitializeWithParent(HasParentWithRef(z)); // expected-note{{Calling constructor for 'HasParentWithRef'}}
+                              // expected-note@-1{{Returning from constructor for 'HasParentWithRef'}}
+                              // expected-note@-2{{Calling 'maybeInitializeWithParent'}}
+                              // expected-note@-3{{Returning from 'maybeInitializeWithParent'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasIndirectRef {
+  HasRef &Ref;
+  HasIndirectRef(HasRef &Ref) : Ref(Ref) {}
+};
+
+void maybeInitializeIndirectly(const HasIndirectRef &pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA.Ref.a = 120;
+} // expected-note{{Returning without writing to 'pA.Ref.a'}}
+
+int useMaybeInitializeIndirectly() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  HasRef r(z); // expected-note{{Calling constructor for 'HasRef'}}
+               // expected-note@-1{{Returning from constructor for 'HasRef'}}
+  maybeInitializeIndirectly(HasIndirectRef(r)); // expected-note{{Calling 'maybeInitializeIndirectly'}}
+                                                // expected-note@-1{{Returning from 'maybeInitializeIndirectly'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasIndirectRefByValue {
+  HasRef Ref;
+  HasIndirectRefByValue(HasRef Ref) : Ref(Ref) {}
+};
+
+void maybeInitializeIndirectly(const HasIndirectRefByValue &pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA.Ref.a = 120;
+} // expected-note{{Returning without writing to 'pA.Ref.a'}}
+
+int useMaybeInitializeIndirectlyIndirectRefByValue() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  HasRef r(z); // expected-note{{Calling constructor for 'HasRef'}}
+               // expected-note@-1{{Returning from constructor for 'HasRef'}}
+  maybeInitializeIndirectly(HasIndirectRefByValue(r)); // expected-note{{Calling 'maybeInitializeIndirectly'}}
+                                                // expected-note@-1{{Returning from 'maybeInitializeIndirectly'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasIndirectPointerRef {
+  HasRef *Ref;
+  HasIndirectPointerRef(HasRef *Ref) : Ref(Ref) {}
+};
+
+void maybeInitializeIndirectly(const HasIndirectPointerRef &pA) {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    pA.Ref->a = 120;
+} // expected-note{{Returning without writing to 'pA.Ref->a'}}
+
+int useMaybeInitializeIndirectlyWithPointer() {
+  int z; // expected-note{{'z' declared without an initial value}}
+  HasRef r(z); // expected-note{{Calling constructor for 'HasRef'}}
+               // expected-note@-1{{Returning from constructor for 'HasRef'}}
+  maybeInitializeIndirectly(HasIndirectPointerRef(&r)); // expected-note{{Calling 'maybeInitializeIndirectly'}}
+                                                // expected-note@-1{{Returning from 'maybeInitializeIndirectly'}}
+  return z; // expected-warning{{Undefined or garbage value returned to caller}}
+            // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.m b/clang/test/Analysis/diagnostics/no-store-func-path-notes.m
index 51d1515..c0aa514 100644
--- a/clang/test/Analysis/diagnostics/no-store-func-path-notes.m
+++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.m
@@ -52,7 +52,6 @@
 extern void expectNonNull(NSString * _Nonnull a);
 
 @interface A : NSObject
-- (void) func;
 - (void) initAMaybe;
 @end
 
@@ -66,7 +65,7 @@
     a = @"string";
 } // expected-note{{Returning without writing to 'self->a'}}
 
-- (void) func {
+- (void) passNullToNonnull {
   a = nil; // expected-note{{nil object reference stored to 'a'}}
   [self initAMaybe]; // expected-note{{Calling 'initAMaybe'}}
                      // expected-note@-1{{Returning from 'initAMaybe'}}
@@ -74,4 +73,33 @@
                     // expected-note@-1{{nil passed to a callee that requires a non-null 1st parameter}}
 }
 
+- (void) initAMaybeWithExplicitSelf {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    self->a = @"string";
+} // expected-note{{Returning without writing to 'self->a'}}
+
+- (void) passNullToNonnullWithExplicitSelf {
+  self->a = nil; // expected-note{{nil object reference stored to 'a'}}
+  [self initAMaybeWithExplicitSelf]; // expected-note{{Calling 'initAMaybeWithExplicitSelf'}}
+                     // expected-note@-1{{Returning from 'initAMaybeWithExplicitSelf'}}
+  expectNonNull(a); // expected-warning{{nil passed to a callee that requires a non-null 1st parameter}}
+                    // expected-note@-1{{nil passed to a callee that requires a non-null 1st parameter}}
+}
+
+- (void) initPassedAMaybe:(A *) param {
+  if (coin()) // expected-note{{Assuming the condition is false}}
+              // expected-note@-1{{Taking false branch}}
+    param->a = @"string";
+} // expected-note{{Returning without writing to 'param->a'}}
+
+- (void) useInitPassedAMaybe:(A *) paramA {
+  paramA->a = nil; // expected-note{{nil object reference stored to 'a'}}
+  [self initPassedAMaybe:paramA]; // expected-note{{Calling 'initPassedAMaybe:'}}
+                                  // expected-note@-1{{Returning from 'initPassedAMaybe:'}}
+  expectNonNull(paramA->a); // expected-warning{{nil passed to a callee that requires a non-null 1st parameter}}
+                            // expected-note@-1{{nil passed to a callee that requires a non-null 1st parameter}}
+
+}
+
 @end