Break tests up to include only one scenario per test. Also make tests more consistent in strategies for grabbing particular Decls, in use of auto, and in naming/capitalization. Also remove duplicative test cases and use parameterized tests for some functions to make the input/output more obvious and reduce the focus on the machinery. PiperOrigin-RevId: 823510868 Change-Id: Ic3b6cc526baab37f1452d8155267fc46f3d886e5
diff --git a/nullability/inference/inferable_test.cc b/nullability/inference/inferable_test.cc index d1fbacf..a98e713 100644 --- a/nullability/inference/inferable_test.cc +++ b/nullability/inference/inferable_test.cc
@@ -4,6 +4,9 @@ #include "nullability/inference/inferable.h" +#include <string> +#include <vector> + #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -21,17 +24,13 @@ namespace clang::tidy::nullability { using ::clang::ast_matchers::anything; using ::clang::ast_matchers::equalsNode; -using ::clang::ast_matchers::functionDecl; using ::clang::ast_matchers::hasDeclContext; using ::clang::ast_matchers::hasName; using ::clang::ast_matchers::isImplicit; -using ::clang::ast_matchers::isTemplateInstantiation; using ::clang::ast_matchers::match; using ::clang::ast_matchers::namedDecl; -using ::clang::ast_matchers::selectFirst; using ::clang::ast_matchers::unless; -using ::testing::IsEmpty; -using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; template <class T = NamedDecl> static const T& lookup(llvm::StringRef Name, ASTContext& Ctx, @@ -71,150 +70,567 @@ class _Nullable custom_smart_int_ptr { using pointer = int*; }; )cc"; -TEST(IsInferenceTargetTest, GlobalVariables) { - TestAST AST((SmartPointerHeader + R"cc( - int* Pointer; - std::unique_ptr<int> StdSmartPointer; - custom_smart_ptr<int> CustomSmartPointer; - custom_smart_int_ptr CustomSmartIntPointer; - int NotPointer; - )cc") - .str()); - - auto &Ctx = AST.context(); - EXPECT_TRUE(isInferenceTarget(lookup("Pointer", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("StdSmartPointer", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartPointer", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartIntPointer", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("NotPointer", Ctx))); +TEST(IsInferenceTargetTest, GlobalRawPointer) { + TestAST AST(R"cc( + int* Pointer; + )cc"); + EXPECT_TRUE(isInferenceTarget(lookup("Pointer", AST.context()))); } -TEST(IsInferenceTargetTest, Functions) { +TEST(IsInferenceTargetTest, GlobalStdSmartPointer) { TestAST AST((SmartPointerHeader + R"cc( - int* func(int* Param, int** NestedParam, - std::unique_ptr<int> StdSmartParam, - custom_smart_ptr<int> CustomSmartParam) { - int* Local; - static int* StaticLocal; - std::unique_ptr<int> StdSmartLocal; - custom_smart_ptr<int> CustomSmartLocal; - } - void empty() {} - auto Lambda = []() {}; - auto LambdaWithPtr = [](int*) {}; + std::unique_ptr<int> StdSmartPointer; )cc") .str()); + EXPECT_TRUE(isInferenceTarget(lookup("StdSmartPointer", AST.context()))); +} - auto &Ctx = AST.context(); +TEST(IsInferenceTargetTest, GlobalCustomSmartPointer) { + TestAST AST((SmartPointerHeader + R"cc( + custom_smart_ptr<int> CustomSmartPointer; + )cc") + .str()); + EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartPointer", AST.context()))); +} + +TEST(IsInferenceTargetTest, GlobalCustomSmartIntPointer) { + TestAST AST((SmartPointerHeader + R"cc( + custom_smart_int_ptr CustomSmartIntPointer; + )cc") + .str()); + EXPECT_TRUE( + isInferenceTarget(lookup("CustomSmartIntPointer", AST.context()))); +} + +TEST(IsInferenceTargetTest, GlobalNonPointer) { + TestAST AST(R"cc( + int NotPointer; + )cc"); + EXPECT_FALSE(isInferenceTarget(lookup("NotPointer", AST.context()))); +} + +TEST(IsInferenceTargetTest, FunctionWithoutPointersIsNotTarget) { + TestAST AST(R"cc( + void func() {} + )cc"); + EXPECT_FALSE(isInferenceTarget(lookup("func", AST.context()))); +} + +TEST(IsInferenceTargetTest, FunctionWithPointerReturnIsTarget) { + TestAST AST(R"cc( + int* func(); + )cc"); + EXPECT_TRUE(isInferenceTarget(lookup("func", AST.context()))); +} + +TEST(IsInferenceTargetTest, FunctionWithPointerParamIsTargetButParamIsNot) { + TestAST AST(R"cc( + void func(int* Param); + )cc"); + ASTContext& Ctx = AST.context(); EXPECT_TRUE(isInferenceTarget(lookup("func", Ctx))); EXPECT_FALSE(isInferenceTarget(lookup("Param", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("NestedParam", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("StdSmartParam", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("CustomSmartParam", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("Local", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("StaticLocal", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("StdSmartLocal", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartLocal", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("empty", Ctx))); - auto &Lambda = lookup<VarDecl>("Lambda", Ctx); - auto *LambdaCtx = cast<LambdaExpr>(Lambda.getInit())->getLambdaClass(); +} + +TEST(IsInferenceTargetTest, FunctionLocalPointerIsTarget) { + TestAST AST((SmartPointerHeader + R"cc( + void func() { int* Local; } + )cc") + .str()); + EXPECT_TRUE(isInferenceTarget(lookup("Local", AST.context()))); +} + +TEST(IsInferenceTargetTest, StaticFunctionLocalPointerIsTarget) { + TestAST AST((SmartPointerHeader + R"cc( + void func() { + static int* StaticLocal; + } + )cc") + .str()); + EXPECT_TRUE(isInferenceTarget(lookup("StaticLocal", AST.context()))); +} + +TEST(IsInferenceTargetTest, FunctionLocalStdSmartPointerIsTarget) { + TestAST AST((SmartPointerHeader + R"cc( + void func() { + std::unique_ptr<int> StdSmartLocal; + } + )cc") + .str()); + EXPECT_TRUE(isInferenceTarget(lookup("StdSmartLocal", AST.context()))); +} + +TEST(IsInferenceTargetTest, FunctionLocalCustomSmartPointerIsTarget) { + TestAST AST((SmartPointerHeader + R"cc( + void func() { + custom_smart_ptr<int> CustomSmartLocal; + } + )cc") + .str()); + EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartLocal", AST.context()))); +} + +TEST(IsInferenceTargetTest, LambdaWithoutPointerIsNotTarget) { + TestAST AST(R"cc( + auto Lambda = []() {}; + )cc"); + ASTContext& Ctx = AST.context(); + auto& Lambda = lookup<VarDecl>("Lambda", Ctx); + CXXRecordDecl* LambdaCtx = + cast<LambdaExpr>(Lambda.getInit())->getLambdaClass(); EXPECT_FALSE(isInferenceTarget(Lambda)); EXPECT_FALSE(isInferenceTarget(lookup("operator()", Ctx, LambdaCtx))); - auto &LambdaWithPtr = lookup<VarDecl>("LambdaWithPtr", Ctx); - auto *LambdaWithPtrCtx = +} + +TEST(IsInferenceTargetTest, LambdaWithPointerIsTarget) { + TestAST AST(R"cc( + auto LambdaWithPtr = [](int*) {}; + )cc"); + ASTContext& Ctx = AST.context(); + auto& LambdaWithPtr = lookup<VarDecl>("LambdaWithPtr", Ctx); + CXXRecordDecl* LambdaWithPtrCtx = cast<LambdaExpr>(LambdaWithPtr.getInit())->getLambdaClass(); EXPECT_FALSE(isInferenceTarget(LambdaWithPtr)); EXPECT_TRUE(isInferenceTarget(lookup("operator()", Ctx, LambdaWithPtrCtx))); } -TEST(IsInferenceTargetTest, ClassAndMembers) { +TEST(IsInferenceTargetTest, ClassDeclIsNotTarget) { + TestAST AST(R"cc( + class C {}; + )cc"); + EXPECT_FALSE(isInferenceTarget(lookup<CXXRecordDecl>("C", AST.context()))); +} + +TEST(IsInferenceTargetTest, MethodWithoutPointerIsNotTarget) { + TestAST AST(R"cc( + class C { + void method(); + }; + )cc"); + EXPECT_FALSE(isInferenceTarget(lookup("method", AST.context()))); +} + +TEST(IsInferenceTargetTest, MethodWithPointerIsTarget) { + TestAST AST(R"cc( + class C { + int* methodWithPtr(); + }; + )cc"); + EXPECT_TRUE(isInferenceTarget(lookup("methodWithPtr", AST.context()))); +} + +TEST(IsInferenceTargetTest, StaticMethodWithPointerIsTarget) { + TestAST AST(R"cc( + class C { + static int* staticMethodWithPtr(); + }; + )cc"); + EXPECT_TRUE(isInferenceTarget(lookup("staticMethodWithPtr", AST.context()))); +} + +TEST(IsInferenceTargetTest, NonPointerFieldIsNotTarget) { + TestAST AST(R"cc( + class C { + int NonPtrField; + }; + )cc"); + EXPECT_FALSE(isInferenceTarget(lookup("NonPtrField", AST.context()))); +} + +TEST(IsInferenceTargetTest, PointerFieldIsTarget) { + TestAST AST(R"cc( + class C { + int* PtrField; + }; + )cc"); + EXPECT_TRUE(isInferenceTarget(lookup("PtrField", AST.context()))); +} + +TEST(IsInferenceTargetTest, StaticPointerFieldIsTarget) { + TestAST AST(R"cc( + class C { + static int* StaticField; + }; + )cc"); + EXPECT_TRUE(isInferenceTarget(lookup("StaticField", AST.context()))); +} + +TEST(IsInferenceTargetTest, StdSmartPointerFieldIsTarget) { TestAST AST((SmartPointerHeader + R"cc( class C { - void method(); - int* methodWithPtr(); - int NonPtrField; - int* PtrField; - static int* StaticField; std::unique_ptr<int> StdSmartField; + }; + )cc") + .str()); + EXPECT_TRUE(isInferenceTarget(lookup("StdSmartField", AST.context()))); +} + +TEST(IsInferenceTargetTest, CustomSmartPointerFieldIsTarget) { + TestAST AST((SmartPointerHeader + R"cc( + class C { custom_smart_ptr<int> CustomSmartField; }; )cc") .str()); - - auto &Ctx = AST.context(); - EXPECT_FALSE(isInferenceTarget(lookup<CXXRecordDecl>("C", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("method", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("methodWithPtr", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("NonPtrField", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("PtrField", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("StaticField", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("StdSmartField", Ctx))); - EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartField", Ctx))); + EXPECT_TRUE(isInferenceTarget(lookup("CustomSmartField", AST.context()))); } -TEST(IsInferenceTargetTest, FunctionTemplate) { +TEST(IsInferenceTargetTest, FunctionTemplateDeclIsNotTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) {} + + // To demonstrate that the existence of an instantiation is not a factor. + void instantiate() { funcTmpl<0>(nullptr); } + )cc"); + const FunctionTemplateDecl& FuncTmpl = + lookup<FunctionTemplateDecl>("funcTmpl", AST.context()); + EXPECT_FALSE(isInferenceTarget(FuncTmpl)); + EXPECT_FALSE(isInferenceTarget(*FuncTmpl.getTemplatedDecl())); +} + +TEST(IsInferenceTargetTest, LocalInFunctionTemplateDeclIsNotTarget) { TestAST AST(R"cc( template <int X> void funcTmpl(int*) { int* LocalInTmpl; + } + // To demonstrate that the existence of an instantiation is not a factor. + void instantiate() { funcTmpl<0>(nullptr); } + )cc"); + ASTContext& Ctx = AST.context(); + const FunctionTemplateDecl& FuncTmpl = + lookup<FunctionTemplateDecl>("funcTmpl", Ctx); + EXPECT_FALSE(isInferenceTarget( + lookup("LocalInTmpl", Ctx, FuncTmpl.getTemplatedDecl()))); +} + +TEST(IsInferenceTargetTest, MethodInFunctionTemplateDeclIsNotTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { struct StructInTmpl { - void funcInStructInTmpl(int* P) { int* LocalInStructInTmpl; } + void funcInStructInTmpl(int* P) {} + }; + } + + // To demonstrate that the existence of an instantiation is not a factor. + void instantiate() { funcTmpl<0>(nullptr); } + )cc"); + ASTContext& Ctx = AST.context(); + const FunctionTemplateDecl& FuncTmpl = + lookup<FunctionTemplateDecl>("funcTmpl", Ctx); + auto* StructInTmpl = + &lookup<CXXRecordDecl>("StructInTmpl", Ctx, FuncTmpl.getTemplatedDecl()); + const NamedDecl& FuncInStructInTmpl = + lookup("funcInStructInTmpl", Ctx, StructInTmpl); + EXPECT_FALSE(isInferenceTarget(FuncInStructInTmpl)); +} + +TEST(IsInferenceTargetTest, LocalInMethodInFunctionTemplateDeclIsNotTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { + struct StructInTmpl { + void funcInStructInTmpl() { int* LocalInStructInTmpl; } + }; + } + + // To demonstrate that the existence of an instantiation is not a factor. + void instantiate() { funcTmpl<0>(nullptr); } + )cc"); + ASTContext& Ctx = AST.context(); + const FunctionTemplateDecl& FuncTmpl = + lookup<FunctionTemplateDecl>("funcTmpl", Ctx); + auto* StructInTmpl = + &lookup<CXXRecordDecl>("StructInTmpl", Ctx, FuncTmpl.getTemplatedDecl()); + const NamedDecl& FuncInStructInTmpl = + lookup("funcInStructInTmpl", Ctx, StructInTmpl); + EXPECT_FALSE(isInferenceTarget( + lookup("LocalInStructInTmpl", Ctx, &FuncInStructInTmpl))); +} + +TEST(IsInferenceTargetTest, FieldInFunctionTemplateDeclIsNotTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { + struct StructInTmpl { + int* FieldInStructInTmpl; + }; + } + + // To demonstrate that the existence of an instantiation is not a factor. + void instantiate() { funcTmpl<0>(nullptr); } + )cc"); + ASTContext& Ctx = AST.context(); + auto& FuncTmpl = lookup<FunctionTemplateDecl>("funcTmpl", Ctx); + auto* StructInTmpl = + &lookup<CXXRecordDecl>("StructInTmpl", Ctx, FuncTmpl.getTemplatedDecl()); + EXPECT_FALSE( + isInferenceTarget(lookup("FieldInStructInTmpl", Ctx, StructInTmpl))); +} + +TEST(IsInferenceTargetTest, FunctionTemplateInstantiationIsTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) {} + + auto& FuncTmplInst = funcTmpl<2>; + )cc"); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>(lookup<VarDecl>("FuncTmplInst", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getDecl(); + EXPECT_TRUE(isInferenceTarget(Instantiation)); +} + +TEST(IsInferenceTargetTest, LocalInFunctionTemplateInstantiationIsTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { + int* LocalInTmpl; + } + + auto& FuncTmplInst = funcTmpl<2>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + EXPECT_TRUE(isInferenceTarget(lookup("LocalInTmpl", Ctx, &Instantiation))); +} + +TEST(IsInferenceTargetTest, MethodInFunctionTemplateInstantiationIsTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { + struct StructInTmpl { + void funcInStructInTmpl(int* P) {} + }; + } + + auto& FuncTmplInst = funcTmpl<2>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + auto* StructInInstantiation = + &lookup<CXXRecordDecl>("StructInTmpl", Ctx, &Instantiation); + const NamedDecl& FuncInStructInInstantiation = + lookup("funcInStructInTmpl", Ctx, StructInInstantiation); + EXPECT_TRUE(isInferenceTarget(FuncInStructInInstantiation)); +} + +TEST(IsInferenceTargetTest, + LocalInMethodInFunctionTemplateInstantiationIsTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { + struct StructInTmpl { + void funcInStructInTmpl() { int* LocalInStructInTmpl; } + }; + } + + auto& FuncTmplInst = funcTmpl<2>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + auto* StructInInstantiation = + &lookup<CXXRecordDecl>("StructInTmpl", Ctx, &Instantiation); + const NamedDecl& FuncInStructInInstantiation = + lookup("funcInStructInTmpl", Ctx, StructInInstantiation); + EXPECT_TRUE(isInferenceTarget( + lookup("LocalInStructInTmpl", Ctx, &FuncInStructInInstantiation))); +} + +TEST(IsInferenceTargetTest, FieldInFunctionTemplateInstantiationIsTarget) { + TestAST AST(R"cc( + template <int X> + void funcTmpl(int*) { + struct StructInTmpl { int* FieldInStructInTmpl; }; } auto& FuncTmplInst = funcTmpl<2>; )cc"); - - auto &Ctx = AST.context(); - // A function template is not an inference target, nor are functions or fields - // or local variables contained within. - const FunctionTemplateDecl &FuncTmpl = - lookup<FunctionTemplateDecl>("funcTmpl", Ctx); - EXPECT_FALSE(isInferenceTarget(FuncTmpl)); - EXPECT_FALSE(isInferenceTarget(*FuncTmpl.getTemplatedDecl())); - EXPECT_FALSE(isInferenceTarget( - lookup("LocalInTmpl", Ctx, FuncTmpl.getTemplatedDecl()))); - auto *StructInTmpl = - &lookup<CXXRecordDecl>("StructInTmpl", Ctx, FuncTmpl.getTemplatedDecl()); - EXPECT_FALSE( - isInferenceTarget(lookup("FieldInStructInTmpl", Ctx, StructInTmpl))); - auto &FuncInStructInTmpl = lookup("funcInStructInTmpl", Ctx, StructInTmpl); - EXPECT_FALSE(isInferenceTarget(FuncInStructInTmpl)); - EXPECT_FALSE(isInferenceTarget( - lookup("LocalInStructInTmpl", Ctx, &FuncInStructInTmpl))); - // The function template instantiation *is* an inference target, as are - // functions, fields, and local variables within. - const ValueDecl &Instantiation = + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = *cast<DeclRefExpr>( lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) ->getDecl(); - EXPECT_TRUE(isInferenceTarget(Instantiation)); - EXPECT_TRUE(isInferenceTarget(lookup("LocalInTmpl", Ctx, &Instantiation))); - auto *StructInInstantiation = + auto* StructInInstantiation = &lookup<CXXRecordDecl>("StructInTmpl", Ctx, &Instantiation); EXPECT_TRUE(isInferenceTarget( lookup("FieldInStructInTmpl", Ctx, StructInInstantiation))); - auto &FuncInStructInInstantiation = - lookup("funcInStructInTmpl", Ctx, StructInInstantiation); - EXPECT_TRUE(isInferenceTarget(FuncInStructInInstantiation)); - EXPECT_TRUE(isInferenceTarget( - lookup("LocalInStructInTmpl", Ctx, &FuncInStructInInstantiation))); } -TEST(IsInferenceTargetTest, ClassTemplateAndMembers) { +TEST(IsInferenceTargetTest, ClassTemplateDeclIsNotTarget) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate {}; + + // To demonstrate that the existence of an instantiation is not a factor. + ClassTemplate<int*> Instantiation; + )cc"); + auto& ClassTemplate = + lookup<ClassTemplateDecl>("ClassTemplate", AST.context()); + EXPECT_FALSE(isInferenceTarget(ClassTemplate)); + EXPECT_FALSE(isInferenceTarget(*ClassTemplate.getTemplatedDecl())); +} + +// We do not exhaustively test all the possible decls that can be inside a class +// template, as this would be repetitive with the function template test cases +// above. +TEST(IsInferenceTargetTest, FieldInClassTemplateDeclIsNotTarget) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { + T* PtrField; + }; + + // To demonstrate that the existence of an instantiation is not a factor. + ClassTemplate<int*> Instantiation; + )cc"); + ASTContext& Ctx = AST.context(); + auto& ClassTemplate = lookup<ClassTemplateDecl>("ClassTemplate", Ctx); + EXPECT_FALSE(isInferenceTarget( + lookup("PtrField", Ctx, ClassTemplate.getTemplatedDecl()))); +} + +TEST(IsInferenceTargetTest, ClassTemplateInstantiationWithNonPointer) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate {}; + + ClassTemplate<int> Instantiation; + )cc"); + EXPECT_FALSE( + isInferenceTarget(*lookup<VarDecl>("Instantiation", AST.context()) + .getType() + ->getAsRecordDecl())); +} + +TEST(IsInferenceTargetTest, NonPointerFieldInClassTemplateInstantiation) { TestAST AST(R"cc( template <typename T> struct ClassTemplate { T NonPtrField; + }; + + ClassTemplate<int> Instantiation; + int Field = Instantiation.NonPtrField; + )cc"); + EXPECT_FALSE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, PointerFieldInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { T* PtrField; + }; + + ClassTemplate<int> Instantiation; + int* Field = Instantiation.PtrField; + )cc"); + EXPECT_TRUE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, StaticPointerFieldInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { static T* StaticField; + }; + + ClassTemplate<int> Instantiation; + int* Field = Instantiation.StaticField; + )cc"); + EXPECT_TRUE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, MethodWithoutPointersInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { T method(); + }; + + ClassTemplate<int> Instantiation; + int MethodResult = Instantiation.method(); + )cc"); + EXPECT_FALSE(isInferenceTarget( + *cast<CXXMemberCallExpr>(lookup<VarDecl>("MethodResult", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getMethodDecl())); +} + +TEST(IsInferenceTargetTest, MethodWithPointerInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { T* methodWithPtr(); + }; + + ClassTemplate<int> Instantiation; + int* MethodResult = Instantiation.methodWithPtr(); + )cc"); + EXPECT_TRUE(isInferenceTarget( + *cast<CXXMemberCallExpr>(lookup<VarDecl>("MethodResult", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getMethodDecl())); +} + +TEST(IsInferenceTargetTest, + PointerFieldInNestedStructInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { struct Nested { struct NestedTwo { T* NestedStructPtrField; + }; + NestedTwo NestedStructTwo; + }; + Nested NestedStruct; + }; + + ClassTemplate<int> Instantiation; + int* Field = Instantiation.NestedStruct.NestedStructTwo.NestedStructPtrField; + )cc"); + EXPECT_TRUE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, + MethodWithNonDependentPointerInNestedStructInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { + struct Nested { + struct NestedTwo { bool* nestedStructMethod(); }; NestedTwo NestedStructTwo; @@ -222,383 +638,590 @@ Nested NestedStruct; }; - ClassTemplate<int> IntClass; - int IntA = IntClass.NonPtrField; - int* IntB = IntClass.PtrField; - int* IntC = IntClass.StaticField; - int IntD = IntClass.method(); - int* IntE = IntClass.methodWithPtr(); - int* IntF = IntClass.NestedStruct.NestedStructTwo.NestedStructPtrField; - bool* IntG = IntClass.NestedStruct.NestedStructTwo.nestedStructMethod(); - - ClassTemplate<int*> IntStarClass; - int* IntStarA = IntStarClass.NonPtrField; - int** IntStarB = IntStarClass.PtrField; - int** IntStarC = IntStarClass.StaticField; - int* IntStarD = IntStarClass.method(); - int** IntStarE = IntStarClass.methodWithPtr(); - int** IntStarF = IntStarClass.NestedStruct.NestedStructTwo.NestedStructPtrField; - bool* IntStarG = IntStarClass.NestedStruct.NestedStructTwo.nestedStructMethod(); + ClassTemplate<int> Instantiation; + bool* MethodResult = + Instantiation.NestedStruct.NestedStructTwo.nestedStructMethod(); )cc"); - - auto &Ctx = AST.context(); - // Class templates and fields and functions inside them are not inference - // targets. - auto &ClassTemplate = lookup<ClassTemplateDecl>("ClassTemplate", Ctx); - EXPECT_FALSE(isInferenceTarget(ClassTemplate)); - EXPECT_FALSE(isInferenceTarget(*ClassTemplate.getTemplatedDecl())); - EXPECT_FALSE(isInferenceTarget( - lookup("NonPtrField", Ctx, ClassTemplate.getTemplatedDecl()))); - EXPECT_FALSE(isInferenceTarget( - lookup("PtrField", Ctx, ClassTemplate.getTemplatedDecl()))); - EXPECT_FALSE(isInferenceTarget( - lookup("StaticField", Ctx, ClassTemplate.getTemplatedDecl()))); - EXPECT_FALSE(isInferenceTarget( - lookup("method", Ctx, ClassTemplate.getTemplatedDecl()))); - EXPECT_FALSE(isInferenceTarget( - lookup("methodWithPtr", Ctx, ClassTemplate.getTemplatedDecl()))); - auto *NestedTwoInClassTemplate = &lookup<CXXRecordDecl>( - "NestedTwo", Ctx, - &lookup<CXXRecordDecl>("Nested", Ctx, ClassTemplate.getTemplatedDecl())); - EXPECT_FALSE(isInferenceTarget( - lookup("NestedStructPtrField", Ctx, NestedTwoInClassTemplate))); - EXPECT_FALSE(isInferenceTarget( - lookup("nestedStructMethod", Ctx, NestedTwoInClassTemplate))); - - // Class template instantiations are inference targets, as are fields - // and functions inside, if they have inferable types, i.e. they are pointers - // and the pointer-ness is specified by the template. - // For the int template argument: - EXPECT_FALSE(isInferenceTarget( - *lookup<VarDecl>("IntClass", Ctx).getType()->getAsRecordDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntA", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntB", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntC", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<CXXMemberCallExpr>( - lookup<VarDecl>("IntD", Ctx).getInit()->IgnoreImplicit()) - ->getMethodDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<CXXMemberCallExpr>( - lookup<VarDecl>("IntE", Ctx).getInit()->IgnoreImplicit()) - ->getMethodDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntF", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<CXXMemberCallExpr>( - lookup<VarDecl>("IntG", Ctx).getInit()->IgnoreImplicit()) - ->getMethodDecl())); - - // For the int* template argument, notably, we get the same results, even - // though the canonical types of more fields are pointers: - EXPECT_FALSE(isInferenceTarget( - *lookup<VarDecl>("IntStarClass", Ctx).getType()->getAsRecordDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntStarA", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntStarB", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntStarC", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<CXXMemberCallExpr>( - lookup<VarDecl>("IntStarD", Ctx).getInit()->IgnoreImplicit()) - ->getMethodDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<CXXMemberCallExpr>( - lookup<VarDecl>("IntStarE", Ctx).getInit()->IgnoreImplicit()) - ->getMethodDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("IntStarF", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<CXXMemberCallExpr>( - lookup<VarDecl>("IntStarG", Ctx).getInit()->IgnoreImplicit()) + *cast<CXXMemberCallExpr>(lookup<VarDecl>("MethodResult", AST.context()) + .getInit() + ->IgnoreImplicit()) ->getMethodDecl())); } -TEST(InferableTest, CountInferableSlots) { - TestAST AST((SmartPointerHeader + R"cc( - using Pointer = int *; - template <class T> - struct S; - struct T; - - void f1(int *); - void f2(Pointer); - void f3(int **); - void f4(Pointer *); - void f5(int *&); - void f6(int (*)()); // function pointer - void f7(std::unique_ptr<int>); - void f8(custom_smart_ptr<int>); - - int *g1(int); - Pointer g2(int); - std::unique_ptr<int> g3(int); - custom_smart_ptr<int> g4(int); - - void h1(S<int *>); - void h2(int T::*); // pointer to data member - void h3(int (T::*)()); // pointer to member function - )cc") - .str()); - auto &Ctx = AST.context(); - - // All the 'f's have a single pointer arg. - EXPECT_EQ(1, countInferableSlots(lookup("f1", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f2", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f3", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f4", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f5", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f6", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f7", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("f8", Ctx))); - - // All the 'g's have a pointer return. - EXPECT_EQ(1, countInferableSlots(lookup("g1", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("g2", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("g3", Ctx))); - EXPECT_EQ(1, countInferableSlots(lookup("g4", Ctx))); - - // The 'h's have types that aren't really pointers. - EXPECT_EQ(0, countInferableSlots(lookup("h1", Ctx))); - EXPECT_EQ(0, countInferableSlots(lookup("h2", Ctx))); - EXPECT_EQ(0, countInferableSlots(lookup("h3", Ctx))); -} - -TEST(InferableTest, InnerPointersNotInferable) { - TestAST AST(R"cc( - int*** ThreePointersOneInferable; - )cc"); - auto &Ctx = AST.context(); - EXPECT_THAT(getInferableSlotIndices(lookup("ThreePointersOneInferable", Ctx)), - UnorderedElementsAre(0)); -} - -TEST(InferableTest, TemplateArgumentPointersNotInferable) { +TEST(IsInferenceTargetTest, ClassTemplateInstantiationWithPointer) { TestAST AST(R"cc( template <typename T> - struct S {}; + struct ClassTemplate {}; - S<int*> NotInferable; + ClassTemplate<int*> Instantiation; )cc"); - auto &Ctx = AST.context(); - EXPECT_EQ(0, countInferableSlots(lookup("NotInferable", Ctx))); - EXPECT_FALSE(isInferenceTarget(lookup("NotInferable", Ctx))); + EXPECT_FALSE( + isInferenceTarget(*lookup<VarDecl>("Instantiation", AST.context()) + .getType() + ->getAsRecordDecl())); } -TEST(InferableTest, TemplateInstantiationOnlyArgWithStarIsInferable) { - TestAST AST((SmartPointerHeader + R"cc( - template <typename T> - struct S { - T* Inferable; - T NotInferable; - using U = T; - U NotInferableThroughAlias; - }; - - S<int*> AnS; - int** FromTStarField = AnS.Inferable; - int* FromTField = AnS.NotInferable; - int* FromTFieldAlias = AnS.NotInferableThroughAlias; - - S<std::unique_ptr<int>> AnSSmart; - std::unique_ptr<int>* FromTStarFieldSmart = AnSSmart.Inferable; - std::unique_ptr<int>& FromTFieldSmart = AnSSmart.NotInferable; - std::unique_ptr<int>& FromTFieldAliasSmart = AnSSmart.NotInferableThroughAlias; - )cc") - .str()); - auto &Ctx = AST.context(); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("FromTStarField", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("FromTField", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("FromTFieldAlias", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>(lookup<VarDecl>("FromTStarFieldSmart", Ctx) - .getInit() - ->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("FromTFieldSmart", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>(lookup<VarDecl>("FromTFieldAliasSmart", Ctx) - .getInit() - ->IgnoreImplicit()) - ->getMemberDecl())); -} - -TEST(InferableTest, TypeAliasTemplate) { +TEST(IsInferenceTargetTest, + FieldMadePointerOnlyByTemplateArgumentInClassTemplateInstantiation) { TestAST AST(R"cc( template <typename T> - using PtrAlias = T*; + struct ClassTemplate { + T Field; + }; + + ClassTemplate<int*> Instantiation; + int* F = Instantiation.Field; + )cc"); + EXPECT_FALSE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("F", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST( + IsInferenceTargetTest, + MethodContainingPointerOnlyByTemplateArgumentInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { + T method(); + }; + + ClassTemplate<int*> Instantiation; + int* MethodResult = Instantiation.method(); + )cc"); + EXPECT_FALSE(isInferenceTarget( + *cast<CXXMemberCallExpr>(lookup<VarDecl>("MethodResult", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getMethodDecl())); +} + +TEST(IsInferenceTargetTest, + FieldMadePointerThroughAliasToTemplateArgInClassTemplateInstantiation) { + TestAST AST(R"cc( + template <typename T> + struct ClassTemplate { + using U = T; + U NotInferableThroughAlias; + }; + + ClassTemplate<int*> Instantiation; + int* F = Instantiation.NotInferableThroughAlias; + )cc"); + EXPECT_FALSE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("F", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, + TypeAliasTemplateUsedWithPointerPresentInClassTemplate) { + TestAST AST(R"cc( template <typename T> using Alias = T; template <typename U> - struct S { - Alias<U*> Inferable; - Alias<U> NotInferable; - PtrAlias<U> InferablePointerInAlias; + struct ClassTemplate { + Alias<U*> FieldOfAliasTypeWithPointerInClassTemplate; }; - S<int*> AnS; - int** FromInferable = AnS.Inferable; - int* FromNotInferable = AnS.NotInferable; - int** FromInferablePointerInAlias = AnS.InferablePointerInAlias; + ClassTemplate<int> Instantiation; + int* Field = Instantiation.FieldOfAliasTypeWithPointerInClassTemplate; )cc"); - auto &Ctx = AST.context(); EXPECT_TRUE(isInferenceTarget( *cast<MemberExpr>( - lookup<VarDecl>("FromInferable", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("FromNotInferable", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>(lookup<VarDecl>("FromInferablePointerInAlias", Ctx) - .getInit() - ->IgnoreImplicit()) + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) ->getMemberDecl())); } -TEST(InferableTest, NestedTemplates) { +TEST(IsInferenceTargetTest, + TypeAliasTemplateUsedWithPointerInClassTemplateArgument) { TestAST AST(R"cc( template <typename T> - struct S { + using Alias = T; + + template <typename U> + struct ClassTemplate { + Alias<U> FieldOfAliasTypeWithoutPointerInClassTemplate; + }; + + ClassTemplate<int*> Instantiation; + int* Field = Instantiation.FieldOfAliasTypeWithoutPointerInClassTemplate; + )cc"); + EXPECT_FALSE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, TypeAliasTemplateWithPointerInAliasTemplate) { + TestAST AST(R"cc( + template <typename T> + using PtrAlias = T*; + + template <typename U> + struct ClassTemplate { + PtrAlias<U> FieldOfAliasTypeWithPointerInAlias; + }; + + ClassTemplate<int> Instantiation; + int* Field = Instantiation.FieldOfAliasTypeWithPointerInAlias; + )cc"); + EXPECT_TRUE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +TEST(IsInferenceTargetTest, NestedTemplatesWithPointerInNestedTemplate) { + TestAST AST(R"cc( + template <typename T> + struct Outer { template <typename U> - struct Nested { - U* InferableU; - U NotInferableU; - T* InferableT; - T NotInferableT; + struct Inner { + U* FieldWithPointerInNestedTemplate; }; - Nested<bool*> ANested; + Inner<bool> AnInner; }; - S<int*> AnS; - bool** FromInferableU = AnS.ANested.InferableU; - bool* FromNotInferableU = AnS.ANested.NotInferableU; - int** FromInferableT = AnS.ANested.InferableT; - int* FromNotInferableT = AnS.ANested.NotInferableT; + Outer<int> AnOuter; + bool* Field = AnOuter.AnInner.FieldWithPointerInNestedTemplate; )cc"); - auto &Ctx = AST.context(); EXPECT_TRUE(isInferenceTarget( *cast<MemberExpr>( - lookup<VarDecl>("FromInferableU", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>(lookup<VarDecl>("FromNotInferableU", Ctx) - .getInit() - ->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_TRUE(isInferenceTarget( - *cast<MemberExpr>( - lookup<VarDecl>("FromInferableT", Ctx).getInit()->IgnoreImplicit()) - ->getMemberDecl())); - EXPECT_FALSE(isInferenceTarget( - *cast<MemberExpr>(lookup<VarDecl>("FromNotInferableT", Ctx) - .getInit() - ->IgnoreImplicit()) + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) ->getMemberDecl())); } -TEST(InferableTest, TemplateParamTypedDeclsInFunctionTemplate) { +TEST(IsInferenceTargetTest, + NestedTemplatesWithPointerInNestedTemplateArgument) { TestAST AST(R"cc( template <typename T> - void func(T* ParamStar, T Param) { - T* LocalStar; - T Local; - } + struct Outer { + template <typename U> + struct Inner { + U FieldWithoutPointerInNestedTemplate; + }; + Inner<bool*> AnInner; + }; - template <typename T> - T* returnStar() {} - - template <typename T> - T returnNoStar() {} - - void instantiate() { - func<int*>(nullptr, nullptr); - returnStar<bool*>(); - returnNoStar<char*>(); - } + Outer<int> AnOuter; + bool* Field = AnOuter.AnInner.FieldWithoutPointerInNestedTemplate; )cc"); - - auto& Ctx = AST.context(); - - { - const FunctionDecl* FuncInstantiation = selectFirst<FunctionDecl>( - "func_instantiation", - match(functionDecl(isTemplateInstantiation(), hasName("func")) - .bind("func_instantiation"), - Ctx)); - ASSERT_NE(FuncInstantiation, nullptr); - EXPECT_TRUE(hasInferable(FuncInstantiation->getParamDecl(0)->getType())); - EXPECT_FALSE(hasInferable(FuncInstantiation->getParamDecl(1)->getType())); - - const VarDecl& LocalStar = - lookup<VarDecl>("LocalStar", Ctx, FuncInstantiation); - EXPECT_TRUE(hasInferable(LocalStar.getType())); - const VarDecl& Local = lookup<VarDecl>("Local", Ctx, FuncInstantiation); - EXPECT_FALSE(hasInferable(Local.getType())); - } - - { - const FunctionDecl* ReturnStarInstantiation = selectFirst<FunctionDecl>( - "returnStar_instantiation", - match(functionDecl(isTemplateInstantiation(), hasName("returnStar")) - .bind("returnStar_instantiation"), - Ctx)); - ASSERT_NE(ReturnStarInstantiation, nullptr); - EXPECT_TRUE(hasInferable(ReturnStarInstantiation->getReturnType())); - } - - { - const FunctionDecl* ReturnNoStarInstantiation = selectFirst<FunctionDecl>( - "returnNoStar_instantiation", - match(functionDecl(isTemplateInstantiation(), hasName("returnNoStar")) - .bind("returnNoStar_instantiation"), - Ctx)); - ASSERT_NE(ReturnNoStarInstantiation, nullptr); - EXPECT_FALSE(hasInferable(ReturnNoStarInstantiation->getReturnType())); - } + EXPECT_FALSE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); } -TEST(InferableTest, TypeInsideTemplateTypeParamIsNotInferable) { +TEST(IsInferenceTargetTest, + NestedTemplatesWithPointerUsingOuterTemplateParameter) { + TestAST AST(R"cc( + template <typename T> + struct Outer { + template <typename U> + struct Inner { + T* FieldwithPointerUsingOuterTemplateParameter; + }; + Inner<bool> AnInner; + }; + + Outer<int> AnOuter; + int* Field = AnOuter.AnInner.FieldwithPointerUsingOuterTemplateParameter; + )cc"); + EXPECT_TRUE(isInferenceTarget( + *cast<MemberExpr>( + lookup<VarDecl>("Field", AST.context()).getInit()->IgnoreImplicit()) + ->getMemberDecl())); +} + +struct CountInferableSlotsTestInput { + std::string TestCaseName; + std::string InputCode; + int ExpectedSlots; + std::string TargetName = "target"; +}; + +class CountInferableSlotsTest + : public testing::TestWithParam<CountInferableSlotsTestInput> {}; + +TEST_P(CountInferableSlotsTest, CountsInferableSlots) { + TestAST AST(GetParam().InputCode); + EXPECT_EQ(countInferableSlots(lookup(GetParam().TargetName, AST.context())), + GetParam().ExpectedSlots); +} + +INSTANTIATE_TEST_SUITE_P( + CountInferableSlotsTests, CountInferableSlotsTest, + testing::Values( + CountInferableSlotsTestInput{.TestCaseName = "ParamRawPointer", + .InputCode = R"cc( + void target(int*); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ParamAlias", + .InputCode = R"cc( + using Pointer = int*; + void target(Pointer); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ParamNested", + .InputCode = R"cc( + void target(int**); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ParamAliasNested", + .InputCode = R"cc( + using Pointer = int*; + void target(Pointer*); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ParamReference", + .InputCode = R"cc( + void target(int*&); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ParamFunctionPointer", + .InputCode = R"cc( + void target(int (*)()); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{ + .TestCaseName = "ParamStdSmartPointer", + .InputCode = (SmartPointerHeader + R"cc( + void target(std::unique_ptr<int>); + )cc") + .str(), + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{ + .TestCaseName = "ParamCustomSmartPointer", + .InputCode = (SmartPointerHeader + R"cc( + void target(custom_smart_ptr<int>); + )cc") + .str(), + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ReturnRawPointer", + .InputCode = R"cc( + int* target(); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ReturnAlias", + .InputCode = R"cc( + using Pointer = int*; + Pointer target(); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ReturnNested", + .InputCode = R"cc( + int** target(); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ReturnAliasNested", + .InputCode = R"cc( + using Pointer = int*; + Pointer* target(); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ReturnReference", + .InputCode = R"cc( + int*& target(); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "ReturnFunctionPointer", + .InputCode = R"cc( + using FnPtr = int (*)(); + FnPtr target(); + )cc", + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{ + .TestCaseName = "ReturnStdSmartPointer", + .InputCode = (SmartPointerHeader + R"cc( + std::unique_ptr<int> target(); + )cc") + .str(), + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{ + .TestCaseName = "ReturnCustomSmartPointer", + .InputCode = (SmartPointerHeader + R"cc( + custom_smart_ptr<int> target(); + )cc") + .str(), + .ExpectedSlots = 1}, + CountInferableSlotsTestInput{.TestCaseName = "TemplateOfPointer", + .InputCode = R"cc( + template <typename T> + struct S {}; + S<int*> Target; + )cc", + .ExpectedSlots = 0, + .TargetName = "Target"}, + CountInferableSlotsTestInput{.TestCaseName = "PointerToDataMember", + .InputCode = R"cc( + struct T; + int T::* Target; + )cc", + .ExpectedSlots = 0, + .TargetName = "Target"}, + CountInferableSlotsTestInput{.TestCaseName = "PointerToMemberFunction", + .InputCode = R"cc( + struct T; + int (T::*Target)(); + )cc", + .ExpectedSlots = 0, + .TargetName = "Target"}), + [](testing::TestParamInfo<CountInferableSlotsTestInput> Info) { + return Info.param.TestCaseName; + }); + +struct GetInferableSlotIndicesTestInput { + std::string TestCaseName; + std::string InputCode; + std::vector<int> ExpectedSlotIndices; + std::string TargetName = "target"; +}; + +class GetInferableSlotIndicesTest + : public testing::TestWithParam<GetInferableSlotIndicesTestInput> {}; + +TEST_P(GetInferableSlotIndicesTest, GetsInferableSlotIndices) { + TestAST AST(GetParam().InputCode); + EXPECT_THAT( + getInferableSlotIndices(lookup(GetParam().TargetName, AST.context())), + UnorderedElementsAreArray(GetParam().ExpectedSlotIndices)); +} + +INSTANTIATE_TEST_SUITE_P( + GetInferableSlotIndicesTest, GetInferableSlotIndicesTest, + testing::Values( + GetInferableSlotIndicesTestInput{ + .TestCaseName = "PointerAndNonPointerParams", + .InputCode = R"cc( + void target(int**, int, char, char*); + )cc", + .ExpectedSlotIndices = {1, 4}, + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "PointerReturnWithPointerAndNonPointerParams", + .InputCode = R"cc( + int* target(bool, bool*); + )cc", + .ExpectedSlotIndices = {0, 2}, + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "StdSmartPointerParam", + .InputCode = (SmartPointerHeader + R"cc( + void target(std::unique_ptr<int>); + )cc") + .str(), + .ExpectedSlotIndices = {1}, + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "CustomSmartPointerParam", + .InputCode = (SmartPointerHeader + R"cc( + void target(custom_smart_ptr<int>); + )cc") + .str(), + .ExpectedSlotIndices = {1}, + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "NonPointerVariable", + .InputCode = R"cc( + int Target; + )cc", + .ExpectedSlotIndices = {}, + .TargetName = "Target", + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "PointerVariable", + .InputCode = R"cc( + int* Target; + )cc", + .ExpectedSlotIndices = {0}, + .TargetName = "Target", + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "NestedPointerVariable", + .InputCode = R"cc( + int** Target; + )cc", + .ExpectedSlotIndices = {0}, + .TargetName = "Target", + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "NonPointerClassMember", + .InputCode = R"cc( + class C { + int Target; + }; + )cc", + .ExpectedSlotIndices = {}, + .TargetName = "Target", + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "PointerClassMember", + .InputCode = R"cc( + class C { + int* Target; + }; + )cc", + .ExpectedSlotIndices = {0}, + .TargetName = "Target", + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "NestedPointerClassMember", + .InputCode = R"cc( + class C { + int** Target; + }; + )cc", + .ExpectedSlotIndices = {0}, + .TargetName = "Target", + }, + GetInferableSlotIndicesTestInput{ + .TestCaseName = "PointerContainingClassMemberFunction", + .InputCode = R"cc( + class C { + int* target(bool, bool*); + }; + )cc", + .ExpectedSlotIndices = {0, 2}, + }), + [](testing::TestParamInfo<GetInferableSlotIndicesTestInput> Info) { + return Info.param.TestCaseName; + }); + +TEST(HasInferableTest, + LocalInFunctionTemplateInstantiationWithTemplateArgumentType) { + TestAST AST(R"cc( + template <typename T> + void funcTmpl() { + T LocalInTmpl; + } + + auto& FuncTmplInst = funcTmpl<int*>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + EXPECT_FALSE(hasInferable( + lookup<VarDecl>("LocalInTmpl", Ctx, &Instantiation).getType())); +} + +TEST(HasInferableTest, + LocalInFunctionTemplateInstantiationWithPointerToTemplateArgumentType) { + TestAST AST(R"cc( + template <typename T> + void funcTmpl() { + T* LocalInTmpl; + } + + auto& FuncTmplInst = funcTmpl<int>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + EXPECT_TRUE(hasInferable( + lookup<VarDecl>("LocalInTmpl", Ctx, &Instantiation).getType())); +} + +TEST(HasInferableTest, + ParamInFunctionTemplateInstantiationWithTemplateArgumentType) { + TestAST AST(R"cc( + template <typename T> + void funcTmpl(T ParamInTmpl) {} + + auto& FuncTmplInst = funcTmpl<int*>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + EXPECT_FALSE(hasInferable( + lookup<ParmVarDecl>("ParamInTmpl", Ctx, &Instantiation).getType())); +} + +TEST(HasInferableTest, + ParamInFunctionTemplateInstantiationWithPointerToTemplateArgumentType) { + TestAST AST(R"cc( + template <typename T> + void funcTmpl(T* ParamInTmpl) {} + + auto& FuncTmplInst = funcTmpl<int>; + )cc"); + ASTContext& Ctx = AST.context(); + const ValueDecl& Instantiation = + *cast<DeclRefExpr>( + lookup<VarDecl>("FuncTmplInst", Ctx).getInit()->IgnoreImplicit()) + ->getDecl(); + EXPECT_TRUE(hasInferable( + lookup<ParmVarDecl>("ParamInTmpl", Ctx, &Instantiation).getType())); +} + +TEST(HasInferableTest, + FunctionTemplateInstantiationReturnOfTemplateArgumentType) { + TestAST AST(R"cc( + template <typename T> + T funcTmpl() {} + + auto& FuncTmplInst = funcTmpl<int*>; + )cc"); + const FunctionDecl* Instantiation = cast<FunctionDecl>( + cast<DeclRefExpr>(lookup<VarDecl>("FuncTmplInst", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getDecl()); + EXPECT_FALSE(hasInferable(Instantiation->getReturnType())); +} + +TEST(HasInferableTest, + FunctionTemplateInstantiationReturnOfPointerToTemplateArgumentType) { + TestAST AST(R"cc( + template <typename T> + T* funcTmpl() {} + + auto& FuncTmplInst = funcTmpl<int*>; + )cc"); + const FunctionDecl* Instantiation = cast<FunctionDecl>( + cast<DeclRefExpr>(lookup<VarDecl>("FuncTmplInst", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getDecl()); + EXPECT_TRUE(hasInferable(Instantiation->getReturnType())); +} + +TEST(HasInferableTest, TypeInsideTemplateTypeParamIsNotInferableBecauseOfName) { TestAST AST(R"cc( template <class T> // Being named Ptr does not guarantee that the return type will always be a // pointer. So, we still don't annotate unless the pointer-ness is part of // the template declaration. T::Ptr ReturnsTypeNamedPtr(); + + class S { + public: + typedef const char* Ptr; + }; + + auto& FuncTmplInst = ReturnsTypeNamedPtr<S>; + )cc"); + const FunctionDecl* Instantiation = cast<FunctionDecl>( + cast<DeclRefExpr>(lookup<VarDecl>("FuncTmplInst", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getDecl()); + EXPECT_FALSE(hasInferable(Instantiation->getReturnType())); +} + +TEST(HasInferableTest, + TypeInsideTemplateTypeParamIsInferableIfPointerIsInsideTemplate) { + TestAST AST(R"cc( template <class T> T::Ptr* ReturnsPtr(); @@ -607,78 +1230,14 @@ typedef const char* Ptr; }; - int main() { - ReturnsTypeNamedPtr<S>(); - ReturnsPtr<S>(); - } + auto& FuncTmplInst = ReturnsPtr<S>; )cc"); - - { - const FunctionDecl* ReturnsTypeNamedPtr = selectFirst<FunctionDecl>( - "func", match(functionDecl(isTemplateInstantiation(), - hasName("ReturnsTypeNamedPtr")) - .bind("func"), - AST.context())); - ASSERT_NE(ReturnsTypeNamedPtr, nullptr); - EXPECT_FALSE(hasInferable(ReturnsTypeNamedPtr->getReturnType())); - } - - { - const FunctionDecl* ReturnsPtr = selectFirst<FunctionDecl>( - "func", - match(functionDecl(isTemplateInstantiation(), hasName("ReturnsPtr")) - .bind("func"), - AST.context())); - ASSERT_NE(ReturnsPtr, nullptr); - EXPECT_TRUE(hasInferable(ReturnsPtr->getReturnType())); - } -} - -TEST(InferableTest, GetInferableSlotIndices) { - TestAST AST((SmartPointerHeader + R"cc( - void f1(int**, int, char, char*); - int* f2(bool, bool*); - void f3(std::unique_ptr<int>); - void f4(custom_smart_ptr<int>); - - int v1; - int* v2; - int** v3; - - class C { - int field1; - int* field2; - int** field3; - - int* method(bool, bool*); - }; - )cc") - .str()); - auto& Ctx = AST.context(); - - EXPECT_THAT(getInferableSlotIndices(lookup("f1", Ctx)), - UnorderedElementsAre(1, 4)); - EXPECT_THAT(getInferableSlotIndices(lookup("f2", Ctx)), - UnorderedElementsAre(0, 2)); - EXPECT_THAT(getInferableSlotIndices(lookup("f3", Ctx)), - UnorderedElementsAre(1)); - EXPECT_THAT(getInferableSlotIndices(lookup("f4", Ctx)), - UnorderedElementsAre(1)); - - EXPECT_THAT(getInferableSlotIndices(lookup("v1", Ctx)), IsEmpty()); - EXPECT_THAT(getInferableSlotIndices(lookup("v2", Ctx)), - UnorderedElementsAre(0)); - EXPECT_THAT(getInferableSlotIndices(lookup("v3", Ctx)), - UnorderedElementsAre(0)); - - EXPECT_THAT(getInferableSlotIndices(lookup("field1", Ctx)), IsEmpty()); - EXPECT_THAT(getInferableSlotIndices(lookup("field2", Ctx)), - UnorderedElementsAre(0)); - EXPECT_THAT(getInferableSlotIndices(lookup("field3", Ctx)), - UnorderedElementsAre(0)); - - EXPECT_THAT(getInferableSlotIndices(lookup("method", Ctx)), - UnorderedElementsAre(0, 2)); + const FunctionDecl* Instantiation = cast<FunctionDecl>( + cast<DeclRefExpr>(lookup<VarDecl>("FuncTmplInst", AST.context()) + .getInit() + ->IgnoreImplicit()) + ->getDecl()); + EXPECT_TRUE(hasInferable(Instantiation->getReturnType())); } } // namespace