aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests/StaticAnalyzer/CheckerRegistration.h
blob: c4c6e7a9a896d701383d6d632e635987c9fc2696 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"

namespace clang {
namespace ento {

class OnlyWarningsDiagConsumer : public PathDiagnosticConsumer {
  llvm::raw_ostream &Output;

public:
  OnlyWarningsDiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
                            FilesMade *filesMade) override {
    for (const auto *PD : Diags) {
      Output << PD->getCheckerName() << ": ";
      Output << PD->getShortDescription() << '\n';
    }
  }

  StringRef getName() const override { return "Test"; }
};

class PathDiagConsumer : public PathDiagnosticConsumer {
  llvm::raw_ostream &Output;

public:
  PathDiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
                            FilesMade *filesMade) override {
    for (const auto *PD : Diags) {
      Output << PD->getCheckerName() << ": ";

      for (PathDiagnosticPieceRef Piece :
           PD->path.flatten(/*ShouldFlattenMacros*/ true)) {
        if (Piece->getKind() != PathDiagnosticPiece::Event)
          continue;
        if (Piece->getString().empty())
          continue;
        // The last event is usually the same as the warning message, skip.
        if (Piece->getString() == PD->getShortDescription())
          continue;

        Output << Piece->getString() << " | ";
      }
      Output << PD->getShortDescription() << '\n';
    }
  }

  StringRef getName() const override { return "Test"; }
};

using AddCheckerFn = void(AnalysisASTConsumer &AnalysisConsumer,
                          AnalyzerOptions &AnOpts);

template <AddCheckerFn Fn1, AddCheckerFn Fn2, AddCheckerFn... Fns>
void addChecker(AnalysisASTConsumer &AnalysisConsumer,
                AnalyzerOptions &AnOpts) {
  Fn1(AnalysisConsumer, AnOpts);
  addChecker<Fn2, Fns...>(AnalysisConsumer, AnOpts);
}

template <AddCheckerFn Fn1>
void addChecker(AnalysisASTConsumer &AnalysisConsumer,
                AnalyzerOptions &AnOpts) {
  Fn1(AnalysisConsumer, AnOpts);
}

template <AddCheckerFn... Fns> class TestAction : public ASTFrontendAction {
  llvm::raw_ostream &DiagsOutput;
  bool OnlyEmitWarnings;

public:
  TestAction(llvm::raw_ostream &DiagsOutput, bool OnlyEmitWarnings)
      : DiagsOutput(DiagsOutput), OnlyEmitWarnings(OnlyEmitWarnings) {}

  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
                                                 StringRef File) override {
    std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer =
        CreateAnalysisConsumer(Compiler);
    if (OnlyEmitWarnings)
      AnalysisConsumer->AddDiagnosticConsumer(
          std::make_unique<OnlyWarningsDiagConsumer>(DiagsOutput));
    else
      AnalysisConsumer->AddDiagnosticConsumer(
          std::make_unique<PathDiagConsumer>(DiagsOutput));
    addChecker<Fns...>(*AnalysisConsumer, Compiler.getAnalyzerOpts());
    return std::move(AnalysisConsumer);
  }
};

inline SmallString<80> getCurrentTestNameAsFileName() {
  const ::testing::TestInfo *Info =
      ::testing::UnitTest::GetInstance()->current_test_info();

  SmallString<80> FileName;
  (Twine{Info->name()} + ".cc").toVector(FileName);
  return FileName;
}

template <AddCheckerFn... Fns>
bool runCheckerOnCode(const std::string &Code, std::string &Diags,
                      bool OnlyEmitWarnings = false) {
  const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();
  llvm::raw_string_ostream OS(Diags);
  return tooling::runToolOnCode(
      std::make_unique<TestAction<Fns...>>(OS, OnlyEmitWarnings), Code,
      FileName);
}

template <AddCheckerFn... Fns> bool runCheckerOnCode(const std::string &Code) {
  std::string Diags;
  return runCheckerOnCode<Fns...>(Code, Diags);
}

template <AddCheckerFn... Fns>
bool runCheckerOnCodeWithArgs(const std::string &Code,
                              const std::vector<std::string> &Args,
                              std::string &Diags,
                              bool OnlyEmitWarnings = false) {
  const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();
  llvm::raw_string_ostream OS(Diags);
  return tooling::runToolOnCodeWithArgs(
      std::make_unique<TestAction<Fns...>>(OS, OnlyEmitWarnings), Code, Args,
      FileName);
}

template <AddCheckerFn... Fns>
bool runCheckerOnCodeWithArgs(const std::string &Code,
                              const std::vector<std::string> &Args) {
  std::string Diags;
  return runCheckerOnCodeWithArgs<Fns...>(Code, Args, Diags);
}

} // namespace ento
} // namespace clang