Coverage Report

Created: 2020-02-25 14:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/CompilationDatabase.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- CompilationDatabase.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
//  This file contains implementations of the CompilationDatabase base class
10
//  and the FixedCompilationDatabase.
11
//
12
//  FIXME: Various functions that take a string &ErrorMessage should be upgraded
13
//  to Expected.
14
//
15
//===----------------------------------------------------------------------===//
16
17
#include "clang/Tooling/CompilationDatabase.h"
18
#include "clang/Basic/Diagnostic.h"
19
#include "clang/Basic/DiagnosticIDs.h"
20
#include "clang/Basic/DiagnosticOptions.h"
21
#include "clang/Basic/LLVM.h"
22
#include "clang/Driver/Action.h"
23
#include "clang/Driver/Compilation.h"
24
#include "clang/Driver/Driver.h"
25
#include "clang/Driver/DriverDiagnostic.h"
26
#include "clang/Driver/Job.h"
27
#include "clang/Frontend/TextDiagnosticPrinter.h"
28
#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
29
#include "clang/Tooling/Tooling.h"
30
#include "llvm/ADT/ArrayRef.h"
31
#include "llvm/ADT/IntrusiveRefCntPtr.h"
32
#include "llvm/ADT/STLExtras.h"
33
#include "llvm/ADT/SmallString.h"
34
#include "llvm/ADT/SmallVector.h"
35
#include "llvm/ADT/StringRef.h"
36
#include "llvm/Option/Arg.h"
37
#include "llvm/Support/Casting.h"
38
#include "llvm/Support/Compiler.h"
39
#include "llvm/Support/ErrorOr.h"
40
#include "llvm/Support/Host.h"
41
#include "llvm/Support/LineIterator.h"
42
#include "llvm/Support/MemoryBuffer.h"
43
#include "llvm/Support/Path.h"
44
#include "llvm/Support/raw_ostream.h"
45
#include <algorithm>
46
#include <cassert>
47
#include <cstring>
48
#include <iterator>
49
#include <memory>
50
#include <sstream>
51
#include <string>
52
#include <system_error>
53
#include <utility>
54
#include <vector>
55
56
using namespace clang;
57
using namespace tooling;
58
59
LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
60
61
620
CompilationDatabase::~CompilationDatabase() = default;
62
63
std::unique_ptr<CompilationDatabase>
64
CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
65
62
                                       std::string &ErrorMessage) {
66
62
  llvm::raw_string_ostream ErrorStream(ErrorMessage);
67
62
  for (CompilationDatabasePluginRegistry::iterator
68
62
       It = CompilationDatabasePluginRegistry::begin(),
69
62
       Ie = CompilationDatabasePluginRegistry::end();
70
163
       It != Ie; 
++It101
) {
71
122
    std::string DatabaseErrorMessage;
72
122
    std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
73
122
    if (std::unique_ptr<CompilationDatabase> DB =
74
21
            Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
75
21
      return DB;
76
101
    ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
77
101
  }
78
62
  
return nullptr41
;
79
62
}
80
81
static std::unique_ptr<CompilationDatabase>
82
findCompilationDatabaseFromDirectory(StringRef Directory,
83
22
                                     std::string &ErrorMessage) {
84
22
  std::stringstream ErrorStream;
85
22
  bool HasErrorMessage = false;
86
63
  while (!Directory.empty()) {
87
61
    std::string LoadErrorMessage;
88
61
89
61
    if (std::unique_ptr<CompilationDatabase> DB =
90
20
            CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
91
20
      return DB;
92
41
93
41
    if (!HasErrorMessage) {
94
10
      ErrorStream << "No compilation database found in " << Directory.str()
95
10
                  << " or any parent directory\n" << LoadErrorMessage;
96
10
      HasErrorMessage = true;
97
10
    }
98
41
99
41
    Directory = llvm::sys::path::parent_path(Directory);
100
41
  }
101
22
  ErrorMessage = ErrorStream.str();
102
2
  return nullptr;
103
22
}
104
105
std::unique_ptr<CompilationDatabase>
106
CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
107
13
                                          std::string &ErrorMessage) {
108
13
  SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
109
13
  StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
110
13
111
13
  std::unique_ptr<CompilationDatabase> DB =
112
13
      findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
113
13
114
13
  if (!DB)
115
2
    ErrorMessage = ("Could not auto-detect compilation database for file \"" +
116
2
                   SourceFile + "\"\n" + ErrorMessage).str();
117
13
  return DB;
118
13
}
119
120
std::unique_ptr<CompilationDatabase>
121
CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
122
9
                                             std::string &ErrorMessage) {
123
9
  SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
124
9
125
9
  std::unique_ptr<CompilationDatabase> DB =
126
9
      findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
127
9
128
9
  if (!DB)
129
0
    ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
130
0
                   SourceDir + "\"\n" + ErrorMessage).str();
131
9
  return DB;
132
9
}
133
134
1
std::vector<CompileCommand> CompilationDatabase::getAllCompileCommands() const {
135
1
  std::vector<CompileCommand> Result;
136
1
  for (const auto &File : getAllFiles()) {
137
0
    auto C = getCompileCommands(File);
138
0
    std::move(C.begin(), C.end(), std::back_inserter(Result));
139
0
  }
140
1
  return Result;
141
1
}
142
143
122
CompilationDatabasePlugin::~CompilationDatabasePlugin() = default;
144
145
namespace {
146
147
// Helper for recursively searching through a chain of actions and collecting
148
// all inputs, direct and indirect, of compile jobs.
149
struct CompileJobAnalyzer {
150
  SmallVector<std::string, 2> Inputs;
151
152
122
  void run(const driver::Action *A) {
153
122
    runImpl(A, false);
154
122
  }
155
156
private:
157
608
  void runImpl(const driver::Action *A, bool Collect) {
158
608
    bool CollectChildren = Collect;
159
608
    switch (A->getKind()) {
160
123
    case driver::Action::CompileJobClass:
161
123
      CollectChildren = true;
162
123
      break;
163
0
164
123
    case driver::Action::InputClass:
165
123
      if (Collect) {
166
123
        const auto *IA = cast<driver::InputAction>(A);
167
123
        Inputs.push_back(std::string(IA->getInputArg().getSpelling()));
168
123
      }
169
123
      break;
170
0
171
362
    default:
172
362
      // Don't care about others
173
362
      break;
174
608
    }
175
608
176
608
    for (const driver::Action *AI : A->inputs())
177
486
      runImpl(AI, CollectChildren);
178
608
  }
179
};
180
181
// Special DiagnosticConsumer that looks for warn_drv_input_file_unused
182
// diagnostics from the driver and collects the option strings for those unused
183
// options.
184
class UnusedInputDiagConsumer : public DiagnosticConsumer {
185
public:
186
118
  UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {}
187
188
  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
189
1
                        const Diagnostic &Info) override {
190
1
    if (Info.getID() == diag::warn_drv_input_file_unused) {
191
1
      // Arg 1 for this diagnostic is the option that didn't get used.
192
1
      UnusedInputs.push_back(Info.getArgStdStr(0));
193
1
    } else 
if (0
DiagLevel >= DiagnosticsEngine::Error0
) {
194
0
      // If driver failed to create compilation object, show the diagnostics
195
0
      // to user.
196
0
      Other.HandleDiagnostic(DiagLevel, Info);
197
0
    }
198
1
  }
199
200
  DiagnosticConsumer &Other;
201
  SmallVector<std::string, 2> UnusedInputs;
202
};
203
204
// Unary functor for asking "Given a StringRef S1, does there exist a string
205
// S2 in Arr where S1 == S2?"
206
struct MatchesAny {
207
236
  MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {}
208
209
713
  bool operator() (StringRef S) {
210
1.03k
    for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; 
++I324
)
211
446
      if (*I == S)
212
122
        return true;
213
713
    
return false591
;
214
713
  }
215
216
private:
217
  ArrayRef<std::string> Arr;
218
};
219
220
// Filter of tools unused flags such as -no-integrated-as and -Wa,*.
221
// They are not used for syntax checking, and could confuse targets
222
// which don't support these options.
223
struct FilterUnusedFlags {
224
423
  bool operator() (StringRef S) {
225
423
    return (S == "-no-integrated-as") || 
S.startswith("-Wa,")418
;
226
423
  }
227
};
228
229
261
std::string GetClangToolCommand() {
230
261
  static int Dummy;
231
261
  std::string ClangExecutable =
232
261
      llvm::sys::fs::getMainExecutable("clang", (void *)&Dummy);
233
261
  SmallString<128> ClangToolPath;
234
261
  ClangToolPath = llvm::sys::path::parent_path(ClangExecutable);
235
261
  llvm::sys::path::append(ClangToolPath, "clang-tool");
236
261
  return std::string(ClangToolPath.str());
237
261
}
238
239
} // namespace
240
241
/// Strips any positional args and possible argv[0] from a command-line
242
/// provided by the user to construct a FixedCompilationDatabase.
243
///
244
/// FixedCompilationDatabase requires a command line to be in this format as it
245
/// constructs the command line for each file by appending the name of the file
246
/// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
247
/// start of the command line although its value is not important as it's just
248
/// ignored by the Driver invoked by the ClangTool using the
249
/// FixedCompilationDatabase.
250
///
251
/// FIXME: This functionality should probably be made available by
252
/// clang::driver::Driver although what the interface should look like is not
253
/// clear.
254
///
255
/// \param[in] Args Args as provided by the user.
256
/// \return Resulting stripped command line.
257
///          \li true if successful.
258
///          \li false if \c Args cannot be used for compilation jobs (e.g.
259
///          contains an option like -E or -version).
260
static bool stripPositionalArgs(std::vector<const char *> Args,
261
                                std::vector<std::string> &Result,
262
118
                                std::string &ErrorMsg) {
263
118
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
264
118
  llvm::raw_string_ostream Output(ErrorMsg);
265
118
  TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts);
266
118
  UnusedInputDiagConsumer DiagClient(DiagnosticPrinter);
267
118
  DiagnosticsEngine Diagnostics(
268
118
      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
269
118
      &*DiagOpts, &DiagClient, false);
270
118
271
118
  // The clang executable path isn't required since the jobs the driver builds
272
118
  // will not be executed.
273
118
  std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
274
118
      /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
275
118
      Diagnostics));
276
118
  NewDriver->setCheckInputsExist(false);
277
118
278
118
  // This becomes the new argv[0]. The value is used to detect libc++ include
279
118
  // dirs on Mac, it isn't used for other platforms.
280
118
  std::string Argv0 = GetClangToolCommand();
281
118
  Args.insert(Args.begin(), Argv0.c_str());
282
118
283
118
  // By adding -c, we force the driver to treat compilation as the last phase.
284
118
  // It will then issue warnings via Diagnostics about un-used options that
285
118
  // would have been used for linking. If the user provided a compiler name as
286
118
  // the original argv[0], this will be treated as a linker input thanks to
287
118
  // insertng a new argv[0] above. All un-used options get collected by
288
118
  // UnusedInputdiagConsumer and get stripped out later.
289
118
  Args.push_back("-c");
290
118
291
118
  // Put a dummy C++ file on to ensure there's at least one compile job for the
292
118
  // driver to construct. If the user specified some other argument that
293
118
  // prevents compilation, e.g. -E or something like -version, we may still end
294
118
  // up with no jobs but then this is the user's fault.
295
118
  Args.push_back("placeholder.cpp");
296
118
297
118
  Args.erase(std::remove_if(Args.begin(), Args.end(), FilterUnusedFlags()),
298
118
             Args.end());
299
118
300
118
  const std::unique_ptr<driver::Compilation> Compilation(
301
118
      NewDriver->BuildCompilation(Args));
302
118
  if (!Compilation)
303
0
    return false;
304
118
305
118
  const driver::JobList &Jobs = Compilation->getJobs();
306
118
307
118
  CompileJobAnalyzer CompileAnalyzer;
308
118
309
127
  for (const auto &Cmd : Jobs) {
310
127
    // Collect only for Assemble, Backend, and Compile jobs. If we do all jobs
311
127
    // we get duplicates since Link jobs point to Assemble jobs as inputs.
312
127
    // -flto* flags make the BackendJobClass, which still needs analyzer.
313
127
    if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass ||
314
127
        
Cmd.getSource().getKind() == driver::Action::BackendJobClass11
||
315
127
        
Cmd.getSource().getKind() == driver::Action::CompileJobClass8
) {
316
122
      CompileAnalyzer.run(&Cmd.getSource());
317
122
    }
318
127
  }
319
118
320
118
  if (CompileAnalyzer.Inputs.empty()) {
321
0
    ErrorMsg = "warning: no compile jobs found\n";
322
0
    return false;
323
0
  }
324
118
325
118
  // Remove all compilation input files from the command line. This is
326
118
  // necessary so that getCompileCommands() can construct a command line for
327
118
  // each file.
328
118
  std::vector<const char *>::iterator End = std::remove_if(
329
118
      Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
330
118
331
118
  // Remove all inputs deemed unused for compilation.
332
118
  End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
333
118
334
118
  // Remove the -c add above as well. It will be at the end right now.
335
118
  assert(strcmp(*(End - 1), "-c") == 0);
336
118
  --End;
337
118
338
118
  Result = std::vector<std::string>(Args.begin() + 1, End);
339
118
  return true;
340
118
}
341
342
std::unique_ptr<FixedCompilationDatabase>
343
FixedCompilationDatabase::loadFromCommandLine(int &Argc,
344
                                              const char *const *Argv,
345
                                              std::string &ErrorMsg,
346
144
                                              Twine Directory) {
347
144
  ErrorMsg.clear();
348
144
  if (Argc == 0)
349
1
    return nullptr;
350
143
  const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
351
143
  if (DoubleDash == Argv + Argc)
352
25
    return nullptr;
353
118
  std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
354
118
  Argc = DoubleDash - Argv;
355
118
356
118
  std::vector<std::string> StrippedArgs;
357
118
  if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg))
358
0
    return nullptr;
359
118
  return std::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
360
118
}
361
362
std::unique_ptr<FixedCompilationDatabase>
363
62
FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) {
364
62
  ErrorMsg.clear();
365
62
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
366
62
      llvm::MemoryBuffer::getFile(Path);
367
62
  if (std::error_code Result = File.getError()) {
368
60
    ErrorMsg = "Error while opening fixed database: " + Result.message();
369
60
    return nullptr;
370
60
  }
371
2
  std::vector<std::string> Args{llvm::line_iterator(**File),
372
2
                                llvm::line_iterator()};
373
2
  return std::make_unique<FixedCompilationDatabase>(
374
2
      llvm::sys::path::parent_path(Path), std::move(Args));
375
2
}
376
377
FixedCompilationDatabase::
378
143
FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
379
143
  std::vector<std::string> ToolCommandLine(1, GetClangToolCommand());
380
143
  ToolCommandLine.insert(ToolCommandLine.end(),
381
143
                         CommandLine.begin(), CommandLine.end());
382
143
  CompileCommands.emplace_back(Directory, StringRef(),
383
143
                               std::move(ToolCommandLine),
384
143
                               StringRef());
385
143
}
386
387
std::vector<CompileCommand>
388
313
FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
389
313
  std::vector<CompileCommand> Result(CompileCommands);
390
313
  Result[0].CommandLine.push_back(std::string(FilePath));
391
313
  Result[0].Filename = std::string(FilePath);
392
313
  return Result;
393
313
}
394
395
namespace {
396
397
class FixedCompilationDatabasePlugin : public CompilationDatabasePlugin {
398
  std::unique_ptr<CompilationDatabase>
399
62
  loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
400
62
    SmallString<1024> DatabasePath(Directory);
401
62
    llvm::sys::path::append(DatabasePath, "compile_flags.txt");
402
62
    return FixedCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage);
403
62
  }
404
};
405
406
} // namespace
407
408
static CompilationDatabasePluginRegistry::Add<FixedCompilationDatabasePlugin>
409
X("fixed-compilation-database", "Reads plain-text flags file");
410
411
namespace clang {
412
namespace tooling {
413
414
// This anchor is used to force the linker to link in the generated object file
415
// and thus register the JSONCompilationDatabasePlugin.
416
extern volatile int JSONAnchorSource;
417
static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource;
418
419
} // namespace tooling
420
} // namespace clang