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