| 1 | //===- MemRegionDescriptiveNameTest.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/Checker.h" |
| 11 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
| 12 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| 13 | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
| 14 | #include "gtest/gtest.h" |
| 15 | |
| 16 | using namespace clang; |
| 17 | using namespace ento; |
| 18 | |
| 19 | namespace { |
| 20 | |
| 21 | class DescriptiveNameChecker : public Checker<check::PreCall> { |
| 22 | public: |
| 23 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const { |
| 24 | if (!HandlerFn.matches(Call)) |
| 25 | return; |
| 26 | |
| 27 | const MemRegion *ArgReg = Call.getArgSVal(Index: 0).getAsRegion(); |
| 28 | assert(ArgReg && "expecting a location as the first argument" ); |
| 29 | |
| 30 | auto DescriptiveName = ArgReg->getDescriptiveName(/*UseQuotes=*/false); |
| 31 | if (ExplodedNode *Node = C.generateNonFatalErrorNode(State: C.getState())) { |
| 32 | auto Report = |
| 33 | std::make_unique<PathSensitiveBugReport>(args: Bug, args&: DescriptiveName, args&: Node); |
| 34 | C.emitReport(R: std::move(Report)); |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | private: |
| 39 | const BugType Bug{this, "DescriptiveNameBug" }; |
| 40 | const CallDescription HandlerFn = { |
| 41 | CDM::SimpleFunc, {"reportDescriptiveName" }, 1}; |
| 42 | }; |
| 43 | |
| 44 | void addDescriptiveNameChecker(AnalysisASTConsumer &AnalysisConsumer, |
| 45 | AnalyzerOptions &AnOpts) { |
| 46 | AnOpts.CheckersAndPackages = {{"DescriptiveNameChecker" , true}}; |
| 47 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) { |
| 48 | Registry.addChecker<DescriptiveNameChecker>(FullName: "DescriptiveNameChecker" , |
| 49 | Desc: "Desc" , DocsUri: "DocsURI" ); |
| 50 | }); |
| 51 | } |
| 52 | |
| 53 | bool runChecker(StringRef Code, std::string &Output) { |
| 54 | return runCheckerOnCode<addDescriptiveNameChecker>(Code: Code.str(), Diags&: Output, |
| 55 | /*OnlyEmitWarnings=*/true); |
| 56 | } |
| 57 | |
| 58 | TEST(MemRegionDescriptiveNameTest, ConcreteIntElementRegionIndex) { |
| 59 | StringRef Code = R"cpp( |
| 60 | void reportDescriptiveName(int *p); |
| 61 | const unsigned int index = 1; |
| 62 | extern int array[3]; |
| 63 | void top() { |
| 64 | reportDescriptiveName(&array[index]); |
| 65 | })cpp" ; |
| 66 | |
| 67 | std::string Output; |
| 68 | ASSERT_TRUE(runChecker(Code, Output)); |
| 69 | EXPECT_EQ(Output, "DescriptiveNameChecker: array[1]\n" ); |
| 70 | } |
| 71 | |
| 72 | TEST(MemRegionDescriptiveNameTest, SymbolicElementRegionIndex) { |
| 73 | StringRef Code = R"cpp( |
| 74 | void reportDescriptiveName(int *p); |
| 75 | extern unsigned int index; |
| 76 | extern int array[3]; |
| 77 | void top() { |
| 78 | reportDescriptiveName(&array[index]); |
| 79 | })cpp" ; |
| 80 | |
| 81 | std::string Output; |
| 82 | ASSERT_TRUE(runChecker(Code, Output)); |
| 83 | EXPECT_EQ(Output, "DescriptiveNameChecker: array[index]\n" ); |
| 84 | } |
| 85 | |
| 86 | TEST(MemRegionDescriptiveNameTest, SymbolicElementRegionIndexSymbolValFails) { |
| 87 | StringRef Code = R"cpp( |
| 88 | void reportDescriptiveName(int *p); |
| 89 | extern int* ptr; |
| 90 | extern int array[3]; |
| 91 | void top() { |
| 92 | reportDescriptiveName(&array[(long long)ptr]); |
| 93 | })cpp" ; |
| 94 | |
| 95 | std::string Output; |
| 96 | ASSERT_TRUE(runChecker(Code, Output)); |
| 97 | EXPECT_EQ(Output, "DescriptiveNameChecker: \n" ); |
| 98 | } |
| 99 | |
| 100 | TEST(MemRegionDescriptiveNameTest, SymbolicElementRegionIndexOrigRegionFails) { |
| 101 | StringRef Code = R"cpp( |
| 102 | void reportDescriptiveName(int *p); |
| 103 | extern int getInt(void); |
| 104 | extern int array[3]; |
| 105 | void top() { |
| 106 | reportDescriptiveName(&array[getInt()]); |
| 107 | })cpp" ; |
| 108 | |
| 109 | std::string Output; |
| 110 | ASSERT_TRUE(runChecker(Code, Output)); |
| 111 | EXPECT_EQ(Output, "DescriptiveNameChecker: \n" ); |
| 112 | } |
| 113 | |
| 114 | TEST(MemRegionDescriptiveNameTest, SymbolicElementRegionIndexDescrNameFails) { |
| 115 | StringRef Code = R"cpp( |
| 116 | void reportDescriptiveName(int *p); |
| 117 | extern int *ptr; |
| 118 | extern int array[3]; |
| 119 | void top() { |
| 120 | reportDescriptiveName(&array[*ptr]); |
| 121 | })cpp" ; |
| 122 | |
| 123 | std::string Output; |
| 124 | ASSERT_TRUE(runChecker(Code, Output)); |
| 125 | EXPECT_EQ(Output, "DescriptiveNameChecker: \n" ); |
| 126 | } |
| 127 | |
| 128 | TEST(MemRegionDescriptiveNameTest, |
| 129 | SymbolicElementRegionIndexIncorrectSymbolName) { |
| 130 | StringRef Code = R"cpp( |
| 131 | void reportDescriptiveName(int *p); |
| 132 | extern int x, y; |
| 133 | extern int array[3]; |
| 134 | void top() { |
| 135 | y = x; |
| 136 | reportDescriptiveName(&array[y]); |
| 137 | })cpp" ; |
| 138 | |
| 139 | std::string Output; |
| 140 | ASSERT_TRUE(runChecker(Code, Output)); |
| 141 | // FIXME: Should return array[y], but returns array[x] (OriginRegion). |
| 142 | EXPECT_EQ(Output, "DescriptiveNameChecker: array[x]\n" ); |
| 143 | } |
| 144 | |
| 145 | TEST(MemRegionDescriptiveNameTest, FieldRegWithSuperElementReg) { |
| 146 | StringRef Code = R"cpp( |
| 147 | void reportDescriptiveName(int *p); |
| 148 | struct val_struct { int val; }; |
| 149 | extern struct val_struct val_struct_array[3]; |
| 150 | void top() { |
| 151 | reportDescriptiveName(&val_struct_array[0].val); |
| 152 | })cpp" ; |
| 153 | |
| 154 | std::string Output; |
| 155 | ASSERT_TRUE(runChecker(Code, Output)); |
| 156 | EXPECT_EQ(Output, "DescriptiveNameChecker: val_struct_array[0].val\n" ); |
| 157 | } |
| 158 | |
| 159 | TEST(MemRegionDescriptiveNameTest, FieldRegWithSuperMultidimElementReg) { |
| 160 | StringRef Code = R"cpp( |
| 161 | void reportDescriptiveName(int *p); |
| 162 | struct val_struct { int val; }; |
| 163 | extern struct val_struct val_struct_array[3][4]; |
| 164 | void top() { |
| 165 | reportDescriptiveName(&val_struct_array[1][2].val); |
| 166 | })cpp" ; |
| 167 | |
| 168 | std::string Output; |
| 169 | ASSERT_TRUE(runChecker(Code, Output)); |
| 170 | EXPECT_EQ(Output, "DescriptiveNameChecker: val_struct_array[1][2].val\n" ); |
| 171 | } |
| 172 | |
| 173 | } // namespace |
| 174 | |