Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / ChromiumCheckModelTest.cpp
bloba2762046665a2ce3b5e02c51bc522e2058619f48
1 //===- ChromiumCheckModelTest.cpp -----------------------------------------===//
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 // FIXME: Move this to clang/unittests/Analysis/FlowSensitive/Models.
10 #include "clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h"
11 #include "TestingSupport.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Analysis/CFG.h"
15 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Testing/Support/Error.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 #include <string>
24 using namespace clang;
25 using namespace dataflow;
26 using namespace test;
28 namespace {
29 using ::testing::NotNull;
30 using ::testing::UnorderedElementsAre;
32 static constexpr char ChromiumCheckHeader[] = R"(
33 namespace std {
34 class ostream;
35 } // namespace std
37 namespace logging {
38 class VoidifyStream {
39 public:
40 VoidifyStream() = default;
41 void operator&(std::ostream&) {}
44 class CheckError {
45 public:
46 static CheckError Check(const char* file, int line, const char* condition);
47 static CheckError DCheck(const char* file, int line, const char* condition);
48 static CheckError PCheck(const char* file, int line, const char* condition);
49 static CheckError PCheck(const char* file, int line);
50 static CheckError DPCheck(const char* file, int line, const char* condition);
52 std::ostream& stream();
54 ~CheckError();
56 CheckError(const CheckError& other) = delete;
57 CheckError& operator=(const CheckError& other) = delete;
58 CheckError(CheckError&& other) = default;
59 CheckError& operator=(CheckError&& other) = default;
62 } // namespace logging
64 #define LAZY_CHECK_STREAM(stream, condition) \
65 !(condition) ? (void)0 : ::logging::VoidifyStream() & (stream)
67 #define CHECK(condition) \
68 LAZY_CHECK_STREAM( \
69 ::logging::CheckError::Check(__FILE__, __LINE__, #condition).stream(), \
70 !(condition))
72 #define PCHECK(condition) \
73 LAZY_CHECK_STREAM( \
74 ::logging::CheckError::PCheck(__FILE__, __LINE__, #condition).stream(), \
75 !(condition))
77 #define DCHECK(condition) \
78 LAZY_CHECK_STREAM( \
79 ::logging::CheckError::DCheck(__FILE__, __LINE__, #condition).stream(), \
80 !(condition))
82 #define DPCHECK(condition) \
83 LAZY_CHECK_STREAM( \
84 ::logging::CheckError::DPCheck(__FILE__, __LINE__, #condition).stream(), \
85 !(condition))
86 )";
88 // A definition of the `CheckError` class that looks like the Chromium one, but
89 // is actually something else.
90 static constexpr char OtherCheckHeader[] = R"(
91 namespace other {
92 namespace logging {
93 class CheckError {
94 public:
95 static CheckError Check(const char* file, int line, const char* condition);
97 } // namespace logging
98 } // namespace other
99 )";
101 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`.
102 std::string ReplacePattern(std::string S, const std::string &Pattern,
103 const std::string &Replacement) {
104 size_t Pos = 0;
105 Pos = S.find(Pattern, Pos);
106 if (Pos != std::string::npos)
107 S.replace(Pos, Pattern.size(), Replacement);
108 return S;
111 template <typename Model>
112 class ModelAdaptorAnalysis
113 : public DataflowAnalysis<ModelAdaptorAnalysis<Model>, NoopLattice> {
114 public:
115 explicit ModelAdaptorAnalysis(ASTContext &Context)
116 : DataflowAnalysis<ModelAdaptorAnalysis, NoopLattice>(Context) {}
118 static NoopLattice initialElement() { return NoopLattice(); }
120 void transfer(const CFGElement &E, NoopLattice &, Environment &Env) {
121 M.transfer(E, Env);
124 private:
125 Model M;
128 template <typename Matcher>
129 void runDataflow(llvm::StringRef Code, Matcher Match) {
130 const tooling::FileContentMappings FileContents = {
131 {"check.h", ChromiumCheckHeader}, {"othercheck.h", OtherCheckHeader}};
133 ASSERT_THAT_ERROR(
134 checkDataflow<ModelAdaptorAnalysis<ChromiumCheckModel>>(
135 AnalysisInputs<ModelAdaptorAnalysis<ChromiumCheckModel>>(
136 Code, ast_matchers::hasName("target"),
137 [](ASTContext &C, Environment &) {
138 return ModelAdaptorAnalysis<ChromiumCheckModel>(C);
140 .withASTBuildArgs({"-fsyntax-only",
141 "-fno-delayed-template-parsing", "-std=c++17"})
142 .withASTBuildVirtualMappedFiles(std::move(FileContents)),
143 /*VerifyResults=*/
144 [&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
145 &Results,
146 const AnalysisOutputs &AO) { Match(Results, AO.ASTCtx); }),
147 llvm::Succeeded());
150 TEST(ChromiumCheckModelTest, CheckSuccessImpliesConditionHolds) {
151 auto Expectations =
152 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
153 ASTContext &ASTCtx) {
154 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
155 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
157 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
158 ASSERT_THAT(FooDecl, NotNull());
160 auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl));
162 EXPECT_TRUE(Env.proves(FooVal->formula()));
165 std::string Code = R"(
166 #include "check.h"
168 void target(bool Foo) {
169 $check(Foo);
170 bool X = true;
171 (void)X;
172 // [[p]]
175 runDataflow(ReplacePattern(Code, "$check", "CHECK"), Expectations);
176 runDataflow(ReplacePattern(Code, "$check", "DCHECK"), Expectations);
177 runDataflow(ReplacePattern(Code, "$check", "PCHECK"), Expectations);
178 runDataflow(ReplacePattern(Code, "$check", "DPCHECK"), Expectations);
181 TEST(ChromiumCheckModelTest, UnrelatedCheckIgnored) {
182 auto Expectations =
183 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
184 ASTContext &ASTCtx) {
185 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
186 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
188 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
189 ASSERT_THAT(FooDecl, NotNull());
191 auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl));
193 EXPECT_FALSE(Env.proves(FooVal->formula()));
196 std::string Code = R"(
197 #include "othercheck.h"
199 void target(bool Foo) {
200 if (!Foo) {
201 (void)other::logging::CheckError::Check(__FILE__, __LINE__, "Foo");
203 bool X = true;
204 (void)X;
205 // [[p]]
208 runDataflow(Code, Expectations);
210 } // namespace