clang-tools 22.0.0git
ConfigCompileTests.cpp
Go to the documentation of this file.
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("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("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("fo*");
72 EXPECT_FALSE(compileAndApply());
73 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
74
75 // Matching regex.
76 Frag = {};
77 Frag.If.PathMatch.emplace_back("fo*");
78 Frag.If.PathMatch.emplace_back("ba*r");
79 EXPECT_TRUE(compileAndApply());
80 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
81
82 // Excluded regex.
83 Frag = {};
84 Frag.If.PathMatch.emplace_back("b.*");
85 Frag.If.PathExclude.emplace_back(".*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("**]@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("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("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("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("tpc.exe");
125 Frag.CompileFlags.Add.emplace_back("-foo");
126 Frag.CompileFlags.Remove.emplace_back("--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("None");
137 EXPECT_TRUE(compileAndApply());
138 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
140
141 Frag.CompileFlags.CompilationDatabase.emplace("Ancestors");
142 EXPECT_TRUE(compileAndApply());
143 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
145
146 // Relative path not allowed without directory set.
147 Frag.CompileFlags.CompilationDatabase.emplace("Something");
148 EXPECT_TRUE(compileAndApply());
149 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
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,
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(testPath("Something2"));
168 EXPECT_TRUE(compileAndApply());
169 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
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("Skip");
177 EXPECT_TRUE(compileAndApply());
178 EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
179
180 Frag = {};
181 Frag.Index.Background.emplace("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(testPath("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 "",
203 llvm::sys::path::convert_to_slash(testPath("foo/bar.h")),
204 true,
205 },
206 {
207 // Absolute path fails.
208 "",
209 llvm::sys::path::convert_to_slash(testPath("bar/bar.h")),
210 false,
211 },
212 {
213 // Relative should fail to match as /foo/bar.h doesn't reside under
214 // /baz/.
215 testPath("baz"),
216 "bar\\.h",
217 false,
218 },
219 {
220 // Relative should pass with /foo as directory.
221 testPath("foo"),
222 "bar\\.h",
223 true,
224 },
225 };
226
227 // PathMatch
228 for (const auto &Case : Cases) {
229 Frag = {};
230 Frag.If.PathMatch.emplace_back(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(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("None");
255 EXPECT_TRUE(compileAndApply());
256 EXPECT_EQ(Conf.Diagnostics.UnusedIncludes, Config::IncludesPolicy::None);
257
258 Frag = {};
259 Frag.Diagnostics.UnusedIncludes.emplace("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 Located<std::string>("foo.h"));
268 Frag.Diagnostics.Includes.IgnoreHeader.push_back(
269 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 Frag = {};
282 EXPECT_FALSE(Conf.Diagnostics.Includes.AnalyzeAngledIncludes);
283 Frag.Diagnostics.Includes.AnalyzeAngledIncludes = true;
284 EXPECT_TRUE(compileAndApply());
285 EXPECT_TRUE(Conf.Diagnostics.Includes.AnalyzeAngledIncludes);
286}
287
288TEST_F(ConfigCompileTests, DiagnosticSuppression) {
289 Frag.Diagnostics.Suppress.emplace_back("bugprone-use-after-move");
290 Frag.Diagnostics.Suppress.emplace_back("unreachable-code");
291 Frag.Diagnostics.Suppress.emplace_back("-Wunused-variable");
292 Frag.Diagnostics.Suppress.emplace_back("typecheck_bool_condition");
293 Frag.Diagnostics.Suppress.emplace_back("err_unexpected_friend");
294 Frag.Diagnostics.Suppress.emplace_back("warn_alloca");
295 EXPECT_TRUE(compileAndApply());
296 EXPECT_THAT(Conf.Diagnostics.Suppress.keys(),
297 UnorderedElementsAre("bugprone-use-after-move",
298 "unreachable-code", "unused-variable",
299 "typecheck_bool_condition",
300 "unexpected_friend", "warn_alloca"));
301 clang::DiagnosticOptions DiagOpts;
302 clang::DiagnosticsEngine DiagEngine(DiagnosticIDs::create(), DiagOpts,
303 new clang::IgnoringDiagConsumer);
304
305 using Diag = clang::Diagnostic;
306 {
307 auto D = DiagEngine.Report(diag::warn_unreachable);
308 EXPECT_TRUE(isDiagnosticSuppressed(
309 Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
310 }
311 // Subcategory not respected/suppressed.
312 {
313 auto D = DiagEngine.Report(diag::warn_unreachable_break);
314 EXPECT_FALSE(isDiagnosticSuppressed(
315 Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
316 }
317 {
318 auto D = DiagEngine.Report(diag::warn_unused_variable);
319 EXPECT_TRUE(isDiagnosticSuppressed(
320 Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
321 }
322 {
323 auto D = DiagEngine.Report(diag::err_typecheck_bool_condition);
324 EXPECT_TRUE(isDiagnosticSuppressed(
325 Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
326 }
327 {
328 auto D = DiagEngine.Report(diag::err_unexpected_friend);
329 EXPECT_TRUE(isDiagnosticSuppressed(
330 Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
331 }
332 {
333 auto D = DiagEngine.Report(diag::warn_alloca);
334 EXPECT_TRUE(isDiagnosticSuppressed(
335 Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
336 }
337
338 Frag.Diagnostics.Suppress.emplace_back("*");
339 EXPECT_TRUE(compileAndApply());
340 EXPECT_TRUE(Conf.Diagnostics.SuppressAll);
341 EXPECT_THAT(Conf.Diagnostics.Suppress, IsEmpty());
342}
343
344TEST_F(ConfigCompileTests, Tidy) {
345 auto &Tidy = Frag.Diagnostics.ClangTidy;
346 Tidy.Add.emplace_back("bugprone-use-after-move");
347 Tidy.Add.emplace_back("llvm-*");
348 Tidy.Remove.emplace_back("llvm-include-order");
349 Tidy.Remove.emplace_back("readability-*");
350 Tidy.CheckOptions.emplace_back(std::make_pair(
351 std::string("example-check.ExampleOption"), std::string("0")));
352 EXPECT_TRUE(compileAndApply());
353 EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.size(), 1U);
354 EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup(
355 "example-check.ExampleOption"),
356 "0");
357#if CLANGD_TIDY_CHECKS
358 EXPECT_EQ(
359 Conf.Diagnostics.ClangTidy.Checks,
360 "bugprone-use-after-move,llvm-*,-llvm-include-order,-readability-*");
361 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
362#else // !CLANGD_TIDY_CHECKS
363 EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "llvm-*,-readability-*");
364 EXPECT_THAT(
365 Diags.Diagnostics,
366 ElementsAre(
367 diagMessage(
368 "clang-tidy check 'bugprone-use-after-move' was not found"),
369 diagMessage("clang-tidy check 'llvm-include-order' was not found")));
370#endif
371}
372
373TEST_F(ConfigCompileTests, TidyBadChecks) {
374 auto &Tidy = Frag.Diagnostics.ClangTidy;
375 Tidy.Add.emplace_back("unknown-check");
376 Tidy.Remove.emplace_back("*");
377 Tidy.Remove.emplace_back("llvm-includeorder");
378 EXPECT_TRUE(compileAndApply());
379 // Ensure bad checks are stripped from the glob.
380 EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "-*");
381 EXPECT_THAT(
382 Diags.Diagnostics,
383 ElementsAre(
384 AllOf(diagMessage("clang-tidy check 'unknown-check' was not found"),
385 diagKind(llvm::SourceMgr::DK_Warning)),
386 AllOf(
387 diagMessage("clang-tidy check 'llvm-includeorder' was not found"),
388 diagKind(llvm::SourceMgr::DK_Warning))));
389}
390
391TEST_F(ConfigCompileTests, ExternalServerNeedsTrusted) {
393 External.Server.emplace("xxx");
394 Frag.Index.External = std::move(External);
395 compileAndApply();
396 EXPECT_THAT(
397 Diags.Diagnostics,
398 ElementsAre(diagMessage(
399 "Remote index may not be specified by untrusted configuration. "
400 "Copy this into user config to use it.")));
401 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
402}
403
404TEST_F(ConfigCompileTests, ExternalBlockWarnOnMultipleSource) {
405 Frag.Source.Trusted = true;
407 External.File.emplace("");
408 External.Server.emplace("");
409 Frag.Index.External = std::move(External);
410 compileAndApply();
411#ifdef CLANGD_ENABLE_REMOTE
412 EXPECT_THAT(
413 Diags.Diagnostics,
414 Contains(
415 AllOf(diagMessage("Exactly one of File, Server or None must be set."),
416 diagKind(llvm::SourceMgr::DK_Error))));
417#else
418 ASSERT_TRUE(Conf.Index.External.hasValue());
419 EXPECT_EQ(Conf.Index.External->Kind, Config::ExternalIndexSpec::File);
420#endif
421}
422
423TEST_F(ConfigCompileTests, ExternalBlockDisableWithNone) {
424 compileAndApply();
425 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
426
428 External.IsNone = true;
429 Frag.Index.External = std::move(External);
430 compileAndApply();
431 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
432}
433
434TEST_F(ConfigCompileTests, ExternalBlockErrOnNoSource) {
435 Frag.Index.External.emplace(Fragment::IndexBlock::ExternalBlock{});
436 compileAndApply();
437 EXPECT_THAT(
438 Diags.Diagnostics,
439 Contains(
440 AllOf(diagMessage("Exactly one of File, Server or None must be set."),
441 diagKind(llvm::SourceMgr::DK_Error))));
442}
443
444TEST_F(ConfigCompileTests, ExternalBlockDisablesBackgroundIndex) {
445 auto BazPath = testPath("foo/bar/baz.h", llvm::sys::path::Style::posix);
446 Parm.Path = BazPath;
447 Frag.Index.Background.emplace("Build");
449 External.File.emplace(testPath("foo"));
450 External.MountPoint.emplace(
451 testPath("foo/bar", llvm::sys::path::Style::posix));
452 Frag.Index.External = std::move(External);
453 compileAndApply();
454 EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
455}
456
457TEST_F(ConfigCompileTests, ExternalBlockMountPoint) {
458 auto GetFrag = [](llvm::StringRef Directory,
459 std::optional<const char *> MountPoint) {
460 Fragment Frag;
461 Frag.Source.Directory = Directory.str();
463 External.File.emplace(testPath("foo"));
464 if (MountPoint)
465 External.MountPoint.emplace(*MountPoint);
466 Frag.Index.External = std::move(External);
467 return Frag;
468 };
469
470 auto BarPath = testPath("foo/bar.h", llvm::sys::path::Style::posix);
471 BarPath = llvm::sys::path::convert_to_slash(BarPath);
472 Parm.Path = BarPath;
473 // Non-absolute MountPoint without a directory raises an error.
474 Frag = GetFrag("", "foo");
475 compileAndApply();
476 ASSERT_THAT(
477 Diags.Diagnostics,
478 ElementsAre(
479 AllOf(diagMessage("MountPoint must be an absolute path, because this "
480 "fragment is not associated with any directory."),
481 diagKind(llvm::SourceMgr::DK_Error))));
482 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
483
484 auto FooPath = testPath("foo/", llvm::sys::path::Style::posix);
485 FooPath = llvm::sys::path::convert_to_slash(FooPath);
486 // Ok when relative.
487 Frag = GetFrag(testRoot(), "foo/");
488 compileAndApply();
489 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
490 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
491 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
492
493 // None defaults to ".".
494 Frag = GetFrag(FooPath, std::nullopt);
495 compileAndApply();
496 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
497 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
498 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
499
500 // Without a file, external index is empty.
501 Parm.Path = "";
502 Frag = GetFrag("", FooPath.c_str());
503 compileAndApply();
504 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
505 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
506
507 // File outside MountPoint, no index.
508 auto BazPath = testPath("bar/baz.h", llvm::sys::path::Style::posix);
509 BazPath = llvm::sys::path::convert_to_slash(BazPath);
510 Parm.Path = BazPath;
511 Frag = GetFrag("", FooPath.c_str());
512 compileAndApply();
513 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
514 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
515
516 // File under MountPoint, index should be set.
517 BazPath = testPath("foo/baz.h", llvm::sys::path::Style::posix);
518 BazPath = llvm::sys::path::convert_to_slash(BazPath);
519 Parm.Path = BazPath;
520 Frag = GetFrag("", FooPath.c_str());
521 compileAndApply();
522 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
523 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
524 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
525
526 // Only matches case-insensitively.
527 BazPath = testPath("fOo/baz.h", llvm::sys::path::Style::posix);
528 BazPath = llvm::sys::path::convert_to_slash(BazPath);
529 Parm.Path = BazPath;
530
531 FooPath = testPath("FOO/", llvm::sys::path::Style::posix);
532 FooPath = llvm::sys::path::convert_to_slash(FooPath);
533 Frag = GetFrag("", FooPath.c_str());
534 compileAndApply();
535 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
536#ifdef CLANGD_PATH_CASE_INSENSITIVE
537 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
538 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
539#else
540 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
541#endif
542}
543
544TEST_F(ConfigCompileTests, AllScopes) {
545 // Defaults to true.
546 EXPECT_TRUE(compileAndApply());
547 EXPECT_TRUE(Conf.Completion.AllScopes);
548
549 Frag = {};
550 Frag.Completion.AllScopes = false;
551 EXPECT_TRUE(compileAndApply());
552 EXPECT_FALSE(Conf.Completion.AllScopes);
553
554 Frag = {};
555 Frag.Completion.AllScopes = true;
556 EXPECT_TRUE(compileAndApply());
557 EXPECT_TRUE(Conf.Completion.AllScopes);
558}
559
560TEST_F(ConfigCompileTests, Style) {
561 Frag = {};
562 Frag.Style.FullyQualifiedNamespaces.push_back(std::string("foo"));
563 Frag.Style.FullyQualifiedNamespaces.push_back(std::string("bar"));
564 EXPECT_TRUE(compileAndApply());
565 EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar"));
566
567 {
568 Frag = {};
569 EXPECT_TRUE(Conf.Style.QuotedHeaders.empty())
570 << Conf.Style.QuotedHeaders.size();
571 Frag.Style.QuotedHeaders.push_back(Located<std::string>("foo.h"));
572 Frag.Style.QuotedHeaders.push_back(Located<std::string>(".*inc"));
573 EXPECT_TRUE(compileAndApply());
574 auto HeaderFilter = [this](llvm::StringRef Path) {
575 for (auto &Filter : Conf.Style.QuotedHeaders) {
576 if (Filter(Path))
577 return true;
578 }
579 return false;
580 };
581 EXPECT_TRUE(HeaderFilter("foo.h"));
582 EXPECT_TRUE(HeaderFilter("prefix/foo.h"));
583 EXPECT_FALSE(HeaderFilter("bar.h"));
584 EXPECT_FALSE(HeaderFilter("foo.h/bar.h"));
585 }
586
587 {
588 Frag = {};
589 EXPECT_TRUE(Conf.Style.AngledHeaders.empty())
590 << Conf.Style.AngledHeaders.size();
591 Frag.Style.AngledHeaders.push_back(Located<std::string>("foo.h"));
592 Frag.Style.AngledHeaders.push_back(Located<std::string>(".*inc"));
593 EXPECT_TRUE(compileAndApply());
594 auto HeaderFilter = [this](llvm::StringRef Path) {
595 for (auto &Filter : Conf.Style.AngledHeaders) {
596 if (Filter(Path))
597 return true;
598 }
599 return false;
600 };
601 EXPECT_TRUE(HeaderFilter("foo.h"));
602 EXPECT_FALSE(HeaderFilter("bar.h"));
603 }
604}
605} // namespace
606} // namespace config
607} // namespace clangd
608} // namespace clang
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
static cl::opt< std::string > Config("config", desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:{x:y}}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:45
bool isDiagnosticSuppressed(const clang::Diagnostic &Diag, const llvm::StringSet<> &Suppress, const LangOptions &LangOpts)
Determine whether a (non-clang-tidy) diagnostic is suppressed by config.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:93
llvm::ArrayRef< std::function< bool(llvm::StringRef)> > HeaderFilter
Definition Headers.h:36
std::string Path
A typedef to represent a file path.
Definition Path.h:26
const char * testRoot()
Definition TestFS.cpp:85
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
@ Strict
Diagnose missing and unused includes.
Definition Config.h:98
An external index uses data source outside of clangd itself.
std::optional< Located< std::string > > Server
Address and port number for a clangd-index-server.
Located< bool > IsNone
Whether the block is explicitly set to None.
std::optional< Located< std::string > > File
Path to an index file generated by clangd-indexer.
std::string Directory
Absolute path to directory the fragment is associated with.
A chunk of configuration obtained from a config file, LSP, or elsewhere.
An entity written in config along, with its optional location in the file.