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