diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -592,6 +592,11 @@ 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 @@ 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 @@ // 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 @@ 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(); Indent(Out, Space, IsDot) << "\"constraints\": "; @@ -2634,3 +2667,140 @@ --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 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(); + + Indent(Out, Space, IsDot) << "\"equivalence_classes\": "; + if (Members.isEmpty()) { + Out << "null," << NL; + return; + } + + std::set MembersStr; + for (std::pair 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(); + + 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; + using DisequalityInfoStrTy = std::map; + DisequalityInfoStrTy DisequalityInfoStr; + for (std::pair 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 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 --- /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) - 2" ], + // CHECK-NEXT: "disequal_to": [ + // CHECK-NEXT: [ "reg_$2" ]] + // CHECK-NEXT: }, + // CHECK-NEXT: { + // CHECK-NEXT: "class": [ "reg_$2" ], + // CHECK-NEXT: "disequal_to": [ + // CHECK-NEXT: [ "(reg_$0) - 2" ], + // CHECK-NEXT: [ "reg_$3" ]] + // CHECK-NEXT: }, + // CHECK-NEXT: { + // CHECK-NEXT: "class": [ "reg_$3" ], + // CHECK-NEXT: "disequal_to": [ + // CHECK-NEXT: [ "reg_$2" ]] + // 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 --- /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) + (reg_$1)) != (reg_$2)", "(reg_$0) != (reg_$2)" ], + // CHECK-NEXT: [ "(reg_$0) + (reg_$1)", "reg_$0", "reg_$2", "reg_$3" ] + // CHECK-NEXT: ], diff --git a/clang/test/Analysis/expr-inspection.c b/clang/test/Analysis/expr-inspection.c --- a/clang/test/Analysis/expr-inspection.c +++ b/clang/test/Analysis/expr-inspection.c @@ -38,6 +38,8 @@ // CHECK-NEXT: "constraints": [ // CHECK-NEXT: { "symbol": "reg_$0", "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,