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`. 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: 115a36c2dd6SYitzhak Mandelbaum explicit ModelAdaptorAnalysis(ASTContext &Context) 116a36c2dd6SYitzhak Mandelbaum : DataflowAnalysis<ModelAdaptorAnalysis, NoopLattice>( 117a36c2dd6SYitzhak Mandelbaum Context, /*ApplyBuiltinTransfer=*/true) {} 118a36c2dd6SYitzhak Mandelbaum 119a36c2dd6SYitzhak Mandelbaum static NoopLattice initialElement() { return NoopLattice(); } 120a36c2dd6SYitzhak Mandelbaum 121*6b991ba4SYitzhak Mandelbaum void transfer(const CFGElement &E, NoopLattice &, Environment &Env) { 12241d52c5aSWei Yi Tee M.transfer(E, Env); 123a36c2dd6SYitzhak Mandelbaum } 124a36c2dd6SYitzhak Mandelbaum 125a36c2dd6SYitzhak Mandelbaum private: 126a36c2dd6SYitzhak Mandelbaum Model M; 127a36c2dd6SYitzhak Mandelbaum }; 128a36c2dd6SYitzhak Mandelbaum 129a36c2dd6SYitzhak Mandelbaum template <typename Matcher> 130a36c2dd6SYitzhak Mandelbaum void runDataflow(llvm::StringRef Code, Matcher Match) { 131a36c2dd6SYitzhak Mandelbaum const tooling::FileContentMappings FileContents = { 132a36c2dd6SYitzhak Mandelbaum {"check.h", ChromiumCheckHeader}, {"othercheck.h", OtherCheckHeader}}; 133a36c2dd6SYitzhak Mandelbaum 134a36c2dd6SYitzhak Mandelbaum ASSERT_THAT_ERROR( 1359cbdef61SWei Yi Tee checkDataflow<ModelAdaptorAnalysis<ChromiumCheckModel>>( 1369cbdef61SWei Yi Tee AnalysisInputs<ModelAdaptorAnalysis<ChromiumCheckModel>>( 1379cbdef61SWei Yi Tee Code, ast_matchers::hasName("target"), 138a36c2dd6SYitzhak Mandelbaum [](ASTContext &C, Environment &) { 139a36c2dd6SYitzhak Mandelbaum return ModelAdaptorAnalysis<ChromiumCheckModel>(C); 1409cbdef61SWei Yi Tee }) 1419cbdef61SWei Yi Tee .withASTBuildArgs({"-fsyntax-only", 1429cbdef61SWei Yi Tee "-fno-delayed-template-parsing", "-std=c++17"}) 1439cbdef61SWei Yi Tee .withASTBuildVirtualMappedFiles(std::move(FileContents)), 1449cbdef61SWei Yi Tee /*VerifyResults=*/ 1459cbdef61SWei Yi Tee [&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> 1469cbdef61SWei Yi Tee &Results, 1479cbdef61SWei Yi Tee const AnalysisOutputs &AO) { Match(Results, AO.ASTCtx); }), 148a36c2dd6SYitzhak Mandelbaum llvm::Succeeded()); 149a36c2dd6SYitzhak Mandelbaum } 150a36c2dd6SYitzhak Mandelbaum 1511d83a16bSSam Estep TEST(ChromiumCheckModelTest, CheckSuccessImpliesConditionHolds) { 152a36c2dd6SYitzhak Mandelbaum auto Expectations = 1539cbdef61SWei Yi Tee [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 154a36c2dd6SYitzhak Mandelbaum ASTContext &ASTCtx) { 1559cbdef61SWei Yi Tee ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1569cbdef61SWei Yi Tee const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 157a36c2dd6SYitzhak Mandelbaum 158a36c2dd6SYitzhak Mandelbaum const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 159a36c2dd6SYitzhak Mandelbaum ASSERT_THAT(FooDecl, NotNull()); 160a36c2dd6SYitzhak Mandelbaum 161a36c2dd6SYitzhak Mandelbaum auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None)); 162a36c2dd6SYitzhak Mandelbaum 163a36c2dd6SYitzhak Mandelbaum EXPECT_TRUE(Env.flowConditionImplies(*FooVal)); 164a36c2dd6SYitzhak Mandelbaum }; 165a36c2dd6SYitzhak Mandelbaum 166a36c2dd6SYitzhak Mandelbaum std::string Code = R"( 167a36c2dd6SYitzhak Mandelbaum #include "check.h" 168a36c2dd6SYitzhak Mandelbaum 169a36c2dd6SYitzhak Mandelbaum void target(bool Foo) { 170a36c2dd6SYitzhak Mandelbaum $check(Foo); 171a36c2dd6SYitzhak Mandelbaum bool X = true; 172a36c2dd6SYitzhak Mandelbaum (void)X; 173a36c2dd6SYitzhak Mandelbaum // [[p]] 174a36c2dd6SYitzhak Mandelbaum } 175a36c2dd6SYitzhak Mandelbaum )"; 176a36c2dd6SYitzhak Mandelbaum runDataflow(ReplacePattern(Code, "$check", "CHECK"), Expectations); 177a36c2dd6SYitzhak Mandelbaum runDataflow(ReplacePattern(Code, "$check", "DCHECK"), Expectations); 178a36c2dd6SYitzhak Mandelbaum runDataflow(ReplacePattern(Code, "$check", "PCHECK"), Expectations); 179a36c2dd6SYitzhak Mandelbaum runDataflow(ReplacePattern(Code, "$check", "DPCHECK"), Expectations); 180a36c2dd6SYitzhak Mandelbaum } 181a36c2dd6SYitzhak Mandelbaum 1821d83a16bSSam Estep TEST(ChromiumCheckModelTest, UnrelatedCheckIgnored) { 183a36c2dd6SYitzhak Mandelbaum auto Expectations = 1849cbdef61SWei Yi Tee [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 185a36c2dd6SYitzhak Mandelbaum ASTContext &ASTCtx) { 1869cbdef61SWei Yi Tee ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 1879cbdef61SWei Yi Tee const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); 188a36c2dd6SYitzhak Mandelbaum 189a36c2dd6SYitzhak Mandelbaum const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); 190a36c2dd6SYitzhak Mandelbaum ASSERT_THAT(FooDecl, NotNull()); 191a36c2dd6SYitzhak Mandelbaum 192a36c2dd6SYitzhak Mandelbaum auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None)); 193a36c2dd6SYitzhak Mandelbaum 194a36c2dd6SYitzhak Mandelbaum EXPECT_FALSE(Env.flowConditionImplies(*FooVal)); 195a36c2dd6SYitzhak Mandelbaum }; 196a36c2dd6SYitzhak Mandelbaum 197a36c2dd6SYitzhak Mandelbaum std::string Code = R"( 198a36c2dd6SYitzhak Mandelbaum #include "othercheck.h" 199a36c2dd6SYitzhak Mandelbaum 200a36c2dd6SYitzhak Mandelbaum void target(bool Foo) { 201a36c2dd6SYitzhak Mandelbaum if (!Foo) { 202a36c2dd6SYitzhak Mandelbaum (void)other::logging::CheckError::Check(__FILE__, __LINE__, "Foo"); 203a36c2dd6SYitzhak Mandelbaum } 204a36c2dd6SYitzhak Mandelbaum bool X = true; 205a36c2dd6SYitzhak Mandelbaum (void)X; 206a36c2dd6SYitzhak Mandelbaum // [[p]] 207a36c2dd6SYitzhak Mandelbaum } 208a36c2dd6SYitzhak Mandelbaum )"; 209a36c2dd6SYitzhak Mandelbaum runDataflow(Code, Expectations); 210a36c2dd6SYitzhak Mandelbaum } 211a36c2dd6SYitzhak Mandelbaum } // namespace 212