[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang-tools-extra / clangd / unittests / InsertionPointTests.cpp
blob62c06bb863772f6a5fcb7f98582b6020019d05ef
1 //===-- InsertionPointTess.cpp -------------------------------------------===//
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 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
10 #include "Protocol.h"
11 #include "SourceCode.h"
12 #include "TestTU.h"
13 #include "XRefs.h"
14 #include "refactor/InsertionPoint.h"
15 #include "clang/AST/DeclBase.h"
16 #include "llvm/Testing/Support/Error.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
20 namespace clang {
21 namespace clangd {
22 namespace {
23 using llvm::HasValue;
25 TEST(InsertionPointTests, Generic) {
26 Annotations Code(R"cpp(
27 namespace ns {
28 $a^int a1;
29 $b^// leading comment
30 int b;
31 $c^int c1; // trailing comment
32 int c2;
33 $a2^int a2;
34 $end^};
35 )cpp");
37 auto StartsWith =
38 [&](llvm::StringLiteral S) -> std::function<bool(const Decl *)> {
39 return [S](const Decl *D) {
40 if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
41 return llvm::StringRef(ND->getNameAsString()).startswith(S);
42 return false;
46 auto AST = TestTU::withCode(Code.code()).build();
47 auto &NS = cast<NamespaceDecl>(findDecl(AST, "ns"));
49 // Test single anchors.
50 auto Point = [&](llvm::StringLiteral Prefix, Anchor::Dir Direction) {
51 auto Loc = insertionPoint(NS, {Anchor{StartsWith(Prefix), Direction}});
52 return sourceLocToPosition(AST.getSourceManager(), Loc);
54 EXPECT_EQ(Point("a", Anchor::Above), Code.point("a"));
55 EXPECT_EQ(Point("a", Anchor::Below), Code.point("b"));
56 EXPECT_EQ(Point("b", Anchor::Above), Code.point("b"));
57 EXPECT_EQ(Point("b", Anchor::Below), Code.point("c"));
58 EXPECT_EQ(Point("c", Anchor::Above), Code.point("c"));
59 EXPECT_EQ(Point("c", Anchor::Below), Code.point("a2"));
60 EXPECT_EQ(Point("", Anchor::Above), Code.point("a"));
61 EXPECT_EQ(Point("", Anchor::Below), Code.point("end"));
62 EXPECT_EQ(Point("no_match", Anchor::Below), Position{});
64 // Test anchor chaining.
65 auto Chain = [&](llvm::StringLiteral P1, llvm::StringLiteral P2) {
66 auto Loc = insertionPoint(NS, {Anchor{StartsWith(P1), Anchor::Above},
67 Anchor{StartsWith(P2), Anchor::Above}});
68 return sourceLocToPosition(AST.getSourceManager(), Loc);
70 EXPECT_EQ(Chain("a", "b"), Code.point("a"));
71 EXPECT_EQ(Chain("b", "a"), Code.point("b"));
72 EXPECT_EQ(Chain("no_match", "a"), Code.point("a"));
74 // Test edit generation.
75 auto Edit = insertDecl("foo;", NS, {Anchor{StartsWith("a"), Anchor::Below}});
76 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
77 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()), Code.point("b"));
78 EXPECT_EQ(Edit->getReplacementText(), "foo;");
79 // If no match, the edit is inserted at the end.
80 Edit = insertDecl("x;", NS, {Anchor{StartsWith("no_match"), Anchor::Below}});
81 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
82 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
83 Code.point("end"));
86 // For CXX, we should check:
87 // - special handling for access specifiers
88 // - unwrapping of template decls
89 TEST(InsertionPointTests, CXX) {
90 Annotations Code(R"cpp(
91 class C {
92 public:
93 $Method^void pubMethod();
94 $Field^int PubField;
96 $private^private:
97 $field^int PrivField;
98 $method^void privMethod();
99 template <typename T> void privTemplateMethod();
100 $end^};
101 )cpp");
103 auto AST = TestTU::withCode(Code.code()).build();
104 const CXXRecordDecl &C = cast<CXXRecordDecl>(findDecl(AST, "C"));
106 auto IsMethod = [](const Decl *D) { return llvm::isa<CXXMethodDecl>(D); };
107 auto Any = [](const Decl *D) { return true; };
109 // Test single anchors.
110 auto Point = [&](Anchor A, AccessSpecifier Protection) {
111 auto Loc = insertionPoint(C, {A}, Protection);
112 return sourceLocToPosition(AST.getSourceManager(), Loc);
114 EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_public), Code.point("Method"));
115 EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_public), Code.point("Field"));
116 EXPECT_EQ(Point({Any, Anchor::Above}, AS_public), Code.point("Method"));
117 EXPECT_EQ(Point({Any, Anchor::Below}, AS_public), Code.point("private"));
118 EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_private), Code.point("method"));
119 EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_private), Code.point("end"));
120 EXPECT_EQ(Point({Any, Anchor::Above}, AS_private), Code.point("field"));
121 EXPECT_EQ(Point({Any, Anchor::Below}, AS_private), Code.point("end"));
122 EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_protected), Position{});
123 EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_protected), Position{});
124 EXPECT_EQ(Point({Any, Anchor::Above}, AS_protected), Position{});
125 EXPECT_EQ(Point({Any, Anchor::Below}, AS_protected), Position{});
127 // Edits when there's no match --> end of matching access control section.
128 auto Edit = insertDecl("x", C, {}, AS_public);
129 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
130 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
131 Code.point("private"));
133 Edit = insertDecl("x", C, {}, AS_private);
134 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
135 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
136 Code.point("end"));
138 Edit = insertDecl("x", C, {}, AS_protected);
139 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
140 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
141 Code.point("end"));
142 EXPECT_EQ(Edit->getReplacementText(), "protected:\nx");
145 MATCHER_P(replacementText, Text, "") {
146 if (arg.getReplacementText() != Text) {
147 *result_listener << "replacement is " << arg.getReplacementText().str();
148 return false;
150 return true;
153 TEST(InsertionPointTests, CXXAccessProtection) {
154 // Empty class uses default access.
155 auto AST = TestTU::withCode("struct S{};").build();
156 const CXXRecordDecl &S = cast<CXXRecordDecl>(findDecl(AST, "S"));
157 ASSERT_THAT_EXPECTED(insertDecl("x", S, {}, AS_public),
158 HasValue(replacementText("x")));
159 ASSERT_THAT_EXPECTED(insertDecl("x", S, {}, AS_private),
160 HasValue(replacementText("private:\nx")));
162 // We won't insert above the first access specifier if there's nothing there.
163 AST = TestTU::withCode("struct T{private:};").build();
164 const CXXRecordDecl &T = cast<CXXRecordDecl>(findDecl(AST, "T"));
165 ASSERT_THAT_EXPECTED(insertDecl("x", T, {}, AS_public),
166 HasValue(replacementText("public:\nx")));
167 ASSERT_THAT_EXPECTED(insertDecl("x", T, {}, AS_private),
168 HasValue(replacementText("x")));
170 // But we will if there are declarations.
171 AST = TestTU::withCode("struct U{int i;private:};").build();
172 const CXXRecordDecl &U = cast<CXXRecordDecl>(findDecl(AST, "U"));
173 ASSERT_THAT_EXPECTED(insertDecl("x", U, {}, AS_public),
174 HasValue(replacementText("x")));
175 ASSERT_THAT_EXPECTED(insertDecl("x", U, {}, AS_private),
176 HasValue(replacementText("x")));
179 // In ObjC we need to take care to get the @end fallback right.
180 TEST(InsertionPointTests, ObjC) {
181 Annotations Code(R"objc(
182 @interface Foo
183 -(void) v;
184 $endIface^@end
185 @implementation Foo
186 -(void) v {}
187 $endImpl^@end
188 )objc");
189 auto TU = TestTU::withCode(Code.code());
190 TU.Filename = "TestTU.m";
191 auto AST = TU.build();
193 auto &Impl =
194 cast<ObjCImplementationDecl>(findDecl(AST, [&](const NamedDecl &D) {
195 return llvm::isa<ObjCImplementationDecl>(D);
196 }));
197 auto &Iface = *Impl.getClassInterface();
198 Anchor End{[](const Decl *) { return true; }, Anchor::Below};
200 const auto &SM = AST.getSourceManager();
201 EXPECT_EQ(sourceLocToPosition(SM, insertionPoint(Iface, {End})),
202 Code.point("endIface"));
203 EXPECT_EQ(sourceLocToPosition(SM, insertionPoint(Impl, {End})),
204 Code.point("endImpl"));
207 } // namespace
208 } // namespace clangd
209 } // namespace clang