Resugar substituted type params found in class template instantiations. For now, only handles Foo<args>::y syntax, propagating sugar from args into y. Adds a broken test for qualifiers whose args are better hidden, to fix later. This generalizes the work on resugaring alias templates, and unfortunately makes it more abstract. PiperOrigin-RevId: 530888965
diff --git a/nullability/type_nullability_test.cc b/nullability/type_nullability_test.cc index 36e4632..051371c 100644 --- a/nullability/type_nullability_test.cc +++ b/nullability/type_nullability_test.cc
@@ -5,6 +5,7 @@ #include "nullability/type_nullability.h" #include "absl/log/check.h" +#include "clang/Basic/Specifiers.h" #include "clang/Testing/TestAST.h" #include "llvm/ADT/StringRef.h" #include "third_party/llvm/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h" @@ -126,10 +127,8 @@ using type = T _Nullable; }; )cpp"; - // TODO: should be [Nullable, Nonnull] - EXPECT_THAT( - nullVec("Nullable<int* _Nonnull *>::type"), - ElementsAre(NullabilityKind::Nullable, NullabilityKind::Unspecified)); + EXPECT_THAT(nullVec("Nullable<int* _Nonnull *>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); } TEST_F(GetNullabilityAnnotationsFromTypeTest, NestedClassTemplate) { @@ -146,6 +145,32 @@ ElementsAre(NullabilityKind::Unspecified)); } +TEST_F(GetNullabilityAnnotationsFromTypeTest, NestedClassInstantiation) { + Preamble = R"cpp( + template <class T, class U> + struct Pair; + template <class T, class U> + struct PairWrapper { + using type = Pair<T _Nullable, U>; + }; + )cpp"; + + EXPECT_THAT(nullVec("PairWrapper<int*, int* _Nonnull>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); + EXPECT_THAT( + nullVec("PairWrapper<int* _Nonnull, int*>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::Unspecified)); + + EXPECT_THAT( + nullVec("PairWrapper<PairWrapper<int*, int* _Nonnull>::type*, " + " PairWrapper<int* _Nonnull, int*>::type*>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::Nullable, + NullabilityKind::NonNull, + + NullabilityKind::Unspecified, NullabilityKind::Nullable, + NullabilityKind::Unspecified)); +} + TEST_F(GetNullabilityAnnotationsFromTypeTest, ReferenceOuterTemplateParam) { // Referencing type-params from indirectly-enclosing template. Preamble = R"cpp( @@ -160,12 +185,70 @@ }; }; )cpp"; - // TODO: should be [Nonnull, Nullable] - EXPECT_THAT( - nullVec("Outer<int *_Nullable>::Inner<int *_Nonnull>::type"), - ElementsAre(NullabilityKind::Unspecified, NullabilityKind::Unspecified)); + EXPECT_THAT(nullVec("Outer<int *_Nullable>::Inner<int *_Nonnull>::type"), + ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable)); + // Same where Inner is an alias template. + Preamble = R"cpp( + template <class A, class B> + struct Pair; + + template <class T> + struct Outer { + template <class U> + using Inner = Pair<U, T>; + }; + )cpp"; + EXPECT_THAT(nullVec("Outer<int *_Nullable>::Inner<int *_Nonnull>"), + ElementsAre(NullabilityKind::NonNull, NullabilityKind::Nullable)); } +TEST_F(GetNullabilityAnnotationsFromTypeTest, MixedQualiferChain) { + Preamble = R"cpp( + template <class A, class B> + class Pair; + + struct Outer1 { + template <class T> + struct Middle { + template <class U> + struct Inner { + using type = Pair<T, U>; + }; + }; + }; + + template <class T> + struct Outer2 { + struct Middle { + template <class U> + struct Inner { + using type = Pair<T, U>; + }; + }; + }; + + template <class T> + struct Outer3 { + template <class U> + struct Middle { + struct Inner { + using type = Pair<T, U>; + }; + }; + }; + )cpp"; + + EXPECT_THAT( + nullVec("Outer1::Middle<int * _Nullable>::Inner<int * _Nonnull>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); + EXPECT_THAT( + nullVec("Outer2<int * _Nullable>::Middle::Inner<int * _Nonnull>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); + EXPECT_THAT( + nullVec("Outer3<int * _Nullable>::Middle<int * _Nonnull>::Inner::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); +}; + TEST_F(GetNullabilityAnnotationsFromTypeTest, DependentlyNamedTemplate) { // Instantiation of dependent-named template Preamble = R"cpp( @@ -202,10 +285,8 @@ )cpp"; EXPECT_THAT(nullVec("Pointer<Nullable, int>::type"), ElementsAre(NullabilityKind::Nullable)); - // TODO: should be [Nullable, Nonnull] - EXPECT_THAT( - nullVec("Pointer<Nullable, Pointer<Nonnull, int>::type>::type"), - ElementsAre(NullabilityKind::Nullable, NullabilityKind::Unspecified)); + EXPECT_THAT(nullVec("Pointer<Nullable, Pointer<Nonnull, int>::type>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); // Same thing, but with alias templates. Preamble = R"cpp( template <class X> @@ -220,10 +301,8 @@ )cpp"; EXPECT_THAT(nullVec("Pointer<Nullable, int>::type"), ElementsAre(NullabilityKind::Nullable)); - // TODO: should be [Nullable, Nonnull] - EXPECT_THAT( - nullVec("Pointer<Nullable, Pointer<Nonnull, int>::type>::type"), - ElementsAre(NullabilityKind::Nullable, NullabilityKind::Unspecified)); + EXPECT_THAT(nullVec("Pointer<Nullable, Pointer<Nonnull, int>::type>::type"), + ElementsAre(NullabilityKind::Nullable, NullabilityKind::NonNull)); } TEST_F(GetNullabilityAnnotationsFromTypeTest, ClassTemplateParamPack) { @@ -259,6 +338,19 @@ ElementsAre(NullabilityKind::Unspecified)); } +TEST_F(GetNullabilityAnnotationsFromTypeTest, TemplateArgsBehindAlias) { + Preamble = R"cpp( + template <class X> + struct Outer { + using Inner = X; + }; + using OuterNullable = Outer<int* _Nullable>; + )cpp"; + // TODO: should be [Nullable] + EXPECT_THAT(nullVec("OuterNullable::Inner"), + ElementsAre(NullabilityKind::Unspecified)); +} + class PrintWithNullabilityTest : public ::testing::Test { protected: // C++ declarations prepended before parsing type in nullVec().