clang-tools  11.0.0
CompileCommandsTests.cpp
Go to the documentation of this file.
1 //===-- CompileCommandsTests.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 "CompileCommands.h"
10 #include "Config.h"
11 #include "TestFS.h"
12 #include "support/Context.h"
13 
14 #include "llvm/ADT/ScopeExit.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Process.h"
19 
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 
23 namespace clang {
24 namespace clangd {
25 namespace {
26 
27 using ::testing::_;
28 using ::testing::Contains;
29 using ::testing::ElementsAre;
30 using ::testing::HasSubstr;
31 using ::testing::Not;
32 
33 // Sadly, CommandMangler::detect(), which contains much of the logic, is
34 // a bunch of untested integration glue. We test the string manipulation here
35 // assuming its results are correct.
36 
37 // Make use of all features and assert the exact command we get out.
38 // Other tests just verify presence/absence of certain args.
39 TEST(CommandMangler, Everything) {
40  auto Mangler = CommandMangler::forTests();
41  Mangler.ClangPath = testPath("fake/clang");
42  Mangler.ResourceDir = testPath("fake/resources");
43  Mangler.Sysroot = testPath("fake/sysroot");
44  std::vector<std::string> Cmd = {"clang++", "-Xclang", "-load", "-Xclang",
45  "plugin", "-MF", "dep", "foo.cc"};
46  Mangler.adjust(Cmd);
47  EXPECT_THAT(Cmd, ElementsAre(testPath("fake/clang++"), "foo.cc",
48  "-fsyntax-only",
49  "-resource-dir=" + testPath("fake/resources"),
50  "-isysroot", testPath("fake/sysroot")));
51 }
52 
53 TEST(CommandMangler, ResourceDir) {
54  auto Mangler = CommandMangler::forTests();
55  Mangler.ResourceDir = testPath("fake/resources");
56  std::vector<std::string> Cmd = {"clang++", "foo.cc"};
57  Mangler.adjust(Cmd);
58  EXPECT_THAT(Cmd, Contains("-resource-dir=" + testPath("fake/resources")));
59 }
60 
61 TEST(CommandMangler, Sysroot) {
62  auto Mangler = CommandMangler::forTests();
63  Mangler.Sysroot = testPath("fake/sysroot");
64 
65  std::vector<std::string> Cmd = {"clang++", "foo.cc"};
66  Mangler.adjust(Cmd);
67  EXPECT_THAT(llvm::join(Cmd, " "),
68  HasSubstr("-isysroot " + testPath("fake/sysroot")));
69 }
70 
71 TEST(CommandMangler, StripPlugins) {
72  auto Mangler = CommandMangler::forTests();
73  std::vector<std::string> Cmd = {"clang++", "-Xclang", "-load",
74  "-Xclang", "plugin", "foo.cc"};
75  Mangler.adjust(Cmd);
76  for (const char* Stripped : {"-Xclang", "-load", "plugin"})
77  EXPECT_THAT(Cmd, Not(Contains(Stripped)));
78 }
79 
80 TEST(CommandMangler, StripOutput) {
81  auto Mangler = CommandMangler::forTests();
82  std::vector<std::string> Cmd = {"clang++", "-MF", "dependency", "-c",
83  "foo.cc"};
84  Mangler.adjust(Cmd);
85  for (const char* Stripped : {"-MF", "dependency"})
86  EXPECT_THAT(Cmd, Not(Contains(Stripped)));
87 }
88 
89 TEST(CommandMangler, StripShowIncludes) {
90  auto Mangler = CommandMangler::forTests();
91  std::vector<std::string> Cmd = {"clang-cl", "/showIncludes", "foo.cc"};
92  Mangler.adjust(Cmd);
93  EXPECT_THAT(Cmd, Not(Contains("/showIncludes")));
94 }
95 
96 TEST(CommandMangler, StripShowIncludesUser) {
97  auto Mangler = CommandMangler::forTests();
98  std::vector<std::string> Cmd = {"clang-cl", "/showIncludes:user", "foo.cc"};
99  Mangler.adjust(Cmd);
100  EXPECT_THAT(Cmd, Not(Contains("/showIncludes:user")));
101 }
102 
103 TEST(CommandMangler, ClangPath) {
104  auto Mangler = CommandMangler::forTests();
105  Mangler.ClangPath = testPath("fake/clang");
106 
107  std::vector<std::string> Cmd = {"clang++", "foo.cc"};
108  Mangler.adjust(Cmd);
109  EXPECT_EQ(testPath("fake/clang++"), Cmd.front());
110 
111  Cmd = {"unknown-binary", "foo.cc"};
112  Mangler.adjust(Cmd);
113  EXPECT_EQ(testPath("fake/unknown-binary"), Cmd.front());
114 
115  Cmd = {testPath("path/clang++"), "foo.cc"};
116  Mangler.adjust(Cmd);
117  EXPECT_EQ(testPath("path/clang++"), Cmd.front());
118 
119  Cmd = {"foo/unknown-binary", "foo.cc"};
120  Mangler.adjust(Cmd);
121  EXPECT_EQ("foo/unknown-binary", Cmd.front());
122 }
123 
124 // Only run the PATH/symlink resolving test on unix, we need to fiddle
125 // with permissions and environment variables...
126 #ifdef LLVM_ON_UNIX
127 MATCHER(Ok, "") {
128  if (arg) {
129  *result_listener << arg.message();
130  return false;
131  }
132  return true;
133 }
134 
135 TEST(CommandMangler, ClangPathResolve) {
136  // Set up filesystem:
137  // /temp/
138  // bin/
139  // foo -> temp/lib/bar
140  // lib/
141  // bar
142  llvm::SmallString<256> TempDir;
143  ASSERT_THAT(llvm::sys::fs::createUniqueDirectory("ClangPathResolve", TempDir),
144  Ok());
145  // /var/tmp is a symlink on Mac. Resolve it so we're asserting the right path.
146  ASSERT_THAT(llvm::sys::fs::real_path(TempDir.str(), TempDir), Ok());
147  auto CleanDir = llvm::make_scope_exit(
148  [&] { llvm::sys::fs::remove_directories(TempDir); });
149  ASSERT_THAT(llvm::sys::fs::create_directory(TempDir + "/bin"), Ok());
150  ASSERT_THAT(llvm::sys::fs::create_directory(TempDir + "/lib"), Ok());
151  int FD;
152  ASSERT_THAT(llvm::sys::fs::openFileForWrite(TempDir + "/lib/bar", FD), Ok());
153  ASSERT_THAT(llvm::sys::Process::SafelyCloseFileDescriptor(FD), Ok());
154  ::chmod((TempDir + "/lib/bar").str().c_str(), 0755); // executable
155  ASSERT_THAT(
156  llvm::sys::fs::create_link(TempDir + "/lib/bar", TempDir + "/bin/foo"),
157  Ok());
158 
159  // Test the case where the driver is an absolute path to a symlink.
160  auto Mangler = CommandMangler::forTests();
161  Mangler.ClangPath = testPath("fake/clang");
162  std::vector<std::string> Cmd = {(TempDir + "/bin/foo").str(), "foo.cc"};
163  Mangler.adjust(Cmd);
164  // Directory based on resolved symlink, basename preserved.
165  EXPECT_EQ((TempDir + "/lib/foo").str(), Cmd.front());
166 
167  // Set PATH to point to temp/bin so we can find 'foo' on it.
168  ASSERT_TRUE(::getenv("PATH"));
169  auto RestorePath =
170  llvm::make_scope_exit([OldPath = std::string(::getenv("PATH"))] {
171  ::setenv("PATH", OldPath.c_str(), 1);
172  });
173  ::setenv("PATH", (TempDir + "/bin").str().c_str(), /*overwrite=*/1);
174 
175  // Test the case where the driver is a $PATH-relative path to a symlink.
176  Mangler = CommandMangler::forTests();
177  Mangler.ClangPath = testPath("fake/clang");
178  // Driver found on PATH.
179  Cmd = {"foo", "foo.cc"};
180  Mangler.adjust(Cmd);
181  // Found the symlink and resolved the path as above.
182  EXPECT_EQ((TempDir + "/lib/foo").str(), Cmd.front());
183 
184  // Symlink not resolved with -no-canonical-prefixes.
185  Cmd = {"foo", "-no-canonical-prefixes", "foo.cc"};
186  Mangler.adjust(Cmd);
187  EXPECT_EQ((TempDir + "/bin/foo").str(), Cmd.front());
188 }
189 #endif
190 
191 TEST(CommandMangler, ConfigEdits) {
192  auto Mangler = CommandMangler::forTests();
193  std::vector<std::string> Cmd = {"clang++", "foo.cc"};
194  {
195  Config Cfg;
196  Cfg.CompileFlags.Edits.push_back([](std::vector<std::string> &Argv) {
197  for (auto &Arg : Argv)
198  for (char &C : Arg)
199  C = llvm::toUpper(C);
200  });
201  Cfg.CompileFlags.Edits.push_back(
202  [](std::vector<std::string> &Argv) { Argv.push_back("--hello"); });
203  WithContextValue WithConfig(Config::Key, std::move(Cfg));
204  Mangler.adjust(Cmd);
205  }
206  // Edits are applied in given order and before other mangling.
207  EXPECT_THAT(Cmd, ElementsAre(_, "FOO.CC", "--hello", "-fsyntax-only"));
208 }
209 
210 static std::string strip(llvm::StringRef Arg, llvm::StringRef Argv) {
211  llvm::SmallVector<llvm::StringRef, 8> Parts;
212  llvm::SplitString(Argv, Parts);
213  std::vector<std::string> Args = {Parts.begin(), Parts.end()};
214  ArgStripper S;
215  S.strip(Arg);
216  S.process(Args);
217  return llvm::join(Args, " ");
218 }
219 
220 TEST(ArgStripperTest, Spellings) {
221  // May use alternate prefixes.
222  EXPECT_EQ(strip("-pedantic", "clang -pedantic foo.cc"), "clang foo.cc");
223  EXPECT_EQ(strip("-pedantic", "clang --pedantic foo.cc"), "clang foo.cc");
224  EXPECT_EQ(strip("--pedantic", "clang -pedantic foo.cc"), "clang foo.cc");
225  EXPECT_EQ(strip("--pedantic", "clang --pedantic foo.cc"), "clang foo.cc");
226  // May use alternate names.
227  EXPECT_EQ(strip("-x", "clang -x c++ foo.cc"), "clang foo.cc");
228  EXPECT_EQ(strip("-x", "clang --language=c++ foo.cc"), "clang foo.cc");
229  EXPECT_EQ(strip("--language=", "clang -x c++ foo.cc"), "clang foo.cc");
230  EXPECT_EQ(strip("--language=", "clang --language=c++ foo.cc"),
231  "clang foo.cc");
232 }
233 
234 TEST(ArgStripperTest, UnknownFlag) {
235  EXPECT_EQ(strip("-xyzzy", "clang -xyzzy foo.cc"), "clang foo.cc");
236  EXPECT_EQ(strip("-xyz*", "clang -xyzzy foo.cc"), "clang foo.cc");
237  EXPECT_EQ(strip("-xyzzy", "clang -Xclang -xyzzy foo.cc"), "clang foo.cc");
238 }
239 
240 TEST(ArgStripperTest, Xclang) {
241  // Flags may be -Xclang escaped.
242  EXPECT_EQ(strip("-ast-dump", "clang -Xclang -ast-dump foo.cc"),
243  "clang foo.cc");
244  // Args may be -Xclang escaped.
245  EXPECT_EQ(strip("-add-plugin", "clang -Xclang -add-plugin -Xclang z foo.cc"),
246  "clang foo.cc");
247 }
248 
249 TEST(ArgStripperTest, ClangCL) {
250  // /I is a synonym for -I in clang-cl mode only.
251  // Not stripped by default.
252  EXPECT_EQ(strip("-I", "clang -I /usr/inc /Interesting/file.cc"),
253  "clang /Interesting/file.cc");
254  // Stripped when invoked as clang-cl.
255  EXPECT_EQ(strip("-I", "clang-cl -I /usr/inc /Interesting/file.cc"),
256  "clang-cl");
257  // Stripped when invoked as CL.EXE
258  EXPECT_EQ(strip("-I", "CL.EXE -I /usr/inc /Interesting/file.cc"), "CL.EXE");
259  // Stripped when passed --driver-mode=cl.
260  EXPECT_EQ(strip("-I", "cc -I /usr/inc /Interesting/file.cc --driver-mode=cl"),
261  "cc --driver-mode=cl");
262 }
263 
264 TEST(ArgStripperTest, ArgStyles) {
265  // Flag
266  EXPECT_EQ(strip("-Qn", "clang -Qn foo.cc"), "clang foo.cc");
267  EXPECT_EQ(strip("-Qn", "clang -QnZ foo.cc"), "clang -QnZ foo.cc");
268  // Joined
269  EXPECT_EQ(strip("-std=", "clang -std= foo.cc"), "clang foo.cc");
270  EXPECT_EQ(strip("-std=", "clang -std=c++11 foo.cc"), "clang foo.cc");
271  // Separate
272  EXPECT_EQ(strip("-mllvm", "clang -mllvm X foo.cc"), "clang foo.cc");
273  EXPECT_EQ(strip("-mllvm", "clang -mllvmX foo.cc"), "clang -mllvmX foo.cc");
274  // RemainingArgsJoined
275  EXPECT_EQ(strip("/link", "clang-cl /link b c d foo.cc"), "clang-cl");
276  EXPECT_EQ(strip("/link", "clang-cl /linka b c d foo.cc"), "clang-cl");
277  // CommaJoined
278  EXPECT_EQ(strip("-Wl,", "clang -Wl,x,y foo.cc"), "clang foo.cc");
279  EXPECT_EQ(strip("-Wl,", "clang -Wl, foo.cc"), "clang foo.cc");
280  // MultiArg
281  EXPECT_EQ(strip("-segaddr", "clang -segaddr a b foo.cc"), "clang foo.cc");
282  EXPECT_EQ(strip("-segaddr", "clang -segaddra b foo.cc"),
283  "clang -segaddra b foo.cc");
284  // JoinedOrSeparate
285  EXPECT_EQ(strip("-G", "clang -GX foo.cc"), "clang foo.cc");
286  EXPECT_EQ(strip("-G", "clang -G X foo.cc"), "clang foo.cc");
287  // JoinedAndSeparate
288  EXPECT_EQ(strip("-plugin-arg-", "clang -cc1 -plugin-arg-X Y foo.cc"),
289  "clang -cc1 foo.cc");
290  EXPECT_EQ(strip("-plugin-arg-", "clang -cc1 -plugin-arg- Y foo.cc"),
291  "clang -cc1 foo.cc");
292 }
293 
294 TEST(ArgStripperTest, EndOfList) {
295  // When we hit the end-of-args prematurely, we don't crash.
296  // We consume the incomplete args if we've matched the target option.
297  EXPECT_EQ(strip("-I", "clang -Xclang"), "clang -Xclang");
298  EXPECT_EQ(strip("-I", "clang -Xclang -I"), "clang");
299  EXPECT_EQ(strip("-I", "clang -I -Xclang"), "clang");
300  EXPECT_EQ(strip("-I", "clang -I"), "clang");
301 }
302 
303 TEST(ArgStripperTest, Multiple) {
304  ArgStripper S;
305  S.strip("-o");
306  S.strip("-c");
307  std::vector<std::string> Args = {"clang", "-o", "foo.o", "foo.cc", "-c"};
308  S.process(Args);
309  EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
310 }
311 
312 TEST(ArgStripperTest, Warning) {
313  {
314  // -W is a flag name
315  ArgStripper S;
316  S.strip("-W");
317  std::vector<std::string> Args = {"clang", "-Wfoo", "-Wno-bar", "-Werror",
318  "foo.cc"};
319  S.process(Args);
320  EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
321  }
322  {
323  // -Wfoo is not a flag name, matched literally.
324  ArgStripper S;
325  S.strip("-Wunused");
326  std::vector<std::string> Args = {"clang", "-Wunused", "-Wno-unused",
327  "foo.cc"};
328  S.process(Args);
329  EXPECT_THAT(Args, ElementsAre("clang", "-Wno-unused", "foo.cc"));
330  }
331 }
332 
333 TEST(ArgStripperTest, Define) {
334  {
335  // -D is a flag name
336  ArgStripper S;
337  S.strip("-D");
338  std::vector<std::string> Args = {"clang", "-Dfoo", "-Dbar=baz", "foo.cc"};
339  S.process(Args);
340  EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
341  }
342  {
343  // -Dbar is not: matched literally
344  ArgStripper S;
345  S.strip("-Dbar");
346  std::vector<std::string> Args = {"clang", "-Dfoo", "-Dbar=baz", "foo.cc"};
347  S.process(Args);
348  EXPECT_THAT(Args, ElementsAre("clang", "-Dfoo", "-Dbar=baz", "foo.cc"));
349  S.strip("-Dfoo");
350  S.process(Args);
351  EXPECT_THAT(Args, ElementsAre("clang", "-Dbar=baz", "foo.cc"));
352  S.strip("-Dbar=*");
353  S.process(Args);
354  EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
355  }
356 }
357 
358 TEST(ArgStripperTest, OrderDependent) {
359  ArgStripper S;
360  // If -include is stripped first, we see -pch as its arg and foo.pch remains.
361  // To get this case right, we must process -include-pch first.
362  S.strip("-include");
363  S.strip("-include-pch");
364  std::vector<std::string> Args = {"clang", "-include-pch", "foo.pch",
365  "foo.cc"};
366  S.process(Args);
367  EXPECT_THAT(Args, ElementsAre("clang", "foo.cc"));
368 }
369 
370 } // namespace
371 } // namespace clangd
372 } // namespace clang
373 
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:704
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::tidy::cppcoreguidelines::join
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
Definition: SpecialMemberFunctionsCheck.cpp:83
CompileCommands.h
TestFS.h
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
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Config::Key
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:44
clang::clangd::CommandMangler::forTests
static CommandMangler forTests()
Definition: CompileCommands.cpp:185
clang::clangd::MATCHER
MATCHER(Declared, "")
Definition: BackgroundIndexTests.cpp:31
Context.h