|  | // 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 |