xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
17*0fca6ea1SDimitry Andric #include "clang/Analysis/FlowSensitive/ASTOps.h"
18fcaf7f86SDimitry Andric #include "clang/Analysis/FlowSensitive/DebugSupport.h"
1906c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Formula.h"
2006c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Logger.h"
215f757f3fSDimitry Andric #include "clang/Analysis/FlowSensitive/SimplifyConstraints.h"
2281ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
23bdd1243dSDimitry Andric #include "llvm/ADT/SetOperations.h"
2406c3fb27SDimitry Andric #include "llvm/ADT/SetVector.h"
2506c3fb27SDimitry Andric #include "llvm/Support/CommandLine.h"
26fcaf7f86SDimitry Andric #include "llvm/Support/Debug.h"
2706c3fb27SDimitry Andric #include "llvm/Support/FileSystem.h"
2806c3fb27SDimitry Andric #include "llvm/Support/Path.h"
2906c3fb27SDimitry Andric #include "llvm/Support/raw_ostream.h"
3081ad6265SDimitry Andric #include <cassert>
3181ad6265SDimitry Andric #include <memory>
3206c3fb27SDimitry Andric #include <string>
3381ad6265SDimitry Andric #include <utility>
3406c3fb27SDimitry Andric #include <vector>
3506c3fb27SDimitry Andric 
3606c3fb27SDimitry Andric static llvm::cl::opt<std::string> DataflowLog(
3706c3fb27SDimitry Andric     "dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional,
3806c3fb27SDimitry Andric     llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual "
3906c3fb27SDimitry Andric                    "log to stderr. With an arg, writes HTML logs under the "
4006c3fb27SDimitry Andric                    "specified directory (one per analyzed function)."));
4181ad6265SDimitry Andric 
4281ad6265SDimitry Andric namespace clang {
4381ad6265SDimitry Andric namespace dataflow {
4481ad6265SDimitry Andric 
4506c3fb27SDimitry Andric FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {
46bdd1243dSDimitry Andric   // During context-sensitive analysis, a struct may be allocated in one
47bdd1243dSDimitry Andric   // function, but its field accessed in a function lower in the stack than
48bdd1243dSDimitry Andric   // the allocation. Since we only collect fields used in the function where
49bdd1243dSDimitry Andric   // the allocation occurs, we can't apply that filter when performing
50bdd1243dSDimitry Andric   // context-sensitive analysis. But, this only applies to storage locations,
51bdd1243dSDimitry Andric   // since field access it not allowed to fail. In contrast, field *values*
52bdd1243dSDimitry Andric   // don't need this allowance, since the API allows for uninitialized fields.
5306c3fb27SDimitry Andric   if (Opts.ContextSensitiveOpts)
5406c3fb27SDimitry Andric     return getObjectFields(Type);
5506c3fb27SDimitry Andric 
5606c3fb27SDimitry Andric   return llvm::set_intersection(getObjectFields(Type), ModeledFields);
5781ad6265SDimitry Andric }
5806c3fb27SDimitry Andric 
5906c3fb27SDimitry Andric void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) {
6006c3fb27SDimitry Andric   ModeledFields.set_union(Fields);
6106c3fb27SDimitry Andric }
6206c3fb27SDimitry Andric 
6306c3fb27SDimitry Andric StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {
6406c3fb27SDimitry Andric   if (!Type.isNull() && Type->isRecordType()) {
6506c3fb27SDimitry Andric     llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
6606c3fb27SDimitry Andric     for (const FieldDecl *Field : getModeledFields(Type))
6706c3fb27SDimitry Andric       if (Field->getType()->isReferenceType())
6806c3fb27SDimitry Andric         FieldLocs.insert({Field, nullptr});
6906c3fb27SDimitry Andric       else
7006c3fb27SDimitry Andric         FieldLocs.insert({Field, &createStorageLocation(
7106c3fb27SDimitry Andric                                      Field->getType().getNonReferenceType())});
725f757f3fSDimitry Andric 
735f757f3fSDimitry Andric     RecordStorageLocation::SyntheticFieldMap SyntheticFields;
745f757f3fSDimitry Andric     for (const auto &Entry : getSyntheticFields(Type))
755f757f3fSDimitry Andric       SyntheticFields.insert(
765f757f3fSDimitry Andric           {Entry.getKey(),
775f757f3fSDimitry Andric            &createStorageLocation(Entry.getValue().getNonReferenceType())});
785f757f3fSDimitry Andric 
795f757f3fSDimitry Andric     return createRecordStorageLocation(Type, std::move(FieldLocs),
805f757f3fSDimitry Andric                                        std::move(SyntheticFields));
8106c3fb27SDimitry Andric   }
8206c3fb27SDimitry Andric   return arena().create<ScalarStorageLocation>(Type);
8381ad6265SDimitry Andric }
8481ad6265SDimitry Andric 
855f757f3fSDimitry Andric // Returns the keys for a given `StringMap`.
865f757f3fSDimitry Andric // Can't use `StringSet` as the return type as it doesn't support `operator==`.
875f757f3fSDimitry Andric template <typename T>
885f757f3fSDimitry Andric static llvm::DenseSet<llvm::StringRef> getKeys(const llvm::StringMap<T> &Map) {
895f757f3fSDimitry Andric   return llvm::DenseSet<llvm::StringRef>(Map.keys().begin(), Map.keys().end());
905f757f3fSDimitry Andric }
915f757f3fSDimitry Andric 
925f757f3fSDimitry Andric RecordStorageLocation &DataflowAnalysisContext::createRecordStorageLocation(
935f757f3fSDimitry Andric     QualType Type, RecordStorageLocation::FieldToLoc FieldLocs,
945f757f3fSDimitry Andric     RecordStorageLocation::SyntheticFieldMap SyntheticFields) {
955f757f3fSDimitry Andric   assert(Type->isRecordType());
965f757f3fSDimitry Andric   assert(containsSameFields(getModeledFields(Type), FieldLocs));
975f757f3fSDimitry Andric   assert(getKeys(getSyntheticFields(Type)) == getKeys(SyntheticFields));
985f757f3fSDimitry Andric 
995f757f3fSDimitry Andric   RecordStorageLocationCreated = true;
1005f757f3fSDimitry Andric   return arena().create<RecordStorageLocation>(Type, std::move(FieldLocs),
1015f757f3fSDimitry Andric                                                std::move(SyntheticFields));
1025f757f3fSDimitry Andric }
1035f757f3fSDimitry Andric 
10481ad6265SDimitry Andric StorageLocation &
1055f757f3fSDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) {
1065f757f3fSDimitry Andric   if (auto *Loc = DeclToLoc.lookup(&D))
10781ad6265SDimitry Andric     return *Loc;
10806c3fb27SDimitry Andric   auto &Loc = createStorageLocation(D.getType().getNonReferenceType());
1095f757f3fSDimitry Andric   DeclToLoc[&D] = &Loc;
11081ad6265SDimitry Andric   return Loc;
11181ad6265SDimitry Andric }
11281ad6265SDimitry Andric 
11381ad6265SDimitry Andric StorageLocation &
11481ad6265SDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
1155f757f3fSDimitry Andric   const Expr &CanonE = ignoreCFGOmittedNodes(E);
1165f757f3fSDimitry Andric 
1175f757f3fSDimitry Andric   if (auto *Loc = ExprToLoc.lookup(&CanonE))
11881ad6265SDimitry Andric     return *Loc;
1195f757f3fSDimitry Andric   auto &Loc = createStorageLocation(CanonE.getType());
1205f757f3fSDimitry Andric   ExprToLoc[&CanonE] = &Loc;
12181ad6265SDimitry Andric   return Loc;
12281ad6265SDimitry Andric }
12381ad6265SDimitry Andric 
12481ad6265SDimitry Andric PointerValue &
12581ad6265SDimitry Andric DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
126753f127fSDimitry Andric   auto CanonicalPointeeType =
127753f127fSDimitry Andric       PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
12881ad6265SDimitry Andric   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
12981ad6265SDimitry Andric   if (Res.second) {
130bdd1243dSDimitry Andric     auto &PointeeLoc = createStorageLocation(CanonicalPointeeType);
13106c3fb27SDimitry Andric     Res.first->second = &arena().create<PointerValue>(PointeeLoc);
13281ad6265SDimitry Andric   }
13381ad6265SDimitry Andric   return *Res.first->second;
13481ad6265SDimitry Andric }
13581ad6265SDimitry Andric 
1365f757f3fSDimitry Andric void DataflowAnalysisContext::addInvariant(const Formula &Constraint) {
1375f757f3fSDimitry Andric   if (Invariant == nullptr)
1385f757f3fSDimitry Andric     Invariant = &Constraint;
1395f757f3fSDimitry Andric   else
1405f757f3fSDimitry Andric     Invariant = &arena().makeAnd(*Invariant, Constraint);
1415f757f3fSDimitry Andric }
1425f757f3fSDimitry Andric 
14381ad6265SDimitry Andric void DataflowAnalysisContext::addFlowConditionConstraint(
14406c3fb27SDimitry Andric     Atom Token, const Formula &Constraint) {
14506c3fb27SDimitry Andric   auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint);
14681ad6265SDimitry Andric   if (!Res.second) {
14706c3fb27SDimitry Andric     Res.first->second =
14806c3fb27SDimitry Andric         &arena().makeAnd(*Res.first->second, Constraint);
14981ad6265SDimitry Andric   }
15081ad6265SDimitry Andric }
15181ad6265SDimitry Andric 
15206c3fb27SDimitry Andric Atom DataflowAnalysisContext::forkFlowCondition(Atom Token) {
15306c3fb27SDimitry Andric   Atom ForkToken = arena().makeFlowConditionToken();
15406c3fb27SDimitry Andric   FlowConditionDeps[ForkToken].insert(Token);
15506c3fb27SDimitry Andric   addFlowConditionConstraint(ForkToken, arena().makeAtomRef(Token));
15681ad6265SDimitry Andric   return ForkToken;
15781ad6265SDimitry Andric }
15881ad6265SDimitry Andric 
15906c3fb27SDimitry Andric Atom
16006c3fb27SDimitry Andric DataflowAnalysisContext::joinFlowConditions(Atom FirstToken,
16106c3fb27SDimitry Andric                                             Atom SecondToken) {
16206c3fb27SDimitry Andric   Atom Token = arena().makeFlowConditionToken();
16306c3fb27SDimitry Andric   FlowConditionDeps[Token].insert(FirstToken);
16406c3fb27SDimitry Andric   FlowConditionDeps[Token].insert(SecondToken);
16581ad6265SDimitry Andric   addFlowConditionConstraint(Token,
16606c3fb27SDimitry Andric                              arena().makeOr(arena().makeAtomRef(FirstToken),
16706c3fb27SDimitry Andric                                             arena().makeAtomRef(SecondToken)));
16881ad6265SDimitry Andric   return Token;
16981ad6265SDimitry Andric }
17081ad6265SDimitry Andric 
17106c3fb27SDimitry Andric Solver::Result DataflowAnalysisContext::querySolver(
17206c3fb27SDimitry Andric     llvm::SetVector<const Formula *> Constraints) {
173*0fca6ea1SDimitry Andric   return S.solve(Constraints.getArrayRef());
17481ad6265SDimitry Andric }
17581ad6265SDimitry Andric 
17606c3fb27SDimitry Andric bool DataflowAnalysisContext::flowConditionImplies(Atom Token,
1775f757f3fSDimitry Andric                                                    const Formula &F) {
1787a6dacacSDimitry Andric   if (F.isLiteral(true))
1797a6dacacSDimitry Andric     return true;
1807a6dacacSDimitry Andric 
18181ad6265SDimitry Andric   // Returns true if and only if truth assignment of the flow condition implies
1825f757f3fSDimitry Andric   // that `F` is also true. We prove whether or not this property holds by
18381ad6265SDimitry Andric   // reducing the problem to satisfiability checking. In other words, we attempt
1845f757f3fSDimitry Andric   // to show that assuming `F` is false makes the constraints induced by the
18581ad6265SDimitry Andric   // flow condition unsatisfiable.
18606c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
18706c3fb27SDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
1885f757f3fSDimitry Andric   Constraints.insert(&arena().makeNot(F));
1895f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
19081ad6265SDimitry Andric   return isUnsatisfiable(std::move(Constraints));
19181ad6265SDimitry Andric }
19281ad6265SDimitry Andric 
1935f757f3fSDimitry Andric bool DataflowAnalysisContext::flowConditionAllows(Atom Token,
1945f757f3fSDimitry Andric                                                   const Formula &F) {
1957a6dacacSDimitry Andric   if (F.isLiteral(false))
1967a6dacacSDimitry Andric     return false;
1977a6dacacSDimitry Andric 
19806c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
1995f757f3fSDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
2005f757f3fSDimitry Andric   Constraints.insert(&F);
2015f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
2025f757f3fSDimitry Andric   return isSatisfiable(std::move(Constraints));
20381ad6265SDimitry Andric }
20481ad6265SDimitry Andric 
20506c3fb27SDimitry Andric bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
20606c3fb27SDimitry Andric                                                  const Formula &Val2) {
20706c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
20806c3fb27SDimitry Andric   Constraints.insert(&arena().makeNot(arena().makeEquals(Val1, Val2)));
20906c3fb27SDimitry Andric   return isUnsatisfiable(std::move(Constraints));
21081ad6265SDimitry Andric }
21181ad6265SDimitry Andric 
21281ad6265SDimitry Andric void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
2135f757f3fSDimitry Andric     Atom Token, llvm::SetVector<const Formula *> &Constraints) {
2145f757f3fSDimitry Andric   llvm::DenseSet<Atom> AddedTokens;
2155f757f3fSDimitry Andric   std::vector<Atom> Remaining = {Token};
2165f757f3fSDimitry Andric 
2175f757f3fSDimitry Andric   if (Invariant)
2185f757f3fSDimitry Andric     Constraints.insert(Invariant);
2195f757f3fSDimitry Andric   // Define all the flow conditions that might be referenced in constraints.
2205f757f3fSDimitry Andric   while (!Remaining.empty()) {
2215f757f3fSDimitry Andric     auto Token = Remaining.back();
2225f757f3fSDimitry Andric     Remaining.pop_back();
2235f757f3fSDimitry Andric     if (!AddedTokens.insert(Token).second)
2245f757f3fSDimitry Andric       continue;
22581ad6265SDimitry Andric 
22606c3fb27SDimitry Andric     auto ConstraintsIt = FlowConditionConstraints.find(Token);
227972a253aSDimitry Andric     if (ConstraintsIt == FlowConditionConstraints.end()) {
22806c3fb27SDimitry Andric       Constraints.insert(&arena().makeAtomRef(Token));
22981ad6265SDimitry Andric     } else {
23081ad6265SDimitry Andric       // Bind flow condition token via `iff` to its set of constraints:
23181ad6265SDimitry Andric       // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
23206c3fb27SDimitry Andric       Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
23306c3fb27SDimitry Andric                                              *ConstraintsIt->second));
23481ad6265SDimitry Andric     }
23581ad6265SDimitry Andric 
2365f757f3fSDimitry Andric     if (auto DepsIt = FlowConditionDeps.find(Token);
2375f757f3fSDimitry Andric         DepsIt != FlowConditionDeps.end())
2385f757f3fSDimitry Andric       for (Atom A : DepsIt->second)
2395f757f3fSDimitry Andric         Remaining.push_back(A);
24081ad6265SDimitry Andric   }
24181ad6265SDimitry Andric }
2425f757f3fSDimitry Andric 
2435f757f3fSDimitry Andric static void printAtomList(const llvm::SmallVector<Atom> &Atoms,
2445f757f3fSDimitry Andric                           llvm::raw_ostream &OS) {
2455f757f3fSDimitry Andric   OS << "(";
2465f757f3fSDimitry Andric   for (size_t i = 0; i < Atoms.size(); ++i) {
2475f757f3fSDimitry Andric     OS << Atoms[i];
2485f757f3fSDimitry Andric     if (i + 1 < Atoms.size())
2495f757f3fSDimitry Andric       OS << ", ";
2505f757f3fSDimitry Andric   }
2515f757f3fSDimitry Andric   OS << ")\n";
25281ad6265SDimitry Andric }
25381ad6265SDimitry Andric 
25406c3fb27SDimitry Andric void DataflowAnalysisContext::dumpFlowCondition(Atom Token,
25506c3fb27SDimitry Andric                                                 llvm::raw_ostream &OS) {
25606c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
25706c3fb27SDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
2585f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
259fcaf7f86SDimitry Andric 
2605f757f3fSDimitry Andric   OS << "Flow condition token: " << Token << "\n";
2615f757f3fSDimitry Andric   SimplifyConstraintsInfo Info;
2625f757f3fSDimitry Andric   llvm::SetVector<const Formula *> OriginalConstraints = Constraints;
2635f757f3fSDimitry Andric   simplifyConstraints(Constraints, arena(), &Info);
2645f757f3fSDimitry Andric   if (!Constraints.empty()) {
2655f757f3fSDimitry Andric     OS << "Constraints:\n";
26606c3fb27SDimitry Andric     for (const auto *Constraint : Constraints) {
2675f757f3fSDimitry Andric       Constraint->print(OS);
2685f757f3fSDimitry Andric       OS << "\n";
2695f757f3fSDimitry Andric     }
2705f757f3fSDimitry Andric   }
2715f757f3fSDimitry Andric   if (!Info.TrueAtoms.empty()) {
2725f757f3fSDimitry Andric     OS << "True atoms: ";
2735f757f3fSDimitry Andric     printAtomList(Info.TrueAtoms, OS);
2745f757f3fSDimitry Andric   }
2755f757f3fSDimitry Andric   if (!Info.FalseAtoms.empty()) {
2765f757f3fSDimitry Andric     OS << "False atoms: ";
2775f757f3fSDimitry Andric     printAtomList(Info.FalseAtoms, OS);
2785f757f3fSDimitry Andric   }
2795f757f3fSDimitry Andric   if (!Info.EquivalentAtoms.empty()) {
2805f757f3fSDimitry Andric     OS << "Equivalent atoms:\n";
2815f757f3fSDimitry Andric     for (const llvm::SmallVector<Atom> &Class : Info.EquivalentAtoms)
2825f757f3fSDimitry Andric       printAtomList(Class, OS);
2835f757f3fSDimitry Andric   }
2845f757f3fSDimitry Andric 
2855f757f3fSDimitry Andric   OS << "\nFlow condition constraints before simplification:\n";
2865f757f3fSDimitry Andric   for (const auto *Constraint : OriginalConstraints) {
2875f757f3fSDimitry Andric     Constraint->print(OS);
28806c3fb27SDimitry Andric     OS << "\n";
28906c3fb27SDimitry Andric   }
290fcaf7f86SDimitry Andric }
291fcaf7f86SDimitry Andric 
292*0fca6ea1SDimitry Andric const AdornedCFG *
293*0fca6ea1SDimitry Andric DataflowAnalysisContext::getAdornedCFG(const FunctionDecl *F) {
294bdd1243dSDimitry Andric   // Canonicalize the key:
295bdd1243dSDimitry Andric   F = F->getDefinition();
296bdd1243dSDimitry Andric   if (F == nullptr)
297bdd1243dSDimitry Andric     return nullptr;
298bdd1243dSDimitry Andric   auto It = FunctionContexts.find(F);
299bdd1243dSDimitry Andric   if (It != FunctionContexts.end())
300bdd1243dSDimitry Andric     return &It->second;
301bdd1243dSDimitry Andric 
3027a6dacacSDimitry Andric   if (F->doesThisDeclarationHaveABody()) {
303*0fca6ea1SDimitry Andric     auto ACFG = AdornedCFG::build(*F);
304bdd1243dSDimitry Andric     // FIXME: Handle errors.
305*0fca6ea1SDimitry Andric     assert(ACFG);
306*0fca6ea1SDimitry Andric     auto Result = FunctionContexts.insert({F, std::move(*ACFG)});
307bdd1243dSDimitry Andric     return &Result.first->second;
308bdd1243dSDimitry Andric   }
309bdd1243dSDimitry Andric 
310bdd1243dSDimitry Andric   return nullptr;
311bdd1243dSDimitry Andric }
312bdd1243dSDimitry Andric 
31306c3fb27SDimitry Andric static std::unique_ptr<Logger> makeLoggerFromCommandLine() {
31406c3fb27SDimitry Andric   if (DataflowLog.empty())
31506c3fb27SDimitry Andric     return Logger::textual(llvm::errs());
31606c3fb27SDimitry Andric 
31706c3fb27SDimitry Andric   llvm::StringRef Dir = DataflowLog;
31806c3fb27SDimitry Andric   if (auto EC = llvm::sys::fs::create_directories(Dir))
31906c3fb27SDimitry Andric     llvm::errs() << "Failed to create log dir: " << EC.message() << "\n";
32006c3fb27SDimitry Andric   // All analysis runs within a process will log to the same directory.
32106c3fb27SDimitry Andric   // Share a counter so they don't all overwrite each other's 0.html.
32206c3fb27SDimitry Andric   // (Don't share a logger, it's not threadsafe).
32306c3fb27SDimitry Andric   static std::atomic<unsigned> Counter = {0};
32406c3fb27SDimitry Andric   auto StreamFactory =
32506c3fb27SDimitry Andric       [Dir(Dir.str())]() mutable -> std::unique_ptr<llvm::raw_ostream> {
32606c3fb27SDimitry Andric     llvm::SmallString<256> File(Dir);
32706c3fb27SDimitry Andric     llvm::sys::path::append(File,
32806c3fb27SDimitry Andric                             std::to_string(Counter.fetch_add(1)) + ".html");
32906c3fb27SDimitry Andric     std::error_code EC;
33006c3fb27SDimitry Andric     auto OS = std::make_unique<llvm::raw_fd_ostream>(File, EC);
33106c3fb27SDimitry Andric     if (EC) {
33206c3fb27SDimitry Andric       llvm::errs() << "Failed to create log " << File << ": " << EC.message()
33306c3fb27SDimitry Andric                    << "\n";
33406c3fb27SDimitry Andric       return std::make_unique<llvm::raw_null_ostream>();
33506c3fb27SDimitry Andric     }
33606c3fb27SDimitry Andric     return OS;
33706c3fb27SDimitry Andric   };
33806c3fb27SDimitry Andric   return Logger::html(std::move(StreamFactory));
33906c3fb27SDimitry Andric }
34006c3fb27SDimitry Andric 
341*0fca6ea1SDimitry Andric DataflowAnalysisContext::DataflowAnalysisContext(
342*0fca6ea1SDimitry Andric     Solver &S, std::unique_ptr<Solver> &&OwnedSolver, Options Opts)
343*0fca6ea1SDimitry Andric     : S(S), OwnedSolver(std::move(OwnedSolver)), A(std::make_unique<Arena>()),
344*0fca6ea1SDimitry Andric       Opts(Opts) {
34506c3fb27SDimitry Andric   // If the -dataflow-log command-line flag was set, synthesize a logger.
34606c3fb27SDimitry Andric   // This is ugly but provides a uniform method for ad-hoc debugging dataflow-
34706c3fb27SDimitry Andric   // based tools.
34806c3fb27SDimitry Andric   if (Opts.Log == nullptr) {
34906c3fb27SDimitry Andric     if (DataflowLog.getNumOccurrences() > 0) {
35006c3fb27SDimitry Andric       LogOwner = makeLoggerFromCommandLine();
35106c3fb27SDimitry Andric       this->Opts.Log = LogOwner.get();
35206c3fb27SDimitry Andric       // FIXME: if the flag is given a value, write an HTML log to a file.
35306c3fb27SDimitry Andric     } else {
35406c3fb27SDimitry Andric       this->Opts.Log = &Logger::null();
35506c3fb27SDimitry Andric     }
35606c3fb27SDimitry Andric   }
35706c3fb27SDimitry Andric }
35806c3fb27SDimitry Andric 
35906c3fb27SDimitry Andric DataflowAnalysisContext::~DataflowAnalysisContext() = default;
36006c3fb27SDimitry Andric 
36181ad6265SDimitry Andric } // namespace dataflow
36281ad6265SDimitry Andric } // namespace clang
363