1 //===-- ASTTests.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 "AST.h"
10
11 #include "Annotations.h"
12 #include "ParsedAST.h"
13 #include "TestTU.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/DeclBase.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Casting.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include <cstddef>
22 #include <string>
23 #include <vector>
24
25 namespace clang {
26 namespace clangd {
27 namespace {
28
TEST(GetDeducedType,KwAutoExpansion)29 TEST(GetDeducedType, KwAutoExpansion) {
30 struct Test {
31 StringRef AnnotatedCode;
32 const char *DeducedType;
33 } Tests[] = {
34 {"^auto i = 0;", "int"},
35 {"^auto f(){ return 1;};", "int"},
36 };
37 for (Test T : Tests) {
38 Annotations File(T.AnnotatedCode);
39 auto AST = TestTU::withCode(File.code()).build();
40 SourceManagerForFile SM("foo.cpp", File.code());
41
42 for (Position Pos : File.points()) {
43 auto Location = sourceLocationInMainFile(SM.get(), Pos);
44 ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError());
45 auto DeducedType = getDeducedType(AST.getASTContext(), *Location);
46 EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
47 }
48 }
49 }
50
TEST(ClangdAST,GetQualification)51 TEST(ClangdAST, GetQualification) {
52 // Tries to insert the decl `Foo` into position of each decl named `insert`.
53 // This is done to get an appropriate DeclContext for the insertion location.
54 // Qualifications are the required nested name specifier to spell `Foo` at the
55 // `insert`ion location.
56 // VisibleNamespaces are assumed to be visible at every insertion location.
57 const struct {
58 llvm::StringRef Test;
59 std::vector<llvm::StringRef> Qualifications;
60 std::vector<std::string> VisibleNamespaces;
61 } Cases[] = {
62 {
63 R"cpp(
64 namespace ns1 { namespace ns2 { class Foo {}; } }
65 void insert(); // ns1::ns2::Foo
66 namespace ns1 {
67 void insert(); // ns2::Foo
68 namespace ns2 {
69 void insert(); // Foo
70 }
71 using namespace ns2;
72 void insert(); // Foo
73 }
74 using namespace ns1;
75 void insert(); // ns2::Foo
76 using namespace ns2;
77 void insert(); // Foo
78 )cpp",
79 {"ns1::ns2::", "ns2::", "", "", "ns2::", ""},
80 {},
81 },
82 {
83 R"cpp(
84 namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } }
85 void insert(); // ns1::ns2::Bar::Foo
86 namespace ns1 {
87 void insert(); // ns2::Bar::Foo
88 namespace ns2 {
89 void insert(); // Bar::Foo
90 }
91 using namespace ns2;
92 void insert(); // Bar::Foo
93 }
94 using namespace ns1;
95 void insert(); // ns2::Bar::Foo
96 using namespace ns2;
97 void insert(); // Bar::Foo
98 )cpp",
99 {"ns1::ns2::Bar::", "ns2::Bar::", "Bar::", "Bar::", "ns2::Bar::",
100 "Bar::"},
101 {},
102 },
103 {
104 R"cpp(
105 namespace ns1 { namespace ns2 { void Foo(); } }
106 void insert(); // ns2::Foo
107 namespace ns1 {
108 void insert(); // ns2::Foo
109 namespace ns2 {
110 void insert(); // Foo
111 }
112 }
113 )cpp",
114 {"ns2::", "ns2::", ""},
115 {"ns1::"},
116 },
117 };
118 for (const auto &Case : Cases) {
119 Annotations Test(Case.Test);
120 TestTU TU = TestTU::withCode(Test.code());
121 ParsedAST AST = TU.build();
122 std::vector<const Decl *> InsertionPoints;
123 const NamedDecl *TargetDecl;
124 findDecl(AST, [&](const NamedDecl &ND) {
125 if (ND.getNameAsString() == "Foo") {
126 TargetDecl = &ND;
127 return true;
128 }
129
130 if (ND.getNameAsString() == "insert")
131 InsertionPoints.push_back(&ND);
132 return false;
133 });
134
135 ASSERT_EQ(InsertionPoints.size(), Case.Qualifications.size());
136 for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
137 const Decl *D = InsertionPoints[I];
138 if (Case.VisibleNamespaces.empty()) {
139 EXPECT_EQ(getQualification(AST.getASTContext(),
140 D->getLexicalDeclContext(), D->getBeginLoc(),
141 TargetDecl),
142 Case.Qualifications[I]);
143 } else {
144 EXPECT_EQ(getQualification(AST.getASTContext(),
145 D->getLexicalDeclContext(), TargetDecl,
146 Case.VisibleNamespaces),
147 Case.Qualifications[I]);
148 }
149 }
150 }
151 }
152
TEST(ClangdAST,PrintType)153 TEST(ClangdAST, PrintType) {
154 const struct {
155 llvm::StringRef Test;
156 std::vector<llvm::StringRef> Types;
157 } Cases[] = {
158 {
159 R"cpp(
160 namespace ns1 { namespace ns2 { class Foo {}; } }
161 void insert(); // ns1::ns2::Foo
162 namespace ns1 {
163 void insert(); // ns2::Foo
164 namespace ns2 {
165 void insert(); // Foo
166 }
167 }
168 )cpp",
169 {"ns1::ns2::Foo", "ns2::Foo", "Foo"},
170 },
171 {
172 R"cpp(
173 namespace ns1 {
174 typedef int Foo;
175 }
176 void insert(); // ns1::Foo
177 namespace ns1 {
178 void insert(); // Foo
179 }
180 )cpp",
181 {"ns1::Foo", "Foo"},
182 },
183 };
184 for (const auto &Case : Cases) {
185 Annotations Test(Case.Test);
186 TestTU TU = TestTU::withCode(Test.code());
187 ParsedAST AST = TU.build();
188 std::vector<const DeclContext *> InsertionPoints;
189 const TypeDecl *TargetDecl = nullptr;
190 findDecl(AST, [&](const NamedDecl &ND) {
191 if (ND.getNameAsString() == "Foo") {
192 if (const auto *TD = llvm::dyn_cast<TypeDecl>(&ND)) {
193 TargetDecl = TD;
194 return true;
195 }
196 } else if (ND.getNameAsString() == "insert")
197 InsertionPoints.push_back(ND.getDeclContext());
198 return false;
199 });
200
201 ASSERT_EQ(InsertionPoints.size(), Case.Types.size());
202 for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
203 const auto *DC = InsertionPoints[I];
204 EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC),
205 Case.Types[I]);
206 }
207 }
208 }
209 } // namespace
210 } // namespace clangd
211 } // namespace clang
212