xref: /llvm-project/clang-tools-extra/clangd/unittests/ExpectedTypeTest.cpp (revision 76fcfea283472a80356d87c89270b0e2d106b54c)
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