brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.9 KiB · 2ba8437 Raw
595 lines · c
1//===--- TestingSupport.h - Testing utils for dataflow analyses -*- 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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines utilities to simplify testing of dataflow analyses.10//11//===----------------------------------------------------------------------===//12 13#ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_14#define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_15 16#include <functional>17#include <memory>18#include <optional>19#include <ostream>20#include <string>21#include <utility>22#include <vector>23 24#include "clang/AST/ASTContext.h"25#include "clang/AST/Decl.h"26#include "clang/AST/Stmt.h"27#include "clang/ASTMatchers/ASTMatchFinder.h"28#include "clang/ASTMatchers/ASTMatchers.h"29#include "clang/ASTMatchers/ASTMatchersInternal.h"30#include "clang/Analysis/CFG.h"31#include "clang/Analysis/FlowSensitive/AdornedCFG.h"32#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"33#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"34#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"35#include "clang/Analysis/FlowSensitive/MatchSwitch.h"36#include "clang/Analysis/FlowSensitive/NoopLattice.h"37#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"38#include "clang/Basic/LLVM.h"39#include "clang/Serialization/PCHContainerOperations.h"40#include "clang/Tooling/ArgumentsAdjusters.h"41#include "clang/Tooling/Tooling.h"42#include "llvm/ADT/ArrayRef.h"43#include "llvm/ADT/DenseMap.h"44#include "llvm/ADT/StringMap.h"45#include "llvm/ADT/StringRef.h"46#include "llvm/Support/Allocator.h"47#include "llvm/Support/Errc.h"48#include "llvm/Support/Error.h"49#include "llvm/Testing/Annotations/Annotations.h"50 51namespace clang {52namespace dataflow {53 54// Requires a `<<` operator for the `Lattice` type.55// FIXME: move to a non-test utility library.56template <typename Lattice>57std::ostream &operator<<(std::ostream &OS,58                         const DataflowAnalysisState<Lattice> &S) {59  // FIXME: add printing support for the environment.60  return OS << "{lattice=" << S.Lattice << ", environment=...}";61}62 63namespace test {64 65// Caps the number of block visits in any individual analysis. Given that test66// code is typically quite small, we set a low number to help catch any problems67// early. But, the choice is arbitrary.68constexpr std::int32_t MaxBlockVisitsInAnalysis = 2'000;69 70/// Returns the environment at the program point marked with `Annotation` from71/// the mapping of annotated program points to analysis state.72///73/// Requirements:74///75///   `Annotation` must be present as a key in `AnnotationStates`.76template <typename LatticeT>77const Environment &getEnvironmentAtAnnotation(78    const llvm::StringMap<DataflowAnalysisState<LatticeT>> &AnnotationStates,79    llvm::StringRef Annotation) {80  auto It = AnnotationStates.find(Annotation);81  assert(It != AnnotationStates.end());82  return It->getValue().Env;83}84 85/// Contains data structures required and produced by a dataflow analysis run.86struct AnalysisOutputs {87  /// Input code that is analyzed. Points within the code may be marked with88  /// annotations to facilitate testing.89  ///90  /// Example:91  /// void target(int *x) {92  ///   *x; // [[p]]93  /// }94  /// From the annotation `p`, the line number and analysis state immediately95  /// after the statement `*x` can be retrieved and verified.96  llvm::Annotations Code;97  /// AST context generated from `Code`.98  ASTContext &ASTCtx;99  /// The function whose body is analyzed.100  const FunctionDecl *Target;101  /// Contains the control flow graph built from the body of the `Target`102  /// function and is analyzed.103  const AdornedCFG &ACFG;104  /// The analysis to be run.105  TypeErasedDataflowAnalysis &Analysis;106  /// Initial state to start the analysis.107  const Environment &InitEnv;108  // Stores the state of a CFG block if it has been evaluated by the analysis.109  // The indices correspond to the block IDs.110  llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>> BlockStates;111};112 113/// A callback to be called with the state before or after visiting a CFG114/// element.115/// This differs from `DiagnosisCallback` in that the return type is void.116template <typename AnalysisT>117using DiagnosisCallbackForTesting = std::function<void(118    ASTContext &, const CFGElement &,119    const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>;120 121/// A pair of callbacks to be called with the state before and after visiting a122/// CFG element.123/// Either or both of the callbacks may be null.124template <typename AnalysisT> struct DiagnosisCallbacksForTesting {125  DiagnosisCallbackForTesting<AnalysisT> Before;126  DiagnosisCallbackForTesting<AnalysisT> After;127};128 129/// Arguments for building the dataflow analysis.130template <typename AnalysisT> struct AnalysisInputs {131  /// Required fields are set in constructor.132  AnalysisInputs(133      llvm::StringRef CodeArg,134      ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcherArg,135      std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysisArg)136      : Code(CodeArg), TargetFuncMatcher(std::move(TargetFuncMatcherArg)),137        MakeAnalysis(std::move(MakeAnalysisArg)) {}138 139  /// Optional fields can be set with methods of the form `withFieldName(...)`.140  AnalysisInputs<AnalysisT> &&141  withSetupTest(std::function<llvm::Error(AnalysisOutputs &)> Arg) && {142    SetupTest = std::move(Arg);143    return std::move(*this);144  }145  AnalysisInputs<AnalysisT> &&146  withDiagnosisCallbacks(DiagnosisCallbacksForTesting<AnalysisT> Arg) && {147    Callbacks = std::move(Arg);148    return std::move(*this);149  }150  /// Provided for backwards compatibility. New callers should use151  /// `withDiagnosisCallbacks()`.152  AnalysisInputs<AnalysisT> &&153  withPostVisitCFG(DiagnosisCallbackForTesting<AnalysisT> Arg) && {154    Callbacks.After = std::move(Arg);155    return std::move(*this);156  }157  AnalysisInputs<AnalysisT> &&withASTBuildArgs(ArrayRef<std::string> Arg) && {158    ASTBuildArgs = std::move(Arg);159    return std::move(*this);160  }161  AnalysisInputs<AnalysisT> &&162  withASTBuildVirtualMappedFiles(tooling::FileContentMappings Arg) && {163    ASTBuildVirtualMappedFiles = std::move(Arg);164    return std::move(*this);165  }166  AnalysisInputs<AnalysisT> &&167  withBuiltinOptions(DataflowAnalysisContext::Options Options) && {168    BuiltinOptions = std::move(Options);169    return std::move(*this);170  }171  AnalysisInputs<AnalysisT> &&172  withSolverFactory(std::function<std::unique_ptr<Solver>()> Factory) && {173    assert(Factory);174    SolverFactory = std::move(Factory);175    return std::move(*this);176  }177 178  /// Required. Input code that is analyzed.179  llvm::StringRef Code;180  /// Required. All functions that match this matcher are analyzed.181  ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher;182  /// Required. The analysis to be run is constructed with this function that183  /// takes as argument the AST generated from the code being analyzed and the184  /// initial state from which the analysis starts with.185  std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis;186  /// Optional. If provided, this function is executed immediately before187  /// running the dataflow analysis to allow for additional setup. All fields in188  /// the `AnalysisOutputs` argument will be initialized except for the189  /// `BlockStates` field which is only computed later during the analysis.190  std::function<llvm::Error(AnalysisOutputs &)> SetupTest = nullptr;191  /// Callbacks to run on each CFG element after the analysis has been run.192  DiagnosisCallbacksForTesting<AnalysisT> Callbacks;193 194  /// Optional. Options for building the AST context.195  ArrayRef<std::string> ASTBuildArgs = {};196  /// Optional. Options for building the AST context.197  tooling::FileContentMappings ASTBuildVirtualMappedFiles = {};198  /// Configuration options for the built-in model.199  DataflowAnalysisContext::Options BuiltinOptions;200  /// SAT solver factory.201  std::function<std::unique_ptr<Solver>()> SolverFactory = [] {202    return std::make_unique<WatchedLiteralsSolver>();203  };204};205 206/// Returns assertions based on annotations that are present after statements in207/// `AnnotatedCode`.208llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>209buildStatementToAnnotationMapping(const FunctionDecl *Func,210                                  llvm::Annotations AnnotatedCode);211 212/// Returns line numbers and content of the annotations in `AnnotatedCode`213/// within the token range `BoundingRange`.214llvm::DenseMap<unsigned, std::string> buildLineToAnnotationMapping(215    const SourceManager &SM, const LangOptions &LangOpts,216    SourceRange BoundingRange, llvm::Annotations AnnotatedCode);217 218/// Runs dataflow specified from `AI.MakeAnalysis` and `AI.Callbacks` on all219/// functions that match `AI.TargetFuncMatcher` in `AI.Code`.  Given the220/// analysis outputs, `VerifyResults` checks that the results from the analysis221/// are correct.222///223/// Requirements:224///225///   `AnalysisT` contains a type `Lattice`.226///227///   `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`.228///229///   `VerifyResults` must be provided.230template <typename AnalysisT>231llvm::Error232checkDataflow(AnalysisInputs<AnalysisT> AI,233              std::function<void(const AnalysisOutputs &)> VerifyResults) {234  // Build AST context from code.235  llvm::Annotations AnnotatedCode(AI.Code);236  auto Unit = tooling::buildASTFromCodeWithArgs(237      AnnotatedCode.code(), AI.ASTBuildArgs, "input.cc", "clang-dataflow-test",238      std::make_shared<PCHContainerOperations>(),239      tooling::getClangStripDependencyFileAdjuster(),240      AI.ASTBuildVirtualMappedFiles);241  auto &Context = Unit->getASTContext();242 243  if (Context.getDiagnostics().getClient()->getNumErrors() != 0) {244    return llvm::make_error<llvm::StringError>(245        llvm::errc::invalid_argument, "Source file has syntax or type errors, "246                                      "they were printed to the test log");247  }248 249  CFGEltCallbacksTypeErased PostAnalysisCallbacks;250  if (AI.Callbacks.Before) {251    PostAnalysisCallbacks.Before =252        [&AI, &Context](const CFGElement &Element,253                        const TypeErasedDataflowAnalysisState &State) {254          AI.Callbacks.Before(255              Context, Element,256              TransferStateForDiagnostics<typename AnalysisT::Lattice>(257                  llvm::any_cast<const typename AnalysisT::Lattice &>(258                      State.Lattice.Value),259                  State.Env));260        };261  }262  if (AI.Callbacks.After) {263    PostAnalysisCallbacks.After =264        [&AI, &Context](const CFGElement &Element,265                        const TypeErasedDataflowAnalysisState &State) {266          AI.Callbacks.After(267              Context, Element,268              TransferStateForDiagnostics<typename AnalysisT::Lattice>(269                  llvm::any_cast<const typename AnalysisT::Lattice &>(270                      State.Lattice.Value),271                  State.Env));272        };273  }274 275  SmallVector<ast_matchers::BoundNodes, 1> MatchResult = ast_matchers::match(276      ast_matchers::functionDecl(ast_matchers::hasBody(ast_matchers::stmt()),277                                 AI.TargetFuncMatcher)278          .bind("target"),279      Context);280  if (MatchResult.empty())281    return llvm::createStringError(llvm::inconvertibleErrorCode(),282                                   "didn't find any matching target functions");283  for (const ast_matchers::BoundNodes &BN : MatchResult) {284    // Get the AST node of the target function.285    const FunctionDecl *Target = BN.getNodeAs<FunctionDecl>("target");286    if (Target == nullptr)287      return llvm::make_error<llvm::StringError>(288          llvm::errc::invalid_argument, "Could not find the target function.");289 290    // Build the control flow graph for the target function.291    auto MaybeACFG = AdornedCFG::build(*Target);292    if (!MaybeACFG)293      return MaybeACFG.takeError();294    auto &ACFG = *MaybeACFG;295 296    // Initialize states for running dataflow analysis.297    DataflowAnalysisContext DACtx(AI.SolverFactory(),298                                  {/*Opts=*/AI.BuiltinOptions});299    Environment InitEnv(DACtx, *Target);300    auto Analysis = AI.MakeAnalysis(Context, InitEnv);301 302    AnalysisOutputs AO{AnnotatedCode, Context, Target, ACFG,303                       Analysis,      InitEnv, {}};304 305    // Additional test setup.306    if (AI.SetupTest) {307      if (auto Error = AI.SetupTest(AO)) return Error;308    }309 310    // If successful, the dataflow analysis returns a mapping from block IDs to311    // the post-analysis states for the CFG blocks that have been evaluated.312    llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>313        MaybeBlockStates =314            runTypeErasedDataflowAnalysis(ACFG, Analysis, InitEnv,315                                          PostAnalysisCallbacks,316                                          MaxBlockVisitsInAnalysis);317    if (!MaybeBlockStates) return MaybeBlockStates.takeError();318    AO.BlockStates = *MaybeBlockStates;319 320    // Verify dataflow analysis outputs.321    VerifyResults(AO);322  }323 324  return llvm::Error::success();325}326 327/// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all328/// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the329/// annotation line numbers and analysis outputs, `VerifyResults` checks that330/// the results from the analysis are correct.331///332/// Requirements:333///334///   `AnalysisT` contains a type `Lattice`.335///336///   `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`.337///338///   `VerifyResults` must be provided.339template <typename AnalysisT>340llvm::Error341checkDataflow(AnalysisInputs<AnalysisT> AI,342              std::function<void(const llvm::DenseMap<unsigned, std::string> &,343                                 const AnalysisOutputs &)>344                  VerifyResults) {345  return checkDataflow<AnalysisT>(346      std::move(AI), [&VerifyResults](const AnalysisOutputs &AO) {347        auto AnnotationLinesAndContent = buildLineToAnnotationMapping(348            AO.ASTCtx.getSourceManager(), AO.ASTCtx.getLangOpts(),349            AO.Target->getSourceRange(), AO.Code);350        VerifyResults(AnnotationLinesAndContent, AO);351      });352}353 354/// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all355/// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the state356/// computed at each annotated statement and analysis outputs, `VerifyResults`357/// checks that the results from the analysis are correct.358///359/// Requirements:360///361///   `AnalysisT` contains a type `Lattice`.362///363///   `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`.364///365///   `VerifyResults` must be provided.366///367///   Any annotations appearing in `Code` must come after a statement.368///369///   There can be at most one annotation attached per statement.370///371///   Annotations must not be repeated.372template <typename AnalysisT>373llvm::Error374checkDataflow(AnalysisInputs<AnalysisT> AI,375              std::function<void(const llvm::StringMap<DataflowAnalysisState<376                                     typename AnalysisT::Lattice>> &,377                                 const AnalysisOutputs &)>378                  VerifyResults) {379  // Compute mapping from nodes of annotated statements to the content in the380  // annotation.381  llvm::DenseMap<const Stmt *, std::string> StmtToAnnotations;382  auto SetupTest = [&StmtToAnnotations,383                    PrevSetupTest = std::move(AI.SetupTest)](384                       AnalysisOutputs &AO) -> llvm::Error {385    auto MaybeStmtToAnnotations =386        buildStatementToAnnotationMapping(AO.InitEnv.getCurrentFunc(), AO.Code);387    if (!MaybeStmtToAnnotations) {388      return MaybeStmtToAnnotations.takeError();389    }390    StmtToAnnotations = std::move(*MaybeStmtToAnnotations);391    return PrevSetupTest ? PrevSetupTest(AO) : llvm::Error::success();392  };393 394  using StateT = DataflowAnalysisState<typename AnalysisT::Lattice>;395 396  // Save the states computed for program points immediately following annotated397  // statements. The saved states are keyed by the content of the annotation.398  llvm::StringMap<StateT> AnnotationStates;399  DiagnosisCallbacksForTesting<AnalysisT> Callbacks;400  Callbacks.Before = std::move(AI.Callbacks.Before);401  Callbacks.After =402      [&StmtToAnnotations, &AnnotationStates,403       PrevCallbackAfter = std::move(AI.Callbacks.After)](404          ASTContext &Ctx, const CFGElement &Elt,405          const TransferStateForDiagnostics<typename AnalysisT::Lattice>406              &State) {407        if (PrevCallbackAfter) {408          PrevCallbackAfter(Ctx, Elt, State);409        }410        // FIXME: Extend retrieval of state for non statement constructs.411        auto Stmt = Elt.getAs<CFGStmt>();412        if (!Stmt)413          return;414        auto It = StmtToAnnotations.find(Stmt->getStmt());415        if (It == StmtToAnnotations.end())416          return;417        auto [_, InsertSuccess] = AnnotationStates.insert(418            {It->second, StateT{State.Lattice, State.Env.fork()}});419        (void)_;420        (void)InsertSuccess;421        assert(InsertSuccess);422      };423  return checkDataflow<AnalysisT>(424      std::move(AI)425          .withSetupTest(std::move(SetupTest))426          .withDiagnosisCallbacks(std::move(Callbacks)),427      [&VerifyResults, &AnnotationStates](const AnalysisOutputs &AO) {428        VerifyResults(AnnotationStates, AO);429 430        // `checkDataflow()` can analyze more than one function.  Reset the431        // variables to prepare for analyzing the next function.432        AnnotationStates.clear();433      });434}435 436using BuiltinOptions = DataflowAnalysisContext::Options;437 438/// Runs dataflow on function named `TargetFun` in `Code` with a `NoopAnalysis`439/// and calls `VerifyResults` to verify the results.440llvm::Error checkDataflowWithNoopAnalysis(441    llvm::StringRef Code,442    std::function<443        void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,444             ASTContext &)>445        VerifyResults = [](const auto &, auto &) {},446    DataflowAnalysisOptions Options = {BuiltinOptions()},447    LangStandard::Kind Std = LangStandard::lang_cxx17,448    llvm::StringRef TargetFun = "target");449 450/// Runs dataflow on function matched by `TargetFuncMatcher` in `Code` with a451/// `NoopAnalysis` and calls `VerifyResults` to verify the results.452llvm::Error checkDataflowWithNoopAnalysis(453    llvm::StringRef Code,454    ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,455    std::function<456        void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,457             ASTContext &)>458        VerifyResults = [](const auto &, auto &) {},459    DataflowAnalysisOptions Options = {BuiltinOptions()},460    LangStandard::Kind Std = LangStandard::lang_cxx17,461    std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback =462        {});463 464/// Returns the `ValueDecl` for the given identifier.465/// The returned pointer is guaranteed to be non-null; the function asserts if466/// no `ValueDecl` with the given name is found.467///468/// Requirements:469///470///   `Name` must be unique in `ASTCtx`.471const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);472 473/// Returns the `IndirectFieldDecl` for the given identifier.474///475/// Requirements:476///477///   `Name` must be unique in `ASTCtx`.478const IndirectFieldDecl *findIndirectFieldDecl(ASTContext &ASTCtx,479                                               llvm::StringRef Name);480 481/// Returns the storage location (of type `LocT`) for the given identifier.482/// `LocT` must be a subclass of `StorageLocation` and must be of the483/// appropriate type.484///485/// Requirements:486///487///   `Name` must be unique in `ASTCtx`.488template <class LocT = StorageLocation>489LocT &getLocForDecl(ASTContext &ASTCtx, const Environment &Env,490                    llvm::StringRef Name) {491  const ValueDecl *VD = findValueDecl(ASTCtx, Name);492  assert(VD != nullptr);493  return *cast<LocT>(Env.getStorageLocation(*VD));494}495 496/// Returns the value (of type `ValueT`) for the given identifier.497/// `ValueT` must be a subclass of `Value` and must be of the appropriate type.498///499/// Requirements:500///501///   `Name` must be unique in `ASTCtx`.502template <class ValueT = Value>503ValueT &getValueForDecl(ASTContext &ASTCtx, const Environment &Env,504                        llvm::StringRef Name) {505  const ValueDecl *VD = findValueDecl(ASTCtx, Name);506  assert(VD != nullptr);507  return *cast<ValueT>(Env.getValue(*VD));508}509 510/// Returns the storage location for the field called `Name` of `Loc`.511/// Optionally casts the field storage location to `T`.512template <typename T = StorageLocation>513std::enable_if_t<std::is_base_of_v<StorageLocation, T>, T &>514getFieldLoc(const RecordStorageLocation &Loc, llvm::StringRef Name,515            ASTContext &ASTCtx) {516  return *cast<T>(Loc.getChild(*findValueDecl(ASTCtx, Name)));517}518 519/// Returns the value of a `Field` on the record referenced by `Loc.`520/// Returns null if `Loc` is null.521inline Value *getFieldValue(const RecordStorageLocation *Loc,522                            const ValueDecl &Field, const Environment &Env) {523  if (Loc == nullptr)524    return nullptr;525  StorageLocation *FieldLoc = Loc->getChild(Field);526  if (FieldLoc == nullptr)527    return nullptr;528  return Env.getValue(*FieldLoc);529}530 531/// Returns the value of a `Field` on the record referenced by `Loc.`532/// Returns null if `Loc` is null.533inline Value *getFieldValue(const RecordStorageLocation *Loc,534                            llvm::StringRef Name, ASTContext &ASTCtx,535                            const Environment &Env) {536  return getFieldValue(Loc, *findValueDecl(ASTCtx, Name), Env);537}538 539/// Creates and owns constraints which are boolean values.540class ConstraintContext {541  unsigned NextAtom = 0;542  llvm::BumpPtrAllocator A;543 544  const Formula *make(Formula::Kind K,545                      llvm::ArrayRef<const Formula *> Operands) {546    return &Formula::create(A, K, Operands);547  }548 549public:550  // Returns a reference to a fresh atomic variable.551  const Formula *atom() {552    return &Formula::create(A, Formula::AtomRef, {}, NextAtom++);553  }554 555  // Returns a reference to a literal boolean value.556  const Formula *literal(bool B) {557    return &Formula::create(A, Formula::Literal, {}, B);558  }559 560  // Creates a boolean conjunction.561  const Formula *conj(const Formula *LHS, const Formula *RHS) {562    return make(Formula::And, {LHS, RHS});563  }564 565  // Creates a boolean disjunction.566  const Formula *disj(const Formula *LHS, const Formula *RHS) {567    return make(Formula::Or, {LHS, RHS});568  }569 570  // Creates a boolean negation.571  const Formula *neg(const Formula *Operand) {572    return make(Formula::Not, {Operand});573  }574 575  // Creates a boolean implication.576  const Formula *impl(const Formula *LHS, const Formula *RHS) {577    return make(Formula::Implies, {LHS, RHS});578  }579 580  // Creates a boolean biconditional.581  const Formula *iff(const Formula *LHS, const Formula *RHS) {582    return make(Formula::Equal, {LHS, RHS});583  }584};585 586/// Parses a list of formulas, separated by newlines, and returns them.587/// On parse errors, calls `ADD_FAILURE()` to fail the current test.588std::vector<const Formula *> parseFormulas(Arena &A, StringRef Lines);589 590} // namespace test591} // namespace dataflow592} // namespace clang593 594#endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_595