xref: /llvm-project/clang/test/Analysis/cast-value-notes.cpp (revision 15f3cd6bfc670ba6106184a903eb04be059e5977)
1 // RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \
2 // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
3 // RUN: -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s -check-prefix=X86-CHECK
4 //
5 // RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \
6 // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
7 // RUN:  -analyzer-config core.NullDereference:SuppressAddressSpaces=false\
8 // RUN:  -analyzer-output=text -verify -DX86 -DNOT_SUPPRESSED %s 2>&1 | FileCheck %s -check-prefix=X86-CHECK
9 //
10 // RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \
11 // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
12 // RUN:  -analyzer-config core.NullDereference:SuppressAddressSpaces=true\
13 // RUN:  -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s -check-prefix=X86-CHECK
14 //
15 // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-unknown-unknown \
16 // RUN:  -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
17 // RUN:  -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s --check-prefix=X86-CHECK
18 //
19 // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-unknown-unknown \
20 // RUN:  -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
21 // RUN:  -analyzer-config core.NullDereference:SuppressAddressSpaces=true\
22 // RUN:  -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s --check-prefix=X86-CHECK-SUPPRESSED
23 //
24 // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-unknown-unknown \
25 // RUN:  -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
26 // RUN:  -analyzer-config core.NullDereference:SuppressAddressSpaces=false\
27 // RUN:  -analyzer-output=text -verify -DX86 -DNOT_SUPPRESSED %s 2>&1 | FileCheck %s --check-prefix=X86-CHECK
28 //
29 // RUN: %clang_analyze_cc1 -std=c++14 -triple mips-unknown-unknown \
30 // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
31 // RUN: -analyzer-output=text -verify -DMIPS %s 2>&1
32 //
33 // RUN: %clang_analyze_cc1 -std=c++14 -triple mips-unknown-unknown \
34 // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
35 // RUN: -analyzer-config core.NullDereference:SuppressAddressSpaces=false\
36 // RUN: -analyzer-output=text -verify -DMIPS %s 2>&1
37 //
38 // RUN: %clang_analyze_cc1 -std=c++14 -triple mips-unknown-unknown \
39 // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
40 // RUN: -analyzer-config core.NullDereference:SuppressAddressSpaces=true\
41 // RUN: -analyzer-output=text -verify -DMIPS_SUPPRESSED %s
42 
43 #include "Inputs/llvm.h"
44 
45 // The amggcn triple case uses an intentionally different address space.
46 // The core.NullDereference checker intentionally ignores checks
47 // that use address spaces, so the case is differentiated here.
48 //
49 // From https://llvm.org/docs/AMDGPUUsage.html#address-spaces,
50 // select address space 3 (local), since the pointer size is
51 // different than Generic.
52 #define DEVICE __attribute__((address_space(3)))
53 
54 namespace clang {
55 struct Shape {
56   template <typename T>
57   const T *castAs() const;
58 
59   template <typename T>
60   const T *getAs() const;
61 };
62 class Triangle : public Shape {};
63 class Rectangle : public Shape {};
64 class Hexagon : public Shape {};
65 class Circle : public Shape {};
66 } // namespace clang
67 
68 using namespace llvm;
69 using namespace clang;
70 
71 void clang_analyzer_printState();
72 
73 #if defined(X86)
evalReferences(const Shape & S)74 void evalReferences(const Shape &S) {
75   const auto &C = dyn_cast<Circle>(S);
76   // expected-note@-1 {{Assuming 'S' is not a 'const class clang::Circle &'}}
77   // expected-note@-2 {{Dereference of null pointer}}
78   // expected-warning@-3 {{Dereference of null pointer}}
79   clang_analyzer_printState();
80   // XX86-CHECK:      "dynamic_types": [
81   // XX86-CHECK-NEXT:   { "region": "SymRegion{reg_$0<const struct clang::Shape & S>}", "dyn_type": "const class clang::Circle &", "sub_classable": true }
82   (void)C;
83 }
84 #if defined(SUPPRESSED)
evalReferences_addrspace(const Shape & S)85 void evalReferences_addrspace(const Shape &S) {
86   const auto &C = dyn_cast<DEVICE Circle>(S);
87   clang_analyzer_printState();
88   // X86-CHECK-SUPPRESSED: "dynamic_types": [
89   // X86-CHECK-SUPPRESSED-NEXT: { "region": "SymRegion{reg_$0<const Shape & S>}", "dyn_type": "const __attribute__((address_space(3))) class clang::Circle &", "sub_classable": true }
90   (void)C;
91 }
92 #endif
93 #if defined(NOT_SUPPRESSED)
evalReferences_addrspace(const Shape & S)94 void evalReferences_addrspace(const Shape &S) {
95   const auto &C = dyn_cast<DEVICE Circle>(S);
96   // expected-note@-1 {{Assuming 'S' is not a 'const __attribute__((address_space(3))) class clang::Circle &'}}
97   // expected-note@-2 {{Dereference of null pointer}}
98   // expected-warning@-3 {{Dereference of null pointer}}
99   clang_analyzer_printState();
100   // X86-CHECK: "dynamic_types": [
101   // X86-CHECK-NEXT: { "region": "SymRegion{reg_$0<const Shape & S>}", "dyn_type": "const __attribute__((address_space(3))) class clang::Circle &", "sub_classable": true }
102   (void)C;
103 }
104 #endif
105 #elif defined(MIPS)
evalReferences(const Shape & S)106 void evalReferences(const Shape &S) {
107   const auto &C = dyn_cast<Circle>(S);
108   // expected-note@-1 {{Assuming 'S' is not a 'const class clang::Circle &'}}
109   // expected-note@-2 {{Dereference of null pointer}}
110   // expected-warning@-3 {{Dereference of null pointer}}
111 }
112 #if defined(MIPS_SUPPRESSED)
evalReferences_addrspace(const Shape & S)113 void evalReferences_addrspace(const Shape &S) {
114   const auto &C = dyn_cast<DEVICE Circle>(S);
115   (void)C;
116 }
117 #endif
118 #endif
119 
evalNonNullParamNonNullReturnReference(const Shape & S)120 void evalNonNullParamNonNullReturnReference(const Shape &S) {
121   const auto *C = dyn_cast_or_null<Circle>(S);
122   // expected-note@-1 {{'C' initialized here}}
123 
124   if (!dyn_cast_or_null<Circle>(C)) {
125     // expected-note@-1 {{Assuming 'C' is a 'const class clang::Circle *'}}
126     // expected-note@-2 {{Taking false branch}}
127     return;
128   }
129 
130   if (dyn_cast_or_null<Triangle>(C)) {
131     // expected-note@-1 {{Assuming 'C' is not a 'const class clang::Triangle *'}}
132     // expected-note@-2 {{Taking false branch}}
133     return;
134   }
135 
136   if (dyn_cast_or_null<Rectangle>(C)) {
137     // expected-note@-1 {{Assuming 'C' is not a 'const class clang::Rectangle *'}}
138     // expected-note@-2 {{Taking false branch}}
139     return;
140   }
141 
142   if (dyn_cast_or_null<Hexagon>(C)) {
143     // expected-note@-1 {{Assuming 'C' is not a 'const class clang::Hexagon *'}}
144     // expected-note@-2 {{Taking false branch}}
145     return;
146   }
147 
148   if (isa<Triangle>(C)) {
149     // expected-note@-1 {{'C' is not a 'Triangle'}}
150     // expected-note@-2 {{Taking false branch}}
151     return;
152   }
153 
154   if (isa<Triangle, Rectangle>(C)) {
155     // expected-note@-1 {{'C' is neither a 'Triangle' nor a 'Rectangle'}}
156     // expected-note@-2 {{Taking false branch}}
157     return;
158   }
159 
160   if (isa<Triangle, Rectangle, Hexagon>(C)) {
161     // expected-note@-1 {{'C' is neither a 'Triangle' nor a 'Rectangle' nor a 'Hexagon'}}
162     // expected-note@-2 {{Taking false branch}}
163     return;
164   }
165 
166   if (isa<Circle, Rectangle, Hexagon>(C)) {
167     // expected-note@-1 {{'C' is a 'Circle'}}
168     // expected-note@-2 {{Taking true branch}}
169 
170     (void)(1 / !C);
171     // expected-note@-1 {{'C' is non-null}}
172     // expected-note@-2 {{Division by zero}}
173     // expected-warning@-3 {{Division by zero}}
174   }
175 }
176 
evalNonNullParamNonNullReturn(const Shape * S)177 void evalNonNullParamNonNullReturn(const Shape *S) {
178   const auto *C = cast<Circle>(S);
179   // expected-note@-1 {{'S' is a 'const class clang::Circle *'}}
180   // expected-note@-2 {{'C' initialized here}}
181 
182   if (!dyn_cast_or_null<Circle>(C)) {
183     // expected-note@-1 {{Assuming 'C' is a 'const class clang::Circle *'}}
184     // expected-note@-2 {{Taking false branch}}
185     return;
186   }
187 
188   if (dyn_cast_or_null<Triangle>(C)) {
189     // expected-note@-1 {{Assuming 'C' is not a 'const class clang::Triangle *'}}
190     // expected-note@-2 {{Taking false branch}}
191     return;
192   }
193 
194   if (dyn_cast_or_null<Rectangle>(C)) {
195     // expected-note@-1 {{Assuming 'C' is not a 'const class clang::Rectangle *'}}
196     // expected-note@-2 {{Taking false branch}}
197     return;
198   }
199 
200   if (dyn_cast_or_null<Hexagon>(C)) {
201     // expected-note@-1 {{Assuming 'C' is not a 'const class clang::Hexagon *'}}
202     // expected-note@-2 {{Taking false branch}}
203     return;
204   }
205 
206   if (isa<Triangle>(C)) {
207     // expected-note@-1 {{'C' is not a 'Triangle'}}
208     // expected-note@-2 {{Taking false branch}}
209     return;
210   }
211 
212   if (isa<Triangle, Rectangle>(C)) {
213     // expected-note@-1 {{'C' is neither a 'Triangle' nor a 'Rectangle'}}
214     // expected-note@-2 {{Taking false branch}}
215     return;
216   }
217 
218   if (isa<Triangle, Rectangle, Hexagon>(C)) {
219     // expected-note@-1 {{'C' is neither a 'Triangle' nor a 'Rectangle' nor a 'Hexagon'}}
220     // expected-note@-2 {{Taking false branch}}
221     return;
222   }
223 
224   if (isa<Circle, Rectangle, Hexagon>(C)) {
225     // expected-note@-1 {{'C' is a 'Circle'}}
226     // expected-note@-2 {{Taking true branch}}
227 
228     (void)(1 / !C);
229     // expected-note@-1 {{'C' is non-null}}
230     // expected-note@-2 {{Division by zero}}
231     // expected-warning@-3 {{Division by zero}}
232   }
233 }
234 
evalNonNullParamNullReturn(const Shape * S)235 void evalNonNullParamNullReturn(const Shape *S) {
236   const auto *C = dyn_cast_or_null<Circle>(S);
237   // expected-note@-1 {{Assuming 'S' is not a 'const class clang::Circle *'}}
238 
239   if (const auto *T = dyn_cast_or_null<Triangle>(S)) {
240     // expected-note@-1 {{Assuming 'S' is a 'const class clang::Triangle *'}}
241     // expected-note@-2 {{'T' initialized here}}
242     // expected-note@-3 {{'T' is non-null}}
243     // expected-note@-4 {{Taking true branch}}
244 
245     (void)(1 / !T);
246     // expected-note@-1 {{'T' is non-null}}
247     // expected-note@-2 {{Division by zero}}
248     // expected-warning@-3 {{Division by zero}}
249   }
250 }
251 
evalNullParamNullReturn(const Shape * S)252 void evalNullParamNullReturn(const Shape *S) {
253   const auto *C = dyn_cast_or_null<Circle>(S);
254   // expected-note@-1 {{Assuming null pointer is passed into cast}}
255   // expected-note@-2 {{'C' initialized to a null pointer value}}
256 
257   (void)(1 / (bool)C);
258   // expected-note@-1 {{Division by zero}}
259   // expected-warning@-2 {{Division by zero}}
260 }
261 
evalZeroParamNonNullReturnPointer(const Shape * S)262 void evalZeroParamNonNullReturnPointer(const Shape *S) {
263   const auto *C = S->castAs<Circle>();
264   // expected-note@-1 {{'S' is a 'const class clang::Circle *'}}
265   // expected-note@-2 {{'C' initialized here}}
266 
267   (void)(1 / !C);
268   // expected-note@-1 {{'C' is non-null}}
269   // expected-note@-2 {{Division by zero}}
270   // expected-warning@-3 {{Division by zero}}
271 }
272 
evalZeroParamNonNullReturn(const Shape & S)273 void evalZeroParamNonNullReturn(const Shape &S) {
274   const auto *C = S.castAs<Circle>();
275   // expected-note@-1 {{'C' initialized here}}
276 
277   (void)(1 / !C);
278   // expected-note@-1 {{'C' is non-null}}
279   // expected-note@-2 {{Division by zero}}
280   // expected-warning@-3 {{Division by zero}}
281 }
282 
evalZeroParamNullReturn(const Shape * S)283 void evalZeroParamNullReturn(const Shape *S) {
284   const auto &C = S->getAs<Circle>();
285   // expected-note@-1 {{Assuming 'S' is not a 'const class clang::Circle *'}}
286   // expected-note@-2 {{Storing null pointer value}}
287   // expected-note@-3 {{'C' initialized here}}
288 
289   if (!dyn_cast_or_null<Triangle>(S)) {
290     // expected-note@-1 {{Assuming 'S' is a 'const class clang::Triangle *'}}
291     // expected-note@-2 {{Taking false branch}}
292     return;
293   }
294 
295   if (!dyn_cast_or_null<Triangle>(S)) {
296     // expected-note@-1 {{'S' is a 'Triangle'}}
297     // expected-note@-2 {{Taking false branch}}
298     return;
299   }
300 
301   (void)(1 / (bool)C);
302   // expected-note@-1 {{Division by zero}}
303   // expected-warning@-2 {{Division by zero}}
304 }
305 
306 // don't crash
307 // CastValueChecker was using QualType()->getPointeeCXXRecordDecl(), in
308 // getNoteTag which evaluated to nullptr, then crashed when attempting to
309 // deref an invocation to getNameAsString(). The fix is to use
310 // QualType().getAsString().
311 //
312 // Example:
313 // std::string CastToName =
314 //       CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
315 //                : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
316 // Changed to:
317 // std::string CastToName =
318 //       CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
319 //                : CastToTy.getAsString();
320 namespace llvm {
321 template <typename, typename a> void isa(a &);
322 template <typename> class PointerUnion {
323 public:
getAs()324   template <typename T> T *getAs() {
325     (void)isa<int>(*this);
326     return nullptr;
327   }
328 };
329 class LLVMContext {
330   PointerUnion<LLVMContext> c;
d()331   void d() { c.getAs<int>(); }
332 };
333 } // namespace llvm
334