Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions clang-tools-extra/clangd/FeatureModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ FeatureModule::Facilities &FeatureModule::facilities() {
return *Fac;
}

void FeatureModuleSet::add(std::unique_ptr<FeatureModule> M) {
Modules.push_back(std::move(M));
}

bool FeatureModuleSet::addImpl(void *Key, std::unique_ptr<FeatureModule> M,
const char *Source) {
if (!Map.try_emplace(Key, M.get()).second) {
Expand All @@ -33,5 +37,16 @@ bool FeatureModuleSet::addImpl(void *Key, std::unique_ptr<FeatureModule> M,
return true;
}

FeatureModuleSet FeatureModuleSet::fromRegistry() {
FeatureModuleSet ModuleSet;
for (FeatureModuleRegistry::entry E : FeatureModuleRegistry::entries()) {
vlog("Adding feature module '{0}' ({1})", E.getName(), E.getDesc());
ModuleSet.add(E.instantiate());
}
return ModuleSet;
}

} // namespace clangd
} // namespace clang

LLVM_INSTANTIATE_REGISTRY(clang::clangd::FeatureModuleRegistry)
22 changes: 19 additions & 3 deletions clang-tools-extra/clangd/FeatureModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Registry.h"
#include <memory>
#include <optional>
#include <type_traits>
Expand Down Expand Up @@ -143,9 +144,14 @@ class FeatureModule {

/// A FeatureModuleSet is a collection of feature modules installed in clangd.
///
/// Modules can be looked up by type, or used via the FeatureModule interface.
/// This allows individual modules to expose a public API.
/// For this reason, there can be only one feature module of each type.
/// Modules added with explicit type specification can be looked up by type, or
/// used via the FeatureModule interface. This allows individual modules to
/// expose a public API. For this reason, there can be only one feature module
/// of each type.
///
/// Modules added using a base class pointer can be used only via the
/// FeatureModule interface and can't be looked up by type, thus custom public
/// API (if provided by the module) can't be used.
///
/// The set owns the modules. It is itself owned by main, not ClangdServer.
class FeatureModuleSet {
Expand All @@ -164,6 +170,8 @@ class FeatureModuleSet {
public:
FeatureModuleSet() = default;

static FeatureModuleSet fromRegistry();

using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;
using const_iterator =
llvm::pointee_iterator<decltype(Modules)::const_iterator>;
Expand All @@ -172,6 +180,7 @@ class FeatureModuleSet {
const_iterator begin() const { return const_iterator(Modules.begin()); }
const_iterator end() const { return const_iterator(Modules.end()); }

void add(std::unique_ptr<FeatureModule> M);
template <typename Mod> bool add(std::unique_ptr<Mod> M) {
return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);
}
Expand All @@ -185,6 +194,13 @@ class FeatureModuleSet {

template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;

using FeatureModuleRegistry = llvm::Registry<FeatureModule>;

} // namespace clangd
} // namespace clang

namespace llvm {
extern template class Registry<clang::clangd::FeatureModule>;
} // namespace llvm

#endif
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "Config.h"
#include "ConfigProvider.h"
#include "Feature.h"
#include "FeatureModule.h"
#include "IncludeCleaner.h"
#include "PathMapping.h"
#include "Protocol.h"
Expand Down Expand Up @@ -1017,6 +1018,10 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
: static_cast<int>(ErrorResultCode::CheckFailed);
}

FeatureModuleSet ModuleSet = FeatureModuleSet::fromRegistry();
if (ModuleSet.begin() != ModuleSet.end())
Opts.FeatureModules = &ModuleSet;

// Initialize and run ClangdLSPServer.
// Change stdin to binary to not lose \r\n on windows.
llvm::sys::ChangeStdinToBinary();
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ add_unittest(ClangdUnitTests ClangdTests
DumpASTTests.cpp
ExpectedTypeTest.cpp
FeatureModulesTests.cpp
FeatureModulesRegistryTests.cpp
FileDistanceTests.cpp
FileIndexTests.cpp
FindSymbolsTests.cpp
Expand Down Expand Up @@ -154,6 +155,8 @@ target_include_directories(ClangdTests PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)

add_subdirectory(feature-modules)

clang_target_link_libraries(ClangdTests
PRIVATE
clangAST
Expand Down Expand Up @@ -183,6 +186,8 @@ target_link_libraries(ClangdTests
clangTidy
clangTidyUtils
clangdSupport

${FEATURE_MODULES}
)

if (CLANGD_ENABLE_REMOTE)
Expand Down
66 changes: 66 additions & 0 deletions clang-tools-extra/clangd/unittests/FeatureModulesRegistryTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===--- FeatureModulesRegistryTests.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 "FeatureModule.h"
#include "feature-modules/ForceLinker.h" // IWYU pragma: keep
#include "refactor/Tweak.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

using testing::ElementsAre;

namespace llvm {
raw_ostream &operator<<(raw_ostream &OS,
const clang::clangd::FeatureModuleRegistry::entry &E) {
OS << "(name = " << E.getName() << ", description = '" << E.getDesc() << "')";
return OS;
}

raw_ostream &operator<<(
raw_ostream &OS,
const iterator_range<Registry<clang::clangd::FeatureModule>::iterator>
&Rng) {
OS << "{ ";
bool First = true;
for (clang::clangd::FeatureModuleRegistry::entry E : Rng) {
if (First)
First = false;
else
OS << ", ";
OS << E;
}
OS << " }";
return OS;
}

raw_ostream &operator<<(raw_ostream &OS, const clang::clangd::Tweak &T) {
OS << "(id = " << T.id() << ", "
<< "title = " << T.title() << ")";
return OS;
}
} // namespace llvm

namespace clang::clangd {
namespace {

MATCHER_P(moduleName, Name, "") { return arg.getName() == Name; }
MATCHER_P(tweakID, ID, "") { return arg->id() == llvm::StringRef(ID); }

TEST(FeatureModulesRegistryTest, DummyModule) {
EXPECT_THAT(FeatureModuleRegistry::entries(),
ElementsAre(moduleName("dummy")));
FeatureModuleSet Set = FeatureModuleSet::fromRegistry();
ASSERT_EQ(Set.end() - Set.begin(), 1u);
std::vector<std::unique_ptr<Tweak>> Tweaks;
Set.begin()->contributeTweaks(Tweaks);
EXPECT_THAT(Tweaks, ElementsAre(tweakID("DummyTweak")));
}

} // namespace
} // namespace clang::clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(dummy)
set(FEATURE_MODULES ${FEATURE_MODULES} PARENT_SCOPE)
20 changes: 20 additions & 0 deletions clang-tools-extra/clangd/unittests/feature-modules/ForceLinker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===- ForceLinker.h ----------------------------------------------*-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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTEST_FEATURE_MODULES_FORCE_LINKER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTEST_FEATURE_MODULES_FORCE_LINKER_H

#include "llvm/Support/Compiler.h"

namespace clang::clangd {
extern volatile int DummyFeatureModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED DummyFeatureModuleAnchorDestination =
DummyFeatureModuleAnchorSource;
} // namespace clang::clangd

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
add_llvm_library(dummyFeatureModule
DummyFeatureModule.cpp
LINK_LIBS
clangDaemon
clangdSupport
)
set(FEATURE_MODULES ${FEATURE_MODULES} dummyFeatureModule PARENT_SCOPE)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===--- DummyFeatureModule.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 "FeatureModule.h"
#include "refactor/Tweak.h"
#include "support/Logger.h"

namespace clang::clangd {

class Dummy final : public FeatureModule {
static constexpr const char *TweakID = "DummyTweak";
struct DummyTweak final : public Tweak {
const char *id() const override { return TweakID; }
bool prepare(const Selection &) override { return true; }
Expected<Effect> apply(const Selection &) override {
return error("not implemented");
}
std::string title() const override { return id(); }
llvm::StringLiteral kind() const override {
return llvm::StringLiteral("");
};
};

void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) override {
Out.emplace_back(new DummyTweak);
}
};

static FeatureModuleRegistry::Add<Dummy>
X("dummy", "Dummy feature module with dummy tweak");

// This anchor is used to force the linker to link in the generated object file
// and thus register the Dummy feature module.
volatile int DummyFeatureModuleAnchorSource = 0;

} // namespace clang::clangd