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. |
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 | |
51 | namespace clang { |
52 | namespace dataflow { |
53 | |
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=...}" ; |
61 | } |
62 | |
63 | namespace test { |
64 | |
65 | // Caps the number of block visits in any individual analysis. Given that test |
66 | // code is typically quite small, we set a low number to help catch any problems |
67 | // early. But, the choice is arbitrary. |
68 | constexpr std::int32_t MaxBlockVisitsInAnalysis = 2'000; |
69 | |
70 | /// Returns the environment at the program point marked with `Annotation` from |
71 | /// the mapping of annotated program points to analysis state. |
72 | /// |
73 | /// Requirements: |
74 | /// |
75 | /// `Annotation` must be present as a key in `AnnotationStates`. |
76 | template <typename LatticeT> |
77 | const 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. |
86 | struct AnalysisOutputs { |
87 | /// Input code that is analyzed. Points within the code may be marked with |
88 | /// 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 immediately |
95 | /// 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 | /// Arguments for building the dataflow analysis. |
114 | template <typename AnalysisT> struct AnalysisInputs { |
115 | /// Required fields are set in constructor. |
116 | AnalysisInputs( |
117 | llvm::StringRef CodeArg, |
118 | ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcherArg, |
119 | std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysisArg) |
120 | : Code(CodeArg), TargetFuncMatcher(std::move(TargetFuncMatcherArg)), |
121 | MakeAnalysis(std::move(MakeAnalysisArg)) {} |
122 | |
123 | /// Optional fields can be set with methods of the form `withFieldName(...)`. |
124 | AnalysisInputs<AnalysisT> && |
125 | withSetupTest(std::function<llvm::Error(AnalysisOutputs &)> Arg) && { |
126 | SetupTest = std::move(Arg); |
127 | return std::move(*this); |
128 | } |
129 | AnalysisInputs<AnalysisT> &&withPostVisitCFG( |
130 | std::function<void( |
131 | ASTContext &, const CFGElement &, |
132 | const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)> |
133 | Arg) && { |
134 | PostVisitCFG = std::move(Arg); |
135 | return std::move(*this); |
136 | } |
137 | AnalysisInputs<AnalysisT> &&withASTBuildArgs(ArrayRef<std::string> Arg) && { |
138 | ASTBuildArgs = std::move(Arg); |
139 | return std::move(*this); |
140 | } |
141 | AnalysisInputs<AnalysisT> && |
142 | withASTBuildVirtualMappedFiles(tooling::FileContentMappings Arg) && { |
143 | ASTBuildVirtualMappedFiles = std::move(Arg); |
144 | return std::move(*this); |
145 | } |
146 | AnalysisInputs<AnalysisT> && |
147 | withBuiltinOptions(DataflowAnalysisContext::Options Options) && { |
148 | BuiltinOptions = std::move(Options); |
149 | return std::move(*this); |
150 | } |
151 | AnalysisInputs<AnalysisT> && |
152 | withSolverFactory(std::function<std::unique_ptr<Solver>()> Factory) && { |
153 | assert(Factory); |
154 | SolverFactory = std::move(Factory); |
155 | return std::move(*this); |
156 | } |
157 | |
158 | /// Required. Input code that is analyzed. |
159 | llvm::StringRef Code; |
160 | /// Required. All functions that match this matcher are analyzed. |
161 | ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher; |
162 | /// Required. The analysis to be run is constructed with this function that |
163 | /// takes as argument the AST generated from the code being analyzed and the |
164 | /// initial state from which the analysis starts with. |
165 | std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis; |
166 | /// Optional. If provided, this function is executed immediately before |
167 | /// running the dataflow analysis to allow for additional setup. All fields in |
168 | /// the `AnalysisOutputs` argument will be initialized except for the |
169 | /// `BlockStates` field which is only computed later during the analysis. |
170 | std::function<llvm::Error(AnalysisOutputs &)> SetupTest = nullptr; |
171 | /// Optional. If provided, this function is applied on each CFG element after |
172 | /// the analysis has been run. |
173 | std::function<void( |
174 | ASTContext &, const CFGElement &, |
175 | const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)> |
176 | PostVisitCFG = nullptr; |
177 | |
178 | /// Optional. Options for building the AST context. |
179 | ArrayRef<std::string> ASTBuildArgs = {}; |
180 | /// Optional. Options for building the AST context. |
181 | tooling::FileContentMappings ASTBuildVirtualMappedFiles = {}; |
182 | /// Configuration options for the built-in model. |
183 | DataflowAnalysisContext::Options BuiltinOptions; |
184 | /// SAT solver factory. |
185 | std::function<std::unique_ptr<Solver>()> SolverFactory = [] { |
186 | return std::make_unique<WatchedLiteralsSolver>(); |
187 | }; |
188 | }; |
189 | |
190 | /// Returns assertions based on annotations that are present after statements in |
191 | /// `AnnotatedCode`. |
192 | llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> |
193 | buildStatementToAnnotationMapping(const FunctionDecl *Func, |
194 | llvm::Annotations AnnotatedCode); |
195 | |
196 | /// Returns line numbers and content of the annotations in `AnnotatedCode` |
197 | /// within the token range `BoundingRange`. |
198 | llvm::DenseMap<unsigned, std::string> buildLineToAnnotationMapping( |
199 | const SourceManager &SM, const LangOptions &LangOpts, |
200 | SourceRange BoundingRange, llvm::Annotations AnnotatedCode); |
201 | |
202 | /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all |
203 | /// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the |
204 | /// analysis outputs, `VerifyResults` checks that the results from the analysis |
205 | /// are correct. |
206 | /// |
207 | /// Requirements: |
208 | /// |
209 | /// `AnalysisT` contains a type `Lattice`. |
210 | /// |
211 | /// `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`. |
212 | /// |
213 | /// `VerifyResults` must be provided. |
214 | template <typename AnalysisT> |
215 | llvm::Error |
216 | checkDataflow(AnalysisInputs<AnalysisT> AI, |
217 | std::function<void(const AnalysisOutputs &)> VerifyResults) { |
218 | // Build AST context from code. |
219 | llvm::Annotations AnnotatedCode(AI.Code); |
220 | auto Unit = tooling::buildASTFromCodeWithArgs( |
221 | Code: AnnotatedCode.code(), Args: AI.ASTBuildArgs, FileName: "input.cc" , ToolName: "clang-dataflow-test" , |
222 | PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
223 | Adjuster: tooling::getClangStripDependencyFileAdjuster(), |
224 | VirtualMappedFiles: AI.ASTBuildVirtualMappedFiles); |
225 | auto &Context = Unit->getASTContext(); |
226 | |
227 | if (Context.getDiagnostics().getClient()->getNumErrors() != 0) { |
228 | return llvm::make_error<llvm::StringError>( |
229 | Args: llvm::errc::invalid_argument, Args: "Source file has syntax or type errors, " |
230 | "they were printed to the test log" ); |
231 | } |
232 | |
233 | std::function<void(const CFGElement &, |
234 | const TypeErasedDataflowAnalysisState &)> |
235 | TypeErasedPostVisitCFG = nullptr; |
236 | if (AI.PostVisitCFG) { |
237 | TypeErasedPostVisitCFG = [&AI, &Context]( |
238 | const CFGElement &Element, |
239 | const TypeErasedDataflowAnalysisState &State) { |
240 | AI.PostVisitCFG(Context, Element, |
241 | TransferStateForDiagnostics<typename AnalysisT::Lattice>( |
242 | llvm::any_cast<const typename AnalysisT::Lattice &>( |
243 | State.Lattice.Value), |
244 | State.Env)); |
245 | }; |
246 | } |
247 | |
248 | SmallVector<ast_matchers::BoundNodes, 1> MatchResult = ast_matchers::match( |
249 | ast_matchers::functionDecl(ast_matchers::hasBody(InnerMatcher: ast_matchers::stmt()), |
250 | AI.TargetFuncMatcher) |
251 | .bind("target" ), |
252 | Context); |
253 | if (MatchResult.empty()) |
254 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
255 | Msg: "didn't find any matching target functions" ); |
256 | for (const ast_matchers::BoundNodes &BN : MatchResult) { |
257 | // Get the AST node of the target function. |
258 | const FunctionDecl *Target = BN.getNodeAs<FunctionDecl>(ID: "target" ); |
259 | if (Target == nullptr) |
260 | return llvm::make_error<llvm::StringError>( |
261 | Args: llvm::errc::invalid_argument, Args: "Could not find the target function." ); |
262 | |
263 | // Build the control flow graph for the target function. |
264 | auto MaybeACFG = AdornedCFG::build(Func: *Target); |
265 | if (!MaybeACFG) |
266 | return MaybeACFG.takeError(); |
267 | auto &ACFG = *MaybeACFG; |
268 | |
269 | // Initialize states for running dataflow analysis. |
270 | DataflowAnalysisContext DACtx(AI.SolverFactory(), |
271 | {/*Opts=*/AI.BuiltinOptions}); |
272 | Environment InitEnv(DACtx, *Target); |
273 | auto Analysis = AI.MakeAnalysis(Context, InitEnv); |
274 | |
275 | AnalysisOutputs AO{AnnotatedCode, Context, Target, ACFG, |
276 | Analysis, InitEnv, {}}; |
277 | |
278 | // Additional test setup. |
279 | if (AI.SetupTest) { |
280 | if (auto Error = AI.SetupTest(AO)) return Error; |
281 | } |
282 | |
283 | // If successful, the dataflow analysis returns a mapping from block IDs to |
284 | // the post-analysis states for the CFG blocks that have been evaluated. |
285 | llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>> |
286 | MaybeBlockStates = |
287 | runTypeErasedDataflowAnalysis(ACFG, Analysis, InitEnv, |
288 | TypeErasedPostVisitCFG, |
289 | MaxBlockVisitsInAnalysis); |
290 | if (!MaybeBlockStates) return MaybeBlockStates.takeError(); |
291 | AO.BlockStates = *MaybeBlockStates; |
292 | |
293 | // Verify dataflow analysis outputs. |
294 | VerifyResults(AO); |
295 | } |
296 | |
297 | return llvm::Error::success(); |
298 | } |
299 | |
300 | /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all |
301 | /// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the |
302 | /// annotation line numbers and analysis outputs, `VerifyResults` checks that |
303 | /// the results from the analysis are correct. |
304 | /// |
305 | /// Requirements: |
306 | /// |
307 | /// `AnalysisT` contains a type `Lattice`. |
308 | /// |
309 | /// `Code`, `TargetFuncMatcher` and `MakeAnalysis` must be provided in `AI`. |
310 | /// |
311 | /// `VerifyResults` must be provided. |
312 | template <typename AnalysisT> |
313 | llvm::Error |
314 | checkDataflow(AnalysisInputs<AnalysisT> AI, |
315 | std::function<void(const llvm::DenseMap<unsigned, std::string> &, |
316 | const AnalysisOutputs &)> |
317 | VerifyResults) { |
318 | return checkDataflow<AnalysisT>( |
319 | std::move(AI), [&VerifyResults](const AnalysisOutputs &AO) { |
320 | auto AnnotationLinesAndContent = buildLineToAnnotationMapping( |
321 | SM: AO.ASTCtx.getSourceManager(), LangOpts: AO.ASTCtx.getLangOpts(), |
322 | BoundingRange: AO.Target->getSourceRange(), AnnotatedCode: AO.Code); |
323 | VerifyResults(AnnotationLinesAndContent, AO); |
324 | }); |
325 | } |
326 | |
327 | /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on all |
328 | /// functions that match `AI.TargetFuncMatcher` in `AI.Code`. Given the state |
329 | /// computed at each annotated statement and analysis outputs, `VerifyResults` |
330 | /// checks that 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. |
339 | /// |
340 | /// Any annotations appearing in `Code` must come after a statement. |
341 | /// |
342 | /// There can be at most one annotation attached per statement. |
343 | /// |
344 | /// Annotations must not be repeated. |
345 | template <typename AnalysisT> |
346 | llvm::Error |
347 | checkDataflow(AnalysisInputs<AnalysisT> AI, |
348 | std::function<void(const llvm::StringMap<DataflowAnalysisState< |
349 | typename AnalysisT::Lattice>> &, |
350 | const AnalysisOutputs &)> |
351 | VerifyResults) { |
352 | // Compute mapping from nodes of annotated statements to the content in the |
353 | // annotation. |
354 | llvm::DenseMap<const Stmt *, std::string> StmtToAnnotations; |
355 | auto SetupTest = [&StmtToAnnotations, |
356 | PrevSetupTest = std::move(AI.SetupTest)]( |
357 | AnalysisOutputs &AO) -> llvm::Error { |
358 | auto MaybeStmtToAnnotations = buildStatementToAnnotationMapping( |
359 | Func: cast<FunctionDecl>(Val: AO.InitEnv.getDeclCtx()), AnnotatedCode: AO.Code); |
360 | if (!MaybeStmtToAnnotations) { |
361 | return MaybeStmtToAnnotations.takeError(); |
362 | } |
363 | StmtToAnnotations = std::move(*MaybeStmtToAnnotations); |
364 | return PrevSetupTest ? PrevSetupTest(AO) : llvm::Error::success(); |
365 | }; |
366 | |
367 | using StateT = DataflowAnalysisState<typename AnalysisT::Lattice>; |
368 | |
369 | // Save the states computed for program points immediately following annotated |
370 | // statements. The saved states are keyed by the content of the annotation. |
371 | llvm::StringMap<StateT> AnnotationStates; |
372 | auto PostVisitCFG = |
373 | [&StmtToAnnotations, &AnnotationStates, |
374 | PrevPostVisitCFG = std::move(AI.PostVisitCFG)]( |
375 | ASTContext &Ctx, const CFGElement &Elt, |
376 | const TransferStateForDiagnostics<typename AnalysisT::Lattice> |
377 | &State) { |
378 | if (PrevPostVisitCFG) { |
379 | PrevPostVisitCFG(Ctx, Elt, State); |
380 | } |
381 | // FIXME: Extend retrieval of state for non statement constructs. |
382 | auto Stmt = Elt.getAs<CFGStmt>(); |
383 | if (!Stmt) |
384 | return; |
385 | auto It = StmtToAnnotations.find(Val: Stmt->getStmt()); |
386 | if (It == StmtToAnnotations.end()) |
387 | return; |
388 | auto [_, InsertSuccess] = AnnotationStates.insert( |
389 | {It->second, StateT{State.Lattice, State.Env.fork()}}); |
390 | (void)_; |
391 | (void)InsertSuccess; |
392 | assert(InsertSuccess); |
393 | }; |
394 | return checkDataflow<AnalysisT>( |
395 | std::move(AI) |
396 | .withSetupTest(std::move(SetupTest)) |
397 | .withPostVisitCFG(std::move(PostVisitCFG)), |
398 | [&VerifyResults, &AnnotationStates](const AnalysisOutputs &AO) { |
399 | VerifyResults(AnnotationStates, AO); |
400 | |
401 | // `checkDataflow()` can analyze more than one function. Reset the |
402 | // variables to prepare for analyzing the next function. |
403 | AnnotationStates.clear(); |
404 | }); |
405 | } |
406 | |
407 | using BuiltinOptions = DataflowAnalysisContext::Options; |
408 | |
409 | /// Runs dataflow on function named `TargetFun` in `Code` with a `NoopAnalysis` |
410 | /// and calls `VerifyResults` to verify the results. |
411 | llvm::Error checkDataflowWithNoopAnalysis( |
412 | llvm::StringRef Code, |
413 | std::function< |
414 | void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &, |
415 | ASTContext &)> |
416 | VerifyResults = [](const auto &, auto &) {}, |
417 | DataflowAnalysisOptions Options = {.BuiltinOpts: BuiltinOptions()}, |
418 | LangStandard::Kind Std = LangStandard::lang_cxx17, |
419 | llvm::StringRef TargetFun = "target" ); |
420 | |
421 | /// Runs dataflow on function matched by `TargetFuncMatcher` in `Code` with a |
422 | /// `NoopAnalysis` and calls `VerifyResults` to verify the results. |
423 | llvm::Error checkDataflowWithNoopAnalysis( |
424 | llvm::StringRef Code, |
425 | ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher, |
426 | std::function< |
427 | void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &, |
428 | ASTContext &)> |
429 | VerifyResults = [](const auto &, auto &) {}, |
430 | DataflowAnalysisOptions Options = {.BuiltinOpts: BuiltinOptions()}, |
431 | LangStandard::Kind Std = LangStandard::lang_cxx17, |
432 | std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback = |
433 | {}); |
434 | |
435 | /// Returns the `ValueDecl` for the given identifier. |
436 | /// The returned pointer is guaranteed to be non-null; the function asserts if |
437 | /// no `ValueDecl` with the given name is found. |
438 | /// |
439 | /// Requirements: |
440 | /// |
441 | /// `Name` must be unique in `ASTCtx`. |
442 | const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name); |
443 | |
444 | /// Returns the `IndirectFieldDecl` for the given identifier. |
445 | /// |
446 | /// Requirements: |
447 | /// |
448 | /// `Name` must be unique in `ASTCtx`. |
449 | const IndirectFieldDecl *findIndirectFieldDecl(ASTContext &ASTCtx, |
450 | llvm::StringRef Name); |
451 | |
452 | /// Returns the storage location (of type `LocT`) for the given identifier. |
453 | /// `LocT` must be a subclass of `StorageLocation` and must be of the |
454 | /// appropriate type. |
455 | /// |
456 | /// Requirements: |
457 | /// |
458 | /// `Name` must be unique in `ASTCtx`. |
459 | template <class LocT = StorageLocation> |
460 | LocT &getLocForDecl(ASTContext &ASTCtx, const Environment &Env, |
461 | llvm::StringRef Name) { |
462 | const ValueDecl *VD = findValueDecl(ASTCtx, Name); |
463 | assert(VD != nullptr); |
464 | return *cast<LocT>(Env.getStorageLocation(D: *VD)); |
465 | } |
466 | |
467 | /// Returns the value (of type `ValueT`) for the given identifier. |
468 | /// `ValueT` must be a subclass of `Value` and must be of the appropriate type. |
469 | /// |
470 | /// Requirements: |
471 | /// |
472 | /// `Name` must be unique in `ASTCtx`. |
473 | template <class ValueT = Value> |
474 | ValueT &getValueForDecl(ASTContext &ASTCtx, const Environment &Env, |
475 | llvm::StringRef Name) { |
476 | const ValueDecl *VD = findValueDecl(ASTCtx, Name); |
477 | assert(VD != nullptr); |
478 | return *cast<ValueT>(Env.getValue(D: *VD)); |
479 | } |
480 | |
481 | /// Returns the storage location for the field called `Name` of `Loc`. |
482 | /// Optionally casts the field storage location to `T`. |
483 | template <typename T = StorageLocation> |
484 | std::enable_if_t<std::is_base_of_v<StorageLocation, T>, T &> |
485 | getFieldLoc(const RecordStorageLocation &Loc, llvm::StringRef Name, |
486 | ASTContext &ASTCtx) { |
487 | return *cast<T>(Loc.getChild(D: *findValueDecl(ASTCtx, Name))); |
488 | } |
489 | |
490 | /// Returns the value of a `Field` on the record referenced by `Loc.` |
491 | /// Returns null if `Loc` is null. |
492 | inline Value *getFieldValue(const RecordStorageLocation *Loc, |
493 | const ValueDecl &Field, const Environment &Env) { |
494 | if (Loc == nullptr) |
495 | return nullptr; |
496 | StorageLocation *FieldLoc = Loc->getChild(D: Field); |
497 | if (FieldLoc == nullptr) |
498 | return nullptr; |
499 | return Env.getValue(Loc: *FieldLoc); |
500 | } |
501 | |
502 | /// Returns the value of a `Field` on the record referenced by `Loc.` |
503 | /// Returns null if `Loc` is null. |
504 | inline Value *getFieldValue(const RecordStorageLocation *Loc, |
505 | llvm::StringRef Name, ASTContext &ASTCtx, |
506 | const Environment &Env) { |
507 | return getFieldValue(Loc, Field: *findValueDecl(ASTCtx, Name), Env); |
508 | } |
509 | |
510 | /// Creates and owns constraints which are boolean values. |
511 | class ConstraintContext { |
512 | unsigned NextAtom = 0; |
513 | llvm::BumpPtrAllocator A; |
514 | |
515 | const Formula *make(Formula::Kind K, |
516 | llvm::ArrayRef<const Formula *> Operands) { |
517 | return &Formula::create(Alloc&: A, K, Operands); |
518 | } |
519 | |
520 | public: |
521 | // Returns a reference to a fresh atomic variable. |
522 | const Formula *atom() { |
523 | return &Formula::create(Alloc&: A, K: Formula::AtomRef, Operands: {}, Value: NextAtom++); |
524 | } |
525 | |
526 | // Returns a reference to a literal boolean value. |
527 | const Formula *literal(bool B) { |
528 | return &Formula::create(Alloc&: A, K: Formula::Literal, Operands: {}, Value: B); |
529 | } |
530 | |
531 | // Creates a boolean conjunction. |
532 | const Formula *conj(const Formula *LHS, const Formula *RHS) { |
533 | return make(K: Formula::And, Operands: {LHS, RHS}); |
534 | } |
535 | |
536 | // Creates a boolean disjunction. |
537 | const Formula *disj(const Formula *LHS, const Formula *RHS) { |
538 | return make(K: Formula::Or, Operands: {LHS, RHS}); |
539 | } |
540 | |
541 | // Creates a boolean negation. |
542 | const Formula *neg(const Formula *Operand) { |
543 | return make(K: Formula::Not, Operands: {Operand}); |
544 | } |
545 | |
546 | // Creates a boolean implication. |
547 | const Formula *impl(const Formula *LHS, const Formula *RHS) { |
548 | return make(K: Formula::Implies, Operands: {LHS, RHS}); |
549 | } |
550 | |
551 | // Creates a boolean biconditional. |
552 | const Formula *iff(const Formula *LHS, const Formula *RHS) { |
553 | return make(K: Formula::Equal, Operands: {LHS, RHS}); |
554 | } |
555 | }; |
556 | |
557 | /// Parses a list of formulas, separated by newlines, and returns them. |
558 | /// On parse errors, calls `ADD_FAILURE()` to fail the current test. |
559 | std::vector<const Formula *> parseFormulas(Arena &A, StringRef Lines); |
560 | |
561 | } // namespace test |
562 | } // namespace dataflow |
563 | } // namespace clang |
564 | |
565 | #endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_ |
566 | |