[clangd] Support .clangd command line modifications for C++ modules (#122606)
Tunnels `Manger` object into the `ScanningAllProjectModules` so it can be used to perform necessary command-line modifications (which also adds `--resources` path previously added there explicitly). This allows using the experimental C++ modules support with gcc. This was discussed in the issue with @ChuanqiXu9 and @kadircet Closes #112635
This commit is contained in:
parent
56592a8108
commit
2b0e2255d6
@ -830,6 +830,16 @@ bool OverlayCDB::setCompileCommand(PathRef File,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ProjectModules>
|
||||
OverlayCDB::getProjectModules(PathRef File) const {
|
||||
auto MDB = DelegatingCDB::getProjectModules(File);
|
||||
MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command,
|
||||
PathRef CommandPath) {
|
||||
Mangler(Command, CommandPath);
|
||||
});
|
||||
return std::move(MDB);
|
||||
}
|
||||
|
||||
DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base)
|
||||
: Base(Base) {
|
||||
if (Base)
|
||||
|
@ -209,6 +209,9 @@ public:
|
||||
setCompileCommand(PathRef File,
|
||||
std::optional<tooling::CompileCommand> CompilationCommand);
|
||||
|
||||
std::unique_ptr<ProjectModules>
|
||||
getProjectModules(PathRef File) const override;
|
||||
|
||||
private:
|
||||
mutable std::mutex Mutex;
|
||||
llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */
|
||||
|
@ -9,8 +9,10 @@
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROJECTMODULES_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROJECTMODULES_H
|
||||
|
||||
#include "support/Function.h"
|
||||
#include "support/Path.h"
|
||||
#include "support/ThreadsafeFS.h"
|
||||
#include "clang/Tooling/CompilationDatabase.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -36,11 +38,16 @@ namespace clangd {
|
||||
/// `<primary-module-name>[:partition-name]`. So module names covers partitions.
|
||||
class ProjectModules {
|
||||
public:
|
||||
using CommandMangler =
|
||||
llvm::unique_function<void(tooling::CompileCommand &, PathRef) const>;
|
||||
|
||||
virtual std::vector<std::string> getRequiredModules(PathRef File) = 0;
|
||||
virtual PathRef
|
||||
getSourceForModuleName(llvm::StringRef ModuleName,
|
||||
PathRef RequiredSrcFile = PathRef()) = 0;
|
||||
|
||||
virtual void setCommandMangler(CommandMangler Mangler) {}
|
||||
|
||||
virtual ~ProjectModules() = default;
|
||||
};
|
||||
|
||||
|
@ -48,7 +48,8 @@ public:
|
||||
};
|
||||
|
||||
/// Scanning the single file specified by \param FilePath.
|
||||
std::optional<ModuleDependencyInfo> scan(PathRef FilePath);
|
||||
std::optional<ModuleDependencyInfo>
|
||||
scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler);
|
||||
|
||||
/// Scanning every source file in the current project to get the
|
||||
/// <module-name> to <module-unit-source> map.
|
||||
@ -57,7 +58,7 @@ public:
|
||||
/// a global module dependency scanner to monitor every file. Or we
|
||||
/// can simply require the build systems (or even the end users)
|
||||
/// to provide the map.
|
||||
void globalScan();
|
||||
void globalScan(const ProjectModules::CommandMangler &Mangler);
|
||||
|
||||
/// Get the source file from the module name. Note that the language
|
||||
/// guarantees all the module names are unique in a valid program.
|
||||
@ -69,7 +70,9 @@ public:
|
||||
|
||||
/// Return the direct required modules. Indirect required modules are not
|
||||
/// included.
|
||||
std::vector<std::string> getRequiredModules(PathRef File);
|
||||
std::vector<std::string>
|
||||
getRequiredModules(PathRef File,
|
||||
const ProjectModules::CommandMangler &Mangler);
|
||||
|
||||
private:
|
||||
std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
|
||||
@ -87,7 +90,8 @@ private:
|
||||
};
|
||||
|
||||
std::optional<ModuleDependencyScanner::ModuleDependencyInfo>
|
||||
ModuleDependencyScanner::scan(PathRef FilePath) {
|
||||
ModuleDependencyScanner::scan(PathRef FilePath,
|
||||
const ProjectModules::CommandMangler &Mangler) {
|
||||
auto Candidates = CDB->getCompileCommands(FilePath);
|
||||
if (Candidates.empty())
|
||||
return std::nullopt;
|
||||
@ -97,10 +101,8 @@ ModuleDependencyScanner::scan(PathRef FilePath) {
|
||||
// DirectoryBasedGlobalCompilationDatabase::getCompileCommand.
|
||||
tooling::CompileCommand Cmd = std::move(Candidates.front());
|
||||
|
||||
static int StaticForMainAddr; // Just an address in this process.
|
||||
Cmd.CommandLine.push_back("-resource-dir=" +
|
||||
CompilerInvocation::GetResourcesPath(
|
||||
"clangd", (void *)&StaticForMainAddr));
|
||||
if (Mangler)
|
||||
Mangler(Cmd, FilePath);
|
||||
|
||||
using namespace clang::tooling::dependencies;
|
||||
|
||||
@ -130,9 +132,10 @@ ModuleDependencyScanner::scan(PathRef FilePath) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
void ModuleDependencyScanner::globalScan() {
|
||||
void ModuleDependencyScanner::globalScan(
|
||||
const ProjectModules::CommandMangler &Mangler) {
|
||||
for (auto &File : CDB->getAllFiles())
|
||||
scan(File);
|
||||
scan(File, Mangler);
|
||||
|
||||
GlobalScanned = true;
|
||||
}
|
||||
@ -150,9 +153,9 @@ PathRef ModuleDependencyScanner::getSourceForModuleName(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
ModuleDependencyScanner::getRequiredModules(PathRef File) {
|
||||
auto ScanningResult = scan(File);
|
||||
std::vector<std::string> ModuleDependencyScanner::getRequiredModules(
|
||||
PathRef File, const ProjectModules::CommandMangler &Mangler) {
|
||||
auto ScanningResult = scan(File, Mangler);
|
||||
if (!ScanningResult)
|
||||
return {};
|
||||
|
||||
@ -177,7 +180,11 @@ public:
|
||||
~ScanningAllProjectModules() override = default;
|
||||
|
||||
std::vector<std::string> getRequiredModules(PathRef File) override {
|
||||
return Scanner.getRequiredModules(File);
|
||||
return Scanner.getRequiredModules(File, Mangler);
|
||||
}
|
||||
|
||||
void setCommandMangler(CommandMangler Mangler) override {
|
||||
this->Mangler = std::move(Mangler);
|
||||
}
|
||||
|
||||
/// RequiredSourceFile is not used intentionally. See the comments of
|
||||
@ -185,12 +192,13 @@ public:
|
||||
PathRef
|
||||
getSourceForModuleName(llvm::StringRef ModuleName,
|
||||
PathRef RequiredSourceFile = PathRef()) override {
|
||||
Scanner.globalScan();
|
||||
Scanner.globalScan(Mangler);
|
||||
return Scanner.getSourceForModuleName(ModuleName);
|
||||
}
|
||||
|
||||
private:
|
||||
ModuleDependencyScanner Scanner;
|
||||
CommandMangler Mangler;
|
||||
};
|
||||
|
||||
std::unique_ptr<ProjectModules> scanningProjectModules(
|
||||
|
@ -11,13 +11,14 @@
|
||||
/// code mode.
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "ModulesBuilder.h"
|
||||
#include "ScanningProjectModules.h"
|
||||
#include "Annotations.h"
|
||||
#include "CodeComplete.h"
|
||||
#include "Compiler.h"
|
||||
#include "ModulesBuilder.h"
|
||||
#include "ScanningProjectModules.h"
|
||||
#include "TestTU.h"
|
||||
#include "support/ThreadsafeFS.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "gmock/gmock.h"
|
||||
@ -191,6 +192,41 @@ export module M;
|
||||
EXPECT_TRUE(MInfo->canReuse(*Invocation, FS.view(TestDir)));
|
||||
}
|
||||
|
||||
TEST_F(PrerequisiteModulesTests, ModuleWithArgumentPatch) {
|
||||
MockDirectoryCompilationDatabase CDB(TestDir, FS);
|
||||
|
||||
CDB.ExtraClangFlags.push_back("-invalid-unknown-flag");
|
||||
|
||||
CDB.addFile("Dep.cppm", R"cpp(
|
||||
export module Dep;
|
||||
)cpp");
|
||||
|
||||
CDB.addFile("M.cppm", R"cpp(
|
||||
export module M;
|
||||
import Dep;
|
||||
)cpp");
|
||||
|
||||
// An invalid flag will break the module compilation and the
|
||||
// getRequiredModules would return an empty array
|
||||
auto ProjectModules = CDB.getProjectModules(getFullPath("M.cppm"));
|
||||
EXPECT_TRUE(
|
||||
ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
|
||||
|
||||
// Set the mangler to filter out the invalid flag
|
||||
ProjectModules->setCommandMangler(
|
||||
[](tooling::CompileCommand &Command, PathRef) {
|
||||
auto const It =
|
||||
std::find(Command.CommandLine.begin(), Command.CommandLine.end(),
|
||||
"-invalid-unknown-flag");
|
||||
Command.CommandLine.erase(It);
|
||||
});
|
||||
|
||||
// And now it returns a non-empty list of required modules since the
|
||||
// compilation succeeded
|
||||
EXPECT_FALSE(
|
||||
ProjectModules->getRequiredModules(getFullPath("M.cppm")).empty());
|
||||
}
|
||||
|
||||
TEST_F(PrerequisiteModulesTests, ModuleWithDepTest) {
|
||||
MockDirectoryCompilationDatabase CDB(TestDir, FS);
|
||||
|
||||
@ -435,7 +471,7 @@ void func() {
|
||||
/*Callback=*/nullptr);
|
||||
EXPECT_TRUE(Preamble);
|
||||
EXPECT_TRUE(Preamble->RequiredModules);
|
||||
|
||||
|
||||
auto Result = codeComplete(getFullPath("Use.cpp"), Test.point(),
|
||||
Preamble.get(), Use, {});
|
||||
EXPECT_FALSE(Result.Completions.empty());
|
||||
@ -474,7 +510,7 @@ void func() {
|
||||
/*Callback=*/nullptr);
|
||||
EXPECT_TRUE(Preamble);
|
||||
EXPECT_TRUE(Preamble->RequiredModules);
|
||||
|
||||
|
||||
auto Result = signatureHelp(getFullPath("Use.cpp"), Test.point(),
|
||||
*Preamble.get(), Use, MarkupKind::PlainText);
|
||||
EXPECT_FALSE(Result.signatures.empty());
|
||||
|
Loading…
x
Reference in New Issue
Block a user