1 //===- unittests/StaticAnalyzer/SValSimplifyerTest.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 "CheckerRegistration.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/CallDescription.h" 14 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 15 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" 16 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" 17 #include "llvm/ADT/Twine.h" 18 #include "llvm/Support/raw_ostream.h" 19 #include "gtest/gtest.h" 20 21 using namespace clang; 22 using namespace ento; 23 24 static std::string toString(SVal V) { 25 std::string Result; 26 llvm::raw_string_ostream Stream(Result); 27 V.dumpToStream(Stream); 28 return Result; 29 } 30 31 static void replace(std::string &Content, StringRef Substr, 32 StringRef Replacement) { 33 std::size_t Pos = 0; 34 while ((Pos = Content.find(Substr, Pos)) != std::string::npos) { 35 Content.replace(Pos, Substr.size(), Replacement); 36 Pos += Replacement.size(); 37 } 38 } 39 40 namespace { 41 42 class SimplifyChecker : public Checker<check::PreCall> { 43 const BugType Bug{this, "SimplifyChecker"}; 44 const CallDescription SimplifyCall{CDM::SimpleFunc, {"simplify"}, 1}; 45 46 void report(CheckerContext &C, const Expr *E, StringRef Description) const { 47 PathDiagnosticLocation Loc(E->getExprLoc(), C.getSourceManager()); 48 auto Report = std::make_unique<BasicBugReport>(Bug, Description, Loc); 49 C.emitReport(std::move(Report)); 50 } 51 52 public: 53 void checkPreCall(const CallEvent &Call, CheckerContext &C) const { 54 if (!SimplifyCall.matches(Call)) 55 return; 56 const Expr *Arg = Call.getArgExpr(0); 57 SVal Val = C.getSVal(Arg); 58 SVal SimplifiedVal = C.getSValBuilder().simplifySVal(C.getState(), Val); 59 std::string Subject = toString(Val); 60 std::string Simplified = toString(SimplifiedVal); 61 std::string Message = (llvm::Twine{Subject} + " -> " + Simplified).str(); 62 report(C, Arg, Message); 63 } 64 }; 65 } // namespace 66 67 static void addSimplifyChecker(AnalysisASTConsumer &AnalysisConsumer, 68 AnalyzerOptions &AnOpts) { 69 AnOpts.CheckersAndPackages = {{"SimplifyChecker", true}}; 70 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { 71 Registry.addChecker<SimplifyChecker>("SimplifyChecker", "EmptyDescription", 72 "EmptyDocsUri"); 73 }); 74 } 75 76 static void runThisCheckerOnCode(const std::string &Code, std::string &Diags) { 77 ASSERT_TRUE(runCheckerOnCode<addSimplifyChecker>(Code, Diags, 78 /*OnlyEmitWarnings=*/true)); 79 ASSERT_FALSE(Diags.empty()); 80 ASSERT_EQ(Diags.back(), '\n'); 81 Diags.pop_back(); 82 } 83 84 namespace { 85 86 TEST(SValSimplifyerTest, LHSConstrainedNullPtrDiff) { 87 constexpr auto Code = R"cpp( 88 template <class T> void simplify(T); 89 void LHSConstrainedNullPtrDiff(char *p, char *q) { 90 int diff = p - q; 91 if (!p) 92 simplify(diff); 93 })cpp"; 94 95 std::string Diags; 96 runThisCheckerOnCode(Code, Diags); 97 replace(Diags, "(reg_$0<char * p>)", "reg_p"); 98 replace(Diags, "(reg_$1<char * q>)", "reg_q"); 99 // This should not be simplified to "Unknown". 100 EXPECT_EQ(Diags, "SimplifyChecker: reg_p - reg_q -> 0U - reg_q"); 101 } 102 103 } // namespace 104