1 //===-- ExpectedTypeTest.cpp -----------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "ExpectedTypes.h" 10 #include "ParsedAST.h" 11 #include "TestTU.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/AST/Decl.h" 14 #include "llvm/ADT/StringRef.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 18 namespace clang { 19 namespace clangd { 20 namespace { 21 22 using ::testing::Field; 23 using ::testing::Matcher; 24 using ::testing::SizeIs; 25 using ::testing::UnorderedElementsAreArray; 26 27 class ExpectedTypeConversionTest : public ::testing::Test { 28 protected: 29 void build(llvm::StringRef Code) { 30 assert(!AST && "AST built twice"); 31 AST = TestTU::withCode(Code).build(); 32 } 33 34 const NamedDecl *decl(llvm::StringRef Name) { return &findDecl(*AST, Name); } 35 36 QualType typeOf(llvm::StringRef Name) { 37 return cast<ValueDecl>(decl(Name))->getType().getCanonicalType(); 38 } 39 40 /// An overload for convenience. 41 llvm::Optional<OpaqueType> fromCompletionResult(const NamedDecl *D) { 42 return OpaqueType::fromCompletionResult( 43 astCtx(), CodeCompletionResult(D, CCP_Declaration)); 44 } 45 46 /// A set of DeclNames whose type match each other computed by 47 /// OpaqueType::fromCompletionResult. 48 using EquivClass = std::set<std::string>; 49 50 Matcher<std::map<std::string, EquivClass>> 51 classesAre(llvm::ArrayRef<EquivClass> Classes) { 52 using MapEntry = std::map<std::string, EquivClass>::value_type; 53 54 std::vector<Matcher<MapEntry>> Elements; 55 Elements.reserve(Classes.size()); 56 for (auto &Cls : Classes) 57 Elements.push_back(Field(&MapEntry::second, Cls)); 58 return UnorderedElementsAreArray(Elements); 59 } 60 61 // Groups \p Decls into equivalence classes based on the result of 62 // 'OpaqueType::fromCompletionResult'. 63 std::map<std::string, EquivClass> 64 buildEquivClasses(llvm::ArrayRef<llvm::StringRef> DeclNames) { 65 std::map<std::string, EquivClass> Classes; 66 for (llvm::StringRef Name : DeclNames) { 67 auto Type = OpaqueType::fromType(astCtx(), typeOf(Name)); 68 Classes[std::string(Type->raw())].insert(std::string(Name)); 69 } 70 return Classes; 71 } 72 73 ASTContext &astCtx() { return AST->getASTContext(); } 74 75 private: 76 // Set after calling build(). 77 llvm::Optional<ParsedAST> AST; 78 }; 79 80 TEST_F(ExpectedTypeConversionTest, BasicTypes) { 81 build(R"cpp( 82 // ints. 83 bool b; 84 int i; 85 unsigned int ui; 86 long long ll; 87 88 // floats. 89 float f; 90 double d; 91 92 // pointers 93 int* iptr; 94 bool* bptr; 95 96 // user-defined types. 97 struct X {}; 98 X user_type; 99 )cpp"); 100 101 EXPECT_THAT(buildEquivClasses({"b", "i", "ui", "ll", "f", "d", "iptr", "bptr", 102 "user_type"}), 103 classesAre({{"b"}, 104 {"i", "ui", "ll"}, 105 {"f", "d"}, 106 {"iptr"}, 107 {"bptr"}, 108 {"user_type"}})); 109 } 110 111 TEST_F(ExpectedTypeConversionTest, ReferencesDontMatter) { 112 build(R"cpp( 113 int noref; 114 int & ref = noref; 115 const int & const_ref = noref; 116 int && rv_ref = 10; 117 )cpp"); 118 119 EXPECT_THAT(buildEquivClasses({"noref", "ref", "const_ref", "rv_ref"}), 120 SizeIs(1)); 121 } 122 123 TEST_F(ExpectedTypeConversionTest, ArraysDecay) { 124 build(R"cpp( 125 int arr[2]; 126 int (&arr_ref)[2] = arr; 127 int *ptr; 128 )cpp"); 129 130 EXPECT_THAT(buildEquivClasses({"arr", "arr_ref", "ptr"}), SizeIs(1)); 131 } 132 133 TEST_F(ExpectedTypeConversionTest, FunctionReturns) { 134 build(R"cpp( 135 int returns_int(); 136 int* returns_ptr(); 137 138 int int_; 139 int* int_ptr; 140 )cpp"); 141 142 OpaqueType IntTy = *OpaqueType::fromType(astCtx(), typeOf("int_")); 143 EXPECT_EQ(fromCompletionResult(decl("returns_int")), IntTy); 144 145 OpaqueType IntPtrTy = *OpaqueType::fromType(astCtx(), typeOf("int_ptr")); 146 EXPECT_EQ(fromCompletionResult(decl("returns_ptr")), IntPtrTy); 147 } 148 149 TEST_F(ExpectedTypeConversionTest, Templates) { 150 build(R"cpp( 151 template <class T> 152 int* returns_not_dependent(); 153 template <class T> 154 T* returns_dependent(); 155 156 template <class T> 157 int* var_not_dependent = nullptr; 158 template <class T> 159 T* var_dependent = nullptr; 160 161 int* int_ptr_; 162 )cpp"); 163 164 auto IntPtrTy = *OpaqueType::fromType(astCtx(), typeOf("int_ptr_")); 165 EXPECT_EQ(fromCompletionResult(decl("returns_not_dependent")), IntPtrTy); 166 EXPECT_EQ(fromCompletionResult(decl("returns_dependent")), std::nullopt); 167 168 EXPECT_EQ(fromCompletionResult(decl("var_not_dependent")), IntPtrTy); 169 EXPECT_EQ(fromCompletionResult(decl("var_dependent")), std::nullopt); 170 } 171 172 } // namespace 173 } // namespace clangd 174 } // namespace clang 175