blob: 1ab2484b4b74ac74b7c99df406a6ddf5630090ff [file] [log] [blame]
Sam McCallb70563a2023-07-28 08:31:23 -07001// 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
Googlerf1f793d2023-10-19 07:51:34 -07005#include "nullability/inference/inferable.h"
Sam McCallb70563a2023-07-28 08:31:23 -07006
Googler73cceb22024-05-07 08:11:10 -07007#include "nullability/type_nullability.h"
Sam McCallb70563a2023-07-28 08:31:23 -07008#include "clang/AST/Decl.h"
9#include "clang/AST/DeclBase.h"
10#include "clang/AST/DeclCXX.h"
Googlered929362024-04-18 07:33:38 -070011#include "clang/AST/DeclTemplate.h"
Sam McCallb70563a2023-07-28 08:31:23 -070012#include "clang/AST/Expr.h"
Googler30f598e2024-07-11 09:57:52 -070013#include "clang/AST/ExprCXX.h"
Googlerbdb21362024-04-26 08:37:42 -070014#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/ASTMatchers/ASTMatchers.h"
Sam McCallb70563a2023-07-28 08:31:23 -070016#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
21namespace clang::tidy::nullability {
22namespace {
Googlerbdb21362024-04-26 08:37:42 -070023using ::clang::ast_matchers::anything;
24using ::clang::ast_matchers::equalsNode;
25using ::clang::ast_matchers::hasDeclContext;
26using ::clang::ast_matchers::hasName;
27using ::clang::ast_matchers::isImplicit;
28using ::clang::ast_matchers::match;
29using ::clang::ast_matchers::namedDecl;
30using ::clang::ast_matchers::unless;
Sam McCallb70563a2023-07-28 08:31:23 -070031
Googler73cceb22024-05-07 08:11:10 -070032test::EnableSmartPointers Enable;
33
Sam McCallb70563a2023-07-28 08:31:23 -070034template <class T = NamedDecl>
Googlerbdb21362024-04-26 08:37:42 -070035const 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 McCallb70563a2023-07-28 08:31:23 -070053}
54
Googlere29825e2024-05-17 06:51:50 -070055constexpr 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
Googlered929362024-04-18 07:33:38 -070070TEST(IsInferenceTargetTest, GlobalVariables) {
Googlere29825e2024-05-17 06:51:50 -070071 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());
Googlered929362024-04-18 07:33:38 -070078
Googlerbdb21362024-04-26 08:37:42 -070079 auto &Ctx = AST.context();
80 EXPECT_TRUE(isInferenceTarget(lookup("Pointer", Ctx)));
Googlere29825e2024-05-17 06:51:50 -070081 EXPECT_TRUE(isInferenceTarget(lookup("StdSmartPointer", Ctx)));
82 EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartPointer", Ctx)));
Googlerbdb21362024-04-26 08:37:42 -070083 EXPECT_FALSE(isInferenceTarget(lookup("NotPointer", Ctx)));
Googlered929362024-04-18 07:33:38 -070084}
85
86TEST(IsInferenceTargetTest, Functions) {
Googlere29825e2024-05-17 06:51:50 -070087 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 = []() {};
Googler30f598e2024-07-11 09:57:52 -070097 auto LambdaWithPtr = [](int*) {};
Googlere29825e2024-05-17 06:51:50 -070098 )cc")
99 .str());
Sam McCallb70563a2023-07-28 08:31:23 -0700100
Googlerbdb21362024-04-26 08:37:42 -0700101 auto &Ctx = AST.context();
102 EXPECT_TRUE(isInferenceTarget(lookup("func", Ctx)));
Googlere41a5882024-04-26 08:41:36 -0700103 EXPECT_FALSE(isInferenceTarget(lookup("Local", Ctx)));
104 EXPECT_FALSE(isInferenceTarget(lookup("StaticLocal", Ctx)));
Googlere29825e2024-05-17 06:51:50 -0700105 EXPECT_FALSE(isInferenceTarget(lookup("StdSmartLocal", Ctx)));
106 EXPECT_FALSE(isInferenceTarget(lookup("CustomSmartLocal", Ctx)));
Googler30f598e2024-07-11 09:57:52 -0700107 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)));
Googlered929362024-04-18 07:33:38 -0700117}
118
119TEST(IsInferenceTargetTest, ClassAndMembers) {
Googlere29825e2024-05-17 06:51:50 -0700120 TestAST AST((SmartPointerHeader + R"cc(
121 class C {
122 void method();
Googler30f598e2024-07-11 09:57:52 -0700123 int* methodWithPtr();
Googlere29825e2024-05-17 06:51:50 -0700124 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 McCallb70563a2023-07-28 08:31:23 -0700132
Googlerbdb21362024-04-26 08:37:42 -0700133 auto &Ctx = AST.context();
134 EXPECT_FALSE(isInferenceTarget(lookup<CXXRecordDecl>("C", Ctx)));
Googler30f598e2024-07-11 09:57:52 -0700135 EXPECT_FALSE(isInferenceTarget(lookup("method", Ctx)));
136 EXPECT_TRUE(isInferenceTarget(lookup("methodWithPtr", Ctx)));
Googlerbdb21362024-04-26 08:37:42 -0700137 EXPECT_FALSE(isInferenceTarget(lookup("NonPtrField", Ctx)));
Googlere41a5882024-04-26 08:41:36 -0700138 EXPECT_TRUE(isInferenceTarget(lookup("PtrField", Ctx)));
139 EXPECT_TRUE(isInferenceTarget(lookup("StaticField", Ctx)));
Googlere29825e2024-05-17 06:51:50 -0700140 EXPECT_TRUE(isInferenceTarget(lookup("StdSmartField", Ctx)));
141 EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartField", Ctx)));
Googlered929362024-04-18 07:33:38 -0700142}
143
144TEST(IsInferenceTargetTest, FunctionTemplate) {
145 TestAST AST(R"cc(
Sam McCallb70563a2023-07-28 08:31:23 -0700146 template <int X>
147 void funcTmpl(int*) {}
148
149 auto& FuncTmplSpec = funcTmpl<2>;
150 )cc");
151
Googlerbdb21362024-04-26 08:37:42 -0700152 auto &Ctx = AST.context();
Sam McCallb70563a2023-07-28 08:31:23 -0700153 // A function template is not an inference target.
154 const FunctionTemplateDecl &FuncTmpl =
Googlerbdb21362024-04-26 08:37:42 -0700155 lookup<FunctionTemplateDecl>("funcTmpl", Ctx);
Sam McCallb70563a2023-07-28 08:31:23 -0700156 EXPECT_FALSE(isInferenceTarget(FuncTmpl));
157 EXPECT_FALSE(isInferenceTarget(*FuncTmpl.getTemplatedDecl()));
Googlerbdb21362024-04-26 08:37:42 -0700158 // The function template specialization is *also* not an inference target.
Googler30f598e2024-07-11 09:57:52 -0700159 const ValueDecl &Specialization =
160 *cast<DeclRefExpr>(
161 lookup<VarDecl>("FuncTmplSpec", Ctx).getInit()->IgnoreImplicit())
162 ->getDecl();
163 EXPECT_FALSE(isInferenceTarget(Specialization));
Sam McCallb70563a2023-07-28 08:31:23 -0700164}
165
Googlered929362024-04-18 07:33:38 -0700166TEST(IsInferenceTargetTest, ClassTemplateAndMembers) {
167 TestAST AST(R"cc(
168 template <typename T>
169 struct ClassTemplate {
170 T NonPtrField;
171 T* PtrField;
Googlere41a5882024-04-26 08:41:36 -0700172 static T* StaticField;
Googlered929362024-04-18 07:33:38 -0700173 };
174
175 ClassTemplate<int> I;
176 int A = I.NonPtrField;
177 int* B = I.PtrField;
Googlere41a5882024-04-26 08:41:36 -0700178 int* C = I.StaticField;
Googlered929362024-04-18 07:33:38 -0700179 )cc");
180
Googlerbdb21362024-04-26 08:37:42 -0700181 auto &Ctx = AST.context();
Googlered929362024-04-18 07:33:38 -0700182 // Class templates and their fields are not inference targets.
Googlerbdb21362024-04-26 08:37:42 -0700183 auto &ClassTemplate = lookup<ClassTemplateDecl>("ClassTemplate", Ctx);
Googlered929362024-04-18 07:33:38 -0700184 EXPECT_FALSE(isInferenceTarget(ClassTemplate));
185 EXPECT_FALSE(isInferenceTarget(*ClassTemplate.getTemplatedDecl()));
186 EXPECT_FALSE(isInferenceTarget(
Googlerbdb21362024-04-26 08:37:42 -0700187 lookup("NonPtrField", Ctx, ClassTemplate.getTemplatedDecl())));
188 EXPECT_FALSE(isInferenceTarget(
189 lookup("PtrField", Ctx, ClassTemplate.getTemplatedDecl())));
Googlere41a5882024-04-26 08:41:36 -0700190 EXPECT_FALSE(isInferenceTarget(
191 lookup("StaticField", Ctx, ClassTemplate.getTemplatedDecl())));
Googlered929362024-04-18 07:33:38 -0700192
193 // Class template specializations and their fields are also not inference
194 // targets.
195 EXPECT_FALSE(isInferenceTarget(
Googlerbdb21362024-04-26 08:37:42 -0700196 *lookup<VarDecl>("I", Ctx).getType()->getAsRecordDecl()));
Googlered929362024-04-18 07:33:38 -0700197 EXPECT_FALSE(isInferenceTarget(
Googlerbdb21362024-04-26 08:37:42 -0700198 *cast<MemberExpr>(lookup<VarDecl>("A", Ctx).getInit()->IgnoreImplicit())
Googlered929362024-04-18 07:33:38 -0700199 ->getMemberDecl()));
200 EXPECT_FALSE(isInferenceTarget(
Googlerbdb21362024-04-26 08:37:42 -0700201 *cast<MemberExpr>(lookup<VarDecl>("B", Ctx).getInit()->IgnoreImplicit())
Googlered929362024-04-18 07:33:38 -0700202 ->getMemberDecl()));
Googlere41a5882024-04-26 08:41:36 -0700203 EXPECT_FALSE(isInferenceTarget(
204 *cast<MemberExpr>(lookup<VarDecl>("C", Ctx).getInit()->IgnoreImplicit())
205 ->getMemberDecl()));
Googlered929362024-04-18 07:33:38 -0700206}
207
Googlerf1f793d2023-10-19 07:51:34 -0700208TEST(InferableTest, CountInferableSlots) {
Googlere29825e2024-05-17 06:51:50 -0700209 TestAST AST((SmartPointerHeader + R"cc(
210 using Pointer = int *;
211 template <class T>
212 struct S;
213 struct T;
Sam McCallb70563a2023-07-28 08:31:23 -0700214
Googlere29825e2024-05-17 06:51:50 -0700215 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 McCallb70563a2023-07-28 08:31:23 -0700223
Googlere29825e2024-05-17 06:51:50 -0700224 int *g1(int);
225 Pointer g2(int);
226 std::unique_ptr<int> g3(int);
227 custom_smart_ptr<int> g4(int);
Sam McCallb70563a2023-07-28 08:31:23 -0700228
Googlere29825e2024-05-17 06:51:50 -0700229 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());
Googlerbdb21362024-04-26 08:37:42 -0700234 auto &Ctx = AST.context();
Sam McCallb70563a2023-07-28 08:31:23 -0700235
236 // All the 'f's have a single pointer arg.
Googlerbdb21362024-04-26 08:37:42 -0700237 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)));
Googlere29825e2024-05-17 06:51:50 -0700243 EXPECT_EQ(1, countInferableSlots(lookup("f7", Ctx)));
244 EXPECT_EQ(1, countInferableSlots(lookup("f8", Ctx)));
Sam McCallb70563a2023-07-28 08:31:23 -0700245
246 // All the 'g's have a pointer return.
Googlerbdb21362024-04-26 08:37:42 -0700247 EXPECT_EQ(1, countInferableSlots(lookup("g1", Ctx)));
248 EXPECT_EQ(1, countInferableSlots(lookup("g2", Ctx)));
Googlere29825e2024-05-17 06:51:50 -0700249 EXPECT_EQ(1, countInferableSlots(lookup("g3", Ctx)));
250 EXPECT_EQ(1, countInferableSlots(lookup("g4", Ctx)));
Sam McCallb70563a2023-07-28 08:31:23 -0700251
252 // The 'h's have types that aren't really pointers.
Googlerbdb21362024-04-26 08:37:42 -0700253 EXPECT_EQ(0, countInferableSlots(lookup("h1", Ctx)));
254 EXPECT_EQ(0, countInferableSlots(lookup("h2", Ctx)));
255 EXPECT_EQ(0, countInferableSlots(lookup("h3", Ctx)));
Sam McCallb70563a2023-07-28 08:31:23 -0700256}
257
258} // namespace
259} // namespace clang::tidy::nullability