xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
181ad6265SDimitry Andric //===-- DataflowAnalysisContext.cpp -----------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric //  This file defines a DataflowAnalysisContext class that owns objects that
1081ad6265SDimitry Andric //  encompass the state of a program and stores context that is used during
1181ad6265SDimitry Andric //  dataflow analysis.
1281ad6265SDimitry Andric //
1381ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1481ad6265SDimitry Andric 
1581ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
1681ad6265SDimitry Andric #include "clang/AST/ExprCXX.h"
17fcaf7f86SDimitry Andric #include "clang/Analysis/FlowSensitive/DebugSupport.h"
1806c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Formula.h"
1906c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Logger.h"
20*5f757f3fSDimitry Andric #include "clang/Analysis/FlowSensitive/SimplifyConstraints.h"
2181ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
22bdd1243dSDimitry Andric #include "llvm/ADT/SetOperations.h"
2306c3fb27SDimitry Andric #include "llvm/ADT/SetVector.h"
2406c3fb27SDimitry Andric #include "llvm/Support/CommandLine.h"
25fcaf7f86SDimitry Andric #include "llvm/Support/Debug.h"
2606c3fb27SDimitry Andric #include "llvm/Support/FileSystem.h"
2706c3fb27SDimitry Andric #include "llvm/Support/Path.h"
2806c3fb27SDimitry Andric #include "llvm/Support/raw_ostream.h"
2981ad6265SDimitry Andric #include <cassert>
3081ad6265SDimitry Andric #include <memory>
3106c3fb27SDimitry Andric #include <string>
3281ad6265SDimitry Andric #include <utility>
3306c3fb27SDimitry Andric #include <vector>
3406c3fb27SDimitry Andric 
3506c3fb27SDimitry Andric static llvm::cl::opt<std::string> DataflowLog(
3606c3fb27SDimitry Andric     "dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional,
3706c3fb27SDimitry Andric     llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual "
3806c3fb27SDimitry Andric                    "log to stderr. With an arg, writes HTML logs under the "
3906c3fb27SDimitry Andric                    "specified directory (one per analyzed function)."));
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric namespace clang {
4281ad6265SDimitry Andric namespace dataflow {
4381ad6265SDimitry Andric 
4406c3fb27SDimitry Andric FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {
45bdd1243dSDimitry Andric   // During context-sensitive analysis, a struct may be allocated in one
46bdd1243dSDimitry Andric   // function, but its field accessed in a function lower in the stack than
47bdd1243dSDimitry Andric   // the allocation. Since we only collect fields used in the function where
48bdd1243dSDimitry Andric   // the allocation occurs, we can't apply that filter when performing
49bdd1243dSDimitry Andric   // context-sensitive analysis. But, this only applies to storage locations,
50bdd1243dSDimitry Andric   // since field access it not allowed to fail. In contrast, field *values*
51bdd1243dSDimitry Andric   // don't need this allowance, since the API allows for uninitialized fields.
5206c3fb27SDimitry Andric   if (Opts.ContextSensitiveOpts)
5306c3fb27SDimitry Andric     return getObjectFields(Type);
5406c3fb27SDimitry Andric 
5506c3fb27SDimitry Andric   return llvm::set_intersection(getObjectFields(Type), ModeledFields);
5681ad6265SDimitry Andric }
5706c3fb27SDimitry Andric 
5806c3fb27SDimitry Andric void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) {
5906c3fb27SDimitry Andric   ModeledFields.set_union(Fields);
6006c3fb27SDimitry Andric }
6106c3fb27SDimitry Andric 
6206c3fb27SDimitry Andric StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {
6306c3fb27SDimitry Andric   if (!Type.isNull() && Type->isRecordType()) {
6406c3fb27SDimitry Andric     llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
6506c3fb27SDimitry Andric     for (const FieldDecl *Field : getModeledFields(Type))
6606c3fb27SDimitry Andric       if (Field->getType()->isReferenceType())
6706c3fb27SDimitry Andric         FieldLocs.insert({Field, nullptr});
6806c3fb27SDimitry Andric       else
6906c3fb27SDimitry Andric         FieldLocs.insert({Field, &createStorageLocation(
7006c3fb27SDimitry Andric                                      Field->getType().getNonReferenceType())});
71*5f757f3fSDimitry Andric 
72*5f757f3fSDimitry Andric     RecordStorageLocation::SyntheticFieldMap SyntheticFields;
73*5f757f3fSDimitry Andric     for (const auto &Entry : getSyntheticFields(Type))
74*5f757f3fSDimitry Andric       SyntheticFields.insert(
75*5f757f3fSDimitry Andric           {Entry.getKey(),
76*5f757f3fSDimitry Andric            &createStorageLocation(Entry.getValue().getNonReferenceType())});
77*5f757f3fSDimitry Andric 
78*5f757f3fSDimitry Andric     return createRecordStorageLocation(Type, std::move(FieldLocs),
79*5f757f3fSDimitry Andric                                        std::move(SyntheticFields));
8006c3fb27SDimitry Andric   }
8106c3fb27SDimitry Andric   return arena().create<ScalarStorageLocation>(Type);
8281ad6265SDimitry Andric }
8381ad6265SDimitry Andric 
84*5f757f3fSDimitry Andric // Returns the keys for a given `StringMap`.
85*5f757f3fSDimitry Andric // Can't use `StringSet` as the return type as it doesn't support `operator==`.
86*5f757f3fSDimitry Andric template <typename T>
87*5f757f3fSDimitry Andric static llvm::DenseSet<llvm::StringRef> getKeys(const llvm::StringMap<T> &Map) {
88*5f757f3fSDimitry Andric   return llvm::DenseSet<llvm::StringRef>(Map.keys().begin(), Map.keys().end());
89*5f757f3fSDimitry Andric }
90*5f757f3fSDimitry Andric 
91*5f757f3fSDimitry Andric RecordStorageLocation &DataflowAnalysisContext::createRecordStorageLocation(
92*5f757f3fSDimitry Andric     QualType Type, RecordStorageLocation::FieldToLoc FieldLocs,
93*5f757f3fSDimitry Andric     RecordStorageLocation::SyntheticFieldMap SyntheticFields) {
94*5f757f3fSDimitry Andric   assert(Type->isRecordType());
95*5f757f3fSDimitry Andric   assert(containsSameFields(getModeledFields(Type), FieldLocs));
96*5f757f3fSDimitry Andric   assert(getKeys(getSyntheticFields(Type)) == getKeys(SyntheticFields));
97*5f757f3fSDimitry Andric 
98*5f757f3fSDimitry Andric   RecordStorageLocationCreated = true;
99*5f757f3fSDimitry Andric   return arena().create<RecordStorageLocation>(Type, std::move(FieldLocs),
100*5f757f3fSDimitry Andric                                                std::move(SyntheticFields));
101*5f757f3fSDimitry Andric }
102*5f757f3fSDimitry Andric 
10381ad6265SDimitry Andric StorageLocation &
104*5f757f3fSDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) {
105*5f757f3fSDimitry Andric   if (auto *Loc = DeclToLoc.lookup(&D))
10681ad6265SDimitry Andric     return *Loc;
10706c3fb27SDimitry Andric   auto &Loc = createStorageLocation(D.getType().getNonReferenceType());
108*5f757f3fSDimitry Andric   DeclToLoc[&D] = &Loc;
10981ad6265SDimitry Andric   return Loc;
11081ad6265SDimitry Andric }
11181ad6265SDimitry Andric 
11281ad6265SDimitry Andric StorageLocation &
11381ad6265SDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
114*5f757f3fSDimitry Andric   const Expr &CanonE = ignoreCFGOmittedNodes(E);
115*5f757f3fSDimitry Andric 
116*5f757f3fSDimitry Andric   if (auto *Loc = ExprToLoc.lookup(&CanonE))
11781ad6265SDimitry Andric     return *Loc;
118*5f757f3fSDimitry Andric   auto &Loc = createStorageLocation(CanonE.getType());
119*5f757f3fSDimitry Andric   ExprToLoc[&CanonE] = &Loc;
12081ad6265SDimitry Andric   return Loc;
12181ad6265SDimitry Andric }
12281ad6265SDimitry Andric 
12381ad6265SDimitry Andric PointerValue &
12481ad6265SDimitry Andric DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
125753f127fSDimitry Andric   auto CanonicalPointeeType =
126753f127fSDimitry Andric       PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
12781ad6265SDimitry Andric   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
12881ad6265SDimitry Andric   if (Res.second) {
129bdd1243dSDimitry Andric     auto &PointeeLoc = createStorageLocation(CanonicalPointeeType);
13006c3fb27SDimitry Andric     Res.first->second = &arena().create<PointerValue>(PointeeLoc);
13181ad6265SDimitry Andric   }
13281ad6265SDimitry Andric   return *Res.first->second;
13381ad6265SDimitry Andric }
13481ad6265SDimitry Andric 
135*5f757f3fSDimitry Andric void DataflowAnalysisContext::addInvariant(const Formula &Constraint) {
136*5f757f3fSDimitry Andric   if (Invariant == nullptr)
137*5f757f3fSDimitry Andric     Invariant = &Constraint;
138*5f757f3fSDimitry Andric   else
139*5f757f3fSDimitry Andric     Invariant = &arena().makeAnd(*Invariant, Constraint);
140*5f757f3fSDimitry Andric }
141*5f757f3fSDimitry Andric 
14281ad6265SDimitry Andric void DataflowAnalysisContext::addFlowConditionConstraint(
14306c3fb27SDimitry Andric     Atom Token, const Formula &Constraint) {
14406c3fb27SDimitry Andric   auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint);
14581ad6265SDimitry Andric   if (!Res.second) {
14606c3fb27SDimitry Andric     Res.first->second =
14706c3fb27SDimitry Andric         &arena().makeAnd(*Res.first->second, Constraint);
14881ad6265SDimitry Andric   }
14981ad6265SDimitry Andric }
15081ad6265SDimitry Andric 
15106c3fb27SDimitry Andric Atom DataflowAnalysisContext::forkFlowCondition(Atom Token) {
15206c3fb27SDimitry Andric   Atom ForkToken = arena().makeFlowConditionToken();
15306c3fb27SDimitry Andric   FlowConditionDeps[ForkToken].insert(Token);
15406c3fb27SDimitry Andric   addFlowConditionConstraint(ForkToken, arena().makeAtomRef(Token));
15581ad6265SDimitry Andric   return ForkToken;
15681ad6265SDimitry Andric }
15781ad6265SDimitry Andric 
15806c3fb27SDimitry Andric Atom
15906c3fb27SDimitry Andric DataflowAnalysisContext::joinFlowConditions(Atom FirstToken,
16006c3fb27SDimitry Andric                                             Atom SecondToken) {
16106c3fb27SDimitry Andric   Atom Token = arena().makeFlowConditionToken();
16206c3fb27SDimitry Andric   FlowConditionDeps[Token].insert(FirstToken);
16306c3fb27SDimitry Andric   FlowConditionDeps[Token].insert(SecondToken);
16481ad6265SDimitry Andric   addFlowConditionConstraint(Token,
16506c3fb27SDimitry Andric                              arena().makeOr(arena().makeAtomRef(FirstToken),
16606c3fb27SDimitry Andric                                             arena().makeAtomRef(SecondToken)));
16781ad6265SDimitry Andric   return Token;
16881ad6265SDimitry Andric }
16981ad6265SDimitry Andric 
17006c3fb27SDimitry Andric Solver::Result DataflowAnalysisContext::querySolver(
17106c3fb27SDimitry Andric     llvm::SetVector<const Formula *> Constraints) {
17206c3fb27SDimitry Andric   return S->solve(Constraints.getArrayRef());
17381ad6265SDimitry Andric }
17481ad6265SDimitry Andric 
17506c3fb27SDimitry Andric bool DataflowAnalysisContext::flowConditionImplies(Atom Token,
176*5f757f3fSDimitry Andric                                                    const Formula &F) {
17781ad6265SDimitry Andric   // Returns true if and only if truth assignment of the flow condition implies
178*5f757f3fSDimitry Andric   // that `F` is also true. We prove whether or not this property holds by
17981ad6265SDimitry Andric   // reducing the problem to satisfiability checking. In other words, we attempt
180*5f757f3fSDimitry Andric   // to show that assuming `F` is false makes the constraints induced by the
18181ad6265SDimitry Andric   // flow condition unsatisfiable.
18206c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
18306c3fb27SDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
184*5f757f3fSDimitry Andric   Constraints.insert(&arena().makeNot(F));
185*5f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
18681ad6265SDimitry Andric   return isUnsatisfiable(std::move(Constraints));
18781ad6265SDimitry Andric }
18881ad6265SDimitry Andric 
189*5f757f3fSDimitry Andric bool DataflowAnalysisContext::flowConditionAllows(Atom Token,
190*5f757f3fSDimitry Andric                                                   const Formula &F) {
19106c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
192*5f757f3fSDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
193*5f757f3fSDimitry Andric   Constraints.insert(&F);
194*5f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
195*5f757f3fSDimitry Andric   return isSatisfiable(std::move(Constraints));
19681ad6265SDimitry Andric }
19781ad6265SDimitry Andric 
19806c3fb27SDimitry Andric bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
19906c3fb27SDimitry Andric                                                  const Formula &Val2) {
20006c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
20106c3fb27SDimitry Andric   Constraints.insert(&arena().makeNot(arena().makeEquals(Val1, Val2)));
20206c3fb27SDimitry Andric   return isUnsatisfiable(std::move(Constraints));
20381ad6265SDimitry Andric }
20481ad6265SDimitry Andric 
20581ad6265SDimitry Andric void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
206*5f757f3fSDimitry Andric     Atom Token, llvm::SetVector<const Formula *> &Constraints) {
207*5f757f3fSDimitry Andric   llvm::DenseSet<Atom> AddedTokens;
208*5f757f3fSDimitry Andric   std::vector<Atom> Remaining = {Token};
209*5f757f3fSDimitry Andric 
210*5f757f3fSDimitry Andric   if (Invariant)
211*5f757f3fSDimitry Andric     Constraints.insert(Invariant);
212*5f757f3fSDimitry Andric   // Define all the flow conditions that might be referenced in constraints.
213*5f757f3fSDimitry Andric   while (!Remaining.empty()) {
214*5f757f3fSDimitry Andric     auto Token = Remaining.back();
215*5f757f3fSDimitry Andric     Remaining.pop_back();
216*5f757f3fSDimitry Andric     if (!AddedTokens.insert(Token).second)
217*5f757f3fSDimitry Andric       continue;
21881ad6265SDimitry Andric 
21906c3fb27SDimitry Andric     auto ConstraintsIt = FlowConditionConstraints.find(Token);
220972a253aSDimitry Andric     if (ConstraintsIt == FlowConditionConstraints.end()) {
22106c3fb27SDimitry Andric       Constraints.insert(&arena().makeAtomRef(Token));
22281ad6265SDimitry Andric     } else {
22381ad6265SDimitry Andric       // Bind flow condition token via `iff` to its set of constraints:
22481ad6265SDimitry Andric       // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
22506c3fb27SDimitry Andric       Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
22606c3fb27SDimitry Andric                                              *ConstraintsIt->second));
22781ad6265SDimitry Andric     }
22881ad6265SDimitry Andric 
229*5f757f3fSDimitry Andric     if (auto DepsIt = FlowConditionDeps.find(Token);
230*5f757f3fSDimitry Andric         DepsIt != FlowConditionDeps.end())
231*5f757f3fSDimitry Andric       for (Atom A : DepsIt->second)
232*5f757f3fSDimitry Andric         Remaining.push_back(A);
23381ad6265SDimitry Andric   }
23481ad6265SDimitry Andric }
235*5f757f3fSDimitry Andric 
236*5f757f3fSDimitry Andric static void printAtomList(const llvm::SmallVector<Atom> &Atoms,
237*5f757f3fSDimitry Andric                           llvm::raw_ostream &OS) {
238*5f757f3fSDimitry Andric   OS << "(";
239*5f757f3fSDimitry Andric   for (size_t i = 0; i < Atoms.size(); ++i) {
240*5f757f3fSDimitry Andric     OS << Atoms[i];
241*5f757f3fSDimitry Andric     if (i + 1 < Atoms.size())
242*5f757f3fSDimitry Andric       OS << ", ";
243*5f757f3fSDimitry Andric   }
244*5f757f3fSDimitry Andric   OS << ")\n";
24581ad6265SDimitry Andric }
24681ad6265SDimitry Andric 
24706c3fb27SDimitry Andric void DataflowAnalysisContext::dumpFlowCondition(Atom Token,
24806c3fb27SDimitry Andric                                                 llvm::raw_ostream &OS) {
24906c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
25006c3fb27SDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
251*5f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
252fcaf7f86SDimitry Andric 
253*5f757f3fSDimitry Andric   OS << "Flow condition token: " << Token << "\n";
254*5f757f3fSDimitry Andric   SimplifyConstraintsInfo Info;
255*5f757f3fSDimitry Andric   llvm::SetVector<const Formula *> OriginalConstraints = Constraints;
256*5f757f3fSDimitry Andric   simplifyConstraints(Constraints, arena(), &Info);
257*5f757f3fSDimitry Andric   if (!Constraints.empty()) {
258*5f757f3fSDimitry Andric     OS << "Constraints:\n";
25906c3fb27SDimitry Andric     for (const auto *Constraint : Constraints) {
260*5f757f3fSDimitry Andric       Constraint->print(OS);
261*5f757f3fSDimitry Andric       OS << "\n";
262*5f757f3fSDimitry Andric     }
263*5f757f3fSDimitry Andric   }
264*5f757f3fSDimitry Andric   if (!Info.TrueAtoms.empty()) {
265*5f757f3fSDimitry Andric     OS << "True atoms: ";
266*5f757f3fSDimitry Andric     printAtomList(Info.TrueAtoms, OS);
267*5f757f3fSDimitry Andric   }
268*5f757f3fSDimitry Andric   if (!Info.FalseAtoms.empty()) {
269*5f757f3fSDimitry Andric     OS << "False atoms: ";
270*5f757f3fSDimitry Andric     printAtomList(Info.FalseAtoms, OS);
271*5f757f3fSDimitry Andric   }
272*5f757f3fSDimitry Andric   if (!Info.EquivalentAtoms.empty()) {
273*5f757f3fSDimitry Andric     OS << "Equivalent atoms:\n";
274*5f757f3fSDimitry Andric     for (const llvm::SmallVector<Atom> &Class : Info.EquivalentAtoms)
275*5f757f3fSDimitry Andric       printAtomList(Class, OS);
276*5f757f3fSDimitry Andric   }
277*5f757f3fSDimitry Andric 
278*5f757f3fSDimitry Andric   OS << "\nFlow condition constraints before simplification:\n";
279*5f757f3fSDimitry Andric   for (const auto *Constraint : OriginalConstraints) {
280*5f757f3fSDimitry Andric     Constraint->print(OS);
28106c3fb27SDimitry Andric     OS << "\n";
28206c3fb27SDimitry Andric   }
283fcaf7f86SDimitry Andric }
284fcaf7f86SDimitry Andric 
285bdd1243dSDimitry Andric const ControlFlowContext *
286bdd1243dSDimitry Andric DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
287bdd1243dSDimitry Andric   // Canonicalize the key:
288bdd1243dSDimitry Andric   F = F->getDefinition();
289bdd1243dSDimitry Andric   if (F == nullptr)
290bdd1243dSDimitry Andric     return nullptr;
291bdd1243dSDimitry Andric   auto It = FunctionContexts.find(F);
292bdd1243dSDimitry Andric   if (It != FunctionContexts.end())
293bdd1243dSDimitry Andric     return &It->second;
294bdd1243dSDimitry Andric 
29506c3fb27SDimitry Andric   if (F->hasBody()) {
29606c3fb27SDimitry Andric     auto CFCtx = ControlFlowContext::build(*F);
297bdd1243dSDimitry Andric     // FIXME: Handle errors.
298bdd1243dSDimitry Andric     assert(CFCtx);
299bdd1243dSDimitry Andric     auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
300bdd1243dSDimitry Andric     return &Result.first->second;
301bdd1243dSDimitry Andric   }
302bdd1243dSDimitry Andric 
303bdd1243dSDimitry Andric   return nullptr;
304bdd1243dSDimitry Andric }
305bdd1243dSDimitry Andric 
30606c3fb27SDimitry Andric static std::unique_ptr<Logger> makeLoggerFromCommandLine() {
30706c3fb27SDimitry Andric   if (DataflowLog.empty())
30806c3fb27SDimitry Andric     return Logger::textual(llvm::errs());
30906c3fb27SDimitry Andric 
31006c3fb27SDimitry Andric   llvm::StringRef Dir = DataflowLog;
31106c3fb27SDimitry Andric   if (auto EC = llvm::sys::fs::create_directories(Dir))
31206c3fb27SDimitry Andric     llvm::errs() << "Failed to create log dir: " << EC.message() << "\n";
31306c3fb27SDimitry Andric   // All analysis runs within a process will log to the same directory.
31406c3fb27SDimitry Andric   // Share a counter so they don't all overwrite each other's 0.html.
31506c3fb27SDimitry Andric   // (Don't share a logger, it's not threadsafe).
31606c3fb27SDimitry Andric   static std::atomic<unsigned> Counter = {0};
31706c3fb27SDimitry Andric   auto StreamFactory =
31806c3fb27SDimitry Andric       [Dir(Dir.str())]() mutable -> std::unique_ptr<llvm::raw_ostream> {
31906c3fb27SDimitry Andric     llvm::SmallString<256> File(Dir);
32006c3fb27SDimitry Andric     llvm::sys::path::append(File,
32106c3fb27SDimitry Andric                             std::to_string(Counter.fetch_add(1)) + ".html");
32206c3fb27SDimitry Andric     std::error_code EC;
32306c3fb27SDimitry Andric     auto OS = std::make_unique<llvm::raw_fd_ostream>(File, EC);
32406c3fb27SDimitry Andric     if (EC) {
32506c3fb27SDimitry Andric       llvm::errs() << "Failed to create log " << File << ": " << EC.message()
32606c3fb27SDimitry Andric                    << "\n";
32706c3fb27SDimitry Andric       return std::make_unique<llvm::raw_null_ostream>();
32806c3fb27SDimitry Andric     }
32906c3fb27SDimitry Andric     return OS;
33006c3fb27SDimitry Andric   };
33106c3fb27SDimitry Andric   return Logger::html(std::move(StreamFactory));
33206c3fb27SDimitry Andric }
33306c3fb27SDimitry Andric 
33406c3fb27SDimitry Andric DataflowAnalysisContext::DataflowAnalysisContext(std::unique_ptr<Solver> S,
33506c3fb27SDimitry Andric                                                  Options Opts)
33606c3fb27SDimitry Andric     : S(std::move(S)), A(std::make_unique<Arena>()), Opts(Opts) {
33706c3fb27SDimitry Andric   assert(this->S != nullptr);
33806c3fb27SDimitry Andric   // If the -dataflow-log command-line flag was set, synthesize a logger.
33906c3fb27SDimitry Andric   // This is ugly but provides a uniform method for ad-hoc debugging dataflow-
34006c3fb27SDimitry Andric   // based tools.
34106c3fb27SDimitry Andric   if (Opts.Log == nullptr) {
34206c3fb27SDimitry Andric     if (DataflowLog.getNumOccurrences() > 0) {
34306c3fb27SDimitry Andric       LogOwner = makeLoggerFromCommandLine();
34406c3fb27SDimitry Andric       this->Opts.Log = LogOwner.get();
34506c3fb27SDimitry Andric       // FIXME: if the flag is given a value, write an HTML log to a file.
34606c3fb27SDimitry Andric     } else {
34706c3fb27SDimitry Andric       this->Opts.Log = &Logger::null();
34806c3fb27SDimitry Andric     }
34906c3fb27SDimitry Andric   }
35006c3fb27SDimitry Andric }
35106c3fb27SDimitry Andric 
35206c3fb27SDimitry Andric DataflowAnalysisContext::~DataflowAnalysisContext() = default;
35306c3fb27SDimitry Andric 
35481ad6265SDimitry Andric } // namespace dataflow
35581ad6265SDimitry Andric } // namespace clang
35681ad6265SDimitry Andric 
35781ad6265SDimitry Andric using namespace clang;
35881ad6265SDimitry Andric 
35981ad6265SDimitry Andric const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) {
36081ad6265SDimitry Andric   const Expr *Current = &E;
36181ad6265SDimitry Andric   if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
36281ad6265SDimitry Andric     Current = EWC->getSubExpr();
36381ad6265SDimitry Andric     assert(Current != nullptr);
36481ad6265SDimitry Andric   }
36581ad6265SDimitry Andric   Current = Current->IgnoreParens();
36681ad6265SDimitry Andric   assert(Current != nullptr);
36781ad6265SDimitry Andric   return *Current;
36881ad6265SDimitry Andric }
36981ad6265SDimitry Andric 
37081ad6265SDimitry Andric const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) {
37181ad6265SDimitry Andric   if (auto *E = dyn_cast<Expr>(&S))
37281ad6265SDimitry Andric     return ignoreCFGOmittedNodes(*E);
37381ad6265SDimitry Andric   return S;
37481ad6265SDimitry Andric }
37581ad6265SDimitry Andric 
37681ad6265SDimitry Andric // FIXME: Does not precisely handle non-virtual diamond inheritance. A single
37781ad6265SDimitry Andric // field decl will be modeled for all instances of the inherited field.
37806c3fb27SDimitry Andric static void getFieldsFromClassHierarchy(QualType Type,
37906c3fb27SDimitry Andric                                         clang::dataflow::FieldSet &Fields) {
38081ad6265SDimitry Andric   if (Type->isIncompleteType() || Type->isDependentType() ||
38181ad6265SDimitry Andric       !Type->isRecordType())
38281ad6265SDimitry Andric     return;
38381ad6265SDimitry Andric 
38481ad6265SDimitry Andric   for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
38581ad6265SDimitry Andric     Fields.insert(Field);
38681ad6265SDimitry Andric   if (auto *CXXRecord = Type->getAsCXXRecordDecl())
38781ad6265SDimitry Andric     for (const CXXBaseSpecifier &Base : CXXRecord->bases())
38881ad6265SDimitry Andric       getFieldsFromClassHierarchy(Base.getType(), Fields);
38981ad6265SDimitry Andric }
39081ad6265SDimitry Andric 
39181ad6265SDimitry Andric /// Gets the set of all fields in the type.
39206c3fb27SDimitry Andric clang::dataflow::FieldSet clang::dataflow::getObjectFields(QualType Type) {
39306c3fb27SDimitry Andric   FieldSet Fields;
39481ad6265SDimitry Andric   getFieldsFromClassHierarchy(Type, Fields);
39581ad6265SDimitry Andric   return Fields;
39681ad6265SDimitry Andric }
397*5f757f3fSDimitry Andric 
398*5f757f3fSDimitry Andric bool clang::dataflow::containsSameFields(
399*5f757f3fSDimitry Andric     const clang::dataflow::FieldSet &Fields,
400*5f757f3fSDimitry Andric     const clang::dataflow::RecordStorageLocation::FieldToLoc &FieldLocs) {
401*5f757f3fSDimitry Andric   if (Fields.size() != FieldLocs.size())
402*5f757f3fSDimitry Andric     return false;
403*5f757f3fSDimitry Andric   for ([[maybe_unused]] auto [Field, Loc] : FieldLocs)
404*5f757f3fSDimitry Andric     if (!Fields.contains(cast_or_null<FieldDecl>(Field)))
405*5f757f3fSDimitry Andric       return false;
406*5f757f3fSDimitry Andric   return true;
407*5f757f3fSDimitry Andric }
408