xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp (revision 1d83a16bd3faa1dfb6e8ed40c53d018dc03e2c81)
1a36c2dd6SYitzhak Mandelbaum //===- ChromiumCheckModelTest.cpp -----------------------------------------===//
2a36c2dd6SYitzhak Mandelbaum //
3a36c2dd6SYitzhak Mandelbaum // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a36c2dd6SYitzhak Mandelbaum // See https://llvm.org/LICENSE.txt for license information.
5a36c2dd6SYitzhak Mandelbaum // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a36c2dd6SYitzhak Mandelbaum //
7a36c2dd6SYitzhak Mandelbaum //===----------------------------------------------------------------------===//
8a36c2dd6SYitzhak Mandelbaum // FIXME: Move this to clang/unittests/Analysis/FlowSensitive/Models.
9a36c2dd6SYitzhak Mandelbaum 
10a36c2dd6SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h"
11a36c2dd6SYitzhak Mandelbaum #include "NoopAnalysis.h"
12a36c2dd6SYitzhak Mandelbaum #include "TestingSupport.h"
13a36c2dd6SYitzhak Mandelbaum #include "clang/AST/ASTContext.h"
14a36c2dd6SYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchers.h"
15a36c2dd6SYitzhak Mandelbaum #include "clang/Tooling/Tooling.h"
16a36c2dd6SYitzhak Mandelbaum #include "llvm/ADT/ArrayRef.h"
17a36c2dd6SYitzhak Mandelbaum #include "llvm/ADT/StringExtras.h"
18a36c2dd6SYitzhak Mandelbaum #include "llvm/Support/Error.h"
19a36c2dd6SYitzhak Mandelbaum #include "llvm/Testing/Support/Error.h"
20a36c2dd6SYitzhak Mandelbaum #include "gmock/gmock.h"
21a36c2dd6SYitzhak Mandelbaum #include "gtest/gtest.h"
22a36c2dd6SYitzhak Mandelbaum #include <string>
23a36c2dd6SYitzhak Mandelbaum 
24a36c2dd6SYitzhak Mandelbaum using namespace clang;
25a36c2dd6SYitzhak Mandelbaum using namespace dataflow;
26a36c2dd6SYitzhak Mandelbaum using namespace test;
27a36c2dd6SYitzhak Mandelbaum 
28a36c2dd6SYitzhak Mandelbaum namespace {
29a36c2dd6SYitzhak Mandelbaum using ::testing::_;
30a36c2dd6SYitzhak Mandelbaum using ::testing::ElementsAre;
31a36c2dd6SYitzhak Mandelbaum using ::testing::NotNull;
32a36c2dd6SYitzhak Mandelbaum using ::testing::Pair;
33a36c2dd6SYitzhak Mandelbaum 
34a36c2dd6SYitzhak Mandelbaum static constexpr char ChromiumCheckHeader[] = R"(
35a36c2dd6SYitzhak Mandelbaum namespace std {
36a36c2dd6SYitzhak Mandelbaum class ostream;
37a36c2dd6SYitzhak Mandelbaum } // namespace std
38a36c2dd6SYitzhak Mandelbaum 
39a36c2dd6SYitzhak Mandelbaum namespace logging {
40a36c2dd6SYitzhak Mandelbaum class VoidifyStream {
41a36c2dd6SYitzhak Mandelbaum  public:
42a36c2dd6SYitzhak Mandelbaum   VoidifyStream() = default;
43a36c2dd6SYitzhak Mandelbaum   void operator&(std::ostream&) {}
44a36c2dd6SYitzhak Mandelbaum };
45a36c2dd6SYitzhak Mandelbaum 
46a36c2dd6SYitzhak Mandelbaum class CheckError {
47a36c2dd6SYitzhak Mandelbaum  public:
48a36c2dd6SYitzhak Mandelbaum   static CheckError Check(const char* file, int line, const char* condition);
49a36c2dd6SYitzhak Mandelbaum   static CheckError DCheck(const char* file, int line, const char* condition);
50a36c2dd6SYitzhak Mandelbaum   static CheckError PCheck(const char* file, int line, const char* condition);
51a36c2dd6SYitzhak Mandelbaum   static CheckError PCheck(const char* file, int line);
52a36c2dd6SYitzhak Mandelbaum   static CheckError DPCheck(const char* file, int line, const char* condition);
53a36c2dd6SYitzhak Mandelbaum 
54a36c2dd6SYitzhak Mandelbaum   std::ostream& stream();
55a36c2dd6SYitzhak Mandelbaum 
56a36c2dd6SYitzhak Mandelbaum   ~CheckError();
57a36c2dd6SYitzhak Mandelbaum 
58a36c2dd6SYitzhak Mandelbaum   CheckError(const CheckError& other) = delete;
59a36c2dd6SYitzhak Mandelbaum   CheckError& operator=(const CheckError& other) = delete;
60a36c2dd6SYitzhak Mandelbaum   CheckError(CheckError&& other) = default;
61a36c2dd6SYitzhak Mandelbaum   CheckError& operator=(CheckError&& other) = default;
62a36c2dd6SYitzhak Mandelbaum };
63a36c2dd6SYitzhak Mandelbaum 
64a36c2dd6SYitzhak Mandelbaum } // namespace logging
65a36c2dd6SYitzhak Mandelbaum 
66a36c2dd6SYitzhak Mandelbaum #define LAZY_CHECK_STREAM(stream, condition) \
67a36c2dd6SYitzhak Mandelbaum   !(condition) ? (void)0 : ::logging::VoidifyStream() & (stream)
68a36c2dd6SYitzhak Mandelbaum 
69a36c2dd6SYitzhak Mandelbaum #define CHECK(condition)                                                     \
70a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                         \
71a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::Check(__FILE__, __LINE__, #condition).stream(), \
72a36c2dd6SYitzhak Mandelbaum       !(condition))
73a36c2dd6SYitzhak Mandelbaum 
74a36c2dd6SYitzhak Mandelbaum #define PCHECK(condition)                                                     \
75a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                          \
76a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::PCheck(__FILE__, __LINE__, #condition).stream(), \
77a36c2dd6SYitzhak Mandelbaum       !(condition))
78a36c2dd6SYitzhak Mandelbaum 
79a36c2dd6SYitzhak Mandelbaum #define DCHECK(condition)                                                     \
80a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                          \
81a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::DCheck(__FILE__, __LINE__, #condition).stream(), \
82a36c2dd6SYitzhak Mandelbaum       !(condition))
83a36c2dd6SYitzhak Mandelbaum 
84a36c2dd6SYitzhak Mandelbaum #define DPCHECK(condition)                                                     \
85a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                           \
86a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::DPCheck(__FILE__, __LINE__, #condition).stream(), \
87a36c2dd6SYitzhak Mandelbaum       !(condition))
88a36c2dd6SYitzhak Mandelbaum )";
89a36c2dd6SYitzhak Mandelbaum 
90a36c2dd6SYitzhak Mandelbaum // A definition of the `CheckError` class that looks like the Chromium one, but
91a36c2dd6SYitzhak Mandelbaum // is actually something else.
92a36c2dd6SYitzhak Mandelbaum static constexpr char OtherCheckHeader[] = R"(
93a36c2dd6SYitzhak Mandelbaum namespace other {
94a36c2dd6SYitzhak Mandelbaum namespace logging {
95a36c2dd6SYitzhak Mandelbaum class CheckError {
96a36c2dd6SYitzhak Mandelbaum  public:
97a36c2dd6SYitzhak Mandelbaum   static CheckError Check(const char* file, int line, const char* condition);
98a36c2dd6SYitzhak Mandelbaum };
99a36c2dd6SYitzhak Mandelbaum } // namespace logging
100a36c2dd6SYitzhak Mandelbaum } // namespace other
101a36c2dd6SYitzhak Mandelbaum )";
102a36c2dd6SYitzhak Mandelbaum 
103a36c2dd6SYitzhak Mandelbaum /// Replaces all occurrences of `Pattern` in `S` with `Replacement`.
104a36c2dd6SYitzhak Mandelbaum std::string ReplacePattern(std::string S, const std::string &Pattern,
105a36c2dd6SYitzhak Mandelbaum                            const std::string &Replacement) {
106a36c2dd6SYitzhak Mandelbaum   size_t Pos = 0;
107a36c2dd6SYitzhak Mandelbaum   Pos = S.find(Pattern, Pos);
108a36c2dd6SYitzhak Mandelbaum   if (Pos != std::string::npos)
109a36c2dd6SYitzhak Mandelbaum     S.replace(Pos, Pattern.size(), Replacement);
110a36c2dd6SYitzhak Mandelbaum   return S;
111a36c2dd6SYitzhak Mandelbaum }
112a36c2dd6SYitzhak Mandelbaum 
113a36c2dd6SYitzhak Mandelbaum template <typename Model>
114a36c2dd6SYitzhak Mandelbaum class ModelAdaptorAnalysis
115a36c2dd6SYitzhak Mandelbaum     : public DataflowAnalysis<ModelAdaptorAnalysis<Model>, NoopLattice> {
116a36c2dd6SYitzhak Mandelbaum public:
117a36c2dd6SYitzhak Mandelbaum   explicit ModelAdaptorAnalysis(ASTContext &Context)
118a36c2dd6SYitzhak Mandelbaum       : DataflowAnalysis<ModelAdaptorAnalysis, NoopLattice>(
119a36c2dd6SYitzhak Mandelbaum             Context, /*ApplyBuiltinTransfer=*/true) {}
120a36c2dd6SYitzhak Mandelbaum 
121a36c2dd6SYitzhak Mandelbaum   static NoopLattice initialElement() { return NoopLattice(); }
122a36c2dd6SYitzhak Mandelbaum 
123a36c2dd6SYitzhak Mandelbaum   void transfer(const Stmt *S, NoopLattice &, Environment &Env) {
124a36c2dd6SYitzhak Mandelbaum     M.transfer(S, Env);
125a36c2dd6SYitzhak Mandelbaum   }
126a36c2dd6SYitzhak Mandelbaum 
127a36c2dd6SYitzhak Mandelbaum private:
128a36c2dd6SYitzhak Mandelbaum   Model M;
129a36c2dd6SYitzhak Mandelbaum };
130a36c2dd6SYitzhak Mandelbaum 
131a36c2dd6SYitzhak Mandelbaum template <typename Matcher>
132a36c2dd6SYitzhak Mandelbaum void runDataflow(llvm::StringRef Code, Matcher Match) {
133a36c2dd6SYitzhak Mandelbaum   const tooling::FileContentMappings FileContents = {
134a36c2dd6SYitzhak Mandelbaum       {"check.h", ChromiumCheckHeader}, {"othercheck.h", OtherCheckHeader}};
135a36c2dd6SYitzhak Mandelbaum 
136a36c2dd6SYitzhak Mandelbaum   ASSERT_THAT_ERROR(
137a36c2dd6SYitzhak Mandelbaum       test::checkDataflow<ModelAdaptorAnalysis<ChromiumCheckModel>>(
138a36c2dd6SYitzhak Mandelbaum           Code, "target",
139a36c2dd6SYitzhak Mandelbaum           [](ASTContext &C, Environment &) {
140a36c2dd6SYitzhak Mandelbaum             return ModelAdaptorAnalysis<ChromiumCheckModel>(C);
141a36c2dd6SYitzhak Mandelbaum           },
142a36c2dd6SYitzhak Mandelbaum           [&Match](
143a36c2dd6SYitzhak Mandelbaum               llvm::ArrayRef<
144a36c2dd6SYitzhak Mandelbaum                   std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
145a36c2dd6SYitzhak Mandelbaum                   Results,
146a36c2dd6SYitzhak Mandelbaum               ASTContext &ASTCtx) { Match(Results, ASTCtx); },
147a36c2dd6SYitzhak Mandelbaum           {"-fsyntax-only", "-fno-delayed-template-parsing", "-std=c++17"},
148a36c2dd6SYitzhak Mandelbaum           FileContents),
149a36c2dd6SYitzhak Mandelbaum       llvm::Succeeded());
150a36c2dd6SYitzhak Mandelbaum }
151a36c2dd6SYitzhak Mandelbaum 
152*1d83a16bSSam Estep TEST(ChromiumCheckModelTest, CheckSuccessImpliesConditionHolds) {
153a36c2dd6SYitzhak Mandelbaum   auto Expectations =
154a36c2dd6SYitzhak Mandelbaum       [](llvm::ArrayRef<
155a36c2dd6SYitzhak Mandelbaum              std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
156a36c2dd6SYitzhak Mandelbaum              Results,
157a36c2dd6SYitzhak Mandelbaum          ASTContext &ASTCtx) {
158a36c2dd6SYitzhak Mandelbaum         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
159a36c2dd6SYitzhak Mandelbaum         const Environment &Env = Results[0].second.Env;
160a36c2dd6SYitzhak Mandelbaum 
161a36c2dd6SYitzhak Mandelbaum         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
162a36c2dd6SYitzhak Mandelbaum         ASSERT_THAT(FooDecl, NotNull());
163a36c2dd6SYitzhak Mandelbaum 
164a36c2dd6SYitzhak Mandelbaum         auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
165a36c2dd6SYitzhak Mandelbaum 
166a36c2dd6SYitzhak Mandelbaum         EXPECT_TRUE(Env.flowConditionImplies(*FooVal));
167a36c2dd6SYitzhak Mandelbaum       };
168a36c2dd6SYitzhak Mandelbaum 
169a36c2dd6SYitzhak Mandelbaum   std::string Code = R"(
170a36c2dd6SYitzhak Mandelbaum     #include "check.h"
171a36c2dd6SYitzhak Mandelbaum 
172a36c2dd6SYitzhak Mandelbaum     void target(bool Foo) {
173a36c2dd6SYitzhak Mandelbaum       $check(Foo);
174a36c2dd6SYitzhak Mandelbaum       bool X = true;
175a36c2dd6SYitzhak Mandelbaum       (void)X;
176a36c2dd6SYitzhak Mandelbaum       // [[p]]
177a36c2dd6SYitzhak Mandelbaum     }
178a36c2dd6SYitzhak Mandelbaum   )";
179a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "CHECK"), Expectations);
180a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "DCHECK"), Expectations);
181a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "PCHECK"), Expectations);
182a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "DPCHECK"), Expectations);
183a36c2dd6SYitzhak Mandelbaum }
184a36c2dd6SYitzhak Mandelbaum 
185*1d83a16bSSam Estep TEST(ChromiumCheckModelTest, UnrelatedCheckIgnored) {
186a36c2dd6SYitzhak Mandelbaum   auto Expectations =
187a36c2dd6SYitzhak Mandelbaum       [](llvm::ArrayRef<
188a36c2dd6SYitzhak Mandelbaum              std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
189a36c2dd6SYitzhak Mandelbaum              Results,
190a36c2dd6SYitzhak Mandelbaum          ASTContext &ASTCtx) {
191a36c2dd6SYitzhak Mandelbaum         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
192a36c2dd6SYitzhak Mandelbaum         const Environment &Env = Results[0].second.Env;
193a36c2dd6SYitzhak Mandelbaum 
194a36c2dd6SYitzhak Mandelbaum         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
195a36c2dd6SYitzhak Mandelbaum         ASSERT_THAT(FooDecl, NotNull());
196a36c2dd6SYitzhak Mandelbaum 
197a36c2dd6SYitzhak Mandelbaum         auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
198a36c2dd6SYitzhak Mandelbaum 
199a36c2dd6SYitzhak Mandelbaum         EXPECT_FALSE(Env.flowConditionImplies(*FooVal));
200a36c2dd6SYitzhak Mandelbaum       };
201a36c2dd6SYitzhak Mandelbaum 
202a36c2dd6SYitzhak Mandelbaum   std::string Code = R"(
203a36c2dd6SYitzhak Mandelbaum     #include "othercheck.h"
204a36c2dd6SYitzhak Mandelbaum 
205a36c2dd6SYitzhak Mandelbaum     void target(bool Foo) {
206a36c2dd6SYitzhak Mandelbaum       if (!Foo) {
207a36c2dd6SYitzhak Mandelbaum         (void)other::logging::CheckError::Check(__FILE__, __LINE__, "Foo");
208a36c2dd6SYitzhak Mandelbaum       }
209a36c2dd6SYitzhak Mandelbaum       bool X = true;
210a36c2dd6SYitzhak Mandelbaum       (void)X;
211a36c2dd6SYitzhak Mandelbaum       // [[p]]
212a36c2dd6SYitzhak Mandelbaum     }
213a36c2dd6SYitzhak Mandelbaum   )";
214a36c2dd6SYitzhak Mandelbaum   runDataflow(Code, Expectations);
215a36c2dd6SYitzhak Mandelbaum }
216a36c2dd6SYitzhak Mandelbaum } // namespace
217