Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 1 | // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| 2 | // Exceptions. See /LICENSE for license information. |
| 3 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 4 | |
| 5 | #include "nullability/inference/collect_evidence.h" |
| 6 | |
Dmitri Gribenko | 61645e1 | 2023-07-17 04:46:21 -0700 | [diff] [blame] | 7 | #include <string> |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 8 | #include <utility> |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 9 | #include <vector> |
| 10 | |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 11 | #include "nullability/inference/inference.proto.h" |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 12 | #include "nullability/inference/slot_fingerprint.h" |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 13 | #include "clang/AST/Decl.h" |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 14 | #include "clang/AST/DeclBase.h" |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 15 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 16 | #include "clang/ASTMatchers/ASTMatchers.h" |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 17 | #include "clang/Basic/LLVM.h" |
| 18 | #include "clang/Testing/TestAST.h" |
| 19 | #include "third_party/llvm/llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.h" |
Googler | 2c9f53f | 2023-10-24 07:33:00 -0700 | [diff] [blame] | 20 | #include "llvm/ADT/DenseMap.h" |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 21 | #include "llvm/ADT/DenseSet.h" |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 22 | #include "llvm/ADT/StringRef.h" |
| 23 | #include "llvm/Support/Error.h" |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 24 | #include "llvm/Support/raw_ostream.h" |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 25 | #include "third_party/llvm/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h" // IWYU pragma: keep |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 26 | #include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h" |
| 27 | |
| 28 | namespace clang::tidy::nullability { |
| 29 | namespace { |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 30 | using ::clang::ast_matchers::functionDecl; |
| 31 | using ::clang::ast_matchers::hasName; |
| 32 | using ::clang::ast_matchers::isTemplateInstantiation; |
| 33 | using ::clang::ast_matchers::match; |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 34 | using ::testing::_; |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 35 | using ::testing::AllOf; |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 36 | using ::testing::Contains; |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 37 | using ::testing::ElementsAre; |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 38 | using ::testing::IsEmpty; |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 39 | using ::testing::IsSupersetOf; |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 40 | using ::testing::Not; |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 41 | using ::testing::ResultOf; |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 42 | using ::testing::SizeIs; |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 43 | using ::testing::UnorderedElementsAre; |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 44 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 45 | MATCHER_P3(isEvidenceMatcher, SlotMatcher, KindMatcher, SymbolMatcher, "") { |
| 46 | return SlotMatcher.Matches(static_cast<Slot>(arg.slot())) && |
| 47 | KindMatcher.Matches(arg.kind()) && SymbolMatcher.Matches(arg.symbol()); |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 48 | } |
| 49 | |
| 50 | testing::Matcher<const Evidence&> evidence( |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 51 | testing::Matcher<Slot> S, testing::Matcher<Evidence::Kind> Kind, |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 52 | testing::Matcher<const Symbol&> SymbolMatcher = testing::_) { |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 53 | return isEvidenceMatcher(S, Kind, SymbolMatcher); |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | MATCHER_P(functionNamed, Name, "") { |
| 57 | return llvm::StringRef(arg.usr()).contains( |
| 58 | ("@" + llvm::StringRef(Name) + "#").str()); |
| 59 | } |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 60 | |
Googler | 1cf79e4 | 2023-07-17 14:31:35 -0700 | [diff] [blame] | 61 | clang::TestInputs getInputsWithAnnotationDefinitions(llvm::StringRef Source) { |
| 62 | clang::TestInputs Inputs = Source; |
| 63 | Inputs.ExtraFiles["nullability.h"] = R"cc( |
| 64 | template <typename T> |
| 65 | using Nullable [[clang::annotate("Nullable")]] = T; |
| 66 | template <typename T> |
| 67 | using Nonnull [[clang::annotate("Nonnull")]] = T; |
Googler | 0b222ba | 2023-11-03 08:02:58 -0700 | [diff] [blame^] | 68 | template <typename T> |
| 69 | using Unknown [[clang::annotate("Nullability_Unspecified")]] = T; |
Googler | 1cf79e4 | 2023-07-17 14:31:35 -0700 | [diff] [blame] | 70 | )cc"; |
| 71 | Inputs.ExtraArgs.push_back("-include"); |
| 72 | Inputs.ExtraArgs.push_back("nullability.h"); |
| 73 | return Inputs; |
| 74 | } |
| 75 | |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 76 | std::vector<Evidence> collectEvidenceFromTargetFunction( |
Googler | 7191949 | 2023-10-23 15:44:06 -0700 | [diff] [blame] | 77 | llvm::StringRef Source, PreviousInferences PreviousInferences = {}) { |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 78 | std::vector<Evidence> Results; |
Googler | 1cf79e4 | 2023-07-17 14:31:35 -0700 | [diff] [blame] | 79 | clang::TestAST AST(getInputsWithAnnotationDefinitions(Source)); |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 80 | USRCache usr_cache; |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 81 | auto Err = collectEvidenceFromImplementation( |
| 82 | cast<FunctionDecl>( |
| 83 | *dataflow::test::findValueDecl(AST.context(), "target")), |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 84 | evidenceEmitter([&](const Evidence& E) { Results.push_back(E); }, |
| 85 | usr_cache), |
Googler | 7191949 | 2023-10-23 15:44:06 -0700 | [diff] [blame] | 86 | usr_cache, PreviousInferences); |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 87 | if (Err) ADD_FAILURE() << toString(std::move(Err)); |
| 88 | return Results; |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 89 | } |
| 90 | |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 91 | std::vector<Evidence> collectEvidenceFromTargetDecl(llvm::StringRef Source) { |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 92 | std::vector<Evidence> Results; |
Googler | 1cf79e4 | 2023-07-17 14:31:35 -0700 | [diff] [blame] | 93 | clang::TestAST AST(getInputsWithAnnotationDefinitions(Source)); |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 94 | USRCache usr_cache; |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 95 | collectEvidenceFromTargetDeclaration( |
| 96 | *dataflow::test::findValueDecl(AST.context(), "target"), |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 97 | evidenceEmitter([&](const Evidence& E) { Results.push_back(E); }, |
| 98 | usr_cache)); |
Sam McCall | ebcc123 | 2023-07-14 11:36:44 -0700 | [diff] [blame] | 99 | return Results; |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 100 | } |
| 101 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 102 | TEST(CollectEvidenceFromImplementationTest, NoParams) { |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 103 | static constexpr llvm::StringRef Src = R"cc( |
| 104 | void target() {} |
| 105 | )cc"; |
| 106 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 107 | } |
| 108 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 109 | TEST(CollectEvidenceFromImplementationTest, OneParamUnused) { |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 110 | static constexpr llvm::StringRef Src = R"cc( |
| 111 | void target(int *p0) {} |
| 112 | )cc"; |
| 113 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 114 | } |
| 115 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 116 | TEST(CollectEvidenceFromImplementationTest, OneParamUsedWithoutRestriction) { |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 117 | static constexpr llvm::StringRef Src = R"cc( |
| 118 | void takesUnknown(int *unknown) {} |
| 119 | |
| 120 | void target(int *p0) { takesUnknown(p0); } |
| 121 | )cc"; |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 122 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 123 | Not(Contains(evidence(_, _, functionNamed("target"))))); |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 124 | } |
| 125 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 126 | TEST(CollectEvidenceFromImplementationTest, Deref) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 127 | static constexpr llvm::StringRef Src = R"cc( |
| 128 | void target(int *p0, int *p1) { |
| 129 | int a = *p0; |
| 130 | if (p1 != nullptr) { |
| 131 | int b = *p1; |
| 132 | } |
| 133 | } |
| 134 | )cc"; |
| 135 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 136 | UnorderedElementsAre( |
| 137 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE))); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 138 | } |
Sam McCall | 5f2bb24 | 2023-09-14 07:02:56 -0700 | [diff] [blame] | 139 | TEST(CollectEvidenceFromImplementationTest, DerefArrow) { |
| 140 | static constexpr llvm::StringRef Src = R"cc( |
| 141 | struct S { |
| 142 | int x; |
| 143 | int y(); |
| 144 | }; |
| 145 | void target(S *a, S *b) { |
| 146 | a->x; |
| 147 | b->y(); |
| 148 | } |
| 149 | )cc"; |
| 150 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 151 | UnorderedElementsAre( |
| 152 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE), |
| 153 | evidence(paramSlot(1), Evidence::UNCHECKED_DEREFERENCE))); |
| 154 | } |
Googler | 1cf79e4 | 2023-07-17 14:31:35 -0700 | [diff] [blame] | 155 | TEST(InferAnnotationsTest, DerefOfNonnull) { |
| 156 | static constexpr llvm::StringRef Src = R"cc( |
| 157 | void target(Nonnull<int *> p) { |
| 158 | *p; |
| 159 | } |
| 160 | )cc"; |
| 161 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 162 | } |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 163 | |
Sam McCall | cfd2dd1 | 2023-07-18 19:35:21 -0700 | [diff] [blame] | 164 | TEST(InferAnnotationsTest, Location) { |
| 165 | llvm::StringRef Code = "void target(int *p) { *p; }"; |
| 166 | // 12345678901234567890123456 |
| 167 | // 0 1 2 |
| 168 | |
| 169 | auto Evidence = collectEvidenceFromTargetFunction(Code); |
| 170 | ASSERT_THAT(Evidence, ElementsAre(evidence(paramSlot(0), |
| 171 | Evidence::UNCHECKED_DEREFERENCE))); |
| 172 | EXPECT_EQ("input.mm:1:23", Evidence.front().location()); |
| 173 | } |
| 174 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 175 | TEST(CollectEvidenceFromImplementationTest, DereferenceBeforeAssignment) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 176 | static constexpr llvm::StringRef Src = R"cc( |
| 177 | void target(int *p) { |
| 178 | *p; |
| 179 | int i = 1; |
| 180 | p = &i; |
| 181 | } |
| 182 | )cc"; |
| 183 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 184 | UnorderedElementsAre( |
| 185 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE))); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 186 | } |
| 187 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 188 | TEST(CollectEvidenceFromImplementationTest, DereferenceAfterAssignment) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 189 | static constexpr llvm::StringRef Src = R"cc( |
| 190 | void target(int *p) { |
| 191 | int i = 1; |
| 192 | p = &i; |
| 193 | *p; |
| 194 | } |
| 195 | )cc"; |
| 196 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 197 | } |
| 198 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 199 | TEST(CollectEvidenceFromImplementationTest, DerefOfPtrRef) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 200 | static constexpr llvm::StringRef Src = R"cc( |
| 201 | void target(int *&p0, int *&p1) { |
| 202 | int a = *p0; |
| 203 | if (p1 != nullptr) { |
| 204 | int b = *p1; |
| 205 | } |
| 206 | } |
| 207 | )cc"; |
| 208 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 209 | UnorderedElementsAre( |
| 210 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE))); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 211 | } |
| 212 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 213 | TEST(CollectEvidenceFromImplementationTest, UnrelatedCondition) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 214 | static constexpr llvm::StringRef Src = R"cc( |
| 215 | void target(int *p0, int *p1, int *p2, bool b) { |
| 216 | if (b) { |
| 217 | int a = *p0; |
| 218 | int b = *p1; |
| 219 | } else { |
| 220 | int a = *p0; |
| 221 | int c = *p2; |
| 222 | } |
| 223 | } |
| 224 | )cc"; |
| 225 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 226 | UnorderedElementsAre( |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 227 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE), |
| 228 | evidence(paramSlot(1), Evidence::UNCHECKED_DEREFERENCE), |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 229 | // We collect two Evidence values for two dereferences of p0 |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 230 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE), |
| 231 | evidence(paramSlot(2), Evidence::UNCHECKED_DEREFERENCE))); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 232 | } |
| 233 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 234 | TEST(CollectEvidenceFromImplementationTest, LaterDeref) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 235 | static constexpr llvm::StringRef Src = R"cc( |
| 236 | void target(int *p0) { |
| 237 | if (p0 == nullptr) { |
| 238 | (void)0; |
| 239 | } else { |
| 240 | (void)0; |
| 241 | } |
| 242 | int a = *p0; |
| 243 | } |
| 244 | )cc"; |
| 245 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 246 | UnorderedElementsAre( |
| 247 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE))); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 248 | } |
| 249 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 250 | TEST(CollectEvidenceFromImplementationTest, DerefBeforeGuardedDeref) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 251 | static constexpr llvm::StringRef Src = R"cc( |
| 252 | void target(int *p0) { |
| 253 | int a = *p0; |
| 254 | if (p0 != nullptr) { |
| 255 | int b = *p0; |
| 256 | } |
| 257 | } |
| 258 | )cc"; |
| 259 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 260 | UnorderedElementsAre( |
| 261 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE))); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 262 | } |
| 263 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 264 | TEST(CollectEvidenceFromImplementationTest, EarlyReturn) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 265 | static constexpr llvm::StringRef Src = R"cc( |
| 266 | void target(int *p0) { |
| 267 | if (!p0) { |
| 268 | return; |
| 269 | } |
| 270 | int a = *p0; |
| 271 | } |
| 272 | )cc"; |
| 273 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 274 | } |
| 275 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 276 | TEST(CollectEvidenceFromImplementationTest, UnreachableCode) { |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 277 | static constexpr llvm::StringRef Src = R"cc( |
| 278 | void target(int *p0, int *p1, int *p2, int *p3) { |
| 279 | if (true) { |
| 280 | int a = *p0; |
| 281 | } else { |
| 282 | int a = *p1; |
| 283 | } |
| 284 | |
| 285 | if (false) { |
| 286 | int a = *p2; |
| 287 | } |
| 288 | |
| 289 | return; |
| 290 | int a = *p3; |
| 291 | } |
| 292 | )cc"; |
| 293 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 294 | UnorderedElementsAre( |
| 295 | evidence(paramSlot(0), Evidence::UNCHECKED_DEREFERENCE))); |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 296 | } |
| 297 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 298 | TEST(CollectEvidenceFromImplementationTest, NullableArgPassed) { |
| 299 | static constexpr llvm::StringRef Src = R"cc( |
| 300 | void callee(int *q); |
| 301 | void target(Nullable<int *> p) { callee(p); } |
| 302 | )cc"; |
| 303 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 304 | Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 305 | functionNamed("callee")))); |
| 306 | } |
| 307 | |
| 308 | TEST(CollectEvidenceFromImplementationTest, NonnullArgPassed) { |
| 309 | static constexpr llvm::StringRef Src = R"cc( |
| 310 | void callee(int *q); |
| 311 | void target(Nonnull<int *> p) { callee(p); } |
| 312 | )cc"; |
| 313 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 314 | Contains(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT, |
| 315 | functionNamed("callee")))); |
| 316 | } |
| 317 | |
| 318 | TEST(CollectEvidenceFromImplementationTest, UnknownArgPassed) { |
| 319 | static constexpr llvm::StringRef Src = R"cc( |
| 320 | void callee(int *q); |
| 321 | void target(int *p) { callee(p); } |
| 322 | )cc"; |
| 323 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 324 | Contains(evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT, |
| 325 | functionNamed("callee")))); |
| 326 | } |
| 327 | |
| 328 | TEST(CollectEvidenceFromImplementationTest, CheckedArgPassed) { |
| 329 | static constexpr llvm::StringRef Src = R"cc( |
| 330 | void callee(int *q); |
| 331 | void target(int *p) { |
| 332 | if (p) callee(p); |
| 333 | } |
| 334 | )cc"; |
| 335 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 336 | Contains(evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT, |
| 337 | functionNamed("callee")))); |
| 338 | } |
| 339 | |
| 340 | TEST(CollectEvidenceFromImplementationTest, NullptrPassed) { |
| 341 | static constexpr llvm::StringRef Src = R"cc( |
| 342 | void callee(int* q); |
| 343 | void target() { |
| 344 | callee(nullptr); |
| 345 | int* p = nullptr; |
| 346 | callee(nullptr); |
| 347 | } |
| 348 | )cc"; |
| 349 | EXPECT_THAT( |
| 350 | collectEvidenceFromTargetFunction(Src), |
| 351 | UnorderedElementsAre(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 352 | functionNamed("callee")), |
| 353 | evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 354 | functionNamed("callee")))); |
| 355 | } |
| 356 | |
| 357 | TEST(CollectEvidenceFromImplementationTest, NonPtrArgPassed) { |
| 358 | static constexpr llvm::StringRef Src = R"cc( |
| 359 | void callee(int q); |
| 360 | void target(int p) { callee(p); } |
| 361 | )cc"; |
| 362 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 363 | } |
| 364 | |
Googler | 3e3ae48 | 2023-07-19 08:50:59 -0700 | [diff] [blame] | 365 | TEST(CollectEvidenceFromImplementationTest, NullableReturn) { |
| 366 | static constexpr llvm::StringRef Src = R"cc( |
| 367 | int* target() { return nullptr; } |
| 368 | )cc"; |
| 369 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 370 | Contains(evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN, |
| 371 | functionNamed("target")))); |
| 372 | } |
| 373 | |
| 374 | TEST(CollectEvidenceFromImplementationTest, NonnullReturn) { |
| 375 | static constexpr llvm::StringRef Src = R"cc( |
| 376 | int* target(Nonnull<int*> p) { |
| 377 | return p; |
| 378 | } |
| 379 | )cc"; |
| 380 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 381 | Contains(evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN, |
| 382 | functionNamed("target")))); |
| 383 | } |
| 384 | |
| 385 | TEST(CollectEvidenceFromImplementationTest, UnknownReturn) { |
| 386 | static constexpr llvm::StringRef Src = R"cc( |
| 387 | int* target(int* p) { return p; } |
| 388 | )cc"; |
| 389 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 390 | Contains(evidence(SLOT_RETURN_TYPE, Evidence::UNKNOWN_RETURN, |
| 391 | functionNamed("target")))); |
| 392 | } |
| 393 | |
| 394 | TEST(CollectEvidenceFromImplementationTest, MultipleReturns) { |
| 395 | static constexpr llvm::StringRef Src = R"cc( |
| 396 | int* target(Nonnull<int*> p, Nullable<int*> q, bool b, bool c) { |
| 397 | if (b) return q; |
| 398 | if (c) return nullptr; |
| 399 | return p; |
| 400 | } |
| 401 | )cc"; |
| 402 | EXPECT_THAT( |
| 403 | collectEvidenceFromTargetFunction(Src), |
| 404 | UnorderedElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN, |
| 405 | functionNamed("target")), |
| 406 | evidence(SLOT_RETURN_TYPE, Evidence::NULLABLE_RETURN, |
| 407 | functionNamed("target")), |
| 408 | evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN, |
| 409 | functionNamed("target")))); |
| 410 | } |
| 411 | |
Googler | 2e9a797 | 2023-07-24 06:02:13 -0700 | [diff] [blame] | 412 | TEST(CollectEvidenceFromImplementationTest, MemberOperatorCall) { |
| 413 | static constexpr llvm::StringRef Src = R"cc( |
| 414 | struct S { |
| 415 | bool operator+(int*); |
| 416 | }; |
| 417 | void target() { S{} + nullptr; } |
| 418 | )cc"; |
| 419 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 420 | Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 421 | functionNamed("operator+")))); |
| 422 | } |
| 423 | |
| 424 | TEST(CollectEvidenceFromImplementationTest, NonMemberOperatorCall) { |
| 425 | static constexpr llvm::StringRef Src = R"cc( |
| 426 | struct S {}; |
| 427 | bool operator+(const S&, int*); |
| 428 | void target() { S{} + nullptr; } |
| 429 | )cc"; |
| 430 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 431 | Contains(evidence(paramSlot(1), Evidence::NULLABLE_ARGUMENT, |
| 432 | functionNamed("operator+")))); |
| 433 | } |
| 434 | |
| 435 | TEST(CollectEvidenceFromImplementationTest, VarArgs) { |
| 436 | static constexpr llvm::StringRef Src = R"cc( |
| 437 | void callee(int*...); |
| 438 | void target() { callee(nullptr, nullptr); } |
| 439 | )cc"; |
| 440 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 441 | Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 442 | functionNamed("callee")))); |
| 443 | } |
| 444 | |
| 445 | TEST(CollectEvidenceFromImplementationTest, MemberOperatorCallVarArgs) { |
| 446 | static constexpr llvm::StringRef Src = R"cc( |
| 447 | struct S { |
| 448 | bool operator()(int*...); |
| 449 | }; |
| 450 | void target() { S{}(nullptr, nullptr); } |
| 451 | )cc"; |
| 452 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), |
| 453 | Contains(evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 454 | functionNamed("operator()")))); |
| 455 | } |
| 456 | |
Googler | e2ee60c | 2023-11-02 11:07:30 -0700 | [diff] [blame] | 457 | TEST(CollectEvidenceFromImplementationTest, ConstructorCall) { |
| 458 | static constexpr llvm::StringRef Src = R"cc( |
| 459 | class S { |
| 460 | public: |
| 461 | S(Nonnull<int*> a); |
| 462 | }; |
| 463 | void target(int* p) { S s(p); } |
| 464 | )cc"; |
| 465 | EXPECT_THAT( |
| 466 | collectEvidenceFromTargetFunction(Src), |
| 467 | UnorderedElementsAre(evidence(paramSlot(0), Evidence::BOUND_TO_NONNULL, |
| 468 | functionNamed("target")), |
| 469 | evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT, |
| 470 | functionNamed("S")))); |
| 471 | } |
| 472 | |
Googler | 43114e8 | 2023-09-26 10:51:01 -0700 | [diff] [blame] | 473 | TEST(CollectEvidenceFromImplementationTest, PassedToNonnull) { |
| 474 | static constexpr llvm::StringRef Src = R"cc( |
| 475 | void callee(Nonnull<int*> i); |
| 476 | |
| 477 | void target(int* p) { callee(p); } |
| 478 | )cc"; |
Googler | 477b00d | 2023-11-02 10:38:52 -0700 | [diff] [blame] | 479 | EXPECT_THAT( |
| 480 | collectEvidenceFromTargetFunction(Src), |
| 481 | UnorderedElementsAre(evidence(paramSlot(0), Evidence::BOUND_TO_NONNULL, |
| 482 | functionNamed("target")), |
| 483 | evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT, |
| 484 | functionNamed("callee")))); |
| 485 | } |
| 486 | |
| 487 | TEST(CollectEvidenceFromImplementationTest, AssignedToNonnull) { |
| 488 | static constexpr llvm::StringRef Src = R"cc( |
| 489 | void target(int* p, int* q, int* r) { |
| 490 | Nonnull<int*> a = p, b = q; |
| 491 | a = r; |
| 492 | } |
| 493 | )cc"; |
| 494 | EXPECT_THAT( |
| 495 | collectEvidenceFromTargetFunction(Src), |
| 496 | UnorderedElementsAre(evidence(paramSlot(0), Evidence::BOUND_TO_NONNULL, |
| 497 | functionNamed("target")), |
| 498 | evidence(paramSlot(1), Evidence::BOUND_TO_NONNULL, |
| 499 | functionNamed("target")), |
| 500 | evidence(paramSlot(2), Evidence::BOUND_TO_NONNULL, |
| 501 | functionNamed("target")))); |
Googler | 43114e8 | 2023-09-26 10:51:01 -0700 | [diff] [blame] | 502 | } |
| 503 | |
Googler | 0b222ba | 2023-11-03 08:02:58 -0700 | [diff] [blame^] | 504 | TEST(CollectEvidenceFromImplementationTest, AssignedToNullableOrUnknown) { |
| 505 | static constexpr llvm::StringRef Src = R"cc( |
| 506 | void target(int* p, int* q, int* r) { |
| 507 | Nullable<int*> a = p; |
| 508 | int* b = q; |
| 509 | Unknown<int*> c = r; |
| 510 | q = r; |
| 511 | } |
| 512 | )cc"; |
| 513 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 514 | } |
| 515 | |
Googler | 1f3b8da | 2023-10-31 10:59:05 -0700 | [diff] [blame] | 516 | // A crash repro involving callable parameters. |
| 517 | TEST(CollectEvidenceFromImplementationTest, FunctionPointerParam) { |
| 518 | static constexpr llvm::StringRef Src = R"cc( |
| 519 | void target(void (*f)()) { f(); } |
| 520 | )cc"; |
| 521 | EXPECT_THAT(collectEvidenceFromTargetFunction(Src), IsEmpty()); |
| 522 | } |
| 523 | |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 524 | TEST(CollectEvidenceFromImplementationTest, NotInferenceTarget) { |
| 525 | static constexpr llvm::StringRef Src = R"cc( |
| 526 | template <typename T> |
| 527 | T* target(T* p) { |
| 528 | *p; |
| 529 | return nullptr; |
| 530 | } |
| 531 | |
| 532 | void instantiate() { target<int>(nullptr); } |
| 533 | )cc"; |
| 534 | |
| 535 | clang::TestAST AST(getInputsWithAnnotationDefinitions(Src)); |
| 536 | auto TargetInstantiationNodes = match( |
| 537 | functionDecl(hasName("target"), isTemplateInstantiation()).bind("target"), |
| 538 | AST.context()); |
| 539 | ASSERT_THAT(TargetInstantiationNodes, SizeIs(1)); |
| 540 | auto* const InstantiationDecl = ast_matchers::selectFirst<FunctionDecl>( |
| 541 | "target", TargetInstantiationNodes); |
| 542 | ASSERT_NE(InstantiationDecl, nullptr); |
| 543 | |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 544 | USRCache usr_cache; |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 545 | std::vector<Evidence> Results; |
| 546 | auto Err = collectEvidenceFromImplementation( |
| 547 | *InstantiationDecl, |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 548 | evidenceEmitter([&](const Evidence& E) { Results.push_back(E); }, |
| 549 | usr_cache), |
| 550 | usr_cache); |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 551 | if (Err) ADD_FAILURE() << toString(std::move(Err)); |
| 552 | EXPECT_THAT(Results, IsEmpty()); |
| 553 | } |
| 554 | |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 555 | TEST(CollectEvidenceFromImplementationTest, PropagatesPreviousInferences) { |
| 556 | static constexpr llvm::StringRef Src = R"cc( |
| 557 | void calledWithToBeNullable(int* x); |
| 558 | void calledWithToBeNonnull(int* a); |
| 559 | void target(int* p, int* q) { |
| 560 | target(nullptr, q); |
| 561 | calledWithToBeNullable(p); |
| 562 | *q; |
| 563 | calledWithToBeNonnull(q); |
| 564 | } |
| 565 | )cc"; |
| 566 | std::string TargetUsr = "c:@F@target#*I#S0_#"; |
| 567 | std::vector ExpectedBothRoundResults = { |
| 568 | evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 569 | AllOf(functionNamed("target"), |
| 570 | // Double-check that target's usr is as expected before we |
| 571 | // use it to create SlotFingerprints. |
| 572 | ResultOf([](Symbol S) { return S.usr(); }, TargetUsr))), |
| 573 | evidence(paramSlot(1), Evidence::UNCHECKED_DEREFERENCE, |
| 574 | functionNamed("target")), |
| 575 | }; |
| 576 | std::vector ExpectedSecondRoundResults = { |
| 577 | evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT, |
| 578 | functionNamed("calledWithToBeNullable")), |
| 579 | evidence(paramSlot(0), Evidence::NONNULL_ARGUMENT, |
| 580 | functionNamed("calledWithToBeNonnull"))}; |
| 581 | |
| 582 | // Only proceed if we have the correct USR for target and the first round |
| 583 | // results contain the evidence needed to produce our expected inferences and |
| 584 | // do not contain the evidence only found from propagating inferences from the |
| 585 | // first round. |
| 586 | auto FirstRoundResults = collectEvidenceFromTargetFunction(Src); |
| 587 | ASSERT_THAT(FirstRoundResults, IsSupersetOf(ExpectedBothRoundResults)); |
| 588 | for (const auto& E : ExpectedSecondRoundResults) { |
| 589 | ASSERT_THAT(FirstRoundResults, Not(Contains(E))); |
| 590 | } |
| 591 | |
Googler | 7191949 | 2023-10-23 15:44:06 -0700 | [diff] [blame] | 592 | EXPECT_THAT(collectEvidenceFromTargetFunction( |
| 593 | Src, {/*Nullable=*/{fingerprint(TargetUsr, paramSlot(0))}, |
| 594 | /*Nonnull=*/{fingerprint(TargetUsr, paramSlot(1))}}), |
| 595 | AllOf(IsSupersetOf(ExpectedBothRoundResults), |
| 596 | IsSupersetOf(ExpectedSecondRoundResults))); |
Googler | ac9ac80 | 2023-10-19 09:09:35 -0700 | [diff] [blame] | 597 | } |
| 598 | |
Googler | 2c9f53f | 2023-10-24 07:33:00 -0700 | [diff] [blame] | 599 | TEST(CollectEvidenceFromImplementationTest, |
| 600 | AnalysisUsesPreviousInferencesForSlotsOutsideTargetImplementation) { |
| 601 | static constexpr llvm::StringRef Src = R"cc( |
| 602 | int* returnsToBeNonnull(int* a) { |
| 603 | return a; |
| 604 | } |
| 605 | int* target(int* q) { |
| 606 | *q; |
| 607 | return returnsToBeNonnull(q); |
| 608 | } |
| 609 | )cc"; |
| 610 | std::string TargetUsr = "c:@F@target#*I#"; |
| 611 | std::string ReturnsToBeNonnullUsr = "c:@F@returnsToBeNonnull#*I#"; |
| 612 | const llvm::DenseMap<int, std::vector<testing::Matcher<const Evidence&>>> |
| 613 | ExpectedNewResultsPerRound = { |
Googler | 0b222ba | 2023-11-03 08:02:58 -0700 | [diff] [blame^] | 614 | {0, |
| 615 | {evidence( |
| 616 | paramSlot(0), Evidence::UNCHECKED_DEREFERENCE, |
| 617 | AllOf(functionNamed("target"), |
| 618 | // Double-check that target's usr is as expected before |
| 619 | // we use it to create SlotFingerprints. |
| 620 | ResultOf([](Symbol S) { return S.usr(); }, TargetUsr)))}}, |
| 621 | {1, |
| 622 | {evidence( |
| 623 | paramSlot(0), Evidence::NONNULL_ARGUMENT, |
| 624 | AllOf(functionNamed("returnsToBeNonnull"), |
| 625 | // Double-check that returnsToBeNonnull's usr is as |
| 626 | // expected before we use it to create SlotFingerprints. |
| 627 | ResultOf([](Symbol S) { return S.usr(); }, |
| 628 | ReturnsToBeNonnullUsr)))}}, |
| 629 | {2, |
| 630 | { |
| 631 | // No new evidence from target's implementation in this round, |
| 632 | // but in a full-TU analysis, this would be the round where we |
| 633 | // decide returnsToBeNonnull returns Nonnull, based on the |
| 634 | // now-Nonnull argument that is the only return value. |
| 635 | }}, |
| 636 | {3, |
| 637 | {evidence(SLOT_RETURN_TYPE, Evidence::NONNULL_RETURN, |
| 638 | functionNamed("target"))}}}; |
Googler | 2c9f53f | 2023-10-24 07:33:00 -0700 | [diff] [blame] | 639 | |
| 640 | // Assert first round results because they don't rely on previous inference |
| 641 | // propagation at all and in this case are test setup and preconditions. |
| 642 | auto FirstRoundResults = collectEvidenceFromTargetFunction(Src); |
| 643 | ASSERT_THAT(FirstRoundResults, |
| 644 | IsSupersetOf(ExpectedNewResultsPerRound.at(0))); |
| 645 | for (const auto& E : ExpectedNewResultsPerRound.at(1)) { |
| 646 | ASSERT_THAT(FirstRoundResults, Not(Contains(E))); |
| 647 | } |
| 648 | |
| 649 | auto SecondRoundResults = collectEvidenceFromTargetFunction( |
| 650 | Src, {.Nonnull = {fingerprint(TargetUsr, paramSlot(0))}}); |
| 651 | EXPECT_THAT(SecondRoundResults, |
| 652 | AllOf(IsSupersetOf(ExpectedNewResultsPerRound.at(0)), |
| 653 | IsSupersetOf(ExpectedNewResultsPerRound.at(1)))); |
| 654 | for (const auto& E : ExpectedNewResultsPerRound.at(2)) { |
| 655 | ASSERT_THAT(SecondRoundResults, Not(Contains(E))); |
| 656 | } |
| 657 | |
| 658 | auto ThirdRoundResults = collectEvidenceFromTargetFunction( |
| 659 | Src, {.Nonnull = {fingerprint(TargetUsr, paramSlot(0)), |
| 660 | fingerprint(ReturnsToBeNonnullUsr, paramSlot(0))}}); |
| 661 | EXPECT_THAT(ThirdRoundResults, |
| 662 | AllOf(IsSupersetOf(ExpectedNewResultsPerRound.at(0)), |
| 663 | IsSupersetOf(ExpectedNewResultsPerRound.at(1)), |
| 664 | IsSupersetOf(ExpectedNewResultsPerRound.at(2)))); |
| 665 | for (const auto& E : ExpectedNewResultsPerRound.at(3)) { |
| 666 | ASSERT_THAT(ThirdRoundResults, Not(Contains(E))); |
| 667 | } |
| 668 | |
| 669 | auto FourthRoundResults = collectEvidenceFromTargetFunction( |
| 670 | Src, |
| 671 | {.Nonnull = { |
| 672 | fingerprint(TargetUsr, paramSlot(0)), |
| 673 | fingerprint(ReturnsToBeNonnullUsr, paramSlot(0)), |
| 674 | // As noted in the Evidence matcher list above, we don't infer the |
| 675 | // return type of returnsToBeNonnull from only collecting evidence |
| 676 | // from target's implementation, but for the sake of this test, let's |
| 677 | // pretend we collected evidence from the entire TU. |
| 678 | fingerprint(ReturnsToBeNonnullUsr, SLOT_RETURN_TYPE)}}); |
| 679 | EXPECT_THAT(FourthRoundResults, |
| 680 | AllOf(IsSupersetOf(ExpectedNewResultsPerRound.at(0)), |
| 681 | IsSupersetOf(ExpectedNewResultsPerRound.at(1)), |
| 682 | IsSupersetOf(ExpectedNewResultsPerRound.at(2)), |
| 683 | IsSupersetOf(ExpectedNewResultsPerRound.at(3)))); |
| 684 | } |
| 685 | |
Googler | 0b222ba | 2023-11-03 08:02:58 -0700 | [diff] [blame^] | 686 | TEST(CollectEvidenceFromImplementationTest, |
| 687 | PreviousInferencesOfNonTargetParameterNullabilitiesPropagate) { |
| 688 | static constexpr llvm::StringRef Src = R"cc( |
| 689 | void takesToBeNonnull(int* a) { |
| 690 | // Not read when collecting evidence only from Target, but corresponding |
| 691 | // inference is explicitly input below. |
| 692 | *a; |
| 693 | } |
| 694 | void target(int* q) { takesToBeNonnull(q); } |
| 695 | )cc"; |
| 696 | std::string TakesToBeNonnullUsr = "c:@F@takesToBeNonnull#*I#"; |
| 697 | |
| 698 | // Pretend that in a first round of inferring for all functions, we made this |
| 699 | // inference about takesToBeNonnull's first parameter. |
| 700 | // This test confirms that we use that information when collecting from |
| 701 | // target's implementation. |
| 702 | EXPECT_THAT( |
| 703 | collectEvidenceFromTargetFunction( |
| 704 | Src, {.Nonnull = {fingerprint(TakesToBeNonnullUsr, paramSlot(0))}}), |
| 705 | Contains(evidence(paramSlot(0), Evidence::BOUND_TO_NONNULL, |
| 706 | functionNamed("target")))); |
| 707 | } |
| 708 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 709 | TEST(CollectEvidenceFromDeclarationTest, VariableDeclIgnored) { |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 710 | llvm::StringLiteral Src = "Nullable<int *> target;"; |
| 711 | EXPECT_THAT(collectEvidenceFromTargetDecl(Src), IsEmpty()); |
| 712 | } |
| 713 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 714 | TEST(CollectEvidenceFromDeclarationTest, FunctionDeclReturnType) { |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 715 | llvm::StringLiteral Src = "Nonnull<int *> target();"; |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 716 | EXPECT_THAT( |
| 717 | collectEvidenceFromTargetDecl(Src), |
| 718 | ElementsAre(evidence(SLOT_RETURN_TYPE, Evidence::ANNOTATED_NONNULL, |
| 719 | functionNamed("target")))); |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 720 | } |
| 721 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 722 | TEST(CollectEvidenceFromDeclarationTest, FunctionDeclParams) { |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 723 | llvm::StringLiteral Src = "void target(Nullable<int*>, int*, Nonnull<int*>);"; |
| 724 | EXPECT_THAT(collectEvidenceFromTargetDecl(Src), |
Sam McCall | 83bc55c | 2023-07-17 09:47:17 -0700 | [diff] [blame] | 725 | ElementsAre(evidence(paramSlot(0), Evidence::ANNOTATED_NULLABLE), |
| 726 | evidence(paramSlot(2), Evidence::ANNOTATED_NONNULL))); |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 727 | } |
| 728 | |
Googler | e1504a6 | 2023-07-18 14:03:23 -0700 | [diff] [blame] | 729 | TEST(CollectEvidenceFromDeclarationTest, FunctionDeclNonTopLevel) { |
Sam McCall | bd1a6e5 | 2023-07-14 01:04:11 -0700 | [diff] [blame] | 730 | llvm::StringLiteral Src = "Nonnull<int*>** target(Nullable<int*>*);"; |
| 731 | EXPECT_THAT(collectEvidenceFromTargetDecl(Src), IsEmpty()); |
Googler | e9210aa | 2023-07-13 10:55:06 -0700 | [diff] [blame] | 732 | } |
| 733 | |
Sam McCall | dba6897 | 2023-07-19 11:01:10 -0700 | [diff] [blame] | 734 | TEST(CollectEvidenceFromDeclarationTest, FunctionTemplateIgnored) { |
| 735 | // We used to inspect the type of `target` and crash. |
| 736 | llvm::StringLiteral Src = R"cc( |
| 737 | template <class A> |
| 738 | struct S { |
| 739 | template <class B> |
| 740 | static void target(const S<B>&) {} |
| 741 | }; |
| 742 | )cc"; |
| 743 | EXPECT_THAT(collectEvidenceFromTargetDecl(Src), IsEmpty()); |
| 744 | } |
| 745 | |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 746 | MATCHER_P(declNamed, Name, "") { |
| 747 | std::string Actual; |
| 748 | llvm::raw_string_ostream OS(Actual); |
| 749 | if (auto* ND = dyn_cast<NamedDecl>(arg)) |
| 750 | ND->getNameForDiagnostic( |
| 751 | OS, arg->getDeclContext()->getParentASTContext().getPrintingPolicy(), |
| 752 | /*Qualified=*/true); |
| 753 | return ::testing::ExplainMatchResult(Name, Actual, result_listener); |
| 754 | } |
| 755 | |
| 756 | TEST(EvidenceSitesTest, Functions) { |
| 757 | TestAST AST(R"cc( |
| 758 | void foo(); |
| 759 | void bar(); |
| 760 | void bar() {} |
| 761 | void baz() {} |
| 762 | auto Lambda = []() {}; // Not analyzed yet. |
| 763 | |
| 764 | struct S { |
Googler | e2ee60c | 2023-11-02 11:07:30 -0700 | [diff] [blame] | 765 | S() {} |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 766 | void member(); |
| 767 | }; |
| 768 | void S::member() {} |
| 769 | )cc"); |
| 770 | auto Sites = EvidenceSites::discover(AST.context()); |
| 771 | EXPECT_THAT(Sites.Declarations, |
| 772 | ElementsAre(declNamed("foo"), declNamed("bar"), declNamed("bar"), |
Googler | e2ee60c | 2023-11-02 11:07:30 -0700 | [diff] [blame] | 773 | declNamed("baz"), declNamed("S::S"), |
| 774 | declNamed("S::member"), declNamed("S::member"))); |
| 775 | EXPECT_THAT(Sites.Implementations, |
| 776 | ElementsAre(declNamed("bar"), declNamed("baz"), declNamed("S::S"), |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 777 | declNamed("S::member"))); |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 778 | } |
| 779 | |
| 780 | TEST(EvidenceSitesTest, Variables) { |
| 781 | TestAST AST(R"cc( |
| 782 | int* x = true ? nullptr : nullptr; |
| 783 | struct S { |
| 784 | int* s; |
| 785 | }; |
| 786 | )cc"); |
| 787 | auto Sites = EvidenceSites::discover(AST.context()); |
Googler | f1f793d | 2023-10-19 07:51:34 -0700 | [diff] [blame] | 788 | // For now, variables are not inferable. |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 789 | EXPECT_THAT(Sites.Declarations, IsEmpty()); |
| 790 | // For now, we don't examine variable initializers. |
| 791 | EXPECT_THAT(Sites.Implementations, IsEmpty()); |
| 792 | } |
| 793 | |
| 794 | TEST(EvidenceSitesTest, Templates) { |
| 795 | TestAST AST(R"cc( |
| 796 | template <int I> |
| 797 | int f() { |
| 798 | return I; |
| 799 | } |
| 800 | template <> |
| 801 | int f<1>() { |
| 802 | return 1; |
| 803 | } |
| 804 | |
| 805 | struct S { |
| 806 | template <int I> |
| 807 | int f() { |
| 808 | return I; |
| 809 | } |
| 810 | }; |
| 811 | |
| 812 | template <int I> |
| 813 | struct T { |
| 814 | int f() { return I; } |
| 815 | }; |
| 816 | |
| 817 | auto Unused = f<0>() + f<1>() + S{}.f<0>() + T<0>{}.f(); |
| 818 | )cc"); |
| 819 | auto Sites = EvidenceSites::discover(AST.context()); |
| 820 | |
Sam McCall | dba6897 | 2023-07-19 11:01:10 -0700 | [diff] [blame] | 821 | // Relevant declarations are the written ones that are not templates. |
| 822 | EXPECT_THAT(Sites.Declarations, ElementsAre(declNamed("f<1>"))); |
Sam McCall | 296d070 | 2023-07-14 13:32:57 -0700 | [diff] [blame] | 823 | // Instantiations are relevant inference targets. |
| 824 | EXPECT_THAT(Sites.Implementations, |
| 825 | ElementsAre(declNamed("f<0>"), declNamed("f<1>"), |
| 826 | declNamed("S::f<0>"), declNamed("T<0>::f"))); |
| 827 | } |
| 828 | |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 829 | TEST(EvidenceEmitterTest, NotInferenceTarget) { |
| 830 | TestAST AST(R"cc( |
| 831 | template <int I> |
| 832 | int target() { |
| 833 | return I; |
| 834 | })cc"); |
| 835 | |
| 836 | const auto* TargetDecl = |
| 837 | dataflow::test::findValueDecl(AST.context(), "target"); |
| 838 | ASSERT_NE(TargetDecl, nullptr); |
| 839 | |
Googler | 6acdc64 | 2023-10-19 08:03:40 -0700 | [diff] [blame] | 840 | USRCache usr_cache; |
| 841 | EXPECT_DEATH(evidenceEmitter([](const Evidence& e) {}, usr_cache)( |
Googler | 6604531 | 2023-09-11 12:28:58 -0700 | [diff] [blame] | 842 | *TargetDecl, Slot{}, Evidence::ANNOTATED_UNKNOWN, |
| 843 | TargetDecl->getLocation()), |
| 844 | "not an inference target"); |
| 845 | } |
| 846 | |
Googler | 113194c | 2023-07-12 11:03:47 -0700 | [diff] [blame] | 847 | } // namespace |
| 848 | } // namespace clang::tidy::nullability |