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 | #include <fstream> |
16 | |
17 | using namespace clang; |
18 | using namespace ento; |
19 | |
20 | namespace { |
21 | |
22 | class DescriptiveNameChecker : public Checker<check::PreCall> { |
23 | public: |
24 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const { |
25 | if (!HandlerFn.matches(Call)) |
26 | return; |
27 | |
28 | const MemRegion *ArgReg = Call.getArgSVal(Index: 0).getAsRegion(); |
29 | assert(ArgReg && "expecting a location as the first argument" ); |
30 | |
31 | auto DescriptiveName = ArgReg->getDescriptiveName(/*UseQuotes=*/false); |
32 | if (ExplodedNode *Node = C.generateNonFatalErrorNode(State: C.getState())) { |
33 | auto Report = |
34 | std::make_unique<PathSensitiveBugReport>(args: Bug, args&: DescriptiveName, args&: Node); |
35 | C.emitReport(R: std::move(Report)); |
36 | } |
37 | } |
38 | |
39 | private: |
40 | const BugType Bug{this, "DescriptiveNameBug" }; |
41 | const CallDescription HandlerFn = {{"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)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 | } // namespace |
146 | |