blob: 01b3a70be4cf972bdacb3505a537d5191f34762a [file] [log] [blame] [edit]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Tests for nullability annotations in template arguments.
#include "nullability/test/check_diagnostics.h"
#include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
namespace clang::tidy::nullability {
namespace {
TEST(PointerNullabilityTest, ClassTemplateInstantiation) {
// Class template specialization with one argument initialised as _Nullable.
// We test types that contain both nullability that is substituted into the
// template argument and nullability that is spelt inside the template. That
// is, we should be able to accurately store nullabilities from different
// sources in a single nullability vector.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 *unknownTPtr;
T0 *_Nullable nullableTPtr;
T0 *_Nonnull nonnullTPtr;
T0 getT();
T0 *getUnknownTPtr();
T0 *_Nullable getNullableTPtr();
T0 *_Nonnull getNonnullTPtr();
};
void target(Struct1Arg<int *_Nullable> p) {
*p.arg0; // [[unsafe]]
*p.unknownTPtr;
*p.nullableTPtr; // [[unsafe]]
*p.nonnullTPtr;
**p.unknownTPtr; // [[unsafe]]
**p.nullableTPtr; // [[unsafe]]
**p.nonnullTPtr; // [[unsafe]]
*p.getT(); // [[unsafe]]
*p.getUnknownTPtr();
*p.getNullableTPtr(); // [[unsafe]]
*p.getNonnullTPtr();
**p.getUnknownTPtr(); // [[unsafe]]
**p.getNullableTPtr(); // [[unsafe]]
**p.getNonnullTPtr(); // [[unsafe]]
}
)cc"));
// Class template specialization with one argument initialised as _Nonnull.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 *unknownTPtr;
T0 *_Nullable nullableTPtr;
T0 *_Nonnull nonnullTPtr;
T0 getT();
T0 *getUnknownTPtr();
T0 *_Nullable getNullableTPtr();
T0 *_Nonnull getNonnullTPtr();
};
void target(Struct1Arg<int *_Nonnull> p) {
*p.getT();
*p.getUnknownTPtr();
*p.getNullableTPtr(); // [[unsafe]]
*p.getNonnullTPtr();
**p.getUnknownTPtr();
**p.getNullableTPtr(); // [[unsafe]]
**p.getNonnullTPtr();
*p.arg0;
*p.unknownTPtr;
*p.nullableTPtr; // [[unsafe]]
*p.nonnullTPtr;
**p.unknownTPtr;
**p.nullableTPtr; // [[unsafe]]
**p.nonnullTPtr;
}
)cc"));
// Class template specialization with one argument initialised without
// nullability annotation.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 *unknownTPtr;
T0 *_Nullable nullableTPtr;
T0 *_Nonnull nonnullTPtr;
T0 getT();
T0 *getUnknownTPtr();
T0 *_Nullable getNullableTPtr();
T0 *_Nonnull getNonnullTPtr();
};
void target(Struct1Arg<int *> p) {
*p.getT();
*p.getUnknownTPtr();
*p.getNullableTPtr(); // [[unsafe]]
*p.getNonnullTPtr();
**p.getUnknownTPtr();
**p.getNullableTPtr(); // [[unasfe]]
**p.getNonnullTPtr();
*p.arg0;
*p.unknownTPtr;
*p.nullableTPtr; // [[unsafe]]
*p.nonnullTPtr;
**p.unknownTPtr;
**p.nullableTPtr; // [[unsafe]]
**p.nonnullTPtr;
}
)cc"));
// Class template specialization with two arguments, whose second argument is
// initialized as nullable.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {
T0 arg0;
T0 *unknownT0Ptr;
T0 *_Nullable nullableT0Ptr;
T0 *_Nonnull nonnullT0Ptr;
T1 arg1;
T1 *unknownT1Ptr;
T1 *_Nullable nullableT1Ptr;
T1 *_Nonnull nonnullT1Ptr;
T0 getT0();
T0 *getUnknownT0Ptr();
T0 *_Nullable getNullableT0Ptr();
T0 *_Nonnull getNonnullT0Ptr();
T1 getT1();
T1 *getUnknownT1Ptr();
T1 *_Nullable getNullableT1Ptr();
T1 *_Nonnull getNonnullT1Ptr();
};
void target(Struct2Arg<int *_Nonnull, double *_Nullable> p) {
*p.arg0;
*p.arg1; // [[unsafe]]
*p.unknownT0Ptr;
*p.nullableT0Ptr; // [[unsafe]]
*p.nonnullT0Ptr;
*p.unknownT1Ptr;
*p.nullableT1Ptr; // [[unsafe]]
*p.nonnullT1Ptr;
*p.getUnknownT0Ptr();
*p.getNullableT0Ptr(); // [[unsafe]]
*p.getNonnullT0Ptr();
*p.getUnknownT1Ptr();
*p.getNullableT1Ptr(); // [[unsafe]]
*p.getNonnullT1Ptr();
}
)cc"));
// Class template specialization with 5 arguments with interleaved
// nullable/nonnull/unknown.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1, typename T2, typename T3, typename T4>
struct Struct5Arg {
T0 arg0;
T1 arg1;
T2 arg2;
T3 arg3;
T4 arg4;
T0 getT0();
T1 getT1();
T2 getT2();
T3 getT3();
T4 getT4();
};
void target(Struct5Arg<int *_Nullable, double *_Nonnull, float *,
double *_Nullable, int *_Nonnull>
p) {
*p.arg0; // [[unsafe]]
*p.arg1;
*p.arg2;
*p.arg3; // [[unsafe]]
*p.arg4;
*p.getT0(); // [[unsafe]]
*p.getT1();
*p.getT2();
*p.getT3(); // [[unsafe]]
*p.getT4();
}
)cc"));
// Class template specialization with 5 arguments with interleaved
// nullable/nonnull/unknown/const.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1, typename T2, typename T3, typename T4>
struct Struct5Arg {
T0 arg0;
T1 arg1;
T2 arg2;
T3 arg3;
T4 arg4;
T0 getT0();
T1 getT1();
T2 getT2();
T3 getT3();
T4 getT4();
};
void target(
Struct5Arg<int *const _Nullable, double const *const _Nonnull, float *,
double const *const _Nullable, int *_Nonnull>
p) {
*p.arg0; // [[unsafe]]
*p.arg1;
*p.arg2;
*p.arg3; // [[unsafe]]
*p.arg4;
*p.getT0(); // [[unsafe]]
*p.getT1();
*p.getT2();
*p.getT3(); // [[unsafe]]
*p.getT4();
}
)cc"));
// Class template specialization with interleaved int and type template
// parameters.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, int I2, typename T3, int I4, typename T5>
struct Struct6ArgWithInt {
T1 arg1;
T3 arg3;
T5 arg5;
T1 getT1();
T3 getT3();
T5 getT5();
};
void target(
Struct6ArgWithInt<0, int *_Nullable, 1, int *_Nullable, 2, int *> &x) {
*x.arg1; // [[unsafe]]
*x.arg3; // [[unsafe]]
*x.arg5;
*x.getT1(); // [[unsafe]]
*x.getT3(); // [[unsafe]]
*x.getT5();
}
)cc"));
}
TEST(PointerNullabilityTest,
ClassTemplateInstantiationWithStructsAsParameters) {
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct3IntPtrs {
int *unknown;
int *_Nullable nullable;
int *_Nonnull nonnull;
int *getUnknown();
int *_Nullable getNullable();
int *_Nonnull getNonnull();
};
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 getT0();
};
void target(Struct1Arg<Struct3IntPtrs> p) {
*p.arg0.unknown;
*p.arg0.nullable; // [[unsafe]]
*p.arg0.nonnull;
*p.arg0.getUnknown();
*p.arg0.getNullable(); // [[unsafe]]
*p.arg0.getNonnull();
*p.getT0().unknown;
*p.getT0().nullable; // [[unsafe]]
*p.getT0().nonnull;
*p.getT0().getUnknown();
*p.getT0().getNullable(); // [[unsafe]]
*p.getT0().getNonnull();
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct1UnknownArg {
char *unknownChar;
char *getUnknownChar();
};
struct Struct1NullableArg {
char *_Nullable nullableChar;
char *_Nullable getNullableChar();
};
struct Struct1NonnullArg {
char *_Nonnull nonnullChar;
char *_Nonnull getNonnullChar();
};
struct StructLotsOfArgs {
int num;
long long *unknownLongLong;
double *_Nullable nullableDouble;
float *_Nonnull nonnullFloat;
short *unknownShort;
unsigned int *_Nullable nullableUInt;
bool *_Nullable nullableBool;
long long *getUnknownLongLong();
double *_Nullable getNullableDouble();
float *_Nonnull getNonnullFloat();
short *getUnknownShort();
unsigned int *_Nullable getNullableUInt();
bool *_Nullable getNullableBool();
};
template <typename T0, typename T1, typename T2, typename T3>
struct Struct4Arg {
T0 arg0;
T1 arg1;
T2 arg2;
T3 arg3;
T0 getT0();
T1 getT1();
T2 getT2();
T3 getT3();
};
void target(Struct4Arg<Struct1UnknownArg, Struct1NullableArg,
Struct1NonnullArg, StructLotsOfArgs>
p) {
*p.arg0.unknownChar;
*p.arg1.nullableChar; // [[unsafe]]
*p.arg2.nonnullChar;
*p.arg3.unknownLongLong;
*p.arg3.nullableDouble; // [[unsafe]]
*p.arg3.nonnullFloat;
*p.arg3.unknownShort;
*p.arg3.nullableUInt; // [[unsafe]]
*p.arg3.nullableBool; // [[unsafe]]
*p.arg0.getUnknownChar();
*p.arg1.getNullableChar(); // [[unsafe]]
*p.arg2.getNonnullChar();
*p.arg3.getUnknownLongLong();
*p.arg3.getNullableDouble(); // [[unsafe]]
*p.arg3.getNonnullFloat();
*p.arg3.getUnknownShort();
*p.arg3.getNullableUInt(); // [[unsafe]]
*p.arg3.getNullableBool(); // [[unsafe]]
*p.getT0().unknownChar;
*p.getT1().nullableChar; // [[unsafe]]
*p.getT2().nonnullChar;
*p.getT3().unknownLongLong;
*p.getT3().nullableDouble; // [[unsafe]]
*p.getT3().nonnullFloat;
*p.getT3().unknownShort;
*p.getT3().nullableUInt; // [[unsafe]]
*p.getT3().nullableBool; // [[unsafe]]
*p.getT0().getUnknownChar();
*p.getT1().getNullableChar(); // [[unsafe]]
*p.getT2().getNonnullChar();
*p.getT3().getUnknownLongLong();
*p.getT3().getNullableDouble(); // [[unsafe]]
*p.getT3().getNonnullFloat();
*p.getT3().getUnknownShort();
*p.getT3().getNullableUInt(); // [[unsafe]]
*p.getT3().getNullableBool(); // [[unsafe]]
}
)cc"));
// With const arguments and int template parameter.
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct1UnknownArg {
char *const constUnknownChar;
char const *unknownConstChar;
char const *const constUnknownConstChar;
char *const getConstUnknownChar();
char const *getUnknownConstChar();
char const *const getConstUnknownConstChar();
};
struct Struct1NullableArg {
char *const _Nullable constNullableChar;
char const *_Nullable nullableConstChar;
char const *const _Nullable constNullableConstChar;
char *const _Nullable getConstNullableChar();
char const *_Nullable getNullableConstChar();
char *const *_Nullable getConstNullableConstChar();
};
struct Struct1NonnullArg {
char *const _Nonnull constNonnullChar;
char const *_Nonnull nonnullConstChar;
char const *const _Nonnull constNonnullConstChar;
char *const _Nonnull getConstNonnullChar();
char const *_Nonnull getNonnullConstChar();
char const *const _Nonnull getConstNonnullConstChar();
};
template <int I0, typename T1, typename T2, typename T3>
struct Struct4Arg {
T1 arg1;
T2 arg2;
T3 arg3;
T1 getT1();
T2 getT2();
T3 getT3();
};
void target(
Struct4Arg<4, Struct1UnknownArg, Struct1NullableArg, Struct1NonnullArg>
p) {
*p.arg1.constUnknownChar;
*p.arg1.unknownConstChar;
*p.arg1.constUnknownConstChar;
*p.arg2.constNullableChar; // [[unsafe]]
*p.arg2.nullableConstChar; // [[unsafe]]
*p.arg2.constNullableConstChar; // [[unsafe]]
*p.arg3.constNonnullChar;
*p.arg3.nonnullConstChar;
*p.arg3.constNonnullConstChar;
*p.arg1.getConstUnknownChar();
*p.arg1.getUnknownConstChar();
*p.arg1.getConstUnknownConstChar();
*p.arg2.getConstNullableChar(); // [[unsafe]]
*p.arg2.getNullableConstChar(); // [[unsafe]]
*p.arg2.getConstNullableConstChar(); // [[unsafe]]
*p.arg3.getConstNonnullChar();
*p.arg3.getNonnullConstChar();
*p.arg3.getConstNonnullConstChar();
*p.getT1().constUnknownChar;
*p.getT1().unknownConstChar;
*p.getT1().constUnknownConstChar;
*p.getT2().constNullableChar; // [[unsafe]]
*p.getT2().nullableConstChar; // [[unsafe]]
*p.getT2().constNullableConstChar; // [[unsafe]]
*p.getT3().constNonnullChar;
*p.getT3().nonnullConstChar;
*p.getT3().constNonnullConstChar;
*p.getT1().getConstUnknownChar();
*p.getT1().getUnknownConstChar();
*p.getT1().getConstUnknownConstChar();
*p.getT2().getConstNullableChar(); // [[unsafe]]
*p.getT2().getNullableConstChar(); // [[unsafe]]
*p.getT2().getConstNullableConstChar(); // [[unsafe]]
*p.getT3().getConstNonnullChar();
*p.getT3().getNonnullConstChar();
*p.getT3().getConstNonnullConstChar();
}
)cc"));
}
TEST(PointerNullabilityTest, MemberFunctionTemplateOfConcreteStruct) {
EXPECT_TRUE(checkDiagnostics(R"cc(
struct S {
template <typename T0>
T0 getT0();
};
void target(S p) {
*p.getT0<int *>();
*p.getT0<int *_Nonnull>();
*p.getT0<int *_Nullable>(); // [[unsafe]]
*p.getT0<int const *>();
*p.getT0<int *const>();
*p.getT0<int const *const>();
*p.getT0<int const *_Nonnull>();
*p.getT0<int *const _Nonnull>();
*p.getT0<int const *const _Nonnull>();
*p.getT0<int const *_Nullable>(); // [[unsafe]]
*p.getT0<int *const _Nullable>(); // [[unsafe]]
*p.getT0<int const *const _Nullable>(); // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
struct S {
template <int I0, typename T1, int I2>
T1 getT1();
};
void target(S p) {
*p.getT1<0, int *, 1>();
*p.getT1<2147483647, int *_Nonnull, -2147483647>();
*p.getT1<4, int *_Nullable, 4>(); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, MemberFunctionTemplateOfTemplateStruct) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0>
struct S {
template <typename TN1>
TN1 getTN1();
};
void target(S<int> p) {
*p.getTN1<int *>();
*p.getTN1<int *_Nonnull>();
*p.getTN1<int *_Nullable>(); // [[unsafe]]
*p.getTN1<int const *>();
*p.getTN1<int *const>();
*p.getTN1<int const *const>();
*p.getTN1<int const *_Nonnull>();
*p.getTN1<int *const _Nonnull>();
*p.getTN1<int const *const _Nonnull>();
*p.getTN1<int const *_Nullable>(); // [[unsafe]]
*p.getTN1<int *const _Nullable>(); // [[unsafe]]
*p.getTN1<int const *const _Nullable>(); // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0>
struct S {
template <int IN1, typename TN2, int IN3>
TN2 getTN2();
};
void target(S<int> p) {
*p.getTN2<0, int *, 1>();
*p.getTN2<2147483647, int *_Nonnull, -2147483647>();
*p.getTN2<4, int *_Nullable, 4>(); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest,
ClassTemplateInstantiationWithTemplateStructsAsParameters) {
// Class template with another class template as parameter
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {
T0 arg0;
T1 arg1;
};
template <typename TN0, typename TN1>
struct Struct2ArgNested {
Struct2Arg<TN1, Struct2Arg<TN0, TN1>> *arg0;
Struct2Arg<TN1, Struct2Arg<TN0, TN1>> *_Nullable arg1;
};
void target(Struct2ArgNested<int *_Nonnull, double *_Nullable> p) {
*p.arg0;
*p.arg1; // [[unsafe]]
*p.arg0->arg0; // [[unsafe]]
*p.arg0->arg1.arg0;
*p.arg0->arg1.arg1; // [[unsafe]]
}
)cc"));
// Class template with itself as parameter
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {
T0 arg0;
T1 arg1;
};
void target(Struct2Arg<Struct2Arg<int *, int *_Nullable>, int *_Nonnull> p) {
*p.arg0.arg0;
*p.arg0.arg1; // [[unsafe]]
*p.arg1;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1, typename T2, typename T3, typename T4>
struct Struct5Arg {
T0 arg0;
T1 arg1;
T2 arg2;
T3 arg3;
T4 arg4;
};
void target(Struct5Arg<
Struct5Arg<Struct5Arg<Struct5Arg<int *_Nullable, int *_Nonnull,
float *_Nullable, int *,
double *_Nullable>,
int, int, int, int *_Nullable>,
int, int *_Nullable, int, int>,
int, int *_Nullable, int *_Nonnull, int>
p) {
*p.arg0.arg0.arg0.arg0; // [[unsafe]]
*p.arg0.arg0.arg0.arg1;
*p.arg0.arg0.arg0.arg2; // [[unsafe]]
*p.arg0.arg0.arg0.arg3;
*p.arg0.arg0.arg0.arg4; // [[unsafe]]
*p.arg0.arg0.arg4; // [[unsafe]]
*p.arg0.arg2; // [[unsafe]]
*p.arg2; // [[unsafe]]
*p.arg3;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, typename T2, typename T3, int I4,
typename T5, typename T6>
struct Struct7ArgWithInt {
T1 arg1;
T2 arg2;
T3 arg3;
T5 arg5;
T6 arg6;
};
void target(Struct7ArgWithInt<
0,
Struct7ArgWithInt<
2147483647,
Struct7ArgWithInt<
0,
Struct7ArgWithInt<-2147483647, int *_Nullable,
int *_Nonnull, float *_Nullable, 0,
int *, double *_Nullable>,
int, int, 1, int, int *_Nullable>,
int, int *_Nullable, 2147483647, int, int>,
int, int *_Nullable, 2, int *_Nonnull, int>
p) {
*p.arg1.arg1.arg1.arg1; // [[unsafe]]
*p.arg1.arg1.arg1.arg2;
*p.arg1.arg1.arg1.arg3; // [[unsafe]]
*p.arg1.arg1.arg1.arg5;
*p.arg1.arg1.arg1.arg6; // [[unsafe]]
*p.arg1.arg1.arg6; // [[unsafe]]
*p.arg1.arg3; // [[unsafe]]
*p.arg3; // [[unsafe]]
*p.arg5;
}
)cc"));
}
TEST(PointerNullabilityTest,
ClassTemplateInstantiationWithPointersToStructsAsParameters) {
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct3IntPtrs {
int *unknown;
int *_Nullable nullable;
int *_Nonnull nonnull;
int *getUnknown();
int *_Nullable getNullable();
int *_Nonnull getNonnull();
};
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 getT0();
};
void target(Struct1Arg<Struct3IntPtrs *> p) {
*p.arg0->unknown;
*p.arg0->nullable; // [[unsafe]]
*p.arg0->nonnull;
*p.arg0->getUnknown();
*p.arg0->getNullable(); // [[unsafe]]
*p.arg0->getNonnull();
*p.getT0()->unknown;
*p.getT0()->nullable; // [[unsafe]]
*p.getT0()->nonnull;
*p.getT0()->getUnknown();
*p.getT0()->getNullable(); // [[unsafe]]
*p.getT0()->getNonnull();
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct3IntPtrs {
int *unknown;
int *_Nullable nullable;
int *_Nonnull nonnull;
int *getUnknown();
int *_Nullable getNullable();
int *_Nonnull getNonnull();
};
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 getT0();
};
void target(Struct1Arg<Struct3IntPtrs *_Nullable> p) {
*p.arg0->unknown; // [[unsafe]]
*p.arg0->nullable; // [[unsafe]]
*p.arg0->nonnull; // [[unsafe]]
*p.arg0->getUnknown(); // [[unsafe]]
*p.arg0->getNullable(); // [[unsafe]]
*p.arg0->getNonnull(); // [[unsafe]]
*p.getT0()->unknown; // [[unsafe]]
*p.getT0()->nullable; // [[unsafe]]
*p.getT0()->nonnull; // [[unsafe]]
*p.getT0()->getUnknown(); // [[unsafe]]
*p.getT0()->getNullable(); // [[unsafe]]
*p.getT0()->getNonnull(); // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct3IntPtrs {
int *unknown;
int *_Nullable nullable;
int *_Nonnull nonnull;
int *getUnknown();
int *_Nullable getNullable();
int *_Nonnull getNonnull();
};
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 getT0();
};
void target(Struct1Arg<Struct3IntPtrs *_Nonnull> p) {
*p.arg0->unknown;
*p.arg0->nullable; // [[unsafe]]
*p.arg0->nonnull;
*p.arg0->getUnknown();
*p.arg0->getNullable(); // [[unsafe]]
*p.arg0->getNonnull();
*p.getT0()->unknown;
*p.getT0()->nullable; // [[unsafe]]
*p.getT0()->nonnull;
*p.getT0()->getUnknown();
*p.getT0()->getNullable(); // [[unsafe]]
*p.getT0()->getNonnull();
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Struct3IntPtrs {
int *unknown;
int *_Nullable nullable;
int *_Nonnull nonnull;
int *getUnknown();
int *_Nullable getNullable();
int *_Nonnull getNonnull();
};
template <int I0, typename T1>
struct Struct2Arg {
T1 arg1;
T1 getT1();
};
void target(Struct2Arg<0, Struct3IntPtrs *> p) {
*p.arg1->unknown;
*p.arg1->nullable; // [[unsafe]]
*p.arg1->nonnull;
*p.arg1->getUnknown();
*p.arg1->getNullable(); // [[unsafe]]
*p.arg1->getNonnull();
*p.getT1()->unknown;
*p.getT1()->nullable; // [[unsafe]]
*p.getT1()->nonnull;
*p.getT1()->getUnknown();
*p.getT1()->getNullable(); // [[unsafe]]
*p.getT1()->getNonnull();
}
)cc"));
}
TEST(PointerNullabilityTest,
ClassTemplateInstantiationWithPointersToTemplateStructsAsParameters) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {
T0 arg0;
T1 arg1;
T0 getT0();
T1 getT1();
};
void target(Struct2Arg<Struct2Arg<int *, int *_Nullable> *_Nullable,
Struct2Arg<int, int *> *_Nonnull>
p) {
*p.arg0; // [[unsafe]]
*p.arg0->arg0; // [[unsafe]]
*p.arg0->arg1; // [[unsafe]]
*p.arg1;
*p.arg1->arg1;
*p.arg0->getT0(); // [[unsafe]]
*p.arg0->getT1(); // [[unsafe]]
*p.arg1->getT1();
*p.getT0(); // [[unsafe]]
*p.getT0()->arg0; // [[unsafe]]
*p.getT0()->arg1; // [[unsafe]]
*p.getT1();
*p.getT1()->arg1;
*p.getT0()->getT0(); // [[unsafe]]
*p.getT0()->getT1(); // [[unsafe]]
*p.getT1()->getT1();
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct StructNonnullUnknown {
T0 nonnull;
T1 unknown;
T0 getNonnull();
T1 getUnknown();
};
template <typename T0, typename T1>
struct StructNonnullNullable {
T0 nonnull;
T1 nullable;
T0 getNonnull();
T1 getNullable();
};
template <typename T0, typename T1>
struct StructNullableNonnull {
T0 nullable;
T1 nonnull;
T0 getNullable();
T1 getNonnull();
};
template <typename T0, typename T1>
struct StructNullableNullable {
T0 nullable0;
T1 nullable1;
T0 getNullable0();
T1 getNullable1();
};
template <typename T0, typename T1>
struct StructNullableUnknown {
T0 nullable;
T1 unknown;
T0 getNullable();
T1 getUnknown();
};
template <typename T0, typename T1>
struct StructUnknownNullable {
T0 unknown;
T1 nullable;
T0 getUnknown();
T1 getNullable();
};
void
target(StructNonnullUnknown<
StructNonnullNullable<
StructNullableNullable<int *_Nullable, int *_Nullable> *_Nonnull,
StructUnknownNullable<int *, int *_Nullable> *_Nullable>
*_Nonnull,
StructUnknownNullable<
StructUnknownNullable<int *, int *_Nullable> *,
StructNullableNonnull<int *_Nullable, int *_Nonnull> *_Nullable>
*>
p) {
*p.nonnull;
*p.nonnull->nonnull;
*p.nonnull->nonnull->nullable0; // [[unsafe]]
*p.nonnull->nonnull->nullable1; // [[unsafe]]
*p.nonnull->nullable; // [[unsafe]]
*p.nonnull->nullable->unknown; // [[unsafe]]
*p.nonnull->nullable->nullable; // [[unsafe]]
*p.unknown->unknown;
*p.unknown->unknown->unknown;
*p.unknown->unknown->nullable; // [[unsafe]]
*p.unknown;
*p.unknown->nullable; // [[unsafe]]
*p.unknown->nullable->nullable; // [[unsafe]]
*p.unknown->nullable->nonnull; // [[unsafe]]
*p.nonnull->getNonnull();
*p.nonnull->getNonnull()->nullable0; // [[unsafe]]
*p.nonnull->getNonnull()->nullable1; // [[unsafe]]
*p.nonnull->getNullable(); // [[unsafe]]
*p.nonnull->getNullable()->unknown; // [[unsafe]]
*p.nonnull->getNullable()->nullable; // [[unsafe]]
*p.unknown->getUnknown();
*p.unknown->getUnknown()->unknown;
*p.unknown->getUnknown()->nullable; // [[unsafe]]
*p.unknown->getNullable(); // [[unsafe]]
*p.unknown->getNullable()->nullable; // [[unsafe]]
*p.unknown->getNullable()->nonnull; // [[unsafe]]
*p.nonnull->getNonnull()->getNullable0(); // [[unsafe]]
*p.nonnull->getNonnull()->getNullable1(); // [[unsafe]]
*p.nonnull->getNullable()->getUnknown(); // [[unsafe]]
*p.nonnull->getNullable()->getNullable(); // [[unsafe]]
*p.unknown->getUnknown()->getUnknown();
*p.unknown->getUnknown()->getNullable(); // [[unsafe]]
*p.unknown->getNullable()->getNullable(); // [[unsafe]]
*p.unknown->getNullable()->getNonnull(); // [[unsafe]]
*p.nonnull->nonnull->getNullable0(); // [[unsafe]]
*p.nonnull->nonnull->getNullable1(); // [[unsafe]]
*p.nonnull->nullable->getUnknown(); // [[unsafe]]
*p.nonnull->nullable->getNullable(); // [[unsafe]]
*p.unknown->unknown->getUnknown();
*p.unknown->unknown->getNullable(); // [[unsafe]]
*p.unknown->nullable->getNullable(); // [[unsafe]]
*p.unknown->nullable->getNonnull(); // [[unsafe]]
*p.getNonnull();
*p.getNonnull()->nonnull;
*p.getNonnull()->nonnull->nullable0; // [[unsafe]]
*p.getNonnull()->nonnull->nullable1; // [[unsafe]]
*p.getNonnull()->nullable; // [[unsafe]]
*p.getNonnull()->nullable->unknown; // [[unsafe]]
*p.getNonnull()->nullable->nullable; // [[unsafe]]
*p.getUnknown()->unknown;
*p.getUnknown()->unknown->unknown;
*p.getUnknown()->unknown->nullable; // [[unsafe]]
*p.getUnknown();
*p.getUnknown()->nullable; // [[unsafe]]
*p.getUnknown()->nullable->nullable; // [[unsafe]]
*p.getUnknown()->nullable->nonnull; // [[unsafe]]
*p.getNonnull()->getNonnull();
*p.getNonnull()->getNonnull()->nullable0; // [[unsafe]]
*p.getNonnull()->getNonnull()->nullable1; // [[unsafe]]
*p.getNonnull()->getNullable(); // [[unsafe]]
*p.getNonnull()->getNullable()->unknown; // [[unsafe]]
*p.getNonnull()->getNullable()->nullable; // [[unsafe]]
*p.getUnknown()->getUnknown();
*p.getUnknown()->getUnknown()->unknown;
*p.getUnknown()->getUnknown()->nullable; // [[unsafe]]
*p.getUnknown()->getNullable(); // [[unsafe]]
*p.getUnknown()->getNullable()->nullable; // [[unsafe]]
*p.getUnknown()->getNullable()->nonnull; // [[unsafe]]
*p.getNonnull()->nonnull->getNullable0(); // [[unsafe]]
*p.getNonnull()->nonnull->getNullable1(); // [[unsafe]]
*p.getNonnull()->nullable->getUnknown(); // [[unsafe]]
*p.getNonnull()->nullable->getNullable(); // [[unsafe]]
*p.getUnknown()->unknown->getUnknown();
*p.getUnknown()->unknown->getNullable(); // [[unsafe]]
*p.getUnknown()->nullable->getNullable(); // [[unsafe]]
*p.getUnknown()->nullable->getNonnull(); // [[unsafe]]
*p.getNonnull()->getNonnull()->getNullable0(); // [[unsafe]]
*p.getNonnull()->getNonnull()->getNullable1(); // [[unsafe]]
*p.getNonnull()->getNullable()->getUnknown(); // [[unsafe]]
*p.getNonnull()->getNullable()->getNullable(); // [[unsafe]]
*p.getUnknown()->getUnknown()->getUnknown();
*p.getUnknown()->getUnknown()->getNullable(); // [[unsafe]]
*p.getUnknown()->getNullable()->getNullable(); // [[unsafe]]
*p.getUnknown()->getNullable()->getNonnull(); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, FunctionTemplates) {
// Call expression that returns the first of two type parameters.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
T0 returnFirst();
void target() {
*returnFirst<int *_Nonnull, int *_Nullable>();
*returnFirst<int *, int *_Nullable>();
*returnFirst<int *_Nullable, int *_Nonnull>(); // [[unsafe]]
}
)cc"));
// Call expression that returns the second of two type parameters.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
T1 returnSecond();
void target() {
*returnSecond<int *_Nullable, int *_Nonnull>();
*returnSecond<int *_Nullable, int *>();
*returnSecond<int *, int *_Nullable>(); // [[unsafe]]
}
)cc"));
// Call expression that has an int parameter and two type parameters,
// returning the first type parameter.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, typename T2>
T1 fn3ArgWithInt();
void target() {
*fn3ArgWithInt<1, int *_Nullable, int *>(); // [[unsafe]]
*fn3ArgWithInt<1, int *, int *_Nullable>();
}
)cc"));
// Call expression with template parameter substituted with a concrete struct.
EXPECT_TRUE(checkDiagnostics(R"cc(
struct StructUnknownNullable {
int *var0;
int *_Nullable var1;
int *getVar0();
int *_Nullable getVar1();
};
template <typename T0, typename T1>
T1 returnSecond();
void target() {
*returnSecond<StructUnknownNullable, int *_Nullable>(); // [[unsafe]]
*returnSecond<int *_Nonnull, StructUnknownNullable *>();
*returnSecond<int *_Nonnull, StructUnknownNullable>().var0;
__assert_nullability<NK_unspecified>(
returnSecond<int *_Nonnull, StructUnknownNullable>().var0);
*returnSecond<int *_Nonnull, StructUnknownNullable>().var1; // [[unsafe]]
*returnSecond<int *_Nonnull, StructUnknownNullable>().getVar0();
*returnSecond<int *_Nonnull, StructUnknownNullable>() // [[unsafe]]
.getVar1();
}
)cc"));
}
TEST(PointerNullabilityTest, OperatorSubscript) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
struct Vec {
typedef T value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
reference operator[](int n);
reference at(int n);
void push_back(const_reference v);
};
void target(Vec<int* _Nullable> nullable, Vec<int* _Nonnull> nonnull) {
// read
*nullable[0]; // [[unsafe]]
*nullable.at(0); // [[unsafe]]
*nonnull[0];
*nonnull.at(0);
// write
nullable[0] = nullptr;
nullable.push_back(nullptr);
nonnull[0] = nullptr; // [[unsafe]]
nonnull.push_back(nullptr); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, OperatorSubscriptPointerIndexType) {
// Try a case where there is more than one pointer type in the method
// signature (e.g., the index is nonnull while the return type is nullable)
// to check we aren't mixing up the nullability vector slots.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
struct Vec {
typedef T value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
reference operator[](int* _Nonnull p);
reference at(int* _Nonnull p);
};
void target(Vec<int* _Nullable> nullable, Vec<int* _Nonnull> nonnull) {
int x;
// read
*nullable[&x]; // [[unsafe]]
*nullable.at(&x); // [[unsafe]]
*nonnull[&x];
*nonnull.at(&x);
// write
nullable[&x] = nullptr;
nonnull[&x] = nullptr; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, OperatorStarAndEquals) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
struct Optional {
using value_type = T;
const value_type& operator*() const&;
const value_type& value() const&;
Optional<T>& operator=(const value_type& v);
};
// Try a non-member operator* too, to make sure we don't crash.
template <typename T>
static Optional<T> operator*(const Optional<T>& L, const Optional<T>& R);
void target(Optional<int* _Nullable> nullable,
Optional<int* _Nonnull> nonnull) {
// read
*(*nullable); // [[unsafe]]
*(nullable.value()); // [[unsafe]]
*(*nonnull);
*(nonnull.value());
// write
nullable = nullptr;
// TODO(b/405355053): This should be unsafe but we are only getting
// the return type from resugaring member operator calls. We aren't yet
// get the argument nullability attached to the parameter of each
// instance yet (compare to `CallInstantiatedMember` test below for
// non-operator member functions).
nonnull = nullptr;
// non-member operator*
Optional<int* _Nullable> nullable2 = nullable * nullable;
Optional<int* _Nullable> nonnull2 = nonnull * nonnull;
}
)cc"));
}
TEST(PointerNullabilityTest, ParenTypeInTemplate1) {
checkDiagnostics(R"cc(
template <typename T>
struct S {
T(a);
T(*(b));
T (*f)();
T(((*g)))();
};
void target(S<int *_Nullable> s) {
*s.a; // [[unsafe]]
**s.b; // [[unsafe]]
*s.f;
*s.g;
*s.f(); // [[unsafe]]
*s.g(); // [[unsafe]]
}
)cc");
}
TEST(PointerNullabilityTest, ParenTypeInTemplate2) {
checkDiagnostics(R"cc(
template <typename T>
struct S {
T(a);
T(*(b));
T (*f)();
T(((*g)))();
};
void target(S<int *_Nonnull> s) {
*s.a;
**s.b;
*s.f;
*s.g;
*s.f();
*s.g();
}
)cc");
}
TEST(PointerNullabilityTest, ParenTypeInTemplate3) {
checkDiagnostics(R"cc(
template <typename T>
struct S {
T arg;
};
void target(S<int *_Nullable>(a), S<int *_Nullable>(*(b)),
S<int(*_Nullable)> c, S<int *(*(*_Nullable))> d,
S<int *(*_Nullable)()> e) {
*a.arg; // [[unsafe]]
*b->arg; // [[unsafe]]
*c.arg; // [[unsafe]]
***d.arg; // [[unsafe]]
*e.arg; // [[unsafe]]
*e.arg(); // [[unsafe]]
}
)cc");
}
TEST(PointerNullabilityTest, ParenTypeInTemplate4) {
checkDiagnostics(R"cc(
template <typename T>
struct S {
T arg;
};
void target(S<int *_Nonnull>(a), S<int *_Nonnull>(*(b)),
S<int(*_Nonnull)> c, S<int *(*(*_Nonnull))> d,
S<int *_Nonnull (*)()> e) {
*a.arg;
*b->arg;
*c.arg;
***d.arg;
*e.arg;
*e.arg();
}
)cc");
}
TEST(PointerNullabilityTest, PartialSpecialization) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int, class>
struct S;
template <class T>
struct S<0, T> {
using Alias = T;
};
template <int i, class T>
typename S<i, T>::Alias f(T);
void target(int *p) { (void *)f<0>(p); }
)cc"));
}
TEST(PointerNullabilityTest, ClassTemplateWithDefaultArgument) {
// Crash repro.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <class T1, class T2 = T1>
struct S {
public:
void f(S<T2>);
};
void target() {
S<int *_Nullable> s;
s.f(s);
}
)cc"));
}
TEST(PointerNullabilityTest, MethodOnPartialSpecialization) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <class T>
struct S {};
template <class T1, class T2>
struct pair {};
template <class T1, class T2>
struct S<pair<T1, T2>> {
T1 Foo(T1, T2);
};
void target(int* _Nonnull p1, char* _Nullable p2) {
S<pair<int* _Nonnull, char* _Nullable>> s;
// TODO: Should be NK_nonnull, but we don't treat partial specializations
// correctly yet.
__assert_nullability<NK_unspecified>(s.Foo(p1, p2));
}
)cc"));
}
TEST(PointerNullabilityTest, CallMethodTakingParameterPack) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <class... Ts>
struct S {
S<Ts...> Foo(Ts...);
};
void target(int* _Nonnull p1, char* _Nullable p2) {
S<int* _Nonnull, char* _Nullable> s;
// TODO: Should be [NK_nonnull, NK_nullable], but we don't treat parameter
// packs correctly yet.
__assert_nullability<NK_unspecified, NK_unspecified>(s.Foo(p1, p2));
}
)cc"));
}
TEST(PointerNullabilityTest, CallFunctionReturningTemplateSpecializationType) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <class T>
struct AliasWrapper {
using Type = T;
};
template <typename>
AliasWrapper<int* _Nonnull>::Type f();
void target() { __assert_nullability<NK_nonnull>(f<int>()); }
)cc"));
}
TEST(PointerNullabilityTest, CallInstantiatedMember) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
struct Sink {
void eat(T);
};
void target(Sink<int *_Nonnull> &S) {
S.eat(nullptr); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, CallInstantiatedFreeFunction) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
void eat(T p);
void target() {
eat<int *_Nullable>(nullptr);
eat<int *_Nonnull>(nullptr); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, CallAnnotatedTemplateParam) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
void eat(T *_Nonnull p);
void target(int *_Nullable p, int *_Nonnull q) {
eat(p); // [[unsafe]]
eat(q);
}
)cc"));
}
// Tests for when the "target" itself is a templated function.
// Case that is unsafe independent of the template arguments (already annotated)
TEST(PointerNullabilityTest, BodyIndependentOfTemplateArgsAnnotatedParam) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
void target(T *_Nullable p) {
T *v = p;
*v; // [[unsafe]]
}
void instantiation() { target<int>(nullptr); }
)cc"));
}
TEST(PointerNullabilityTest, BodyIndependentOfTemplateArgAnnotatedCall) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
T *_Nullable getNullable();
template <typename T>
void target() {
T *v = getNullable<T>();
*v; // [[unsafe]]
}
void instantiation() { target<int>(); }
)cc"));
}
// Case is unsafe that is partly dependent on the template arguments.
// - The *v is unsafe independently (and we flag it).
// - The T v = nullptr perhaps should be unsafe when T is _Nonnull but
// we currently do not get access to that annotation information.
TEST(PointerNullabilityTest, BodyPartlyDependentOnTemplateArgs) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
void target() {
T v = nullptr;
*v; // [[unsafe]]
}
void instantiation() { target<int *_Nonnull>(); }
)cc"));
}
// Case that is dependent on the template arguments.
TEST(PointerNullabilityTest, BodyDependentOnTemplateArgs) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T>
void target(T p) {
*p;
}
void instantiation() {
// This should make the *p above unsafe, but verification doesn't yet
// handle this (b/268347355).
target<int *_Nullable>(nullptr);
}
)cc"));
}
} // namespace
} // namespace clang::tidy::nullability