1 //===- unittests/Analysis/FlowSensitive/DeterminismTest.cpp ---------------===//
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 #include "TestingSupport.h"
10 #include "clang/AST/Decl.h"
11 #include "clang/Analysis/FlowSensitive/AdornedCFG.h"
12 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
13 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
14 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
15 #include "clang/Analysis/FlowSensitive/Formula.h"
16 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
17 #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
18 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
19 #include "clang/Basic/LLVM.h"
20 #include "clang/Testing/TestAST.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include "gtest/gtest.h"
27 namespace clang::dataflow
{
29 // Run a no-op analysis, and return a textual representation of the
30 // flow-condition at function exit.
31 std::string
analyzeAndPrintExitCondition(llvm::StringRef Code
) {
32 DataflowAnalysisContext
DACtx(std::make_unique
<WatchedLiteralsSolver
>());
33 TestInputs
Inputs(Code
);
34 Inputs
.Language
= TestLanguage::Lang_CXX17
;
35 clang::TestAST
AST(Inputs
);
37 cast
<FunctionDecl
>(test::findValueDecl(AST
.context(), "target"));
38 Environment
InitEnv(DACtx
, *Target
);
39 auto ACFG
= cantFail(AdornedCFG::build(*Target
));
41 NoopAnalysis
Analysis(AST
.context(), DataflowAnalysisOptions
{});
43 auto Result
= runDataflowAnalysis(ACFG
, Analysis
, InitEnv
);
44 EXPECT_FALSE(!Result
) << Result
.takeError();
46 Atom FinalFC
= (*Result
)[ACFG
.getCFG().getExit().getBlockID()]
47 ->Env
.getFlowConditionToken();
49 llvm::raw_string_ostream
OS(Textual
);
50 DACtx
.dumpFlowCondition(FinalFC
, OS
);
54 TEST(DeterminismTest
, NestedSwitch
) {
55 // Example extracted from real-world code that had wildly nondeterministic
57 // Its flow condition depends on the order we join predecessor blocks.
58 const char *Code
= R
"cpp(
73 RetVal make(int size);
74 inline RetVal target(int size, Tree& self) {
76 const int height = self.height();
81 tree = tree->edge(0)->tree();
82 if (maybe()) return {};
85 tree = tree->edge(0)->tree();
87 if (maybe()) return {};
89 tree = tree->edge(0)->tree();
90 if (maybe()) return {};
92 Rep* edge = tree->edge(0);
93 if (maybe()) return {};
95 if (avail == 0) return {};
98 edge->length += delta;
116 std::string Cond
= analyzeAndPrintExitCondition(Code
);
117 for (unsigned I
= 0; I
< 10; ++I
)
118 EXPECT_EQ(Cond
, analyzeAndPrintExitCondition(Code
));
121 TEST(DeterminismTest
, ValueMergeOrder
) {
122 // Artificial example whose final flow condition variable numbering depends
123 // on the order in which we merge a, b, and c.
124 const char *Code
= R
"cpp(
125 bool target(bool a, bool b, bool c) {
134 std::string Cond
= analyzeAndPrintExitCondition(Code
);
135 for (unsigned I
= 0; I
< 10; ++I
)
136 EXPECT_EQ(Cond
, analyzeAndPrintExitCondition(Code
));
139 } // namespace clang::dataflow