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 llvm::opt::Visibility(
ClangCLMode ? CLOption : ClangOption)));
172 const llvm::opt::Option &Opt = Arg->getOption();
175 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
177 Opt.matches(OPT__SLASH_Fe) ||
178 Opt.matches(OPT__SLASH_Fi) ||
179 Opt.matches(OPT__SLASH_Fo))))
183 if (Opt.matches(OPT__DASH_DASH))
187 if (
const auto GivenType = tryParseTypeArg(*Arg)) {
193 if (
const auto GivenStd = tryParseStdArg(*Arg)) {
199 Cmd.CommandLine.insert(
Cmd.CommandLine.end(),
200 OldArgs.data() + OldPos, OldArgs.data() + Pos);
214 CompileCommand transferTo(StringRef
Filename) && {
215 CompileCommand
Result = std::move(Cmd);
219 auto TargetType = guessType(
Filename, &TypeCertain);
221 if ((!TargetType || !TypeCertain) && Type) {
229 const StringRef Flag = toCLFlag(TargetType);
231 Result.CommandLine.push_back(std::string(Flag));
233 Result.CommandLine.push_back(
"-x");
240 Result.CommandLine.emplace_back((
241 llvm::Twine(ClangCLMode ?
"/std:" :
"-std=") +
244 Result.CommandLine.push_back(
"--");
256 return types::TY_CXX;
258 return types::TY_ObjC;
260 return types::TY_ObjCXX;
267 static StringRef toCLFlag(
types::ID Type) {
270 case types::TY_CHeader:
273 case types::TY_CXXHeader:
281 std::optional<types::ID> tryParseTypeArg(
const llvm::opt::Arg &Arg) {
282 const llvm::opt::Option &Opt = Arg.getOption();
283 using namespace driver::options;
285 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
287 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
288 return types::TY_CXX;
290 if (Opt.matches(driver::options::OPT_x))
297 std::optional<LangStandard::Kind> tryParseStdArg(
const llvm::opt::Arg &Arg) {
298 using namespace driver::options;
299 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
318 FileIndex(std::vector<std::string> Files)
319 : OriginalPaths(
std::move(Files)), Strings(Arena) {
321 llvm::sort(OriginalPaths);
322 Paths.reserve(OriginalPaths.size());
323 Types.reserve(OriginalPaths.size());
324 Stems.reserve(OriginalPaths.size());
325 for (
size_t I = 0; I < OriginalPaths.size(); ++I) {
326 StringRef
Path = Strings.save(StringRef(OriginalPaths[I]).lower());
328 Paths.emplace_back(
Path, I);
329 Types.push_back(foldType(guessType(OriginalPaths[I])));
330 Stems.emplace_back(sys::path::stem(
Path), I);
331 auto Dir = ++sys::path::rbegin(
Path), DirEnd = sys::path::rend(
Path);
332 for (
int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
333 if (Dir->size() > ShortDirectorySegment)
334 Components.emplace_back(*Dir, I);
338 llvm::sort(Components);
341 bool empty()
const {
return Paths.empty(); }
346 StringRef chooseProxy(StringRef OriginalFilename,
348 assert(!empty() &&
"need at least one candidate!");
349 std::string
Filename = OriginalFilename.lower();
350 auto Candidates = scoreCandidates(
Filename);
351 std::pair<size_t, int> Best =
352 pickWinner(Candidates,
Filename, PreferLanguage);
356 llvm::dbgs() <<
"interpolate: chose " << OriginalPaths[Best.first]
357 <<
" as proxy for " << OriginalFilename <<
" preferring "
361 <<
" score=" << Best.second <<
"\n");
362 return OriginalPaths[Best.first];
366 using SubstringAndIndex = std::pair<StringRef, size_t>;
370 constexpr static int DirectorySegmentsIndexed = 4;
371 constexpr static int DirectorySegmentsQueried = 2;
372 constexpr static int ShortDirectorySegment = 1;
376 DenseMap<size_t, int> scoreCandidates(StringRef
Filename)
const {
380 StringRef Stem = sys::path::stem(
Filename);
382 llvm::StringRef Prefix;
383 auto Dir = ++sys::path::rbegin(
Filename),
385 for (
int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
386 if (Dir->size() > ShortDirectorySegment)
387 Dirs.push_back(*Dir);
388 Prefix =
Filename.substr(0, Dir - DirEnd);
392 DenseMap<size_t, int> Candidates;
394 for (
const auto &Entry : Range)
395 Candidates[Entry.second] += Points;
399 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
400 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
403 for (StringRef Dir : Dirs)
404 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
406 if (sys::path::root_directory(Prefix) != Prefix)
407 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
413 std::pair<size_t, int> pickWinner(
const DenseMap<size_t, int> &Candidates,
416 struct ScoredCandidate {
423 ScoredCandidate Best = {
size_t(-1),
false, 0, 0};
424 for (
const auto &Candidate : Candidates) {
426 S.Index = Candidate.first;
428 PreferredLanguage == Types[S.Index];
429 S.Points = Candidate.second;
430 if (!S.Preferred && Best.Preferred)
432 if (S.Preferred == Best.Preferred) {
433 if (S.Points < Best.Points)
435 if (S.Points == Best.Points) {
436 S.PrefixLength = matchingPrefix(
Filename, Paths[S.Index].first);
437 if (S.PrefixLength < Best.PrefixLength)
440 if (S.PrefixLength == Best.PrefixLength)
441 if (S.Index > Best.Index)
447 S.PrefixLength = matchingPrefix(
Filename, Paths[S.Index].first);
452 if (Best.Index ==
size_t(-1))
453 return {longestMatch(
Filename, Paths).second, 0};
454 return {Best.Index, Best.Points};
459 template <
bool Prefix>
463 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
469 SubstringAndIndex longestMatch(StringRef Key,
471 assert(!Idx.empty());
473 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
474 if (It == Idx.begin())
479 size_t Prefix = matchingPrefix(Key, It->first);
480 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
481 return Prefix > PrevPrefix ? *It : *--It;
485 std::vector<std::string> OriginalPaths;
486 BumpPtrAllocator Arena;
490 std::vector<SubstringAndIndex> Paths;
493 std::vector<types::ID> Types;
494 std::vector<SubstringAndIndex> Stems;
495 std::vector<SubstringAndIndex> Components;
501class InterpolatingCompilationDatabase :
public CompilationDatabase {
503 InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
504 : Inner(
std::move(Inner)), Index(this->Inner->getAllFiles()) {}
506 std::vector<CompileCommand>
507 getCompileCommands(StringRef
Filename)
const override {
508 auto Known = Inner->getCompileCommands(
Filename);
509 if (Index.empty() || !Known.empty())
516 Inner->getCompileCommands(Index.chooseProxy(
Filename, foldType(Lang)));
517 if (ProxyCommands.empty())
522 std::vector<std::string> getAllFiles()
const override {
523 return Inner->getAllFiles();
526 std::vector<CompileCommand> getAllCompileCommands()
const override {
527 return Inner->getAllCompileCommands();
531 std::unique_ptr<CompilationDatabase> Inner;
537std::unique_ptr<CompilationDatabase>
539 return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
544 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.
The JSON file list parser is used to communicate input to InstallAPI.
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.
Diagnostic wrappers for TextAPI types for error reporting.
static const LangStandard & getLangStandardForKind(Kind K)
static Kind getLangKind(StringRef Name)