blob: 3b38d12969700be3a2032923ddfab2add6057cfb [file] [log] [blame]
// 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
#include <optional>
#include <set>
#include <string>
#include "nullability_verification/test/check_diagnostics.h"
#include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
namespace clang {
namespace tidy {
namespace nullability {
namespace {
TEST(PointerNullabilityTest, ReturnStatements) {
// nonnull return type
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull target() {
return nullptr; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull target(int* _Nonnull ptr_nonnull) {
return ptr_nonnull;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull target(int* _Nullable ptr_nullable) {
return ptr_nullable; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nonnull target(int *ptr_unannotated) {
return ptr_unannotated;
}
)cc"));
// nullable return type
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nullable target() { return nullptr; }
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nullable target(int* _Nonnull ptr_nonnull) {
return ptr_nonnull;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nullable target(int* _Nullable ptr_nullable) {
return ptr_nullable;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nullable target(int *ptr_unannotated) {
return ptr_unannotated;
}
)cc"));
// unannotated return type
EXPECT_TRUE(checkDiagnostics(R"cc(
int* target() { return nullptr; }
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* target(int* _Nonnull ptr_nonnull) {
return ptr_nonnull;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* target(int* _Nullable ptr_nullable) {
return ptr_nullable;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int *target(int *ptr_unannotated) {
return ptr_unannotated;
}
)cc"));
// multiple return statements
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull target(bool b, int* _Nonnull ptr_nonnull) {
if (b) {
return nullptr; // [[unsafe]]
}
return ptr_nonnull;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull target(int* _Nullable ptr_nullable,
int* _Nonnull ptr_nonnull) {
if (ptr_nullable) {
return ptr_nullable;
}
return ptr_nonnull;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull target(int* _Nullable ptr_nullable_1,
int* _Nullable ptr_nullable_2) {
if (ptr_nullable_1) {
return ptr_nullable_2; // [[unsafe]]
}
return ptr_nullable_1; // [[unsafe]]
}
)cc"));
// return result of merging 2 pointer values
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nonnull target(bool b, int i) {
int *ptr;
if (b) {
ptr = &i;
} else {
ptr = nullptr;
}
return ptr; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, ConstructExpr) {
// Constructor call assigned to local variable.
EXPECT_TRUE(checkDiagnostics(R"cc(
struct TakeNonnull {
explicit TakeNonnull(int *_Nonnull) {}
};
struct TakeNullable {
explicit TakeNullable(int *_Nullable) {}
};
struct TakeUnannotated {
explicit TakeUnannotated(int *) {}
};
int *_Nonnull makeNonnull();
int *_Nullable makeNullable();
int *makeUnannotated();
void target() {
auto NN1 = TakeNonnull(makeNonnull());
auto NN2 = TakeNonnull(makeNullable()); // [[unsafe]]
auto NN3 = TakeNonnull(makeUnannotated());
auto NB1 = TakeNullable(makeNonnull());
auto NB2 = TakeNullable(makeNullable());
auto NB3 = TakeNullable(makeUnannotated());
auto UN1 = TakeUnannotated(makeNonnull());
auto UN2 = TakeUnannotated(makeNullable());
auto UN3 = TakeUnannotated(makeUnannotated());
}
)cc"));
// Constructor call in a base initializer.
EXPECT_TRUE(checkDiagnostics(R"cc(
struct TakeNonnull {
explicit TakeNonnull(int* _Nonnull);
};
struct target : TakeNonnull {
target(int* _Nullable ptr_nullable) // Forced line break.
: TakeNonnull(ptr_nullable) // [[unsafe]]
{}
};
)cc"));
// Call to a delegating constructor.
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nullable makeNullable();
struct target {
target(int* _Nonnull);
target() // Forced line break.
: target(makeNullable()) // [[unsafe]]
{}
};
)cc"));
}
TEST(PointerNullabilityTest, ConstructorMemberInitializer) {
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nullable makeNullable();
struct target {
int* _Nonnull ptr_nonnull;
int* _Nullable ptr_nullable;
int* ptr_unannotated;
target()
: ptr_nonnull(makeNullable()), // [[unsafe]]
ptr_nullable(makeNullable()),
ptr_unannotated(makeNullable()) {}
};
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int* _Nonnull makeNonnull();
struct target {
int* _Nonnull ptr_nonnull;
int* _Nullable ptr_nullable;
int* ptr_unannotated;
target()
: ptr_nonnull(makeNonnull()),
ptr_nullable(makeNonnull()),
ptr_unannotated(makeNonnull()) {}
};
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
int *makeUnannotated();
struct target {
int *_Nonnull ptr_nonnull;
int *_Nullable ptr_nullable;
int *ptr_unannotated;
target()
: ptr_nonnull(makeUnannotated()),
ptr_nullable(makeUnannotated()),
ptr_unannotated(makeUnannotated()) {}
};
)cc"));
}
TEST(PointerNullabilityTest, AssertNullability) {
// Concrete struct.
EXPECT_TRUE(checkDiagnostics(R"cc(
struct StructNonnullNullable {
int* _Nonnull nonnull;
int* _Nullable nullable;
};
void target(StructNonnullNullable p) {
__assert_nullability<>(p);
__assert_nullability<NK_nonnull>(p); // [[unsafe]]
__assert_nullability<NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_nonnull, NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_nonnull, NK_unspecified>(p); // [[unsafe]]
__assert_nullability<NK_nonnull, NK_nonnull>(p); // [[unsafe]]
__assert_nullability<NK_nullable, NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_unspecified, NK_nullable>(p); // [[unsafe]]
}
)cc"));
// Struct with two template type parameters.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {};
void target(Struct2Arg<int *, int *_Nullable> p) {
__assert_nullability<NK_unspecified>(p); // [[unsafe]]
__assert_nullability<NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_unspecified, NK_nonnull>(p); // [[unsafe]]
__assert_nullability<NK_unspecified, NK_nullable>(p);
__assert_nullability<NK_unspecified, NK_unspecified>(p); // [[unsafe]]
__assert_nullability<NK_nonnull, NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_nullable, NK_nullable>(p); // [[unsafe]]
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_unspecified>(p);
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nullable>(p);
}
)cc"));
// Struct with one type and non-type template parameters.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, typename T2>
struct Struct3ArgWithInt {};
void target(Struct3ArgWithInt<2147483647, int* _Nullable, int* _Nonnull> p) {
__assert_nullability<>(p); // [[unsafe]]
__assert_nullability<NK_nonnull>(p); // [[unsafe]]
__assert_nullability<NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_unspecified, NK_nonnull>(p); // [[unsafe]]
__assert_nullability<NK_nonnull, NK_nonnull>(p); // [[unsafe]]
__assert_nullability<NK_nonnull, NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_nullable, NK_nonnull>(p);
__assert_nullability<NK_nullable, NK_nullable>(p); // [[unsafe]]
__assert_nullability<NK_nullable, NK_unspecified>(p); // [[unsafe]]
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nonnull>(p);
}
)cc"));
// Nested template arguments.
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {};
void target(
Struct2Arg<Struct2Arg<int *, int *_Nullable>,
Struct2Arg<Struct2Arg<int *_Nullable, int *_Nonnull>,
Struct2Arg<int *_Nullable, int *_Nullable>>>
p) {
__assert_nullability<>(p); // [[unsafe]]
__assert_nullability<NK_unspecified, NK_nullable, NK_nullable, NK_nonnull,
NK_nullable, NK_nullable>(p);
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nullable, NK_nonnull, NK_nullable,
NK_nullable, NK_nullable>(p);
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nullable, NK_nonnull, NK_nullable>(
p);
}
)cc"));
// Struct with two template parameters substituted with concrete structs.
EXPECT_TRUE(checkDiagnostics(R"cc(
struct StructUnknownNullable {
int* unknown;
int* _Nullable nullable;
};
struct StructNullableNonnull {
int* _Nullable nullable;
int* _Nonnull nonnull;
};
template <typename T1, typename T2>
struct Struct2Arg {};
void target(Struct2Arg<StructUnknownNullable, StructNullableNonnull> p) {
__assert_nullability<>(p);
__assert_nullability<NK_unspecified, NK_nullable>(p); // [[unsafe]]
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nullable>(p);
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nullable, NK_nonnull>(p);
__assert_nullability // [[unsafe]]
<NK_nonnull, NK_nullable, NK_nullable, NK_nonnull>(p);
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nullable, NK_nonnull,
NK_unspecified>(p);
}
)cc"));
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>,
Struct2Arg<Struct2Arg<int *_Nullable, int *_Nonnull>,
Struct2Arg<int *_Nullable, int *_Nullable>>>
p) {
__assert_nullability<NK_unspecified, NK_nullable, NK_nullable, NK_nonnull,
NK_nullable, NK_nullable>(p);
__assert_nullability<NK_unspecified, NK_nullable>(p.arg0);
__assert_nullability<NK_unspecified>(p.arg0.arg0);
__assert_nullability<NK_nullable>(p.arg0.arg1);
__assert_nullability<NK_nullable, NK_nonnull, NK_nullable, NK_nullable>(
p.arg1);
__assert_nullability<NK_nullable, NK_nonnull>(p.arg1.arg0);
__assert_nullability<NK_nullable>(p.arg1.arg0.arg0);
__assert_nullability<NK_nonnull>(p.arg1.arg0.arg1);
__assert_nullability<NK_nullable, NK_nullable>(p.arg1.arg1);
__assert_nullability<NK_nullable>(p.arg1.arg1.arg0);
__assert_nullability<NK_nullable>(p.arg1.arg1.arg1);
__assert_nullability<>(p.arg0.arg0); // [[unsafe]]
__assert_nullability<NK_unspecified>(p.arg0); // [[unsafe]]
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_nonnull, NK_nullable, NK_nullable>(
p.arg1);
__assert_nullability<NK_unspecified, NK_nullable>(p.getT0());
__assert_nullability<NK_nonnull>(p.getT1().getT0().getT1());
__assert_nullability // [[unsafe]]
<NK_unspecified, NK_nullable, NK_unspecified>(p.getT0());
__assert_nullability // [[unsafe]]
<NK_unspecified>(p.getT0());
__assert_nullability<NK_nonnull>(p.getT1().arg0.getT1());
__assert_nullability<NK_nonnull>(p.arg1.getT0().arg1);
__assert_nullability<NK_nonnull>(p.arg1.arg0.arg1);
__assert_nullability // [[unsafe]]
<>(p.getT1().getT0().getT1());
__assert_nullability // [[unsafe]]
<NK_nonnull, NK_nonnull>(p.arg1.getT0().arg1);
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable p, int* _Nonnull q, int* r) {
__assert_nullability<NK_nonnull, NK_nullable>(&p);
__assert_nullability<NK_nonnull, NK_nonnull>(&q);
__assert_nullability<NK_nonnull>(&*p); // [[unsafe]]
__assert_nullability<NK_nonnull>(&*q);
__assert_nullability<NK_nonnull>(&*r);
}
)cc"));
}
TEST(PointerNullabilityTest, CastExpression) {
// TODO: We currently do not warn on local variables
// whose annotations conflict with the initializer. Decide whether to do so,
// and then treat static casts in an equivalent manner.
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable p) {
static_cast<int *_Nonnull>(p); // TODO: To warn, or not to warn, that is
// the question.
static_cast<int *>(p);
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, typename T2>
struct Struct3Arg {
T1 arg1;
T2 arg2;
};
void target(Struct3Arg<1, int *_Nullable, int *> &p) {
*static_cast<const Struct3Arg<1, int *, int *> &>(p).arg1; // [[unsafe]]
*static_cast<const Struct3Arg<1, int *, int *> &>(p).arg2;
*static_cast<int *>(p.arg1); // [[unsafe]]
*static_cast<int *>(p.arg2);
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Base {};
struct Derived : public Base {};
void target(Derived *_Nullable x, Derived *_Nonnull y) {
*static_cast<Base *>(x); // [[unsafe]]
*static_cast<Base *>(y); // [[unsafe]] TODO: Fix false positive.
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, typename T2>
struct Struct3Arg {
T1 arg1;
T2 arg2;
};
void target(Struct3Arg<1, int *_Nullable, int *> &p) {
*((const Struct3Arg<1, int *, int *> &)p).arg1; // [[unsafe]]
*((const Struct3Arg<1, int *, int *> &)p) // [[unsafe]]
.arg2; // TODO: Fix false positive.
*(int *)p.arg1; // [[unsafe]]
*(int *)p.arg2; // [[unsafe]] TODO: Fix false positive.
*(float *)p.arg1; // [[unsafe]]
*(char *)p.arg2; // [[unsafe]] TODO: Fix false positive.
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {
T0 arg0;
T1 arg1;
};
void target(Struct2Arg<const int *, const int *_Nullable> &p) {
*const_cast<int *>(p.arg0); // [[unsafe]] TODO: Fix false positive.
*const_cast<int *>(p.arg1); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, NonFlowSensitiveMaterializeTemporaryExpr) {
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nonnull makeNonnull();
int *_Nullable makeNullable();
int *makeUnannotated();
template <typename T>
T identity(const T &);
void target() {
{
*identity<int *_Nonnull>(makeNonnull());
int *const &p = makeNonnull();
*p;
}
{
*identity<int *_Nullable>(makeNullable()); // [[unsafe]]
int *const &p = makeNullable();
*p; // [[unsafe]]
}
{
*identity<int *>(makeUnannotated());
int *const &p = makeUnannotated();
*p;
}
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0, typename T1>
struct Struct2Arg {
T0 getT0();
T1 getT1();
};
template <typename T>
T make();
template <typename T>
T identity(const T &);
void target(Struct2Arg<int *, int *_Nullable> &p) {
*identity<Struct2Arg<int *, int *_Nullable>>(p).getT0();
*identity<Struct2Arg<int *, int *_Nullable>>(
make<Struct2Arg<int *, int *_Nullable>>())
.getT0();
*identity<Struct2Arg<int *, int *_Nullable>>(
Struct2Arg<int *, int *_Nullable>(p))
.getT0();
*identity<int *>(p.getT0());
*identity<Struct2Arg<int *, int *_Nullable>>(p).getT1(); // [[unsafe]]
*identity<Struct2Arg<int *, int *_Nullable>>( // [[unsafe]]
make<Struct2Arg<int *, int *_Nullable>>())
.getT1();
*identity<Struct2Arg<int *, int *_Nullable>>( // [[unsafe]]
Struct2Arg<int *, int *_Nullable>(p))
.getT1();
*identity<int *_Nullable>(p.getT1()); // [[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 *>();
// TODO: The following line is a false positive. We correctly compute the
// nullability of the expression, as confirmed by the call to
// `assert_nullability`. However, the dataflow framework currently does
// not model pointer values for this expression, which results in a (in
// this case incorrect) nullptr value.
*returnSecond<int *_Nonnull, StructUnknownNullable>() // [[unsafe]]
.var0; // TODO: Fix false positive.
__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, ParenthesizedExpressions) {
EXPECT_TRUE(checkDiagnostics(R"cc(
template <typename T0>
struct Struct1Arg {
T0 arg0;
T0 getT0();
};
void target(Struct1Arg<int *_Nullable> p) {
*(p).arg0; // [[unsafe]]
*((p)).arg0; // [[unsafe]]
*(p).getT0(); // [[unsafe]]
*(((p))).getT0(); // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
template <int I0, typename T1, typename T2>
struct Struct3ArgWithInt {
T1 arg1;
T2 arg2;
T1 getT1();
T2 getT2();
};
void target(Struct3ArgWithInt<1, int *, int *_Nullable> p) {
*(((p)).arg1);
*(((p))).getT1();
(*((p)).arg2); // [[unsafe]]
*(((((p)))).getT2()); // [[unsafe]]
}
)cc"));
}
// TODO: fix false positives due to unsupported PointerValues in the framework.
TEST(PointerNullabilityTest, PointerArithmetic) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable p, int *_Nonnull q, int *r) {
*++p; // [[unsafe]]
*p++; // [[unsafe]]
*--p; // [[unsafe]]
*p--; // [[unsafe]]
*+p; // [[unsafe]]
*++q; // [[unsafe]] TODO: fix false positive
*q++; // [[unsafe]] TODO: fix false positive
*--q; // [[unsafe]] TODO: fix false positive
*q--; // [[unsafe]] TODO: fix false positive
*+q; // [[unsafe]] TODO: fix false positive
*++r; // [[unsafe]] TODO: fix false positive
*r++; // [[unsafe]] TODO: fix false positive
*--r; // [[unsafe]] TODO: fix false positive
*r--; // [[unsafe]] TODO: fix false positive
*+r; // [[unsafe]] TODO: fix false positive
}
)cc"));
}
// TODO: fix false positives due to unsupported PointerValues in the framework.
TEST(PointerNullabilityTest, Deref) {
EXPECT_TRUE(checkDiagnostics(R"cc(
struct S {
S* _Nonnull nonnull;
S* _Nullable nullable;
S* unknown;
};
void target(S& s) {
*(*s.nonnull).nonnull; // [[unsafe]] TODO: fix false positive
*(*s.nonnull).nullable; // [[unsafe]]
*(*s.nonnull).unknown; // [[unsafe]] TODO: fix false positive
s.nonnull->nonnull->nonnull; // [[unsafe]] TODO: fix false positive
s.nonnull->nonnull->nullable; // [[unsafe]] TODO: fix false positive
s.nonnull->nullable->nonnull; // [[unsafe]]
s.nonnull->unknown->nonnull; // [[unsafe]] TODO: fix false positive
*&s;
}
)cc"));
}
TEST(PointerNullabilityTest, NonPointerReturnType) {
checkDiagnostics(R"cc(
struct S {
int* p;
int*& target() { return p; }
};
)cc");
checkDiagnostics(R"cc(
struct S {
int* _Nullable p;
int* _Nonnull& target() {
return p; // TODO: Fix false negative.
}
};
)cc");
}
TEST(PointerNullabilityTest, ParenTypeInTemplate) {
checkDiagnostics(R"cc(
template <typename T>
struct S {
T(a);
T(*(b));
T (*f)();
T(((*g)))();
};
void targetNullable(S<int* _Nullable> s) {
*s.a; // [[unsafe]]
**s.b; // [[unsafe]]
*s.f;
*s.g;
// TODO: Handle function pointers. The analysis currently crashes.
// *s.f();
// *s.g();
}
void targetNonnull(S<int* _Nonnull> s) {
*s.a;
**s.b;
*s.f;
*s.g;
// TODO: Handle function pointers. The analysis currently crashes.
// *s.f();
// *s.g();
}
)cc");
checkDiagnostics(R"cc(
template <typename T>
struct S {
T arg;
};
void targetNullable(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]]
// TODO: Handle function pointers. The analysis currently crashes.
// *e.arg();
}
void targetNonnull(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;
// TODO: Handle function pointers. The analysis currently crashes.
// *e.arg();
}
)cc");
}
} // namespace
} // namespace nullability
} // namespace tidy
} // namespace clang