ASDenysPetrov updated this revision to Diff 444336.
ASDenysPetrov added a comment.

Constrained the test by adding llvm-lit `REQUIRES` command.
Documented a new function at 
https://clang.llvm.org/docs/analyzer/developer-docs/DebugChecks.html


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129498/new/

https://reviews.llvm.org/D129498

Files:
  clang/docs/analyzer/developer-docs/DebugChecks.rst
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
  clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/SVals.cpp
  clang/test/Analysis/print-ranges.cpp

Index: clang/test/Analysis/print-ranges.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/print-ranges.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config eagerly-assume=false -verify %s
+// REQUIRES: no-z3
+
+template <typename T>
+void clang_analyzer_value(T x);
+void clang_analyzer_value();
+template <typename T1, typename T2>
+void clang_analyzer_value(T1 x, T2 y);
+
+void test1(char x) {
+  clang_analyzer_value(x); // expected-warning{{8s:{ [-128, 127] }}}
+  if (x > 42)
+    clang_analyzer_value(x); // expected-warning{{8s:{ [43, 127] }}}
+  if (x == 42)
+    clang_analyzer_value(x); // expected-warning{{8s:42}}
+}
+
+void test2(short x) {
+  clang_analyzer_value(x); // expected-warning{{16s:{ [-32768, 32767] }}}
+  if (x < 4200)
+    clang_analyzer_value(x); // expected-warning{{16s:{ [-32768, 4199] }}}
+  if (x == 4200)
+    clang_analyzer_value(x); // expected-warning{{16s:4200}}
+}
+
+void test3(unsigned long long x) {
+  clang_analyzer_value(x); // expected-warning{{64u:{ [0, 18446744073709551615] }}}
+  if (x != 42000000)
+    clang_analyzer_value(x); // expected-warning{{64u:{ [0, 41999999], [42000001, 18446744073709551615] }}}
+  if (x == 18446744073709551615ull)
+    clang_analyzer_value(x); // expected-warning{{64u:18446744073709551615}}
+}
+
+struct S {};
+void test4(S s) {
+  clang_analyzer_value(s); // expected-warning{{n/a}}
+}
+
+void test5() {
+  clang_analyzer_value(); // expected-warning{{Missing argument}}
+}
+
+void test6(int x, int y) {
+  if (x == 42 && y == 24)
+    // Ignore 'y'.
+    clang_analyzer_value(x, y); // expected-warning{{32s:42}}
+}
Index: clang/lib/StaticAnalyzer/Core/SVals.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/SVals.cpp
+++ clang/lib/StaticAnalyzer/Core/SVals.cpp
@@ -109,6 +109,14 @@
   return getAsLocSymbol(IncludeBaseRegions);
 }
 
+const llvm::APSInt *SVal::getAsInteger() const {
+  if (auto CI = getAs<nonloc::ConcreteInt>())
+    return &CI->getValue();
+  if (auto CI = getAs<loc::ConcreteInt>())
+    return &CI->getValue();
+  return nullptr;
+}
+
 const MemRegion *SVal::getAsRegion() const {
   if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
     return X->getRegion();
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -1801,6 +1801,8 @@
 
   void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
                  unsigned int Space = 0, bool IsDot = false) const override;
+  void printRange(raw_ostream &Out, ProgramStateRef State,
+                  SymbolRef Sym) override;
   void printConstraints(raw_ostream &Out, ProgramStateRef State,
                         const char *NL = "\n", unsigned int Space = 0,
                         bool IsDot = false) const;
@@ -3154,6 +3156,13 @@
   printDisequalities(Out, State, NL, Space, IsDot);
 }
 
+void RangeConstraintManager::printRange(raw_ostream &Out, ProgramStateRef State,
+                                        SymbolRef Sym) {
+  const RangeSet RS = getRange(State, Sym);
+  Out << RS.getBitWidth() << (RS.isUnsigned() ? "u:" : "s:");
+  RS.dump(Out);
+}
+
 static std::string toString(const SymbolRef &Sym) {
   std::string S;
   llvm::raw_string_ostream O(S);
Index: clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -40,6 +40,7 @@
   void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;
   void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
   void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerValue(const CallExpr *CE, CheckerContext &C) const;
   void analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const;
   void analyzerDump(const CallExpr *CE, CheckerContext &C) const;
   void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
@@ -60,6 +61,7 @@
                           Optional<SVal> ExprVal = None) const;
   ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
                           Optional<SVal> ExprVal = None) const;
+  template <typename T> void printAndReport(CheckerContext &C, T What) const;
 
   const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;
   const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const;
@@ -99,6 +101,7 @@
                 &ExprInspectionChecker::analyzerDumpExtent)
           .Case("clang_analyzer_dumpElementCount",
                 &ExprInspectionChecker::analyzerDumpElementCount)
+          .Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue)
           .StartsWith("clang_analyzer_dumpSvalType",
                       &ExprInspectionChecker::analyzerDumpSValType)
           .StartsWith("clang_analyzer_dump",
@@ -258,6 +261,45 @@
   reportBug(Ex.Visit(V), C);
 }
 
+static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
+                        const llvm::APSInt &I) {
+  Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:");
+  Out << I;
+}
+
+static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
+                        SymbolRef Sym) {
+  C.getConstraintManager().printRange(Out, C.getState(), Sym);
+}
+
+static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
+                        SVal V) {
+  Out << V;
+}
+
+template <typename T>
+void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const {
+  llvm::SmallString<64> Str;
+  llvm::raw_svector_ostream OS(Str);
+  printHelper(OS, C, What);
+  reportBug(OS.str(), C);
+}
+
+void ExprInspectionChecker::analyzerValue(const CallExpr *CE,
+                                          CheckerContext &C) const {
+  const Expr *Arg = getArgExpr(CE, C);
+  if (!Arg)
+    return;
+
+  SVal V = C.getSVal(Arg);
+  if (const SymbolRef Sym = V.getAsSymbol())
+    printAndReport(C, Sym);
+  else if (const llvm::APSInt *I = V.getAsInteger())
+    printAndReport(C, *I);
+  else
+    reportBug("n/a", C);
+}
+
 void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE,
                                                  CheckerContext &C) const {
   const Expr *Arg = getArgExpr(CE, C);
@@ -275,11 +317,7 @@
     return;
 
   SVal V = C.getSVal(Arg);
-
-  llvm::SmallString<32> Str;
-  llvm::raw_svector_ostream OS(Str);
-  V.dumpToStream(OS);
-  reportBug(OS.str(), C);
+  printAndReport(C, V);
 }
 
 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
@@ -303,11 +341,7 @@
 
   DefinedOrUnknownSVal Size =
       getDynamicExtent(C.getState(), MR, C.getSValBuilder());
-
-  SmallString<64> Msg;
-  llvm::raw_svector_ostream Out(Msg);
-  Out << Size;
-  reportBug(Out.str(), C);
+  printAndReport(C, Size);
 }
 
 void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
@@ -328,11 +362,7 @@
 
   DefinedOrUnknownSVal ElementCount =
       getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy);
-
-  SmallString<128> Msg;
-  llvm::raw_svector_ostream Out(Msg);
-  Out << ElementCount;
-  reportBug(Out.str(), C);
+  printAndReport(C, ElementCount);
 }
 
 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
@@ -169,6 +169,11 @@
   /// should continue to the base regions if the region is not symbolic.
   SymbolRef getAsSymbol(bool IncludeBaseRegions = false) const;
 
+  /// If this SVal is loc::ConcreteInt or nonloc::ConcreteInt,
+  /// return a pointer to APSInt which is held in it.
+  /// Otherwise, return nullptr.
+  const llvm::APSInt *getAsInteger() const;
+
   const MemRegion *getAsRegion() const;
 
   /// printJson - Pretty-prints in JSON format.
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
@@ -123,6 +123,9 @@
                          const char *NL, unsigned int Space,
                          bool IsDot) const = 0;
 
+  virtual void printRange(raw_ostream &Out, ProgramStateRef State,
+                          SymbolRef Sym) {}
+
   /// Convenience method to query the state to see if a symbol is null or
   /// not null, or if neither assumption can be made.
   ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym) {
Index: clang/docs/analyzer/developer-docs/DebugChecks.rst
===================================================================
--- clang/docs/analyzer/developer-docs/DebugChecks.rst
+++ clang/docs/analyzer/developer-docs/DebugChecks.rst
@@ -309,6 +309,32 @@
       clang_analyzer_dumpExtent(a);       // expected-warning {{8 S64b}}
       clang_analyzer_dumpElementCount(a); // expected-warning {{2 S64b}}
     }
+    
+- ``clang_analyzer_value(a single argument of integer or pointer type)``
+
+  Prints an associated value for the given argument.
+  The value can be represented either as a range set or as a concrete integer.
+  The argument shall be of an integer or pointer type.
+  
+  **Note:** This function will print nothing for clang built with Z3 constraint manager.
+  This may cause crashes of your tests. To manage this use one of the test constraining
+  techniques:
+  
+  * llvm-lit commands ``REQUIRES no-z3`` or ``UNSUPPORTED z3`` `See for details. <https://llvm.org/docs/TestingGuide.html#constraining-test-execution>`_
+  
+  * a preprocessor directive ``#ifndef ANALYZER_CM_Z3``
+  
+  * a clang command argument ``-analyzer-constraints=range``
+
+  Example usage::
+
+    void print(char c, unsigned u) {
+      clang_analyzer_value(c); // expected-warning {{8s:{ [-128, 127] }}}
+      if(u != 42)
+         clang_analyzer_value(u); // expected-warning {{32u:{ [0, 41], [43, 4294967295] }}}
+      else
+         clang_analyzer_value(u); // expected-warning {{32u:42}}
+    }
 
 Statistics
 ==========
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to