[clang] bdf3147 - [Analyzer][solver] Add dump methods for (dis)equality classes.

Gabor Marton via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 14 05:15:11 PDT 2021


Author: Gabor Marton
Date: 2021-07-14T13:45:02+02:00
New Revision: bdf31471c76b5ded9e8d5a039250c2a7ba7aead6

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

LOG: [Analyzer][solver] Add dump methods for (dis)equality classes.

This proved to be very useful during debugging.

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

Added: 
    clang/test/Analysis/expr-inspection-printState-diseq-info.c
    clang/test/Analysis/expr-inspection-printState-eq-classes.c

Modified: 
    clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
    clang/test/Analysis/expr-inspection.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 9e014e9fce4e2..aba97e48756aa 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -592,6 +592,11 @@ class EquivalenceClass : public llvm::FoldingSetNode {
                                           RangeSet::Factory &F,
                                           ProgramStateRef State);
 
+  void dumpToStream(ProgramStateRef State, raw_ostream &os) const;
+  LLVM_DUMP_METHOD void dump(ProgramStateRef State) const {
+    dumpToStream(State, llvm::errs());
+  }
+
   /// Check equivalence data for consistency.
   LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
   isClassDataConsistent(ProgramStateRef State);
@@ -1599,6 +1604,15 @@ class RangeConstraintManager : public RangedConstraintManager {
 
   void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
                  unsigned int Space = 0, bool IsDot = false) const override;
+  void printConstraints(raw_ostream &Out, ProgramStateRef State,
+                        const char *NL = "\n", unsigned int Space = 0,
+                        bool IsDot = false) const;
+  void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State,
+                               const char *NL = "\n", unsigned int Space = 0,
+                               bool IsDot = false) const;
+  void printDisequalities(raw_ostream &Out, ProgramStateRef State,
+                          const char *NL = "\n", unsigned int Space = 0,
+                          bool IsDot = false) const;
 
   //===------------------------------------------------------------------===//
   // Implementation for interface from RangedConstraintManager.
@@ -1749,6 +1763,15 @@ ConstraintMap ento::getConstraintMap(ProgramStateRef State) {
 //                     EqualityClass implementation details
 //===----------------------------------------------------------------------===//
 
+LLVM_DUMP_METHOD void EquivalenceClass::dumpToStream(ProgramStateRef State,
+                                                     raw_ostream &os) const {
+  SymbolSet ClassMembers = getClassMembers(State);
+  for (const SymbolRef &MemberSym : ClassMembers) {
+    MemberSym->dump();
+    os << "\n";
+  }
+}
+
 inline EquivalenceClass EquivalenceClass::find(ProgramStateRef State,
                                                SymbolRef Sym) {
   assert(State && "State should not be null");
@@ -2601,6 +2624,16 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
 void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
                                        const char *NL, unsigned int Space,
                                        bool IsDot) const {
+  printConstraints(Out, State, NL, Space, IsDot);
+  printEquivalenceClasses(Out, State, NL, Space, IsDot);
+  printDisequalities(Out, State, NL, Space, IsDot);
+}
+
+void RangeConstraintManager::printConstraints(raw_ostream &Out,
+                                              ProgramStateRef State,
+                                              const char *NL,
+                                              unsigned int Space,
+                                              bool IsDot) const {
   ConstraintRangeTy Constraints = State->get<ConstraintRange>();
 
   Indent(Out, Space, IsDot) << "\"constraints\": ";
@@ -2634,3 +2667,140 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
   --Space;
   Indent(Out, Space, IsDot) << "]," << NL;
 }
+
+static std::string toString(const SymbolRef &Sym) {
+  std::string S;
+  llvm::raw_string_ostream O(S);
+  Sym->dumpToStream(O);
+  return O.str();
+}
+
+static std::string toString(ProgramStateRef State, EquivalenceClass Class) {
+  SymbolSet ClassMembers = Class.getClassMembers(State);
+  llvm::SmallVector<SymbolRef, 8> ClassMembersSorted(ClassMembers.begin(),
+                                                     ClassMembers.end());
+  llvm::sort(ClassMembersSorted,
+             [](const SymbolRef &LHS, const SymbolRef &RHS) {
+               return toString(LHS) < toString(RHS);
+             });
+
+  bool FirstMember = true;
+
+  std::string Str;
+  llvm::raw_string_ostream Out(Str);
+  Out << "[ ";
+  for (SymbolRef ClassMember : ClassMembersSorted) {
+    if (FirstMember)
+      FirstMember = false;
+    else
+      Out << ", ";
+    Out << "\"" << ClassMember << "\"";
+  }
+  Out << " ]";
+  return Out.str();
+}
+
+void RangeConstraintManager::printEquivalenceClasses(raw_ostream &Out,
+                                                     ProgramStateRef State,
+                                                     const char *NL,
+                                                     unsigned int Space,
+                                                     bool IsDot) const {
+  ClassMembersTy Members = State->get<ClassMembers>();
+
+  Indent(Out, Space, IsDot) << "\"equivalence_classes\": ";
+  if (Members.isEmpty()) {
+    Out << "null," << NL;
+    return;
+  }
+
+  std::set<std::string> MembersStr;
+  for (std::pair<EquivalenceClass, SymbolSet> ClassToSymbolSet : Members)
+    MembersStr.insert(toString(State, ClassToSymbolSet.first));
+
+  ++Space;
+  Out << '[' << NL;
+  bool FirstClass = true;
+  for (const std::string &Str : MembersStr) {
+    if (FirstClass) {
+      FirstClass = false;
+    } else {
+      Out << ',';
+      Out << NL;
+    }
+    Indent(Out, Space, IsDot);
+    Out << Str;
+  }
+  Out << NL;
+
+  --Space;
+  Indent(Out, Space, IsDot) << "]," << NL;
+}
+
+void RangeConstraintManager::printDisequalities(raw_ostream &Out,
+                                                ProgramStateRef State,
+                                                const char *NL,
+                                                unsigned int Space,
+                                                bool IsDot) const {
+  DisequalityMapTy Disequalities = State->get<DisequalityMap>();
+
+  Indent(Out, Space, IsDot) << "\"disequality_info\": ";
+  if (Disequalities.isEmpty()) {
+    Out << "null," << NL;
+    return;
+  }
+
+  // Transform the disequality info to an ordered map of
+  // [string -> (ordered set of strings)]
+  using EqClassesStrTy = std::set<std::string>;
+  using DisequalityInfoStrTy = std::map<std::string, EqClassesStrTy>;
+  DisequalityInfoStrTy DisequalityInfoStr;
+  for (std::pair<EquivalenceClass, ClassSet> ClassToDisEqSet : Disequalities) {
+    EquivalenceClass Class = ClassToDisEqSet.first;
+    ClassSet DisequalClasses = ClassToDisEqSet.second;
+    EqClassesStrTy MembersStr;
+    for (EquivalenceClass DisEqClass : DisequalClasses)
+      MembersStr.insert(toString(State, DisEqClass));
+    DisequalityInfoStr.insert({toString(State, Class), MembersStr});
+  }
+
+  ++Space;
+  Out << '[' << NL;
+  bool FirstClass = true;
+  for (std::pair<std::string, EqClassesStrTy> ClassToDisEqSet :
+       DisequalityInfoStr) {
+    const std::string &Class = ClassToDisEqSet.first;
+    if (FirstClass) {
+      FirstClass = false;
+    } else {
+      Out << ',';
+      Out << NL;
+    }
+    Indent(Out, Space, IsDot) << "{" << NL;
+    unsigned int DisEqSpace = Space + 1;
+    Indent(Out, DisEqSpace, IsDot) << "\"class\": ";
+    Out << Class;
+    const EqClassesStrTy &DisequalClasses = ClassToDisEqSet.second;
+    if (!DisequalClasses.empty()) {
+      Out << "," << NL;
+      Indent(Out, DisEqSpace, IsDot) << "\"disequal_to\": [" << NL;
+      unsigned int DisEqClassSpace = DisEqSpace + 1;
+      Indent(Out, DisEqClassSpace, IsDot);
+      bool FirstDisEqClass = true;
+      for (const std::string &DisEqClass : DisequalClasses) {
+        if (FirstDisEqClass) {
+          FirstDisEqClass = false;
+        } else {
+          Out << ',' << NL;
+          Indent(Out, DisEqClassSpace, IsDot);
+        }
+        Out << DisEqClass;
+      }
+      Out << "]" << NL;
+    }
+    Indent(Out, Space, IsDot) << "}";
+  }
+  Out << NL;
+
+  --Space;
+  Indent(Out, Space, IsDot) << "]," << NL;
+}

diff  --git a/clang/test/Analysis/expr-inspection-printState-diseq-info.c b/clang/test/Analysis/expr-inspection-printState-diseq-info.c
new file mode 100644
index 0000000000000..8db3722a8f263
--- /dev/null
+++ b/clang/test/Analysis/expr-inspection-printState-diseq-info.c
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=debug.ExprInspection %s 2>&1 | FileCheck %s
+
+void clang_analyzer_printState();
+
+void test_disequality_info(int e0, int b0, int b1, int c0) {
+  int e1 = e0 - b0;
+  if (b0 == 2) {
+    int e2 = e1 - b1;
+    if (e2 > 0) {
+      if (b1 != c0)
+        clang_analyzer_printState();
+    }
+  }
+}
+
+ // CHECK:        "disequality_info": [
+ // CHECK-NEXT:     {
+ // CHECK-NEXT:       "class": [ "(reg_$0<int e0>) - 2" ],
+ // CHECK-NEXT:       "disequal_to": [
+ // CHECK-NEXT:         [ "reg_$2<int b1>" ]]
+ // CHECK-NEXT:     },
+ // CHECK-NEXT:     {
+ // CHECK-NEXT:       "class": [ "reg_$2<int b1>" ],
+ // CHECK-NEXT:       "disequal_to": [
+ // CHECK-NEXT:         [ "(reg_$0<int e0>) - 2" ],
+ // CHECK-NEXT:         [ "reg_$3<int c0>" ]]
+ // CHECK-NEXT:     },
+ // CHECK-NEXT:     {
+ // CHECK-NEXT:       "class": [ "reg_$3<int c0>" ],
+ // CHECK-NEXT:       "disequal_to": [
+ // CHECK-NEXT:         [ "reg_$2<int b1>" ]]
+ // CHECK-NEXT:     }
+ // CHECK-NEXT:   ],

diff  --git a/clang/test/Analysis/expr-inspection-printState-eq-classes.c b/clang/test/Analysis/expr-inspection-printState-eq-classes.c
new file mode 100644
index 0000000000000..34514335f9e80
--- /dev/null
+++ b/clang/test/Analysis/expr-inspection-printState-eq-classes.c
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=debug.ExprInspection %s 2>&1 | FileCheck %s
+
+void clang_analyzer_printState();
+
+void test_equivalence_classes(int a, int b, int c, int d) {
+  if (a + b != c)
+    return;
+  if (a != d)
+    return;
+  if (b != 0)
+    return;
+  clang_analyzer_printState();
+  (void)(a * b * c * d);
+  return;
+}
+
+  // CHECK:      "equivalence_classes": [
+  // CHECK-NEXT:   [ "((reg_$0<int a>) + (reg_$1<int b>)) != (reg_$2<int c>)", "(reg_$0<int a>) != (reg_$2<int c>)" ],
+  // CHECK-NEXT:   [ "(reg_$0<int a>) + (reg_$1<int b>)", "reg_$0<int a>", "reg_$2<int c>", "reg_$3<int d>" ]
+  // CHECK-NEXT: ],

diff  --git a/clang/test/Analysis/expr-inspection.c b/clang/test/Analysis/expr-inspection.c
index 283fa9bdb724a..76118a76e71ca 100644
--- a/clang/test/Analysis/expr-inspection.c
+++ b/clang/test/Analysis/expr-inspection.c
@@ -38,6 +38,8 @@ void foo(int x) {
 // CHECK-NEXT:   "constraints": [
 // CHECK-NEXT:     { "symbol": "reg_$0<int x>", "range": "{ [-2147483648, 13] }" }
 // CHECK-NEXT:   ],
+// CHECK-NEXT:   "equivalence_classes": null,
+// CHECK-NEXT:   "disequality_info": null,
 // CHECK-NEXT:   "dynamic_types": null,
 // CHECK-NEXT:   "dynamic_casts": null,
 // CHECK-NEXT:   "constructing_objects": null,


        


More information about the cfe-commits mailing list