blob: fa7dda1288c7f7a3a6f3ac7223d6f3b8d937ec1f [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 diagnosis seeing through forwarding functions to the underlying
// constructor.
#include "nullability/test/check_diagnostics.h"
#include "clang/Testing/CommandLineArgs.h"
#include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
namespace clang::tidy::nullability {
namespace {
TEST(PointerNullabilityTest, ConstructorThroughMakeUnique) {
EXPECT_TRUE(checkDiagnostics(R"cc(
#include <memory>
struct S {
// Check that we find the right constructor overload amongst several.
S() = default;
explicit S(int *_Nonnull p0);
S(int x, int *_Nullable p0);
S(int x, int *_Nullable p0, int *_Nonnull p1);
};
void target(int x, int *_Nullable p, int *_Nonnull q,
int *_Nullable const cp, int *_Nonnull const cq) {
if (x == 0) {
std::make_unique<S>(p); // [[unsafe]]
} else if (x == 1) {
std::make_unique<S>(q);
} else if (x == 2) {
std::make_unique<S>(x, p);
} else if (x == 3) {
std::make_unique<S>(x, p, q);
} else if (x == 4) {
std::make_unique<S>(x, q, p); // [[unsafe]]
} else if (x == 5) {
std::make_unique<S>(x, cq, cp); // [[unsafe]]
std::make_unique<S>(x, cq, cp); // [[unsafe]]
} else if (x == 6) {
// Test with nullptr literal, which results in a make_unique
// instantiation with a parameter of type nullptr_t (which isn't
// considered a PointerValue)
std::make_unique<S>(x, cq, nullptr); // [[unsafe]]
} else if (x == 7) {
// Also test uninteresting constructors (e.g., the 0-arg one)
std::make_unique<S[]>(10);
} else {
std::make_unique<double>(1.0);
}
}
)cc"));
}
TEST(PointerNullabilityTest, TemplateConstructorThroughMakeUnique) {
EXPECT_TRUE(checkDiagnostics(R"cc(
#include <memory>
template <typename T, typename U, typename V>
struct S {
S() = default;
explicit S(T *_Nonnull p0);
S(T x, U *_Nullable p0, V *_Nonnull p1);
};
void target(int x, double *_Nullable p, double *_Nonnull q) {
if (x == 0) {
std::make_unique<S<double, char, int>>(p); // [[unsafe]]
std::make_unique<S<double, char, int>>(q);
} else if (x == 1) {
std::make_unique<S<int, double, double>>(x, p, q);
} else if (x == 2) {
std::make_unique<S<int, double, double>>(x, q, p); // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, ConstructorWithSafeDefaultArg) {
EXPECT_TRUE(checkDiagnostics(R"cc(
#include <memory>
struct S {
S(int *_Nonnull p0, int *_Nullable p1 = kDefault);
static int *_Nullable kDefault;
};
int *_Nullable S::kDefault = nullptr;
// Case where the non-default arg for p0 is unsafe, but the default arg for
// p1 is safe.
void target(int x, int *_Nullable p) {
if (x == 0) {
new S(p, p); // [[unsafe]]
} else if (x == 1) {
new S(p); // [[unsafe]]
} else if (x == 2) {
std::make_unique<S>(p, p); // [[unsafe]]
} else {
std::make_unique<S>(p); // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, InitListInsteadOfConstructor) {
// Test when a struct doesn't have a constructor, so make_unique's `new
// S2(...)` will be an `CXXParenListInitExpr` instead.
// This is a C++20 (and onward) feature.
EXPECT_TRUE(checkDiagnosticsWithMin(
// force indenting
R"cc(
#include <memory>
// A struct to test implicit conversion from int.
struct S1 {
S1() = default;
S1(int x) : x(x) {}
int x = 0;
};
struct S2 {
S1 s1a;
int *_Nullable p;
int *_Nonnull q;
int x;
int y = -1;
int z = {};
bool b = z;
S1 s1b;
};
void target(int x, int *_Nullable p) {
if (x == 0) {
new S2{x, new int, p, x, 0, 1, false, S1{x}}; // [[unsafe]]
} else if (x == 1) {
// Try `x` vs `S1{x}` to test implicit conversion.
// That will make an unrelated CXXConstructExpr within the body of
// make_unique<S2> to set up the argument for `s1a`.
std::make_unique<S2>(x, new int, p, x, 0, 1, false, S1{x}); // [[unsafe]]
} else if (x == 2) {
// Test fewer arguments than fields.
std::make_unique<S2>(x, p, p); // [[unsafe]]
} else if (x == 3) {
// Test nullptr literal.
std::make_unique<S2>(x, p, nullptr); // [[unsafe]]
}
}
)cc",
TestLanguage::Lang_CXX20));
}
TEST(PointerNullabilityTest, InitListInsteadOfConstructorWithBaseClass) {
EXPECT_TRUE(checkDiagnosticsWithMin(
R"cc(
#include <memory>
struct Base {
int *_Nonnull a;
int *_Nullable b;
};
struct S : public Base {
int *_Nullable p;
int *_Nonnull q;
};
void target(int x, int *_Nullable null, int *_Nonnull nonnull) {
if (x == 0) {
// Brace aggregation initialization
S{nonnull, null, nonnull, null}; // [[unsafe]]
} else if (x == 1) {
// make_unique cases
std::make_unique<S>(Base(nonnull, null), nonnull, null); // [[unsafe]]
} else if (x == 2) {
// Test nullptr literal
std::make_unique<S>(Base(nonnull, null), nonnull, nullptr); // [[unsafe]]
}
}
)cc",
TestLanguage::Lang_CXX20));
}
TEST(PointerNullabilityTest, MakeUniqueUserDefConversionNothingToForward) {
EXPECT_TRUE(checkDiagnostics(
R"cc(
#include <memory>
struct Foo {
int *_Nonnull p;
};
struct Bar {
int *_Nonnull q;
operator Foo() { return Foo{q}; }
};
// Nothing to forward and diagnose -- the make_unique just calls the
// user-defined-conversion operator Foo() with no arguments.
void target(Bar b) { std::make_unique<Foo>(b); }
)cc"));
}
} // namespace
} // namespace clang::tidy::nullability