clang-tools 24.0.0git
ConfigProviderTests.cpp
Go to the documentation of this file.
1//===-- ConfigProviderTests.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 "ConfigProvider.h"
11#include "ConfigTesting.h"
12#include "TestFS.h"
13#include "llvm/Support/Path.h"
14#include "llvm/Support/SourceMgr.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17#include <atomic>
18#include <chrono>
19
20namespace clang {
21namespace clangd {
22namespace config {
23namespace {
24using ::testing::ElementsAre;
25using ::testing::IsEmpty;
26
27// Provider that appends an arg to compile flags.
28// The arg is prefix<N>, where N is the times getFragments() was called.
29// It also yields a diagnostic each time it's called.
30class FakeProvider : public Provider {
31 std::string Prefix;
32 mutable std::atomic<unsigned> Index = {0};
33
34 std::vector<CompiledFragment>
35 getFragments(const Params &, DiagnosticCallback DC) const override {
36 DC(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Prefix));
38 [Arg(Prefix + std::to_string(++Index))](const Params &P, Config &C) {
39 C.CompileFlags.Edits.push_back(
40 [Arg](std::vector<std::string> &Argv) { Argv.push_back(Arg); });
41 return true;
42 };
43 return {F};
44 }
45
46public:
47 FakeProvider(llvm::StringRef Prefix) : Prefix(Prefix) {}
48};
49
50std::vector<std::string> getAddedArgs(Config &C) {
51 std::vector<std::string> Argv;
52 for (auto &Edit : C.CompileFlags.Edits)
53 Edit(Argv);
54 return Argv;
55}
56
57// The provider from combine() should invoke its providers in order, and not
58// cache their results.
59TEST(ProviderTest, Combine) {
60 CapturedDiags Diags;
61 std::vector<std::unique_ptr<Provider>> Providers;
62 Providers.push_back(std::make_unique<FakeProvider>("foo"));
63 Providers.push_back(std::make_unique<FakeProvider>("bar"));
64 auto Combined = Provider::combine(std::move(Providers));
65 Config Cfg = Combined->getConfig(Params(), Diags.callback());
66 EXPECT_THAT(Diags.Diagnostics,
67 ElementsAre(diagMessage("foo"), diagMessage("bar")));
68 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo1", "bar1"));
69 Diags.Diagnostics.clear();
70
71 Cfg = Combined->getConfig(Params(), Diags.callback());
72 EXPECT_THAT(Diags.Diagnostics,
73 ElementsAre(diagMessage("foo"), diagMessage("bar")));
74 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo2", "bar2"));
75}
76
77const char *AddFooWithErr = R"yaml(
78CompileFlags:
79 Add: foo
80 Unknown: 42
81)yaml";
82
83const char *AddFooWithTypoErr = R"yaml(
84CompileFlags:
85 Add: foo
86 Removr: 42
87)yaml";
88
89const char *AddBarBaz = R"yaml(
90CompileFlags:
91 Add: bar
92---
93CompileFlags:
94 Add: baz
95)yaml";
96
97TEST(ProviderTest, FromYAMLFile) {
98 MockFS FS;
99 FS.Files["foo.yaml"] = AddFooWithErr;
100
101 CapturedDiags Diags;
102 auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
103 auto Cfg = P->getConfig(Params(), Diags.callback());
104 EXPECT_THAT(Diags.Diagnostics,
105 ElementsAre(diagMessage("Unknown CompileFlags key 'Unknown'")));
106 EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
107 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
108 Diags.clear();
109
110 Cfg = P->getConfig(Params(), Diags.callback());
111 EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
112 EXPECT_THAT(Diags.Files, IsEmpty());
113 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
114
115 FS.Files["foo.yaml"] = AddFooWithTypoErr;
116 Cfg = P->getConfig(Params(), Diags.callback());
117 EXPECT_THAT(
118 Diags.Diagnostics,
119 ElementsAre(diagMessage(
120 "Unknown CompileFlags key 'Removr'; did you mean 'Remove'?")));
121 EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
122 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
123 Diags.clear();
124
125 FS.Files["foo.yaml"] = AddBarBaz;
126 Cfg = P->getConfig(Params(), Diags.callback());
127 EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
128 EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
129 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
130 Diags.clear();
131
132 FS.Files.erase("foo.yaml");
133 Cfg = P->getConfig(Params(), Diags.callback());
134 EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Missing file is not an error";
135 EXPECT_THAT(Diags.Files, IsEmpty());
136 EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
137}
138
139TEST(ProviderTest, FromAncestorRelativeYAMLFiles) {
140 MockFS FS;
141 FS.Files["a/b/c/foo.yaml"] = AddBarBaz;
142 FS.Files["a/foo.yaml"] = AddFooWithErr;
143
144 std::string ABCPath =
145 testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix);
146 Params ABCParams;
147 ABCParams.Path = ABCPath;
148 std::string APath =
149 testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix);
150 Params AParams;
151 AParams.Path = APath;
152
153 CapturedDiags Diags;
154 auto P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
155
156 auto Cfg = P->getConfig(Params(), Diags.callback());
157 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
158 EXPECT_THAT(Diags.Files, IsEmpty());
159 EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
160
161 Cfg = P->getConfig(ABCParams, Diags.callback());
162 EXPECT_THAT(Diags.Diagnostics,
163 ElementsAre(diagMessage("Unknown CompileFlags key 'Unknown'")));
164 // FIXME: fails on windows: paths have mixed slashes like C:\a/b\c.yaml
165 EXPECT_THAT(Diags.Files,
166 ElementsAre(testPath("a/foo.yaml"), testPath("a/b/c/foo.yaml")));
167 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo", "bar", "baz"));
168 Diags.clear();
169
170 Cfg = P->getConfig(AParams, Diags.callback());
171 EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached config";
172 EXPECT_THAT(Diags.Files, IsEmpty());
173 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
174
175 FS.Files.erase("a/foo.yaml");
176 Cfg = P->getConfig(ABCParams, Diags.callback());
177 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
178 EXPECT_THAT(Diags.Files, IsEmpty());
179 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
180}
181
182TEST(ProviderTest, SourceInfo) {
183 MockFS FS;
184
185 FS.Files["baz/foo.yaml"] = R"yaml(
186If:
187 PathMatch: .*
188 PathExclude: bar.h
189CompileFlags:
190 Add: bar
191)yaml";
192 const auto BarPath = testPath("baz/bar.h", llvm::sys::path::Style::posix);
193 CapturedDiags Diags;
194 Params Bar;
195 Bar.Path = BarPath;
196
197 // This should be an absolute match/exclude hence baz/bar.h should not be
198 // excluded.
199 auto P =
200 Provider::fromYAMLFile(testPath("baz/foo.yaml"), /*Directory=*/"", FS);
201 auto Cfg = P->getConfig(Bar, Diags.callback());
202 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
203 EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar"));
204 Diags.clear();
205
206 // This should be a relative match/exclude hence baz/bar.h should be excluded.
208 Cfg = P->getConfig(Bar, Diags.callback());
209 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
210 EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
211 Diags.clear();
212}
213} // namespace
214} // namespace config
215} // namespace clangd
216} // namespace clang
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))
A source of configuration fragments.
static std::unique_ptr< Provider > fromYAMLFile(llvm::StringRef AbsPath, llvm::StringRef Directory, const ThreadsafeFS &, bool Trusted=false)
Reads fragments from a single YAML file with a fixed path.
static std::unique_ptr< Provider > fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath, const ThreadsafeFS &, bool Trusted=false)
static std::unique_ptr< Provider > combine(std::vector< std::unique_ptr< Provider > >)
A provider that includes fragments from all the supplied providers.
std::function< bool(const Params &, Config &)> CompiledFragment
A chunk of configuration that has been fully analyzed and is ready to apply.
llvm::function_ref< void(const llvm::SMDiagnostic &)> DiagnosticCallback
Used to report problems in parsing or interpreting a config.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:94
TEST(BackgroundQueueTest, Priority)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Describes the context used to evaluate configuration fragments.
llvm::StringRef Path
Absolute path to a source file we're applying the config to.