brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.4 KiB · 92dcd93 Raw
160 lines · c
1//===- UncheckedStatusOrAccessModelTestFixture.h --------------------------===//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 9#ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_10#define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_11 12#include <algorithm>13#include <iterator>14#include <string>15#include <utility>16#include <vector>17 18#include "TestingSupport.h"19#include "clang/AST/ASTContext.h"20#include "clang/AST/Decl.h"21#include "clang/ASTMatchers/ASTMatchers.h"22#include "clang/Analysis/CFG.h"23#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"24#include "clang/Analysis/FlowSensitive/MatchSwitch.h"25#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h"26#include "clang/Basic/SourceLocation.h"27#include "clang/Tooling/Tooling.h"28#include "llvm/ADT/DenseMap.h"29#include "llvm/ADT/DenseSet.h"30#include "llvm/ADT/STLExtras.h"31#include "llvm/Support/Error.h"32#include "gmock/gmock.h"33#include "gtest/gtest.h"34 35namespace clang::dataflow::statusor_model {36 37enum class UncheckedStatusOrAccessModelTestAliasKind {38  kUnaliased = 0,        // no alias39  kPartiallyAliased = 1, // template<typename T> using Alias = absl::StatusOr;40  kFullyAliased = 2,     // using Alias = absl::StatusOr<int>;41};42 43// Base class for the test executors. This is needed to abstract away the44// template parameter from the UncheckedStatusOrAccessModelTestExecutor. This45// allows us to use UncheckedStatusOrAccessModelTestExecutorBase* in the46// UncheckedStatusOrAccessModelTest.47class UncheckedStatusOrAccessModelTestExecutorBase {48public:49  virtual void50  ExpectDiagnosticsFor(std::string SourceCode,51                       UncheckedStatusOrAccessModelTestAliasKind) const = 0;52  virtual void ExpectDiagnosticsForLambda(53      std::string SourceCode,54      UncheckedStatusOrAccessModelTestAliasKind) const = 0;55  virtual ~UncheckedStatusOrAccessModelTestExecutorBase() = default;56};57 58// Returns these macros according to the alias kind:59//  - STATUS60//  - STATUSOR_INT61//  - STATUSOR_BOOL62//  - STATUSOR_VOIDPTR63// Tests should use these macros instead of e.g. absl::StatusOr<int> to ensure64// the model is insensitive to whether the StatusOr<> is aliased or not.65std::string GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind);66 67std::vector<std::pair<std::string, std::string>>68GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind);69 70// This allows us to run the same test suite for multiple models. This allows71// vendors to model internal APIs in an extension of the base model, and make72// sure that these tests still pass.73template <typename Model>74class UncheckedStatusOrAccessModelTestExecutor75    : public UncheckedStatusOrAccessModelTestExecutorBase {76public:77  void ExpectDiagnosticsFor(78      std::string SourceCode,79      UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override {80    using namespace ::clang::ast_matchers; // NOLINT: Too many names81    ExpectDiagnosticsFor(SourceCode, hasName("target"), AliasKind);82  }83 84  void ExpectDiagnosticsForLambda(85      std::string SourceCode,86      UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override {87    using namespace ::clang::ast_matchers; // NOLINT: Too many names88    ExpectDiagnosticsFor(SourceCode,89                         allOf(hasOverloadedOperatorName("()"),90                               hasDeclContext(cxxRecordDecl(isLambda()))),91                         AliasKind);92  }93 94  template <typename FuncDeclMatcher>95  void ExpectDiagnosticsFor(96      std::string SourceCode, FuncDeclMatcher FuncMatcher,97      UncheckedStatusOrAccessModelTestAliasKind AliasKind) const {98    std::vector<std::pair<std::string, std::string>> Headers =99        GetHeaders(AliasKind);100 101    UncheckedStatusOrAccessModelOptions Options{};102    std::vector<SourceLocation> Diagnostics;103    llvm::Error Error = test::checkDataflow<Model>(104        test::AnalysisInputs<Model>(105            SourceCode, std::move(FuncMatcher),106            [](ASTContext &Ctx, Environment &Env) { return Model(Ctx, Env); })107            .withPostVisitCFG(108                [&Diagnostics,109                 Diagnoser = UncheckedStatusOrAccessDiagnoser(Options)](110                    ASTContext &Ctx, const CFGElement &Elt,111                    const TransferStateForDiagnostics<112                        UncheckedStatusOrAccessModel::Lattice> &State) mutable {113                  auto EltDiagnostics = Diagnoser(Elt, Ctx, State);114                  llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));115                })116            .withASTBuildArgs(117                {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})118            .withASTBuildVirtualMappedFiles(119                tooling::FileContentMappings(Headers.begin(), Headers.end())),120        /*VerifyResults=*/[&Diagnostics, SourceCode](121                              const llvm::DenseMap<unsigned, std::string>122                                  &Annotations,123                              const test::AnalysisOutputs &AO) {124          llvm::DenseSet<unsigned> AnnotationLines;125          for (const auto &[Line, _] : Annotations)126            AnnotationLines.insert(Line);127          auto &SrcMgr = AO.ASTCtx.getSourceManager();128          llvm::DenseSet<unsigned> DiagnosticLines;129          for (SourceLocation &Loc : Diagnostics)130            DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));131 132          EXPECT_THAT(DiagnosticLines, testing::ContainerEq(AnnotationLines))133              << "\nFailing code:\n"134              << SourceCode;135        });136    if (Error)137      FAIL() << llvm::toString(std::move(Error));138  }139 140  ~UncheckedStatusOrAccessModelTestExecutor() override = default;141};142 143class UncheckedStatusOrAccessModelTest144    : public ::testing::TestWithParam<145          std::pair<UncheckedStatusOrAccessModelTestExecutorBase *,146                    UncheckedStatusOrAccessModelTestAliasKind>> {147protected:148  void ExpectDiagnosticsFor(std::string SourceCode) {149    GetParam().first->ExpectDiagnosticsFor(SourceCode, GetParam().second);150  }151 152  void ExpectDiagnosticsForLambda(std::string SourceCode) {153    GetParam().first->ExpectDiagnosticsForLambda(SourceCode, GetParam().second);154  }155};156 157} // namespace clang::dataflow::statusor_model158 159#endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_160