blob: f10f2fc034b661cb308b4f19e73517069b234237 [file] [log] [blame]
Sam McCall5fc2a802023-05-02 05:41:27 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "nullability/type_nullability.h"
6
7#include "absl/log/check.h"
Sam McCall901c35b2023-05-10 06:14:51 -07008#include "clang/Basic/Specifiers.h"
Sam McCall5fc2a802023-05-02 05:41:27 -07009#include "clang/Testing/TestAST.h"
10#include "llvm/ADT/StringRef.h"
11#include "third_party/llvm/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h"
12#include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
13
14namespace clang::tidy::nullability {
15namespace {
16using testing::ElementsAre;
17
18class GetNullabilityAnnotationsFromTypeTest : public ::testing::Test {
19 protected:
20 // C++ declarations prepended before parsing type in nullVec().
21 std::string Preamble;
22
23 // Parses `Type` and returns getNullabilityAnnotationsFromType().
Sam McCalld127f932023-05-02 07:15:27 -070024 TypeNullability nullVec(llvm::StringRef Type) {
Sam McCall5fc2a802023-05-02 05:41:27 -070025 clang::TestAST AST((Preamble + "\nusing Target = " + Type + ";").str());
26 auto Target = AST.context().getTranslationUnitDecl()->lookup(
27 &AST.context().Idents.get("Target"));
28 CHECK(Target.isSingleResult());
29 QualType TargetType =
30 AST.context().getTypedefType(Target.find_first<TypeAliasDecl>());
31 return getNullabilityAnnotationsFromType(TargetType);
32 }
33};
34
35TEST_F(GetNullabilityAnnotationsFromTypeTest, Pointers) {
36 EXPECT_THAT(nullVec("int"), ElementsAre());
37 EXPECT_THAT(nullVec("int *"), ElementsAre(NullabilityKind::Unspecified));
38 EXPECT_THAT(nullVec("int **"), ElementsAre(NullabilityKind::Unspecified,
39 NullabilityKind::Unspecified));
40 EXPECT_THAT(nullVec("int *_Nullable*_Nonnull"),
41 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
42}
43
44TEST_F(GetNullabilityAnnotationsFromTypeTest, Sugar) {
45 Preamble = "using X = int* _Nonnull;";
46
47 EXPECT_THAT(nullVec("X"), ElementsAre(NullabilityKind::NonNull));
48 EXPECT_THAT(nullVec("X*"), ElementsAre(NullabilityKind::Unspecified,
49 NullabilityKind::NonNull));
50
51 EXPECT_THAT(nullVec("X(*)"), ElementsAre(NullabilityKind::Unspecified,
52 NullabilityKind::NonNull));
53}
54
Sam McCall9a893de2023-05-02 06:06:55 -070055TEST_F(GetNullabilityAnnotationsFromTypeTest, References) {
56 // Top-level references can't be expression types, but we support them anyway
57 EXPECT_THAT(nullVec("int * _Nonnull &"),
58 ElementsAre(NullabilityKind::NonNull));
59 EXPECT_THAT(nullVec("int * _Nonnull &&"),
60 ElementsAre(NullabilityKind::NonNull));
61
62 // ... and other types involving references can appear in expressions
63 EXPECT_THAT(nullVec("int * _Nullable& (* _Nonnull)()"),
64 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
65 EXPECT_THAT(nullVec("int * _Nullable&& (* _Nonnull)()"),
66 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
67}
68
Sam McCallf2b62b32023-05-02 06:45:40 -070069TEST_F(GetNullabilityAnnotationsFromTypeTest, Arrays) {
70 EXPECT_THAT(nullVec("int * _Nonnull[][2]"),
71 ElementsAre(NullabilityKind::NonNull));
72}
73
Sam McCall5fc2a802023-05-02 05:41:27 -070074TEST_F(GetNullabilityAnnotationsFromTypeTest, AliasTemplates) {
75 Preamble = R"cpp(
76 template <typename T>
77 using Nullable = T _Nullable;
78 template <typename T>
79 using Nonnull = T _Nonnull;
80 )cpp";
81 EXPECT_THAT(nullVec("Nullable<int*>"),
82 ElementsAre(NullabilityKind::Nullable));
83
84 EXPECT_THAT(
85 nullVec("Nullable<Nullable<int*>*>"),
86 ElementsAre(NullabilityKind::Nullable, NullabilityKind::Nullable));
87
88 EXPECT_THAT(nullVec("Nullable<Nullable<Nonnull<int*>*>*>"),
89 ElementsAre(NullabilityKind::Nullable, NullabilityKind::Nullable,
90 NullabilityKind::NonNull));
91
92 Preamble = R"cpp(
93 template <typename T, typename U>
94 struct Pair;
95 template <typename T>
96 using Two = Pair<T, T>;
97 )cpp";
98 EXPECT_THAT(
99 nullVec("Two<int* _Nullable>"),
100 ElementsAre(NullabilityKind::Nullable, NullabilityKind::Nullable));
101
102 Preamble = R"cpp(
103 template <typename T1>
Sam McCall7d9afee2023-06-27 01:43:24 -0700104 using A = T1 *_Nullable;
Sam McCall5fc2a802023-05-02 05:41:27 -0700105 template <typename T2>
Sam McCall7d9afee2023-06-27 01:43:24 -0700106 using B = A<T2> *_Nonnull;
Sam McCall5fc2a802023-05-02 05:41:27 -0700107 )cpp";
108 EXPECT_THAT(nullVec("B<int>"),
109 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
110
111 Preamble = R"cpp(
112 template <typename T, typename U, typename V>
113 struct Triple;
114 template <typename A, typename... Rest>
115 using TripleAlias = Triple<A _Nonnull, Rest...>;
116 )cpp";
117 EXPECT_THAT(nullVec("TripleAlias<int *, int *_Nullable, int*>"),
118 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable,
119 NullabilityKind::Unspecified));
Sam McCallc18ef6b2023-06-19 01:14:21 -0700120
121 Preamble = R"cpp(
122 template <class... Ts>
123 using First = __type_pack_element<0, Ts...>;
124 )cpp";
125 EXPECT_THAT(nullVec("First<int * _Nonnull>"),
126 ElementsAre(NullabilityKind::NonNull));
Sam McCall5fc2a802023-05-02 05:41:27 -0700127}
128
129TEST_F(GetNullabilityAnnotationsFromTypeTest, DependentAlias) {
130 // Simple dependent type-aliases.
131 Preamble = R"cpp(
132 template <class T>
133 struct Nullable {
134 using type = T _Nullable;
135 };
136 )cpp";
Sam McCall901c35b2023-05-10 06:14:51 -0700137 EXPECT_THAT(nullVec("Nullable<int* _Nonnull *>::type"),
138 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
Sam McCall5fc2a802023-05-02 05:41:27 -0700139}
140
141TEST_F(GetNullabilityAnnotationsFromTypeTest, NestedClassTemplate) {
142 // Simple struct inside template.
143 Preamble = R"cpp(
144 template <class T>
145 struct Outer {
146 struct Inner;
147 };
Sam McCall7d9afee2023-06-27 01:43:24 -0700148 using OuterNullableInner = Outer<int *_Nonnull>::Inner;
Sam McCall5fc2a802023-05-02 05:41:27 -0700149 )cpp";
150 // TODO: should be [NonNull]
151 EXPECT_THAT(nullVec("Outer<int* _Nonnull>::Inner"),
152 ElementsAre(NullabilityKind::Unspecified));
153}
154
Sam McCall901c35b2023-05-10 06:14:51 -0700155TEST_F(GetNullabilityAnnotationsFromTypeTest, NestedClassInstantiation) {
156 Preamble = R"cpp(
157 template <class T, class U>
158 struct Pair;
159 template <class T, class U>
160 struct PairWrapper {
161 using type = Pair<T _Nullable, U>;
162 };
163 )cpp";
164
165 EXPECT_THAT(nullVec("PairWrapper<int*, int* _Nonnull>::type"),
166 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
167 EXPECT_THAT(
168 nullVec("PairWrapper<int* _Nonnull, int*>::type"),
169 ElementsAre(NullabilityKind::Nullable, NullabilityKind::Unspecified));
170
171 EXPECT_THAT(
172 nullVec("PairWrapper<PairWrapper<int*, int* _Nonnull>::type*, "
173 " PairWrapper<int* _Nonnull, int*>::type*>::type"),
174 ElementsAre(NullabilityKind::Nullable, NullabilityKind::Nullable,
175 NullabilityKind::NonNull,
176
177 NullabilityKind::Unspecified, NullabilityKind::Nullable,
178 NullabilityKind::Unspecified));
179}
180
Sam McCall5fc2a802023-05-02 05:41:27 -0700181TEST_F(GetNullabilityAnnotationsFromTypeTest, ReferenceOuterTemplateParam) {
182 // Referencing type-params from indirectly-enclosing template.
183 Preamble = R"cpp(
184 template <class A, class B>
185 struct Pair;
186
187 template <class T>
188 struct Outer {
189 template <class U>
190 struct Inner {
191 using type = Pair<U, T>;
192 };
193 };
194 )cpp";
Sam McCall901c35b2023-05-10 06:14:51 -0700195 EXPECT_THAT(nullVec("Outer<int *_Nullable>::Inner<int *_Nonnull>::type"),
196 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
197 // Same where Inner is an alias template.
198 Preamble = R"cpp(
199 template <class A, class B>
200 struct Pair;
201
202 template <class T>
203 struct Outer {
204 template <class U>
205 using Inner = Pair<U, T>;
206 };
207 )cpp";
208 EXPECT_THAT(nullVec("Outer<int *_Nullable>::Inner<int *_Nonnull>"),
209 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
Sam McCall5fc2a802023-05-02 05:41:27 -0700210}
211
Sam McCall901c35b2023-05-10 06:14:51 -0700212TEST_F(GetNullabilityAnnotationsFromTypeTest, MixedQualiferChain) {
213 Preamble = R"cpp(
214 template <class A, class B>
215 class Pair;
216
217 struct Outer1 {
218 template <class T>
219 struct Middle {
220 template <class U>
221 struct Inner {
222 using type = Pair<T, U>;
223 };
224 };
225 };
226
227 template <class T>
228 struct Outer2 {
229 struct Middle {
230 template <class U>
231 struct Inner {
232 using type = Pair<T, U>;
233 };
234 };
235 };
236
237 template <class T>
238 struct Outer3 {
239 template <class U>
240 struct Middle {
241 struct Inner {
242 using type = Pair<T, U>;
243 };
244 };
245 };
246 )cpp";
247
248 EXPECT_THAT(
249 nullVec("Outer1::Middle<int * _Nullable>::Inner<int * _Nonnull>::type"),
250 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
251 EXPECT_THAT(
252 nullVec("Outer2<int * _Nullable>::Middle::Inner<int * _Nonnull>::type"),
253 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
254 EXPECT_THAT(
255 nullVec("Outer3<int * _Nullable>::Middle<int * _Nonnull>::Inner::type"),
256 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
257};
258
Sam McCall5fc2a802023-05-02 05:41:27 -0700259TEST_F(GetNullabilityAnnotationsFromTypeTest, DependentlyNamedTemplate) {
260 // Instantiation of dependent-named template
261 Preamble = R"cpp(
262 struct Wrapper {
263 template <class T>
264 using Nullable = T _Nullable;
265 };
266
267 template <class U, class WrapT>
268 struct S {
Sam McCall7d9afee2023-06-27 01:43:24 -0700269 using type = typename WrapT::template Nullable<U> *_Nonnull;
Sam McCall5fc2a802023-05-02 05:41:27 -0700270 };
271 )cpp";
272 EXPECT_THAT(nullVec("S<int *, Wrapper>::type"),
273 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
274}
275
Sam McCalla6b35b62023-06-20 01:48:38 -0700276TEST_F(GetNullabilityAnnotationsFromTypeTest, PartialSpecialization) {
277 Preamble = R"cpp(
278 template <class>
279 struct S;
280 template <class T>
Sam McCall7d9afee2023-06-27 01:43:24 -0700281 struct S<T *> {
Sam McCalla6b35b62023-06-20 01:48:38 -0700282 using Alias = T;
283 };
284 )cpp";
285 EXPECT_THAT(nullVec("S<int*>::Alias"), testing::IsEmpty());
286}
287
Sam McCall5fc2a802023-05-02 05:41:27 -0700288TEST_F(GetNullabilityAnnotationsFromTypeTest, TemplateTemplateParams) {
289 // Template template params
290 Preamble = R"cpp(
291 template <class X>
292 struct Nullable {
293 using type = X _Nullable;
294 };
295 template <class X>
296 struct Nonnull {
297 using type = X _Nonnull;
298 };
299
300 template <template <class> class Nullability, class T>
301 struct Pointer {
Sam McCall7d9afee2023-06-27 01:43:24 -0700302 using type = typename Nullability<T *>::type;
Sam McCall5fc2a802023-05-02 05:41:27 -0700303 };
304 )cpp";
305 EXPECT_THAT(nullVec("Pointer<Nullable, int>::type"),
306 ElementsAre(NullabilityKind::Nullable));
Sam McCall901c35b2023-05-10 06:14:51 -0700307 EXPECT_THAT(nullVec("Pointer<Nullable, Pointer<Nonnull, int>::type>::type"),
308 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
Sam McCall5fc2a802023-05-02 05:41:27 -0700309 // Same thing, but with alias templates.
310 Preamble = R"cpp(
311 template <class X>
312 using Nullable = X _Nullable;
313 template <class X>
314 using Nonnull = X _Nonnull;
315
316 template <template <class> class Nullability, class T>
317 struct Pointer {
Sam McCall7d9afee2023-06-27 01:43:24 -0700318 using type = Nullability<T *>;
Sam McCall5fc2a802023-05-02 05:41:27 -0700319 };
320 )cpp";
321 EXPECT_THAT(nullVec("Pointer<Nullable, int>::type"),
322 ElementsAre(NullabilityKind::Nullable));
Sam McCall901c35b2023-05-10 06:14:51 -0700323 EXPECT_THAT(nullVec("Pointer<Nullable, Pointer<Nonnull, int>::type>::type"),
324 ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull));
Sam McCall5fc2a802023-05-02 05:41:27 -0700325}
326
327TEST_F(GetNullabilityAnnotationsFromTypeTest, ClassTemplateParamPack) {
328 // Parameter packs
329 Preamble = R"cpp(
330 template <class... X>
331 struct TupleWrapper {
332 class Tuple;
333 };
334
335 template <class... X>
336 struct NullableTuple {
337 using type = TupleWrapper<X _Nullable...>::Tuple;
338 };
339 )cpp";
340 // TODO: should be [Unspecified, Nonnull]
341 EXPECT_THAT(
342 nullVec("TupleWrapper<int*, int* _Nonnull>::Tuple"),
343 ElementsAre(NullabilityKind::Unspecified, NullabilityKind::Unspecified));
344 // TODO: should be [Nullable, Nullable]
345 EXPECT_THAT(
346 nullVec("NullableTuple<int*, int* _Nonnull>::type"),
347 ElementsAre(NullabilityKind::Unspecified, NullabilityKind::Unspecified));
348}
349
Martin Brænnee8a33622023-05-08 02:04:59 -0700350TEST_F(GetNullabilityAnnotationsFromTypeTest, AliasTemplateWithDefaultArg) {
351 Preamble =
352 "template <typename T1, typename T2 = T1> using AliasTemplate = T2;";
353
354 // TODO(b/281474380): This should be [Nullable], but we don't yet handle
355 // default arguments correctly.
356 EXPECT_THAT(nullVec("AliasTemplate<int * _Nullable>"),
357 ElementsAre(NullabilityKind::Unspecified));
358}
359
Martin Brænnea354fb72023-06-26 06:16:22 -0700360TEST_F(GetNullabilityAnnotationsFromTypeTest, ClassTemplateWithDefaultArg) {
361 Preamble = "template <typename T1, typename T2 = T1> class ClassTemplate {};";
362
363 // TODO(b/281474380): This should be [Nullable, Nullable], but we don't yet
364 // handle default arguments correctly.
365 EXPECT_THAT(
366 nullVec("ClassTemplate<int * _Nullable>"),
367 ElementsAre(NullabilityKind::Nullable, NullabilityKind::Unspecified));
368}
369
Sam McCall901c35b2023-05-10 06:14:51 -0700370TEST_F(GetNullabilityAnnotationsFromTypeTest, TemplateArgsBehindAlias) {
371 Preamble = R"cpp(
372 template <class X>
373 struct Outer {
374 using Inner = X;
375 };
Sam McCall7d9afee2023-06-27 01:43:24 -0700376 using OuterNullable = Outer<int *_Nullable>;
Sam McCall901c35b2023-05-10 06:14:51 -0700377 )cpp";
378 // TODO: should be [Nullable]
379 EXPECT_THAT(nullVec("OuterNullable::Inner"),
380 ElementsAre(NullabilityKind::Unspecified));
381}
382
Sam McCall26940a22023-05-16 07:44:21 -0700383TEST_F(GetNullabilityAnnotationsFromTypeTest, AnnotateNullable) {
384 Preamble = R"cpp(
385 namespace custom {
386 template <class T>
387 using Nullable [[clang::annotate("Nullable")]] = T;
388 template <class T>
389 using NonNull [[clang::annotate("Nonnull")]] = T;
390 } // namespace custom
391
392 template <class T, class U>
393 class pair;
394
395 template <class X>
396 using twice = pair<X, X>;
397 )cpp";
398 EXPECT_THAT(nullVec("custom::Nullable<int*>"),
399 ElementsAre(NullabilityKind::Nullable));
400 EXPECT_THAT(nullVec("custom::NonNull<int*>"),
401 ElementsAre(NullabilityKind::NonNull));
402 EXPECT_THAT(nullVec("pair<custom::NonNull<int*>, custom::Nullable<int*>>"),
403 ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable));
404 EXPECT_THAT(nullVec("twice<custom::NonNull<int*>>"),
405 ElementsAre(NullabilityKind::NonNull, NullabilityKind::NonNull));
406
407 // Should still work if aliases *do* apply _Nullable.
408 Preamble = R"cpp(
409 namespace custom {
410 template <class T>
411 using Nullable [[clang::annotate("Nullable")]] = T _Nullable;
412 template <class T>
413 using NonNull [[clang::annotate("Nonnull")]] = T _Nonnull;
414 } // namespace custom
415 )cpp";
416 EXPECT_THAT(nullVec("custom::Nullable<int*>"),
417 ElementsAre(NullabilityKind::Nullable));
418 EXPECT_THAT(nullVec("custom::NonNull<int*>"),
419 ElementsAre(NullabilityKind::NonNull));
420}
421
Sam McCall5fc2a802023-05-02 05:41:27 -0700422class PrintWithNullabilityTest : public ::testing::Test {
423 protected:
424 // C++ declarations prepended before parsing type in nullVec().
425 std::string Preamble;
426
427 // Parses `Type`, augments it with Nulls, and prints the result.
Sam McCall7d9afee2023-06-27 01:43:24 -0700428 std::string print(llvm::StringRef Type, const TypeNullability &Nulls) {
Sam McCall5fc2a802023-05-02 05:41:27 -0700429 clang::TestAST AST((Preamble + "\n using Target = " + Type + ";").str());
430 auto Target = AST.context().getTranslationUnitDecl()->lookup(
431 &AST.context().Idents.get("Target"));
432 CHECK(Target.isSingleResult());
433 QualType TargetType =
434 AST.context().getTypedefType(Target.find_first<TypeAliasDecl>());
435 return printWithNullability(TargetType, Nulls, AST.context());
436 }
437};
438
439TEST_F(PrintWithNullabilityTest, Pointers) {
440 EXPECT_EQ(print("int*", {NullabilityKind::Nullable}), "int * _Nullable");
441 EXPECT_EQ(
442 print("int***", {NullabilityKind::Nullable, NullabilityKind::NonNull,
443 NullabilityKind::Unspecified}),
444 "int ** _Nonnull * _Nullable");
445}
446
447TEST_F(PrintWithNullabilityTest, Sugar) {
448 Preamble = R"cpp(
449 template <class T>
Sam McCall7d9afee2023-06-27 01:43:24 -0700450 using Ptr = T *;
Sam McCall5fc2a802023-05-02 05:41:27 -0700451 using Int = int;
452 using IntPtr = Ptr<Int>;
453 )cpp";
454 EXPECT_EQ(print("IntPtr", {NullabilityKind::Nullable}), "int * _Nullable");
455}
456
457TEST_F(PrintWithNullabilityTest, Templates) {
458 Preamble = R"cpp(
459 template <class>
460 struct vector;
461 template <class, class>
462 struct pair;
463 )cpp";
464 EXPECT_EQ(print("vector<pair<int*, int*>*>",
465 {NullabilityKind::Nullable, NullabilityKind::NonNull,
466 NullabilityKind::Unspecified}),
467 "vector<pair<int * _Nonnull, int *> * _Nullable>");
468}
469
470TEST_F(PrintWithNullabilityTest, Functions) {
471 EXPECT_EQ(print("float*(*)(double*, double*)",
472 {NullabilityKind::Nullable, NullabilityKind::NonNull,
473 NullabilityKind::NonNull, NullabilityKind::Unspecified}),
474 "float * _Nonnull (* _Nullable)(double * _Nonnull, double *)");
475}
476
Sam McCallf2b62b32023-05-02 06:45:40 -0700477TEST_F(PrintWithNullabilityTest, Arrays) {
478 EXPECT_EQ(print("int*[][2]", {NullabilityKind::Nullable}),
479 "int * _Nullable[][2]");
480 // variable length array not allowed at file scope, wrap in a function...
481 Preamble = R"cpp(
482 int n;
Sam McCall7d9afee2023-06-27 01:43:24 -0700483 auto &makeArray() {
484 float *array[n];
Sam McCallf2b62b32023-05-02 06:45:40 -0700485 return array;
486 }
487 )cpp";
488 EXPECT_EQ(print("decltype(makeArray())", {NullabilityKind::Nullable}),
489 "float * _Nullable (&)[n]");
490}
491
Sam McCall5fc2a802023-05-02 05:41:27 -0700492} // namespace
493} // namespace clang::tidy::nullability