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" 14*9cbdef61SWei Yi Tee #include "clang/Analysis/FlowSensitive/NoopLattice.h" 15a36c2dd6SYitzhak Mandelbaum #include "clang/Tooling/Tooling.h" 16a36c2dd6SYitzhak Mandelbaum #include "llvm/ADT/ArrayRef.h" 17a36c2dd6SYitzhak Mandelbaum #include "llvm/Support/Error.h" 18a36c2dd6SYitzhak Mandelbaum #include "llvm/Testing/Support/Error.h" 19a36c2dd6SYitzhak Mandelbaum #include "gmock/gmock.h" 20a36c2dd6SYitzhak Mandelbaum #include "gtest/gtest.h" 21a36c2dd6SYitzhak Mandelbaum #include <string> 22a36c2dd6SYitzhak Mandelbaum 23a36c2dd6SYitzhak Mandelbaum using namespace clang; 24a36c2dd6SYitzhak Mandelbaum using namespace dataflow; 25a36c2dd6SYitzhak Mandelbaum using namespace test; 26a36c2dd6SYitzhak Mandelbaum 27a36c2dd6SYitzhak Mandelbaum namespace { 28a36c2dd6SYitzhak Mandelbaum using ::testing::NotNull; 29*9cbdef61SWei Yi Tee using ::testing::UnorderedElementsAre; 30a36c2dd6SYitzhak Mandelbaum 31a36c2dd6SYitzhak Mandelbaum static constexpr char ChromiumCheckHeader[] = R"( 32a36c2dd6SYitzhak Mandelbaum namespace std { 33a36c2dd6SYitzhak Mandelbaum class ostream; 34a36c2dd6SYitzhak Mandelbaum } // namespace std 35a36c2dd6SYitzhak Mandelbaum 36a36c2dd6SYitzhak Mandelbaum namespace logging { 37a36c2dd6SYitzhak Mandelbaum class VoidifyStream { 38a36c2dd6SYitzhak Mandelbaum public: 39a36c2dd6SYitzhak Mandelbaum VoidifyStream() = default; 40a36c2dd6SYitzhak Mandelbaum void operator&(std::ostream&) {} 41a36c2dd6SYitzhak Mandelbaum }; 42a36c2dd6SYitzhak Mandelbaum 43a36c2dd6SYitzhak Mandelbaum class CheckError { 44a36c2dd6SYitzhak Mandelbaum public: 45a36c2dd6SYitzhak Mandelbaum static CheckError Check(const char* file, int line, const char* condition); 46a36c2dd6SYitzhak Mandelbaum static CheckError DCheck(const char* file, int line, const char* condition); 47a36c2dd6SYitzhak Mandelbaum static CheckError PCheck(const char* file, int line, const char* condition); 48a36c2dd6SYitzhak Mandelbaum static CheckError PCheck(const char* file, int line); 49a36c2dd6SYitzhak Mandelbaum static CheckError DPCheck(const char* file, int line, const char* condition); 50a36c2dd6SYitzhak Mandelbaum 51a36c2dd6SYitzhak Mandelbaum std::ostream& stream(); 52a36c2dd6SYitzhak Mandelbaum 53a36c2dd6SYitzhak Mandelbaum ~CheckError(); 54a36c2dd6SYitzhak Mandelbaum 55a36c2dd6SYitzhak Mandelbaum CheckError(const CheckError& other) = delete; 56a36c2dd6SYitzhak Mandelbaum CheckError& operator=(const CheckError& other) = delete; 57a36c2dd6SYitzhak Mandelbaum CheckError(CheckError&& other) = default; 58a36c2dd6SYitzhak Mandelbaum CheckError& operator=(CheckError&& other) = default; 59a36c2dd6SYitzhak Mandelbaum }; 60a36c2dd6SYitzhak Mandelbaum 61a36c2dd6SYitzhak Mandelbaum } // namespace logging 62a36c2dd6SYitzhak Mandelbaum 63a36c2dd6SYitzhak Mandelbaum #define LAZY_CHECK_STREAM(stream, condition) \ 64a36c2dd6SYitzhak Mandelbaum !(condition) ? (void)0 : ::logging::VoidifyStream() & (stream) 65a36c2dd6SYitzhak Mandelbaum 66a36c2dd6SYitzhak Mandelbaum #define CHECK(condition) \ 67a36c2dd6SYitzhak Mandelbaum LAZY_CHECK_STREAM( \ 68a36c2dd6SYitzhak Mandelbaum ::logging::CheckError::Check(__FILE__, __LINE__, #condition).stream(), \ 69a36c2dd6SYitzhak Mandelbaum !(condition)) 70a36c2dd6SYitzhak Mandelbaum 71a36c2dd6SYitzhak Mandelbaum #define PCHECK(condition) \ 72a36c2dd6SYitzhak Mandelbaum LAZY_CHECK_STREAM( \ 73a36c2dd6SYitzhak Mandelbaum ::logging::CheckError::PCheck(__FILE__, __LINE__, #condition).stream(), \ 74a36c2dd6SYitzhak Mandelbaum !(condition)) 75a36c2dd6SYitzhak Mandelbaum 76a36c2dd6SYitzhak Mandelbaum #define DCHECK(condition) \ 77a36c2dd6SYitzhak Mandelbaum LAZY_CHECK_STREAM( \ 78a36c2dd6SYitzhak Mandelbaum ::logging::CheckError::DCheck(__FILE__, __LINE__, #condition).stream(), \ 79a36c2dd6SYitzhak Mandelbaum !(condition)) 80a36c2dd6SYitzhak Mandelbaum 81a36c2dd6SYitzhak Mandelbaum #define DPCHECK(condition) \ 82a36c2dd6SYitzhak Mandelbaum LAZY_CHECK_STREAM( \ 83a36c2dd6SYitzhak Mandelbaum ::logging::CheckError::DPCheck(__FILE__, __LINE__, #condition).stream(), \ 84a36c2dd6SYitzhak Mandelbaum !(condition)) 85a36c2dd6SYitzhak Mandelbaum )"; 86a36c2dd6SYitzhak Mandelbaum 87a36c2dd6SYitzhak Mandelbaum // A definition of the `CheckError` class that looks like the Chromium one, but 88a36c2dd6SYitzhak Mandelbaum // is actually something else. 89a36c2dd6SYitzhak Mandelbaum static constexpr char OtherCheckHeader[] = R"( 90a36c2dd6SYitzhak Mandelbaum namespace other { 91a36c2dd6SYitzhak Mandelbaum namespace logging { 92a36c2dd6SYitzhak Mandelbaum class CheckError { 93a36c2dd6SYitzhak Mandelbaum public: 94a36c2dd6SYitzhak Mandelbaum static CheckError Check(const char* file, int line, const char* condition); 95a36c2dd6SYitzhak Mandelbaum }; 96a36c2dd6SYitzhak Mandelbaum } // namespace logging 97a36c2dd6SYitzhak Mandelbaum } // namespace other 98a36c2dd6SYitzhak Mandelbaum )"; 99a36c2dd6SYitzhak Mandelbaum 100a36c2dd6SYitzhak Mandelbaum /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. 101a36c2dd6SYitzhak Mandelbaum std::string ReplacePattern(std::string S, const std::string &Pattern, 102a36c2dd6SYitzhak Mandelbaum const std::string &Replacement) { 103a36c2dd6SYitzhak Mandelbaum size_t Pos = 0; 104a36c2dd6SYitzhak Mandelbaum Pos = S.find(Pattern, Pos); 105a36c2dd6SYitzhak Mandelbaum if (Pos != std::string::npos) 106a36c2dd6SYitzhak Mandelbaum S.replace(Pos, Pattern.size(), Replacement); 107a36c2dd6SYitzhak Mandelbaum return S; 108a36c2dd6SYitzhak Mandelbaum } 109a36c2dd6SYitzhak Mandelbaum 110a36c2dd6SYitzhak Mandelbaum template <typename Model> 111a36c2dd6SYitzhak Mandelbaum class ModelAdaptorAnalysis 112a36c2dd6SYitzhak Mandelbaum : public DataflowAnalysis<ModelAdaptorAnalysis<Model>, NoopLattice> { 113a36c2dd6SYitzhak Mandelbaum public: 114a36c2dd6SYitzhak Mandelbaum explicit ModelAdaptorAnalysis(ASTContext &Context) 115a36c2dd6SYitzhak Mandelbaum : DataflowAnalysis<ModelAdaptorAnalysis, NoopLattice>( 116a36c2dd6SYitzhak Mandelbaum Context, /*ApplyBuiltinTransfer=*/true) {} 117a36c2dd6SYitzhak Mandelbaum 118a36c2dd6SYitzhak Mandelbaum static NoopLattice initialElement() { return NoopLattice(); } 119a36c2dd6SYitzhak Mandelbaum 120a36c2dd6SYitzhak Mandelbaum void transfer(const Stmt *S, NoopLattice &, Environment &Env) { 121a36c2dd6SYitzhak Mandelbaum M.transfer(S, Env); 122a36c2dd6SYitzhak Mandelbaum } 123a36c2dd6SYitzhak Mandelbaum 124a36c2dd6SYitzhak Mandelbaum private: 125a36c2dd6SYitzhak Mandelbaum Model M; 126a36c2dd6SYitzhak Mandelbaum }; 127a36c2dd6SYitzhak Mandelbaum 128a36c2dd6SYitzhak Mandelbaum template <typename Matcher> 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( 134*9cbdef61SWei Yi Tee checkDataflow<ModelAdaptorAnalysis<ChromiumCheckModel>>( 135*9cbdef61SWei Yi Tee AnalysisInputs<ModelAdaptorAnalysis<ChromiumCheckModel>>( 136*9cbdef61SWei Yi Tee Code, ast_matchers::hasName("target"), 137a36c2dd6SYitzhak Mandelbaum [](ASTContext &C, Environment &) { 138a36c2dd6SYitzhak Mandelbaum return ModelAdaptorAnalysis<ChromiumCheckModel>(C); 139*9cbdef61SWei Yi Tee }) 140*9cbdef61SWei Yi Tee .withASTBuildArgs({"-fsyntax-only", 141*9cbdef61SWei Yi Tee "-fno-delayed-template-parsing", "-std=c++17"}) 142*9cbdef61SWei Yi Tee .withASTBuildVirtualMappedFiles(std::move(FileContents)), 143*9cbdef61SWei Yi Tee /*VerifyResults=*/ 144*9cbdef61SWei Yi Tee [&Match](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> 145*9cbdef61SWei Yi Tee &Results, 146*9cbdef61SWei Yi Tee const AnalysisOutputs &AO) { Match(Results, AO.ASTCtx); }), 147a36c2dd6SYitzhak Mandelbaum llvm::Succeeded()); 148a36c2dd6SYitzhak Mandelbaum } 149a36c2dd6SYitzhak Mandelbaum 1501d83a16bSSam Estep TEST(ChromiumCheckModelTest, CheckSuccessImpliesConditionHolds) { 151a36c2dd6SYitzhak Mandelbaum auto Expectations = 152*9cbdef61SWei Yi Tee [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 153a36c2dd6SYitzhak Mandelbaum ASTContext &ASTCtx) { 154*9cbdef61SWei Yi Tee ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 155*9cbdef61SWei 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 160a36c2dd6SYitzhak Mandelbaum auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None)); 161a36c2dd6SYitzhak Mandelbaum 162a36c2dd6SYitzhak Mandelbaum EXPECT_TRUE(Env.flowConditionImplies(*FooVal)); 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 1811d83a16bSSam Estep TEST(ChromiumCheckModelTest, UnrelatedCheckIgnored) { 182a36c2dd6SYitzhak Mandelbaum auto Expectations = 183*9cbdef61SWei Yi Tee [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 184a36c2dd6SYitzhak Mandelbaum ASTContext &ASTCtx) { 185*9cbdef61SWei Yi Tee ASSERT_THAT(Results.keys(), UnorderedElementsAre("p")); 186*9cbdef61SWei 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 191a36c2dd6SYitzhak Mandelbaum auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None)); 192a36c2dd6SYitzhak Mandelbaum 193a36c2dd6SYitzhak Mandelbaum EXPECT_FALSE(Env.flowConditionImplies(*FooVal)); 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