609 lines · cpp
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-exception6//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_INSENSITIVE108 EXPECT_TRUE(compileAndApply());109#else110 EXPECT_FALSE(compileAndApply());111#endif112 113 Frag = {};114 Frag.If.PathExclude.emplace_back("B.*R");115 EXPECT_THAT(Diags.Diagnostics, IsEmpty());116#ifdef CLANGD_PATH_CASE_INSENSITIVE117 EXPECT_FALSE(compileAndApply());118#else119 EXPECT_TRUE(compileAndApply());120#endif121}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,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 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 under214 // /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 // PathMatch228 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 // PathEclude237 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_CHECKS358 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_CHECKS363 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#endif371}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) {392 Fragment::IndexBlock::ExternalBlock External;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;406 Fragment::IndexBlock::ExternalBlock External;407 External.File.emplace("");408 External.Server.emplace("");409 Frag.Index.External = std::move(External);410 compileAndApply();411#ifdef CLANGD_ENABLE_REMOTE412 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#else418 ASSERT_TRUE(Conf.Index.External.hasValue());419 EXPECT_EQ(Conf.Index.External->Kind, Config::ExternalIndexSpec::File);420#endif421}422 423TEST_F(ConfigCompileTests, ExternalBlockDisableWithNone) {424 compileAndApply();425 EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);426 427 Fragment::IndexBlock::ExternalBlock External;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");448 Fragment::IndexBlock::ExternalBlock External;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();462 Fragment::IndexBlock::ExternalBlock External;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_INSENSITIVE537 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);538 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);539#else540 ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);541#endif542}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} // namespace606} // namespace config607} // namespace clangd608} // namespace clang609