1 //===--- TestingSupport.h - Testing utils for dataflow analyses -*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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_
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"
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=...}";
65 /// Returns the environment at the program point marked with `Annotation` from
66 /// the mapping of annotated program points to analysis state.
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.
86 /// void target(int *x) {
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`.
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.
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(
126 ASTContext
&, const CFGElement
&,
127 const TransferStateForDiagnostics
<typename
AnalysisT::Lattice
> &)>
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
) && {
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.
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
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
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
>
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
),
243 SmallVector
<ast_matchers::BoundNodes
, 1> MatchResult
= ast_matchers::match(
244 ast_matchers::functionDecl(ast_matchers::hasBody(ast_matchers::stmt()),
245 AI
.TargetFuncMatcher
)
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.
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.
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.
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
>
306 checkDataflow(AnalysisInputs
<AnalysisT
> AI
,
307 std::function
<void(const llvm::DenseMap
<unsigned, std::string
> &,
308 const AnalysisOutputs
&)>
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.
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
>
339 checkDataflow(AnalysisInputs
<AnalysisT
> AI
,
340 std::function
<void(const llvm::StringMap
<DataflowAnalysisState
<
341 typename
AnalysisT::Lattice
>> &,
342 const AnalysisOutputs
&)>
344 // Compute mapping from nodes of annotated statements to the content in the
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
;
365 [&StmtToAnnotations
, &AnnotationStates
,
366 PrevPostVisitCFG
= std::move(AI
.PostVisitCFG
)](
367 ASTContext
&Ctx
, const CFGElement
&Elt
,
368 const TransferStateForDiagnostics
<typename
AnalysisT::Lattice
>
370 if (PrevPostVisitCFG
) {
371 PrevPostVisitCFG(Ctx
, Elt
, State
);
373 // FIXME: Extend retrieval of state for non statement constructs.
374 auto Stmt
= Elt
.getAs
<CFGStmt
>();
377 auto It
= StmtToAnnotations
.find(Stmt
->getStmt());
378 if (It
== StmtToAnnotations
.end())
380 auto [_
, InsertSuccess
] = AnnotationStates
.insert(
381 {It
->second
, StateT
{State
.Lattice
, State
.Env
.fork()}});
384 assert(InsertSuccess
);
386 return checkDataflow
<AnalysisT
>(
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
,
406 void(const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
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
,
419 void(const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
421 VerifyResults
= [](const auto &, auto &) {},
422 DataflowAnalysisOptions Options
= {BuiltinOptions()},
423 LangStandard::Kind Std
= LangStandard::lang_cxx17
);
425 /// Returns the `ValueDecl` for the given identifier.
429 /// `Name` must be unique in `ASTCtx`.
430 const ValueDecl
*findValueDecl(ASTContext
&ASTCtx
, llvm::StringRef Name
);
432 /// Returns the `IndirectFieldDecl` for the given identifier.
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.
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.
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
) {
475 StorageLocation
*FieldLoc
= Loc
->getChild(Field
);
476 if (FieldLoc
== 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
);
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
);
533 } // namespace dataflow
536 #endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_