clang-tools  14.0.0git
GlobalCompilationDatabaseTests.cpp
Go to the documentation of this file.
1 //===-- GlobalCompilationDatabaseTests.cpp ----------------------*- C++ -*-===//
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 
10 
11 #include "Config.h"
12 #include "Matchers.h"
13 #include "TestFS.h"
14 #include "support/Path.h"
15 #include "support/ThreadsafeFS.h"
16 #include "clang/Tooling/CompilationDatabase.h"
17 #include "llvm/ADT/Optional.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/FormatVariadic.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include "llvm/Support/Path.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include <chrono>
30 #include <fstream>
31 #include <string>
32 
33 namespace clang {
34 namespace clangd {
35 namespace {
36 using ::testing::AllOf;
37 using ::testing::Contains;
38 using ::testing::ElementsAre;
39 using ::testing::EndsWith;
40 using ::testing::HasSubstr;
41 using ::testing::IsEmpty;
42 using ::testing::Not;
43 using ::testing::StartsWith;
44 using ::testing::UnorderedElementsAre;
45 
46 TEST(GlobalCompilationDatabaseTest, FallbackCommand) {
47  MockFS TFS;
48  DirectoryBasedGlobalCompilationDatabase DB(TFS);
49  auto Cmd = DB.getFallbackCommand(testPath("foo/bar.cc"));
50  EXPECT_EQ(Cmd.Directory, testPath("foo"));
51  EXPECT_THAT(Cmd.CommandLine, ElementsAre("clang", testPath("foo/bar.cc")));
52  EXPECT_EQ(Cmd.Output, "");
53 
54  // .h files have unknown language, so they are parsed liberally as obj-c++.
55  Cmd = DB.getFallbackCommand(testPath("foo/bar.h"));
56  EXPECT_THAT(Cmd.CommandLine, ElementsAre("clang", "-xobjective-c++-header",
57  testPath("foo/bar.h")));
58  Cmd = DB.getFallbackCommand(testPath("foo/bar"));
59  EXPECT_THAT(Cmd.CommandLine, ElementsAre("clang", "-xobjective-c++-header",
60  testPath("foo/bar")));
61 }
62 
63 static tooling::CompileCommand cmd(llvm::StringRef File, llvm::StringRef Arg) {
64  return tooling::CompileCommand(
65  testRoot(), File, {"clang", std::string(Arg), std::string(File)}, "");
66 }
67 
68 class OverlayCDBTest : public ::testing::Test {
69  class BaseCDB : public GlobalCompilationDatabase {
70  public:
71  llvm::Optional<tooling::CompileCommand>
72  getCompileCommand(llvm::StringRef File) const override {
73  if (File == testPath("foo.cc"))
74  return cmd(File, "-DA=1");
75  return None;
76  }
77 
78  tooling::CompileCommand
79  getFallbackCommand(llvm::StringRef File) const override {
80  return cmd(File, "-DA=2");
81  }
82 
83  llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override {
84  return ProjectInfo{testRoot()};
85  }
86  };
87 
88 protected:
89  OverlayCDBTest() : Base(std::make_unique<BaseCDB>()) {}
90  std::unique_ptr<GlobalCompilationDatabase> Base;
91 };
92 
93 TEST_F(OverlayCDBTest, GetCompileCommand) {
94  OverlayCDB CDB(Base.get());
95  EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc"))->CommandLine,
96  AllOf(Contains(testPath("foo.cc")), Contains("-DA=1")));
97  EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), llvm::None);
98 
99  auto Override = cmd(testPath("foo.cc"), "-DA=3");
100  CDB.setCompileCommand(testPath("foo.cc"), Override);
101  EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc"))->CommandLine,
102  Contains("-DA=3"));
103  EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), llvm::None);
104  CDB.setCompileCommand(testPath("missing.cc"), Override);
105  EXPECT_THAT(CDB.getCompileCommand(testPath("missing.cc"))->CommandLine,
106  Contains("-DA=3"));
107 }
108 
109 TEST_F(OverlayCDBTest, GetFallbackCommand) {
110  OverlayCDB CDB(Base.get(), {"-DA=4"});
111  EXPECT_THAT(CDB.getFallbackCommand(testPath("bar.cc")).CommandLine,
112  ElementsAre("clang", "-DA=2", testPath("bar.cc"), "-DA=4"));
113 }
114 
115 TEST_F(OverlayCDBTest, NoBase) {
116  OverlayCDB CDB(nullptr, {"-DA=6"});
117  EXPECT_EQ(CDB.getCompileCommand(testPath("bar.cc")), None);
118  auto Override = cmd(testPath("bar.cc"), "-DA=5");
119  CDB.setCompileCommand(testPath("bar.cc"), Override);
120  EXPECT_THAT(CDB.getCompileCommand(testPath("bar.cc"))->CommandLine,
121  Contains("-DA=5"));
122 
123  EXPECT_THAT(CDB.getFallbackCommand(testPath("foo.cc")).CommandLine,
124  ElementsAre("clang", testPath("foo.cc"), "-DA=6"));
125 }
126 
127 TEST_F(OverlayCDBTest, Watch) {
128  OverlayCDB Inner(nullptr);
129  OverlayCDB Outer(&Inner);
130 
131  std::vector<std::vector<std::string>> Changes;
132  auto Sub = Outer.watch([&](const std::vector<std::string> &ChangedFiles) {
133  Changes.push_back(ChangedFiles);
134  });
135 
136  Inner.setCompileCommand("A.cpp", tooling::CompileCommand());
137  Outer.setCompileCommand("B.cpp", tooling::CompileCommand());
138  Inner.setCompileCommand("A.cpp", llvm::None);
139  Outer.setCompileCommand("C.cpp", llvm::None);
140  EXPECT_THAT(Changes, ElementsAre(ElementsAre("A.cpp"), ElementsAre("B.cpp"),
141  ElementsAre("A.cpp"), ElementsAre("C.cpp")));
142 }
143 
144 TEST_F(OverlayCDBTest, Adjustments) {
145  OverlayCDB CDB(Base.get(), {"-DFallback"},
146  [](const std::vector<std::string> &Cmd, llvm::StringRef File) {
147  auto Ret = Cmd;
148  Ret.push_back(
149  ("-DAdjust_" + llvm::sys::path::filename(File)).str());
150  return Ret;
151  });
152  // Command from underlying gets adjusted.
153  auto Cmd = CDB.getCompileCommand(testPath("foo.cc")).getValue();
154  EXPECT_THAT(Cmd.CommandLine, ElementsAre("clang", "-DA=1", testPath("foo.cc"),
155  "-DAdjust_foo.cc"));
156 
157  // Command from overlay gets adjusted.
158  tooling::CompileCommand BarCommand;
159  BarCommand.Filename = testPath("bar.cc");
160  BarCommand.CommandLine = {"clang++", "-DB=1", testPath("bar.cc")};
161  CDB.setCompileCommand(testPath("bar.cc"), BarCommand);
162  Cmd = CDB.getCompileCommand(testPath("bar.cc")).getValue();
163  EXPECT_THAT(
164  Cmd.CommandLine,
165  ElementsAre("clang++", "-DB=1", testPath("bar.cc"), "-DAdjust_bar.cc"));
166 
167  // Fallback gets adjusted.
168  Cmd = CDB.getFallbackCommand("baz.cc");
169  EXPECT_THAT(Cmd.CommandLine, ElementsAre("clang", "-DA=2", "baz.cc",
170  "-DFallback", "-DAdjust_baz.cc"));
171 }
172 
173 TEST(GlobalCompilationDatabaseTest, DiscoveryWithNestedCDBs) {
174  const char *const CDBOuter =
175  R"cdb(
176  [
177  {
178  "file": "a.cc",
179  "command": "",
180  "directory": "{0}",
181  },
182  {
183  "file": "build/gen.cc",
184  "command": "",
185  "directory": "{0}",
186  },
187  {
188  "file": "build/gen2.cc",
189  "command": "",
190  "directory": "{0}",
191  }
192  ]
193  )cdb";
194  const char *const CDBInner =
195  R"cdb(
196  [
197  {
198  "file": "gen.cc",
199  "command": "",
200  "directory": "{0}/build",
201  }
202  ]
203  )cdb";
204  MockFS FS;
205  FS.Files[testPath("compile_commands.json")] =
206  llvm::formatv(CDBOuter, llvm::sys::path::convert_to_slash(testRoot()));
207  FS.Files[testPath("build/compile_commands.json")] =
208  llvm::formatv(CDBInner, llvm::sys::path::convert_to_slash(testRoot()));
209  FS.Files[testPath("foo/compile_flags.txt")] = "-DFOO";
210 
211  // Note that gen2.cc goes missing with our following model, not sure this
212  // happens in practice though.
213  {
214  SCOPED_TRACE("Default ancestor scanning");
215  DirectoryBasedGlobalCompilationDatabase DB(FS);
216  std::vector<std::string> DiscoveredFiles;
217  auto Sub =
218  DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
219  DiscoveredFiles = Changes;
220  });
221 
222  DB.getCompileCommand(testPath("build/../a.cc"));
223  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
224  EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(AllOf(
225  EndsWith("a.cc"), Not(HasSubstr("..")))));
226  DiscoveredFiles.clear();
227 
228  DB.getCompileCommand(testPath("build/gen.cc"));
229  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
230  EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(EndsWith("gen.cc")));
231  }
232 
233  {
234  SCOPED_TRACE("With config");
235  DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
236  Opts.ContextProvider = [&](llvm::StringRef Path) {
237  Config Cfg;
238  if (Path.endswith("a.cc")) {
239  // a.cc uses another directory's CDB, so it won't be discovered.
240  Cfg.CompileFlags.CDBSearch.Policy = Config::CDBSearchSpec::FixedDir;
241  Cfg.CompileFlags.CDBSearch.FixedCDBPath = testPath("foo");
242  } else if (Path.endswith("gen.cc")) {
243  // gen.cc has CDB search disabled, so it won't be discovered.
244  Cfg.CompileFlags.CDBSearch.Policy = Config::CDBSearchSpec::NoCDBSearch;
245  } else if (Path.endswith("gen2.cc")) {
246  // gen2.cc explicitly lists this directory, so it will be discovered.
247  Cfg.CompileFlags.CDBSearch.Policy = Config::CDBSearchSpec::FixedDir;
248  Cfg.CompileFlags.CDBSearch.FixedCDBPath = testRoot();
249  }
250  return Context::current().derive(Config::Key, std::move(Cfg));
251  };
252  DirectoryBasedGlobalCompilationDatabase DB(Opts);
253  std::vector<std::string> DiscoveredFiles;
254  auto Sub =
255  DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
256  DiscoveredFiles = Changes;
257  });
258 
259  // Does not use the root CDB, so no broadcast.
260  auto Cmd = DB.getCompileCommand(testPath("build/../a.cc"));
261  ASSERT_TRUE(Cmd.hasValue());
262  EXPECT_THAT(Cmd->CommandLine, Contains("-DFOO")) << "a.cc uses foo/ CDB";
263  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
264  EXPECT_THAT(DiscoveredFiles, IsEmpty()) << "Root CDB not discovered yet";
265 
266  // No special config for b.cc, so we trigger broadcast of the root CDB.
267  DB.getCompileCommand(testPath("b.cc"));
268  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
269  EXPECT_THAT(DiscoveredFiles, ElementsAre(testPath("build/gen2.cc")));
270  DiscoveredFiles.clear();
271 
272  // No CDB search so no discovery/broadcast triggered for build/ CDB.
273  DB.getCompileCommand(testPath("build/gen.cc"));
274  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
275  EXPECT_THAT(DiscoveredFiles, IsEmpty());
276  }
277 
278  {
279  SCOPED_TRACE("With custom compile commands dir");
280  DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
281  Opts.CompileCommandsDir = testRoot();
282  DirectoryBasedGlobalCompilationDatabase DB(Opts);
283  std::vector<std::string> DiscoveredFiles;
284  auto Sub =
285  DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
286  DiscoveredFiles = Changes;
287  });
288 
289  DB.getCompileCommand(testPath("a.cc"));
290  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
291  EXPECT_THAT(DiscoveredFiles,
292  UnorderedElementsAre(EndsWith("a.cc"), EndsWith("gen.cc"),
293  EndsWith("gen2.cc")));
294  DiscoveredFiles.clear();
295 
296  DB.getCompileCommand(testPath("build/gen.cc"));
297  ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
298  EXPECT_THAT(DiscoveredFiles, IsEmpty());
299  }
300 }
301 
302 TEST(GlobalCompilationDatabaseTest, BuildDir) {
303  MockFS FS;
304  auto Command = [&](llvm::StringRef Relative) {
305  DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
306  return DirectoryBasedGlobalCompilationDatabase(Opts)
307  .getCompileCommand(testPath(Relative))
308  .getValueOr(tooling::CompileCommand())
309  .CommandLine;
310  };
311  EXPECT_THAT(Command("x/foo.cc"), IsEmpty());
312  const char *const CDB =
313  R"cdb(
314  [
315  {
316  "file": "{0}/x/foo.cc",
317  "command": "clang -DXYZZY {0}/x/foo.cc",
318  "directory": "{0}",
319  },
320  {
321  "file": "{0}/bar.cc",
322  "command": "clang -DXYZZY {0}/bar.cc",
323  "directory": "{0}",
324  }
325  ]
326  )cdb";
327  FS.Files[testPath("x/build/compile_commands.json")] =
328  llvm::formatv(CDB, llvm::sys::path::convert_to_slash(testRoot()));
329  EXPECT_THAT(Command("x/foo.cc"), Contains("-DXYZZY"));
330  EXPECT_THAT(Command("bar.cc"), IsEmpty())
331  << "x/build/compile_flags.json only applicable to x/";
332 }
333 
334 TEST(GlobalCompilationDatabaseTest, CompileFlagsDirectory) {
335  MockFS FS;
336  FS.Files[testPath("x/compile_flags.txt")] = "-DFOO";
337  DirectoryBasedGlobalCompilationDatabase CDB(FS);
338  auto Commands = CDB.getCompileCommand(testPath("x/y.cpp"));
339  ASSERT_TRUE(Commands.hasValue());
340  EXPECT_THAT(Commands.getValue().CommandLine, Contains("-DFOO"));
341  // Make sure we pick the right working directory.
342  EXPECT_EQ(testPath("x"), Commands.getValue().Directory);
343 }
344 
345 MATCHER_P(hasArg, Flag, "") {
346  if (!arg.hasValue()) {
347  *result_listener << "command is null";
348  return false;
349  }
350  if (!llvm::is_contained(arg->CommandLine, Flag)) {
351  *result_listener << "flags are " << printArgv(arg->CommandLine);
352  return false;
353  }
354  return true;
355 }
356 
357 TEST(GlobalCompilationDatabaseTest, Config) {
358  MockFS FS;
359  FS.Files[testPath("x/compile_flags.txt")] = "-DX";
360  FS.Files[testPath("x/y/z/compile_flags.txt")] = "-DZ";
361 
362  Config::CDBSearchSpec Spec;
363  DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
364  Opts.ContextProvider = [&](llvm::StringRef Path) {
365  Config C;
366  C.CompileFlags.CDBSearch = Spec;
367  return Context::current().derive(Config::Key, std::move(C));
368  };
369  DirectoryBasedGlobalCompilationDatabase CDB(Opts);
370 
371  // Default ancestor behavior.
372  EXPECT_FALSE(CDB.getCompileCommand(testPath("foo.cc")));
373  EXPECT_THAT(CDB.getCompileCommand(testPath("x/foo.cc")), hasArg("-DX"));
374  EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/foo.cc")), hasArg("-DX"));
375  EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/z/foo.cc")), hasArg("-DZ"));
376 
378  EXPECT_FALSE(CDB.getCompileCommand(testPath("foo.cc")));
379  EXPECT_FALSE(CDB.getCompileCommand(testPath("x/foo.cc")));
380  EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/foo.cc")));
381  EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/z/foo.cc")));
382 
383  Spec.Policy = Config::CDBSearchSpec::FixedDir;
384  Spec.FixedCDBPath = testPath("w"); // doesn't exist
385  EXPECT_FALSE(CDB.getCompileCommand(testPath("foo.cc")));
386  EXPECT_FALSE(CDB.getCompileCommand(testPath("x/foo.cc")));
387  EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/foo.cc")));
388  EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/z/foo.cc")));
389 
390  Spec.FixedCDBPath = testPath("x/y/z");
391  EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc")), hasArg("-DZ"));
392  EXPECT_THAT(CDB.getCompileCommand(testPath("x/foo.cc")), hasArg("-DZ"));
393  EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/foo.cc")), hasArg("-DZ"));
394  EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/z/foo.cc")), hasArg("-DZ"));
395 }
396 
397 TEST(GlobalCompilationDatabaseTest, NonCanonicalFilenames) {
398  OverlayCDB DB(nullptr);
399  std::vector<std::string> DiscoveredFiles;
400  auto Sub =
401  DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
402  DiscoveredFiles = Changes;
403  });
404 
405  llvm::SmallString<128> Root(testRoot());
406  llvm::sys::path::append(Root, "build", "..", "a.cc");
407  DB.setCompileCommand(Root.str(), tooling::CompileCommand());
408  EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(testPath("a.cc")));
409  DiscoveredFiles.clear();
410 
411  llvm::SmallString<128> File(testRoot());
412  llvm::sys::path::append(File, "blabla", "..", "a.cc");
413 
414  EXPECT_TRUE(DB.getCompileCommand(File));
415  EXPECT_FALSE(DB.getProjectInfo(File));
416 }
417 
418 TEST_F(OverlayCDBTest, GetProjectInfo) {
419  OverlayCDB DB(Base.get());
420  Path File = testPath("foo.cc");
421  Path Header = testPath("foo.h");
422 
423  EXPECT_EQ(DB.getProjectInfo(File)->SourceRoot, testRoot());
424  EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot, testRoot());
425 
426  // Shouldn't change after an override.
427  DB.setCompileCommand(File, tooling::CompileCommand());
428  EXPECT_EQ(DB.getProjectInfo(File)->SourceRoot, testRoot());
429  EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot, testRoot());
430 }
431 } // namespace
432 
433 // Friend test has access to internals.
435  : public ::testing::Test {
436 protected:
437  std::shared_ptr<const tooling::CompilationDatabase>
439  llvm::StringRef Path,
440  std::chrono::steady_clock::time_point FreshTime) {
441  DirectoryBasedGlobalCompilationDatabase::CDBLookupRequest Req;
442  Req.FileName = Path;
443  Req.FreshTime = Req.FreshTimeMissing = FreshTime;
444  if (auto Result = GDB.lookupCDB(Req))
445  return std::move(Result->CDB);
446  return nullptr;
447  }
448 };
449 
450 // Matches non-null CDBs which include the specified flag.
451 MATCHER_P2(hasFlag, Flag, Path, "") {
452  if (arg == nullptr)
453  return false;
454  auto Cmds = arg->getCompileCommands(Path);
455  if (Cmds.empty()) {
456  *result_listener << "yields no commands";
457  return false;
458  }
459  if (!llvm::is_contained(Cmds.front().CommandLine, Flag)) {
460  *result_listener << "flags are: " << printArgv(Cmds.front().CommandLine);
461  return false;
462  }
463  return true;
464 }
465 
466 auto hasFlag(llvm::StringRef Flag) {
467  return hasFlag(Flag, "mock_file_name.cc");
468 }
469 
471  MockFS FS;
472  auto Stale = std::chrono::steady_clock::now() - std::chrono::minutes(1);
473  auto Fresh = std::chrono::steady_clock::now() + std::chrono::hours(24);
474 
476  FS.Files["compile_flags.txt"] = "-DROOT";
477  auto Root = lookupCDB(GDB, testPath("foo/test.cc"), Stale);
478  EXPECT_THAT(Root, hasFlag("-DROOT"));
479 
480  // Add a compilation database to a subdirectory - CDB loaded.
481  FS.Files["foo/compile_flags.txt"] = "-DFOO";
482  EXPECT_EQ(Root, lookupCDB(GDB, testPath("foo/test.cc"), Stale))
483  << "cache still valid";
484  auto Foo = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
485  EXPECT_THAT(Foo, hasFlag("-DFOO")) << "new cdb loaded";
486  EXPECT_EQ(Foo, lookupCDB(GDB, testPath("foo/test.cc"), Stale))
487  << "new cdb in cache";
488 
489  // Mtime changed, but no content change - CDB not reloaded.
490  ++FS.Timestamps["foo/compile_flags.txt"];
491  auto FooAgain = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
492  EXPECT_EQ(Foo, FooAgain) << "Same content, read but not reloaded";
493  // Content changed, but not size or mtime - CDB not reloaded.
494  FS.Files["foo/compile_flags.txt"] = "-DBAR";
495  auto FooAgain2 = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
496  EXPECT_EQ(Foo, FooAgain2) << "Same filesize, change not detected";
497  // Mtime change forces a re-read, and we notice the different content.
498  ++FS.Timestamps["foo/compile_flags.txt"];
499  auto Bar = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
500  EXPECT_THAT(Bar, hasFlag("-DBAR")) << "refreshed with mtime change";
501 
502  // Size and content both change - CDB reloaded.
503  FS.Files["foo/compile_flags.txt"] = "-DFOOBAR";
504  EXPECT_EQ(Bar, lookupCDB(GDB, testPath("foo/test.cc"), Stale))
505  << "cache still valid";
506  auto FooBar = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
507  EXPECT_THAT(FooBar, hasFlag("-DFOOBAR")) << "cdb reloaded";
508 
509  // compile_commands.json takes precedence over compile_flags.txt.
510  FS.Files["foo/compile_commands.json"] =
511  llvm::formatv(R"json([{
512  "file": "{0}/foo/mock_file.cc",
513  "command": "clang -DBAZ mock_file.cc",
514  "directory": "{0}/foo",
515  }])json",
516  llvm::sys::path::convert_to_slash(testRoot()));
517  EXPECT_EQ(FooBar, lookupCDB(GDB, testPath("foo/test.cc"), Stale))
518  << "cache still valid";
519  auto Baz = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
520  EXPECT_THAT(Baz, hasFlag("-DBAZ", testPath("foo/mock_file.cc")))
521  << "compile_commands overrides compile_flags";
522 
523  // Removing compile_commands.json reveals compile_flags.txt again.
524  // However this *does* cause a CDB reload (we cache only one CDB per dir).
525  FS.Files.erase("foo/compile_commands.json");
526  auto FoobarAgain = lookupCDB(GDB, testPath("foo/test.cc"), Fresh);
527  EXPECT_THAT(FoobarAgain, hasFlag("-DFOOBAR")) << "reloaded compile_flags";
528  EXPECT_NE(FoobarAgain, FooBar) << "CDB discarded (shadowed within directory)";
529 
530  // Removing the directory's CDB leaves the parent CDB active.
531  // The parent CDB is *not* reloaded (we cache the CDB per-directory).
532  FS.Files.erase("foo/compile_flags.txt");
533  EXPECT_EQ(Root, lookupCDB(GDB, testPath("foo/test.cc"), Fresh))
534  << "CDB retained (shadowed by another directory)";
535 }
536 
537 } // namespace clangd
538 } // namespace clang
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:90
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
clang::clangd::MATCHER_P2
MATCHER_P2(hasFlag, Flag, Path, "")
Definition: GlobalCompilationDatabaseTests.cpp:451
clang::clangd::timeoutSeconds
Deadline timeoutSeconds(llvm::Optional< double > Seconds)
Makes a deadline from a timeout in seconds. None means wait forever.
Definition: Threading.cpp:105
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::printArgv
std::string printArgv(llvm::ArrayRef< llvm::StringRef > Args)
Definition: CompileCommands.cpp:582
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
Path.h
clang::clangd::Context::current
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
clang::clangd::DirectoryBasedGlobalCompilationDatabaseCacheTest
Definition: GlobalCompilationDatabaseTests.cpp:434
Root
ASTNode Root
Definition: DumpAST.cpp:332
Outer
std::pair< Context, Canceler > Outer
Definition: CancellationTests.cpp:49
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:94
clang::clangd::GlobalCompilationDatabase::getCompileCommand
virtual llvm::Optional< tooling::CompileCommand > getCompileCommand(PathRef File) const =0
If there are any known-good commands for building this file, returns one.
Changes
tooling::Replacements Changes
Definition: Format.cpp:110
clang::clangd::DirectoryBasedGlobalCompilationDatabaseCacheTest::lookupCDB
std::shared_ptr< const tooling::CompilationDatabase > lookupCDB(const DirectoryBasedGlobalCompilationDatabase &GDB, llvm::StringRef Path, std::chrono::steady_clock::time_point FreshTime)
Definition: GlobalCompilationDatabaseTests.cpp:438
clang::clangd::Config::CDBSearchSpec::FixedDir
@ FixedDir
Definition: Config.h:56
ThreadsafeFS.h
clang::clangd::testRoot
const char * testRoot()
Definition: TestFS.cpp:74
clang::clangd::hasFlag
auto hasFlag(llvm::StringRef Flag)
Definition: GlobalCompilationDatabaseTests.cpp:466
clang::clangd::Config::CDBSearchSpec::NoCDBSearch
@ NoCDBSearch
Definition: Config.h:56
GlobalCompilationDatabase.h
clang::clangd::MockFS
Definition: TestFS.h:34
Inner
std::pair< Context, Canceler > Inner
Definition: CancellationTests.cpp:49
CommandLine
std::vector< llvm::StringRef > CommandLine
Definition: Serialization.cpp:420
TestFS.h
clang::clangd::GlobalCompilationDatabase::getFallbackCommand
virtual tooling::CompileCommand getFallbackCommand(PathRef File) const
Makes a guess at how to build a file.
Definition: GlobalCompilationDatabase.cpp:58
Bar
Definition: sample.cpp:5
Config
static cl::opt< std::string > Config("config", cl::desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:[{key:x, value: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))
Config.h
clang::clangd::Context::derive
Context derive(const Key< Type > &Key, typename std::decay< Type >::type Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive().
Definition: Context.h:119
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:29
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:31
clang::tidy::bugprone::model::MixFlags::None
@ None
Mix between the two parameters is not possible.
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Commands
static cl::list< std::string > Commands("c", cl::desc("Specify command to run"), cl::value_desc("command"), cl::cat(ClangQueryCategory))
clang::clangd::Config::Key
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:47
clang::clangd::DirectoryBasedGlobalCompilationDatabase
Gets compile args from tooling::CompilationDatabases built for parent directories.
Definition: GlobalCompilationDatabase.h:92
Matchers.h