blob: 9d563bf4049d666019d63fe55d74efa7e072723b [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/pointer_nullability_analysis.h"
#include "nullability_verification/pointer_nullability_diagnosis.h"
#include "clang/Basic/SourceManager.h"
#include "third_party/llvm/llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Testing/Support/Error.h"
#include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
namespace clang {
namespace tidy {
namespace nullability {
namespace {
using dataflow::Environment;
using dataflow::TransferStateForDiagnostics;
using dataflow::test::AnalysisInputs;
using dataflow::test::AnalysisOutputs;
using dataflow::test::checkDataflow;
using ::testing::ContainerEq;
using ::testing::Test;
bool checkDiagnostics(llvm::StringRef SourceCode) {
std::vector<CFGElement> Diagnostics;
PointerNullabilityDiagnoser Diagnoser;
bool Failed = false;
EXPECT_THAT_ERROR(
checkDataflow<PointerNullabilityAnalysis>(
AnalysisInputs<PointerNullabilityAnalysis>(
SourceCode, ast_matchers::hasName("target"),
[](ASTContext &ASTCtx, Environment &) {
return PointerNullabilityAnalysis(ASTCtx);
})
.withPostVisitCFG([&Diagnostics, &Diagnoser](
ASTContext &Ctx, const CFGElement &Elt,
const TransferStateForDiagnostics<
PointerNullabilityLattice> &State) {
auto EltDiagnostics = Diagnoser.diagnose(&Elt, Ctx, State);
if (EltDiagnostics.has_value()) {
Diagnostics.push_back(EltDiagnostics.value());
}
})
.withASTBuildArgs({"-fsyntax-only", "-std=c++17",
"-Wno-unused-value", "-Wno-nonnull"}),
[&Diagnostics, &Failed](
const llvm::DenseMap<unsigned, std::string> &Annotations,
const AnalysisOutputs &AnalysisData) {
// Note: use sorted sets for expected and actual lines to improve
// readability of the error output in case the test fails.
std::set<unsigned> ExpectedLines, ActualLines;
for (const auto &[Line, _] : Annotations) {
ExpectedLines.insert(Line);
}
auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
for (auto Element : Diagnostics) {
if (std::optional<CFGStmt> stmt = Element.getAs<CFGStmt>()) {
ActualLines.insert(SrcMgr.getPresumedLineNumber(
stmt->getStmt()->getBeginLoc()));
} else if (std::optional<CFGInitializer> init =
Element.getAs<CFGInitializer>()) {
ActualLines.insert(SrcMgr.getPresumedLineNumber(
init->getInitializer()->getSourceLocation()));
} else {
ADD_FAILURE() << "this code should not be reached";
}
}
EXPECT_THAT(ActualLines, ContainerEq(ExpectedLines));
if (ActualLines != ExpectedLines) {
Failed = true;
}
}),
llvm::Succeeded());
return !Failed;
}
TEST(PointerNullabilityTest, NoPointerOperations) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target() { 1 + 2; }
)cc"));
}
TEST(PointerNullabilityTest, DerefNullPtr) {
// nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target() {
int *x = nullptr;
*x; // [[unsafe]]
}
)cc"));
// 0
EXPECT_TRUE(checkDiagnostics(R"cc(
void target() {
int *x = 0;
*x; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, DerefAddrOf) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target() {
int i;
int *x = &i;
*x;
}
)cc"));
// transitive
EXPECT_TRUE(checkDiagnostics(R"cc(
void target() {
int i;
int *x = &i;
int *y = x;
*y;
}
)cc"));
}
TEST(PointerNullabilityTest, DerefPtrAnnotatedNonNullWithoutACheck) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) { *x; }
)cc"));
// transitive
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nonnull x) {
int *y = x;
*y;
}
)cc"));
}
TEST(PointerNullabilityTest, DerefPtrAnnotatedNullableWithoutACheck) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
}
)cc"));
// transitive
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x) {
int *y = x;
*y; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, DerefUnknownPtrWithoutACheck) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) { *x; }
)cc"));
// transitive
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
int *y = x;
*y;
}
)cc"));
}
// TODO(b/233582219): Implement diagnosis of unreachable program points
TEST(PointerNullabilityTest, NonNullPtrImplicitCastToBool) {
// x
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) {
*x;
if (x) {
*x;
} else {
*x; // unreachable
}
*x;
}
)cc"));
// !x
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) {
*x;
if (!x) {
*x; // unreachable
} else {
*x;
}
*x;
}
)cc"));
}
TEST(PointerNullabilityTest, NullablePtrImplicitCastToBool) {
// x
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
if (x) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)cc"));
// !x
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
if (!x) {
*x; // [[unsafe]]
} else {
*x;
}
*x; // [[unsafe]]
}
)cc"));
}
// TODO(b/233582219): Fix false negatives. Casting the pointer to boolean is
// evidence of the author considering null a possibility, hence the unnannotated
// pointer should be considered nullable and emit warnings where it fails or is
// not null checked.
TEST(PointerNullabilityTest, UnknownPtrImplicitCastToBool) {
// x
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
*x; // false-negative
if (x) {
*x;
} else {
*x; // false-negative
}
*x; // false-negative
}
)cc"));
// !x
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
*x; // false-negative
if (!x) {
*x; // false-negative
} else {
*x;
}
*x; // false-negative
}
)cc"));
}
TEST(PointerNullabilityTest, CompareNonNullPtrAndNonNullPtr) {
// nonnull == nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x, int* _Nonnull y) {
*x;
*y;
if (x == y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
// nonnull != nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x, int* _Nonnull y) {
*x;
*y;
if (x != y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
}
TEST(PointerNullabilityTest, CompareNullablePtrAndNullablePtr) {
// nullable == nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nullable y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
if (x == y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
} else {
*x; // [[unsafe]]
*y; // [[unsafe]]
}
*x; // [[unsafe]]
*y; // [[unsafe]]
}
)cc"));
// nullable != nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nullable y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
if (x != y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
} else {
*x; // [[unsafe]]
*y; // [[unsafe]]
}
*x; // [[unsafe]]
*y; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, CompareUnknownPtrAndUnknownPtr) {
// unknown == unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x, int *y) {
*x;
*y;
if (x == y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
// unknown != unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x, int *y) {
*x;
*y;
if (x != y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
}
// TODO(b/233582219): Implement diagnosis of unreachable program points
TEST(PointerNullabilityTest, CompareNonNullPtrAndNullPtr) {
// nonnull == nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) {
*x;
if (x == nullptr) {
*x; // unreachable
} else {
*x;
}
*x;
}
)cc"));
// nullptr == nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) {
*x;
if (nullptr == x) {
*x; // unreachable
} else {
*x;
}
*x;
}
)cc"));
// nonnull != nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) {
*x;
if (x != nullptr) {
*x;
} else {
*x; // unreachable
}
*x;
}
)cc"));
// nullptr != nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull x) {
*x;
if (nullptr != x) {
*x;
} else {
*x; // unreachable
}
*x;
}
)cc"));
}
TEST(PointerNullabilityTest, CompareNullablePtrAndNullPtr) {
// nullable == nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
if (x == nullptr) {
*x; // [[unsafe]]
} else {
*x;
}
*x; // [[unsafe]]
}
)cc"));
// nullptr == nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
if (nullptr == x) {
*x; // [[unsafe]]
} else {
*x;
}
*x; // [[unsafe]]
}
)cc"));
// nullable != nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
if (x != nullptr) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)cc"));
// nullptr != nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x) {
*x; // [[unsafe]]
if (nullptr != x) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, CompareNullablePtrAndNonNullPtr) {
// nullable == nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nonnull y) {
*x; // [[unsafe]]
*y;
if (x == y) {
*x;
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
// nonnull == nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nonnull y) {
*x; // [[unsafe]]
*y;
if (y == x) {
*x;
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
// nullable != nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nonnull y) {
*x; // [[unsafe]]
*y;
if (x != y) {
*x; // [[unsafe]]
*y;
} else {
*x;
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
// nonnull != nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nonnull y) {
*x; // [[unsafe]]
*y;
if (y != x) {
*x; // [[unsafe]]
*y;
} else {
*x;
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
}
TEST(PointerNullabilityTest, CompareNullablePtrAndUnknownPtr) {
// nullable == unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (x == y) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
// unknown == nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (y == x) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
// nullable != unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (x != y) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
// unknown != nullable
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (y != x) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)cc"));
}
// TODO(b/233582219): Fix false negatives. The pointer is compared to nullptr,
// hence the unnannotated pointer should be considered nullable and emit
// warnings where it fails or is not null checked.
TEST(PointerNullabilityTest, CompareUnknownPtrAndNullPtr) {
// unknown == nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
*x; // false-negative
if (x == nullptr) {
*x; // false-negative
} else {
*x;
}
*x; // false-negative
}
)cc"));
// nullptr == unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
*x; // false-negative
if (nullptr == x) {
*x; // false-negative
} else {
*x;
}
*x; // false-negative
}
)cc"));
// unknown != nullptr
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
*x; // false-negative
if (x != nullptr) {
*x;
} else {
*x; // false-negative
}
*x; // false-negative
}
)cc"));
// nullptr != unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x) {
*x; // false-negative
if (nullptr != x) {
*x;
} else {
*x; // false-negative
}
*x; // false-negative
}
)cc"));
}
TEST(PointerNullabilityTest, CompareUnknownPtrAndNonNullPtr) {
// unknown == nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x, int *_Nonnull y) {
*x;
*y;
if (x == y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
// nonnull == unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x, int *_Nonnull y) {
*x;
*y;
if (y == x) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
// unknown != nonnull
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x, int *_Nonnull y) {
*x;
*y;
if (x != y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
// nonnull != unknown
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *x, int *_Nonnull y) {
*x;
*y;
if (y != x) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)cc"));
}
TEST(PointerNullabilityTest, TransitiveNullCheck) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x) {
int *y = x;
*x; // [[unsafe]]
if (y) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable x) {
int *y = x;
*y; // [[unsafe]]
if (x) {
*y;
} else {
*y; // [[unsafe]]
}
*y; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, BinaryExpressions) {
// x && y
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nullable y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
if (x && y) {
*x;
*y;
} else {
*x; // [[unsafe]]
*y; // [[unsafe]]
}
*x; // [[unsafe]]
*y; // [[unsafe]]
}
)cc"));
// x || y
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nullable y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
if (x || y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
} else {
*x; // [[unsafe]]
*y; // [[unsafe]]
}
*x; // [[unsafe]]
*y; // [[unsafe]]
}
)cc"));
// !x && !y
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nullable y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
if (!x && !y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
} else {
*x; // [[unsafe]]
*y; // [[unsafe]]
}
*x; // [[unsafe]]
*y; // [[unsafe]]
}
)cc"));
// !x || !y
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable x, int* _Nullable y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
if (!x || !y) {
*x; // [[unsafe]]
*y; // [[unsafe]]
} else {
*x;
*y;
}
*x; // [[unsafe]]
*y; // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, ArrowOperatorOnNonNullPtr) {
// (->) member field
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *foo;
};
void target(Foo *_Nonnull foo) { foo->foo; }
)cc"));
// (->) member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *foo();
};
void target(Foo *_Nonnull foo) { foo->foo(); }
)cc"));
}
TEST(PointerNullabilityTest, ArrowOperatorOnNullablePtr) {
// (->) member field
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *foo;
};
void target(Foo *_Nullable foo) {
foo->foo; // [[unsafe]]
if (foo) {
foo->foo;
} else {
foo->foo; // [[unsafe]]
}
foo->foo; // [[unsafe]]
}
)cc"));
// (->) member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *foo();
};
void target(Foo *_Nullable foo) {
foo->foo(); // [[unsafe]]
if (foo) {
foo->foo();
} else {
foo->foo(); // [[unsafe]]
}
foo->foo(); // [[unsafe]]
}
)cc"));
}
TEST(PointerNullabilityTest, ArrowOperatorOnUnknownPtr) {
// (->) member field
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *foo;
};
void target(Foo *foo) { foo->foo; }
)cc"));
// (->) member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *foo();
};
void target(Foo *foo) { foo->foo(); }
)cc"));
}
TEST(PointerNullabilityTest, ThisPointer) {
// (->) implicit `this`
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
void foo();
void target() { foo(); }
};
)cc"));
// (->) explicit `this`
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
void foo();
void target() { this->foo(); }
};
)cc"));
}
TEST(PointerNullabilityTest, NonNullFieldsOfPointerType) {
// dereference field of pointer type
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo* _Nonnull ptr;
};
void target(Foo foo) { *foo.ptr; }
)cc"));
// dereference field of pointer type in member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo* _Nonnull ptr;
void target() { *ptr; }
};
)cc"));
}
TEST(PointerNullabilityTest, NullableFieldsOfPointerType) {
// dereference field of pointer type
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo* _Nullable ptr;
};
void target(Foo foo) {
*foo.ptr; // [[unsafe]]
if (foo.ptr) {
*foo.ptr;
} else {
*foo.ptr; // [[unsafe]]
}
*foo.ptr; // [[unsafe]]
}
)cc"));
// dereference field of pointer type in member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo* _Nullable ptr;
void target() {
*ptr; // [[unsafe]]
if (ptr) {
*ptr;
} else {
*ptr; // [[unsafe]]
}
*ptr; // [[unsafe]]
}
};
)cc"));
}
TEST(PointerNullabilityTest, UnknownFieldsOfPointerType) {
// dereference field of pointer type
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *ptr;
};
void target(Foo foo) { *foo.ptr; }
)cc"));
// dereference field of pointer type in member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
Foo *ptr;
void target() { *ptr; }
};
)cc"));
}
TEST(PointerNullabilityTest, MergeNullAndNonNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nonnull y, bool b) {
int *x = nullptr;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = y;
*x;
}
*x; // [[unsafe]]
if (b) {
*x;
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNullAndNullable) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable y, bool b) {
int *x = nullptr;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = y;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNullAndUnknown) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *y, bool b) {
int *x = nullptr;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = y;
*x;
}
*x; // [[unsafe]]
if (b) {
*x;
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNonNullAndNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nonnull y, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = nullptr;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNonNullAndNonNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nonnull y, int *_Nonnull z, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = z;
*x;
}
*x;
if (b) {
*x;
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNonNullAndNullable) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nonnull y, int *_Nullable z, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = z;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNonNullAndUnknown) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nonnull y, int *z, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = z;
*x;
}
*x;
if (b) {
*x;
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNullableAndNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable y, bool b) {
int *x = y;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = nullptr;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNullableAndNonNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable y, int *_Nonnull z, bool b) {
int *x = y;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = z;
*x;
}
*x; // [[unsafe]]
if (b) {
*x;
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNullableAndNullable) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable y, int *_Nullable z, bool b) {
int *x = y;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = z;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeNullableAndUnknown) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable y, int *z, bool b) {
int *x = y;
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
x = z;
*x;
}
*x; // [[unsafe]]
if (b) {
*x;
} else {
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeUnknownAndNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *y, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = nullptr;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeUnknownAndNonNull) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *y, int *_Nonnull z, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = z;
*x;
}
*x;
if (b) {
*x;
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeUnknownAndNullable) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *y, int *_Nullable z, bool b) {
int *x = y;
*x;
if (b) {
*x;
x = z;
*x; // [[unsafe]]
}
*x; // [[unsafe]]
if (b) {
*x; // [[unsafe]]
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, MergeUnknownAndUnknown) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *y, int *z, bool b) {
int *x = y;
if (b) {
*x;
x = z;
*x;
}
*x;
if (b) {
*x;
} else {
*x;
}
}
)cc"));
}
TEST(PointerNullabilityTest, CallExprWithPointerReturnType) {
// free function
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nonnull makeNonnull();
int *_Nullable makeNullable();
int *makeUnannotated();
void target() {
*makeNonnull();
*makeNullable(); // [[unsafe]]
*makeUnannotated();
}
)cc"));
// member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
int *_Nonnull makeNonnull();
int *_Nullable makeNullable();
int *makeUnannotated();
};
void target(Foo foo) {
*foo.makeNonnull();
*foo.makeNullable(); // [[unsafe]]
*foo.makeUnannotated();
}
)cc"));
// overloaded operator call
EXPECT_TRUE(checkDiagnostics(R"cc(
struct MakeNonnull {
int *_Nonnull operator()();
};
struct MakeNullable {
int *_Nullable operator()();
};
struct MakeUnannotated {
int *operator()();
};
void target() {
MakeNonnull makeNonnull;
*makeNonnull();
MakeNullable makeNullable;
*makeNullable(); // [[unsafe]]
MakeUnannotated makeUnannotated;
*makeUnannotated();
}
)cc"));
// function pointer
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull (*makeNonnull)(),
int* _Nullable (*makeNullable)(), int* (*makeUnannotated)()) {
*makeNonnull();
*makeNullable(); // [[unsafe]]
*makeUnannotated();
}
)cc"));
// pointer to function pointer
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull (**makeNonnull)(),
int* _Nullable (**makeNullable)(), int* (**makeUnannotated)()) {
*(*makeNonnull)();
*(*makeNullable)(); // [[unsafe]]
*(*makeUnannotated)();
}
)cc"));
// function returning a function pointer which returns a pointer
EXPECT_TRUE(checkDiagnostics(R"cc(
typedef int* _Nonnull (*MakeNonnullT)();
typedef int* _Nullable (*MakeNullableT)();
typedef int* (*MakeUnannotatedT)();
void target(MakeNonnullT (*makeNonnull)(), MakeNullableT (*makeNullable)(),
MakeUnannotatedT (*makeUnannotated)()) {
*(*makeNonnull)()();
*(*makeNullable)()(); // [[unsafe]]
*(*makeUnannotated)()();
}
)cc"));
// free function returns reference to pointer
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nonnull &makeNonnull();
int *_Nullable &makeNullable();
int *&makeUnannotated();
void target() {
*makeNonnull();
*makeNullable(); // [[unsafe]]
*makeUnannotated();
}
)cc"));
// function called in loop
EXPECT_TRUE(checkDiagnostics(R"cc(
int *_Nullable makeNullable();
bool makeBool();
void target() {
bool first = true;
while (true) {
int *x = makeNullable();
if (first && x == nullptr) return;
first = false;
*x; // [[unsafe]]
}
}
)cc"));
}
TEST(PointerNullabilityTest, DoubleDereference) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int** p) {
*p;
**p;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int** _Nonnull p) {
*p;
**p;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull* p) {
*p;
**p;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull* _Nonnull p) {
*p;
**p;
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int** _Nullable p) {
*p; // [[unsafe]]
**p; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable* p) {
*p;
**p; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable* _Nullable p) {
*p; // [[unsafe]]
**p; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nullable* _Nonnull p) {
*p;
**p; // [[unsafe]]
}
)cc"));
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int* _Nonnull* _Nullable p) {
*p; // [[unsafe]]
**p; // [[unsafe]]
}
)cc"));
}
// TODO: Fix false negatives.
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"));
}
// TODO: Fix false positives and false negatives.
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; // [[unsafe]] TODO: fix false positive.
*p.getT0().nullable; // [[unsafe]]
*p.getT0().nonnull; // [[unsafe]] TODO: fix false positive.
*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; // [[unsafe]] TODO: fix false positive.
*p.getT1().nullableChar; // [[unsafe]]
*p.getT2().nonnullChar; // [[unsafe]] TODO: fix false positive.
*p.getT3().unknownLongLong; // [[unsafe]] TODO: fix false positive.
*p.getT3().nullableDouble; // [[unsafe]]
*p.getT3().nonnullFloat; // [[unsafe]] TODO: fix false positive.
*p.getT3().unknownShort; // [[unsafe]] TODO: fix false positive.
*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; // [[unsafe]] TODO: fix false positive.
*p.getT1().unknownConstChar; // [[unsafe]] TODO: fix false positive.
*p.getT1().constUnknownConstChar; // [[unsafe]] TODO: fix false positive.
*p.getT2().constNullableChar; // [[unsafe]]
*p.getT2().nullableConstChar; // [[unsafe]]
*p.getT2().constNullableConstChar; // [[unsafe]]
*p.getT3().constNonnullChar; // [[unsafe]] TODO: fix false positive.
*p.getT3().nonnullConstChar; // [[unsafe]] TODO: fix false positive.
*p.getT3().constNonnullConstChar; // [[unsafe]] TODO: fix false positive.
*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"));
}
// TODO: Fix false negatives.
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>(); // TODO: fix false negative.
*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>(); // TODO: fix false negative.
*p.getT0<int *const _Nullable>(); // TODO: fix false negative.
*p.getT0<int const *const _Nullable>(); // TODO: fix false negative.
}
)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>(); // TODO: fix false negative.
}
)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>(); // TODO: fix false negative.
*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>(); // TODO: fix false negative.
*p.getTN1<int *const _Nullable>(); // TODO: fix false negative.
*p.getTN1<int const *const _Nullable>(); // TODO: fix false negative.
}
)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>(); // TODO: fix false negative.
}
)cc"));
}
// TODO: Fix false positives.
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;
*p.arg0->arg1.arg0;
*p.arg0->arg1.arg1;
}
)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; // [[unsafe]] TODO: fix false positive.
*p.arg0.arg0.arg0.arg2; // [[unsafe]]
*p.arg0.arg0.arg0.arg3; // [[unsafe]] TODO: fix false positive.
*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; // [[unsafe]] TODO: fix false positive.
*p.arg1.arg1.arg1.arg3; // [[unsafe]]
*p.arg1.arg1.arg1.arg5; // [[unsafe]] TODO: fix false positive.
*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; // TODO: fix false negative.
*p.nonnull->nonnull->nullable1; // TODO: fix false negative.
*p.nonnull->nullable; // TODO: fix false negative.
*p.nonnull->nullable->unknown; // TODO: fix false negative.
*p.nonnull->nullable->nullable; // TODO: fix false negative.
*p.unknown->unknown;
*p.unknown->unknown->unknown;
*p.unknown->unknown->nullable; // TODO: fix false negative.
*p.unknown;
*p.unknown->nullable; // TODO: fix false negative.
*p.unknown->nullable->nullable; // TODO: fix false negative.
*p.unknown->nullable->nonnull; // TODO: fix false negative.
*p.nonnull->getNonnull();
*p.nonnull->getNonnull()->nullable0; // TODO: fix false negative.
*p.nonnull->getNonnull()->nullable1; // TODO: fix false negative.
*p.nonnull->getNullable();
*p.nonnull->getNullable()->unknown; // TODO: fix false negative.
*p.nonnull->getNullable()->nullable; // TODO: fix false negative.
*p.unknown->getUnknown();
*p.unknown->getUnknown()->unknown;
*p.unknown->getUnknown()->nullable; // TODO: fix false negative.
*p.unknown->getNullable(); // TODO: fix false negative.
*p.unknown->getNullable()->nullable; // TODO: fix false negative.
*p.unknown->getNullable()->nonnull; // TODO: fix false negative.
*p.nonnull->getNonnull()->getNullable0(); // TODO: fix false negative.
*p.nonnull->getNonnull()->getNullable1(); // TODO: fix false negative.
*p.nonnull->getNullable()->getUnknown(); // TODO: fix false negative.
*p.nonnull->getNullable()->getNullable(); // TODO: fix false negative.
*p.unknown->getUnknown()->getUnknown();
*p.unknown->getUnknown()->getNullable(); // TODO: fix false negative.
*p.unknown->getNullable()->getNullable(); // TODO: fix false negative.
*p.unknown->getNullable()->getNonnull(); // TODO: fix false negative.
*p.nonnull->nonnull->getNullable0(); // TODO: fix false negative.
*p.nonnull->nonnull->getNullable1(); // TODO: fix false negative.
*p.nonnull->nullable->getUnknown(); // TODO: fix false negative.
*p.nonnull->nullable->getNullable(); // TODO: fix false negative.
*p.unknown->unknown->getUnknown();
*p.unknown->unknown->getNullable(); // TODO: fix false negative.
*p.unknown->nullable->getNullable(); // TODO: fix false negative.
*p.unknown->nullable->getNonnull(); // TODO: fix false negative.
*p.getNonnull();
*p.getNonnull()->nonnull;
*p.getNonnull()->nonnull->nullable0; // TODO: fix false negative.
*p.getNonnull()->nonnull->nullable1; // TODO: fix false negative.
*p.getNonnull()->nullable; // TODO: fix false negative.
*p.getNonnull()->nullable->unknown; // TODO: fix false negative.
*p.getNonnull()->nullable->nullable; // TODO: fix false negative.
*p.getUnknown()->unknown;
*p.getUnknown()->unknown->unknown;
*p.getUnknown()->unknown->nullable; // TODO: fix false negative.
*p.getUnknown();
*p.getUnknown()->nullable; // TODO: fix false negative.
*p.getUnknown()->nullable->nullable; // TODO: fix false negative.
*p.getUnknown()->nullable->nonnull; // TODO: fix false negative.
*p.getNonnull()->getNonnull();
*p.getNonnull()->getNonnull()->nullable0; // TODO: fix false negative.
*p.getNonnull()->getNonnull()->nullable1; // TODO: fix false negative.
*p.getNonnull()->getNullable(); // TODO: fix false negative.
*p.getNonnull()->getNullable()->unknown; // TODO: fix false negative.
*p.getNonnull()->getNullable()->nullable; // TODO: fix false negative.
*p.getUnknown()->getUnknown();
*p.getUnknown()->getUnknown()->unknown;
*p.getUnknown()->getUnknown()->nullable; // TODO: fix false negative.
*p.getUnknown()->getNullable(); // TODO: fix false negative.
*p.getUnknown()->getNullable()->nullable; // TODO: fix false negative.
*p.getUnknown()->getNullable()->nonnull; // TODO: fix false negative.
*p.getNonnull()->nonnull->getNullable0(); // TODO: fix false negative.
*p.getNonnull()->nonnull->getNullable1(); // TODO: fix false negative.
*p.getNonnull()->nullable->getUnknown(); // TODO: fix false negative.
*p.getNonnull()->nullable->getNullable(); // TODO: fix false negative.
*p.getUnknown()->unknown->getUnknown();
*p.getUnknown()->unknown->getNullable(); // TODO: fix false negative.
*p.getUnknown()->nullable->getNullable(); // TODO: fix false negative.
*p.getUnknown()->nullable->getNonnull(); // TODO: fix false negative.
*p.getNonnull()->getNonnull()->getNullable0(); // TODO: fix false
// negative.
*p.getNonnull()->getNonnull()->getNullable1(); // TODO: fix false
// negative.
*p.getNonnull()->getNullable()->getUnknown(); // TODO: fix false
// negative.
*p.getNonnull()->getNullable()->getNullable(); // TODO: fix false
// negative.
*p.getUnknown()->getUnknown()->getUnknown();
*p.getUnknown()->getUnknown()->getNullable(); // TODO: fix false
// negative.
*p.getUnknown()->getNullable()->getNullable(); // TODO: fix false
// negative.
*p.getUnknown()->getNullable()->getNonnull(); // TODO: fix false
// negative.
}
)cc"));
}
TEST(PointerNullabilityTest, CallExprParamAssignment) {
// free function with single param
EXPECT_TRUE(checkDiagnostics(R"cc(
void takeNonnull(int *_Nonnull);
void takeNullable(int *_Nullable);
void takeUnannotated(int *);
void target(int *_Nonnull ptr_nonnull, int *_Nullable ptr_nullable,
int *ptr_unannotated) {
takeNonnull(nullptr); // [[unsafe]]
takeNonnull(ptr_nonnull);
takeNonnull(ptr_nullable); // [[unsafe]]
takeNonnull(ptr_unannotated);
takeNullable(nullptr);
takeNullable(ptr_nonnull);
takeNullable(ptr_nullable);
takeNullable(ptr_unannotated);
takeUnannotated(nullptr);
takeUnannotated(ptr_nonnull);
takeUnannotated(ptr_nullable);
takeUnannotated(ptr_unannotated);
}
)cc"));
// overloaded operator with single param
EXPECT_TRUE(checkDiagnostics(R"cc(
// map<int * _Nonnull, int>
struct MapWithNonnullKeys {
int &operator[](int *_Nonnull key);
};
// map<int * _Nullable, int>
struct MapWithNullableKeys {
int &operator[](int *_Nullable key);
};
// map<int *, int>
struct MapWithUnannotatedKeys {
int &operator[](int *key);
};
void target(int *_Nonnull ptr_nonnull, int *_Nullable ptr_nullable,
int *ptr_unannotated) {
MapWithNonnullKeys nonnull_keys;
nonnull_keys[nullptr] = 42; // [[unsafe]]
nonnull_keys[ptr_nonnull] = 42;
nonnull_keys[ptr_nullable] = 42; // [[unsafe]]
nonnull_keys[ptr_unannotated] = 42;
MapWithNullableKeys nullable_keys;
nullable_keys[nullptr] = 42;
nullable_keys[ptr_nonnull] = 42;
nullable_keys[ptr_nullable] = 42;
nullable_keys[ptr_unannotated] = 42;
MapWithUnannotatedKeys unannotated_keys;
unannotated_keys[nullptr] = 42;
unannotated_keys[ptr_nonnull] = 42;
unannotated_keys[ptr_nullable] = 42;
unannotated_keys[ptr_unannotated] = 42;
}
)cc"));
// free function with multiple params of mixed nullability
EXPECT_TRUE(checkDiagnostics(R"cc(
void takeMixed(int *, int *_Nullable, int *_Nonnull);
void target() {
takeMixed(nullptr, nullptr, nullptr); // [[unsafe]]
}
)cc"));
// overloaded operator with multiple params of mixed nullability
EXPECT_TRUE(checkDiagnostics(R"cc(
struct TakeMixed {
void operator()(int *, int *_Nullable, int *_Nonnull);
};
void target() {
TakeMixed takeMixed;
takeMixed(nullptr, nullptr, nullptr); // [[unsafe]]
}
)cc"));
// member function
EXPECT_TRUE(checkDiagnostics(R"cc(
struct Foo {
void takeNonnull(int *_Nonnull);
void takeNullable(int *_Nullable);
void takeUnannotated(int *);
};
void target(Foo foo) {
foo.takeNonnull(nullptr); // [[unsafe]]
foo.takeNullable(nullptr);
foo.takeUnannotated(nullptr);
}
)cc"));
// function pointer
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(void (*takeNonnull)(int *_Nonnull),
void (*takeNullable)(int *_Nullable),
void (*takeUnannotated)(int *)) {
takeNonnull(nullptr); // [[unsafe]]
takeNullable(nullptr);
takeUnannotated(nullptr);
}
)cc"));
// pointer to function pointer
//
// TODO(b/233582219): Fix false negative. Implement support for retrieving
// parameter types from a pointer to function pointer.
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(void (**takeNonnull)(int *_Nonnull),
void (**takeNullable)(int *_Nullable),
void (**takeUnannotated)(int *)) {
(*takeNonnull)(nullptr); // false-negative
(*takeNullable)(nullptr);
(*takeUnannotated)(nullptr);
}
)cc"));
// function returned from function
//
// TODO(b/233582219): Fix false negative. Implement support for retrieving
// parameter types for functions returned by another function.
EXPECT_TRUE(checkDiagnostics(R"cc(
typedef void (*takeNonnullF)(int *_Nonnull);
typedef void (*takeNullableF)(int *_Nullable);
typedef void (*takeUnannotatedF)(int *);
void target(takeNonnullF (*takeNonnull)(), takeNullableF (*takeNullable)(),
takeUnannotatedF (*takeUnannotated)()) {
(*takeNonnull)()(nullptr); // false-negative
(*takeNullable)()(nullptr);
(*takeUnannotated)()(nullptr);
}
)cc"));
// passing a reference to a nonnull pointer
//
// TODO(b/233582219): Fix false negative. When the nonnull pointer is passed
// by reference into the callee which takes a nullable parameter, its value
// may be changed to null, making it unsafe to dereference when we return from
// the function call. Some possible approaches for handling this case:
// (1) Disallow passing a nonnull pointer as a nullable reference - and warn
// at the function call.
// (2) Assume in worst case the nonnull pointer becomes nullable after the
// call - and warn at the dereference.
// (3) Sacrifice soundness for reduction in noise, and skip the warning.
EXPECT_TRUE(checkDiagnostics(R"cc(
void takeNonnullRef(int *_Nonnull &);
void takeNullableRef(int *_Nullable &);
void takeUnannotatedRef(int *&);
void target(int *_Nonnull ptr_nonnull) {
takeNonnullRef(ptr_nonnull);
*ptr_nonnull;
// false-negative
takeNullableRef(ptr_nonnull);
*ptr_nonnull;
takeUnannotatedRef(ptr_nonnull);
*ptr_nonnull;
}
)cc"));
// passing a reference to a nullable pointer
EXPECT_TRUE(checkDiagnostics(R"cc(
void takeNonnullRef(int *_Nonnull &);
void takeNullableRef(int *_Nullable &);
void takeUnannotatedRef(int *&);
void target(int *_Nullable ptr_nullable) {
takeNonnullRef(ptr_nullable); // [[unsafe]]
*ptr_nullable; // [[unsafe]]
takeNullableRef(ptr_nullable);
*ptr_nullable; // [[unsafe]]
takeUnannotatedRef(ptr_nullable);
*ptr_nullable; // [[unsafe]]
}
)cc"));
// passing a reference to an unannotated pointer
//
// TODO(b/233582219): Fix false negative. The unannotated pointer should be
// considered nullable if it has been used as a nullable pointer.
EXPECT_TRUE(checkDiagnostics(R"cc(
void takeNonnullRef(int *_Nonnull &);
void takeNullableRef(int *_Nullable &);
void takeUnannotatedRef(int *&);
void target(int *ptr_unannotated) {
takeNonnullRef(ptr_unannotated);
*ptr_unannotated;
takeNullableRef(ptr_unannotated);
*ptr_unannotated; // false-negative
takeUnannotatedRef(ptr_unannotated);
*ptr_unannotated;
}
)cc"));
}
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"));
}
// TODO: Move the definitions of `NullabilityKind` and `__assert_nullability()`
// into a preamble that `checkDiagnostics()` prepends to every test.
TEST(PointerNullabilityTest, AssertNullability) {
const std::string Declarations = R"cc(
enum NullabilityKind {
NK_nonnull,
NK_nullable,
NK_unspecified,
};
template <NullabilityKind... NK, typename T>
void __assert_nullability(const T&);
)cc";
// Concrete struct.
EXPECT_TRUE(checkDiagnostics(Declarations + 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(Declarations + 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(Declarations + 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(Declarations + 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(Declarations + 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(Declarations + 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(Declarations + 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(
enum NullabilityKind {
NK_nonnull,
NK_nullable,
NK_unspecified,
};
template <NullabilityKind... NK, typename T>
void __assert_nullability(const T &);
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