blob: a69630be9ca12e317e2435375958dfeb317172b6 [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 <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/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Testing/Support/Error.h"
#include "third_party/llvm/llvm-project/llvm/utils/unittest/googletest/include/gtest/gtest.h"
namespace clang {
namespace tidy {
namespace nullability {
namespace {
using dataflow::Environment;
using dataflow::TypeErasedDataflowAnalysisState;
using dataflow::test::AnalysisData;
using dataflow::test::checkDataflow;
using ::testing::ContainerEq;
using ::testing::Test;
void checkDiagnostics(llvm::StringRef SourceCode) {
std::vector<const Stmt *> Diagnostics;
PointerNullabilityDiagnoser Diagnoser;
ASSERT_THAT_ERROR(
checkDataflow<PointerNullabilityAnalysis>(
SourceCode, ast_matchers::hasName("target"),
[](ASTContext &ASTCtx, Environment &) {
return PointerNullabilityAnalysis(ASTCtx);
},
[&Diagnostics, &Diagnoser](
ASTContext &Ctx, const CFGStmt &Stmt,
const TypeErasedDataflowAnalysisState &State) {
auto StmtDiagnostics =
Diagnoser.diagnose(Stmt.getStmt(), Ctx, State.Env);
if (StmtDiagnostics.has_value()) {
Diagnostics.push_back(StmtDiagnostics.value());
}
},
[&Diagnostics](AnalysisData AnalysisData) {
llvm::DenseSet<unsigned> ExpectedLines, ActualLines;
auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
for (auto [Stmt, _] : AnalysisData.Annotations) {
ExpectedLines.insert(
SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
}
for (auto *Stmt : Diagnostics) {
ActualLines.insert(
SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
}
EXPECT_THAT(ActualLines, ContainerEq(ExpectedLines));
},
{"-fsyntax-only", "-std=c++17", "-Wno-unused-value"}),
llvm::Succeeded());
}
TEST(PointerNullabilityTest, NoPointerOperations) {
checkDiagnostics(R"(
void target() {
1 + 2;
}
)");
}
TEST(PointerNullabilityTest, DerefNullPtr) {
// nullptr
checkDiagnostics(R"(
void target() {
int *x = nullptr;
*x; // [[unsafe]]
}
)");
// 0
checkDiagnostics(R"(
void target() {
int *x = 0;
*x; // [[unsafe]]
}
)");
}
TEST(PointerNullabilityTest, DerefAddrOf) {
checkDiagnostics(R"(
void target() {
int i;
int *x = &i;
*x;
}
)");
// transitive
checkDiagnostics(R"(
void target() {
int i;
int *x = &i;
int *y = x;
*y;
}
)");
}
TEST(PointerNullabilityTest, DerefPtrAnnotatedNonNullWithoutACheck) {
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
}
)");
// transitive
checkDiagnostics(R"(
void target(int * _Nonnull x) {
int *y = x;
*y;
}
)");
}
TEST(PointerNullabilityTest, DerefPtrAnnotatedNullableWithoutACheck) {
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
}
)");
// transitive
checkDiagnostics(R"(
void target(int * _Nullable x) {
int *y = x;
*y; // [[unsafe]]
}
)");
}
TEST(PointerNullabilityTest, DerefUnknownPtrWithoutACheck) {
checkDiagnostics(R"(
void target(int *x) {
*x;
}
)");
// transitive
checkDiagnostics(R"(
void target(int *x) {
int *y = x;
*y;
}
)");
}
// TODO(b/233582219): Implement diagnosis of unreachable program points
TEST(PointerNullabilityTest, NonNullPtrImplicitCastToBool) {
// x
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
if (x) {
*x;
} else {
*x; // unreachable
}
*x;
}
)");
// !x
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
if (!x) {
*x; // unreachable
} else {
*x;
}
*x;
}
)");
}
TEST(PointerNullabilityTest, NullablePtrImplicitCastToBool) {
// x
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
if (x) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)");
// !x
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
if (!x) {
*x; // [[unsafe]]
} else {
*x;
}
*x; // [[unsafe]]
}
)");
}
// 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
checkDiagnostics(R"(
void target(int *x) {
*x; // false-negative
if (x) {
*x;
} else {
*x; // false-negative
}
*x; // false-negative
}
)");
// !x
checkDiagnostics(R"(
void target(int *x) {
*x; // false-negative
if (!x) {
*x; // false-negative
} else {
*x;
}
*x; // false-negative
}
)");
}
TEST(PointerNullabilityTest, CompareNonNullPtrAndNonNullPtr) {
// nonnull == nonnull
checkDiagnostics(R"(
void target(int * _Nonnull x, int * _Nonnull y) {
*x;
*y;
if (x == y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
// nonnull != nonnull
checkDiagnostics(R"(
void target(int * _Nonnull x, int * _Nonnull y) {
*x;
*y;
if (x != y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
}
TEST(PointerNullabilityTest, CompareNullablePtrAndNullablePtr) {
// nullable == nullable
checkDiagnostics(R"(
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]]
}
)");
// nullable != nullable
checkDiagnostics(R"(
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]]
}
)");
}
TEST(PointerNullabilityTest, CompareUnknownPtrAndUnknownPtr) {
// unknown == unknown
checkDiagnostics(R"(
void target(int *x, int *y) {
*x;
*y;
if (x == y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
// unknown != unknown
checkDiagnostics(R"(
void target(int *x, int *y) {
*x;
*y;
if (x != y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
}
// TODO(b/233582219): Implement diagnosis of unreachable program points
TEST(PointerNullabilityTest, CompareNonNullPtrAndNullPtr) {
// nonnull == nullptr
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
if (x == nullptr) {
*x; // unreachable
} else {
*x;
}
*x;
}
)");
// nullptr == nonnull
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
if (nullptr == x) {
*x; // unreachable
} else {
*x;
}
*x;
}
)");
// nonnull != nullptr
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
if (x != nullptr) {
*x;
} else {
*x; // unreachable
}
*x;
}
)");
// nullptr != nonnull
checkDiagnostics(R"(
void target(int * _Nonnull x) {
*x;
if (nullptr != x) {
*x;
} else {
*x; // unreachable
}
*x;
}
)");
}
TEST(PointerNullabilityTest, CompareNullablePtrAndNullPtr) {
// nullable == nullptr
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
if (x == nullptr) {
*x; // [[unsafe]]
} else {
*x;
}
*x; // [[unsafe]]
}
)");
// nullptr == nullable
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
if (nullptr == x) {
*x; // [[unsafe]]
} else {
*x;
}
*x; // [[unsafe]]
}
)");
// nullable != nullptr
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
if (x != nullptr) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)");
// nullptr != nullable
checkDiagnostics(R"(
void target(int * _Nullable x) {
*x; // [[unsafe]]
if (nullptr != x) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)");
}
TEST(PointerNullabilityTest, CompareNullablePtrAndNonNullPtr) {
// nullable == nonnull
checkDiagnostics(R"(
void target(int * _Nullable x, int * _Nonnull y) {
*x; // [[unsafe]]
*y;
if (x == y) {
*x;
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)");
// nonnull == nullable
checkDiagnostics(R"(
void target(int * _Nullable x, int * _Nonnull y) {
*x; // [[unsafe]]
*y;
if (y == x) {
*x;
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)");
// nullable != nonnull
checkDiagnostics(R"(
void target(int * _Nullable x, int * _Nonnull y) {
*x; // [[unsafe]]
*y;
if (x != y) {
*x; // [[unsafe]]
*y;
} else {
*x;
*y;
}
*x; // [[unsafe]]
*y;
}
)");
// nonnull != nullable
checkDiagnostics(R"(
void target(int * _Nullable x, int * _Nonnull y) {
*x; // [[unsafe]]
*y;
if (y != x) {
*x; // [[unsafe]]
*y;
} else {
*x;
*y;
}
*x; // [[unsafe]]
*y;
}
)");
}
TEST(PointerNullabilityTest, CompareNullablePtrAndUnknownPtr) {
// nullable == unknown
checkDiagnostics(R"(
void target(int * _Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (x == y) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)");
// unknown == nullable
checkDiagnostics(R"(
void target(int * _Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (y == x) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)");
// nullable != unknown
checkDiagnostics(R"(
void target(int * _Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (x != y) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)");
// unknown != nullable
checkDiagnostics(R"(
void target(int * _Nullable x, int *y) {
*x; // [[unsafe]]
*y;
if (y != x) {
*x; // [[unsafe]]
*y;
} else {
*x; // [[unsafe]]
*y;
}
*x; // [[unsafe]]
*y;
}
)");
}
// 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
checkDiagnostics(R"(
void target(int *x) {
*x; // false-negative
if (x == nullptr) {
*x; // false-negative
} else {
*x;
}
*x; // false-negative
}
)");
// nullptr == unknown
checkDiagnostics(R"(
void target(int *x) {
*x; // false-negative
if (nullptr == x) {
*x; // false-negative
} else {
*x;
}
*x; // false-negative
}
)");
// unknown != nullptr
checkDiagnostics(R"(
void target(int *x) {
*x; // false-negative
if (x != nullptr) {
*x;
} else {
*x; // false-negative
}
*x; // false-negative
}
)");
// nullptr != unknown
checkDiagnostics(R"(
void target(int *x) {
*x; // false-negative
if (nullptr != x) {
*x;
} else {
*x; // false-negative
}
*x; // false-negative
}
)");
}
TEST(PointerNullabilityTest, CompareUnknownPtrAndNonNullPtr) {
// unknown == nonnull
checkDiagnostics(R"(
void target(int *x, int * _Nonnull y) {
*x;
*y;
if (x == y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
// nonnull == unknown
checkDiagnostics(R"(
void target(int *x, int * _Nonnull y) {
*x;
*y;
if (y == x) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
// unknown != nonnull
checkDiagnostics(R"(
void target(int *x, int * _Nonnull y) {
*x;
*y;
if (x != y) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
// nonnull != unknown
checkDiagnostics(R"(
void target(int *x, int * _Nonnull y) {
*x;
*y;
if (y != x) {
*x;
*y;
} else {
*x;
*y;
}
*x;
*y;
}
)");
}
TEST(PointerNullabilityTest, TransitiveNullCheck) {
checkDiagnostics(R"(
void target(int * _Nullable x) {
int *y = x;
*x; // [[unsafe]]
if (y) {
*x;
} else {
*x; // [[unsafe]]
}
*x; // [[unsafe]]
}
)");
checkDiagnostics(R"(
void target(int * _Nullable x) {
int *y = x;
*y; // [[unsafe]]
if (x) {
*y;
} else {
*y; // [[unsafe]]
}
*y; // [[unsafe]]
}
)");
}
TEST(PointerNullabilityTest, BinaryExpressions) {
// x && y
checkDiagnostics(R"(
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]]
}
)");
// x || y
checkDiagnostics(R"(
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]]
}
)");
// !x && !y
checkDiagnostics(R"(
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]]
}
)");
// !x || !y
checkDiagnostics(R"(
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]]
}
)");
}
TEST(PointerNullabilityTest, ArrowOperatorOnNonNullPtr) {
// (->) member field
checkDiagnostics(R"(
struct Foo {
Foo *foo;
};
void target(Foo * _Nonnull foo) {
foo->foo;
}
)");
// (->) member function
checkDiagnostics(R"(
struct Foo {
Foo *foo();
};
void target(Foo * _Nonnull foo) {
foo->foo();
}
)");
}
TEST(PointerNullabilityTest, ArrowOperatorOnNullablePtr) {
// (->) member field
checkDiagnostics(R"(
struct Foo {
Foo *foo;
};
void target(Foo * _Nullable foo) {
foo->foo; // [[unsafe]]
if (foo) {
foo->foo;
} else {
foo->foo; // [[unsafe]]
}
foo->foo; // [[unsafe]]
}
)");
// (->) member function
checkDiagnostics(R"(
struct Foo {
Foo *foo();
};
void target(Foo * _Nullable foo) {
foo->foo(); // [[unsafe]]
if (foo) {
foo->foo();
} else {
foo->foo(); // [[unsafe]]
}
foo->foo(); // [[unsafe]]
}
)");
}
TEST(PointerNullabilityTest, ArrowOperatorOnUnknownPtr) {
// (->) member field
checkDiagnostics(R"(
struct Foo {
Foo *foo;
};
void target(Foo *foo) {
foo->foo;
}
)");
// (->) member function
checkDiagnostics(R"(
struct Foo {
Foo *foo();
};
void target(Foo *foo) {
foo->foo();
}
)");
}
TEST(PointerNullabilityTest, ThisPointer) {
// (->) implicit `this`
checkDiagnostics(R"(
struct Foo {
void foo();
void target() {
foo();
}
};
)");
// (->) explicit `this`
checkDiagnostics(R"(
struct Foo {
void foo();
void target() {
this->foo();
}
};
)");
}
TEST(PointerNullabilityTest, NonNullFieldsOfPointerType) {
// dereference field of pointer type
checkDiagnostics(R"(
struct Foo {
Foo * _Nonnull ptr;
};
void target(Foo foo) {
*foo.ptr;
}
)");
// dereference field of pointer type in member function
checkDiagnostics(R"(
struct Foo {
Foo * _Nonnull ptr;
void target() {
*ptr;
}
};
)");
}
TEST(PointerNullabilityTest, NullableFieldsOfPointerType) {
// dereference field of pointer type
checkDiagnostics(R"(
struct Foo {
Foo * _Nullable ptr;
};
void target(Foo foo) {
*foo.ptr; // [[unsafe]]
if (foo.ptr) {
*foo.ptr;
} else {
*foo.ptr; // [[unsafe]]
}
*foo.ptr; // [[unsafe]]
}
)");
// dereference field of pointer type in member function
checkDiagnostics(R"(
struct Foo {
Foo * _Nullable ptr;
void target() {
*ptr; // [[unsafe]]
if (ptr) {
*ptr;
} else {
*ptr; // [[unsafe]]
}
*ptr; // [[unsafe]]
}
};
)");
}
TEST(PointerNullabilityTest, UnknownFieldsOfPointerType) {
// dereference field of pointer type
checkDiagnostics(R"(
struct Foo {
Foo *ptr;
};
void target(Foo foo) {
*foo.ptr;
}
)");
// dereference field of pointer type in member function
checkDiagnostics(R"(
struct Foo {
Foo *ptr;
void target() {
*ptr;
}
};
)");
}
TEST(PointerNullabilityTest, MergeNullAndNonNull) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeNullAndNullable) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeNullAndUnknown) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeNonNullAndNull) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeNonNullAndNonNull) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeNonNullAndNullable) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeNonNullAndUnknown) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeNullableAndNull) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeNullableAndNonNull) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeNullableAndNullable) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeNullableAndUnknown) {
checkDiagnostics(R"(
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]]
}
}
)");
}
TEST(PointerNullabilityTest, MergeUnknownAndNull) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeUnknownAndNonNull) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeUnknownAndNullable) {
checkDiagnostics(R"(
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;
}
}
)");
}
TEST(PointerNullabilityTest, MergeUnknownAndUnknown) {
checkDiagnostics(R"(
void target(int *y, int *z, bool b) {
int *x = y;
if (b) {
*x;
x = z;
*x;
}
*x;
if (b) {
*x;
} else {
*x;
}
}
)");
}
} // namespace
} // namespace nullability
} // namespace tidy
} // namespace clang