xref: /llvm-project/clang/test/Analysis/const-method-call.cpp (revision 12f78e740c5419f7d1fbcf8f2106e7a40cd1d6f7)
1 // RUN: %clang_analyze_cc1 -Wno-error=return-type -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s
2 
3 void clang_analyzer_eval(bool);
4 
5 struct A {
6   int x;
7   void foo() const;
8   void bar();
9 
10   void testImplicitThisSyntax() {
11     x = 3;
12     foo();
13     clang_analyzer_eval(x == 3); // expected-warning{{TRUE}}
14     bar();
15     clang_analyzer_eval(x == 3); // expected-warning{{UNKNOWN}}
16   }
17 };
18 
19 struct B {
20   mutable int mut;
21   void foo() const;
22 };
23 
24 struct C {
25   int *p;
26   void foo() const;
27 };
28 
29 struct MutBase {
30   mutable int b_mut;
31 };
32 
33 struct MutDerived : MutBase {
34   void foo() const;
35 };
36 
37 struct PBase {
38   int *p;
39 };
40 
41 struct PDerived : PBase {
42   void foo() const;
43 };
44 
45 struct Inner {
46   int x;
47   int *p;
48   void bar() const;
49 };
50 
51 struct Outer {
52   int x;
53   Inner in;
54   void foo() const;
55 };
56 
57 void checkThatConstMethodWithoutDefinitionDoesNotInvalidateObject() {
58   A t;
59   t.x = 3;
60   t.foo();
61   clang_analyzer_eval(t.x == 3); // expected-warning{{TRUE}}
62   // Test non-const does invalidate
63   t.bar();
64   clang_analyzer_eval(t.x); // expected-warning{{UNKNOWN}}
65 }
66 
67 void checkThatConstMethodDoesInvalidateMutableFields() {
68   B t;
69   t.mut = 4;
70   t.foo();
71   clang_analyzer_eval(t.mut); // expected-warning{{UNKNOWN}}
72 }
73 
74 void checkThatConstMethodDoesInvalidatePointedAtMemory() {
75   int x = 1;
76   C t;
77   t.p = &x;
78   t.foo();
79   clang_analyzer_eval(x); // expected-warning{{UNKNOWN}}
80   clang_analyzer_eval(t.p == &x); // expected-warning{{TRUE}}
81 }
82 
83 void checkThatConstMethodDoesInvalidateInheritedMutableFields() {
84   MutDerived t;
85   t.b_mut = 4;
86   t.foo();
87   clang_analyzer_eval(t.b_mut); // expected-warning{{UNKNOWN}}
88 }
89 
90 void checkThatConstMethodDoesInvalidateInheritedPointedAtMemory() {
91   int x = 1;
92   PDerived t;
93   t.p = &x;
94   t.foo();
95   clang_analyzer_eval(x); // expected-warning{{UNKNOWN}}
96   clang_analyzer_eval(t.p == &x); // expected-warning{{TRUE}}
97 }
98 
99 void checkThatConstMethodDoesInvalidateContainedPointedAtMemory() {
100   int x = 1;
101   Outer t;
102   t.x = 2;
103   t.in.p = &x;
104   t.foo();
105   clang_analyzer_eval(x); // expected-warning{{UNKNOWN}}
106   clang_analyzer_eval(t.x == 2); // expected-warning{{TRUE}}
107   clang_analyzer_eval(t.in.p == &x); // expected-warning{{TRUE}}
108 }
109 
110 void checkThatContainedConstMethodDoesNotInvalidateObjects() {
111   Outer t;
112   t.x = 1;
113   t.in.x = 2;
114   t.in.bar();
115   clang_analyzer_eval(t.x == 1); // expected-warning{{TRUE}}
116   clang_analyzer_eval(t.in.x == 2); // expected-warning{{TRUE}}
117 }
118 
119 void checkPointerTypedThisExpression(A *a) {
120   a->x = 3;
121   a->foo();
122   clang_analyzer_eval(a->x == 3); // expected-warning{{TRUE}}
123   a->bar();
124   clang_analyzer_eval(a->x == 3); // expected-warning{{UNKNOWN}}
125 }
126 
127 void checkReferenceTypedThisExpression(A &a) {
128   a.x = 3;
129   a.foo();
130   clang_analyzer_eval(a.x == 3); // expected-warning{{TRUE}}
131   a.bar();
132   clang_analyzer_eval(a.x == 3); // expected-warning{{UNKNOWN}}
133 }
134 
135 // --- Versions of the above tests where the const method is inherited --- //
136 
137 struct B1 {
138   void foo() const;
139 };
140 
141 struct D1 : public B1 {
142   int x;
143 };
144 
145 struct D2 : public B1 {
146   mutable int mut;
147 };
148 
149 struct D3 : public B1 {
150   int *p;
151 };
152 
153 struct DInner : public B1 {
154   int x;
155   int *p;
156 };
157 
158 struct DOuter : public B1 {
159   int x;
160   DInner in;
161 };
162 
163 void checkThatInheritedConstMethodDoesNotInvalidateObject() {
164   D1 t;
165   t.x = 1;
166   t.foo();
167   clang_analyzer_eval(t.x == 1); // expected-warning{{TRUE}}
168 }
169 
170 void checkThatInheritedConstMethodDoesInvalidateMutableFields() {
171   D2 t;
172   t.mut = 1;
173   t.foo();
174   clang_analyzer_eval(t.mut); // expected-warning{{UNKNOWN}}
175 }
176 
177 void checkThatInheritedConstMethodDoesInvalidatePointedAtMemory() {
178   int x = 1;
179   D3 t;
180   t.p = &x;
181   t.foo();
182   clang_analyzer_eval(x); // expected-warning{{UNKNOWN}}
183   clang_analyzer_eval(t.p == &x); // expected-warning{{TRUE}}
184 }
185 
186 void checkThatInheritedConstMethodDoesInvalidateContainedPointedAtMemory() {
187   int x = 1;
188   DOuter t;
189   t.x = 2;
190   t.in.x = 3;
191   t.in.p = &x;
192   t.foo();
193   clang_analyzer_eval(x); // expected-warning{{UNKNOWN}}
194   clang_analyzer_eval(t.x == 2); // expected-warning{{TRUE}}
195   clang_analyzer_eval(t.in.x == 3); // expected-warning{{TRUE}}
196   clang_analyzer_eval(t.in.p == &x); // expected-warning{{TRUE}}
197 }
198 
199 void checkThatInheritedContainedConstMethodDoesNotInvalidateObjects() {
200   DOuter t;
201   t.x = 1;
202   t.in.x = 2;
203   t.in.foo();
204   clang_analyzer_eval(t.x == 1); // expected-warning{{TRUE}}
205   clang_analyzer_eval(t.in.x == 2); // expected-warning{{TRUE}}
206 }
207 
208 // --- PR21606 --- //
209 
210 struct s1 {
211     void g(const int *i) const;
212 };
213 
214 struct s2 {
215     void f(int *i) {
216         m_i = i;
217         m_s.g(m_i);
218         if (m_i)
219             *i = 42; // no-warning
220     }
221 
222     int *m_i;
223     s1 m_s;
224 };
225 
226 void PR21606()
227 {
228     s2().f(0);
229 }
230 
231 // --- PR25392 --- //
232 
233 struct HasConstMemberFunction {
234 public:
235   void constMemberFunction() const;
236 };
237 
238 HasConstMemberFunction hasNoReturn() { } // expected-warning {{non-void function does not return a value}}
239 
240 void testUnknownWithConstMemberFunction() {
241   hasNoReturn().constMemberFunction();
242 }
243 
244 void testNonRegionLocWithConstMemberFunction() {
245   (*((HasConstMemberFunction *)(&&label))).constMemberFunction();
246 
247   label: return;
248 }
249 
250 // FIXME
251 // When there is a circular reference to an object and a const method is called
252 // the object is not invalidated because TK_PreserveContents has already been
253 // set.
254 struct Outer2;
255 
256 struct InnerWithRef {
257   Outer2 *ref;
258 };
259 
260 struct Outer2 {
261   int x;
262   InnerWithRef in;
263   void foo() const;
264 };
265 
266 void checkThatConstMethodCallDoesInvalidateObjectForCircularReferences() {
267   Outer2 t;
268   t.x = 1;
269   t.in.ref = &t;
270   t.foo();
271   // FIXME: Should be UNKNOWN.
272   clang_analyzer_eval(t.x); // expected-warning{{TRUE}}
273 }
274 
275 namespace gh77378 {
276 template <typename Signature> class callable;
277 
278 template <typename R> class callable<R()> {
279   struct CallableType {
280     bool operator()();
281   };
282   using MethodType = R (CallableType::*)();
283   CallableType *object_{nullptr};
284   MethodType method_;
285 
286 public:
287   callable() = default;
288 
289   template <typename T>
290   constexpr callable(const T &obj)
291       : object_{reinterpret_cast<CallableType *>(&const_cast<T &>(obj))},
292         method_{reinterpret_cast<MethodType>(
293             static_cast<bool (T::*)() const>(&T::operator()))} {}
294 
295   constexpr bool const_method() const {
296     return (object_->*(method_))();
297   }
298 
299   callable call() const & {
300     static const auto L = [this]() {
301       while (true) {
302         // This should not crash when conservative eval calling the member function
303         // when it unwinds the call stack due to exhausting the budget or reaching
304         // the inlining limit.
305         if (this->const_method()) {
306           break;
307         }
308       }
309       return true;
310     };
311     return L;
312   }
313 };
314 
315 void entry() {
316   callable<bool()>{}.call().const_method();
317   // expected-warning@-1 {{Address of stack memory associated with temporary object of type 'callable<bool ()>' is still referred to by the static variable 'L' upon returning to the caller.  This will be a dangling reference}}
318 }
319 } // namespace gh77378
320