1ea9773acSSam McCall //===- InterpolatingCompilationDatabase.cpp ---------------------*- C++ -*-===//
2ea9773acSSam McCall //
3ea9773acSSam McCall //                     The LLVM Compiler Infrastructure
4ea9773acSSam McCall //
5ea9773acSSam McCall // This file is distributed under the University of Illinois Open Source
6ea9773acSSam McCall // License. See LICENSE.TXT for details.
7ea9773acSSam McCall //
8ea9773acSSam McCall //===----------------------------------------------------------------------===//
9ea9773acSSam McCall //
10ea9773acSSam McCall // InterpolatingCompilationDatabase wraps another CompilationDatabase and
11ea9773acSSam McCall // attempts to heuristically determine appropriate compile commands for files
12ea9773acSSam McCall // that are not included, such as headers or newly created files.
13ea9773acSSam McCall //
14ea9773acSSam McCall // Motivating cases include:
15ea9773acSSam McCall //   Header files that live next to their implementation files. These typically
16ea9773acSSam McCall // share a base filename. (libclang/CXString.h, libclang/CXString.cpp).
17ea9773acSSam McCall //   Some projects separate headers from includes. Filenames still typically
18ea9773acSSam McCall // match, maybe other path segments too. (include/llvm/IR/Use.h, lib/IR/Use.cc).
19ea9773acSSam McCall //   Matches are sometimes only approximate (Sema.h, SemaDecl.cpp). This goes
20ea9773acSSam McCall // for directories too (Support/Unix/Process.inc, lib/Support/Process.cpp).
21ea9773acSSam McCall //   Even if we can't find a "right" compile command, even a random one from
22ea9773acSSam McCall // the project will tend to get important flags like -I and -x right.
23ea9773acSSam McCall //
24ea9773acSSam McCall // We "borrow" the compile command for the closest available file:
25ea9773acSSam McCall //   - points are awarded if the filename matches (ignoring extension)
26ea9773acSSam McCall //   - points are awarded if the directory structure matches
27ea9773acSSam McCall //   - ties are broken by length of path prefix match
28ea9773acSSam McCall //
29ea9773acSSam McCall // The compile command is adjusted, replacing the filename and removing output
30ea9773acSSam McCall // file arguments. The -x and -std flags may be affected too.
31ea9773acSSam McCall //
32ea9773acSSam McCall // Source language is a tricky issue: is it OK to use a .c file's command
33ea9773acSSam McCall // for building a .cc file? What language is a .h file in?
34ea9773acSSam McCall //   - We only consider compile commands for c-family languages as candidates.
35ea9773acSSam McCall //   - For files whose language is implied by the filename (e.g. .m, .hpp)
36ea9773acSSam McCall //     we prefer candidates from the same language.
37ea9773acSSam McCall //     If we must cross languages, we drop any -x and -std flags.
38ea9773acSSam McCall //   - For .h files, candidates from any c-family language are acceptable.
39ea9773acSSam McCall //     We use the candidate's language, inserting  e.g. -x c++-header.
40ea9773acSSam McCall //
41ea9773acSSam McCall // This class is only useful when wrapping databases that can enumerate all
42ea9773acSSam McCall // their compile commands. If getAllFilenames() is empty, no inference occurs.
43ea9773acSSam McCall //
44ea9773acSSam McCall //===----------------------------------------------------------------------===//
45ea9773acSSam McCall 
46ea9773acSSam McCall #include "clang/Driver/Options.h"
47ea9773acSSam McCall #include "clang/Driver/Types.h"
48ea9773acSSam McCall #include "clang/Frontend/LangStandard.h"
49ea9773acSSam McCall #include "clang/Tooling/CompilationDatabase.h"
50ea9773acSSam McCall #include "llvm/ADT/DenseMap.h"
51ea9773acSSam McCall #include "llvm/ADT/StringExtras.h"
52ea9773acSSam McCall #include "llvm/ADT/StringSwitch.h"
53ea9773acSSam McCall #include "llvm/Option/ArgList.h"
54ea9773acSSam McCall #include "llvm/Option/OptTable.h"
55ea9773acSSam McCall #include "llvm/Support/Debug.h"
56ea9773acSSam McCall #include "llvm/Support/Path.h"
57ea9773acSSam McCall #include "llvm/Support/StringSaver.h"
58ea9773acSSam McCall #include "llvm/Support/raw_ostream.h"
59ea9773acSSam McCall #include <memory>
60ea9773acSSam McCall 
61ea9773acSSam McCall namespace clang {
62ea9773acSSam McCall namespace tooling {
63ea9773acSSam McCall namespace {
64ea9773acSSam McCall using namespace llvm;
65ea9773acSSam McCall namespace types = clang::driver::types;
66ea9773acSSam McCall namespace path = llvm::sys::path;
67ea9773acSSam McCall 
68ea9773acSSam McCall // The length of the prefix these two strings have in common.
69ea9773acSSam McCall size_t matchingPrefix(StringRef L, StringRef R) {
70ea9773acSSam McCall   size_t Limit = std::min(L.size(), R.size());
71ea9773acSSam McCall   for (size_t I = 0; I < Limit; ++I)
72ea9773acSSam McCall     if (L[I] != R[I])
73ea9773acSSam McCall       return I;
74ea9773acSSam McCall   return Limit;
75ea9773acSSam McCall }
76ea9773acSSam McCall 
77ea9773acSSam McCall // A comparator for searching SubstringWithIndexes with std::equal_range etc.
78ea9773acSSam McCall // Optionaly prefix semantics: compares equal if the key is a prefix.
79ea9773acSSam McCall template <bool Prefix> struct Less {
80ea9773acSSam McCall   bool operator()(StringRef Key, std::pair<StringRef, size_t> Value) const {
81ea9773acSSam McCall     StringRef V = Prefix ? Value.first.substr(0, Key.size()) : Value.first;
82ea9773acSSam McCall     return Key < V;
83ea9773acSSam McCall   }
84ea9773acSSam McCall   bool operator()(std::pair<StringRef, size_t> Value, StringRef Key) const {
85ea9773acSSam McCall     StringRef V = Prefix ? Value.first.substr(0, Key.size()) : Value.first;
86ea9773acSSam McCall     return V < Key;
87ea9773acSSam McCall   }
88ea9773acSSam McCall };
89ea9773acSSam McCall 
90ea9773acSSam McCall // Infer type from filename. If we might have gotten it wrong, set *Certain.
91ea9773acSSam McCall // *.h will be inferred as a C header, but not certain.
92ea9773acSSam McCall types::ID guessType(StringRef Filename, bool *Certain = nullptr) {
93ea9773acSSam McCall   // path::extension is ".cpp", lookupTypeForExtension wants "cpp".
94ea9773acSSam McCall   auto Lang =
95ea9773acSSam McCall       types::lookupTypeForExtension(path::extension(Filename).substr(1));
96ea9773acSSam McCall   if (Certain)
97ea9773acSSam McCall     *Certain = Lang != types::TY_CHeader && Lang != types::TY_INVALID;
98ea9773acSSam McCall   return Lang;
99ea9773acSSam McCall }
100ea9773acSSam McCall 
101ea9773acSSam McCall // Return Lang as one of the canonical supported types.
102ea9773acSSam McCall // e.g. c-header --> c; fortran --> TY_INVALID
103ea9773acSSam McCall static types::ID foldType(types::ID Lang) {
104ea9773acSSam McCall   switch (Lang) {
105ea9773acSSam McCall   case types::TY_C:
106ea9773acSSam McCall   case types::TY_CHeader:
107ea9773acSSam McCall     return types::TY_C;
108ea9773acSSam McCall   case types::TY_ObjC:
109ea9773acSSam McCall   case types::TY_ObjCHeader:
110ea9773acSSam McCall     return types::TY_ObjC;
111ea9773acSSam McCall   case types::TY_CXX:
112ea9773acSSam McCall   case types::TY_CXXHeader:
113ea9773acSSam McCall     return types::TY_CXX;
114ea9773acSSam McCall   case types::TY_ObjCXX:
115ea9773acSSam McCall   case types::TY_ObjCXXHeader:
116ea9773acSSam McCall     return types::TY_ObjCXX;
117ea9773acSSam McCall   default:
118ea9773acSSam McCall     return types::TY_INVALID;
119ea9773acSSam McCall   }
120ea9773acSSam McCall }
121ea9773acSSam McCall 
122ea9773acSSam McCall // A CompileCommand that can be applied to another file.
123ea9773acSSam McCall struct TransferableCommand {
124ea9773acSSam McCall   // Flags that should not apply to all files are stripped from CommandLine.
125ea9773acSSam McCall   CompileCommand Cmd;
126ea9773acSSam McCall   // Language detected from -x or the filename.
127ea9773acSSam McCall   types::ID Type = types::TY_INVALID;
128ea9773acSSam McCall   // Standard specified by -std.
129ea9773acSSam McCall   LangStandard::Kind Std = LangStandard::lang_unspecified;
130ea9773acSSam McCall 
131ea9773acSSam McCall   TransferableCommand(CompileCommand C)
132ea9773acSSam McCall       : Cmd(std::move(C)), Type(guessType(Cmd.Filename)) {
133ea9773acSSam McCall     std::vector<std::string> NewArgs = {Cmd.CommandLine.front()};
134ea9773acSSam McCall     // Parse the old args in order to strip out and record unwanted flags.
135ea9773acSSam McCall     auto OptTable = clang::driver::createDriverOptTable();
136ea9773acSSam McCall     std::vector<const char *> Argv;
137ea9773acSSam McCall     for (unsigned I = 1; I < Cmd.CommandLine.size(); ++I)
138ea9773acSSam McCall       Argv.push_back(Cmd.CommandLine[I].c_str());
139ea9773acSSam McCall     unsigned MissingI, MissingC;
140ea9773acSSam McCall     auto ArgList = OptTable->ParseArgs(Argv, MissingI, MissingC);
141ea9773acSSam McCall     for (const auto *Arg : ArgList) {
142ea9773acSSam McCall       const auto &option = Arg->getOption();
143ea9773acSSam McCall       // Strip input and output files.
144ea9773acSSam McCall       if (option.matches(clang::driver::options::OPT_INPUT) ||
145ea9773acSSam McCall           option.matches(clang::driver::options::OPT_o)) {
146ea9773acSSam McCall         continue;
147ea9773acSSam McCall       }
148ea9773acSSam McCall       // Strip -x, but record the overridden language.
149ea9773acSSam McCall       if (option.matches(clang::driver::options::OPT_x)) {
150ea9773acSSam McCall         for (const char *Value : Arg->getValues())
151ea9773acSSam McCall           Type = types::lookupTypeForTypeSpecifier(Value);
152ea9773acSSam McCall         continue;
153ea9773acSSam McCall       }
154ea9773acSSam McCall       // Strip --std, but record the value.
155ea9773acSSam McCall       if (option.matches(clang::driver::options::OPT_std_EQ)) {
156ea9773acSSam McCall         for (const char *Value : Arg->getValues()) {
157ea9773acSSam McCall           Std = llvm::StringSwitch<LangStandard::Kind>(Value)
158ea9773acSSam McCall #define LANGSTANDARD(id, name, lang, desc, features)                           \
159ea9773acSSam McCall   .Case(name, LangStandard::lang_##id)
160ea9773acSSam McCall #define LANGSTANDARD_ALIAS(id, alias) .Case(alias, LangStandard::lang_##id)
161ea9773acSSam McCall #include "clang/Frontend/LangStandards.def"
162ea9773acSSam McCall                     .Default(Std);
163ea9773acSSam McCall         }
164ea9773acSSam McCall         continue;
165ea9773acSSam McCall       }
166ea9773acSSam McCall       llvm::opt::ArgStringList ArgStrs;
167ea9773acSSam McCall       Arg->render(ArgList, ArgStrs);
168ea9773acSSam McCall       NewArgs.insert(NewArgs.end(), ArgStrs.begin(), ArgStrs.end());
169ea9773acSSam McCall     }
170ea9773acSSam McCall     Cmd.CommandLine = std::move(NewArgs);
171ea9773acSSam McCall 
172ea9773acSSam McCall     if (Std != LangStandard::lang_unspecified) // -std take precedence over -x
173ea9773acSSam McCall       Type = toType(LangStandard::getLangStandardForKind(Std).getLanguage());
174ea9773acSSam McCall     Type = foldType(Type);
175ea9773acSSam McCall   }
176ea9773acSSam McCall 
177ea9773acSSam McCall   // Produce a CompileCommand for \p filename, based on this one.
178ea9773acSSam McCall   CompileCommand transferTo(StringRef Filename) const {
179ea9773acSSam McCall     CompileCommand Result = Cmd;
180ea9773acSSam McCall     Result.Filename = Filename;
181ea9773acSSam McCall     bool TypeCertain;
182ea9773acSSam McCall     auto TargetType = guessType(Filename, &TypeCertain);
183ea9773acSSam McCall     // If the filename doesn't determine the language (.h), transfer with -x.
184ea9773acSSam McCall     if (!TypeCertain) {
185ea9773acSSam McCall       TargetType = types::onlyPrecompileType(TargetType) // header?
186ea9773acSSam McCall                        ? types::lookupHeaderTypeForSourceType(Type)
187ea9773acSSam McCall                        : Type;
188ea9773acSSam McCall       Result.CommandLine.push_back("-x");
189ea9773acSSam McCall       Result.CommandLine.push_back(types::getTypeName(TargetType));
190ea9773acSSam McCall     }
191ea9773acSSam McCall     // --std flag may only be transferred if the language is the same.
192ea9773acSSam McCall     // We may consider "translating" these, e.g. c++11 -> c11.
193ea9773acSSam McCall     if (Std != LangStandard::lang_unspecified && foldType(TargetType) == Type) {
194ea9773acSSam McCall       Result.CommandLine.push_back("-std");
195ea9773acSSam McCall       Result.CommandLine.push_back(
196ea9773acSSam McCall           LangStandard::getLangStandardForKind(Std).getName());
197ea9773acSSam McCall     }
198ea9773acSSam McCall     Result.CommandLine.push_back(Filename);
199ea9773acSSam McCall     return Result;
200ea9773acSSam McCall   }
201ea9773acSSam McCall 
202ea9773acSSam McCall private:
203ea9773acSSam McCall   // Map the language from the --std flag to that of the -x flag.
204ea9773acSSam McCall   static types::ID toType(InputKind::Language Lang) {
205ea9773acSSam McCall     switch (Lang) {
206ea9773acSSam McCall     case InputKind::C:
207ea9773acSSam McCall       return types::TY_C;
208ea9773acSSam McCall     case InputKind::CXX:
209ea9773acSSam McCall       return types::TY_CXX;
210ea9773acSSam McCall     case InputKind::ObjC:
211ea9773acSSam McCall       return types::TY_ObjC;
212ea9773acSSam McCall     case InputKind::ObjCXX:
213ea9773acSSam McCall       return types::TY_ObjCXX;
214ea9773acSSam McCall     default:
215ea9773acSSam McCall       return types::TY_INVALID;
216ea9773acSSam McCall     }
217ea9773acSSam McCall   }
218ea9773acSSam McCall };
219ea9773acSSam McCall 
220ea9773acSSam McCall // CommandIndex does the real work: given a filename, it produces the best
221ea9773acSSam McCall // matching TransferableCommand by matching filenames. Basic strategy:
222ea9773acSSam McCall // - Build indexes of each of the substrings we want to look up by.
223ea9773acSSam McCall //   These indexes are just sorted lists of the substrings.
224ea9773acSSam McCall // - Forward requests to the inner CDB. If it fails, we must pick a proxy.
225ea9773acSSam McCall // - Each criterion corresponds to a range lookup into the index, so we only
226ea9773acSSam McCall //   need O(log N) string comparisons to determine scores.
227ea9773acSSam McCall // - We then break ties among the candidates with the highest score.
228ea9773acSSam McCall class CommandIndex {
229ea9773acSSam McCall public:
230ea9773acSSam McCall   CommandIndex(std::vector<TransferableCommand> AllCommands)
231ea9773acSSam McCall       : Commands(std::move(AllCommands)), Strings(Arena) {
232ea9773acSSam McCall     // Sort commands by filename for determinism (index is a tiebreaker later).
233ea9773acSSam McCall     llvm::sort(
234ea9773acSSam McCall         Commands.begin(), Commands.end(),
235ea9773acSSam McCall         [](const TransferableCommand &Left, const TransferableCommand &Right) {
236ea9773acSSam McCall           return Left.Cmd.Filename < Right.Cmd.Filename;
237ea9773acSSam McCall         });
238ea9773acSSam McCall     for (size_t I = 0; I < Commands.size(); ++I) {
239ea9773acSSam McCall       StringRef Path =
240ea9773acSSam McCall           Strings.save(StringRef(Commands[I].Cmd.Filename).lower());
241ea9773acSSam McCall       Paths.push_back({Path, I});
242ea9773acSSam McCall       Stems.emplace_back(sys::path::stem(Path), I);
243ea9773acSSam McCall       auto Dir = ++sys::path::rbegin(Path), DirEnd = sys::path::rend(Path);
244ea9773acSSam McCall       for (int J = 0; J < DirectorySegmentsIndexed && Dir != DirEnd; ++J, ++Dir)
245ea9773acSSam McCall         if (Dir->size() > ShortDirectorySegment) // not trivial ones
246ea9773acSSam McCall           Components.emplace_back(*Dir, I);
247ea9773acSSam McCall     }
248ea9773acSSam McCall     llvm::sort(Paths.begin(), Paths.end());
249ea9773acSSam McCall     llvm::sort(Stems.begin(), Stems.end());
250ea9773acSSam McCall     llvm::sort(Components.begin(), Components.end());
251ea9773acSSam McCall   }
252ea9773acSSam McCall 
253ea9773acSSam McCall   bool empty() const { return Commands.empty(); }
254ea9773acSSam McCall 
255ea9773acSSam McCall   // Returns the command that best fits OriginalFilename.
256ea9773acSSam McCall   // Candidates with PreferLanguage will be chosen over others (unless it's
257ea9773acSSam McCall   // TY_INVALID, or all candidates are bad).
258ea9773acSSam McCall   const TransferableCommand &chooseProxy(StringRef OriginalFilename,
259ea9773acSSam McCall                                          types::ID PreferLanguage) const {
260ea9773acSSam McCall     assert(!empty() && "need at least one candidate!");
261ea9773acSSam McCall     std::string Filename = OriginalFilename.lower();
262ea9773acSSam McCall     auto Candidates = scoreCandidates(Filename);
263ea9773acSSam McCall     std::pair<size_t, int> Best =
264ea9773acSSam McCall         pickWinner(Candidates, Filename, PreferLanguage);
265ea9773acSSam McCall 
266ea9773acSSam McCall     DEBUG_WITH_TYPE("interpolate",
267ea9773acSSam McCall                     llvm::dbgs()
268ea9773acSSam McCall                         << "interpolate: chose "
269ea9773acSSam McCall                         << Commands[Best.first].Cmd.Filename << " as proxy for "
270ea9773acSSam McCall                         << OriginalFilename << " preferring "
271ea9773acSSam McCall                         << (PreferLanguage == types::TY_INVALID
272ea9773acSSam McCall                                 ? "none"
273ea9773acSSam McCall                                 : types::getTypeName(PreferLanguage))
274ea9773acSSam McCall                         << " score=" << Best.second << "\n");
275ea9773acSSam McCall     return Commands[Best.first];
276ea9773acSSam McCall   }
277ea9773acSSam McCall 
278ea9773acSSam McCall private:
279ea9773acSSam McCall   using SubstringAndIndex = std::pair<StringRef, size_t>;
280ea9773acSSam McCall   // Directory matching parameters: we look at the last two segments of the
281ea9773acSSam McCall   // parent directory (usually the semantically significant ones in practice).
282ea9773acSSam McCall   // We search only the last four of each candidate (for efficiency).
283ea9773acSSam McCall   constexpr static int DirectorySegmentsIndexed = 4;
284ea9773acSSam McCall   constexpr static int DirectorySegmentsQueried = 2;
285ea9773acSSam McCall   constexpr static int ShortDirectorySegment = 1; // Only look at longer names.
286ea9773acSSam McCall 
287ea9773acSSam McCall   // Award points to candidate entries that should be considered for the file.
288ea9773acSSam McCall   // Returned keys are indexes into paths, and the values are (nonzero) scores.
289ea9773acSSam McCall   DenseMap<size_t, int> scoreCandidates(StringRef Filename) const {
290ea9773acSSam McCall     // Decompose Filename into the parts we care about.
291ea9773acSSam McCall     // /some/path/complicated/project/Interesting.h
292ea9773acSSam McCall     // [-prefix--][---dir---] [-dir-] [--stem---]
293ea9773acSSam McCall     StringRef Stem = sys::path::stem(Filename);
294ea9773acSSam McCall     llvm::SmallVector<StringRef, DirectorySegmentsQueried> Dirs;
295ea9773acSSam McCall     llvm::StringRef Prefix;
296ea9773acSSam McCall     auto Dir = ++sys::path::rbegin(Filename),
297ea9773acSSam McCall          DirEnd = sys::path::rend(Filename);
298ea9773acSSam McCall     for (int I = 0; I < DirectorySegmentsQueried && Dir != DirEnd; ++I, ++Dir) {
299ea9773acSSam McCall       if (Dir->size() > ShortDirectorySegment)
300ea9773acSSam McCall         Dirs.push_back(*Dir);
301ea9773acSSam McCall       Prefix = Filename.substr(0, Dir - DirEnd);
302ea9773acSSam McCall     }
303ea9773acSSam McCall 
304ea9773acSSam McCall     // Now award points based on lookups into our various indexes.
305ea9773acSSam McCall     DenseMap<size_t, int> Candidates; // Index -> score.
306ea9773acSSam McCall     auto Award = [&](int Points, ArrayRef<SubstringAndIndex> Range) {
307ea9773acSSam McCall       for (const auto &Entry : Range)
308ea9773acSSam McCall         Candidates[Entry.second] += Points;
309ea9773acSSam McCall     };
310ea9773acSSam McCall     // Award one point if the file's basename is a prefix of the candidate,
311ea9773acSSam McCall     // and another if it's an exact match (so exact matches get two points).
312ea9773acSSam McCall     Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
313ea9773acSSam McCall     Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
314ea9773acSSam McCall     // For each of the last few directories in the Filename, award a point
315ea9773acSSam McCall     // if it's present in the candidate.
316ea9773acSSam McCall     for (StringRef Dir : Dirs)
317ea9773acSSam McCall       Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
318ea9773acSSam McCall     // Award one more point if the whole rest of the path matches.
319ea9773acSSam McCall     if (sys::path::root_directory(Prefix) != Prefix)
320ea9773acSSam McCall       Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
321ea9773acSSam McCall     return Candidates;
322ea9773acSSam McCall   }
323ea9773acSSam McCall 
324ea9773acSSam McCall   // Pick a single winner from the set of scored candidates.
325ea9773acSSam McCall   // Returns (index, score).
326ea9773acSSam McCall   std::pair<size_t, int> pickWinner(const DenseMap<size_t, int> &Candidates,
327ea9773acSSam McCall                                     StringRef Filename,
328ea9773acSSam McCall                                     types::ID PreferredLanguage) const {
329ea9773acSSam McCall     struct ScoredCandidate {
330ea9773acSSam McCall       size_t Index;
331ea9773acSSam McCall       bool Preferred;
332ea9773acSSam McCall       int Points;
333ea9773acSSam McCall       size_t PrefixLength;
334ea9773acSSam McCall     };
335ea9773acSSam McCall     // Choose the best candidate by (preferred, points, prefix length, alpha).
336ea9773acSSam McCall     ScoredCandidate Best = {size_t(-1), false, 0, 0};
337ea9773acSSam McCall     for (const auto &Candidate : Candidates) {
338ea9773acSSam McCall       ScoredCandidate S;
339ea9773acSSam McCall       S.Index = Candidate.first;
340ea9773acSSam McCall       S.Preferred = PreferredLanguage == types::TY_INVALID ||
341ea9773acSSam McCall                     PreferredLanguage == Commands[S.Index].Type;
342ea9773acSSam McCall       S.Points = Candidate.second;
343ea9773acSSam McCall       if (!S.Preferred && Best.Preferred)
344ea9773acSSam McCall         continue;
345ea9773acSSam McCall       if (S.Preferred == Best.Preferred) {
346ea9773acSSam McCall         if (S.Points < Best.Points)
347ea9773acSSam McCall           continue;
348ea9773acSSam McCall         if (S.Points == Best.Points) {
349ea9773acSSam McCall           S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
350ea9773acSSam McCall           if (S.PrefixLength < Best.PrefixLength)
351ea9773acSSam McCall             continue;
352ea9773acSSam McCall           // hidden heuristics should at least be deterministic!
353ea9773acSSam McCall           if (S.PrefixLength == Best.PrefixLength)
354ea9773acSSam McCall             if (S.Index > Best.Index)
355ea9773acSSam McCall               continue;
356ea9773acSSam McCall         }
357ea9773acSSam McCall       }
358ea9773acSSam McCall       // PrefixLength was only set above if actually needed for a tiebreak.
359ea9773acSSam McCall       // But it definitely needs to be set to break ties in the future.
360ea9773acSSam McCall       S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
361ea9773acSSam McCall       Best = S;
362ea9773acSSam McCall     }
363ea9773acSSam McCall     // Edge case: no candidate got any points.
364ea9773acSSam McCall     // We ignore PreferredLanguage at this point (not ideal).
365ea9773acSSam McCall     if (Best.Index == size_t(-1))
366ea9773acSSam McCall       return {longestMatch(Filename, Paths).second, 0};
367ea9773acSSam McCall     return {Best.Index, Best.Points};
368ea9773acSSam McCall   }
369ea9773acSSam McCall 
370ea9773acSSam McCall   // Returns the range within a sorted index that compares equal to Key.
371ea9773acSSam McCall   // If Prefix is true, it's instead the range starting with Key.
372ea9773acSSam McCall   template <bool Prefix>
373ea9773acSSam McCall   ArrayRef<SubstringAndIndex>
374ea9773acSSam McCall   indexLookup(StringRef Key, const std::vector<SubstringAndIndex> &Idx) const {
375ea9773acSSam McCall     // Use pointers as iteratiors to ease conversion of result to ArrayRef.
376*44f2f4ecSSam McCall     auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
377*44f2f4ecSSam McCall                                   Less<Prefix>());
378ea9773acSSam McCall     return {Range.first, Range.second};
379ea9773acSSam McCall   }
380ea9773acSSam McCall 
381ea9773acSSam McCall   // Performs a point lookup into a nonempty index, returning a longest match.
382ea9773acSSam McCall   SubstringAndIndex
383ea9773acSSam McCall   longestMatch(StringRef Key, const std::vector<SubstringAndIndex> &Idx) const {
384ea9773acSSam McCall     assert(!Idx.empty());
385ea9773acSSam McCall     // Longest substring match will be adjacent to a direct lookup.
386ea9773acSSam McCall     auto It =
387ea9773acSSam McCall         std::lower_bound(Idx.begin(), Idx.end(), SubstringAndIndex{Key, 0});
388ea9773acSSam McCall     if (It == Idx.begin())
389ea9773acSSam McCall       return *It;
390ea9773acSSam McCall     if (It == Idx.end())
391ea9773acSSam McCall       return *--It;
392ea9773acSSam McCall     // Have to choose between It and It-1
393ea9773acSSam McCall     size_t Prefix = matchingPrefix(Key, It->first);
394ea9773acSSam McCall     size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
395ea9773acSSam McCall     return Prefix > PrevPrefix ? *It : *--It;
396ea9773acSSam McCall   }
397ea9773acSSam McCall 
398ea9773acSSam McCall   std::vector<TransferableCommand> Commands; // Indexes point into this.
399ea9773acSSam McCall   BumpPtrAllocator Arena;
400ea9773acSSam McCall   StringSaver Strings;
401ea9773acSSam McCall   // Indexes of candidates by certain substrings.
402ea9773acSSam McCall   // String is lowercase and sorted, index points into OriginalPaths.
403ea9773acSSam McCall   std::vector<SubstringAndIndex> Paths;      // Full path.
404ea9773acSSam McCall   std::vector<SubstringAndIndex> Stems;      // Basename, without extension.
405ea9773acSSam McCall   std::vector<SubstringAndIndex> Components; // Last path components.
406ea9773acSSam McCall };
407ea9773acSSam McCall 
408ea9773acSSam McCall // The actual CompilationDatabase wrapper delegates to its inner database.
409ea9773acSSam McCall // If no match, looks up a command in CommandIndex and transfers it to the file.
410ea9773acSSam McCall class InterpolatingCompilationDatabase : public CompilationDatabase {
411ea9773acSSam McCall public:
412ea9773acSSam McCall   InterpolatingCompilationDatabase(std::unique_ptr<CompilationDatabase> Inner)
413ea9773acSSam McCall       : Inner(std::move(Inner)), Index(allCommands()) {}
414ea9773acSSam McCall 
415ea9773acSSam McCall   std::vector<CompileCommand>
416ea9773acSSam McCall   getCompileCommands(StringRef Filename) const override {
417ea9773acSSam McCall     auto Known = Inner->getCompileCommands(Filename);
418ea9773acSSam McCall     if (Index.empty() || !Known.empty())
419ea9773acSSam McCall       return Known;
420ea9773acSSam McCall     bool TypeCertain;
421ea9773acSSam McCall     auto Lang = guessType(Filename, &TypeCertain);
422ea9773acSSam McCall     if (!TypeCertain)
423ea9773acSSam McCall       Lang = types::TY_INVALID;
424ea9773acSSam McCall     return {Index.chooseProxy(Filename, foldType(Lang)).transferTo(Filename)};
425ea9773acSSam McCall   }
426ea9773acSSam McCall 
427ea9773acSSam McCall   std::vector<std::string> getAllFiles() const override {
428ea9773acSSam McCall     return Inner->getAllFiles();
429ea9773acSSam McCall   }
430ea9773acSSam McCall 
431ea9773acSSam McCall   std::vector<CompileCommand> getAllCompileCommands() const override {
432ea9773acSSam McCall     return Inner->getAllCompileCommands();
433ea9773acSSam McCall   }
434ea9773acSSam McCall 
435ea9773acSSam McCall private:
436ea9773acSSam McCall   std::vector<TransferableCommand> allCommands() {
437ea9773acSSam McCall     std::vector<TransferableCommand> Result;
438ea9773acSSam McCall     for (auto Command : Inner->getAllCompileCommands()) {
439ea9773acSSam McCall       Result.emplace_back(std::move(Command));
440ea9773acSSam McCall       if (Result.back().Type == types::TY_INVALID)
441ea9773acSSam McCall         Result.pop_back();
442ea9773acSSam McCall     }
443ea9773acSSam McCall     return Result;
444ea9773acSSam McCall   }
445ea9773acSSam McCall 
446ea9773acSSam McCall   std::unique_ptr<CompilationDatabase> Inner;
447ea9773acSSam McCall   CommandIndex Index;
448ea9773acSSam McCall };
449ea9773acSSam McCall 
450ea9773acSSam McCall } // namespace
451ea9773acSSam McCall 
452ea9773acSSam McCall std::unique_ptr<CompilationDatabase>
453ea9773acSSam McCall inferMissingCompileCommands(std::unique_ptr<CompilationDatabase> Inner) {
454ea9773acSSam McCall   return llvm::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
455ea9773acSSam McCall }
456ea9773acSSam McCall 
457ea9773acSSam McCall } // namespace tooling
458ea9773acSSam McCall } // namespace clang
459