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