clang: lib/Tooling/InterpolatingCompilationDatabase.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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"
59#include
60#include
61
63namespace tooling {
64namespace {
65using namespace llvm;
67namespace path = llvm::sys::path;
68
69
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)
73 if (L[I] != R[I])
74 return I;
75 return Limit;
76}
77
78
79
80template 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;
83 return Key < V;
84 }
85 bool operator()(std::pair<StringRef, size_t> Value, StringRef Key) const {
86 StringRef V = Prefix ? Value.first.substr(0, Key.size()) : Value.first;
87 return V < Key;
88 }
89};
90
91
92
94
97 if (Certain)
100}
101
102
103
105 switch (Lang) {
106 case types::TY_C:
107 case types::TY_CHeader:
108 return types::TY_C;
109 case types::TY_ObjC:
110 case types::TY_ObjCHeader:
111 return types::TY_ObjC;
112 case types::TY_CXX:
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;
118 case types::TY_CUDA:
119 case types::TY_CUDA_DEVICE:
120 return types::TY_CUDA;
121 default:
123 }
124}
125
126
127struct TransferableCommand {
128
130
131 std::optionaltypes::ID Type;
132
134
136
137 TransferableCommand(CompileCommand C)
139 std::vectorstd::string OldArgs = std::move(Cmd.CommandLine);
140 Cmd.CommandLine.clear();
141
142
143 llvm::opt::InputArgList ArgList;
144 {
146 for (const std::string &S : OldArgs)
147 TmpArgv.push_back(S.c_str());
151 ArgList = {TmpArgv.begin(), TmpArgv.end()};
152 }
153
154
155
156
157
159 if (!OldArgs.empty())
160 Cmd.CommandLine.emplace_back(OldArgs.front());
161 for (unsigned Pos = 1; Pos < OldArgs.size();) {
162 using namespace driver::options;
163
164 const unsigned OldPos = Pos;
165 std::unique_ptrllvm::opt::Arg Arg(OptTable.ParseOneArg(
166 ArgList, Pos,
167 llvm::opt::Visibility(ClangCLMode ? CLOption : ClangOption)));
168
169 if (!Arg)
170 continue;
171
172 const llvm::opt::Option &Opt = Arg->getOption();
173
174
175 if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) ||
176 (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) ||
177 Opt.matches(OPT__SLASH_Fe) ||
178 Opt.matches(OPT__SLASH_Fi) ||
179 Opt.matches(OPT__SLASH_Fo))))
180 continue;
181
182
183 if (Opt.matches(OPT__DASH_DASH))
184 break;
185
186
187 if (const auto GivenType = tryParseTypeArg(*Arg)) {
188 Type = *GivenType;
189 continue;
190 }
191
192
193 if (const auto GivenStd = tryParseStdArg(*Arg)) {
195 Std = *GivenStd;
196 continue;
197 }
198
199 Cmd.CommandLine.insert(Cmd.CommandLine.end(),
200 OldArgs.data() + OldPos, OldArgs.data() + Pos);
201 }
202
203
207
209 Type = std::nullopt;
210 }
211
212
213
214 CompileCommand transferTo(StringRef Filename) && {
215 CompileCommand Result = std::move(Cmd);
216 Result.Heuristic = "inferred from " + Result.Filename;
218 bool TypeCertain;
219 auto TargetType = guessType(Filename, &TypeCertain);
220
221 if ((!TargetType || !TypeCertain) && Type) {
222
223
224 TargetType =
228 if (ClangCLMode) {
229 const StringRef Flag = toCLFlag(TargetType);
230 if (!Flag.empty())
231 Result.CommandLine.push_back(std::string(Flag));
232 } else {
233 Result.CommandLine.push_back("-x");
235 }
236 }
237
238
240 Result.CommandLine.emplace_back((
241 llvm::Twine(ClangCLMode ? "/std:" : "-std=") +
243 }
244 Result.CommandLine.push_back("--");
247 }
248
249private:
250
252 switch (Lang) {
254 return types::TY_C;
256 return types::TY_CXX;
258 return types::TY_ObjC;
260 return types::TY_ObjCXX;
261 default:
263 }
264 }
265
266
267 static StringRef toCLFlag(types::ID Type) {
268 switch (Type) {
269 case types::TY_C:
270 case types::TY_CHeader:
271 return "/TC";
272 case types::TY_CXX:
273 case types::TY_CXXHeader:
274 return "/TP";
275 default:
276 return StringRef();
277 }
278 }
279
280
281 std::optionaltypes::ID tryParseTypeArg(const llvm::opt::Arg &Arg) {
282 const llvm::opt::Option &Opt = Arg.getOption();
283 using namespace driver::options;
284 if (ClangCLMode) {
285 if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc))
286 return types::TY_C;
287 if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp))
288 return types::TY_CXX;
289 } else {
290 if (Opt.matches(driver::options::OPT_x))
292 }
293 return std::nullopt;
294 }
295
296
297 std::optionalLangStandard::Kind tryParseStdArg(const llvm::opt::Arg &Arg) {
298 using namespace driver::options;
299 if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
301 return std::nullopt;
302 }
303};
304
305
306
307
308
309
310
311
312
313
314
315
316class FileIndex {
317public:
318 FileIndex(std::vectorstd::string Files)
319 : OriginalPaths(std::move(Files)), Strings(Arena) {
320
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());
327
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);
335 }
336 llvm::sort(Paths);
337 llvm::sort(Stems);
338 llvm::sort(Components);
339 }
340
341 bool empty() const { return Paths.empty(); }
342
343
344
345
346 StringRef chooseProxy(StringRef OriginalFilename,
347 types::ID PreferLanguage) const {
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);
353
354 DEBUG_WITH_TYPE(
355 "interpolate",
356 llvm::dbgs() << "interpolate: chose " << OriginalPaths[Best.first]
357 << " as proxy for " << OriginalFilename << " preferring "
359 ? "none"
361 << " score=" << Best.second << "\n");
362 return OriginalPaths[Best.first];
363 }
364
365private:
366 using SubstringAndIndex = std::pair<StringRef, size_t>;
367
368
369
370 constexpr static int DirectorySegmentsIndexed = 4;
371 constexpr static int DirectorySegmentsQueried = 2;
372 constexpr static int ShortDirectorySegment = 1;
373
374
375
376 DenseMap<size_t, int> scoreCandidates(StringRef Filename) const {
377
378
379
380 StringRef Stem = sys::path::stem(Filename);
382 llvm::StringRef Prefix;
383 auto Dir = ++sys::path::rbegin(Filename),
384 DirEnd = sys::path::rend(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);
389 }
390
391
392 DenseMap<size_t, int> Candidates;
394 for (const auto &Entry : Range)
395 Candidates[Entry.second] += Points;
396 };
397
398
399 Award(1, indexLookup</*Prefix=*/true>(Stem, Stems));
400 Award(1, indexLookup</*Prefix=*/false>(Stem, Stems));
401
402
403 for (StringRef Dir : Dirs)
404 Award(1, indexLookup</*Prefix=*/false>(Dir, Components));
405
406 if (sys::path::root_directory(Prefix) != Prefix)
407 Award(1, indexLookup</*Prefix=*/true>(Prefix, Paths));
408 return Candidates;
409 }
410
411
412
413 std::pair<size_t, int> pickWinner(const DenseMap<size_t, int> &Candidates,
415 types::ID PreferredLanguage) const {
416 struct ScoredCandidate {
417 size_t Index;
418 bool Preferred;
419 int Points;
420 size_t PrefixLength;
421 };
422
423 ScoredCandidate Best = {size_t(-1), false, 0, 0};
424 for (const auto &Candidate : Candidates) {
425 ScoredCandidate S;
426 S.Index = Candidate.first;
428 PreferredLanguage == Types[S.Index];
429 S.Points = Candidate.second;
430 if (!S.Preferred && Best.Preferred)
431 continue;
432 if (S.Preferred == Best.Preferred) {
433 if (S.Points < Best.Points)
434 continue;
435 if (S.Points == Best.Points) {
436 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
437 if (S.PrefixLength < Best.PrefixLength)
438 continue;
439
440 if (S.PrefixLength == Best.PrefixLength)
441 if (S.Index > Best.Index)
442 continue;
443 }
444 }
445
446
447 S.PrefixLength = matchingPrefix(Filename, Paths[S.Index].first);
448 Best = S;
449 }
450
451
452 if (Best.Index == size_t(-1))
453 return {longestMatch(Filename, Paths).second, 0};
454 return {Best.Index, Best.Points};
455 }
456
457
458
459 template
462
463 auto Range = std::equal_range(Idx.data(), Idx.data() + Idx.size(), Key,
464 Less());
466 }
467
468
469 SubstringAndIndex longestMatch(StringRef Key,
471 assert(!Idx.empty());
472
473 auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
474 if (It == Idx.begin())
475 return *It;
476 if (It == Idx.end())
477 return *--It;
478
479 size_t Prefix = matchingPrefix(Key, It->first);
480 size_t PrevPrefix = matchingPrefix(Key, (It - 1)->first);
481 return Prefix > PrevPrefix ? *It : *--It;
482 }
483
484
485 std::vectorstd::string OriginalPaths;
486 BumpPtrAllocator Arena;
487 StringSaver Strings;
488
489
490 std::vector Paths;
491
492
493 std::vectortypes::ID Types;
494 std::vector Stems;
495 std::vector Components;
496};
497
498
499
500
501class InterpolatingCompilationDatabase : public CompilationDatabase {
502public:
503 InterpolatingCompilationDatabase(std::unique_ptr Inner)
504 : Inner(std::move(Inner)), Index(this->Inner->getAllFiles()) {}
505
506 std::vector
507 getCompileCommands(StringRef Filename) const override {
508 auto Known = Inner->getCompileCommands(Filename);
509 if (Index.empty() || !Known.empty())
510 return Known;
511 bool TypeCertain;
512 auto Lang = guessType(Filename, &TypeCertain);
513 if (!TypeCertain)
515 auto ProxyCommands =
516 Inner->getCompileCommands(Index.chooseProxy(Filename, foldType(Lang)));
517 if (ProxyCommands.empty())
518 return {};
520 }
521
522 std::vectorstd::string getAllFiles() const override {
523 return Inner->getAllFiles();
524 }
525
526 std::vector getAllCompileCommands() const override {
527 return Inner->getAllCompileCommands();
528 }
529
530private:
531 std::unique_ptr Inner;
532 FileIndex Index;
533};
534
535}
536
537std::unique_ptr
539 return std::make_unique(std::move(Inner));
540}
541
544 return TransferableCommand(std::move(Cmd)).transferTo(Filename);
545}
546
547}
548}
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)