1 //===-- CodeCompletionStringsTests.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 "CodeCompletionStrings.h"
10 #include "clang/Sema/CodeCompleteConsumer.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13 
14 namespace clang {
15 namespace clangd {
16 namespace {
17 
18 class CompletionStringTest : public ::testing::Test {
19 public:
CompletionStringTest()20   CompletionStringTest()
21       : Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
22         CCTUInfo(Allocator), Builder(*Allocator, CCTUInfo) {}
23 
24 protected:
computeSignature(const CodeCompletionString & CCS,bool CompletingPattern=false)25   void computeSignature(const CodeCompletionString &CCS,
26                         bool CompletingPattern = false) {
27     Signature.clear();
28     Snippet.clear();
29     getSignature(CCS, &Signature, &Snippet, /*RequiredQualifier=*/nullptr,
30                  CompletingPattern);
31   }
32 
33   std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
34   CodeCompletionTUInfo CCTUInfo;
35   CodeCompletionBuilder Builder;
36   std::string Signature;
37   std::string Snippet;
38 };
39 
TEST_F(CompletionStringTest,ReturnType)40 TEST_F(CompletionStringTest, ReturnType) {
41   Builder.AddResultTypeChunk("result");
42   Builder.AddResultTypeChunk("redundant result no no");
43   EXPECT_EQ(getReturnType(*Builder.TakeString()), "result");
44 }
45 
TEST_F(CompletionStringTest,Documentation)46 TEST_F(CompletionStringTest, Documentation) {
47   Builder.addBriefComment("This is ignored");
48   EXPECT_EQ(formatDocumentation(*Builder.TakeString(), "Is this brief?"),
49             "Is this brief?");
50 }
51 
TEST_F(CompletionStringTest,DocumentationWithAnnotation)52 TEST_F(CompletionStringTest, DocumentationWithAnnotation) {
53   Builder.addBriefComment("This is ignored");
54   Builder.AddAnnotation("Ano");
55   EXPECT_EQ(formatDocumentation(*Builder.TakeString(), "Is this brief?"),
56             "Annotation: Ano\n\nIs this brief?");
57 }
58 
TEST_F(CompletionStringTest,MultipleAnnotations)59 TEST_F(CompletionStringTest, MultipleAnnotations) {
60   Builder.AddAnnotation("Ano1");
61   Builder.AddAnnotation("Ano2");
62   Builder.AddAnnotation("Ano3");
63 
64   EXPECT_EQ(formatDocumentation(*Builder.TakeString(), ""),
65             "Annotations: Ano1 Ano2 Ano3\n");
66 }
67 
TEST_F(CompletionStringTest,EmptySignature)68 TEST_F(CompletionStringTest, EmptySignature) {
69   Builder.AddTypedTextChunk("X");
70   Builder.AddResultTypeChunk("result no no");
71   computeSignature(*Builder.TakeString());
72   EXPECT_EQ(Signature, "");
73   EXPECT_EQ(Snippet, "");
74 }
75 
TEST_F(CompletionStringTest,Function)76 TEST_F(CompletionStringTest, Function) {
77   Builder.AddResultTypeChunk("result no no");
78   Builder.addBriefComment("This comment is ignored");
79   Builder.AddTypedTextChunk("Foo");
80   Builder.AddChunk(CodeCompletionString::CK_LeftParen);
81   Builder.AddPlaceholderChunk("p1");
82   Builder.AddChunk(CodeCompletionString::CK_Comma);
83   Builder.AddPlaceholderChunk("p2");
84   Builder.AddChunk(CodeCompletionString::CK_RightParen);
85 
86   auto *CCS = Builder.TakeString();
87   computeSignature(*CCS);
88   EXPECT_EQ(Signature, "(p1, p2)");
89   EXPECT_EQ(Snippet, "(${1:p1}, ${2:p2})");
90   EXPECT_EQ(formatDocumentation(*CCS, "Foo's comment"), "Foo's comment");
91 }
92 
TEST_F(CompletionStringTest,FunctionWithDefaultParams)93 TEST_F(CompletionStringTest, FunctionWithDefaultParams) {
94   // return_type foo(p1, p2 = 0, p3 = 0)
95   Builder.AddChunk(CodeCompletionString::CK_Comma);
96   Builder.AddTypedTextChunk("p3 = 0");
97   auto *DefaultParam2 = Builder.TakeString();
98 
99   Builder.AddChunk(CodeCompletionString::CK_Comma);
100   Builder.AddTypedTextChunk("p2 = 0");
101   Builder.AddOptionalChunk(DefaultParam2);
102   auto *DefaultParam1 = Builder.TakeString();
103 
104   Builder.AddResultTypeChunk("return_type");
105   Builder.AddTypedTextChunk("Foo");
106   Builder.AddChunk(CodeCompletionString::CK_LeftParen);
107   Builder.AddPlaceholderChunk("p1");
108   Builder.AddOptionalChunk(DefaultParam1);
109   Builder.AddChunk(CodeCompletionString::CK_RightParen);
110 
111   auto *CCS = Builder.TakeString();
112   computeSignature(*CCS);
113   EXPECT_EQ(Signature, "(p1, p2 = 0, p3 = 0)");
114   EXPECT_EQ(Snippet, "(${1:p1})");
115 }
116 
TEST_F(CompletionStringTest,EscapeSnippet)117 TEST_F(CompletionStringTest, EscapeSnippet) {
118   Builder.AddTypedTextChunk("Foo");
119   Builder.AddChunk(CodeCompletionString::CK_LeftParen);
120   Builder.AddPlaceholderChunk("$p}1\\");
121   Builder.AddChunk(CodeCompletionString::CK_RightParen);
122 
123   computeSignature(*Builder.TakeString());
124   EXPECT_EQ(Signature, "($p}1\\)");
125   EXPECT_EQ(Snippet, "(${1:\\$p\\}1\\\\})");
126 }
127 
TEST_F(CompletionStringTest,SnippetsInPatterns)128 TEST_F(CompletionStringTest, SnippetsInPatterns) {
129   auto MakeCCS = [this]() -> const CodeCompletionString & {
130     CodeCompletionBuilder Builder(*Allocator, CCTUInfo);
131     Builder.AddTypedTextChunk("namespace");
132     Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
133     Builder.AddPlaceholderChunk("name");
134     Builder.AddChunk(CodeCompletionString::CK_Equal);
135     Builder.AddPlaceholderChunk("target");
136     Builder.AddChunk(CodeCompletionString::CK_SemiColon);
137     return *Builder.TakeString();
138   };
139   computeSignature(MakeCCS(), /*CompletingPattern=*/false);
140   EXPECT_EQ(Snippet, " ${1:name} = ${2:target};");
141 
142   // When completing a pattern, the last placeholder holds the cursor position.
143   computeSignature(MakeCCS(), /*CompletingPattern=*/true);
144   EXPECT_EQ(Snippet, " ${1:name} = ${0:target};");
145 }
146 
TEST_F(CompletionStringTest,IgnoreInformativeQualifier)147 TEST_F(CompletionStringTest, IgnoreInformativeQualifier) {
148   Builder.AddTypedTextChunk("X");
149   Builder.AddInformativeChunk("info ok");
150   Builder.AddInformativeChunk("info no no::");
151   computeSignature(*Builder.TakeString());
152   EXPECT_EQ(Signature, "info ok");
153   EXPECT_EQ(Snippet, "");
154 }
155 
TEST_F(CompletionStringTest,ObjectiveCMethodNoArguments)156 TEST_F(CompletionStringTest, ObjectiveCMethodNoArguments) {
157   Builder.AddResultTypeChunk("void");
158   Builder.AddTypedTextChunk("methodName");
159 
160   auto *CCS = Builder.TakeString();
161   computeSignature(*CCS);
162   EXPECT_EQ(Signature, "");
163   EXPECT_EQ(Snippet, "");
164 }
165 
TEST_F(CompletionStringTest,ObjectiveCMethodOneArgument)166 TEST_F(CompletionStringTest, ObjectiveCMethodOneArgument) {
167   Builder.AddResultTypeChunk("void");
168   Builder.AddTypedTextChunk("methodWithArg:");
169   Builder.AddPlaceholderChunk("(type)");
170 
171   auto *CCS = Builder.TakeString();
172   computeSignature(*CCS);
173   EXPECT_EQ(Signature, "(type)");
174   EXPECT_EQ(Snippet, "${1:(type)}");
175 }
176 
TEST_F(CompletionStringTest,ObjectiveCMethodTwoArgumentsFromBeginning)177 TEST_F(CompletionStringTest, ObjectiveCMethodTwoArgumentsFromBeginning) {
178   Builder.AddResultTypeChunk("int");
179   Builder.AddTypedTextChunk("withFoo:");
180   Builder.AddPlaceholderChunk("(type)");
181   Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
182   Builder.AddTypedTextChunk("bar:");
183   Builder.AddPlaceholderChunk("(type2)");
184 
185   auto *CCS = Builder.TakeString();
186   computeSignature(*CCS);
187   EXPECT_EQ(Signature, "(type) bar:(type2)");
188   EXPECT_EQ(Snippet, "${1:(type)} bar:${2:(type2)}");
189 }
190 
TEST_F(CompletionStringTest,ObjectiveCMethodTwoArgumentsFromMiddle)191 TEST_F(CompletionStringTest, ObjectiveCMethodTwoArgumentsFromMiddle) {
192   Builder.AddResultTypeChunk("int");
193   Builder.AddInformativeChunk("withFoo:");
194   Builder.AddTypedTextChunk("bar:");
195   Builder.AddPlaceholderChunk("(type2)");
196 
197   auto *CCS = Builder.TakeString();
198   computeSignature(*CCS);
199   EXPECT_EQ(Signature, "(type2)");
200   EXPECT_EQ(Snippet, "${1:(type2)}");
201 }
202 
203 } // namespace
204 } // namespace clangd
205 } // namespace clang
206