Author: george.karpenkov
Date: Fri Feb 23 15:26:56 2018
New Revision: 325976

URL: http://llvm.org/viewvc/llvm-project?rev=325976&view=rev
Log:
[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

Added:
    cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c
    cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp
    cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m
Modified:
    cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
    cfe/trunk/test/Analysis/diagnostics/undef-value-param.c
    cfe/trunk/test/Analysis/diagnostics/undef-value-param.m

Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=325976&r1=325975&r2=325976&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Fri Feb 23 
15:26:56 2018
@@ -184,6 +184,276 @@ static bool isFunctionMacroExpansion(Sou
 
 namespace {
 
+/// Put a diagnostic on return statement of all inlined functions
+/// for which  the region of interest \p RegionOfInterest was passed into,
+/// but not written inside, and it has caused an undefined read or a null
+/// pointer dereference outside.
+class NoStoreFuncVisitor final
+    : public BugReporterVisitorImpl<NoStoreFuncVisitor> {
+
+  const SubRegion *RegionOfInterest;
+  static constexpr const char *DiagnosticsMsg =
+      "Returning without writing to '";
+  bool Initialized = false;
+
+  /// Frames writing into \c RegionOfInterest.
+  /// This visitor generates a note only if a function does not write into
+  /// a region of interest. This information is not immediately available
+  /// by looking at the node associated with the exit from the function
+  /// (usually the return statement). To avoid recomputing the same information
+  /// many times (going up the path for each node and checking whether the
+  /// region was written into) we instead pre-compute and store all
+  /// stack frames along the path which write into the region of interest
+  /// on the first \c VisitNode invocation.
+  llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
+
+public:
+  NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {}
+
+  void Profile(llvm::FoldingSetNodeID &ID) const override {
+    static int Tag = 0;
+    ID.AddPointer(&Tag);
+  }
+
+  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+                                                 const ExplodedNode *PrevN,
+                                                 BugReporterContext &BRC,
+                                                 BugReport &BR) override {
+    if (!Initialized) {
+      findModifyingFrames(N);
+      Initialized = true;
+    }
+
+    const LocationContext *Ctx = N->getLocationContext();
+    const StackFrameContext *SCtx = Ctx->getCurrentStackFrame();
+    ProgramStateRef State = N->getState();
+    auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+    // No diagnostic if region was modified inside the frame.
+    if (!CallExitLoc || FramesModifyingRegion.count(SCtx))
+      return nullptr;
+
+    CallEventRef<> Call =
+        BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+    const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy();
+    const SourceManager &SM = BRC.getSourceManager();
+    if (auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+      const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion();
+      if (RegionOfInterest->isSubRegionOf(ThisRegion) &&
+          !CCall->getDecl()->isImplicit())
+        return notModifiedInConstructorDiagnostics(Ctx, SM, PP, *CallExitLoc,
+                                                   CCall, ThisRegion);
+    }
+
+    ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
+    for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
+      const ParmVarDecl *PVD = parameters[I];
+      SVal S = Call->getArgSVal(I);
+      unsigned IndirectionLevel = 1;
+      QualType T = PVD->getType();
+      while (const MemRegion *R = S.getAsRegion()) {
+        if (RegionOfInterest->isSubRegionOf(R) &&
+            !isPointerToConst(PVD->getType()))
+          return notModifiedDiagnostics(
+              Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel);
+        QualType PT = T->getPointeeType();
+        if (PT.isNull() || PT->isVoidType()) break;
+        S = State->getSVal(R, PT);
+        T = PT;
+        IndirectionLevel++;
+      }
+    }
+
+    return nullptr;
+  }
+
+private:
+  /// Write to \c FramesModifyingRegion all stack frames along
+  /// the path which modify \c RegionOfInterest.
+  void findModifyingFrames(const ExplodedNode *N) {
+    ProgramStateRef LastReturnState;
+    do {
+      ProgramStateRef State = N->getState();
+      auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+      if (CallExitLoc) {
+        LastReturnState = State;
+      }
+      if (LastReturnState &&
+          wasRegionOfInterestModifiedAt(N, LastReturnState)) {
+        const StackFrameContext *SCtx =
+            N->getLocationContext()->getCurrentStackFrame();
+        while (!SCtx->inTopFrame()) {
+          auto p = FramesModifyingRegion.insert(SCtx);
+          if (!p.second)
+            break; // Frame and all its parents already inserted.
+          SCtx = SCtx->getParent()->getCurrentStackFrame();
+        }
+      }
+
+      N = N->getFirstPred();
+    } while (N);
+  }
+
+  /// \return Whether \c RegionOfInterest was modified at \p N,
+  /// where \p ReturnState is a state associated with the return
+  /// from the current frame.
+  bool wasRegionOfInterestModifiedAt(const ExplodedNode *N,
+                                     ProgramStateRef ReturnState) {
+    SVal ValueAtReturn = ReturnState->getSVal(RegionOfInterest);
+
+    // Writing into region of interest.
+    if (auto PS = N->getLocationAs<PostStmt>())
+      if (auto *BO = PS->getStmtAs<BinaryOperator>())
+        if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
+                                        
N->getSVal(BO->getLHS()).getAsRegion()))
+          return true;
+
+    // SVal after the state is possibly different.
+    SVal ValueAtN = N->getState()->getSVal(RegionOfInterest);
+    if (!ReturnState->areEqual(ValueAtN, ValueAtReturn).isConstrainedTrue() &&
+        (!ValueAtN.isUndef() || !ValueAtReturn.isUndef()))
+      return true;
+
+    return false;
+  }
+
+  /// Get parameters associated with runtime definition in order
+  /// to get the correct parameter name.
+  ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
+    if (isa<FunctionDecl>(Call->getDecl()))
+      return dyn_cast<FunctionDecl>(Call->getRuntimeDefinition().getDecl())
+          ->parameters();
+    return Call->parameters();
+  }
+
+  /// \return whether \p Ty points to a const type, or is a const reference.
+  bool isPointerToConst(QualType Ty) {
+    return !Ty->getPointeeType().isNull() &&
+           Ty->getPointeeType().getCanonicalType().isConstQualified();
+  }
+
+  std::shared_ptr<PathDiagnosticPiece> notModifiedInConstructorDiagnostics(
+      const LocationContext *Ctx,
+      const SourceManager &SM,
+      const PrintingPolicy &PP,
+      CallExitBegin &CallExitLoc,
+      const CXXConstructorCall *Call,
+      const MemRegion *ArgRegion) {
+
+    SmallString<256> sbuf;
+    llvm::raw_svector_ostream os(sbuf);
+    os << DiagnosticsMsg;
+    bool out = prettyPrintRegionName(
+        "this", "->", /*IsReference=*/true,
+        /*IndirectionLevel=*/1, ArgRegion, os, PP);
+
+    // Return nothing if we have failed to pretty-print.
+    if (!out)
+      return nullptr;
+
+    os << "'";
+    PathDiagnosticLocation L =
+        getPathDiagnosticLocation(nullptr, SM, Ctx, Call);
+    return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+  }
+
+  /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced
+  /// before we get to the super region of \c RegionOfInterest
+  std::shared_ptr<PathDiagnosticPiece>
+  notModifiedDiagnostics(const LocationContext *Ctx,
+                         const SourceManager &SM,
+                         const PrintingPolicy &PP,
+                         CallExitBegin &CallExitLoc,
+                         CallEventRef<> Call,
+                         const ParmVarDecl *PVD,
+                         const MemRegion *ArgRegion,
+                         unsigned IndirectionLevel) {
+
+    PathDiagnosticLocation L = getPathDiagnosticLocation(
+        CallExitLoc.getReturnStmt(), SM, Ctx, Call);
+    SmallString<256> sbuf;
+    llvm::raw_svector_ostream os(sbuf);
+    os << DiagnosticsMsg;
+    bool IsReference = PVD->getType()->isReferenceType();
+    const char *Sep = IsReference && IndirectionLevel == 1 ? "." : "->";
+    bool Success = prettyPrintRegionName(
+        PVD->getQualifiedNameAsString().c_str(),
+        Sep, IsReference, IndirectionLevel, ArgRegion, os, PP);
+
+    // Print the parameter name if the pretty-printing has failed.
+    if (!Success)
+      PVD->printQualifiedName(os);
+    os << "'";
+    return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+  }
+
+  /// \return a path diagnostic location for the optionally
+  /// present return statement \p RS.
+  PathDiagnosticLocation getPathDiagnosticLocation(const ReturnStmt *RS,
+                                                   const SourceManager &SM,
+                                                   const LocationContext *Ctx,
+                                                   CallEventRef<> Call) {
+    if (RS)
+      return PathDiagnosticLocation::createBegin(RS, SM, Ctx);
+    return PathDiagnosticLocation(
+        Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
+  }
+
+  /// Pretty-print region \p ArgRegion starting from parent to \p os.
+  /// \return whether printing has succeeded
+  bool prettyPrintRegionName(const char *TopRegionName,
+                             const char *Sep,
+                             bool IsReference,
+                             int IndirectionLevel,
+                             const MemRegion *ArgRegion,
+                             llvm::raw_svector_ostream &os,
+                             const PrintingPolicy &PP) {
+    SmallVector<const MemRegion *, 5> Subregions;
+    const MemRegion *R = RegionOfInterest;
+    while (R != ArgRegion) {
+      if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R)))
+        return false; // Pattern-matching failed.
+      Subregions.push_back(R);
+      R = dyn_cast<SubRegion>(R)->getSuperRegion();
+    }
+    bool IndirectReference = !Subregions.empty();
+
+    if (IndirectReference)
+      IndirectionLevel--; // Due to "->" symbol.
+
+    if (IsReference)
+      IndirectionLevel--; // Due to reference semantics.
+
+    bool ShouldSurround = IndirectReference && IndirectionLevel > 0;
+
+    if (ShouldSurround)
+      os << "(";
+    for (int i=0; i<IndirectionLevel; i++)
+      os << "*";
+    os << TopRegionName;
+    if (ShouldSurround)
+      os << ")";
+
+    for (auto I = Subregions.rbegin(), E = Subregions.rend(); I != E; ++I) {
+      if (auto *FR = dyn_cast<FieldRegion>(*I)) {
+        os << Sep;
+        FR->getDecl()->getDeclName().print(os, PP);
+        Sep = ".";
+      } else if (auto *CXXR = dyn_cast<CXXBaseObjectRegion>(*I)) {
+        continue; // Just keep going up to the base region.
+      } else {
+        llvm_unreachable("Previous check has missed an unexpected region");
+      }
+    }
+    return true;
+  }
+};
+
+} // namespace
+
+namespace {
+
 class MacroNullReturnSuppressionVisitor final
     : public BugReporterVisitorImpl<MacroNullReturnSuppressionVisitor> {
 
@@ -1215,6 +1485,8 @@ bool bugreporter::trackNullOrUndefValue(
     if (R) {
       // Mark both the variable region and its contents as interesting.
       SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
+      report.addVisitor(
+          llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R)));
 
       MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
           N, R, EnableNullFPSuppression, report, V);

Added: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c?rev=325976&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c (added)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c Fri Feb 23 
15:26:56 2018
@@ -0,0 +1,226 @@
+// RUN: %clang_analyze_cc1 -x c -analyzer-checker=core -analyzer-output=text 
-verify %s
+
+typedef __typeof(sizeof(int)) size_t;
+void *memset(void *__s, int __c, size_t __n);
+
+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}}
+}
+
+int param_initialized_properly() {
+  int p;
+  int out = initializer1(&p, 1);
+  return p; //no-warning
+}
+
+static int global;
+
+int initializer2(int **p, int x) {
+  if (x) { // expected-note{{Taking false branch}}
+    *p = &global;
+    return 0;
+  } else {
+    return 1; // expected-note {{Returning without writing to '*p'}}
+  }
+}
+
+int param_not_written_into_by_func() {
+  int *p = 0;                    // expected-note{{'p' initialized to a null 
pointer value}}
+  int out = initializer2(&p, 0); // expected-note{{Calling 'initializer2'}}
+                                 // expected-note@-1{{Returning from 
'initializer2'}}
+  return *p;                     // expected-warning{{Dereference of null 
pointer (loaded from variable 'p')}}
+                                 // expected-note@-1{{Dereference of null 
pointer (loaded from variable 'p')}}
+}
+
+void initializer3(int *p, int param) {
+  if (param) // expected-note{{Taking false branch}}
+    *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+int param_written_into_by_void_func() {
+  int p;               // expected-note{{'p' declared without an initial 
value}}
+  initializer3(&p, 0); // expected-note{{Calling 'initializer3'}}
+                       // expected-note@-1{{Returning from 'initializer3'}}
+  return p;            // expected-warning{{Undefined or garbage value 
returned to caller}}
+                       // expected-note@-1{{Undefined or garbage value 
returned to caller}}
+}
+
+void initializer4(int *p, int param) {
+  if (param) // expected-note{{Taking false branch}}
+    *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+void initializer5(int *p, int param) {
+  if (!param) // expected-note{{Taking false branch}}
+    *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+int multi_init_tries_func() {
+  int p;               // expected-note{{'p' declared without an initial 
value}}
+  initializer4(&p, 0); // expected-note{{Calling 'initializer4'}}
+                       // expected-note@-1{{Returning from 'initializer4'}}
+  initializer5(&p, 1); // expected-note{{Calling 'initializer5'}}
+                       // expected-note@-1{{Returning from 'initializer5'}}
+  return p;            // expected-warning{{Undefined or garbage value 
returned to caller}}
+                       // expected-note@-1{{Undefined or garbage value 
returned to caller}}
+}
+
+int initializer6(const int *p) {
+  return 0;
+}
+
+int no_msg_on_const() {
+  int p; // expected-note{{'p' declared without an initial value}}
+  initializer6(&p);
+  return p; // expected-warning{{Undefined or garbage value returned to 
caller}}
+            // expected-note@-1{{Undefined or garbage value returned to 
caller}}
+}
+
+typedef struct {
+  int x;
+} S;
+
+int initializer7(S *s, int param) {
+  if (param) { // expected-note{{Taking false branch}}
+    s->x = 0;
+    return 0;
+  }
+  return 1; // expected-note{{Returning without writing to 's->x'}}
+}
+
+int initialize_struct_field() {
+  S local;
+  initializer7(&local, 0); // expected-note{{Calling 'initializer7'}}
+                           // expected-note@-1{{Returning from 'initializer7'}}
+  return local.x;          // expected-warning{{Undefined or garbage value 
returned to caller}}
+                           // expected-note@-1{{Undefined or garbage value 
returned to caller}}
+}
+
+void nullwriter(int **p) {
+  *p = 0; // expected-note{{Null pointer value stored to 'p'}}
+} // no extra note
+
+int usage() {
+  int x = 0;
+  int *p = &x;
+  nullwriter(&p); // expected-note{{Calling 'nullwriter'}}
+                  // expected-note@-1{{Returning from 'nullwriter'}}
+  return *p;      // expected-warning{{Dereference of null pointer (loaded 
from variable 'p')}}
+                  // expected-note@-1{{Dereference of null pointer (loaded 
from variable 'p')}}
+}
+
+typedef struct {
+  int x;
+  int y;
+} A;
+
+void partial_initializer(A *a) {
+  a->x = 0;
+} // expected-note{{Returning without writing to 'a->y'}}
+
+int use_partial_initializer() {
+  A a;
+  partial_initializer(&a); // expected-note{{Calling 'partial_initializer'}}
+                           // expected-note@-1{{Returning from 
'partial_initializer'}}
+  return a.y;              // expected-warning{{Undefined or garbage value 
returned to caller}}
+                           // expected-note@-1{{Undefined or garbage value 
returned to caller}}
+}
+
+typedef struct {
+  int x;
+  int y;
+} B;
+
+typedef struct {
+  B b;
+} C;
+
+void partial_nested_initializer(C *c) {
+  c->b.x = 0;
+} // expected-note{{Returning without writing to 'c->b.y'}}
+
+int use_partial_nested_initializer() {
+  B localB;
+  C localC;
+  localC.b = localB;
+  partial_nested_initializer(&localC); // expected-note{{Calling 
'partial_nested_initializer'}}
+                                       // expected-note@-1{{Returning from 
'partial_nested_initializer'}}
+  return localC.b.y;                   // expected-warning{{Undefined or 
garbage value returned to caller}}
+                                       // expected-note@-1{{Undefined or 
garbage value returned to caller}}
+}
+
+void test_subregion_assignment(C* c) {
+  B b;
+  c->b = b;
+}
+
+int use_subregion_assignment() {
+  C c;
+  test_subregion_assignment(&c); // expected-note{{Calling 
'test_subregion_assignment'}}
+                                 // expected-note@-1{{Returning from 
'test_subregion_assignment'}}
+  return c.b.x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+                // expected-note@-1{{Undefined or garbage value returned to 
caller}}
+}
+
+int confusing_signature(int *);
+int confusing_signature(int *p) {
+  return 0; // expected-note{{Returning without writing to '*p'}}
+}
+
+int use_confusing_signature() {
+  int a; // expected-note {{'a' declared without an initial value}}
+  confusing_signature(&a); // expected-note{{Calling 'confusing_signature'}}
+                           // expected-note@-1{{Returning from 
'confusing_signature'}}
+  return a; // expected-note{{Undefined or garbage value returned to caller}}
+            // expected-warning@-1{{Undefined or garbage value returned to 
caller}}
+}
+
+int coin();
+
+int multiindirection(int **p) {
+  if (coin()) // expected-note{{Assuming the condition is true}}
+              // expected-note@-1{{Taking true branch}}
+    return 1; // expected-note{{Returning without writing to '**p'}}
+  *(*p) = 0;
+  return 0;
+}
+
+int usemultiindirection() {
+  int a; // expected-note {{'a' declared without an initial value}}
+  int *b = &a;
+  multiindirection(&b); // expected-note{{Calling 'multiindirection'}}
+                        // expected-note@-1{{Returning from 
'multiindirection'}}
+  return a; // expected-note{{Undefined or garbage value returned to caller}}
+            // expected-warning@-1{{Undefined or garbage value returned to 
caller}}
+}
+
+int indirectingstruct(S** 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 useindirectingstruct() {
+  S s;
+  S* p = &s;
+  indirectingstruct(&p); //expected-note{{Calling 'indirectingstruct'}}
+                         //expected-note@-1{{Returning from 
'indirectingstruct'}}
+  return s.x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+              // expected-note@-1{{Undefined or garbage value returned to 
caller}}
+}

Added: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp?rev=325976&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp (added)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp Fri Feb 23 
15:26:56 2018
@@ -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}}
+}

Added: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m?rev=325976&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m (added)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m Fri Feb 23 
15:26:56 2018
@@ -0,0 +1,46 @@
+// RUN: %clang_analyze_cc1 -x objective-c -analyzer-checker=core 
-analyzer-output=text -Wno-objc-root-class -fblocks -verify %s
+
+@interface I
+- (int)initVar:(int *)var param:(int)param;
+@end
+
+@implementation I
+- (int)initVar:(int *)var param:(int)param {
+  if (param) { // expected-note{{Taking false branch}}
+    *var = 1;
+    return 0;
+  }
+  return 1; // expected-note{{Returning without writing to '*var'}}
+}
+@end
+
+int foo(I *i) {
+  int x;                            //expected-note{{'x' declared without an 
initial value}}
+  int out = [i initVar:&x param:0]; //expected-note{{Calling 'initVar:param:'}}
+                                    //expected-note@-1{{Returning from 
'initVar:param:'}}
+  if (out)                          // expected-note{{Taking true branch}}
+    return x;                       //expected-warning{{Undefined or garbage 
value returned to caller}}
+                                    //expected-note@-1{{Undefined or garbage 
value returned to caller}}
+  return 0;
+}
+
+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 initFromBlock() {
+  __block int z;
+  ^{                     // expected-note {{Calling anonymous block}}
+    int p;               // expected-note{{'p' declared without an initial 
value}}
+    initializer1(&p, 0); // expected-note{{Calling 'initializer1'}}
+                         // expected-note@-1{{Returning from 'initializer1'}}
+    z = p;               // expected-warning{{Assigned value is garbage or 
undefined}}
+                         // expected-note@-1{{Assigned value is garbage or 
undefined}}
+  }();
+  return z;
+}

Modified: cfe/trunk/test/Analysis/diagnostics/undef-value-param.c
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/undef-value-param.c?rev=325976&r1=325975&r2=325976&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/undef-value-param.c (original)
+++ cfe/trunk/test/Analysis/diagnostics/undef-value-param.c Fri Feb 23 15:26:56 
2018
@@ -12,7 +12,7 @@ void foo(int c, int *x) {
     if (c)
            //expected-note@-1{{Assuming 'c' is not equal to 0}}
            //expected-note@-2{{Taking true branch}}
-        return;
+           return; // expected-note{{Returning without writing to '*x'}}
     *x = 5;
 }
 
@@ -51,7 +51,7 @@ void initStruct(int x, struct WithFields
   if (x <= 0) //expected-note {{Taking true branch}}
               //expected-note@-1 {{Assuming 'x' is <= 0}}
 
-    return;
+    return; //expected-note{{Returning without writing to 'X->f1'}}
   X->f1 = getValidPtr();
 }
 double testPassingParentRegionStruct(int x) {
@@ -293,12 +293,12 @@ double testPassingParentRegionStruct(int
 // CHECK-NEXT:          <array>
 // CHECK-NEXT:           <dict>
 // CHECK-NEXT:            <key>line</key><integer>15</integer>
-// CHECK-NEXT:            <key>col</key><integer>9</integer>
+// CHECK-NEXT:            <key>col</key><integer>12</integer>
 // CHECK-NEXT:            <key>file</key><integer>0</integer>
 // CHECK-NEXT:           </dict>
 // CHECK-NEXT:           <dict>
 // CHECK-NEXT:            <key>line</key><integer>15</integer>
-// CHECK-NEXT:            <key>col</key><integer>14</integer>
+// CHECK-NEXT:            <key>col</key><integer>17</integer>
 // CHECK-NEXT:            <key>file</key><integer>0</integer>
 // CHECK-NEXT:           </dict>
 // CHECK-NEXT:          </array>
@@ -309,6 +309,20 @@ double testPassingParentRegionStruct(int
 // CHECK-NEXT:      <key>kind</key><string>event</string>
 // CHECK-NEXT:      <key>location</key>
 // CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>15</integer>
+// CHECK-NEXT:       <key>col</key><integer>12</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>depth</key><integer>1</integer>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Returning without writing to 
&apos;*x&apos;</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Returning without writing to 
&apos;*x&apos;</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>event</string>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
 // CHECK-NEXT:       <key>line</key><integer>22</integer>
 // CHECK-NEXT:       <key>col</key><integer>5</integer>
 // CHECK-NEXT:       <key>file</key><integer>0</integer>
@@ -1044,6 +1058,20 @@ double testPassingParentRegionStruct(int
 // CHECK-NEXT:     </dict>
 // CHECK-NEXT:     <dict>
 // CHECK-NEXT:      <key>kind</key><string>event</string>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>54</integer>
+// CHECK-NEXT:       <key>col</key><integer>5</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>depth</key><integer>1</integer>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Returning without writing to 
&apos;X-&gt;f1&apos;</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Returning without writing to 
&apos;X-&gt;f1&apos;</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>event</string>
 // CHECK-NEXT:      <key>location</key>
 // CHECK-NEXT:      <dict>
 // CHECK-NEXT:       <key>line</key><integer>60</integer>

Modified: cfe/trunk/test/Analysis/diagnostics/undef-value-param.m
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/undef-value-param.m?rev=325976&r1=325975&r2=325976&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/undef-value-param.m (original)
+++ cfe/trunk/test/Analysis/diagnostics/undef-value-param.m Fri Feb 23 15:26:56 
2018
@@ -69,7 +69,7 @@ static void CreateRefUndef(SCDynamicStor
              //expected-note@-1{{Assuming 'err' is not equal to 0}}
              //expected-note@-2{{Taking true branch}}
     CFRelease(ref);
-    return;
+    return; // expected-note{{Returning without writing to '*storeRef'}}
   }
   *storeRef = ref;
 }
@@ -831,6 +831,54 @@ static void CreateRefUndef(SCDynamicStor
 // CHECK-NEXT:       </array>
 // CHECK-NEXT:     </dict>
 // CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>control</string>
+// CHECK-NEXT:      <key>edges</key>
+// CHECK-NEXT:       <array>
+// CHECK-NEXT:        <dict>
+// CHECK-NEXT:         <key>start</key>
+// CHECK-NEXT:          <array>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>71</integer>
+// CHECK-NEXT:            <key>col</key><integer>5</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>71</integer>
+// CHECK-NEXT:            <key>col</key><integer>13</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:          </array>
+// CHECK-NEXT:         <key>end</key>
+// CHECK-NEXT:          <array>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>72</integer>
+// CHECK-NEXT:            <key>col</key><integer>5</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:           <dict>
+// CHECK-NEXT:            <key>line</key><integer>72</integer>
+// CHECK-NEXT:            <key>col</key><integer>10</integer>
+// CHECK-NEXT:            <key>file</key><integer>0</integer>
+// CHECK-NEXT:           </dict>
+// CHECK-NEXT:          </array>
+// CHECK-NEXT:        </dict>
+// CHECK-NEXT:       </array>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>kind</key><string>event</string>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>72</integer>
+// CHECK-NEXT:       <key>col</key><integer>5</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>depth</key><integer>1</integer>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Returning without writing to 
&apos;*storeRef&apos;</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Returning without writing to 
&apos;*storeRef&apos;</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:     <dict>
 // CHECK-NEXT:      <key>kind</key><string>event</string>
 // CHECK-NEXT:      <key>location</key>
 // CHECK-NEXT:      <dict>


_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to