clang-tools 24.0.0git
InsertionPointTests.cpp
Go to the documentation of this file.
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//===----------------------------------------------------------------------===//
8
9#include "Annotations.h"
10#include "Protocol.h"
11#include "SourceCode.h"
12#include "TestTU.h"
13#include "XRefs.h"
15#include "clang/AST/DeclBase.h"
16#include "clang/AST/DeclObjC.h"
17#include "llvm/Testing/Support/Error.h"
18#include "gmock/gmock.h"
19#include "gtest/gtest.h"
20
21namespace clang {
22namespace clangd {
23namespace {
24using llvm::HasValue;
25
26TEST(InsertionPointTests, Generic) {
27 Annotations Code(R"cpp(
28 namespace ns {
29 $a^int a1;
30 $b^// leading comment
31 int b;
32 $c^int c1; // trailing comment
33 int c2;
34 $a2^int a2;
35 $end^};
36 )cpp");
37
38 auto StartsWith =
39 [&](llvm::StringLiteral S) -> std::function<bool(const Decl *)> {
40 return [S](const Decl *D) {
41 if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
42 return llvm::StringRef(ND->getNameAsString()).starts_with(S);
43 return false;
44 };
45 };
46
47 auto AST = TestTU::withCode(Code.code()).build();
48 auto &NS = cast<NamespaceDecl>(findDecl(AST, "ns"));
49
50 // Test single anchors.
51 auto Point = [&](llvm::StringLiteral Prefix, Anchor::Dir Direction) {
52 auto Loc = insertionPoint(NS, {Anchor{StartsWith(Prefix), Direction}});
53 return sourceLocToPosition(AST.getSourceManager(), Loc);
54 };
55 EXPECT_EQ(Point("a", Anchor::Above), Code.point("a"));
56 EXPECT_EQ(Point("a", Anchor::Below), Code.point("b"));
57 EXPECT_EQ(Point("b", Anchor::Above), Code.point("b"));
58 EXPECT_EQ(Point("b", Anchor::Below), Code.point("c"));
59 EXPECT_EQ(Point("c", Anchor::Above), Code.point("c"));
60 EXPECT_EQ(Point("c", Anchor::Below), Code.point("a2"));
61 EXPECT_EQ(Point("", Anchor::Above), Code.point("a"));
62 EXPECT_EQ(Point("", Anchor::Below), Code.point("end"));
63 EXPECT_EQ(Point("no_match", Anchor::Below), Position{});
64
65 // Test anchor chaining.
66 auto Chain = [&](llvm::StringLiteral P1, llvm::StringLiteral P2) {
67 auto Loc = insertionPoint(NS, {Anchor{StartsWith(P1), Anchor::Above},
68 Anchor{StartsWith(P2), Anchor::Above}});
69 return sourceLocToPosition(AST.getSourceManager(), Loc);
70 };
71 EXPECT_EQ(Chain("a", "b"), Code.point("a"));
72 EXPECT_EQ(Chain("b", "a"), Code.point("b"));
73 EXPECT_EQ(Chain("no_match", "a"), Code.point("a"));
74
75 // Test edit generation.
76 auto Edit = insertDecl("foo;", NS, {Anchor{StartsWith("a"), Anchor::Below}});
77 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
78 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()), Code.point("b"));
79 EXPECT_EQ(Edit->getReplacementText(), "foo;");
80 // If no match, the edit is inserted at the end.
81 Edit = insertDecl("x;", NS, {Anchor{StartsWith("no_match"), Anchor::Below}});
82 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
83 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
84 Code.point("end"));
85}
86
87// For CXX, we should check:
88// - special handling for access specifiers
89// - unwrapping of template decls
90TEST(InsertionPointTests, CXX) {
91 Annotations Code(R"cpp(
92 class C {
93 public:
94 $Method^void pubMethod();
95 $Field^int PubField;
96
97 $private^private:
98 $field^int PrivField;
99 $method^void privMethod();
100 template <typename T> void privTemplateMethod();
101 $end^};
102 )cpp");
103
104 auto AST = TestTU::withCode(Code.code()).build();
105 const CXXRecordDecl &C = cast<CXXRecordDecl>(findDecl(AST, "C"));
106
107 auto IsMethod = [](const Decl *D) { return llvm::isa<CXXMethodDecl>(D); };
108 auto Any = [](const Decl *D) { return true; };
109
110 // Test single anchors.
111 auto Point = [&](Anchor A, AccessSpecifier Protection) {
112 auto Loc = insertionPoint(C, {A}, Protection);
113 return sourceLocToPosition(AST.getSourceManager(), Loc);
114 };
115 EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_public), Code.point("Method"));
116 EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_public), Code.point("Field"));
117 EXPECT_EQ(Point({Any, Anchor::Above}, AS_public), Code.point("Method"));
118 EXPECT_EQ(Point({Any, Anchor::Below}, AS_public), Code.point("private"));
119 EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_private), Code.point("method"));
120 EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_private), Code.point("end"));
121 EXPECT_EQ(Point({Any, Anchor::Above}, AS_private), Code.point("field"));
122 EXPECT_EQ(Point({Any, Anchor::Below}, AS_private), Code.point("end"));
123 EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_protected), Position{});
124 EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_protected), Position{});
125 EXPECT_EQ(Point({Any, Anchor::Above}, AS_protected), Position{});
126 EXPECT_EQ(Point({Any, Anchor::Below}, AS_protected), Position{});
127
128 // Edits when there's no match --> end of matching access control section.
129 auto Edit = insertDecl("x", C, {}, AS_public);
130 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
131 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
132 Code.point("private"));
133
134 Edit = insertDecl("x", C, {}, AS_private);
135 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
136 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
137 Code.point("end"));
138
139 Edit = insertDecl("x", C, {}, AS_protected);
140 ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());
141 EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),
142 Code.point("end"));
143 EXPECT_EQ(Edit->getReplacementText(), "protected:\nx");
144}
145
146MATCHER_P(replacementText, Text, "") {
147 if (arg.getReplacementText() != Text) {
148 *result_listener << "replacement is " << arg.getReplacementText().str();
149 return false;
150 }
151 return true;
152}
153
154TEST(InsertionPointTests, CXXAccessProtection) {
155 // Empty class uses default access.
156 auto AST = TestTU::withCode("struct S{};").build();
157 const CXXRecordDecl &S = cast<CXXRecordDecl>(findDecl(AST, "S"));
158 ASSERT_THAT_EXPECTED(insertDecl("x", S, {}, AS_public),
159 HasValue(replacementText("x")));
160 ASSERT_THAT_EXPECTED(insertDecl("x", S, {}, AS_private),
161 HasValue(replacementText("private:\nx")));
162
163 // We won't insert above the first access specifier if there's nothing there.
164 AST = TestTU::withCode("struct T{private:};").build();
165 const CXXRecordDecl &T = cast<CXXRecordDecl>(findDecl(AST, "T"));
166 ASSERT_THAT_EXPECTED(insertDecl("x", T, {}, AS_public),
167 HasValue(replacementText("public:\nx")));
168 ASSERT_THAT_EXPECTED(insertDecl("x", T, {}, AS_private),
169 HasValue(replacementText("x")));
170
171 // But we will if there are declarations.
172 AST = TestTU::withCode("struct U{int i;private:};").build();
173 const CXXRecordDecl &U = cast<CXXRecordDecl>(findDecl(AST, "U"));
174 ASSERT_THAT_EXPECTED(insertDecl("x", U, {}, AS_public),
175 HasValue(replacementText("x")));
176 ASSERT_THAT_EXPECTED(insertDecl("x", U, {}, AS_private),
177 HasValue(replacementText("x")));
178}
179
180// In ObjC we need to take care to get the @end fallback right.
181TEST(InsertionPointTests, ObjC) {
182 Annotations Code(R"objc(
183 @interface Foo
184 -(void) v;
185 $endIface^@end
186 @implementation Foo
187 -(void) v {}
188 $endImpl^@end
189 )objc");
190 auto TU = TestTU::withCode(Code.code());
191 TU.Filename = "TestTU.m";
192 auto AST = TU.build();
193
194 auto &Impl =
195 cast<ObjCImplementationDecl>(findDecl(AST, [&](const NamedDecl &D) {
196 return llvm::isa<ObjCImplementationDecl>(D);
197 }));
198 auto &Iface = *Impl.getClassInterface();
199 Anchor End{[](const Decl *) { return true; }, Anchor::Below};
200
201 const auto &SM = AST.getSourceManager();
202 EXPECT_EQ(sourceLocToPosition(SM, insertionPoint(Iface, {End})),
203 Code.point("endIface"));
204 EXPECT_EQ(sourceLocToPosition(SM, insertionPoint(Impl, {End})),
205 Code.point("endImpl"));
206}
207
208} // namespace
209} // namespace clangd
210} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
MATCHER_P(named, N, "")
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
TEST(BackgroundQueueTest, Priority)
SourceLocation insertionPoint(const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
llvm::Expected< tooling::Replacement > insertDecl(llvm::StringRef Code, const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
Definition SourceCode.h:189
ParsedAST build() const
Definition TestTU.cpp:115
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36