152 lines · c
1//===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.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 9#include "clang/Analysis/PathDiagnostic.h"10#include "clang/Frontend/CompilerInstance.h"11#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"12#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"13#include "clang/StaticAnalyzer/Core/Checker.h"14#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"15#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"16#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"17#include "clang/Tooling/Tooling.h"18#include "gtest/gtest.h"19 20namespace clang {21namespace ento {22 23class OnlyWarningsDiagConsumer : public PathDiagnosticConsumer {24 llvm::raw_ostream &Output;25 26public:27 OnlyWarningsDiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}28 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,29 FilesMade *filesMade) override {30 for (const auto *PD : Diags) {31 Output << PD->getCheckerName() << ": ";32 Output << PD->getShortDescription() << '\n';33 }34 }35 36 StringRef getName() const override { return "Test"; }37};38 39class PathDiagConsumer : public PathDiagnosticConsumer {40 llvm::raw_ostream &Output;41 42public:43 PathDiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}44 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,45 FilesMade *filesMade) override {46 for (const auto *PD : Diags) {47 Output << PD->getCheckerName() << ": ";48 49 for (PathDiagnosticPieceRef Piece :50 PD->path.flatten(/*ShouldFlattenMacros*/ true)) {51 if (Piece->getKind() != PathDiagnosticPiece::Event)52 continue;53 if (Piece->getString().empty())54 continue;55 // The last event is usually the same as the warning message, skip.56 if (Piece->getString() == PD->getShortDescription())57 continue;58 59 Output << Piece->getString() << " | ";60 }61 Output << PD->getShortDescription() << '\n';62 }63 }64 65 StringRef getName() const override { return "Test"; }66};67 68using AddCheckerFn = void(AnalysisASTConsumer &AnalysisConsumer,69 AnalyzerOptions &AnOpts);70 71template <AddCheckerFn Fn1, AddCheckerFn Fn2, AddCheckerFn... Fns>72void addChecker(AnalysisASTConsumer &AnalysisConsumer,73 AnalyzerOptions &AnOpts) {74 Fn1(AnalysisConsumer, AnOpts);75 addChecker<Fn2, Fns...>(AnalysisConsumer, AnOpts);76}77 78template <AddCheckerFn Fn1>79void addChecker(AnalysisASTConsumer &AnalysisConsumer,80 AnalyzerOptions &AnOpts) {81 Fn1(AnalysisConsumer, AnOpts);82}83 84template <AddCheckerFn... Fns> class TestAction : public ASTFrontendAction {85 llvm::raw_ostream &DiagsOutput;86 bool OnlyEmitWarnings;87 88public:89 TestAction(llvm::raw_ostream &DiagsOutput, bool OnlyEmitWarnings)90 : DiagsOutput(DiagsOutput), OnlyEmitWarnings(OnlyEmitWarnings) {}91 92 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,93 StringRef File) override {94 std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer =95 CreateAnalysisConsumer(Compiler);96 if (OnlyEmitWarnings)97 AnalysisConsumer->AddDiagnosticConsumer(98 std::make_unique<OnlyWarningsDiagConsumer>(DiagsOutput));99 else100 AnalysisConsumer->AddDiagnosticConsumer(101 std::make_unique<PathDiagConsumer>(DiagsOutput));102 addChecker<Fns...>(*AnalysisConsumer, Compiler.getAnalyzerOpts());103 return std::move(AnalysisConsumer);104 }105};106 107inline SmallString<80> getCurrentTestNameAsFileName() {108 const ::testing::TestInfo *Info =109 ::testing::UnitTest::GetInstance()->current_test_info();110 111 SmallString<80> FileName;112 (Twine{Info->name()} + ".cc").toVector(FileName);113 return FileName;114}115 116template <AddCheckerFn... Fns>117bool runCheckerOnCode(const std::string &Code, std::string &Diags,118 bool OnlyEmitWarnings = false) {119 const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();120 llvm::raw_string_ostream OS(Diags);121 return tooling::runToolOnCode(122 std::make_unique<TestAction<Fns...>>(OS, OnlyEmitWarnings), Code,123 FileName);124}125 126template <AddCheckerFn... Fns> bool runCheckerOnCode(const std::string &Code) {127 std::string Diags;128 return runCheckerOnCode<Fns...>(Code, Diags);129}130 131template <AddCheckerFn... Fns>132bool runCheckerOnCodeWithArgs(const std::string &Code,133 const std::vector<std::string> &Args,134 std::string &Diags,135 bool OnlyEmitWarnings = false) {136 const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();137 llvm::raw_string_ostream OS(Diags);138 return tooling::runToolOnCodeWithArgs(139 std::make_unique<TestAction<Fns...>>(OS, OnlyEmitWarnings), Code, Args,140 FileName);141}142 143template <AddCheckerFn... Fns>144bool runCheckerOnCodeWithArgs(const std::string &Code,145 const std::vector<std::string> &Args) {146 std::string Diags;147 return runCheckerOnCodeWithArgs<Fns...>(Code, Args, Diags);148}149 150} // namespace ento151} // namespace clang152