Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / TestingSupport.h
blob100d78378695d3c86c83c5c938ffe202cc4e0da7
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines utilities to simplify testing of dataflow analyses.
11 //===----------------------------------------------------------------------===//
13 #ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
14 #define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
16 #include <functional>
17 #include <memory>
18 #include <optional>
19 #include <ostream>
20 #include <string>
21 #include <utility>
22 #include <vector>
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/ControlFlowContext.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/NoopAnalysis.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"
51 namespace clang {
52 namespace dataflow {
54 // Requires a `<<` operator for the `Lattice` type.
55 // FIXME: move to a non-test utility library.
56 template <typename Lattice>
57 std::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=...}";
63 namespace test {
65 /// Returns the environment at the program point marked with `Annotation` from
66 /// the mapping of annotated program points to analysis state.
67 ///
68 /// Requirements:
69 ///
70 /// `Annotation` must be present as a key in `AnnotationStates`.
71 template <typename LatticeT>
72 const Environment &getEnvironmentAtAnnotation(
73 const llvm::StringMap<DataflowAnalysisState<LatticeT>> &AnnotationStates,
74 llvm::StringRef Annotation) {
75 auto It = AnnotationStates.find(Annotation);
76 assert(It != AnnotationStates.end());
77 return It->getValue().Env;
80 /// Contains data structures required and produced by a dataflow analysis run.
81 struct AnalysisOutputs {
82 /// Input code that is analyzed. Points within the code may be marked with
83 /// annotations to facilitate testing.
84 ///
85 /// Example:
86 /// void target(int *x) {
87 /// *x; // [[p]]
88 /// }
89 /// From the annotation `p`, the line number and analysis state immediately
90 /// after the statement `*x` can be retrieved and verified.
91 llvm::Annotations Code;
92 /// AST context generated from `Code`.
93 ASTContext &ASTCtx;
94 /// The function whose body is analyzed.
95 const FunctionDecl *Target;
96 /// Contains the control flow graph built from the body of the `Target`
97 /// function and is analyzed.
98 const ControlFlowContext &CFCtx;
99 /// The analysis to be run.
100 TypeErasedDataflowAnalysis &Analysis;
101 /// Initial state to start the analysis.
102 const Environment &InitEnv;
103 // Stores the state of a CFG block if it has been evaluated by the analysis.
104 // The indices correspond to the block IDs.
105 llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>> BlockStates;
108 /// Arguments for building the dataflow analysis.
109 template <typename AnalysisT> struct AnalysisInputs {
110 /// Required fields are set in constructor.
111 AnalysisInputs(
112 llvm::StringRef CodeArg,
113 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcherArg,
114 std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysisArg)
115 : Code(CodeArg), TargetFuncMatcher(std::move(TargetFuncMatcherArg)),
116 MakeAnalysis(std::move(MakeAnalysisArg)) {}
118 /// Optional fields can be set with methods of the form `withFieldName(...)`.
119 AnalysisInputs<AnalysisT> &&
120 withSetupTest(std::function<llvm::Error(AnalysisOutputs &)> Arg) && {
121 SetupTest = std::move(Arg);
122 return std::move(*this);
124 AnalysisInputs<AnalysisT> &&withPostVisitCFG(
125 std::function<void(
126 ASTContext &, const CFGElement &,
127 const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>
128 Arg) && {
129 PostVisitCFG = std::move(Arg);
130 return std::move(*this);
132 AnalysisInputs<AnalysisT> &&withASTBuildArgs(ArrayRef<std::string> Arg) && {
133 ASTBuildArgs = std::move(Arg);
134 return std::move(*this);
136 AnalysisInputs<AnalysisT> &&
137 withASTBuildVirtualMappedFiles(tooling::FileContentMappings Arg) && {
138 ASTBuildVirtualMappedFiles = std::move(Arg);
139 return std::move(*this);
141 AnalysisInputs<AnalysisT> &&
142 withBuiltinOptions(DataflowAnalysisContext::Options Options) && {
143 BuiltinOptions = std::move(Options);
144 return std::move(*this);
146 AnalysisInputs<AnalysisT> &&
147 withSolverFactory(std::function<std::unique_ptr<Solver>()> Factory) && {
148 assert(Factory);
149 SolverFactory = std::move(Factory);
150 return std::move(*this);
153 /// Required. Input code that is analyzed.
154 llvm::StringRef Code;
155 /// Required. All functions that match this matcher are analyzed.
156 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher;
157 /// Required. The analysis to be run is constructed with this function that
158 /// takes as argument the AST generated from the code being analyzed and the
159 /// initial state from which the analysis starts with.
160 std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis;
161 /// Optional. If provided, this function is executed immediately before
162 /// running the dataflow analysis to allow for additional setup. All fields in
163 /// the `AnalysisOutputs` argument will be initialized except for the
164 /// `BlockStates` field which is only computed later during the analysis.
165 std::function<llvm::Error(AnalysisOutputs &)> SetupTest = nullptr;
166 /// Optional. If provided, this function is applied on each CFG element after
167 /// the analysis has been run.
168 std::function<void(
169 ASTContext &, const CFGElement &,
170 const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>
171 PostVisitCFG = nullptr;
173 /// Optional. Options for building the AST context.
174 ArrayRef<std::string> ASTBuildArgs = {};
175 /// Optional. Options for building the AST context.
176 tooling::FileContentMappings ASTBuildVirtualMappedFiles = {};
177 /// Configuration options for the built-in model.
178 DataflowAnalysisContext::Options BuiltinOptions;
179 /// SAT solver factory.
180 std::function<std::unique_ptr<Solver>()> SolverFactory = [] {
181 return std::make_unique<WatchedLiteralsSolver>();
185 /// Returns assertions based on annotations that are present after statements in
186 /// `AnnotatedCode`.
187 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
188 buildStatementToAnnotationMapping(const FunctionDecl *Func,
189 llvm::Annotations AnnotatedCode);
191 /// Returns line numbers and content of the annotations in `AnnotatedCode`
192 /// within the token range `BoundingRange`.
193 llvm::DenseMap<unsigned, std::string> buildLineToAnnotationMapping(
194 const SourceManager &SM, const LangOptions &LangOpts,
195 SourceRange BoundingRange, llvm::Annotations AnnotatedCode);
197 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all
198 /// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the
199 /// analysis outputs, `VerifyResults` checks that the results from the analysis
200 /// are correct.
202 /// Requirements:
204 /// `AnalysisT` contains a type `Lattice`.
206 /// `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`.
208 /// `VerifyResults` must be provided.
209 template <typename AnalysisT>
210 llvm::Error
211 checkDataflow(AnalysisInputs<AnalysisT> AI,
212 std::function<void(const AnalysisOutputs &)> VerifyResults) {
213 // Build AST context from code.
214 llvm::Annotations AnnotatedCode(AI.Code);
215 auto Unit = tooling::buildASTFromCodeWithArgs(
216 AnnotatedCode.code(), AI.ASTBuildArgs, "input.cc", "clang-dataflow-test",
217 std::make_shared<PCHContainerOperations>(),
218 tooling::getClangStripDependencyFileAdjuster(),
219 AI.ASTBuildVirtualMappedFiles);
220 auto &Context = Unit->getASTContext();
222 if (Context.getDiagnostics().getClient()->getNumErrors() != 0) {
223 return llvm::make_error<llvm::StringError>(
224 llvm::errc::invalid_argument, "Source file has syntax or type errors, "
225 "they were printed to the test log");
228 std::function<void(const CFGElement &,
229 const TypeErasedDataflowAnalysisState &)>
230 TypeErasedPostVisitCFG = nullptr;
231 if (AI.PostVisitCFG) {
232 TypeErasedPostVisitCFG = [&AI, &Context](
233 const CFGElement &Element,
234 const TypeErasedDataflowAnalysisState &State) {
235 AI.PostVisitCFG(Context, Element,
236 TransferStateForDiagnostics<typename AnalysisT::Lattice>(
237 llvm::any_cast<const typename AnalysisT::Lattice &>(
238 State.Lattice.Value),
239 State.Env));
243 SmallVector<ast_matchers::BoundNodes, 1> MatchResult = ast_matchers::match(
244 ast_matchers::functionDecl(ast_matchers::hasBody(ast_matchers::stmt()),
245 AI.TargetFuncMatcher)
246 .bind("target"),
247 Context);
248 if (MatchResult.empty())
249 return llvm::createStringError(llvm::inconvertibleErrorCode(),
250 "didn't find any matching target functions");
251 for (const ast_matchers::BoundNodes &BN : MatchResult) {
252 // Get the AST node of the target function.
253 const FunctionDecl *Target = BN.getNodeAs<FunctionDecl>("target");
254 if (Target == nullptr)
255 return llvm::make_error<llvm::StringError>(
256 llvm::errc::invalid_argument, "Could not find the target function.");
258 // Build the control flow graph for the target function.
259 auto MaybeCFCtx = ControlFlowContext::build(*Target);
260 if (!MaybeCFCtx) return MaybeCFCtx.takeError();
261 auto &CFCtx = *MaybeCFCtx;
263 // Initialize states for running dataflow analysis.
264 DataflowAnalysisContext DACtx(AI.SolverFactory(),
265 {/*Opts=*/AI.BuiltinOptions});
266 Environment InitEnv(DACtx, *Target);
267 auto Analysis = AI.MakeAnalysis(Context, InitEnv);
269 AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
270 Analysis, InitEnv, {}};
272 // Additional test setup.
273 if (AI.SetupTest) {
274 if (auto Error = AI.SetupTest(AO)) return Error;
277 // If successful, the dataflow analysis returns a mapping from block IDs to
278 // the post-analysis states for the CFG blocks that have been evaluated.
279 llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
280 MaybeBlockStates = runTypeErasedDataflowAnalysis(
281 CFCtx, Analysis, InitEnv, TypeErasedPostVisitCFG);
282 if (!MaybeBlockStates) return MaybeBlockStates.takeError();
283 AO.BlockStates = *MaybeBlockStates;
285 // Verify dataflow analysis outputs.
286 VerifyResults(AO);
289 return llvm::Error::success();
292 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all
293 /// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the
294 /// annotation line numbers and analysis outputs, `VerifyResults` checks that
295 /// the results from the analysis are correct.
297 /// Requirements:
299 /// `AnalysisT` contains a type `Lattice`.
301 /// `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`.
303 /// `VerifyResults` must be provided.
304 template <typename AnalysisT>
305 llvm::Error
306 checkDataflow(AnalysisInputs<AnalysisT> AI,
307 std::function<void(const llvm::DenseMap<unsigned, std::string> &,
308 const AnalysisOutputs &)>
309 VerifyResults) {
310 return checkDataflow<AnalysisT>(
311 std::move(AI), [&VerifyResults](const AnalysisOutputs &AO) {
312 auto AnnotationLinesAndContent = buildLineToAnnotationMapping(
313 AO.ASTCtx.getSourceManager(), AO.ASTCtx.getLangOpts(),
314 AO.Target->getSourceRange(), AO.Code);
315 VerifyResults(AnnotationLinesAndContent, AO);
319 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all
320 /// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the state
321 /// computed at each annotated statement and analysis outputs, `VerifyResults`
322 /// checks that the results from the analysis are correct.
324 /// Requirements:
326 /// `AnalysisT` contains a type `Lattice`.
328 /// `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`.
330 /// `VerifyResults` must be provided.
332 /// Any annotations appearing in `Code` must come after a statement.
334 /// There can be at most one annotation attached per statement.
336 /// Annotations must not be repeated.
337 template <typename AnalysisT>
338 llvm::Error
339 checkDataflow(AnalysisInputs<AnalysisT> AI,
340 std::function<void(const llvm::StringMap<DataflowAnalysisState<
341 typename AnalysisT::Lattice>> &,
342 const AnalysisOutputs &)>
343 VerifyResults) {
344 // Compute mapping from nodes of annotated statements to the content in the
345 // annotation.
346 llvm::DenseMap<const Stmt *, std::string> StmtToAnnotations;
347 auto SetupTest = [&StmtToAnnotations,
348 PrevSetupTest = std::move(AI.SetupTest)](
349 AnalysisOutputs &AO) -> llvm::Error {
350 auto MaybeStmtToAnnotations = buildStatementToAnnotationMapping(
351 cast<FunctionDecl>(AO.InitEnv.getDeclCtx()), AO.Code);
352 if (!MaybeStmtToAnnotations) {
353 return MaybeStmtToAnnotations.takeError();
355 StmtToAnnotations = std::move(*MaybeStmtToAnnotations);
356 return PrevSetupTest ? PrevSetupTest(AO) : llvm::Error::success();
359 using StateT = DataflowAnalysisState<typename AnalysisT::Lattice>;
361 // Save the states computed for program points immediately following annotated
362 // statements. The saved states are keyed by the content of the annotation.
363 llvm::StringMap<StateT> AnnotationStates;
364 auto PostVisitCFG =
365 [&StmtToAnnotations, &AnnotationStates,
366 PrevPostVisitCFG = std::move(AI.PostVisitCFG)](
367 ASTContext &Ctx, const CFGElement &Elt,
368 const TransferStateForDiagnostics<typename AnalysisT::Lattice>
369 &State) {
370 if (PrevPostVisitCFG) {
371 PrevPostVisitCFG(Ctx, Elt, State);
373 // FIXME: Extend retrieval of state for non statement constructs.
374 auto Stmt = Elt.getAs<CFGStmt>();
375 if (!Stmt)
376 return;
377 auto It = StmtToAnnotations.find(Stmt->getStmt());
378 if (It == StmtToAnnotations.end())
379 return;
380 auto [_, InsertSuccess] = AnnotationStates.insert(
381 {It->second, StateT{State.Lattice, State.Env.fork()}});
382 (void)_;
383 (void)InsertSuccess;
384 assert(InsertSuccess);
386 return checkDataflow<AnalysisT>(
387 std::move(AI)
388 .withSetupTest(std::move(SetupTest))
389 .withPostVisitCFG(std::move(PostVisitCFG)),
390 [&VerifyResults, &AnnotationStates](const AnalysisOutputs &AO) {
391 VerifyResults(AnnotationStates, AO);
393 // `checkDataflow()` can analyze more than one function. Reset the
394 // variables to prepare for analyzing the next function.
395 AnnotationStates.clear();
399 using BuiltinOptions = DataflowAnalysisContext::Options;
401 /// Runs dataflow on function named `TargetFun` in `Code` with a `NoopAnalysis`
402 /// and calls `VerifyResults` to verify the results.
403 llvm::Error checkDataflowWithNoopAnalysis(
404 llvm::StringRef Code,
405 std::function<
406 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
407 ASTContext &)>
408 VerifyResults = [](const auto &, auto &) {},
409 DataflowAnalysisOptions Options = {BuiltinOptions()},
410 LangStandard::Kind Std = LangStandard::lang_cxx17,
411 llvm::StringRef TargetFun = "target");
413 /// Runs dataflow on function matched by `TargetFuncMatcher` in `Code` with a
414 /// `NoopAnalysis` and calls `VerifyResults` to verify the results.
415 llvm::Error checkDataflowWithNoopAnalysis(
416 llvm::StringRef Code,
417 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
418 std::function<
419 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
420 ASTContext &)>
421 VerifyResults = [](const auto &, auto &) {},
422 DataflowAnalysisOptions Options = {BuiltinOptions()},
423 LangStandard::Kind Std = LangStandard::lang_cxx17);
425 /// Returns the `ValueDecl` for the given identifier.
427 /// Requirements:
429 /// `Name` must be unique in `ASTCtx`.
430 const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);
432 /// Returns the `IndirectFieldDecl` for the given identifier.
434 /// Requirements:
436 /// `Name` must be unique in `ASTCtx`.
437 const IndirectFieldDecl *findIndirectFieldDecl(ASTContext &ASTCtx,
438 llvm::StringRef Name);
440 /// Returns the storage location (of type `LocT`) for the given identifier.
441 /// `LocT` must be a subclass of `StorageLocation` and must be of the
442 /// appropriate type.
444 /// Requirements:
446 /// `Name` must be unique in `ASTCtx`.
447 template <class LocT>
448 LocT &getLocForDecl(ASTContext &ASTCtx, const Environment &Env,
449 llvm::StringRef Name) {
450 const ValueDecl *VD = findValueDecl(ASTCtx, Name);
451 assert(VD != nullptr);
452 return *cast<LocT>(Env.getStorageLocation(*VD));
455 /// Returns the value (of type `ValueT`) for the given identifier.
456 /// `ValueT` must be a subclass of `Value` and must be of the appropriate type.
458 /// Requirements:
460 /// `Name` must be unique in `ASTCtx`.
461 template <class ValueT>
462 ValueT &getValueForDecl(ASTContext &ASTCtx, const Environment &Env,
463 llvm::StringRef Name) {
464 const ValueDecl *VD = findValueDecl(ASTCtx, Name);
465 assert(VD != nullptr);
466 return *cast<ValueT>(Env.getValue(*VD));
469 /// Returns the value of a `Field` on the record referenced by `Loc.`
470 /// Returns null if `Loc` is null.
471 inline Value *getFieldValue(const RecordStorageLocation *Loc,
472 const ValueDecl &Field, const Environment &Env) {
473 if (Loc == nullptr)
474 return nullptr;
475 StorageLocation *FieldLoc = Loc->getChild(Field);
476 if (FieldLoc == nullptr)
477 return nullptr;
478 return Env.getValue(*FieldLoc);
481 /// Creates and owns constraints which are boolean values.
482 class ConstraintContext {
483 unsigned NextAtom = 0;
484 llvm::BumpPtrAllocator A;
486 const Formula *make(Formula::Kind K,
487 llvm::ArrayRef<const Formula *> Operands) {
488 return &Formula::create(A, K, Operands);
491 public:
492 // Returns a reference to a fresh atomic variable.
493 const Formula *atom() {
494 return &Formula::create(A, Formula::AtomRef, {}, NextAtom++);
497 // Returns a reference to a literal boolean value.
498 const Formula *literal(bool B) {
499 return &Formula::create(A, Formula::Literal, {}, B);
502 // Creates a boolean conjunction.
503 const Formula *conj(const Formula *LHS, const Formula *RHS) {
504 return make(Formula::And, {LHS, RHS});
507 // Creates a boolean disjunction.
508 const Formula *disj(const Formula *LHS, const Formula *RHS) {
509 return make(Formula::Or, {LHS, RHS});
512 // Creates a boolean negation.
513 const Formula *neg(const Formula *Operand) {
514 return make(Formula::Not, {Operand});
517 // Creates a boolean implication.
518 const Formula *impl(const Formula *LHS, const Formula *RHS) {
519 return make(Formula::Implies, {LHS, RHS});
522 // Creates a boolean biconditional.
523 const Formula *iff(const Formula *LHS, const Formula *RHS) {
524 return make(Formula::Equal, {LHS, RHS});
528 /// Parses a list of formulas, separated by newlines, and returns them.
529 /// On parse errors, calls `ADD_FAILURE()` to fail the current test.
530 std::vector<const Formula *> parseFormulas(Arena &A, StringRef Lines);
532 } // namespace test
533 } // namespace dataflow
534 } // namespace clang
536 #endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_