1 //===-- InsertionPointTess.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
11 #include "SourceCode.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"
25 TEST(InsertionPointTests
, Generic
) {
26 Annotations
Code(R
"cpp(
31 $c^int c1; // trailing comment
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
);
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()),
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(
93 $Method^void pubMethod();
98 $method^void privMethod();
99 template <typename T> void privTemplateMethod();
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()),
138 Edit
= insertDecl("x", C
, {}, AS_protected
);
139 ASSERT_THAT_EXPECTED(Edit
, llvm::Succeeded());
140 EXPECT_EQ(offsetToPosition(Code
.code(), Edit
->getOffset()),
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();
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(
189 auto TU
= TestTU::withCode(Code
.code());
190 TU
.Filename
= "TestTU.m";
191 auto AST
= TU
.build();
194 cast
<ObjCImplementationDecl
>(findDecl(AST
, [&](const NamedDecl
&D
) {
195 return llvm::isa
<ObjCImplementationDecl
>(D
);
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"));
208 } // namespace clangd