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