diff options
Diffstat (limited to 'clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h')
-rw-r--r-- | clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h new file mode 100644 index 0000000..92dcd93 --- /dev/null +++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h @@ -0,0 +1,159 @@ +//===- UncheckedStatusOrAccessModelTestFixture.h --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_ +#define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_ + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> +#include <vector> + +#include "TestingSupport.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" +#include "clang/Analysis/FlowSensitive/MatchSwitch.h" +#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang::dataflow::statusor_model { + +enum class UncheckedStatusOrAccessModelTestAliasKind { + kUnaliased = 0, // no alias + kPartiallyAliased = 1, // template<typename T> using Alias = absl::StatusOr; + kFullyAliased = 2, // using Alias = absl::StatusOr<int>; +}; + +// Base class for the test executors. This is needed to abstract away the +// template parameter from the UncheckedStatusOrAccessModelTestExecutor. This +// allows us to use UncheckedStatusOrAccessModelTestExecutorBase* in the +// UncheckedStatusOrAccessModelTest. +class UncheckedStatusOrAccessModelTestExecutorBase { +public: + virtual void + ExpectDiagnosticsFor(std::string SourceCode, + UncheckedStatusOrAccessModelTestAliasKind) const = 0; + virtual void ExpectDiagnosticsForLambda( + std::string SourceCode, + UncheckedStatusOrAccessModelTestAliasKind) const = 0; + virtual ~UncheckedStatusOrAccessModelTestExecutorBase() = default; +}; + +// Returns these macros according to the alias kind: +// - STATUS +// - STATUSOR_INT +// - STATUSOR_BOOL +// - STATUSOR_VOIDPTR +// Tests should use these macros instead of e.g. absl::StatusOr<int> to ensure +// the model is insensitive to whether the StatusOr<> is aliased or not. +std::string GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind); + +std::vector<std::pair<std::string, std::string>> +GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind); + +// This allows us to run the same test suite for multiple models. This allows +// vendors to model internal APIs in an extension of the base model, and make +// sure that these tests still pass. +template <typename Model> +class UncheckedStatusOrAccessModelTestExecutor + : public UncheckedStatusOrAccessModelTestExecutorBase { +public: + void ExpectDiagnosticsFor( + std::string SourceCode, + UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + ExpectDiagnosticsFor(SourceCode, hasName("target"), AliasKind); + } + + void ExpectDiagnosticsForLambda( + std::string SourceCode, + UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + ExpectDiagnosticsFor(SourceCode, + allOf(hasOverloadedOperatorName("()"), + hasDeclContext(cxxRecordDecl(isLambda()))), + AliasKind); + } + + template <typename FuncDeclMatcher> + void ExpectDiagnosticsFor( + std::string SourceCode, FuncDeclMatcher FuncMatcher, + UncheckedStatusOrAccessModelTestAliasKind AliasKind) const { + std::vector<std::pair<std::string, std::string>> Headers = + GetHeaders(AliasKind); + + UncheckedStatusOrAccessModelOptions Options{}; + std::vector<SourceLocation> Diagnostics; + llvm::Error Error = test::checkDataflow<Model>( + test::AnalysisInputs<Model>( + SourceCode, std::move(FuncMatcher), + [](ASTContext &Ctx, Environment &Env) { return Model(Ctx, Env); }) + .withPostVisitCFG( + [&Diagnostics, + Diagnoser = UncheckedStatusOrAccessDiagnoser(Options)]( + ASTContext &Ctx, const CFGElement &Elt, + const TransferStateForDiagnostics< + UncheckedStatusOrAccessModel::Lattice> &State) mutable { + auto EltDiagnostics = Diagnoser(Elt, Ctx, State); + llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); + }) + .withASTBuildArgs( + {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}) + .withASTBuildVirtualMappedFiles( + tooling::FileContentMappings(Headers.begin(), Headers.end())), + /*VerifyResults=*/[&Diagnostics, SourceCode]( + const llvm::DenseMap<unsigned, std::string> + &Annotations, + const test::AnalysisOutputs &AO) { + llvm::DenseSet<unsigned> AnnotationLines; + for (const auto &[Line, _] : Annotations) + AnnotationLines.insert(Line); + auto &SrcMgr = AO.ASTCtx.getSourceManager(); + llvm::DenseSet<unsigned> DiagnosticLines; + for (SourceLocation &Loc : Diagnostics) + DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc)); + + EXPECT_THAT(DiagnosticLines, testing::ContainerEq(AnnotationLines)) + << "\nFailing code:\n" + << SourceCode; + }); + if (Error) + FAIL() << llvm::toString(std::move(Error)); + } + + ~UncheckedStatusOrAccessModelTestExecutor() override = default; +}; + +class UncheckedStatusOrAccessModelTest + : public ::testing::TestWithParam< + std::pair<UncheckedStatusOrAccessModelTestExecutorBase *, + UncheckedStatusOrAccessModelTestAliasKind>> { +protected: + void ExpectDiagnosticsFor(std::string SourceCode) { + GetParam().first->ExpectDiagnosticsFor(SourceCode, GetParam().second); + } + + void ExpectDiagnosticsForLambda(std::string SourceCode) { + GetParam().first->ExpectDiagnosticsForLambda(SourceCode, GetParam().second); + } +}; + +} // namespace clang::dataflow::statusor_model + +#endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_ |