1 //===- ChromiumCheckModelTest.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 //===----------------------------------------------------------------------===//
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"
24 using namespace clang
;
25 using namespace dataflow
;
29 using ::testing::NotNull
;
30 using ::testing::UnorderedElementsAre
;
32 static constexpr char ChromiumCheckHeader
[] = R
"(
40 VoidifyStream() = default;
41 void operator&(std::ostream&) {}
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();
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) \
69 ::logging::CheckError::Check(__FILE__, __LINE__, #condition).stream(), \
72 #define PCHECK(condition) \
74 ::logging::CheckError::PCheck(__FILE__, __LINE__, #condition).stream(), \
77 #define DCHECK(condition) \
79 ::logging::CheckError::DCheck(__FILE__, __LINE__, #condition).stream(), \
82 #define DPCHECK(condition) \
84 ::logging::CheckError::DPCheck(__FILE__, __LINE__, #condition).stream(), \
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
"(
95 static CheckError Check(const char* file, int line, const char* condition);
97 } // namespace logging
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
) {
105 Pos
= S
.find(Pattern
, Pos
);
106 if (Pos
!= std::string::npos
)
107 S
.replace(Pos
, Pattern
.size(), Replacement
);
111 template <typename Model
>
112 class ModelAdaptorAnalysis
113 : public DataflowAnalysis
<ModelAdaptorAnalysis
<Model
>, NoopLattice
> {
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
) {
128 template <typename Matcher
>
129 void runDataflow(llvm::StringRef Code
, Matcher Match
) {
130 const tooling::FileContentMappings FileContents
= {
131 {"check.h", ChromiumCheckHeader
}, {"othercheck.h", OtherCheckHeader
}};
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
)),
144 [&Match
](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>>
146 const AnalysisOutputs
&AO
) { Match(Results
, AO
.ASTCtx
); }),
150 TEST(ChromiumCheckModelTest
, CheckSuccessImpliesConditionHolds
) {
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
"(
168 void target(bool Foo) {
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
) {
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) {
201 (void)other::logging::CheckError::Check(__FILE__, __LINE__, "Foo
");
208 runDataflow(Code
, Expectations
);