1 //===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
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 
20 namespace clang {
21 namespace ento {
22 namespace {
23 
24 template <typename CheckerT>
25 class TestAction : public ASTFrontendAction {
26   class DiagConsumer : public PathDiagnosticConsumer {
27     llvm::raw_ostream &Output;
28 
29   public:
30     DiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
31     void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
32                               FilesMade *filesMade) override {
33       for (const auto *PD : Diags)
34         Output << PD->getCheckName() << ":" << PD->getShortDescription();
35     }
36 
37     StringRef getName() const override { return "Test"; }
38   };
39 
40   llvm::raw_ostream &DiagsOutput;
41 
42 public:
43   TestAction(llvm::raw_ostream &DiagsOutput) : DiagsOutput(DiagsOutput) {}
44 
45   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
46                                                  StringRef File) override {
47     std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer =
48         CreateAnalysisConsumer(Compiler);
49     AnalysisConsumer->AddDiagnosticConsumer(new DiagConsumer(DiagsOutput));
50     Compiler.getAnalyzerOpts()->CheckersControlList = {
51         {"custom.CustomChecker", true}};
52     AnalysisConsumer->AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
53       Registry.addChecker<CheckerT>("custom.CustomChecker", "Description", "");
54     });
55     return std::move(AnalysisConsumer);
56   }
57 };
58 
59 template <typename CheckerT>
60 bool runCheckerOnCode(const std::string &Code, std::string &Diags) {
61   llvm::raw_string_ostream OS(Diags);
62   return tooling::runToolOnCode(new TestAction<CheckerT>(OS), Code);
63 }
64 template <typename CheckerT>
65 bool runCheckerOnCode(const std::string &Code) {
66   std::string Diags;
67   return runCheckerOnCode<CheckerT>(Code, Diags);
68 }
69 
70 
71 class CustomChecker : public Checker<check::ASTCodeBody> {
72 public:
73   void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
74                         BugReporter &BR) const {
75     BR.EmitBasicReport(D, this, "Custom diagnostic", categories::LogicError,
76                        "Custom diagnostic description",
77                        PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
78   }
79 };
80 
81 TEST(RegisterCustomCheckers, RegisterChecker) {
82   std::string Diags;
83   EXPECT_TRUE(runCheckerOnCode<CustomChecker>("void f() {;}", Diags));
84   EXPECT_EQ(Diags, "custom.CustomChecker:Custom diagnostic description");
85 }
86 
87 class LocIncDecChecker : public Checker<check::Location> {
88 public:
89   void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
90                      CheckerContext &C) const {
91     auto UnaryOp = dyn_cast<UnaryOperator>(S);
92     if (UnaryOp && !IsLoad)
93       EXPECT_FALSE(UnaryOp->isIncrementOp());
94   }
95 };
96 
97 TEST(RegisterCustomCheckers, CheckLocationIncDec) {
98   EXPECT_TRUE(
99       runCheckerOnCode<LocIncDecChecker>("void f() { int *p; (*p)++; }"));
100 }
101 
102 }
103 }
104 }
105