xref: /llvm-project/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp (revision 0865ecc5150b9a55ba1f9e30b6d463a66ac362a6)
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 
24 namespace clang {
25 namespace clangd {
26 namespace config {
27 namespace {
28 using ::testing::AllOf;
29 using ::testing::Contains;
30 using ::testing::ElementsAre;
31 using ::testing::IsEmpty;
32 using ::testing::SizeIs;
33 using ::testing::StartsWith;
34 using ::testing::UnorderedElementsAre;
35 
36 class ConfigCompileTests : public ::testing::Test {
37 protected:
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 
51 TEST_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 
123 TEST_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 
135 TEST_F(ConfigCompileTests, CompilationDatabase) {
136   Frag.CompileFlags.CompilationDatabase.emplace("None");
137   EXPECT_TRUE(compileAndApply());
138   EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
139             Config::CDBSearchSpec::NoCDBSearch);
140 
141   Frag.CompileFlags.CompilationDatabase.emplace("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("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(testPath("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 
175 TEST_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 
191 TEST_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 
248 TEST_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 
288 TEST_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::DiagnosticsEngine DiagEngine(new DiagnosticIDs, nullptr,
302                                       new clang::IgnoringDiagConsumer);
303 
304   using Diag = clang::Diagnostic;
305   {
306     auto D = DiagEngine.Report(diag::warn_unreachable);
307     EXPECT_TRUE(isDiagnosticSuppressed(
308         Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
309   }
310   // Subcategory not respected/suppressed.
311   {
312     auto D = DiagEngine.Report(diag::warn_unreachable_break);
313     EXPECT_FALSE(isDiagnosticSuppressed(
314         Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
315   }
316   {
317     auto D = DiagEngine.Report(diag::warn_unused_variable);
318     EXPECT_TRUE(isDiagnosticSuppressed(
319         Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
320   }
321   {
322     auto D = DiagEngine.Report(diag::err_typecheck_bool_condition);
323     EXPECT_TRUE(isDiagnosticSuppressed(
324         Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
325   }
326   {
327     auto D = DiagEngine.Report(diag::err_unexpected_friend);
328     EXPECT_TRUE(isDiagnosticSuppressed(
329         Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
330   }
331   {
332     auto D = DiagEngine.Report(diag::warn_alloca);
333     EXPECT_TRUE(isDiagnosticSuppressed(
334         Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
335   }
336 
337   Frag.Diagnostics.Suppress.emplace_back("*");
338   EXPECT_TRUE(compileAndApply());
339   EXPECT_TRUE(Conf.Diagnostics.SuppressAll);
340   EXPECT_THAT(Conf.Diagnostics.Suppress, IsEmpty());
341 }
342 
343 TEST_F(ConfigCompileTests, Tidy) {
344   auto &Tidy = Frag.Diagnostics.ClangTidy;
345   Tidy.Add.emplace_back("bugprone-use-after-move");
346   Tidy.Add.emplace_back("llvm-*");
347   Tidy.Remove.emplace_back("llvm-include-order");
348   Tidy.Remove.emplace_back("readability-*");
349   Tidy.CheckOptions.emplace_back(
350       std::make_pair(std::string("StrictMode"), std::string("true")));
351   Tidy.CheckOptions.emplace_back(std::make_pair(
352       std::string("example-check.ExampleOption"), std::string("0")));
353   EXPECT_TRUE(compileAndApply());
354   EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.size(), 2U);
355   EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup("StrictMode"),
356             "true");
357   EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup(
358                 "example-check.ExampleOption"),
359             "0");
360 #if CLANGD_TIDY_CHECKS
361   EXPECT_EQ(
362       Conf.Diagnostics.ClangTidy.Checks,
363       "bugprone-use-after-move,llvm-*,-llvm-include-order,-readability-*");
364   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
365 #else // !CLANGD_TIDY_CHECKS
366   EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "llvm-*,-readability-*");
367   EXPECT_THAT(
368       Diags.Diagnostics,
369       ElementsAre(
370           diagMessage(
371               "clang-tidy check 'bugprone-use-after-move' was not found"),
372           diagMessage("clang-tidy check 'llvm-include-order' was not found")));
373 #endif
374 }
375 
376 TEST_F(ConfigCompileTests, TidyBadChecks) {
377   auto &Tidy = Frag.Diagnostics.ClangTidy;
378   Tidy.Add.emplace_back("unknown-check");
379   Tidy.Remove.emplace_back("*");
380   Tidy.Remove.emplace_back("llvm-includeorder");
381   EXPECT_TRUE(compileAndApply());
382   // Ensure bad checks are stripped from the glob.
383   EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "-*");
384   EXPECT_THAT(
385       Diags.Diagnostics,
386       ElementsAre(
387           AllOf(diagMessage("clang-tidy check 'unknown-check' was not found"),
388                 diagKind(llvm::SourceMgr::DK_Warning)),
389           AllOf(
390               diagMessage("clang-tidy check 'llvm-includeorder' was not found"),
391               diagKind(llvm::SourceMgr::DK_Warning))));
392 }
393 
394 TEST_F(ConfigCompileTests, ExternalServerNeedsTrusted) {
395   Fragment::IndexBlock::ExternalBlock External;
396   External.Server.emplace("xxx");
397   Frag.Index.External = std::move(External);
398   compileAndApply();
399   EXPECT_THAT(
400       Diags.Diagnostics,
401       ElementsAre(diagMessage(
402           "Remote index may not be specified by untrusted configuration. "
403           "Copy this into user config to use it.")));
404   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
405 }
406 
407 TEST_F(ConfigCompileTests, ExternalBlockWarnOnMultipleSource) {
408   Frag.Source.Trusted = true;
409   Fragment::IndexBlock::ExternalBlock External;
410   External.File.emplace("");
411   External.Server.emplace("");
412   Frag.Index.External = std::move(External);
413   compileAndApply();
414 #ifdef CLANGD_ENABLE_REMOTE
415   EXPECT_THAT(
416       Diags.Diagnostics,
417       Contains(
418           AllOf(diagMessage("Exactly one of File, Server or None must be set."),
419                 diagKind(llvm::SourceMgr::DK_Error))));
420 #else
421   ASSERT_TRUE(Conf.Index.External.hasValue());
422   EXPECT_EQ(Conf.Index.External->Kind, Config::ExternalIndexSpec::File);
423 #endif
424 }
425 
426 TEST_F(ConfigCompileTests, ExternalBlockDisableWithNone) {
427   compileAndApply();
428   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
429 
430   Fragment::IndexBlock::ExternalBlock External;
431   External.IsNone = true;
432   Frag.Index.External = std::move(External);
433   compileAndApply();
434   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
435 }
436 
437 TEST_F(ConfigCompileTests, ExternalBlockErrOnNoSource) {
438   Frag.Index.External.emplace(Fragment::IndexBlock::ExternalBlock{});
439   compileAndApply();
440   EXPECT_THAT(
441       Diags.Diagnostics,
442       Contains(
443           AllOf(diagMessage("Exactly one of File, Server or None must be set."),
444                 diagKind(llvm::SourceMgr::DK_Error))));
445 }
446 
447 TEST_F(ConfigCompileTests, ExternalBlockDisablesBackgroundIndex) {
448   auto BazPath = testPath("foo/bar/baz.h", llvm::sys::path::Style::posix);
449   Parm.Path = BazPath;
450   Frag.Index.Background.emplace("Build");
451   Fragment::IndexBlock::ExternalBlock External;
452   External.File.emplace(testPath("foo"));
453   External.MountPoint.emplace(
454       testPath("foo/bar", llvm::sys::path::Style::posix));
455   Frag.Index.External = std::move(External);
456   compileAndApply();
457   EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
458 }
459 
460 TEST_F(ConfigCompileTests, ExternalBlockMountPoint) {
461   auto GetFrag = [](llvm::StringRef Directory,
462                     std::optional<const char *> MountPoint) {
463     Fragment Frag;
464     Frag.Source.Directory = Directory.str();
465     Fragment::IndexBlock::ExternalBlock External;
466     External.File.emplace(testPath("foo"));
467     if (MountPoint)
468       External.MountPoint.emplace(*MountPoint);
469     Frag.Index.External = std::move(External);
470     return Frag;
471   };
472 
473   auto BarPath = testPath("foo/bar.h", llvm::sys::path::Style::posix);
474   BarPath = llvm::sys::path::convert_to_slash(BarPath);
475   Parm.Path = BarPath;
476   // Non-absolute MountPoint without a directory raises an error.
477   Frag = GetFrag("", "foo");
478   compileAndApply();
479   ASSERT_THAT(
480       Diags.Diagnostics,
481       ElementsAre(
482           AllOf(diagMessage("MountPoint must be an absolute path, because this "
483                             "fragment is not associated with any directory."),
484                 diagKind(llvm::SourceMgr::DK_Error))));
485   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
486 
487   auto FooPath = testPath("foo/", llvm::sys::path::Style::posix);
488   FooPath = llvm::sys::path::convert_to_slash(FooPath);
489   // Ok when relative.
490   Frag = GetFrag(testRoot(), "foo/");
491   compileAndApply();
492   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
493   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
494   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
495 
496   // None defaults to ".".
497   Frag = GetFrag(FooPath, std::nullopt);
498   compileAndApply();
499   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
500   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
501   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
502 
503   // Without a file, external index is empty.
504   Parm.Path = "";
505   Frag = GetFrag("", FooPath.c_str());
506   compileAndApply();
507   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
508   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
509 
510   // File outside MountPoint, no index.
511   auto BazPath = testPath("bar/baz.h", llvm::sys::path::Style::posix);
512   BazPath = llvm::sys::path::convert_to_slash(BazPath);
513   Parm.Path = BazPath;
514   Frag = GetFrag("", FooPath.c_str());
515   compileAndApply();
516   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
517   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
518 
519   // File under MountPoint, index should be set.
520   BazPath = testPath("foo/baz.h", llvm::sys::path::Style::posix);
521   BazPath = llvm::sys::path::convert_to_slash(BazPath);
522   Parm.Path = BazPath;
523   Frag = GetFrag("", FooPath.c_str());
524   compileAndApply();
525   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
526   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
527   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
528 
529   // Only matches case-insensitively.
530   BazPath = testPath("fOo/baz.h", llvm::sys::path::Style::posix);
531   BazPath = llvm::sys::path::convert_to_slash(BazPath);
532   Parm.Path = BazPath;
533 
534   FooPath = testPath("FOO/", llvm::sys::path::Style::posix);
535   FooPath = llvm::sys::path::convert_to_slash(FooPath);
536   Frag = GetFrag("", FooPath.c_str());
537   compileAndApply();
538   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
539 #ifdef CLANGD_PATH_CASE_INSENSITIVE
540   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
541   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
542 #else
543   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
544 #endif
545 }
546 
547 TEST_F(ConfigCompileTests, AllScopes) {
548   // Defaults to true.
549   EXPECT_TRUE(compileAndApply());
550   EXPECT_TRUE(Conf.Completion.AllScopes);
551 
552   Frag = {};
553   Frag.Completion.AllScopes = false;
554   EXPECT_TRUE(compileAndApply());
555   EXPECT_FALSE(Conf.Completion.AllScopes);
556 
557   Frag = {};
558   Frag.Completion.AllScopes = true;
559   EXPECT_TRUE(compileAndApply());
560   EXPECT_TRUE(Conf.Completion.AllScopes);
561 }
562 
563 TEST_F(ConfigCompileTests, Style) {
564   Frag = {};
565   Frag.Style.FullyQualifiedNamespaces.push_back(std::string("foo"));
566   Frag.Style.FullyQualifiedNamespaces.push_back(std::string("bar"));
567   EXPECT_TRUE(compileAndApply());
568   EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar"));
569 
570   {
571     Frag = {};
572     EXPECT_TRUE(Conf.Style.QuotedHeaders.empty())
573         << Conf.Style.QuotedHeaders.size();
574     Frag.Style.QuotedHeaders.push_back(Located<std::string>("foo.h"));
575     Frag.Style.QuotedHeaders.push_back(Located<std::string>(".*inc"));
576     EXPECT_TRUE(compileAndApply());
577     auto HeaderFilter = [this](llvm::StringRef Path) {
578       for (auto &Filter : Conf.Style.QuotedHeaders) {
579         if (Filter(Path))
580           return true;
581       }
582       return false;
583     };
584     EXPECT_TRUE(HeaderFilter("foo.h"));
585     EXPECT_TRUE(HeaderFilter("prefix/foo.h"));
586     EXPECT_FALSE(HeaderFilter("bar.h"));
587     EXPECT_FALSE(HeaderFilter("foo.h/bar.h"));
588   }
589 
590   {
591     Frag = {};
592     EXPECT_TRUE(Conf.Style.AngledHeaders.empty())
593         << Conf.Style.AngledHeaders.size();
594     Frag.Style.AngledHeaders.push_back(Located<std::string>("foo.h"));
595     Frag.Style.AngledHeaders.push_back(Located<std::string>(".*inc"));
596     EXPECT_TRUE(compileAndApply());
597     auto HeaderFilter = [this](llvm::StringRef Path) {
598       for (auto &Filter : Conf.Style.AngledHeaders) {
599         if (Filter(Path))
600           return true;
601       }
602       return false;
603     };
604     EXPECT_TRUE(HeaderFilter("foo.h"));
605     EXPECT_FALSE(HeaderFilter("bar.h"));
606   }
607 }
608 } // namespace
609 } // namespace config
610 } // namespace clangd
611 } // namespace clang
612