brintos

brintos / llvm-project-archived public Read only

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