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