50#include "llvm/ADT/ArrayRef.h"
51#include "llvm/ADT/DenseMap.h"
52#include "llvm/ADT/StringExtras.h"
53#include "llvm/Option/ArgList.h"
54#include "llvm/Option/OptTable.h"
55#include "llvm/Support/Debug.h"
56#include "llvm/Support/Path.h"
57#include "llvm/Support/StringSaver.h"
58#include "llvm/Support/raw_ostream.h"
67namespace path = llvm::sys::path;
70size_t matchingPrefix(StringRef L, StringRef R) {
71 size_t Limit = std::min(L.size(), R.size());
72 for (
size_t I = 0; I < Limit; ++I)
80template <
bool Prefix>
struct Less {
81 bool operator()(StringRef Key, std::pair<StringRef, size_t>
Value)
const {
82 StringRef
V = Prefix ?
Value.first.substr(0, Key.size()) :
Value.first;
85 bool operator()(std::pair<StringRef, size_t>
Value, StringRef Key)
const {
86 StringRef
V = Prefix ?
Value.first.substr(0, Key.size()) :
Value.first;
107 case types::TY_CHeader:
110 case types::TY_ObjCHeader:
111 return types::TY_ObjC;
113 case types::TY_CXXHeader:
114 return types::TY_CXX;
115 case types::TY_ObjCXX:
116 case types::TY_ObjCXXHeader:
117 return types::TY_ObjCXX;
119 case types::TY_CUDA_DEVICE:
120 return types::TY_CUDA;
127struct TransferableCommand {
137 TransferableCommand(CompileCommand
C)
139 std::vector<std::string> OldArgs = std::move(
Cmd.CommandLine);
140 Cmd.CommandLine.clear();
143 llvm::opt::InputArgList ArgList;
146 for (
const std::string &S : OldArgs)
147 TmpArgv.push_back(S.c_str());
151 ArgList = {TmpArgv.begin(), TmpArgv.end()};
159 if (!OldArgs.empty())
160 Cmd.CommandLine.emplace_back(OldArgs.front());
161 for (
unsigned Pos = 1; Pos < OldArgs.size();) {
162 using namespace driver::options;
164 const unsigned OldPos = Pos;
165 std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
167 ClangCLMode ? CoreOption | CLOption | CLDXCOption : 0,
173 const llvm::opt::Option &Opt = Arg->getOption();
176 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
178 Opt.matches(OPT__SLASH_Fe) ||
179 Opt.matches(OPT__SLASH_Fi) ||
180 Opt.matches(OPT__SLASH_Fo))))
184 if (Opt.matches(OPT__DASH_DASH))
188 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
194 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
200 Cmd.CommandLine.insert(
Cmd.CommandLine.end(),
201 OldArgs.data() + OldPos, OldArgs.data() + Pos);
215 CompileCommand transferTo(StringRef
Filename) && {
216 CompileCommand
Result = std::move(Cmd);
220 auto TargetType = guessType(
Filename, &TypeCertain);
222 if ((!TargetType || !TypeCertain) && Type) {
230 const StringRef Flag = toCLFlag(TargetType);
232 Result.CommandLine.push_back(std::string(Flag));
234 Result.CommandLine.push_back(
"-x");
241 Result.CommandLine.emplace_back((
242 llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") +
245 Result.CommandLine.push_back(
"--");
257 return types::TY_CXX;
259 return types::TY_ObjC;
261 return types::TY_ObjCXX;
268 static StringRef toCLFlag(
types::ID Type) {
271 case types::TY_CHeader:
274 case types::TY_CXXHeader:
282 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
283 const llvm::opt::Option &Opt = Arg.getOption();
284 using namespace driver::options;
286 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
288 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
289 return types::TY_CXX;
291 if (Opt.matches(driver::options::OPT_x))
298 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
299 using namespace driver::options;
300 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
319 FileIndex(std::vector<std::string> Files)
320 : OriginalPaths(
std::move(Files)), Strings(Arena) {
322 llvm::sort(OriginalPaths);
323 Paths.reserve(OriginalPaths.size());
324 Types.reserve(OriginalPaths.size());
325 Stems.reserve(OriginalPaths.size());
326 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
327 StringRef Path = Strings.save(StringRef(OriginalPaths[I]).lower());
329 Paths.emplace_back(Path, I);
330 Types.push_back(foldType(guessType(OriginalPaths[I])));
331 Stems.emplace_back(sys::path::stem(Path), I);
332 auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
333 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
334 if (Dir->size() > ShortDirectorySegment)
335 Components.emplace_back(*Dir, I);
339 llvm::sort(Components);
342 bool empty()
const {
return Paths.empty(); }
347 StringRef chooseProxy(StringRef OriginalFilename,
349 assert(!empty() &&
"need at least one candidate!");
350 std::string
Filename = OriginalFilename.lower();
351 auto Candidates = scoreCandidates(
Filename);
352 std::pair<size_t, int> Best =
353 pickWinner(Candidates,
Filename, PreferLanguage);
357 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
358 <<
" as proxy for " << OriginalFilename <<
" preferring "
362 <<
" score=" << Best.second <<
"\n");
363 return OriginalPaths[Best.first];
367 using SubstringAndIndex = std::pair<StringRef, size_t>;
371 constexpr static int DirectorySegmentsIndexed = 4;
372 constexpr static int DirectorySegmentsQueried = 2;
373 constexpr static int ShortDirectorySegment = 1;
377 DenseMap<size_t, int> scoreCandidates(StringRef
Filename)
const {
381 StringRef Stem = sys::path::stem(
Filename);
383 llvm::StringRef Prefix;
384 auto Dir = ++sys::path::rbegin(
Filename),
386 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
387 if (Dir->size() > ShortDirectorySegment)
388 Dirs.push_back(*Dir);
389 Prefix =
Filename.substr(0, Dir - DirEnd);
393 DenseMap<size_t, int> Candidates;
395 for (
const auto &Entry : Range)
396 Candidates[Entry.second] += Points;
400 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
401 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
404 for (StringRef Dir : Dirs)
405 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
407 if (sys::path::root_directory(Prefix) != Prefix)
408 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
414 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
417 struct ScoredCandidate {
424 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
425 for (
const auto &Candidate : Candidates) {
427 S.Index = Candidate.first;
429 PreferredLanguage == Types[S.Index];
430 S.Points = Candidate.second;
431 if (!S.Preferred && Best.Preferred)
433 if (S.Preferred == Best.Preferred) {
434 if (S.Points < Best.Points)
436 if (S.Points == Best.Points) {
437 S.PrefixLength = matchingPrefix(
Filename, Paths[S.Index].first);
438 if (S.PrefixLength < Best.PrefixLength)
441 if (S.PrefixLength == Best.PrefixLength)
442 if (S.Index > Best.Index)
448 S.PrefixLength = matchingPrefix(
Filename, Paths[S.Index].first);
453 if (Best.Index ==
size_t(-1))
454 return {longestMatch(
Filename, Paths).second, 0};
455 return {Best.Index, Best.Points};
460 template <
bool Prefix>
464 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
470 SubstringAndIndex longestMatch(StringRef Key,
472 assert(!Idx.empty());
474 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
475 if (It == Idx.begin())
480 size_t Prefix = matchingPrefix(Key, It->first);
481 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
482 return Prefix > PrevPrefix ? *It : *--It;
486 std::vector<std::string> OriginalPaths;
487 BumpPtrAllocator Arena;
491 std::vector<SubstringAndIndex> Paths;
494 std::vector<types::ID> Types;
495 std::vector<SubstringAndIndex> Stems;
496 std::vector<SubstringAndIndex> Components;
502class InterpolatingCompilationDatabase :
public CompilationDatabase {
504 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
505 : Inner(
std::move(Inner)), Index(this->Inner->getAllFiles()) {}
507 std::vector<CompileCommand>
508 getCompileCommands(StringRef
Filename)
const override {
509 auto Known = Inner->getCompileCommands(
Filename);
510 if (Index.empty() || !Known.empty())
517 Inner->getCompileCommands(Index.chooseProxy(
Filename, foldType(Lang)));
518 if (ProxyCommands.empty())
523 std::vector<std::string> getAllFiles()
const override {
524 return Inner->getAllFiles();
527 std::vector<CompileCommand> getAllCompileCommands()
const override {
528 return Inner->getAllCompileCommands();
532 std::unique_ptr<CompilationDatabase> Inner;
538std::unique_ptr<CompilationDatabase>
540 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
545 return TransferableCommand(std::move(
Cmd)).transferTo(
Filename);
static std::string getName(const CallEvent &Call)
ID lookupTypeForTypeSpecifier(const char *Name)
lookupTypeForTypSpecifier - Lookup the type to use for a user specified type name.
bool onlyPrecompileType(ID Id)
onlyPrecompileType - Should this type only be precompiled.
const char * getTypeName(ID Id)
getTypeName - Return the name of the type for Id.
ID lookupHeaderTypeForSourceType(ID Id)
Lookup header file input type that corresponds to given source file type (used for clang-cl emulation...
ID lookupTypeForExtension(llvm::StringRef Ext)
lookupTypeForExtension - Lookup the type to use for the file extension Ext.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
const llvm::opt::OptTable & getDriverOptTable()
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Language
The language for the input, used to select and validate the language standard and possible actions.
@ C
Languages that the frontend can parse and compile.
@ Result
The result type of a method or function.
YAML serialization mapping.
__SIZE_TYPE__ size_t
The unsigned integer type of the result of the sizeof operator.
static const LangStandard & getLangStandardForKind(Kind K)
static Kind getLangKind(StringRef Name)