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"
28using ::testing::AllOf;
29using ::testing::Contains;
30using ::testing::ElementsAre;
31using ::testing::IsEmpty;
32using ::testing::SizeIs;
33using ::testing::StartsWith;
34using ::testing::UnorderedElementsAre;
36class ConfigCompileTests :
public ::testing::Test {
43 bool compileAndApply() {
45 Diags.Diagnostics.clear();
46 auto Compiled = std::move(Frag).compile(Diags.callback());
47 return Compiled(Parm, Conf);
51TEST_F(ConfigCompileTests, Condition) {
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));
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));
71 Frag.If.PathMatch.emplace_back(
"fo*");
72 EXPECT_FALSE(compileAndApply());
73 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
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());
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());
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"));
98 Frag.If.HasUnrecognizedCondition =
true;
99 Frag.If.PathMatch.emplace_back(
"ba*r");
100 EXPECT_FALSE(compileAndApply());
101 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
105 Frag.If.PathMatch.emplace_back(
"B.*R");
106 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
107#ifdef CLANGD_PATH_CASE_INSENSITIVE
108 EXPECT_TRUE(compileAndApply());
110 EXPECT_FALSE(compileAndApply());
114 Frag.If.PathExclude.emplace_back(
"B.*R");
115 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
116#ifdef CLANGD_PATH_CASE_INSENSITIVE
117 EXPECT_FALSE(compileAndApply());
119 EXPECT_TRUE(compileAndApply());
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)
132 EXPECT_THAT(Argv, ElementsAre(
"tpc.exe",
"-foo",
"--",
"a.cc"));
135TEST_F(ConfigCompileTests, CompilationDatabase) {
136 Frag.CompileFlags.CompilationDatabase.emplace(
"None");
137 EXPECT_TRUE(compileAndApply());
138 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
141 Frag.CompileFlags.CompilationDatabase.emplace(
"Ancestors");
142 EXPECT_TRUE(compileAndApply());
143 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
147 Frag.CompileFlags.CompilationDatabase.emplace(
"Something");
148 EXPECT_TRUE(compileAndApply());
149 EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
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.")));
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());
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());
175TEST_F(ConfigCompileTests, Index) {
176 Frag.Index.Background.emplace(
"Skip");
177 EXPECT_TRUE(compileAndApply());
181 Frag.Index.Background.emplace(
"Foo");
182 EXPECT_TRUE(compileAndApply());
187 ElementsAre(diagMessage(
188 "Invalid Background value 'Foo'. Valid values are Build, Skip.")));
191TEST_F(ConfigCompileTests, PathSpecMatch) {
192 auto BarPath = llvm::sys::path::convert_to_slash(
testPath(
"foo/bar.h"));
197 std::string PathSpec;
203 llvm::sys::path::convert_to_slash(
testPath(
"foo/bar.h")),
209 llvm::sys::path::convert_to_slash(
testPath(
"bar/bar.h")),
228 for (
const auto &Case : Cases) {
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());
237 for (
const auto &Case : Cases) {
238 SCOPED_TRACE(Case.Directory);
239 SCOPED_TRACE(Case.PathSpec);
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());
248TEST_F(ConfigCompileTests, DiagnosticsIncludeCleaner) {
250 EXPECT_TRUE(compileAndApply());
254 Frag.Diagnostics.UnusedIncludes.emplace(
"None");
255 EXPECT_TRUE(compileAndApply());
259 Frag.Diagnostics.UnusedIncludes.emplace(
"Strict");
260 EXPECT_TRUE(compileAndApply());
264 EXPECT_TRUE(Conf.Diagnostics.Includes.IgnoreHeader.empty())
265 << Conf.Diagnostics.Includes.IgnoreHeader.size();
266 Frag.Diagnostics.Includes.IgnoreHeader.push_back(
268 Frag.Diagnostics.Includes.IgnoreHeader.push_back(
270 EXPECT_TRUE(compileAndApply());
272 for (
auto &Filter : Conf.Diagnostics.Includes.IgnoreHeader) {
282 EXPECT_FALSE(Conf.Diagnostics.Includes.AnalyzeAngledIncludes);
283 Frag.Diagnostics.Includes.AnalyzeAngledIncludes =
true;
284 EXPECT_TRUE(compileAndApply());
285 EXPECT_TRUE(Conf.Diagnostics.Includes.AnalyzeAngledIncludes);
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);
305 using Diag = clang::Diagnostic;
307 auto D = DiagEngine.Report(diag::warn_unreachable);
309 Diag{&DiagEngine,
D}, Conf.Diagnostics.Suppress, LangOptions()));
313 auto D = DiagEngine.Report(diag::warn_unreachable_break);
315 Diag{&DiagEngine,
D}, Conf.Diagnostics.Suppress, LangOptions()));
318 auto D = DiagEngine.Report(diag::warn_unused_variable);
320 Diag{&DiagEngine,
D}, Conf.Diagnostics.Suppress, LangOptions()));
323 auto D = DiagEngine.Report(diag::err_typecheck_bool_condition);
325 Diag{&DiagEngine,
D}, Conf.Diagnostics.Suppress, LangOptions()));
328 auto D = DiagEngine.Report(diag::err_unexpected_friend);
330 Diag{&DiagEngine,
D}, Conf.Diagnostics.Suppress, LangOptions()));
333 auto D = DiagEngine.Report(diag::warn_alloca);
335 Diag{&DiagEngine,
D}, Conf.Diagnostics.Suppress, LangOptions()));
338 Frag.Diagnostics.Suppress.emplace_back(
"*");
339 EXPECT_TRUE(compileAndApply());
340 EXPECT_TRUE(Conf.Diagnostics.SuppressAll);
341 EXPECT_THAT(Conf.Diagnostics.Suppress, IsEmpty());
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"),
357#if CLANGD_TIDY_CHECKS
359 Conf.Diagnostics.ClangTidy.Checks,
360 "bugprone-use-after-move,llvm-*,-llvm-include-order,-readability-*");
361 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
363 EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks,
"llvm-*,-readability-*");
368 "clang-tidy check 'bugprone-use-after-move' was not found"),
369 diagMessage(
"clang-tidy check 'llvm-include-order' was not found")));
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());
380 EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks,
"-*");
384 AllOf(diagMessage(
"clang-tidy check 'unknown-check' was not found"),
385 diagKind(llvm::SourceMgr::DK_Warning)),
387 diagMessage(
"clang-tidy check 'llvm-includeorder' was not found"),
388 diagKind(llvm::SourceMgr::DK_Warning))));
391TEST_F(ConfigCompileTests, ExternalServerNeedsTrusted) {
393 External.
Server.emplace(
"xxx");
394 Frag.Index.External = std::move(External);
398 ElementsAre(diagMessage(
399 "Remote index may not be specified by untrusted configuration. "
400 "Copy this into user config to use it.")));
404TEST_F(ConfigCompileTests, ExternalBlockWarnOnMultipleSource) {
405 Frag.Source.Trusted =
true;
407 External.
File.emplace(
"");
408 External.Server.emplace(
"");
409 Frag.Index.External = std::move(External);
411#ifdef CLANGD_ENABLE_REMOTE
415 AllOf(diagMessage(
"Exactly one of File, Server or None must be set."),
416 diagKind(llvm::SourceMgr::DK_Error))));
418 ASSERT_TRUE(Conf.Index.External.hasValue());
423TEST_F(ConfigCompileTests, ExternalBlockDisableWithNone) {
429 Frag.Index.External = std::move(External);
434TEST_F(ConfigCompileTests, ExternalBlockErrOnNoSource) {
440 AllOf(diagMessage(
"Exactly one of File, Server or None must be set."),
441 diagKind(llvm::SourceMgr::DK_Error))));
444TEST_F(ConfigCompileTests, ExternalBlockDisablesBackgroundIndex) {
445 auto BazPath =
testPath(
"foo/bar/baz.h", llvm::sys::path::Style::posix);
447 Frag.Index.Background.emplace(
"Build");
450 External.MountPoint.emplace(
451 testPath(
"foo/bar", llvm::sys::path::Style::posix));
452 Frag.Index.External = std::move(External);
457TEST_F(ConfigCompileTests, ExternalBlockMountPoint) {
458 auto GetFrag = [](llvm::StringRef
Directory,
459 std::optional<const char *> MountPoint) {
465 External.MountPoint.emplace(*MountPoint);
466 Frag.Index.External = std::move(External);
470 auto BarPath =
testPath(
"foo/bar.h", llvm::sys::path::Style::posix);
471 BarPath = llvm::sys::path::convert_to_slash(BarPath);
474 Frag = GetFrag(
"",
"foo");
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))));
484 auto FooPath =
testPath(
"foo/", llvm::sys::path::Style::posix);
485 FooPath = llvm::sys::path::convert_to_slash(FooPath);
489 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
491 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
494 Frag = GetFrag(FooPath, std::nullopt);
496 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
498 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
502 Frag = GetFrag(
"", FooPath.c_str());
504 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
508 auto BazPath =
testPath(
"bar/baz.h", llvm::sys::path::Style::posix);
509 BazPath = llvm::sys::path::convert_to_slash(BazPath);
511 Frag = GetFrag(
"", FooPath.c_str());
513 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
517 BazPath =
testPath(
"foo/baz.h", llvm::sys::path::Style::posix);
518 BazPath = llvm::sys::path::convert_to_slash(BazPath);
520 Frag = GetFrag(
"", FooPath.c_str());
522 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
524 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
527 BazPath =
testPath(
"fOo/baz.h", llvm::sys::path::Style::posix);
528 BazPath = llvm::sys::path::convert_to_slash(BazPath);
531 FooPath =
testPath(
"FOO/", llvm::sys::path::Style::posix);
532 FooPath = llvm::sys::path::convert_to_slash(FooPath);
533 Frag = GetFrag(
"", FooPath.c_str());
535 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
536#ifdef CLANGD_PATH_CASE_INSENSITIVE
538 EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
544TEST_F(ConfigCompileTests, AllScopes) {
546 EXPECT_TRUE(compileAndApply());
547 EXPECT_TRUE(Conf.Completion.AllScopes);
550 Frag.Completion.AllScopes =
false;
551 EXPECT_TRUE(compileAndApply());
552 EXPECT_FALSE(Conf.Completion.AllScopes);
555 Frag.Completion.AllScopes =
true;
556 EXPECT_TRUE(compileAndApply());
557 EXPECT_TRUE(Conf.Completion.AllScopes);
560TEST_F(ConfigCompileTests, Style) {
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"));
569 EXPECT_TRUE(Conf.Style.QuotedHeaders.empty())
570 << Conf.Style.QuotedHeaders.size();
573 EXPECT_TRUE(compileAndApply());
575 for (
auto &Filter : Conf.Style.QuotedHeaders) {
589 EXPECT_TRUE(Conf.Style.AngledHeaders.empty())
590 << Conf.Style.AngledHeaders.size();
593 EXPECT_TRUE(compileAndApply());
595 for (
auto &Filter : Conf.Style.AngledHeaders) {
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.
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)
llvm::ArrayRef< std::function< bool(llvm::StringRef)> > HeaderFilter
std::string Path
A typedef to represent a file path.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
@ Strict
Diagnose missing and unused includes.
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.