[clang] 39670ae - [clang][analyzer] Add and change NoteTags in StdLibraryFunctionsChecker.

Balázs Kéri via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 18 00:29:48 PDT 2023


Author: Balázs Kéri
Date: 2023-07-18T09:29:15+02:00
New Revision: 39670ae3b93470b2d29fe78e6d40c5d82a05e4a1

URL: https://github.com/llvm/llvm-project/commit/39670ae3b93470b2d29fe78e6d40c5d82a05e4a1
DIFF: https://github.com/llvm/llvm-project/commit/39670ae3b93470b2d29fe78e6d40c5d82a05e4a1.diff

LOG: [clang][analyzer] Add and change NoteTags in StdLibraryFunctionsChecker.

Change 1: ErrnoChecker notes show only messages related to errno,
not to assumption of success or failure of functions.
Change 2: StdLibraryFunctionsChecker adds its own note about success
or failure of functions, and the errno related note, independently.
Change 3: Every modeled function in StdLibraryFunctionsChecker
should have a note tag message in all "cases". This is not implemented yet,
only for file (stream) related functions.

Reviewed By: donat.nagy

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

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
    clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
    clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
    clang/test/Analysis/errno-stdlibraryfunctions-notes.c
    clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
    clang/test/Analysis/std-c-library-functions-arg-constraints.c
    clang/test/Analysis/std-c-library-functions-path-notes.c
    clang/test/Analysis/stream-errno-note.c
    clang/test/Analysis/stream-note.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
index 51f39c606d5c12..be2fa91b994a26 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
@@ -28,6 +28,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FormatVariadic.h"
 #include <optional>
 
 using namespace clang;
@@ -269,12 +270,6 @@ bool isErrno(const Decl *D) {
   return false;
 }
 
-const char *describeErrnoCheckState(ErrnoCheckState CS) {
-  assert(CS == errno_modeling::MustNotBeChecked &&
-         "Errno description not applicable.");
-  return "may be undefined after the call and should not be used";
-}
-
 const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
   return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
     const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
@@ -319,18 +314,14 @@ ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
 
 const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) {
   return getErrnoNoteTag(
-      C, (Twine("Assuming that function '") + Twine(Fn) +
-          Twine("' is successful, in this case the value 'errno' ") +
-          Twine(describeErrnoCheckState(MustNotBeChecked)))
-             .str());
+      C, llvm::formatv(
+             "'errno' may be undefined after successful call to '{0}'", Fn));
 }
 
 const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C,
                                              llvm::StringRef Fn) {
   return getErrnoNoteTag(
-      C, (Twine("Function '") + Twine(Fn) +
-          Twine("' indicates failure only by setting of 'errno'"))
-             .str());
+      C, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn));
 }
 
 } // namespace errno_modeling

diff  --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
index 2ca3979944e365..0707fd16d6e60a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
@@ -78,14 +78,6 @@ ProgramStateRef clearErrnoState(ProgramStateRef State);
 /// declaration.
 bool isErrno(const Decl *D);
 
-/// Produce a textual description about how \c errno is allowed to be used
-/// (in a \c ErrnoCheckState).
-/// The returned string is insertable into a longer warning message in the form
-/// "the value 'errno' <...>".
-/// Currently only the \c errno_modeling::MustNotBeChecked state is supported,
-/// others are not used by the clients.
-const char *describeErrnoCheckState(ErrnoCheckState CS);
-
 /// Create a NoteTag that displays the message if the 'errno' memory region is
 /// marked as interesting, and resets the interestingness.
 const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message);

diff  --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 0228e8278f057a..8d4fb12061c3d6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -52,6 +52,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
 
 #include <optional>
 #include <string>
@@ -1273,7 +1274,7 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
   // Now apply the constraints.
   const Summary &Summary = *FoundSummary;
   ProgramStateRef State = C.getState();
-  const ExplodedNode *Node = C.getPredecessor();
+  ExplodedNode *Node = C.getPredecessor();
 
   // Apply case/branch specifications.
   for (const SummaryCase &Case : Summary.getCases()) {
@@ -1287,35 +1288,59 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
     if (NewState)
       NewState = Case.getErrnoConstraint().apply(NewState, Call, Summary, C);
 
-    if (NewState && NewState != State) {
-      if (Case.getNote().empty()) {
-        const NoteTag *NT = nullptr;
-        if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
-          NT = Case.getErrnoConstraint().describe(C, D->getNameAsString());
-        C.addTransition(NewState, NT);
-      } else {
-        StringRef Note = Case.getNote();
+    if (!NewState)
+      continue;
+
+    // It is possible that NewState == State is true.
+    // It can occur if another checker has applied the state before us.
+    // Still add these note tags, the other checker should add only its
+    // specialized note tags. These general note tags are handled always by
+    // StdLibraryFunctionsChecker.
+    ExplodedNode *Pred = Node;
+    if (!Case.getNote().empty()) {
+      // If there is a description for this execution branch (summary case),
+      // use it as a note tag.
+      std::string Note =
+          llvm::formatv(Case.getNote().str().c_str(),
+                        cast<NamedDecl>(Call.getDecl())->getDeclName());
+      if (Summary.getInvalidationKd() == EvalCallAsPure) {
         const NoteTag *Tag = C.getNoteTag(
-            // Sorry couldn't help myself.
-            [Node, Note]() -> std::string {
-              // Don't emit "Assuming..." note when we ended up
-              // knowing in advance which branch is taken.
-              return (Node->succ_size() > 1) ? Note.str() : "";
+            [Node, Note](PathSensitiveBugReport &BR) -> std::string {
+              // Try to omit the note if we know in advance which branch is
+              // taken (this means, only one branch exists).
+              // This check is performed inside the lambda, after other
+              // (or this) checkers had a chance to add other successors.
+              // Dereferencing the saved node object is valid because it's part
+              // of a bug report call sequence.
+              // FIXME: This check is not exact. We may be here after a state
+              // split that was performed by another checker (and can not find
+              // the successors). This is why this check is only used in the
+              // EvalCallAsPure case.
+              if (Node->succ_size() > 1)
+                return Note;
+              return "";
             },
             /*IsPrunable=*/true);
-        C.addTransition(NewState, Tag);
+        Pred = C.addTransition(NewState, Pred, Tag);
+      } else {
+        const NoteTag *Tag = C.getNoteTag(Note, /*IsPrunable=*/true);
+        Pred = C.addTransition(NewState, Pred, Tag);
       }
-    } else if (NewState == State) {
-      // It is possible that the function was evaluated in a checker callback
-      // where the state constraints are already applied, then no change happens
-      // here to the state (if the ErrnoConstraint did not change it either).
-      // If the evaluated function requires a NoteTag for errno change, it is
-      // added here.
-      if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
-        if (const NoteTag *NT =
-                Case.getErrnoConstraint().describe(C, D->getNameAsString()))
-          C.addTransition(NewState, NT);
+      if (!Pred)
+        break;
     }
+
+    // If we can get a note tag for the errno change, add this additionally to
+    // the previous. This note is only about value of 'errno' and is displayed
+    // if 'errno' is interesting.
+    if (const auto *D = dyn_cast<FunctionDecl>(Call.getDecl()))
+      if (const NoteTag *NT =
+              Case.getErrnoConstraint().describe(C, D->getNameAsString()))
+        Pred = C.addTransition(NewState, Pred, NT);
+
+    // Add the transition if no note tag could be added.
+    if (Pred == Node && NewState != State)
+      C.addTransition(NewState);
   }
 }
 
@@ -1660,6 +1685,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
   std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy));
   std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy);
 
+  constexpr llvm::StringLiteral GenericSuccessMsg(
+      "Assuming that '{0}' is successful");
+  constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails");
+
   // We are finally ready to define specifications for all supported functions.
   //
   // Argument ranges should always cover all variants. If return value
@@ -1892,14 +1921,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
                  ArgumentCondition(2U, WithinRange, Range(1, SizeMax)),
                  ReturnValueCondition(BO_LT, ArgNo(2)),
                  ReturnValueCondition(WithinRange, Range(0, SizeMax))},
-                ErrnoNEZeroIrrelevant)
+                ErrnoNEZeroIrrelevant, GenericFailureMsg)
           .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)),
                  ReturnValueCondition(BO_EQ, ArgNo(2)),
                  ReturnValueCondition(WithinRange, Range(0, SizeMax))},
-                ErrnoMustNotBeChecked)
+                ErrnoMustNotBeChecked, GenericSuccessMsg)
           .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)),
                  ReturnValueCondition(WithinRange, SingleValue(0))},
-                ErrnoMustNotBeChecked)
+                ErrnoMustNotBeChecked, GenericSuccessMsg)
           .ArgConstraint(NotNull(ArgNo(0)))
           .ArgConstraint(NotNull(ArgNo(3)))
           // FIXME: It should be allowed to have a null buffer if any of
@@ -2016,17 +2045,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
         Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy},
                   RetType{FilePtrTy}),
         Summary(NoEvalCall)
-            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked)
-            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)
+            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0)))
             .ArgConstraint(NotNull(ArgNo(1))));
 
     // FILE *tmpfile(void);
-    addToFunctionSummaryMap("tmpfile",
-                            Signature(ArgTypes{}, RetType{FilePtrTy}),
-                            Summary(NoEvalCall)
-                                .Case({NotNull(Ret)}, ErrnoMustNotBeChecked)
-                                .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant));
+    addToFunctionSummaryMap(
+        "tmpfile", Signature(ArgTypes{}, RetType{FilePtrTy}),
+        Summary(NoEvalCall)
+            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg));
 
     // FILE *freopen(const char *restrict pathname, const char *restrict mode,
     //               FILE *restrict stream);
@@ -2037,8 +2066,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
                   RetType{FilePtrTy}),
         Summary(NoEvalCall)
             .Case({ReturnValueCondition(BO_EQ, ArgNo(2))},
-                  ErrnoMustNotBeChecked)
-            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)
+                  ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(1)))
             .ArgConstraint(NotNull(ArgNo(2))));
 
@@ -2046,9 +2075,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
     addToFunctionSummaryMap(
         "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
         Summary(NoEvalCall)
-            .Case(ReturnsZero, ErrnoMustNotBeChecked)
+            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
             .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))},
-                  ErrnoNEZeroIrrelevant)
+                  ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0))));
 
     // int fseek(FILE *stream, long offset, int whence);
@@ -2058,8 +2087,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
     addToFunctionSummaryMap(
         "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}),
         Summary(NoEvalCall)
-            .Case(ReturnsZero, ErrnoMustNotBeChecked)
-            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0)))
             .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));
 
@@ -2072,8 +2101,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
         Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy},
                   RetType{IntTy}),
         Summary(NoEvalCall)
-            .Case(ReturnsZero, ErrnoUnchanged)
-            .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant)
+            .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg)
+            .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0)))
             .ArgConstraint(NotNull(ArgNo(1))));
 
@@ -2085,8 +2114,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
         "fsetpos",
         Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}),
         Summary(NoEvalCall)
-            .Case(ReturnsZero, ErrnoUnchanged)
-            .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant)
+            .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg)
+            .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0)))
             .ArgConstraint(NotNull(ArgNo(1))));
 
@@ -2098,16 +2127,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
         "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}),
         Summary(NoEvalCall)
             .Case({ReturnValueCondition(WithinRange, Range(1, LongMax))},
-                  ErrnoUnchanged)
-            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+                  ErrnoUnchanged, GenericSuccessMsg)
+            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0))));
 
     // int fileno(FILE *stream);
     addToFunctionSummaryMap(
         "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
         Summary(NoEvalCall)
-            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked)
-            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+                  GenericSuccessMsg)
+            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0))));
 
     // void rewind(FILE *stream);
@@ -2149,8 +2179,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
     addToFunctionSummaryMap(
         "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}),
         Summary(NoEvalCall)
-            .Case(ReturnsZero, ErrnoMustNotBeChecked)
-            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
             .ArgConstraint(NotNull(ArgNo(0))));
 
     // int faccessat(int dirfd, const char *pathname, int mode, int flags);

diff  --git a/clang/test/Analysis/errno-stdlibraryfunctions-notes.c b/clang/test/Analysis/errno-stdlibraryfunctions-notes.c
index 4172935e1e3f6a..e520e4fa76497f 100644
--- a/clang/test/Analysis/errno-stdlibraryfunctions-notes.c
+++ b/clang/test/Analysis/errno-stdlibraryfunctions-notes.c
@@ -13,9 +13,11 @@ int access(const char *path, int amode);
 void clang_analyzer_warnIfReached();
 
 void test1() {
-  access("path", 0); // no note here
   access("path", 0);
-  // expected-note at -1{{Assuming that function 'access' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
+  // expected-note at -1{{Assuming that 'access' fails}}
+  access("path", 0);
+  // expected-note at -1{{Assuming that 'access' is successful}}
+  // expected-note at -2{{'errno' may be undefined after successful call to 'access'}}
   if (errno != 0) {
     // expected-warning at -1{{An undefined value may be read from 'errno'}}
     // expected-note at -2{{An undefined value may be read from 'errno'}}
@@ -24,7 +26,8 @@ void test1() {
 
 void test2() {
   if (access("path", 0) == -1) {
-    // expected-note at -1{{Taking true branch}}
+    // expected-note at -1{{Assuming that 'access' fails}}
+    // expected-note at -2{{Taking true branch}}
     // Failure path.
     if (errno != 0) {
       // expected-note at -1{{'errno' is not equal to 0}}
@@ -39,8 +42,9 @@ void test2() {
 void test3() {
   if (access("path", 0) != -1) {
     // Success path.
-    // expected-note at -2{{Assuming that function 'access' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
-    // expected-note at -3{{Taking true branch}}
+    // expected-note at -2{{Assuming that 'access' is successful}}
+    // expected-note at -3{{'errno' may be undefined after successful call to 'access'}}
+    // expected-note at -4{{Taking true branch}}
     if (errno != 0) {
       // expected-warning at -1{{An undefined value may be read from 'errno'}}
       // expected-note at -2{{An undefined value may be read from 'errno'}}

diff  --git a/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp b/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
index f31ce589072abc..fc7116d3e669ce 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
@@ -52,6 +52,7 @@ void test_buffer_size_note(char *buf, int y) {
 int __test_case_note();
 
 int test_case_note_1(int y) {
+  int x0 = __test_case_note(); // expected-note{{Function returns 1}}
   int x = __test_case_note(); // expected-note{{Function returns 0}} \
                               // expected-note{{'x' initialized here}}
   return y / x; // expected-warning{{Division by zero}} \

diff  --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
index 6a5f9454fd1ee6..2fdea1fecd831e 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -170,7 +170,8 @@ void test_notnull_concrete(FILE *fp) {
   // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}}
 }
 void test_notnull_symbolic(FILE *fp, int *buf) {
-  fread(buf, sizeof(int), 10, fp);
+  fread(buf, sizeof(int), 10, fp); // \
+  // bugpath-note{{'fread' fails}}
   clang_analyzer_eval(buf != 0); // \
   // report-warning{{TRUE}} \
   // bugpath-warning{{TRUE}} \

diff  --git a/clang/test/Analysis/std-c-library-functions-path-notes.c b/clang/test/Analysis/std-c-library-functions-path-notes.c
index 98772fa6a11e59..33d6152376c57c 100644
--- a/clang/test/Analysis/std-c-library-functions-path-notes.c
+++ b/clang/test/Analysis/std-c-library-functions-path-notes.c
@@ -1,7 +1,9 @@
 // RUN: %clang_analyze_cc1 -verify %s \
 // RUN:     -analyzer-checker=core,alpha.unix.StdCLibraryFunctions \
+// RUN:     -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true \
 // RUN:     -analyzer-output=text
 
+#include "Inputs/std-c-library-functions-POSIX.h"
 #define NULL ((void *)0)
 
 char *getenv(const char *);
@@ -19,10 +21,12 @@ char test_getenv() {
   // expected-note   {{Array access (from variable 'env') results in a null pointer dereference}}
 }
 
-int test_isalpha(int *x, char c) {
+int test_isalpha(int *x, char c, char d) {
+  int iad = isalpha(d);// \
+  // expected-note{{Assuming the character is non-alphabetical}}
   if (isalpha(c)) {// \
-    // expected-note{{Assuming the character is alphabetical}} \
-    // expected-note{{Taking true branch}}
+    // expected-note{{Taking true branch}} \
+    // expected-note{{Assuming the character is alphabetical}}
     x = NULL; // \
     // expected-note{{Null pointer value stored to 'x'}}
   }
@@ -34,8 +38,8 @@ int test_isalpha(int *x, char c) {
 
 int test_isdigit(int *x, char c) {
   if (!isdigit(c)) {// \
-    // expected-note{{Assuming the character is not a digit}} \
-    // expected-note{{Taking true branch}}
+  // expected-note{{Assuming the character is not a digit}} \
+  // expected-note{{Taking true branch}}
     x = NULL; // \
     // expected-note{{Null pointer value stored to 'x'}}
   }
@@ -58,3 +62,18 @@ int test_islower(int *x) {
   // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} \
   // expected-note   {{Dereference of null pointer (loaded from variable 'x')}}
 }
+
+int test_bugpath_notes(FILE *f1, char c, FILE *f2) {
+  int f = fileno(f2); // \
+  // expected-note{{Assuming that 'fileno' is successful}}
+  if (f == -1) // \
+    // expected-note{{Taking false branch}}
+    return 0;
+  int l = islower(c);
+  f = fileno(f1); // \
+  // expected-note{{Value assigned to 'f'}} \
+  // expected-note{{Assuming that 'fileno' fails}}
+  return dup(f); // \
+  // expected-warning{{The 1st argument to 'dup' is -1 but should be >= 0}} \
+  // expected-note{{The 1st argument to 'dup' is -1 but should be >= 0}}
+}

diff  --git a/clang/test/Analysis/stream-errno-note.c b/clang/test/Analysis/stream-errno-note.c
index 111841efe0e5a1..d8aefbabd0b81c 100644
--- a/clang/test/Analysis/stream-errno-note.c
+++ b/clang/test/Analysis/stream-errno-note.c
@@ -10,7 +10,8 @@
 
 void check_fopen(void) {
   FILE *F = fopen("xxx", "r");
-  // expected-note at -1{{Assuming that function 'fopen' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fopen'}}
+  // expected-note at -2{{'fopen' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
@@ -22,7 +23,8 @@ void check_fopen(void) {
 
 void check_tmpfile(void) {
   FILE *F = tmpfile();
-  // expected-note at -1{{Assuming that function 'tmpfile' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'tmpfile'}}
+  // expected-note at -2{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
@@ -34,12 +36,14 @@ void check_tmpfile(void) {
 
 void check_freopen(void) {
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   F = freopen("xxx", "w", F);
-  // expected-note at -1{{Assuming that function 'freopen' is successful}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'freopen'}}
+  // expected-note at -2{{'freopen' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
@@ -51,12 +55,14 @@ void check_freopen(void) {
 
 void check_fclose(void) {
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   (void)fclose(F);
-  // expected-note at -1{{Assuming that function 'fclose' is successful}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fclose'}}
+  // expected-note at -2{{'fclose' is successful}}
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
   // expected-note at -1{{An undefined value may be read from 'errno'}}
 }
@@ -64,26 +70,60 @@ void check_fclose(void) {
 void check_fread(void) {
   char Buf[10];
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   (void)fread(Buf, 1, 10, F);
-  // expected-note at -1{{Assuming that function 'fread' is successful}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fread'}}
+  // expected-note at -2{{'fread' is successful}}
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
   // expected-note at -1{{An undefined value may be read from 'errno'}}
   (void)fclose(F);
 }
 
+void check_fread_size0(void) {
+  char Buf[10];
+  FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
+  // expected-note at +2{{'F' is non-null}}
+  // expected-note at +1{{Taking false branch}}
+  if (!F)
+    return;
+  fread(Buf, 0, 1, F);
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fread'}}
+  // expected-note at -2{{'fread' is successful}}
+  if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+  // expected-note at -1{{An undefined value may be read from 'errno'}}
+}
+
+void check_fread_nmemb0(void) {
+  char Buf[10];
+  FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
+  // expected-note at +2{{'F' is non-null}}
+  // expected-note at +1{{Taking false branch}}
+  if (!F)
+    return;
+  fread(Buf, 1, 0, F);
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fread'}}
+  // expected-note at -2{{'fread' is successful}}
+  if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+  // expected-note at -1{{An undefined value may be read from 'errno'}}
+}
+
 void check_fwrite(void) {
   char Buf[] = "0123456789";
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   int R = fwrite(Buf, 1, 10, F);
-  // expected-note at -1{{Assuming that function 'fwrite' is successful}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fwrite'}}
+  // expected-note at -2{{'fwrite' is successful}}
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
   // expected-note at -1{{An undefined value may be read from 'errno'}}
   (void)fclose(F);
@@ -91,12 +131,14 @@ void check_fwrite(void) {
 
 void check_fseek(void) {
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   (void)fseek(F, 11, SEEK_SET);
-  // expected-note at -1{{Assuming that function 'fseek' is successful}}
+  // expected-note at -1{{'errno' may be undefined after successful call to 'fseek'}}
+  // expected-note at -2{{'fseek' is successful}}
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
   // expected-note at -1{{An undefined value may be read from 'errno'}}
   (void)fclose(F);
@@ -104,24 +146,27 @@ void check_fseek(void) {
 
 void check_rewind_errnocheck(void) {
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   errno = 0;
-  rewind(F); // expected-note{{Function 'rewind' indicates failure only by setting of 'errno'}}
+  rewind(F); // expected-note{{'rewind' indicates failure only by setting 'errno'}}
   fclose(F); // expected-warning{{Value of 'errno' was not checked and may be overwritten by function 'fclose' [alpha.unix.Errno]}}
   // expected-note at -1{{Value of 'errno' was not checked and may be overwritten by function 'fclose'}}
 }
 
 void check_fileno(void) {
   FILE *F = tmpfile();
+  // expected-note at -1{{'tmpfile' is successful}}
   // expected-note at +2{{'F' is non-null}}
   // expected-note at +1{{Taking false branch}}
   if (!F)
     return;
   fileno(F);
-  // expected-note at -1{{Assuming that function 'fileno' is successful}}
+  // expected-note at -1{{Assuming that 'fileno' is successful}}
+  // expected-note at -2{{'errno' may be undefined after successful call to 'fileno'}}
   if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
   // expected-note at -1{{An undefined value may be read from 'errno'}}
   (void)fclose(F);

diff  --git a/clang/test/Analysis/stream-note.c b/clang/test/Analysis/stream-note.c
index 6466630782f5d8..8d297715b03a3a 100644
--- a/clang/test/Analysis/stream-note.c
+++ b/clang/test/Analysis/stream-note.c
@@ -7,11 +7,13 @@
 
 void check_note_at_correct_open(void) {
   FILE *F1 = tmpfile(); // expected-note {{Stream opened here}}
+  // stdargs-note at -1 {{'tmpfile' is successful}}
   if (!F1)
     // expected-note at -1 {{'F1' is non-null}}
     // expected-note at -2 {{Taking false branch}}
     return;
   FILE *F2 = tmpfile();
+  // stdargs-note at -1 {{'tmpfile' is successful}}
   if (!F2) {
     // expected-note at -1 {{'F2' is non-null}}
     // expected-note at -2 {{Taking false branch}}
@@ -20,6 +22,7 @@ void check_note_at_correct_open(void) {
   }
   rewind(F2);
   fclose(F2);
+  // stdargs-note at -1 {{'fclose' fails}}
   rewind(F1);
 }
 // expected-warning at -1 {{Opened stream never closed. Potential resource leak}}
@@ -27,6 +30,7 @@ void check_note_at_correct_open(void) {
 
 void check_note_fopen(void) {
   FILE *F = fopen("file", "r"); // expected-note {{Stream opened here}}
+  // stdargs-note at -1 {{'fopen' is successful}}
   if (!F)
     // expected-note at -1 {{'F' is non-null}}
     // expected-note at -2 {{Taking false branch}}
@@ -37,11 +41,13 @@ void check_note_fopen(void) {
 
 void check_note_freopen(void) {
   FILE *F = fopen("file", "r"); // expected-note {{Stream opened here}}
+  // stdargs-note at -1 {{'fopen' is successful}}
   if (!F)
     // expected-note at -1 {{'F' is non-null}}
     // expected-note at -2 {{Taking false branch}}
     return;
   F = freopen(0, "w", F); // expected-note {{Stream reopened here}}
+  // stdargs-note at -1 {{'freopen' is successful}}
   if (!F)
     // expected-note at -1 {{'F' is non-null}}
     // expected-note at -2 {{Taking false branch}}
@@ -52,6 +58,8 @@ void check_note_freopen(void) {
 
 void check_note_leak_2(int c) {
   FILE *F1 = fopen("foo1.c", "r"); // expected-note {{Stream opened here}}
+  // stdargs-note at -1 {{'fopen' is successful}}
+  // stdargs-note at -2 {{'fopen' is successful}}
   if (!F1)
     // expected-note at -1 {{'F1' is non-null}}
     // expected-note at -2 {{Taking false branch}}
@@ -59,6 +67,8 @@ void check_note_leak_2(int c) {
     // expected-note at -4 {{Taking false branch}}
     return;
   FILE *F2 = fopen("foo2.c", "r"); // expected-note {{Stream opened here}}
+  // stdargs-note at -1 {{'fopen' is successful}}
+  // stdargs-note at -2 {{'fopen' is successful}}
   if (!F2) {
     // expected-note at -1 {{'F2' is non-null}}
     // expected-note at -2 {{Taking false branch}}
@@ -84,6 +94,7 @@ void check_note_leak_2(int c) {
 void check_track_null(void) {
   FILE *F;
   F = fopen("foo1.c", "r"); // expected-note {{Value assigned to 'F'}} expected-note {{Assuming pointer value is null}}
+  // stdargs-note at -1 {{'fopen' fails}}
   if (F != NULL) {          // expected-note {{Taking false branch}} expected-note {{'F' is equal to NULL}}
     fclose(F);
     return;
@@ -96,13 +107,16 @@ void check_eof_notes_feof_after_feof(void) {
   FILE *F;
   char Buf[10];
   F = fopen("foo1.c", "r");
+  // stdargs-note at -1 {{'fopen' is successful}}
   if (F == NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
     return;
   }
   fread(Buf, 1, 1, F);
+  // stdargs-note at -1 {{'fread' fails}}
   if (feof(F)) { // expected-note {{Taking true branch}}
     clearerr(F);
     fread(Buf, 1, 1, F);   // expected-note {{Assuming stream reaches end-of-file here}}
+    // stdargs-note at -1 {{'fread' fails}}
     if (feof(F)) {         // expected-note {{Taking true branch}}
       fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
       // expected-note at -1 {{Read function called when stream is in EOF state. Function has no effect}}
@@ -115,10 +129,12 @@ void check_eof_notes_feof_after_no_feof(void) {
   FILE *F;
   char Buf[10];
   F = fopen("foo1.c", "r");
+  // stdargs-note at -1 {{'fopen' is successful}}
   if (F == NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
     return;
   }
   fread(Buf, 1, 1, F);
+  // stdargs-note at -1 {{'fread' is successful}}
   if (feof(F)) { // expected-note {{Taking false branch}}
     fclose(F);
     return;
@@ -127,6 +143,7 @@ void check_eof_notes_feof_after_no_feof(void) {
     return;
   }
   fread(Buf, 1, 1, F);   // expected-note {{Assuming stream reaches end-of-file here}}
+  // stdargs-note at -1 {{'fread' fails}}
   if (feof(F)) {         // expected-note {{Taking true branch}}
     fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
     // expected-note at -1 {{Read function called when stream is in EOF state. Function has no effect}}
@@ -138,9 +155,11 @@ void check_eof_notes_feof_or_no_error(void) {
   FILE *F;
   char Buf[10];
   F = fopen("foo1.c", "r");
+  // stdargs-note at -1 {{'fopen' is successful}}
   if (F == NULL) // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
     return;
   int RRet = fread(Buf, 1, 1, F); // expected-note {{Assuming stream reaches end-of-file here}}
+  // stdargs-note at -1 {{'fread' fails}}
   if (ferror(F)) {                // expected-note {{Taking false branch}}
   } else {
     fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}


        


More information about the cfe-commits mailing list