xref: /llvm-project/clang/lib/Analysis/FlowSensitive/DebugSupport.cpp (revision c9666d2339e5e11fa4f2de2e0294948b871c50ab)
1 //===- DebugSupport.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  This file defines functions which generate more readable forms of data
10 //  structures used in the dataflow analyses, for debugging purposes.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
15 #include "clang/Analysis/FlowSensitive/Value.h"
16 #include "llvm/ADT/DenseMap.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/Support/ErrorHandling.h"
19 #include "llvm/Support/FormatAdapters.h"
20 #include "llvm/Support/FormatVariadic.h"
21 
22 namespace clang {
23 namespace dataflow {
24 
25 using llvm::fmt_pad;
26 using llvm::formatv;
27 
28 namespace {
29 
30 class DebugStringGenerator {
31 public:
32   explicit DebugStringGenerator(
33       llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNamesArg)
34       : Counter(0), AtomNames(std::move(AtomNamesArg)) {
35 #ifndef NDEBUG
36     llvm::StringSet<> Names;
37     for (auto &N : AtomNames) {
38       assert(Names.insert(N.second).second &&
39              "The same name must not assigned to different atoms");
40     }
41 #endif
42   }
43 
44   /// Returns a string representation of a boolean value `B`.
45   std::string debugString(const BoolValue &B, size_t Depth = 0) {
46     std::string S;
47     switch (B.getKind()) {
48     case Value::Kind::AtomicBool: {
49       S = getAtomName(&cast<AtomicBoolValue>(B));
50       break;
51     }
52     case Value::Kind::Conjunction: {
53       auto &C = cast<ConjunctionValue>(B);
54       auto L = debugString(C.getLeftSubValue(), Depth + 1);
55       auto R = debugString(C.getRightSubValue(), Depth + 1);
56       S = formatv("(and\n{0}\n{1})", L, R);
57       break;
58     }
59     case Value::Kind::Disjunction: {
60       auto &D = cast<DisjunctionValue>(B);
61       auto L = debugString(D.getLeftSubValue(), Depth + 1);
62       auto R = debugString(D.getRightSubValue(), Depth + 1);
63       S = formatv("(or\n{0}\n{1})", L, R);
64       break;
65     }
66     case Value::Kind::Negation: {
67       auto &N = cast<NegationValue>(B);
68       S = formatv("(not\n{0})", debugString(N.getSubVal(), Depth + 1));
69       break;
70     }
71     default:
72       llvm_unreachable("Unhandled value kind");
73     }
74     auto Indent = Depth * 4;
75     return formatv("{0}", fmt_pad(S, Indent, 0));
76   }
77 
78 private:
79   /// Returns the name assigned to `Atom`, either user-specified or created by
80   /// default rules (B0, B1, ...).
81   std::string getAtomName(const AtomicBoolValue *Atom) {
82     auto Entry = AtomNames.try_emplace(Atom, formatv("B{0}", Counter));
83     if (Entry.second) {
84       Counter++;
85     }
86     return Entry.first->second;
87   }
88 
89   // Keep track of number of atoms without a user-specified name, used to assign
90   // non-repeating default names to such atoms.
91   size_t Counter;
92 
93   // Keep track of names assigned to atoms.
94   llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames;
95 };
96 
97 } // namespace
98 
99 std::string
100 debugString(const BoolValue &B,
101             llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) {
102   return DebugStringGenerator(std::move(AtomNames)).debugString(B);
103 }
104 
105 } // namespace dataflow
106 } // namespace clang
107