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