xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp (revision 526c9b7e37fa12abc17eebc68f21c1d213477ba8)
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 "TestingSupport.h"
12a36c2dd6SYitzhak Mandelbaum #include "clang/AST/ASTContext.h"
13a36c2dd6SYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchers.h"
14cf94c52eSWei Yi Tee #include "clang/Analysis/CFG.h"
159cbdef61SWei Yi Tee #include "clang/Analysis/FlowSensitive/NoopLattice.h"
16a36c2dd6SYitzhak Mandelbaum #include "clang/Tooling/Tooling.h"
17a36c2dd6SYitzhak Mandelbaum #include "llvm/ADT/ArrayRef.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::NotNull;
309cbdef61SWei Yi Tee using ::testing::UnorderedElementsAre;
31a36c2dd6SYitzhak Mandelbaum 
32a36c2dd6SYitzhak Mandelbaum static constexpr char ChromiumCheckHeader[] = R"(
33a36c2dd6SYitzhak Mandelbaum namespace std {
34a36c2dd6SYitzhak Mandelbaum class ostream;
35a36c2dd6SYitzhak Mandelbaum } // namespace std
36a36c2dd6SYitzhak Mandelbaum 
37a36c2dd6SYitzhak Mandelbaum namespace logging {
38a36c2dd6SYitzhak Mandelbaum class VoidifyStream {
39a36c2dd6SYitzhak Mandelbaum  public:
40a36c2dd6SYitzhak Mandelbaum   VoidifyStream() = default;
41a36c2dd6SYitzhak Mandelbaum   void operator&(std::ostream&) {}
42a36c2dd6SYitzhak Mandelbaum };
43a36c2dd6SYitzhak Mandelbaum 
44a36c2dd6SYitzhak Mandelbaum class CheckError {
45a36c2dd6SYitzhak Mandelbaum  public:
46a36c2dd6SYitzhak Mandelbaum   static CheckError Check(const char* file, int line, const char* condition);
47a36c2dd6SYitzhak Mandelbaum   static CheckError DCheck(const char* file, int line, const char* condition);
48a36c2dd6SYitzhak Mandelbaum   static CheckError PCheck(const char* file, int line, const char* condition);
49a36c2dd6SYitzhak Mandelbaum   static CheckError PCheck(const char* file, int line);
50a36c2dd6SYitzhak Mandelbaum   static CheckError DPCheck(const char* file, int line, const char* condition);
51a36c2dd6SYitzhak Mandelbaum 
52a36c2dd6SYitzhak Mandelbaum   std::ostream& stream();
53a36c2dd6SYitzhak Mandelbaum 
54a36c2dd6SYitzhak Mandelbaum   ~CheckError();
55a36c2dd6SYitzhak Mandelbaum 
56a36c2dd6SYitzhak Mandelbaum   CheckError(const CheckError& other) = delete;
57a36c2dd6SYitzhak Mandelbaum   CheckError& operator=(const CheckError& other) = delete;
58a36c2dd6SYitzhak Mandelbaum   CheckError(CheckError&& other) = default;
59a36c2dd6SYitzhak Mandelbaum   CheckError& operator=(CheckError&& other) = default;
60a36c2dd6SYitzhak Mandelbaum };
61a36c2dd6SYitzhak Mandelbaum 
62a36c2dd6SYitzhak Mandelbaum } // namespace logging
63a36c2dd6SYitzhak Mandelbaum 
64a36c2dd6SYitzhak Mandelbaum #define LAZY_CHECK_STREAM(stream, condition) \
65a36c2dd6SYitzhak Mandelbaum   !(condition) ? (void)0 : ::logging::VoidifyStream() & (stream)
66a36c2dd6SYitzhak Mandelbaum 
67a36c2dd6SYitzhak Mandelbaum #define CHECK(condition)                                                     \
68a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                         \
69a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::Check(__FILE__, __LINE__, #condition).stream(), \
70a36c2dd6SYitzhak Mandelbaum       !(condition))
71a36c2dd6SYitzhak Mandelbaum 
72a36c2dd6SYitzhak Mandelbaum #define PCHECK(condition)                                                     \
73a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                          \
74a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::PCheck(__FILE__, __LINE__, #condition).stream(), \
75a36c2dd6SYitzhak Mandelbaum       !(condition))
76a36c2dd6SYitzhak Mandelbaum 
77a36c2dd6SYitzhak Mandelbaum #define DCHECK(condition)                                                     \
78a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                          \
79a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::DCheck(__FILE__, __LINE__, #condition).stream(), \
80a36c2dd6SYitzhak Mandelbaum       !(condition))
81a36c2dd6SYitzhak Mandelbaum 
82a36c2dd6SYitzhak Mandelbaum #define DPCHECK(condition)                                                     \
83a36c2dd6SYitzhak Mandelbaum   LAZY_CHECK_STREAM(                                                           \
84a36c2dd6SYitzhak Mandelbaum       ::logging::CheckError::DPCheck(__FILE__, __LINE__, #condition).stream(), \
85a36c2dd6SYitzhak Mandelbaum       !(condition))
86a36c2dd6SYitzhak Mandelbaum )";
87a36c2dd6SYitzhak Mandelbaum 
88a36c2dd6SYitzhak Mandelbaum // A definition of the `CheckError` class that looks like the Chromium one, but
89a36c2dd6SYitzhak Mandelbaum // is actually something else.
90a36c2dd6SYitzhak Mandelbaum static constexpr char OtherCheckHeader[] = R"(
91a36c2dd6SYitzhak Mandelbaum namespace other {
92a36c2dd6SYitzhak Mandelbaum namespace logging {
93a36c2dd6SYitzhak Mandelbaum class CheckError {
94a36c2dd6SYitzhak Mandelbaum  public:
95a36c2dd6SYitzhak Mandelbaum   static CheckError Check(const char* file, int line, const char* condition);
96a36c2dd6SYitzhak Mandelbaum };
97a36c2dd6SYitzhak Mandelbaum } // namespace logging
98a36c2dd6SYitzhak Mandelbaum } // namespace other
99a36c2dd6SYitzhak Mandelbaum )";
100a36c2dd6SYitzhak Mandelbaum 
101a36c2dd6SYitzhak Mandelbaum /// Replaces all occurrences of `Pattern` in `S` with `Replacement`.
ReplacePattern(std::string S,const std::string & Pattern,const std::string & Replacement)102a36c2dd6SYitzhak Mandelbaum std::string ReplacePattern(std::string S, const std::string &Pattern,
103a36c2dd6SYitzhak Mandelbaum                            const std::string &Replacement) {
104a36c2dd6SYitzhak Mandelbaum   size_t Pos = 0;
105a36c2dd6SYitzhak Mandelbaum   Pos = S.find(Pattern, Pos);
106a36c2dd6SYitzhak Mandelbaum   if (Pos != std::string::npos)
107a36c2dd6SYitzhak Mandelbaum     S.replace(Pos, Pattern.size(), Replacement);
108a36c2dd6SYitzhak Mandelbaum   return S;
109a36c2dd6SYitzhak Mandelbaum }
110a36c2dd6SYitzhak Mandelbaum 
111a36c2dd6SYitzhak Mandelbaum template <typename Model>
112a36c2dd6SYitzhak Mandelbaum class ModelAdaptorAnalysis
113a36c2dd6SYitzhak Mandelbaum     : public DataflowAnalysis<ModelAdaptorAnalysis<Model>, NoopLattice> {
114a36c2dd6SYitzhak Mandelbaum public:
ModelAdaptorAnalysis(ASTContext & Context)115a36c2dd6SYitzhak Mandelbaum   explicit ModelAdaptorAnalysis(ASTContext &Context)
11637458c66SMartin Braenne       : DataflowAnalysis<ModelAdaptorAnalysis, NoopLattice>(Context) {}
117a36c2dd6SYitzhak Mandelbaum 
initialElement()118a36c2dd6SYitzhak Mandelbaum   static NoopLattice initialElement() { return NoopLattice(); }
119a36c2dd6SYitzhak Mandelbaum 
transfer(const CFGElement & E,NoopLattice &,Environment & Env)1206b991ba4SYitzhak Mandelbaum   void transfer(const CFGElement &E, NoopLattice &, Environment &Env) {
12141d52c5aSWei Yi Tee     M.transfer(E, Env);
122a36c2dd6SYitzhak Mandelbaum   }
123a36c2dd6SYitzhak Mandelbaum 
124a36c2dd6SYitzhak Mandelbaum private:
125a36c2dd6SYitzhak Mandelbaum   Model M;
126a36c2dd6SYitzhak Mandelbaum };
127a36c2dd6SYitzhak Mandelbaum 
128a36c2dd6SYitzhak Mandelbaum template <typename Matcher>
runDataflow(llvm::StringRef Code,Matcher Match)129a36c2dd6SYitzhak Mandelbaum void runDataflow(llvm::StringRef Code, Matcher Match) {
130a36c2dd6SYitzhak Mandelbaum   const tooling::FileContentMappings FileContents = {
131a36c2dd6SYitzhak Mandelbaum       {"check.h", ChromiumCheckHeader}, {"othercheck.h", OtherCheckHeader}};
132a36c2dd6SYitzhak Mandelbaum 
133a36c2dd6SYitzhak Mandelbaum   ASSERT_THAT_ERROR(
1349cbdef61SWei Yi Tee       checkDataflow<ModelAdaptorAnalysis<ChromiumCheckModel>>(
1359cbdef61SWei Yi Tee           AnalysisInputs<ModelAdaptorAnalysis<ChromiumCheckModel>>(
1369cbdef61SWei Yi Tee               Code, ast_matchers::hasName("target"),
137a36c2dd6SYitzhak Mandelbaum               [](ASTContext &C, Environment &) {
138a36c2dd6SYitzhak Mandelbaum                 return ModelAdaptorAnalysis<ChromiumCheckModel>(C);
1399cbdef61SWei Yi Tee               })
1409cbdef61SWei Yi Tee               .withASTBuildArgs({"-fsyntax-only",
1419cbdef61SWei Yi Tee                                  "-fno-delayed-template-parsing", "-std=c++17"})
1429cbdef61SWei Yi Tee               .withASTBuildVirtualMappedFiles(std::move(FileContents)),
1439cbdef61SWei Yi Tee           /*VerifyResults=*/
1449cbdef61SWei Yi Tee           [&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
1459cbdef61SWei Yi Tee                        &Results,
1469cbdef61SWei Yi Tee                    const AnalysisOutputs &AO) { Match(Results, AO.ASTCtx); }),
147a36c2dd6SYitzhak Mandelbaum       llvm::Succeeded());
148a36c2dd6SYitzhak Mandelbaum }
149a36c2dd6SYitzhak Mandelbaum 
TEST(ChromiumCheckModelTest,CheckSuccessImpliesConditionHolds)1501d83a16bSSam Estep TEST(ChromiumCheckModelTest, CheckSuccessImpliesConditionHolds) {
151a36c2dd6SYitzhak Mandelbaum   auto Expectations =
1529cbdef61SWei Yi Tee       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
153a36c2dd6SYitzhak Mandelbaum          ASTContext &ASTCtx) {
1549cbdef61SWei Yi Tee         ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1559cbdef61SWei Yi Tee         const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
156a36c2dd6SYitzhak Mandelbaum 
157a36c2dd6SYitzhak Mandelbaum         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
158a36c2dd6SYitzhak Mandelbaum         ASSERT_THAT(FooDecl, NotNull());
159a36c2dd6SYitzhak Mandelbaum 
1600c852dc8SMartin Braenne         auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl));
161a36c2dd6SYitzhak Mandelbaum 
162*526c9b7eSmartinboehme         EXPECT_TRUE(Env.proves(FooVal->formula()));
163a36c2dd6SYitzhak Mandelbaum       };
164a36c2dd6SYitzhak Mandelbaum 
165a36c2dd6SYitzhak Mandelbaum   std::string Code = R"(
166a36c2dd6SYitzhak Mandelbaum     #include "check.h"
167a36c2dd6SYitzhak Mandelbaum 
168a36c2dd6SYitzhak Mandelbaum     void target(bool Foo) {
169a36c2dd6SYitzhak Mandelbaum       $check(Foo);
170a36c2dd6SYitzhak Mandelbaum       bool X = true;
171a36c2dd6SYitzhak Mandelbaum       (void)X;
172a36c2dd6SYitzhak Mandelbaum       // [[p]]
173a36c2dd6SYitzhak Mandelbaum     }
174a36c2dd6SYitzhak Mandelbaum   )";
175a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "CHECK"), Expectations);
176a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "DCHECK"), Expectations);
177a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "PCHECK"), Expectations);
178a36c2dd6SYitzhak Mandelbaum   runDataflow(ReplacePattern(Code, "$check", "DPCHECK"), Expectations);
179a36c2dd6SYitzhak Mandelbaum }
180a36c2dd6SYitzhak Mandelbaum 
TEST(ChromiumCheckModelTest,UnrelatedCheckIgnored)1811d83a16bSSam Estep TEST(ChromiumCheckModelTest, UnrelatedCheckIgnored) {
182a36c2dd6SYitzhak Mandelbaum   auto Expectations =
1839cbdef61SWei Yi Tee       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
184a36c2dd6SYitzhak Mandelbaum          ASTContext &ASTCtx) {
1859cbdef61SWei Yi Tee         ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1869cbdef61SWei Yi Tee         const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
187a36c2dd6SYitzhak Mandelbaum 
188a36c2dd6SYitzhak Mandelbaum         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
189a36c2dd6SYitzhak Mandelbaum         ASSERT_THAT(FooDecl, NotNull());
190a36c2dd6SYitzhak Mandelbaum 
1910c852dc8SMartin Braenne         auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl));
192a36c2dd6SYitzhak Mandelbaum 
193*526c9b7eSmartinboehme         EXPECT_FALSE(Env.proves(FooVal->formula()));
194a36c2dd6SYitzhak Mandelbaum       };
195a36c2dd6SYitzhak Mandelbaum 
196a36c2dd6SYitzhak Mandelbaum   std::string Code = R"(
197a36c2dd6SYitzhak Mandelbaum     #include "othercheck.h"
198a36c2dd6SYitzhak Mandelbaum 
199a36c2dd6SYitzhak Mandelbaum     void target(bool Foo) {
200a36c2dd6SYitzhak Mandelbaum       if (!Foo) {
201a36c2dd6SYitzhak Mandelbaum         (void)other::logging::CheckError::Check(__FILE__, __LINE__, "Foo");
202a36c2dd6SYitzhak Mandelbaum       }
203a36c2dd6SYitzhak Mandelbaum       bool X = true;
204a36c2dd6SYitzhak Mandelbaum       (void)X;
205a36c2dd6SYitzhak Mandelbaum       // [[p]]
206a36c2dd6SYitzhak Mandelbaum     }
207a36c2dd6SYitzhak Mandelbaum   )";
208a36c2dd6SYitzhak Mandelbaum   runDataflow(Code, Expectations);
209a36c2dd6SYitzhak Mandelbaum }
210a36c2dd6SYitzhak Mandelbaum } // namespace
211