1 | //===-- HighlighterTest.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 "gtest/gtest.h" |
10 | |
11 | #include "lldb/Core/Highlighter.h" |
12 | #include "lldb/Host/FileSystem.h" |
13 | |
14 | #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" |
15 | #include "Plugins/Language/ObjC/ObjCLanguage.h" |
16 | #include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h" |
17 | #include "TestingSupport/SubsystemRAII.h" |
18 | #include <optional> |
19 | |
20 | using namespace lldb_private; |
21 | |
22 | namespace { |
23 | class HighlighterTest : public testing::Test { |
24 | SubsystemRAII<FileSystem, CPlusPlusLanguage, ObjCLanguage, |
25 | ObjCPlusPlusLanguage> |
26 | subsystems; |
27 | }; |
28 | } // namespace |
29 | |
30 | static std::string getName(lldb::LanguageType type) { |
31 | HighlighterManager m; |
32 | return m.getHighlighterFor(language_type: type, path: "" ).GetName().str(); |
33 | } |
34 | |
35 | static std::string getName(llvm::StringRef path) { |
36 | HighlighterManager m; |
37 | return m.getHighlighterFor(language_type: lldb::eLanguageTypeUnknown, path).GetName().str(); |
38 | } |
39 | |
40 | TEST_F(HighlighterTest, HighlighterSelectionType) { |
41 | EXPECT_EQ(getName(lldb::eLanguageTypeC_plus_plus), "clang" ); |
42 | EXPECT_EQ(getName(lldb::eLanguageTypeC_plus_plus_03), "clang" ); |
43 | EXPECT_EQ(getName(lldb::eLanguageTypeC_plus_plus_11), "clang" ); |
44 | EXPECT_EQ(getName(lldb::eLanguageTypeC_plus_plus_14), "clang" ); |
45 | EXPECT_EQ(getName(lldb::eLanguageTypeObjC), "clang" ); |
46 | EXPECT_EQ(getName(lldb::eLanguageTypeObjC_plus_plus), "clang" ); |
47 | |
48 | EXPECT_EQ(getName(lldb::eLanguageTypeUnknown), "none" ); |
49 | EXPECT_EQ(getName(lldb::eLanguageTypeJulia), "none" ); |
50 | EXPECT_EQ(getName(lldb::eLanguageTypeHaskell), "none" ); |
51 | } |
52 | |
53 | TEST_F(HighlighterTest, HighlighterSelectionPath) { |
54 | EXPECT_EQ(getName("myfile.cc" ), "clang" ); |
55 | EXPECT_EQ(getName("moo.cpp" ), "clang" ); |
56 | EXPECT_EQ(getName("mar.cxx" ), "clang" ); |
57 | EXPECT_EQ(getName("foo.C" ), "clang" ); |
58 | EXPECT_EQ(getName("bar.CC" ), "clang" ); |
59 | EXPECT_EQ(getName("a/dir.CC" ), "clang" ); |
60 | EXPECT_EQ(getName("/a/dir.hpp" ), "clang" ); |
61 | EXPECT_EQ(getName("header.h" ), "clang" ); |
62 | EXPECT_EQ(getName("foo.m" ), "clang" ); |
63 | EXPECT_EQ(getName("foo.mm" ), "clang" ); |
64 | |
65 | EXPECT_EQ(getName("" ), "none" ); |
66 | EXPECT_EQ(getName("/dev/null" ), "none" ); |
67 | EXPECT_EQ(getName("Factory.java" ), "none" ); |
68 | EXPECT_EQ(getName("poll.py" ), "none" ); |
69 | EXPECT_EQ(getName("reducer.hs" ), "none" ); |
70 | } |
71 | |
72 | TEST_F(HighlighterTest, FallbackHighlighter) { |
73 | HighlighterManager mgr; |
74 | const Highlighter &h = |
75 | mgr.getHighlighterFor(language_type: lldb::eLanguageTypePascal83, path: "foo.pas" ); |
76 | |
77 | HighlightStyle style; |
78 | style.identifier.Set(prefix: "[" , suffix: "]" ); |
79 | style.semicolons.Set(prefix: "<" , suffix: ">" ); |
80 | |
81 | const char *code = "program Hello;" ; |
82 | std::string output = h.Highlight(options: style, line: code, cursor_pos: std::optional<size_t>()); |
83 | |
84 | EXPECT_STREQ(output.c_str(), code); |
85 | } |
86 | |
87 | static std::string |
88 | highlightDefault(llvm::StringRef code, HighlightStyle style, |
89 | std::optional<size_t> cursor = std::optional<size_t>()) { |
90 | HighlighterManager mgr; |
91 | return mgr.getDefaultHighlighter().Highlight(options: style, line: code, cursor_pos: cursor); |
92 | } |
93 | |
94 | TEST_F(HighlighterTest, DefaultHighlighter) { |
95 | const char *code = "int my_main() { return 22; } \n" ; |
96 | |
97 | HighlightStyle style; |
98 | EXPECT_EQ(code, highlightDefault(code, style)); |
99 | } |
100 | |
101 | TEST_F(HighlighterTest, DefaultHighlighterWithCursor) { |
102 | HighlightStyle style; |
103 | style.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
104 | EXPECT_EQ("<c>a</c> bc" , highlightDefault("a bc" , style, 0)); |
105 | EXPECT_EQ("a<c> </c>bc" , highlightDefault("a bc" , style, 1)); |
106 | EXPECT_EQ("a <c>b</c>c" , highlightDefault("a bc" , style, 2)); |
107 | EXPECT_EQ("a b<c>c</c>" , highlightDefault("a bc" , style, 3)); |
108 | } |
109 | |
110 | TEST_F(HighlighterTest, DefaultHighlighterWithCursorOutOfBounds) { |
111 | HighlightStyle style; |
112 | style.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
113 | EXPECT_EQ("a bc" , highlightDefault("a bc" , style, 4)); |
114 | } |
115 | // Tests highlighting with the Clang highlighter. |
116 | |
117 | static std::string |
118 | highlightC(llvm::StringRef code, HighlightStyle style, |
119 | std::optional<size_t> cursor = std::optional<size_t>()) { |
120 | HighlighterManager mgr; |
121 | const Highlighter &h = mgr.getHighlighterFor(language_type: lldb::eLanguageTypeC, path: "main.c" ); |
122 | return h.Highlight(options: style, line: code, cursor_pos: cursor); |
123 | } |
124 | |
125 | TEST_F(HighlighterTest, ClangEmptyInput) { |
126 | HighlightStyle s; |
127 | EXPECT_EQ("" , highlightC("" , s)); |
128 | } |
129 | |
130 | TEST_F(HighlighterTest, ClangScalarLiterals) { |
131 | HighlightStyle s; |
132 | s.scalar_literal.Set(prefix: "<scalar>" , suffix: "</scalar>" ); |
133 | |
134 | EXPECT_EQ(" int i = <scalar>22</scalar>;" , highlightC(" int i = 22;" , s)); |
135 | } |
136 | |
137 | TEST_F(HighlighterTest, ClangStringLiterals) { |
138 | HighlightStyle s; |
139 | s.string_literal.Set(prefix: "<str>" , suffix: "</str>" ); |
140 | |
141 | EXPECT_EQ("const char *f = 22 + <str>\"foo\"</str>;" , |
142 | highlightC("const char *f = 22 + \"foo\";" , s)); |
143 | } |
144 | |
145 | TEST_F(HighlighterTest, ClangUnterminatedString) { |
146 | HighlightStyle s; |
147 | s.string_literal.Set(prefix: "<str>" , suffix: "</str>" ); |
148 | |
149 | EXPECT_EQ(" f = \"" , highlightC(" f = \"" , s)); |
150 | } |
151 | |
152 | TEST_F(HighlighterTest, Keywords) { |
153 | HighlightStyle s; |
154 | s.keyword.Set(prefix: "<k>" , suffix: "</k>" ); |
155 | |
156 | EXPECT_EQ(" <k>return</k> 1; " , highlightC(" return 1; " , s)); |
157 | } |
158 | |
159 | TEST_F(HighlighterTest, Colons) { |
160 | HighlightStyle s; |
161 | s.colon.Set(prefix: "<c>" , suffix: "</c>" ); |
162 | |
163 | EXPECT_EQ("foo<c>::</c>bar<c>:</c>" , highlightC("foo::bar:" , s)); |
164 | } |
165 | |
166 | TEST_F(HighlighterTest, ClangBraces) { |
167 | HighlightStyle s; |
168 | s.braces.Set(prefix: "<b>" , suffix: "</b>" ); |
169 | |
170 | EXPECT_EQ("a<b>{</b><b>}</b>" , highlightC("a{}" , s)); |
171 | } |
172 | |
173 | TEST_F(HighlighterTest, ClangSquareBrackets) { |
174 | HighlightStyle s; |
175 | s.square_brackets.Set(prefix: "<sb>" , suffix: "</sb>" ); |
176 | |
177 | EXPECT_EQ("a<sb>[</sb><sb>]</sb>" , highlightC("a[]" , s)); |
178 | } |
179 | |
180 | TEST_F(HighlighterTest, ClangCommas) { |
181 | HighlightStyle s; |
182 | s.comma.Set(prefix: "<comma>" , suffix: "</comma>" ); |
183 | |
184 | EXPECT_EQ(" bool f = foo()<comma>,</comma> 1;" , |
185 | highlightC(" bool f = foo(), 1;" , s)); |
186 | } |
187 | |
188 | TEST_F(HighlighterTest, ClangPPDirectives) { |
189 | HighlightStyle s; |
190 | s.pp_directive.Set(prefix: "<pp>" , suffix: "</pp>" ); |
191 | |
192 | EXPECT_EQ("<pp>#</pp><pp>include</pp><pp> </pp><pp>\"foo\"</pp><pp> </pp>//c" , |
193 | highlightC("#include \"foo\" //c" , s)); |
194 | } |
195 | |
196 | TEST_F(HighlighterTest, ClangPreserveNewLine) { |
197 | HighlightStyle s; |
198 | s.comment.Set(prefix: "<cc>" , suffix: "</cc>" ); |
199 | |
200 | EXPECT_EQ("<cc>//</cc>\n" , highlightC("//\n" , s)); |
201 | } |
202 | |
203 | TEST_F(HighlighterTest, ClangTrailingBackslashBeforeNewline) { |
204 | HighlightStyle s; |
205 | |
206 | EXPECT_EQ("\\\n" , highlightC("\\\n" , s)); |
207 | EXPECT_EQ("\\\r\n" , highlightC("\\\r\n" , s)); |
208 | |
209 | EXPECT_EQ("#define a \\\n" , highlightC("#define a \\\n" , s)); |
210 | EXPECT_EQ("#define a \\\r\n" , highlightC("#define a \\\r\n" , s)); |
211 | EXPECT_EQ("#define a \\\r" , highlightC("#define a \\\r" , s)); |
212 | } |
213 | |
214 | TEST_F(HighlighterTest, ClangTrailingBackslashWithWhitespace) { |
215 | HighlightStyle s; |
216 | |
217 | EXPECT_EQ("\\ \n" , highlightC("\\ \n" , s)); |
218 | EXPECT_EQ("\\ \t\n" , highlightC("\\ \t\n" , s)); |
219 | EXPECT_EQ("\\ \n" , highlightC("\\ \n" , s)); |
220 | EXPECT_EQ("\\\t\n" , highlightC("\\\t\n" , s)); |
221 | |
222 | EXPECT_EQ("#define a \\ \n" , highlightC("#define a \\ \n" , s)); |
223 | EXPECT_EQ("#define a \\ \t\n" , highlightC("#define a \\ \t\n" , s)); |
224 | EXPECT_EQ("#define a \\ \n" , highlightC("#define a \\ \n" , s)); |
225 | EXPECT_EQ("#define a \\\t\n" , highlightC("#define a \\\t\n" , s)); |
226 | } |
227 | |
228 | TEST_F(HighlighterTest, ClangTrailingBackslashMissingNewLine) { |
229 | HighlightStyle s; |
230 | EXPECT_EQ("\\" , highlightC("\\" , s)); |
231 | EXPECT_EQ("#define a\\" , highlightC("#define a\\" , s)); |
232 | } |
233 | |
234 | TEST_F(HighlighterTest, ClangComments) { |
235 | HighlightStyle s; |
236 | s.comment.Set(prefix: "<cc>" , suffix: "</cc>" ); |
237 | |
238 | EXPECT_EQ(" <cc>/*com */</cc> <cc>// com /*n*/</cc>" , |
239 | highlightC(" /*com */ // com /*n*/" , s)); |
240 | } |
241 | |
242 | TEST_F(HighlighterTest, ClangOperators) { |
243 | HighlightStyle s; |
244 | s.operators.Set(prefix: "[" , suffix: "]" ); |
245 | |
246 | EXPECT_EQ(" 1[+]2[/]a[*]f[&]x[|][~]l" , highlightC(" 1+2/a*f&x|~l" , s)); |
247 | } |
248 | |
249 | TEST_F(HighlighterTest, ClangIdentifiers) { |
250 | HighlightStyle s; |
251 | s.identifier.Set(prefix: "<id>" , suffix: "</id>" ); |
252 | |
253 | EXPECT_EQ(" <id>foo</id> <id>c</id> = <id>bar</id>(); return 1;" , |
254 | highlightC(" foo c = bar(); return 1;" , s)); |
255 | } |
256 | |
257 | TEST_F(HighlighterTest, ClangCursorPos) { |
258 | HighlightStyle s; |
259 | s.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
260 | |
261 | EXPECT_EQ("<c> </c>foo c = bar(); return 1;" , |
262 | highlightC(" foo c = bar(); return 1;" , s, 0)); |
263 | EXPECT_EQ(" <c>foo</c> c = bar(); return 1;" , |
264 | highlightC(" foo c = bar(); return 1;" , s, 1)); |
265 | EXPECT_EQ(" <c>foo</c> c = bar(); return 1;" , |
266 | highlightC(" foo c = bar(); return 1;" , s, 2)); |
267 | EXPECT_EQ(" <c>foo</c> c = bar(); return 1;" , |
268 | highlightC(" foo c = bar(); return 1;" , s, 3)); |
269 | EXPECT_EQ(" foo<c> </c>c = bar(); return 1;" , |
270 | highlightC(" foo c = bar(); return 1;" , s, 4)); |
271 | EXPECT_EQ(" foo <c>c</c> = bar(); return 1;" , |
272 | highlightC(" foo c = bar(); return 1;" , s, 5)); |
273 | } |
274 | |
275 | TEST_F(HighlighterTest, ClangCursorPosEndOfLine) { |
276 | HighlightStyle s; |
277 | s.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
278 | |
279 | EXPECT_EQ("f" , highlightC("f" , s, 1)); |
280 | } |
281 | |
282 | TEST_F(HighlighterTest, ClangCursorOutOfBounds) { |
283 | HighlightStyle s; |
284 | s.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
285 | EXPECT_EQ("f" , highlightC("f" , s, 2)); |
286 | EXPECT_EQ("f" , highlightC("f" , s, 3)); |
287 | EXPECT_EQ("f" , highlightC("f" , s, 4)); |
288 | } |
289 | |
290 | TEST_F(HighlighterTest, ClangCursorPosBeforeOtherToken) { |
291 | HighlightStyle s; |
292 | s.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
293 | s.identifier.Set(prefix: "<id>" , suffix: "</id>" ); |
294 | |
295 | EXPECT_EQ("<c> </c><id>foo</id> <id>c</id> = <id>bar</id>(); return 1;" , |
296 | highlightC(" foo c = bar(); return 1;" , s, 0)); |
297 | } |
298 | |
299 | TEST_F(HighlighterTest, ClangCursorPosAfterOtherToken) { |
300 | HighlightStyle s; |
301 | s.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
302 | s.identifier.Set(prefix: "<id>" , suffix: "</id>" ); |
303 | |
304 | EXPECT_EQ(" <id>foo</id><c> </c><id>c</id> = <id>bar</id>(); return 1;" , |
305 | highlightC(" foo c = bar(); return 1;" , s, 4)); |
306 | } |
307 | |
308 | TEST_F(HighlighterTest, ClangCursorPosInOtherToken) { |
309 | HighlightStyle s; |
310 | s.selected.Set(prefix: "<c>" , suffix: "</c>" ); |
311 | s.identifier.Set(prefix: "<id>" , suffix: "</id>" ); |
312 | |
313 | EXPECT_EQ(" <id><c>foo</c></id> <id>c</id> = <id>bar</id>(); return 1;" , |
314 | highlightC(" foo c = bar(); return 1;" , s, 1)); |
315 | EXPECT_EQ(" <id><c>foo</c></id> <id>c</id> = <id>bar</id>(); return 1;" , |
316 | highlightC(" foo c = bar(); return 1;" , s, 2)); |
317 | EXPECT_EQ(" <id><c>foo</c></id> <id>c</id> = <id>bar</id>(); return 1;" , |
318 | highlightC(" foo c = bar(); return 1;" , s, 3)); |
319 | } |
320 | |