balazske created this revision.
Herald added subscribers: steakhal, manas, ASDenysPetrov, martong, gamesh411, 
dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, 
baloghadamsoftware, xazax.hun.
Herald added a reviewer: Szelethus.
Herald added a reviewer: NoQ.
Herald added a project: All.
balazske requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Add an additional explanation of what is wrong if a constraint is
not satisfied, in some cases.
Additionally the bug report generation is changed to use raw_ostream.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D144003

Files:
  clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
  clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp

Index: clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp
===================================================================
--- clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp
+++ clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp
@@ -78,34 +78,36 @@
 int __range_out_1_2__4_6(int); // [1, 2], [4, 6]
 int __range_out_1_2__4_inf(int); // [1, 2], [4, inf]
 
+int __test_case_range_1_2__4_6(int);
+
 void test_range_values(int x) {
   switch (x) {
   case 0:
-    __single_val_0(1); // expected-warning{{should be zero}}
+    __single_val_0(1); // expected-warning{{should be zero but is 1}}
     break;
   case 1:
-    __single_val_1(2); // expected-warning{{should be 1}}
+    __single_val_1(2); // expected-warning{{should be 1 but is 2}}
     break;
   case 2:
-    __range_1_2(3); // expected-warning{{should be 1 or 2}}
+    __range_1_2(3); // expected-warning{{should be 1 or 2 but is 3}}
     break;
   case 3:
-    __range_m1_1(3); // expected-warning{{should be between -1 and 1}}
+    __range_m1_1(3); // expected-warning{{should be between -1 and 1 but is 3}}
     break;
   case 4:
-    __range_m2_m1(1); // expected-warning{{should be -2 or -1}}
+    __range_m2_m1(1); // expected-warning{{should be -2 or -1 but is 1}}
     break;
   case 5:
-    __range_m10_10(11); // expected-warning{{should be between -10 and 10}}
+    __range_m10_10(11); // expected-warning{{should be between -10 and 10 but is 11}}
     break;
   case 6:
-    __range_m10_10(-11); // expected-warning{{should be between -10 and 10}}
+    __range_m10_10(-11); // expected-warning{{should be between -10 and 10 but is -11}}
     break;
   case 7:
-    __range_1_2__4_6(3); // expected-warning{{should be 1 or 2 or 4, 5 or 6}}
+    __range_1_2__4_6(3); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3}}
     break;
   case 8:
-    __range_1_2__4_inf(3); // expected-warning{{should be 1 or 2 or >= 4}}
+    __range_1_2__4_inf(3); // expected-warning{{should be 1 or 2 or >= 4 but is 3}}
     break;
   }
 }
@@ -113,22 +115,22 @@
 void test_range_values_inf(int x) {
   switch (x) {
   case 1:
-    __range_m1_inf(-2); // expected-warning{{should be >= -1}}
+    __range_m1_inf(-2); // expected-warning{{should be >= -1 but is -2}}
     break;
   case 2:
-    __range_0_inf(-1); // expected-warning{{should be >= 0}}
+    __range_0_inf(-1); // expected-warning{{should be >= 0 but is -1}}
     break;
   case 3:
-    __range_1_inf(0); // expected-warning{{should be > 0}}
+    __range_1_inf(0); // expected-warning{{should be > 0 but is 0}}
     break;
   case 4:
-    __range_minf_m1(0); // expected-warning{{should be < 0}}
+    __range_minf_m1(0); // expected-warning{{should be < 0 but is 0}}
     break;
   case 5:
-    __range_minf_0(1); // expected-warning{{should be <= 0}}
+    __range_minf_0(1); // expected-warning{{should be <= 0 but is 1}}
     break;
   case 6:
-    __range_minf_1(2); // expected-warning{{should be <= 1}}
+    __range_minf_1(2); // expected-warning{{should be <= 1 but is 2}}
     break;
   }
 }
@@ -136,28 +138,28 @@
 void test_range_values_out(int x) {
   switch (x) {
   case 0:
-    __single_val_out_0(0); // expected-warning{{should be nonzero}}
+    __single_val_out_0(0); // expected-warning{{should be nonzero but is 0}}
     break;
   case 1:
-    __single_val_out_1(1); // expected-warning{{should be not equal to 1}}
+    __single_val_out_1(1); // expected-warning{{should be not equal to 1 but is 1}}
     break;
   case 2:
-    __range_out_1_2(2); // expected-warning{{should be not 1 and not 2}}
+    __range_out_1_2(2); // expected-warning{{should be not 1 and not 2 but is 2}}
     break;
   case 3:
-    __range_out_m1_1(-1); // expected-warning{{should be not between -1 and 1}}
+    __range_out_m1_1(-1); // expected-warning{{should be not between -1 and 1 but is -1}}
     break;
   case 4:
-    __range_out_m2_m1(-2); // expected-warning{{should be not -2 and not -1}}
+    __range_out_m2_m1(-2); // expected-warning{{should be not -2 and not -1 but is -2}}
     break;
   case 5:
-    __range_out_m10_10(0); // expected-warning{{should be not between -10 and 10}}
+    __range_out_m10_10(0); // expected-warning{{should be not between -10 and 10 but is 0}}
     break;
   case 6:
-    __range_out_1_2__4_6(1); // expected-warning{{should be not 1 and not 2 and not between 4 and 6}}
+    __range_out_1_2__4_6(1); // expected-warning{{should be not 1 and not 2 and not between 4 and 6 but is 1}}
     break;
   case 7:
-    __range_out_1_2__4_inf(4); // expected-warning{{should be not 1 and not 2 and < 4}}
+    __range_out_1_2__4_inf(4); // expected-warning{{should be not 1 and not 2 and < 4 but is 4}}
     break;
   }
 }
@@ -165,22 +167,71 @@
 void test_range_values_out_inf(int x) {
   switch (x) {
   case 1:
-    __range_out_minf_m1(-1); // expected-warning{{should be >= 0}}
+    __range_out_minf_m1(-1); // expected-warning{{should be >= 0 but is -1}}
     break;
   case 2:
-    __range_out_minf_0(0); // expected-warning{{should be > 0}}
+    __range_out_minf_0(0); // expected-warning{{should be > 0 but is 0}}
     break;
   case 3:
-    __range_out_minf_1(1); // expected-warning{{should be > 1}}
+    __range_out_minf_1(1); // expected-warning{{should be > 1 but is 1}}
     break;
   case 4:
-    __range_out_m1_inf(-1); // expected-warning{{should be < -1}}
+    __range_out_m1_inf(-1); // expected-warning{{should be < -1 but is -1}}
     break;
   case 5:
-    __range_out_0_inf(0); // expected-warning{{should be < 0}}
+    __range_out_0_inf(0); // expected-warning{{should be < 0 but is 0}}
     break;
   case 6:
-    __range_out_1_inf(1); // expected-warning{{should be <= 0}}
+    __range_out_1_inf(1); // expected-warning{{should be <= 0 but is 1}}
     break;
   }
 }
+
+void test_explanation(int x, int y) {
+  switch (y) {
+  case 1:
+    if (x > 0)
+      __single_val_0(x); // expected-warning{{should be zero but is > 0 [}}
+    return;
+  case 2:
+    if (x < 0)
+      __single_val_0(x); // expected-warning{{should be zero but is < 0 [}}
+    return;
+  case 3:
+    if (x < -1)
+      __single_val_0(x); // expected-warning{{should be zero but is < 0 [}}
+    return;
+  case 4:
+    if (x != 0)
+      __single_val_0(x); // expected-warning{{should be zero [}}
+    return;
+  case 5:
+    if (x == 3)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3 [}}
+    return;
+  case 6:
+    if (x > 6)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is >= 7 [}}
+    return;
+  case 7:
+    if (x < 1)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is <= 0 [}}
+    return;
+  case 8:
+    if (__test_case_range_1_2__4_6(x) == 1)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3 or <= 0 [}}
+    return;
+  case 9:
+    if (__test_case_range_1_2__4_6(x) == 2)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3 or >= 7 [}}
+    return;
+  case 10:
+    if (__test_case_range_1_2__4_6(x) == 3)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is <= 0 or >= 7 [}}
+    return;
+  case 11:
+    if (__test_case_range_1_2__4_6(x) == 4)
+      __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 [}}
+    return;
+  }
+}
Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -98,21 +98,19 @@
   /// Special argument number for specifying the return value.
   static const ArgNo Ret;
 
-  using DescString = SmallString<96>;
-
-  /// Returns the string representation of an argument index.
+  /// Get a string representation of an argument index.
   /// E.g.: (1) -> '1st arg', (2) - > '2nd arg'
-  static SmallString<8> getArgDesc(ArgNo);
-  /// Append textual description of a numeric range [RMin,RMax] to the string
-  /// 'Out'.
+  static void printArgDesc(ArgNo, llvm::raw_ostream &Out);
+  /// Append textual description of a numeric range [RMin,RMax] to
+  /// \p Out.
   static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax,
                                     QualType ArgT, BasicValueFactory &BVF,
-                                    DescString &Out);
-  /// Append textual description of a numeric range out of [RMin,RMax] to the
-  /// string 'Out'.
+                                    llvm::raw_ostream &Out);
+  /// Append textual description of a numeric range out of [RMin,RMax] to
+  /// \p Out.
   static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax,
                                    QualType ArgT, BasicValueFactory &BVF,
-                                   DescString &Out);
+                                   llvm::raw_ostream &Out);
 
   class ValueConstraint;
 
@@ -160,9 +158,9 @@
     /// Give a description that explains the constraint to the user. Used when
     /// a bug is reported or when the constraint is applied and displayed as a
     /// note.
-    virtual std::string describe(DescriptionKind DK, const CallEvent &Call,
-                                 ProgramStateRef State,
-                                 const Summary &Summary) const {
+    virtual void describe(DescriptionKind DK, const CallEvent &Call,
+                          ProgramStateRef State, const Summary &Summary,
+                          llvm::raw_ostream &Out) const {
       // There are some descendant classes that are not used as argument
       // constraints, e.g. ComparisonConstraint. In that case we can safely
       // ignore the implementation of this function.
@@ -170,6 +168,24 @@
           "Description not implemented for summary case constraints");
     }
 
+    /// This function is called when the current constraint represents the
+    /// opposite of a constraint that was not satisfied in a given state, but
+    /// the opposite is satisfied. In this case the available information in the
+    /// program state and this constraint can be used to get a more detailed
+    /// description about the original constraint violation. This can be get by
+    /// try to narrow the current constraint while it remains satisfied in the
+    /// given program state. If useful information is found it is put into
+    /// \p Out, but it is possible to produce no output. If anything is added to
+    /// \p Out it should look like " but is ...", the string is added to the bug
+    /// report about a constraint violation.
+    virtual void describeBug(const CallEvent &Call, ProgramStateRef State,
+                             const Summary &Summary,
+                             llvm::raw_ostream &Out) const {
+      if (auto N = getArgSVal(Call, getArgNo()).getAs<NonLoc>())
+        if (const llvm::APSInt *Int = N->getAsInteger())
+          Out << " but is " << *Int;
+    }
+
     /// Return those arguments that should be tracked when we report a bug about
     /// argument constraint violation. By default it is the argument that is
     /// constrained, however, in some special cases we need to track other
@@ -249,9 +265,13 @@
                           const Summary &Summary,
                           CheckerContext &C) const override;
 
-    std::string describe(DescriptionKind DK, const CallEvent &Call,
-                         ProgramStateRef State,
-                         const Summary &Summary) const override;
+    void describe(DescriptionKind DK, const CallEvent &Call,
+                  ProgramStateRef State, const Summary &Summary,
+                  llvm::raw_ostream &Out) const override;
+
+    void describeBug(const CallEvent &Call, ProgramStateRef State,
+                     const Summary &Summary,
+                     llvm::raw_ostream &Out) const override;
 
     ValueConstraintPtr negate() const override {
       RangeConstraint Tmp(*this);
@@ -272,15 +292,15 @@
     using RangeApplyFunction =
         std::function<bool(const llvm::APSInt &Min, const llvm::APSInt &Max)>;
 
-    /// @brief Apply a function on all intervals in the range.
+    /// Apply a function on all intervals in the range.
     void applyOnWithinRange(
         BasicValueFactory &BVF, QualType ArgT,
         const RangeApplyFunction &F) const;
-    /// @brief Apply a function on all intervals in the complement range.
+    /// Apply a function on all intervals in the complementary range.
     void applyOnOutOfRange(
         BasicValueFactory &BVF, QualType ArgT,
         const RangeApplyFunction &F) const;
-    /// @brief Apply a function on all intervals of the range or the complement
+    /// Apply a function on all intervals of the range or the complementary
     /// range.
     void applyOnRange(RangeKind Kind,
         BasicValueFactory &BVF, QualType ArgT,
@@ -326,9 +346,9 @@
                           const Summary &Summary,
                           CheckerContext &C) const override;
 
-    std::string describe(DescriptionKind DK, const CallEvent &Call,
-                         ProgramStateRef State,
-                         const Summary &Summary) const override;
+    void describe(DescriptionKind DK, const CallEvent &Call,
+                  ProgramStateRef State, const Summary &Summary,
+                  llvm::raw_ostream &Out) const override;
 
     ValueConstraintPtr negate() const override {
       NotNullConstraint Tmp(*this);
@@ -380,9 +400,9 @@
                           const Summary &Summary,
                           CheckerContext &C) const override;
 
-    std::string describe(DescriptionKind DK, const CallEvent &Call,
-                         ProgramStateRef State,
-                         const Summary &Summary) const override;
+    void describe(DescriptionKind DK, const CallEvent &Call,
+                  ProgramStateRef State, const Summary &Summary,
+                  llvm::raw_ostream &Out) const override;
 
     std::vector<ArgNo> getArgsToTrack() const override {
       std::vector<ArgNo> Result{ArgN};
@@ -757,8 +777,12 @@
       return;
     assert(Call.getDecl() &&
            "Function found in summary must have a declaration available");
-    std::string Msg = VC->describe(ValueConstraint::DescriptionKind::Violation,
-                                   Call, C.getState(), Summary);
+    SmallString<256> Msg;
+    llvm::raw_svector_ostream MsgOs(Msg);
+
+    VC->describe(ValueConstraint::DescriptionKind::Violation, Call,
+                 C.getState(), Summary, MsgOs);
+    NegatedVC->describeBug(Call, N->getState(), Summary, MsgOs);
     Msg[0] = toupper(Msg[0]);
     if (!BT_InvalidArg)
       BT_InvalidArg = std::make_unique<BugType>(
@@ -800,55 +824,40 @@
 
 } // end of anonymous namespace
 
-SmallString<8>
-StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) {
-  SmallString<8> Result;
-  Result += std::to_string(ArgN + 1);
-  Result += llvm::getOrdinalSuffix(ArgN + 1);
-  Result += " argument";
-  return Result;
+void StdLibraryFunctionsChecker::printArgDesc(
+    StdLibraryFunctionsChecker::ArgNo ArgN, llvm::raw_ostream &Out) {
+  Out << std::to_string(ArgN + 1);
+  Out << llvm::getOrdinalSuffix(ArgN + 1);
+  Out << " argument";
 }
 
 void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin,
                                                        llvm::APSInt RMax,
                                                        QualType ArgT,
                                                        BasicValueFactory &BVF,
-                                                       DescString &Out) {
+                                                       llvm::raw_ostream &Out) {
   if (RMin.isZero() && RMax.isZero())
-    Out.append("zero");
+    Out << "zero";
   else if (RMin == RMax)
-    RMin.toString(Out);
+    Out << RMin;
   else if (RMin == BVF.getMinValue(ArgT)) {
     if (RMax == -1)
-      Out.append("< 0");
-    else {
-      Out.append("<= ");
-      RMax.toString(Out);
-    }
+      Out << "< 0";
+    else
+      Out << "<= " << RMax;
   } else if (RMax == BVF.getMaxValue(ArgT)) {
     if (RMin.isOne())
-      Out.append("> 0");
-    else {
-      Out.append(">= ");
-      RMin.toString(Out);
-    }
+      Out << "> 0";
+    else
+      Out << ">= " << RMin;
   } else if (RMin.isNegative() == RMax.isNegative() &&
              RMin.getLimitedValue() == RMax.getLimitedValue() - 1) {
-    RMin.toString(Out);
-    Out.append(" or ");
-    RMax.toString(Out);
+    Out << RMin << " or " << RMax;
   } else if (RMin.isNegative() == RMax.isNegative() &&
              RMin.getLimitedValue() == RMax.getLimitedValue() - 2) {
-    RMin.toString(Out);
-    Out.append(", ");
-    (RMin + 1).toString(Out, 10, RMin.isSigned());
-    Out.append(" or ");
-    RMax.toString(Out);
+    Out << RMin << ", " << (RMin + 1) << " or " << RMax;
   } else {
-    Out.append("between ");
-    RMin.toString(Out);
-    Out.append(" and ");
-    RMax.toString(Out);
+    Out << "between " << RMin << " and " << RMax;
   }
 }
 
@@ -856,37 +865,26 @@
                                                       llvm::APSInt RMax,
                                                       QualType ArgT,
                                                       BasicValueFactory &BVF,
-                                                      DescString &Out) {
+                                                      llvm::raw_ostream &Out) {
   if (RMin.isZero() && RMax.isZero())
-    Out.append("nonzero");
+    Out << "nonzero";
   else if (RMin == RMax) {
-    Out.append("not equal to ");
-    RMin.toString(Out);
+    Out << "not equal to " << RMin;
   } else if (RMin == BVF.getMinValue(ArgT)) {
     if (RMax == -1)
-      Out.append(">= 0");
-    else {
-      Out.append("> ");
-      RMax.toString(Out);
-    }
+      Out << ">= 0";
+    else
+      Out << "> " << RMax;
   } else if (RMax == BVF.getMaxValue(ArgT)) {
     if (RMin.isOne())
-      Out.append("<= 0");
-    else {
-      Out.append("< ");
-      RMin.toString(Out);
-    }
+      Out << "<= 0";
+    else
+      Out << "< " << RMin;
   } else if (RMin.isNegative() == RMax.isNegative() &&
              RMin.getLimitedValue() == RMax.getLimitedValue() - 1) {
-    Out.append("not ");
-    RMin.toString(Out);
-    Out.append(" and not ");
-    RMax.toString(Out);
+    Out << "not " << RMin << " and not " << RMax;
   } else {
-    Out.append("not between ");
-    RMin.toString(Out);
-    Out.append(" and ");
-    RMax.toString(Out);
+    Out << "not between " << RMin << " and " << RMax;
   }
 }
 
@@ -968,42 +966,77 @@
   return State;
 }
 
-std::string StdLibraryFunctionsChecker::RangeConstraint::describe(
+void StdLibraryFunctionsChecker::RangeConstraint::describe(
     DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
-    const Summary &Summary) const {
+    const Summary &Summary, llvm::raw_ostream &Out) const {
 
   BasicValueFactory &BVF = getBVF(State);
   QualType T = Summary.getArgType(getArgNo());
-  DescString Result;
   const auto Violation = ValueConstraint::DescriptionKind::Violation;
 
-  Result += "the ";
-  Result += getArgDesc(ArgN);
-  Result += " to '";
-  Result += getFunctionName(Call);
-  Result += DK == Violation ? "' should be " : "' is ";
+  Out << "the ";
+  printArgDesc(ArgN, Out);
+  Out << " to '";
+  Out << getFunctionName(Call);
+  Out << ((DK == Violation) ? "' should be " : "' is ");
   if (!Description.empty()) {
-    Result += Description;
+    Out << Description;
   } else {
     unsigned I = Ranges.size();
     if (Kind == WithinRange) {
       for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
         appendInsideRangeDesc(BVF.getValue(R.first, T),
-                              BVF.getValue(R.second, T), T, BVF, Result);
+                              BVF.getValue(R.second, T), T, BVF, Out);
         if (--I > 0)
-          Result += " or ";
+          Out << " or ";
       }
     } else {
       for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
         appendOutOfRangeDesc(BVF.getValue(R.first, T),
-                             BVF.getValue(R.second, T), T, BVF, Result);
+                             BVF.getValue(R.second, T), T, BVF, Out);
         if (--I > 0)
-          Result += " and ";
+          Out << " and ";
       }
     }
   }
+}
+
+void StdLibraryFunctionsChecker::RangeConstraint::describeBug(
+    const CallEvent &Call, ProgramStateRef State, const Summary &Summary,
+    llvm::raw_ostream &Out) const {
+  unsigned int NRanges = 0;
+  bool HaveAllRanges = true;
+
+  ProgramStateManager &Mgr = State->getStateManager();
+  BasicValueFactory &BVF = Mgr.getSValBuilder().getBasicValueFactory();
+  ConstraintManager &CM = Mgr.getConstraintManager();
+  SVal V = getArgSVal(Call, getArgNo());
 
-  return Result.c_str();
+  if (auto N = V.getAs<NonLoc>()) {
+    if (const llvm::APSInt *Int = N->getAsInteger()) {
+      Out << " but is " << *Int;
+      return;
+    }
+    QualType T = Summary.getArgType(getArgNo());
+    SmallString<128> MoreInfo;
+    llvm::raw_svector_ostream MoreInfoOs(MoreInfo);
+    auto ApplyF = [&](const llvm::APSInt &Min, const llvm::APSInt &Max) {
+      if (CM.assumeInclusiveRange(State, *N, Min, Max, true)) {
+        if (NRanges > 0)
+          MoreInfoOs << " or ";
+        appendInsideRangeDesc(Min, Max, T, BVF, MoreInfoOs);
+        ++NRanges;
+      } else {
+        HaveAllRanges = false;
+      }
+      return true;
+    };
+
+    applyOnRange(Kind, BVF, T, ApplyF);
+    assert(NRanges > 0);
+    if (!HaveAllRanges || NRanges == 1)
+      Out << " but is " << MoreInfo;
+  }
 }
 
 ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply(
@@ -1042,17 +1075,15 @@
   return State->assume(L, CannotBeNull);
 }
 
-std::string StdLibraryFunctionsChecker::NotNullConstraint::describe(
+void StdLibraryFunctionsChecker::NotNullConstraint::describe(
     DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
-    const Summary &Summary) const {
-  SmallString<48> Result;
+    const Summary &Summary, llvm::raw_ostream &Out) const {
   const auto Violation = ValueConstraint::DescriptionKind::Violation;
-  Result += "the ";
-  Result += getArgDesc(ArgN);
-  Result += " to '";
-  Result += getFunctionName(Call);
-  Result += DK == Violation ? "' should not be NULL" : "' is not NULL";
-  return Result.c_str();
+  Out << "the ";
+  printArgDesc(ArgN, Out);
+  Out << " to '";
+  Out << getFunctionName(Call);
+  Out << (DK == Violation ? "' should not be NULL" : "' is not NULL");
 }
 
 ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply(ProgramStateRef State, const CallEvent &Call,
@@ -1097,28 +1128,26 @@
   llvm_unreachable("Size argument or the dynamic size is Undefined");
 }
 
-std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe(
+void StdLibraryFunctionsChecker::BufferSizeConstraint::describe(
     DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
-    const Summary &Summary) const {
-  SmallString<96> Result;
+    const Summary &Summary, llvm::raw_ostream &Out) const {
   const auto Violation = ValueConstraint::DescriptionKind::Violation;
-  Result += "the size of the ";
-  Result += getArgDesc(ArgN);
-  Result += " to '";
-  Result += getFunctionName(Call);
-  Result += DK == Violation ? "' should be " : "' is ";
-  Result += "equal to or greater than ";
+  Out << "the size of the ";
+  printArgDesc(ArgN, Out);
+  Out << " to '";
+  Out << getFunctionName(Call);
+  Out << (DK == Violation ? "' should be " : "' is ");
+  Out << "equal to or greater than ";
   if (ConcreteSize) {
-    ConcreteSize->toString(Result);
+    Out << *ConcreteSize;
   } else if (SizeArgN) {
-    Result += "the value of the ";
-    Result += getArgDesc(*SizeArgN);
+    Out << "the value of the ";
+    printArgDesc(*SizeArgN, Out);
     if (SizeMultiplierArgN) {
-      Result += " times the ";
-      Result += getArgDesc(*SizeMultiplierArgN);
+      Out << " times the ";
+      printArgDesc(*SizeMultiplierArgN, Out);
     }
   }
-  return Result.c_str();
 }
 
 void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
@@ -1150,10 +1179,11 @@
     assert(SuccessSt);
     NewState = SuccessSt;
     if (NewState != State) {
-      SmallString<64> Msg;
-      Msg += "Assuming ";
-      Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption,
-                                  Call, NewState, Summary);
+      SmallString<128> Msg;
+      llvm::raw_svector_ostream Os(Msg);
+      Os << "Assuming ";
+      Constraint->describe(ValueConstraint::DescriptionKind::Assumption, Call,
+                           NewState, Summary, Os);
       const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo());
       NewNode = C.addTransition(
           NewState, NewNode,
@@ -3457,6 +3487,27 @@
                   ErrnoIrrelevant, "Function returns 0")
             .Case({ReturnValueCondition(WithinRange, SingleValue(1))},
                   ErrnoIrrelevant, "Function returns 1"));
+    addToFunctionSummaryMap(
+        "__test_case_range_1_2__4_6",
+        Signature(ArgTypes{IntTy}, RetType{IntTy}),
+        Summary(EvalCallAsPure)
+            .Case({ArgumentCondition(0U, WithinRange,
+                                     IntRangeVector{{IntMin, 0}, {3, 3}}),
+                   ReturnValueCondition(WithinRange, SingleValue(1))},
+                  ErrnoIrrelevant)
+            .Case({ArgumentCondition(0U, WithinRange,
+                                     IntRangeVector{{3, 3}, {7, IntMax}}),
+                   ReturnValueCondition(WithinRange, SingleValue(2))},
+                  ErrnoIrrelevant)
+            .Case({ArgumentCondition(0U, WithinRange,
+                                     IntRangeVector{{IntMin, 0}, {7, IntMax}}),
+                   ReturnValueCondition(WithinRange, SingleValue(3))},
+                  ErrnoIrrelevant)
+            .Case({ArgumentCondition(
+                       0U, WithinRange,
+                       IntRangeVector{{IntMin, 0}, {3, 3}, {7, IntMax}}),
+                   ReturnValueCondition(WithinRange, SingleValue(4))},
+                  ErrnoIrrelevant));
   }
 
   SummariesInitialized = true;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to