Sam McCall | b70563a | 2023-07-28 08:31:23 -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 | |
Googler | f1f793d | 2023-10-19 07:51:34 -0700 | [diff] [blame] | 5 | #include "nullability/inference/inferable.h" |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 6 | |
Googler | 73cceb2 | 2024-05-07 08:11:10 -0700 | [diff] [blame] | 7 | #include "nullability/type_nullability.h" |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 8 | #include "clang/AST/Decl.h" |
| 9 | #include "clang/AST/DeclBase.h" |
| 10 | #include "clang/AST/DeclCXX.h" |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 11 | #include "clang/AST/DeclTemplate.h" |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 12 | #include "clang/AST/Expr.h" |
Googler | 30f598e | 2024-07-11 09:57:52 -0700 | [diff] [blame] | 13 | #include "clang/AST/ExprCXX.h" |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 14 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 15 | #include "clang/ASTMatchers/ASTMatchers.h" |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 16 | #include "clang/Basic/LLVM.h" |
| 17 | #include "clang/Testing/TestAST.h" |
| 18 | #include "llvm/ADT/StringRef.h" |
| 19 | #include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h" |
| 20 | |
| 21 | namespace clang::tidy::nullability { |
| 22 | namespace { |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 23 | using ::clang::ast_matchers::anything; |
| 24 | using ::clang::ast_matchers::equalsNode; |
| 25 | using ::clang::ast_matchers::hasDeclContext; |
| 26 | using ::clang::ast_matchers::hasName; |
| 27 | using ::clang::ast_matchers::isImplicit; |
| 28 | using ::clang::ast_matchers::match; |
| 29 | using ::clang::ast_matchers::namedDecl; |
| 30 | using ::clang::ast_matchers::unless; |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 31 | |
Googler | 73cceb2 | 2024-05-07 08:11:10 -0700 | [diff] [blame] | 32 | test::EnableSmartPointers Enable; |
| 33 | |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 34 | template <class T = NamedDecl> |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 35 | const T &lookup(llvm::StringRef Name, ASTContext &Ctx, |
| 36 | const Decl *DeclContext = nullptr) { |
| 37 | const auto &ContextMatcher = |
| 38 | DeclContext ? hasDeclContext(equalsNode(DeclContext)) : anything(); |
| 39 | const auto &BoundNodes = |
| 40 | match(namedDecl(hasName(Name), ContextMatcher, unless(isImplicit())) |
| 41 | .bind("decl"), |
| 42 | Ctx); |
| 43 | const T *Match = nullptr; |
| 44 | for (const auto &N : BoundNodes) { |
| 45 | if (const auto *NAsT = N.getNodeAs<T>("decl")) { |
| 46 | if (Match) |
| 47 | ADD_FAILURE() << "Found more than one matching node for " << Name; |
| 48 | Match = NAsT; |
| 49 | } |
| 50 | } |
| 51 | EXPECT_NE(Match, nullptr) << Name; |
| 52 | return *Match; |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 53 | } |
| 54 | |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 55 | constexpr llvm::StringRef SmartPointerHeader = R"cc( |
| 56 | namespace std { |
| 57 | template <typename T> |
| 58 | struct unique_ptr { |
| 59 | using pointer = T*; |
| 60 | }; |
| 61 | } // namespace std |
| 62 | |
| 63 | template <typename T> |
| 64 | struct custom_smart_ptr { |
| 65 | using absl_nullability_compatible = void; |
| 66 | using pointer = T*; |
| 67 | }; |
| 68 | )cc"; |
| 69 | |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 70 | TEST(IsInferenceTargetTest, GlobalVariables) { |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 71 | TestAST AST((SmartPointerHeader + R"cc( |
| 72 | int* Pointer; |
| 73 | std::unique_ptr<int> StdSmartPointer; |
| 74 | custom_smart_ptr<int> CustomSmartPointer; |
| 75 | int NotPointer; |
| 76 | )cc") |
| 77 | .str()); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 78 | |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 79 | auto &Ctx = AST.context(); |
| 80 | EXPECT_TRUE(isInferenceTarget(lookup("Pointer", Ctx))); |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 81 | EXPECT_TRUE(isInferenceTarget(lookup("StdSmartPointer", Ctx))); |
| 82 | EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartPointer", Ctx))); |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 83 | EXPECT_FALSE(isInferenceTarget(lookup("NotPointer", Ctx))); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | TEST(IsInferenceTargetTest, Functions) { |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 87 | TestAST AST((SmartPointerHeader + R"cc( |
| 88 | int* func(int*, int**, std::unique_ptr<int>, |
| 89 | custom_smart_ptr<int>) { |
| 90 | int* Local; |
| 91 | static int* StaticLocal; |
| 92 | std::unique_ptr<int> StdSmartLocal; |
| 93 | custom_smart_ptr<int> CustomSmartLocal; |
| 94 | } |
| 95 | void empty() {} |
| 96 | auto Lambda = []() {}; |
Googler | 30f598e | 2024-07-11 09:57:52 -0700 | [diff] [blame] | 97 | auto LambdaWithPtr = [](int*) {}; |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 98 | )cc") |
| 99 | .str()); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 100 | |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 101 | auto &Ctx = AST.context(); |
| 102 | EXPECT_TRUE(isInferenceTarget(lookup("func", Ctx))); |
Googler | e41a588 | 2024-04-26 08:41:36 -0700 | [diff] [blame] | 103 | EXPECT_FALSE(isInferenceTarget(lookup("Local", Ctx))); |
| 104 | EXPECT_FALSE(isInferenceTarget(lookup("StaticLocal", Ctx))); |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 105 | EXPECT_FALSE(isInferenceTarget(lookup("StdSmartLocal", Ctx))); |
| 106 | EXPECT_FALSE(isInferenceTarget(lookup("CustomSmartLocal", Ctx))); |
Googler | 30f598e | 2024-07-11 09:57:52 -0700 | [diff] [blame] | 107 | EXPECT_FALSE(isInferenceTarget(lookup("empty", Ctx))); |
| 108 | auto &Lambda = lookup<VarDecl>("Lambda", Ctx); |
| 109 | auto *LambdaCtx = cast<LambdaExpr>(Lambda.getInit())->getLambdaClass(); |
| 110 | EXPECT_FALSE(isInferenceTarget(Lambda)); |
| 111 | EXPECT_FALSE(isInferenceTarget(lookup("operator()", Ctx, LambdaCtx))); |
| 112 | auto &LambdaWithPtr = lookup<VarDecl>("LambdaWithPtr", Ctx); |
| 113 | auto *LambdaWithPtrCtx = |
| 114 | cast<LambdaExpr>(LambdaWithPtr.getInit())->getLambdaClass(); |
| 115 | EXPECT_FALSE(isInferenceTarget(LambdaWithPtr)); |
| 116 | EXPECT_TRUE(isInferenceTarget(lookup("operator()", Ctx, LambdaWithPtrCtx))); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | TEST(IsInferenceTargetTest, ClassAndMembers) { |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 120 | TestAST AST((SmartPointerHeader + R"cc( |
| 121 | class C { |
| 122 | void method(); |
Googler | 30f598e | 2024-07-11 09:57:52 -0700 | [diff] [blame] | 123 | int* methodWithPtr(); |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 124 | int NonPtrField; |
| 125 | int* PtrField; |
| 126 | static int* StaticField; |
| 127 | std::unique_ptr<int> StdSmartField; |
| 128 | custom_smart_ptr<int> CustomSmartField; |
| 129 | }; |
| 130 | )cc") |
| 131 | .str()); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 132 | |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 133 | auto &Ctx = AST.context(); |
| 134 | EXPECT_FALSE(isInferenceTarget(lookup<CXXRecordDecl>("C", Ctx))); |
Googler | 30f598e | 2024-07-11 09:57:52 -0700 | [diff] [blame] | 135 | EXPECT_FALSE(isInferenceTarget(lookup("method", Ctx))); |
| 136 | EXPECT_TRUE(isInferenceTarget(lookup("methodWithPtr", Ctx))); |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 137 | EXPECT_FALSE(isInferenceTarget(lookup("NonPtrField", Ctx))); |
Googler | e41a588 | 2024-04-26 08:41:36 -0700 | [diff] [blame] | 138 | EXPECT_TRUE(isInferenceTarget(lookup("PtrField", Ctx))); |
| 139 | EXPECT_TRUE(isInferenceTarget(lookup("StaticField", Ctx))); |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 140 | EXPECT_TRUE(isInferenceTarget(lookup("StdSmartField", Ctx))); |
| 141 | EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartField", Ctx))); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 142 | } |
| 143 | |
| 144 | TEST(IsInferenceTargetTest, FunctionTemplate) { |
| 145 | TestAST AST(R"cc( |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 146 | template <int X> |
| 147 | void funcTmpl(int*) {} |
| 148 | |
| 149 | auto& FuncTmplSpec = funcTmpl<2>; |
| 150 | )cc"); |
| 151 | |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 152 | auto &Ctx = AST.context(); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 153 | // A function template is not an inference target. |
| 154 | const FunctionTemplateDecl &FuncTmpl = |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 155 | lookup<FunctionTemplateDecl>("funcTmpl", Ctx); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 156 | EXPECT_FALSE(isInferenceTarget(FuncTmpl)); |
| 157 | EXPECT_FALSE(isInferenceTarget(*FuncTmpl.getTemplatedDecl())); |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 158 | // The function template specialization is *also* not an inference target. |
Googler | 30f598e | 2024-07-11 09:57:52 -0700 | [diff] [blame] | 159 | const ValueDecl &Specialization = |
| 160 | *cast<DeclRefExpr>( |
| 161 | lookup<VarDecl>("FuncTmplSpec", Ctx).getInit()->IgnoreImplicit()) |
| 162 | ->getDecl(); |
| 163 | EXPECT_FALSE(isInferenceTarget(Specialization)); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 164 | } |
| 165 | |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 166 | TEST(IsInferenceTargetTest, ClassTemplateAndMembers) { |
| 167 | TestAST AST(R"cc( |
| 168 | template <typename T> |
| 169 | struct ClassTemplate { |
| 170 | T NonPtrField; |
| 171 | T* PtrField; |
Googler | e41a588 | 2024-04-26 08:41:36 -0700 | [diff] [blame] | 172 | static T* StaticField; |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 173 | }; |
| 174 | |
| 175 | ClassTemplate<int> I; |
| 176 | int A = I.NonPtrField; |
| 177 | int* B = I.PtrField; |
Googler | e41a588 | 2024-04-26 08:41:36 -0700 | [diff] [blame] | 178 | int* C = I.StaticField; |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 179 | )cc"); |
| 180 | |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 181 | auto &Ctx = AST.context(); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 182 | // Class templates and their fields are not inference targets. |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 183 | auto &ClassTemplate = lookup<ClassTemplateDecl>("ClassTemplate", Ctx); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 184 | EXPECT_FALSE(isInferenceTarget(ClassTemplate)); |
| 185 | EXPECT_FALSE(isInferenceTarget(*ClassTemplate.getTemplatedDecl())); |
| 186 | EXPECT_FALSE(isInferenceTarget( |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 187 | lookup("NonPtrField", Ctx, ClassTemplate.getTemplatedDecl()))); |
| 188 | EXPECT_FALSE(isInferenceTarget( |
| 189 | lookup("PtrField", Ctx, ClassTemplate.getTemplatedDecl()))); |
Googler | e41a588 | 2024-04-26 08:41:36 -0700 | [diff] [blame] | 190 | EXPECT_FALSE(isInferenceTarget( |
| 191 | lookup("StaticField", Ctx, ClassTemplate.getTemplatedDecl()))); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 192 | |
| 193 | // Class template specializations and their fields are also not inference |
| 194 | // targets. |
| 195 | EXPECT_FALSE(isInferenceTarget( |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 196 | *lookup<VarDecl>("I", Ctx).getType()->getAsRecordDecl())); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 197 | EXPECT_FALSE(isInferenceTarget( |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 198 | *cast<MemberExpr>(lookup<VarDecl>("A", Ctx).getInit()->IgnoreImplicit()) |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 199 | ->getMemberDecl())); |
| 200 | EXPECT_FALSE(isInferenceTarget( |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 201 | *cast<MemberExpr>(lookup<VarDecl>("B", Ctx).getInit()->IgnoreImplicit()) |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 202 | ->getMemberDecl())); |
Googler | e41a588 | 2024-04-26 08:41:36 -0700 | [diff] [blame] | 203 | EXPECT_FALSE(isInferenceTarget( |
| 204 | *cast<MemberExpr>(lookup<VarDecl>("C", Ctx).getInit()->IgnoreImplicit()) |
| 205 | ->getMemberDecl())); |
Googler | ed92936 | 2024-04-18 07:33:38 -0700 | [diff] [blame] | 206 | } |
| 207 | |
Googler | f1f793d | 2023-10-19 07:51:34 -0700 | [diff] [blame] | 208 | TEST(InferableTest, CountInferableSlots) { |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 209 | TestAST AST((SmartPointerHeader + R"cc( |
| 210 | using Pointer = int *; |
| 211 | template <class T> |
| 212 | struct S; |
| 213 | struct T; |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 214 | |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 215 | void f1(int *); |
| 216 | void f2(Pointer); |
| 217 | void f3(int **); |
| 218 | void f4(Pointer *); |
| 219 | void f5(int *&); |
| 220 | void f6(int (*)()); // function pointer |
| 221 | void f7(std::unique_ptr<int>); |
| 222 | void f8(custom_smart_ptr<int>); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 223 | |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 224 | int *g1(int); |
| 225 | Pointer g2(int); |
| 226 | std::unique_ptr<int> g3(int); |
| 227 | custom_smart_ptr<int> g4(int); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 228 | |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 229 | void h1(S<int *>); |
| 230 | void h2(int T::*); // pointer to data member |
| 231 | void h3(int (T::*)()); // pointer to member function |
| 232 | )cc") |
| 233 | .str()); |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 234 | auto &Ctx = AST.context(); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 235 | |
| 236 | // All the 'f's have a single pointer arg. |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 237 | EXPECT_EQ(1, countInferableSlots(lookup("f1", Ctx))); |
| 238 | EXPECT_EQ(1, countInferableSlots(lookup("f2", Ctx))); |
| 239 | EXPECT_EQ(1, countInferableSlots(lookup("f3", Ctx))); |
| 240 | EXPECT_EQ(1, countInferableSlots(lookup("f4", Ctx))); |
| 241 | EXPECT_EQ(1, countInferableSlots(lookup("f5", Ctx))); |
| 242 | EXPECT_EQ(1, countInferableSlots(lookup("f6", Ctx))); |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 243 | EXPECT_EQ(1, countInferableSlots(lookup("f7", Ctx))); |
| 244 | EXPECT_EQ(1, countInferableSlots(lookup("f8", Ctx))); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 245 | |
| 246 | // All the 'g's have a pointer return. |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 247 | EXPECT_EQ(1, countInferableSlots(lookup("g1", Ctx))); |
| 248 | EXPECT_EQ(1, countInferableSlots(lookup("g2", Ctx))); |
Googler | e29825e | 2024-05-17 06:51:50 -0700 | [diff] [blame] | 249 | EXPECT_EQ(1, countInferableSlots(lookup("g3", Ctx))); |
| 250 | EXPECT_EQ(1, countInferableSlots(lookup("g4", Ctx))); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 251 | |
| 252 | // The 'h's have types that aren't really pointers. |
Googler | bdb2136 | 2024-04-26 08:37:42 -0700 | [diff] [blame] | 253 | EXPECT_EQ(0, countInferableSlots(lookup("h1", Ctx))); |
| 254 | EXPECT_EQ(0, countInferableSlots(lookup("h2", Ctx))); |
| 255 | EXPECT_EQ(0, countInferableSlots(lookup("h3", Ctx))); |
Sam McCall | b70563a | 2023-07-28 08:31:23 -0700 | [diff] [blame] | 256 | } |
| 257 | |
| 258 | } // namespace |
| 259 | } // namespace clang::tidy::nullability |