1//===-- ConfigCompileTests.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 "Config.h"
10#include "ConfigFragment.h"
11#include "ConfigTesting.h"
12#include "Diagnostics.h"
13#include "Feature.h"
14#include "TestFS.h"
15#include "clang/Basic/DiagnosticSema.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/SourceMgr.h"
19#include "gmock/gmock.h"
20#include "gtest/gtest.h"
21#include <optional>
22#include <string>
23
24namespace clang {
25namespace clangd {
26namespace config {
27namespace {
28using ::testing::AllOf;
29using ::testing::Contains;
30using ::testing::ElementsAre;
31using ::testing::IsEmpty;
32using ::testing::SizeIs;
33using ::testing::StartsWith;
34using ::testing::UnorderedElementsAre;
35
36class ConfigCompileTests : public ::testing::Test {
37protected:
38 CapturedDiags Diags;
39 Config Conf;
40 Fragment Frag;
41 Params Parm;
42
43 bool compileAndApply() {
44 Conf = Config();
45 Diags.Diagnostics.clear();
46 auto Compiled = std::move(Frag).compile(Diags.callback());
47 return Compiled(Parm, Conf);
48 }
49};
50
51TEST_F(ConfigCompileTests, Condition) {
52 // No condition.
53 Frag = {};
54 Frag.CompileFlags.Add.emplace_back(args: "X");
55 EXPECT_TRUE(compileAndApply()) << "Empty config";
56 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
57 EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(1));
58
59 // Regex with no file.
60 Frag = {};
61 Frag.If.PathMatch.emplace_back(args: "fo*");
62 EXPECT_FALSE(compileAndApply());
63 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
64 EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(0));
65
66 // Following tests have a file path set.
67 Parm.Path = "bar";
68
69 // Non-matching regex.
70 Frag = {};
71 Frag.If.PathMatch.emplace_back(args: "fo*");
72 EXPECT_FALSE(compileAndApply());
73 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
74
75 // Matching regex.
76 Frag = {};
77 Frag.If.PathMatch.emplace_back(args: "fo*");
78 Frag.If.PathMatch.emplace_back(args: "ba*r");
79 EXPECT_TRUE(compileAndApply());
80 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
81
82 // Excluded regex.
83 Frag = {};
84 Frag.If.PathMatch.emplace_back(args: "b.*");
85 Frag.If.PathExclude.emplace_back(args: ".*r");
86 EXPECT_FALSE(compileAndApply()) << "Included but also excluded";
87 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
88
89 // Invalid regex.
90 Frag = {};
91 Frag.If.PathMatch.emplace_back(args: "**]@theu");
92 EXPECT_TRUE(compileAndApply());
93 EXPECT_THAT(Diags.Diagnostics, SizeIs(1));
94 EXPECT_THAT(Diags.Diagnostics.front().Message, StartsWith("Invalid regex"));
95
96 // Valid regex and unknown key.
97 Frag = {};
98 Frag.If.HasUnrecognizedCondition = true;
99 Frag.If.PathMatch.emplace_back(args: "ba*r");
100 EXPECT_FALSE(compileAndApply());
101 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
102
103 // Only matches case-insensitively.
104 Frag = {};
105 Frag.If.PathMatch.emplace_back(args: "B.*R");
106 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
107#ifdef CLANGD_PATH_CASE_INSENSITIVE
108 EXPECT_TRUE(compileAndApply());
109#else
110 EXPECT_FALSE(compileAndApply());
111#endif
112
113 Frag = {};
114 Frag.If.PathExclude.emplace_back(args: "B.*R");
115 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
116#ifdef CLANGD_PATH_CASE_INSENSITIVE
117 EXPECT_FALSE(compileAndApply());
118#else
119 EXPECT_TRUE(compileAndApply());
120#endif
121}
122
123TEST_F(ConfigCompileTests, CompileCommands) {
124 Frag.CompileFlags.Compiler.emplace(args: "tpc.exe");
125 Frag.CompileFlags.Add.emplace_back(args: "-foo");
126 Frag.CompileFlags.Remove.emplace_back(args: "--include-directory=");
127 std::vector<std::string> Argv = {"clang", "-I", "bar/", "--", "a.cc"};
128 EXPECT_TRUE(compileAndApply());
129 EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(3));
130 for (auto &Edit : Conf.CompileFlags.Edits)
131 Edit(Argv);
132 EXPECT_THAT(Argv, ElementsAre("tpc.exe", "-foo", "--", "a.cc"));
133}
134
135TEST_F(ConfigCompileTests, CompilationDatabase) {
136 Frag.CompileFlags.CompilationDatabase.emplace(args: "None");
137 EXPECT_TRUE(compileAndApply());
138 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
139 Config::CDBSearchSpec::NoCDBSearch);
140
141 Frag.CompileFlags.CompilationDatabase.emplace(args: "Ancestors");
142 EXPECT_TRUE(compileAndApply());
143 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
144 Config::CDBSearchSpec::Ancestors);
145
146 // Relative path not allowed without directory set.
147 Frag.CompileFlags.CompilationDatabase.emplace(args: "Something");
148 EXPECT_TRUE(compileAndApply());
149 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
150 Config::CDBSearchSpec::Ancestors)
151 << "default value";
152 EXPECT_THAT(Diags.Diagnostics,
153 ElementsAre(diagMessage(
154 "CompilationDatabase must be an absolute path, because this "
155 "fragment is not associated with any directory.")));
156
157 // Relative path allowed if directory is set.
158 Frag.Source.Directory = testRoot();
159 EXPECT_TRUE(compileAndApply());
160 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
161 Config::CDBSearchSpec::FixedDir);
162 EXPECT_EQ(Conf.CompileFlags.CDBSearch.FixedCDBPath, testPath("Something"));
163 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
164
165 // Absolute path allowed.
166 Frag.Source.Directory.clear();
167 Frag.CompileFlags.CompilationDatabase.emplace(args: testPath(File: "Something2"));
168 EXPECT_TRUE(compileAndApply());
169 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
170 Config::CDBSearchSpec::FixedDir);
171 EXPECT_EQ(Conf.CompileFlags.CDBSearch.FixedCDBPath, testPath("Something2"));
172 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
173}
174
175TEST_F(ConfigCompileTests, Index) {
176 Frag.Index.Background.emplace(args: "Skip");
177 EXPECT_TRUE(compileAndApply());
178 EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
179
180 Frag = {};
181 Frag.Index.Background.emplace(args: "Foo");
182 EXPECT_TRUE(compileAndApply());
183 EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Build)
184 << "by default";
185 EXPECT_THAT(
186 Diags.Diagnostics,
187 ElementsAre(diagMessage(
188 "Invalid Background value 'Foo'. Valid values are Build, Skip.")));
189}
190
191TEST_F(ConfigCompileTests, PathSpecMatch) {
192 auto BarPath = llvm::sys::path::convert_to_slash(path: testPath(File: "foo/bar.h"));
193 Parm.Path = BarPath;
194
195 struct {
196 std::string Directory;
197 std::string PathSpec;
198 bool ShouldMatch;
199 } Cases[] = {
200 {
201 // Absolute path matches.
202 .Directory: "",
203 .PathSpec: llvm::sys::path::convert_to_slash(path: testPath(File: "foo/bar.h")),
204 .ShouldMatch: true,
205 },
206 {
207 // Absolute path fails.
208 .Directory: "",
209 .PathSpec: llvm::sys::path::convert_to_slash(path: testPath(File: "bar/bar.h")),
210 .ShouldMatch: false,
211 },
212 {
213 // Relative should fail to match as /foo/bar.h doesn't reside under
214 // /baz/.
215 .Directory: testPath(File: "baz"),
216 .PathSpec: "bar\\.h",
217 .ShouldMatch: false,
218 },
219 {
220 // Relative should pass with /foo as directory.
221 .Directory: testPath(File: "foo"),
222 .PathSpec: "bar\\.h",
223 .ShouldMatch: true,
224 },
225 };
226
227 // PathMatch
228 for (const auto &Case : Cases) {
229 Frag = {};
230 Frag.If.PathMatch.emplace_back(args: Case.PathSpec);
231 Frag.Source.Directory = Case.Directory;
232 EXPECT_EQ(compileAndApply(), Case.ShouldMatch);
233 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
234 }
235
236 // PathEclude
237 for (const auto &Case : Cases) {
238 SCOPED_TRACE(Case.Directory);
239 SCOPED_TRACE(Case.PathSpec);
240 Frag = {};
241 Frag.If.PathExclude.emplace_back(args: Case.PathSpec);
242 Frag.Source.Directory = Case.Directory;
243 EXPECT_NE(compileAndApply(), Case.ShouldMatch);
244 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
245 }
246}
247
248TEST_F(ConfigCompileTests, DiagnosticsIncludeCleaner) {
249 // Defaults to Strict.
250 EXPECT_TRUE(compileAndApply());
251 EXPECT_EQ(Conf.Diagnostics.UnusedIncludes, Config::IncludesPolicy::Strict);
252
253 Frag = {};
254 Frag.Diagnostics.UnusedIncludes.emplace(args: "None");
255 EXPECT_TRUE(compileAndApply());
256 EXPECT_EQ(Conf.Diagnostics.UnusedIncludes, Config::IncludesPolicy::None);
257
258 Frag = {};
259 Frag.Diagnostics.UnusedIncludes.emplace(args: "Strict");
260 EXPECT_TRUE(compileAndApply());
261 EXPECT_EQ(Conf.Diagnostics.UnusedIncludes, Config::IncludesPolicy::Strict);
262
263 Frag = {};
264 EXPECT_TRUE(Conf.Diagnostics.Includes.IgnoreHeader.empty())
265 << Conf.Diagnostics.Includes.IgnoreHeader.size();
266 Frag.Diagnostics.Includes.IgnoreHeader.push_back(
267 x: Located<std::string>("foo.h"));
268 Frag.Diagnostics.Includes.IgnoreHeader.push_back(
269 x: Located<std::string>(".*inc"));
270 EXPECT_TRUE(compileAndApply());
271 auto HeaderFilter = [this](llvm::StringRef Path) {
272 for (auto &Filter : Conf.Diagnostics.Includes.IgnoreHeader) {
273 if (Filter(Path))
274 return true;
275 }
276 return false;
277 };
278 EXPECT_TRUE(HeaderFilter("foo.h"));
279 EXPECT_FALSE(HeaderFilter("bar.h"));
280}
281
282TEST_F(ConfigCompileTests, DiagnosticSuppression) {
283 Frag.Diagnostics.Suppress.emplace_back(args: "bugprone-use-after-move");
284 Frag.Diagnostics.Suppress.emplace_back(args: "unreachable-code");
285 Frag.Diagnostics.Suppress.emplace_back(args: "-Wunused-variable");
286 Frag.Diagnostics.Suppress.emplace_back(args: "typecheck_bool_condition");
287 Frag.Diagnostics.Suppress.emplace_back(args: "err_unexpected_friend");
288 Frag.Diagnostics.Suppress.emplace_back(args: "warn_alloca");
289 EXPECT_TRUE(compileAndApply());
290 EXPECT_THAT(Conf.Diagnostics.Suppress.keys(),
291 UnorderedElementsAre("bugprone-use-after-move",
292 "unreachable-code", "unused-variable",
293 "typecheck_bool_condition",
294 "unexpected_friend", "warn_alloca"));
295 EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
296 diag::warn_unreachable, Conf.Diagnostics.Suppress, LangOptions()));
297 // Subcategory not respected/suppressed.
298 EXPECT_FALSE(isBuiltinDiagnosticSuppressed(
299 diag::warn_unreachable_break, Conf.Diagnostics.Suppress, LangOptions()));
300 EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
301 diag::warn_unused_variable, Conf.Diagnostics.Suppress, LangOptions()));
302 EXPECT_TRUE(isBuiltinDiagnosticSuppressed(diag::err_typecheck_bool_condition,
303 Conf.Diagnostics.Suppress,
304 LangOptions()));
305 EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
306 diag::err_unexpected_friend, Conf.Diagnostics.Suppress, LangOptions()));
307 EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
308 diag::warn_alloca, Conf.Diagnostics.Suppress, LangOptions()));
309
310 Frag.Diagnostics.Suppress.emplace_back(args: "*");
311 EXPECT_TRUE(compileAndApply());
312 EXPECT_TRUE(Conf.Diagnostics.SuppressAll);
313 EXPECT_THAT(Conf.Diagnostics.Suppress, IsEmpty());
314}
315
316TEST_F(ConfigCompileTests, Tidy) {
317 auto &Tidy = Frag.Diagnostics.ClangTidy;
318 Tidy.Add.emplace_back(args: "bugprone-use-after-move");
319 Tidy.Add.emplace_back(args: "llvm-*");
320 Tidy.Remove.emplace_back(args: "llvm-include-order");
321 Tidy.Remove.emplace_back(args: "readability-*");
322 Tidy.CheckOptions.emplace_back(
323 args: std::make_pair(x: std::string("StrictMode"), y: std::string("true")));
324 Tidy.CheckOptions.emplace_back(args: std::make_pair(
325 x: std::string("example-check.ExampleOption"), y: std::string("0")));
326 EXPECT_TRUE(compileAndApply());
327 EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.size(), 2U);
328 EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup("StrictMode"),
329 "true");
330 EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup(
331 "example-check.ExampleOption"),
332 "0");
333#if CLANGD_TIDY_CHECKS
334 EXPECT_EQ(
335 Conf.Diagnostics.ClangTidy.Checks,
336 "bugprone-use-after-move,llvm-*,-llvm-include-order,-readability-*");
337 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
338#else // !CLANGD_TIDY_CHECKS
339 EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "llvm-*,-readability-*");
340 EXPECT_THAT(
341 Diags.Diagnostics,
342 ElementsAre(
343 diagMessage(
344 "clang-tidy check 'bugprone-use-after-move' was not found"),
345 diagMessage("clang-tidy check 'llvm-include-order' was not found")));
346#endif
347}
348
349TEST_F(ConfigCompileTests, TidyBadChecks) {
350 auto &Tidy = Frag.Diagnostics.ClangTidy;
351 Tidy.Add.emplace_back(args: "unknown-check");
352 Tidy.Remove.emplace_back(args: "*");
353 Tidy.Remove.emplace_back(args: "llvm-includeorder");
354 EXPECT_TRUE(compileAndApply());
355 // Ensure bad checks are stripped from the glob.
356 EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "-*");
357 EXPECT_THAT(
358 Diags.Diagnostics,
359 ElementsAre(
360 AllOf(diagMessage("clang-tidy check 'unknown-check' was not found"),
361 diagKind(llvm::SourceMgr::DK_Warning)),
362 AllOf(
363 diagMessage("clang-tidy check 'llvm-includeorder' was not found"),
364 diagKind(llvm::SourceMgr::DK_Warning))));
365}
366
367TEST_F(ConfigCompileTests, ExternalServerNeedsTrusted) {
368 Fragment::IndexBlock::ExternalBlock External;
369 External.Server.emplace(args: "xxx");
370 Frag.Index.External = std::move(External);
371 compileAndApply();
372 EXPECT_THAT(
373 Diags.Diagnostics,
374 ElementsAre(diagMessage(
375 "Remote index may not be specified by untrusted configuration. "
376 "Copy this into user config to use it.")));
377 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
378}
379
380TEST_F(ConfigCompileTests, ExternalBlockWarnOnMultipleSource) {
381 Frag.Source.Trusted = true;
382 Fragment::IndexBlock::ExternalBlock External;
383 External.File.emplace(args: "");
384 External.Server.emplace(args: "");
385 Frag.Index.External = std::move(External);
386 compileAndApply();
387#ifdef CLANGD_ENABLE_REMOTE
388 EXPECT_THAT(
389 Diags.Diagnostics,
390 Contains(
391 AllOf(diagMessage("Exactly one of File, Server or None must be set."),
392 diagKind(llvm::SourceMgr::DK_Error))));
393#else
394 ASSERT_TRUE(Conf.Index.External.hasValue());
395 EXPECT_EQ(Conf.Index.External->Kind, Config::ExternalIndexSpec::File);
396#endif
397}
398
399TEST_F(ConfigCompileTests, ExternalBlockDisableWithNone) {
400 compileAndApply();
401 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
402
403 Fragment::IndexBlock::ExternalBlock External;
404 External.IsNone = true;
405 Frag.Index.External = std::move(External);
406 compileAndApply();
407 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
408}
409
410TEST_F(ConfigCompileTests, ExternalBlockErrOnNoSource) {
411 Frag.Index.External.emplace(args: Fragment::IndexBlock::ExternalBlock{});
412 compileAndApply();
413 EXPECT_THAT(
414 Diags.Diagnostics,
415 Contains(
416 AllOf(diagMessage("Exactly one of File, Server or None must be set."),
417 diagKind(llvm::SourceMgr::DK_Error))));
418}
419
420TEST_F(ConfigCompileTests, ExternalBlockDisablesBackgroundIndex) {
421 auto BazPath = testPath(File: "foo/bar/baz.h", llvm::sys::path::Style::posix);
422 Parm.Path = BazPath;
423 Frag.Index.Background.emplace(args: "Build");
424 Fragment::IndexBlock::ExternalBlock External;
425 External.File.emplace(args: testPath(File: "foo"));
426 External.MountPoint.emplace(
427 args: testPath(File: "foo/bar", llvm::sys::path::Style::posix));
428 Frag.Index.External = std::move(External);
429 compileAndApply();
430 EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
431}
432
433TEST_F(ConfigCompileTests, ExternalBlockMountPoint) {
434 auto GetFrag = [](llvm::StringRef Directory,
435 std::optional<const char *> MountPoint) {
436 Fragment Frag;
437 Frag.Source.Directory = Directory.str();
438 Fragment::IndexBlock::ExternalBlock External;
439 External.File.emplace(args: testPath(File: "foo"));
440 if (MountPoint)
441 External.MountPoint.emplace(args&: *MountPoint);
442 Frag.Index.External = std::move(External);
443 return Frag;
444 };
445
446 auto BarPath = testPath(File: "foo/bar.h", llvm::sys::path::Style::posix);
447 BarPath = llvm::sys::path::convert_to_slash(path: BarPath);
448 Parm.Path = BarPath;
449 // Non-absolute MountPoint without a directory raises an error.
450 Frag = GetFrag("", "foo");
451 compileAndApply();
452 ASSERT_THAT(
453 Diags.Diagnostics,
454 ElementsAre(
455 AllOf(diagMessage("MountPoint must be an absolute path, because this "
456 "fragment is not associated with any directory."),
457 diagKind(llvm::SourceMgr::DK_Error))));
458 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
459
460 auto FooPath = testPath(File: "foo/", llvm::sys::path::Style::posix);
461 FooPath = llvm::sys::path::convert_to_slash(path: FooPath);
462 // Ok when relative.
463 Frag = GetFrag(testRoot(), "foo/");
464 compileAndApply();
465 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
466 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
467 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
468
469 // None defaults to ".".
470 Frag = GetFrag(FooPath, std::nullopt);
471 compileAndApply();
472 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
473 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
474 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
475
476 // Without a file, external index is empty.
477 Parm.Path = "";
478 Frag = GetFrag("", FooPath.c_str());
479 compileAndApply();
480 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
481 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
482
483 // File outside MountPoint, no index.
484 auto BazPath = testPath(File: "bar/baz.h", llvm::sys::path::Style::posix);
485 BazPath = llvm::sys::path::convert_to_slash(path: BazPath);
486 Parm.Path = BazPath;
487 Frag = GetFrag("", FooPath.c_str());
488 compileAndApply();
489 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
490 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
491
492 // File under MountPoint, index should be set.
493 BazPath = testPath(File: "foo/baz.h", llvm::sys::path::Style::posix);
494 BazPath = llvm::sys::path::convert_to_slash(path: BazPath);
495 Parm.Path = BazPath;
496 Frag = GetFrag("", FooPath.c_str());
497 compileAndApply();
498 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
499 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
500 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
501
502 // Only matches case-insensitively.
503 BazPath = testPath(File: "fOo/baz.h", llvm::sys::path::Style::posix);
504 BazPath = llvm::sys::path::convert_to_slash(path: BazPath);
505 Parm.Path = BazPath;
506
507 FooPath = testPath(File: "FOO/", llvm::sys::path::Style::posix);
508 FooPath = llvm::sys::path::convert_to_slash(path: FooPath);
509 Frag = GetFrag("", FooPath.c_str());
510 compileAndApply();
511 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
512#ifdef CLANGD_PATH_CASE_INSENSITIVE
513 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
514 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
515#else
516 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
517#endif
518}
519
520TEST_F(ConfigCompileTests, AllScopes) {
521 // Defaults to true.
522 EXPECT_TRUE(compileAndApply());
523 EXPECT_TRUE(Conf.Completion.AllScopes);
524
525 Frag = {};
526 Frag.Completion.AllScopes = false;
527 EXPECT_TRUE(compileAndApply());
528 EXPECT_FALSE(Conf.Completion.AllScopes);
529
530 Frag = {};
531 Frag.Completion.AllScopes = true;
532 EXPECT_TRUE(compileAndApply());
533 EXPECT_TRUE(Conf.Completion.AllScopes);
534}
535
536TEST_F(ConfigCompileTests, Style) {
537 Frag = {};
538 Frag.Style.FullyQualifiedNamespaces.push_back(x: std::string("foo"));
539 Frag.Style.FullyQualifiedNamespaces.push_back(x: std::string("bar"));
540 EXPECT_TRUE(compileAndApply());
541 EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar"));
542}
543} // namespace
544} // namespace config
545} // namespace clangd
546} // namespace clang
547

source code of clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp