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