119 lines · cpp
1//===- CompilationDatabase.cpp - LSP Compilation Database -----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Tools/lsp-server-support/CompilationDatabase.h"10#include "mlir/Support/FileUtilities.h"11#include "llvm/ADT/SetVector.h"12#include "llvm/ADT/StringRef.h"13#include "llvm/Support/LSP/Logging.h"14#include "llvm/Support/LSP/Protocol.h"15#include "llvm/Support/YAMLTraits.h"16 17using namespace mlir;18using namespace mlir::lsp;19using llvm::lsp::Logger;20 21//===----------------------------------------------------------------------===//22// YamlFileInfo23//===----------------------------------------------------------------------===//24 25namespace {26struct YamlFileInfo {27 /// The absolute path to the file.28 std::string filename;29 /// The include directories available for the file.30 std::vector<std::string> includeDirs;31};32} // namespace33 34//===----------------------------------------------------------------------===//35// CompilationDatabase36//===----------------------------------------------------------------------===//37 38LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(YamlFileInfo)39 40namespace llvm {41namespace yaml {42template <>43struct MappingTraits<YamlFileInfo> {44 static void mapping(IO &io, YamlFileInfo &info) {45 // Parse the filename and normalize it to the form we will expect from46 // incoming URIs.47 io.mapRequired("filepath", info.filename);48 49 // Normalize the filename to avoid incompatability with incoming URIs.50 if (Expected<lsp::URIForFile> uri =51 lsp::URIForFile::fromFile(info.filename))52 info.filename = uri->file().str();53 54 // Parse the includes from the yaml stream. These are in the form of a55 // semi-colon delimited list.56 std::string combinedIncludes;57 io.mapRequired("includes", combinedIncludes);58 for (StringRef include : llvm::split(combinedIncludes, ";")) {59 if (!include.empty())60 info.includeDirs.push_back(include.str());61 }62 }63};64} // end namespace yaml65} // end namespace llvm66 67CompilationDatabase::CompilationDatabase(ArrayRef<std::string> databases) {68 for (StringRef filename : databases)69 loadDatabase(filename);70}71 72const CompilationDatabase::FileInfo &73CompilationDatabase::getFileInfo(StringRef filename) const {74 auto it = files.find(filename);75 return it == files.end() ? defaultFileInfo : it->second;76}77 78void CompilationDatabase::loadDatabase(StringRef filename) {79 if (filename.empty())80 return;81 82 // Set up the input file.83 std::string errorMessage;84 std::unique_ptr<llvm::MemoryBuffer> inputFile =85 openInputFile(filename, &errorMessage);86 if (!inputFile) {87 Logger::error("Failed to open compilation database: {0}", errorMessage);88 return;89 }90 llvm::yaml::Input yaml(inputFile->getBuffer());91 92 // Parse the yaml description and add any new files to the database.93 std::vector<YamlFileInfo> parsedFiles;94 yaml >> parsedFiles;95 96 SetVector<StringRef> knownIncludes;97 for (auto &file : parsedFiles) {98 auto it = files.try_emplace(file.filename, std::move(file.includeDirs));99 100 // If we encounter a duplicate file, log a warning and ignore it.101 if (!it.second) {102 Logger::info("Duplicate file in compilation database: {0}",103 file.filename);104 continue;105 }106 107 // Track the includes for the file.108 knownIncludes.insert_range(it.first->second.includeDirs);109 }110 111 // Add all of the known includes to the default file info. We don't know any112 // information about how to treat these files, but these may be project files113 // that we just don't yet have information for. In these cases, providing some114 // heuristic information provides a better user experience, and generally115 // shouldn't lead to any negative side effects.116 for (StringRef include : knownIncludes)117 defaultFileInfo.includeDirs.push_back(include.str());118}119