//===- InterpolatingCompilationDatabase.cpp ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // InterpolatingCompilationDatabase wraps another CompilationDatabase and // attempts to heuristically determine appropriate compile commands for files // that are not included, such as headers or newly created files. // // Motivating cases include: // Header files that live next to their implementation files. These typically // share a base filename. (libclang/CXString.h, libclang/CXString.cpp). // Some projects separate headers from includes. Filenames still typically // match, maybe other path segments too. (include/llvm/IR/Use.h, lib/IR/Use.cc). // Matches are sometimes only approximate (Sema.h, SemaDecl.cpp). This goes // for directories too (Support/Unix/Process.inc, lib/Support/Process.cpp). // Even if we can't find a "right" compile command, even a random one from // the project will tend to get important flags like -I and -x right. // // We "borrow" the compile command for the closest available file: // - points are awarded if the filename matches (ignoring extension) // - points are awarded if the directory structure matches // - ties are broken by length of path prefix match // // The compile command is adjusted, replacing the filename and removing output // file arguments. The -x and -std flags may be affected too. // // Source language is a tricky issue: is it OK to use a .c file's command // for building a .cc file? What language is a .h file in? // - We only consider compile commands for c-family languages as candidates. // - For files whose language is implied by the filename (e.g. .m, .hpp) // we prefer candidates from the same language. // If we must cross languages, we drop any -x and -std flags. // - For .h files, candidates from any c-family language are acceptable. // We use the candidate's language, inserting e.g. -x c++-header. // // This class is only useful when wrapping databases that can enumerate all // their compile commands. If getAllFilenames() is empty, no inference occurs. // //===----------------------------------------------------------------------===// #include "clang/Basic/LangStandard.h" #include "clang/Driver/Driver.h" #include "clang/Driver/Options.h" #include "clang/Driver/Types.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/raw_ostream.h" #include <memory> #include <optional> namespace clang { namespace tooling { namespace { usingnamespacellvm; types; path; // The length of the prefix these two strings have in common. size_t matchingPrefix(StringRef L, StringRef R) { … } // A comparator for searching SubstringWithIndexes with std::equal_range etc. // Optionaly prefix semantics: compares equal if the key is a prefix. template <bool Prefix> struct Less { … }; // Infer type from filename. If we might have gotten it wrong, set *Certain. // *.h will be inferred as a C header, but not certain. types::ID guessType(StringRef Filename, bool *Certain = nullptr) { … } // Return Lang as one of the canonical supported types. // e.g. c-header --> c; fortran --> TY_INVALID static types::ID foldType(types::ID Lang) { … } // A CompileCommand that can be applied to another file. struct TransferableCommand { … }; // Given a filename, FileIndex picks the best matching file from the underlying // DB. This is the proxy file whose CompileCommand will be reused. The // heuristics incorporate file name, extension, and directory structure. // Strategy: // - Build indexes of each of the substrings we want to look up by. // These indexes are just sorted lists of the substrings. // - Each criterion corresponds to a range lookup into the index, so we only // need O(log N) string comparisons to determine scores. // // Apart from path proximity signals, also takes file extensions into account // when scoring the candidates. class FileIndex { … }; // The actual CompilationDatabase wrapper delegates to its inner database. // If no match, looks up a proxy file in FileIndex and transfers its // command to the requested file. class InterpolatingCompilationDatabase : public CompilationDatabase { … }; } // namespace std::unique_ptr<CompilationDatabase> inferMissingCompileCommands(std::unique_ptr<CompilationDatabase> Inner) { … } tooling::CompileCommand transferCompileCommand(CompileCommand Cmd, StringRef Filename) { … } } // namespace tooling } // namespace clang