xref: /llvm-project/clang/unittests/StaticAnalyzer/SValSimplifyerTest.cpp (revision e67e03a22c27b26125247efeae1b2d36edeb3617)
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