mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-09 13:36:07 +00:00

Compilation database might have empty string as a command line argument. But ExpandResponseFilesDatabase::expand doesn't expect this and assumes that string.front() can be used for any argument. It is undefined behaviour if string is empty. With debug build mode it causes crash in clangd. Test Plan: check-clang Differential Revision: https://reviews.llvm.org/D105120
93 lines
3.1 KiB
C++
93 lines
3.1 KiB
C++
//===- ExpandResponseFileCompilationDataBase.cpp --------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Tooling/CompilationDatabase.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/ConvertUTF.h"
|
|
#include "llvm/Support/ErrorOr.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
|
|
namespace clang {
|
|
namespace tooling {
|
|
namespace {
|
|
|
|
class ExpandResponseFilesDatabase : public CompilationDatabase {
|
|
public:
|
|
ExpandResponseFilesDatabase(
|
|
std::unique_ptr<CompilationDatabase> Base,
|
|
llvm::cl::TokenizerCallback Tokenizer,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
|
|
: Base(std::move(Base)), Tokenizer(Tokenizer), FS(std::move(FS)) {
|
|
assert(this->Base != nullptr);
|
|
assert(this->Tokenizer != nullptr);
|
|
assert(this->FS != nullptr);
|
|
}
|
|
|
|
std::vector<std::string> getAllFiles() const override {
|
|
return Base->getAllFiles();
|
|
}
|
|
|
|
std::vector<CompileCommand>
|
|
getCompileCommands(StringRef FilePath) const override {
|
|
return expand(Base->getCompileCommands(FilePath));
|
|
}
|
|
|
|
std::vector<CompileCommand> getAllCompileCommands() const override {
|
|
return expand(Base->getAllCompileCommands());
|
|
}
|
|
|
|
private:
|
|
std::vector<CompileCommand> expand(std::vector<CompileCommand> Cmds) const {
|
|
for (auto &Cmd : Cmds) {
|
|
bool SeenRSPFile = false;
|
|
llvm::SmallVector<const char *, 20> Argv;
|
|
Argv.reserve(Cmd.CommandLine.size());
|
|
for (auto &Arg : Cmd.CommandLine) {
|
|
Argv.push_back(Arg.c_str());
|
|
if (!Arg.empty())
|
|
SeenRSPFile |= Arg.front() == '@';
|
|
}
|
|
if (!SeenRSPFile)
|
|
continue;
|
|
llvm::BumpPtrAllocator Alloc;
|
|
llvm::StringSaver Saver(Alloc);
|
|
llvm::cl::ExpandResponseFiles(Saver, Tokenizer, Argv, false, false,
|
|
llvm::StringRef(Cmd.Directory), *FS);
|
|
// Don't assign directly, Argv aliases CommandLine.
|
|
std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
|
|
Cmd.CommandLine = std::move(ExpandedArgv);
|
|
}
|
|
return Cmds;
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<CompilationDatabase> Base;
|
|
llvm::cl::TokenizerCallback Tokenizer;
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<CompilationDatabase>
|
|
expandResponseFiles(std::unique_ptr<CompilationDatabase> Base,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
|
auto Tokenizer = llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows()
|
|
? llvm::cl::TokenizeWindowsCommandLine
|
|
: llvm::cl::TokenizeGNUCommandLine;
|
|
return std::make_unique<ExpandResponseFilesDatabase>(
|
|
std::move(Base), Tokenizer, std::move(FS));
|
|
}
|
|
|
|
} // namespace tooling
|
|
} // namespace clang
|