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