[clang-tools-extra] feat: highlight for import, export keyword of CXX Module (PR #204511)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 27 06:45:02 PDT 2026
https://github.com/Decodetalkers updated https://github.com/llvm/llvm-project/pull/204511
>From fe14332bfc0507e85cb9fa86c6b8b9acb979950c Mon Sep 17 00:00:00 2001
From: ShootingStarDragons <ShootingStarDragons at protonmail.com>
Date: Thu, 18 Jun 2026 18:08:05 +0900
Subject: [PATCH 1/4] feat: highlight for keyword of import, export in CXX
module
This pr aims to add highlight for import and export keyword
And induced new SemanticHighlight `keyword`
---
.../clangd/SemanticHighlighting.cpp | 17 +++++++++++++++++
clang-tools-extra/clangd/SemanticHighlighting.h | 1 +
2 files changed, 18 insertions(+)
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 856904bc810d1..19b5af7e58f30 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -582,6 +582,19 @@ class CollectExtraHighlightings
return true;
}
+ bool VisitImportDecl(const ImportDecl *D) {
+ H.addToken(D->getLocation(), HighlightingKind::Keyword);
+ for (const auto ModuleLoc : D->getIdentifierLocs()) {
+ H.addToken(ModuleLoc, HighlightingKind::Namespace)
+ .addModifier(HighlightingModifier::DependentName);
+ }
+ return true;
+ }
+ bool VisitExportDecl(const ExportDecl *D) {
+ H.addToken(D->getLocation(), HighlightingKind::Keyword);
+ return true;
+ }
+
bool VisitTagDecl(TagDecl *D) {
for (TemplateParameterList *TPL : D->getTemplateParameterLists())
H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc());
@@ -1120,6 +1133,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) {
return OS << "Function";
case HighlightingKind::Method:
return OS << "Method";
+ case HighlightingKind::Keyword:
+ return OS << "Keyword";
case HighlightingKind::StaticMethod:
return OS << "StaticMethod";
case HighlightingKind::Field:
@@ -1323,6 +1338,8 @@ llvm::StringRef toSemanticTokenType(HighlightingKind Kind) {
return "function";
case HighlightingKind::Method:
return "method";
+ case HighlightingKind::Keyword:
+ return "keyword";
case HighlightingKind::StaticMethod:
// FIXME: better method with static modifier?
return "function";
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h
index 59d742b83ee52..3b1c12418becb 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.h
+++ b/clang-tools-extra/clangd/SemanticHighlighting.h
@@ -34,6 +34,7 @@ enum class HighlightingKind {
Parameter,
Function,
Method,
+ Keyword,
StaticMethod,
Field,
StaticField,
>From 5d2a238864c0d7b18290316497f93d1b976226d0 Mon Sep 17 00:00:00 2001
From: ShootingStarDragons <ShootingStarDragons at protonmail.com>
Date: Sat, 20 Jun 2026 18:49:58 +0900
Subject: [PATCH 2/4] chore: add highlight for export in CXX Module
since I cannot fix the build for cxx module, so I cannot test import, so
I test export first
---
.../clangd/unittests/SemanticHighlightingTests.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
index f8b7c242be9ff..569760294e455 100644
--- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -1123,6 +1123,18 @@ sizeof...($TemplateParameter[[Elements]]);
)cpp"}},
~ScopeModifierMask);
+ checkHighlightings(R"cpp(
+ module;
+ export module highlight;
+ $Keyword[[export]] void $Function_def[[foo]]() {
+ }
+ )cpp",
+ {{"imp.h", R"cpp(
+ module;
+ export module ABC;
+ )cpp"}},
+ ~ScopeModifierMask);
+
// A separate test for macros in headers.
checkHighlightings(R"cpp(
#include "imp.h"
>From b6d04465c79eff731835d726e2405ab082570fab Mon Sep 17 00:00:00 2001
From: ShootingStarDragons <ShootingStarDragons at protonmail.com>
Date: Mon, 22 Jun 2026 22:31:42 +0900
Subject: [PATCH 3/4] chore: remove keyword and reuse Modifier instead
---
clang-tools-extra/clangd/SemanticHighlighting.cpp | 11 +++--------
clang-tools-extra/clangd/SemanticHighlighting.h | 1 -
.../clangd/unittests/SemanticHighlightingTests.cpp | 4 ++--
3 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 19b5af7e58f30..0be505ba12f59 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -583,15 +583,14 @@ class CollectExtraHighlightings
}
bool VisitImportDecl(const ImportDecl *D) {
- H.addToken(D->getLocation(), HighlightingKind::Keyword);
+ H.addToken(D->getLocation(), HighlightingKind::Modifier);
for (const auto ModuleLoc : D->getIdentifierLocs()) {
- H.addToken(ModuleLoc, HighlightingKind::Namespace)
- .addModifier(HighlightingModifier::DependentName);
+ H.addToken(ModuleLoc, HighlightingKind::Namespace);
}
return true;
}
bool VisitExportDecl(const ExportDecl *D) {
- H.addToken(D->getLocation(), HighlightingKind::Keyword);
+ H.addToken(D->getLocation(), HighlightingKind::Modifier);
return true;
}
@@ -1133,8 +1132,6 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) {
return OS << "Function";
case HighlightingKind::Method:
return OS << "Method";
- case HighlightingKind::Keyword:
- return OS << "Keyword";
case HighlightingKind::StaticMethod:
return OS << "StaticMethod";
case HighlightingKind::Field:
@@ -1338,8 +1335,6 @@ llvm::StringRef toSemanticTokenType(HighlightingKind Kind) {
return "function";
case HighlightingKind::Method:
return "method";
- case HighlightingKind::Keyword:
- return "keyword";
case HighlightingKind::StaticMethod:
// FIXME: better method with static modifier?
return "function";
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h
index 3b1c12418becb..59d742b83ee52 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.h
+++ b/clang-tools-extra/clangd/SemanticHighlighting.h
@@ -34,7 +34,6 @@ enum class HighlightingKind {
Parameter,
Function,
Method,
- Keyword,
StaticMethod,
Field,
StaticField,
diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
index 569760294e455..2fdabb1462b46 100644
--- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -1126,10 +1126,10 @@ sizeof...($TemplateParameter[[Elements]]);
checkHighlightings(R"cpp(
module;
export module highlight;
- $Keyword[[export]] void $Function_def[[foo]]() {
+ $Modifier[[export]] void $Function_def[[foo]]() {
}
)cpp",
- {{"imp.h", R"cpp(
+ {{"imp.cxx", R"cpp(
module;
export module ABC;
)cpp"}},
>From c5e27e1ae921abac3f91e82121b7ae012c381964 Mon Sep 17 00:00:00 2001
From: ShootingStarDragons <ShootingStarDragons at protonmail.com>
Date: Sat, 27 Jun 2026 22:39:20 +0900
Subject: [PATCH 4/4] chore: move sematichighlight to module
MockDirectoryCompilationDataBase can handle the task for module, so the
highttest can be finished there
---
.../unittests/PrerequisiteModulesTest.cpp | 89 +++++++++++++++++++
.../unittests/SemanticHighlightingTests.cpp | 12 ---
clang-tools-extra/clangd/unittests/TestTU.cpp | 2 +-
3 files changed, 90 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
index b88213decea44..f9385634b7d09 100644
--- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
+++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
@@ -9,6 +9,7 @@
/// FIXME: Skip testing on windows temporarily due to the different escaping
/// code mode.
+#include "SemanticHighlighting.h"
#ifndef _WIN32
#include "Annotations.h"
@@ -1612,7 +1613,95 @@ struct TypeFromHeader {};
testing::UnorderedElementsAre(named("TypeFromModule"),
named("TypeFromHeader")));
}
+/// Annotates the input code with provided semantic highlightings. Results look
+/// something like:
+/// class $Class[[X]] {
+/// $Primitive[[int]] $Field[[a]] = 0;
+/// };
+std::string annotate(llvm::StringRef Input,
+ llvm::ArrayRef<HighlightingToken> Tokens) {
+ assert(llvm::is_sorted(
+ Tokens, [](const HighlightingToken &L, const HighlightingToken &R) {
+ return L.R.start < R.R.start;
+ }));
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ unsigned NextChar = 0;
+ for (auto &T : Tokens) {
+ unsigned StartOffset = llvm::cantFail(positionToOffset(Input, T.R.start));
+ unsigned EndOffset = llvm::cantFail(positionToOffset(Input, T.R.end));
+ assert(StartOffset <= EndOffset);
+ assert(NextChar <= StartOffset);
+
+ bool hasDef =
+ T.Modifiers & (1 << uint32_t(HighlightingModifier::Definition));
+ bool hasDecl =
+ T.Modifiers & (1 << uint32_t(HighlightingModifier::Declaration));
+ EXPECT_TRUE(!hasDef || hasDecl);
+
+ OS << Input.substr(NextChar, StartOffset - NextChar);
+ OS << '$' << T.Kind;
+ for (unsigned I = 0;
+ I <= static_cast<uint32_t>(HighlightingModifier::LastModifier); ++I) {
+ if (T.Modifiers & (1 << I)) {
+ // _decl_def is common and redundant, just print _def instead.
+ if (I != uint32_t(HighlightingModifier::Declaration) || !hasDef)
+ OS << '_' << static_cast<HighlightingModifier>(I);
+ }
+ }
+ OS << "[[" << Input.substr(StartOffset, EndOffset - StartOffset) << "]]";
+ NextChar = EndOffset;
+ }
+ OS << Input.substr(NextChar);
+ return std::move(OS.str());
+}
+
+TEST_F(PrerequisiteModulesTests, ModuleSemanticHighlighting) {
+ MockDirectoryCompilationDatabase CDB(TestDir, FS);
+
+ llvm::StringRef AnnotatedCode = R"cpp(
+ module;
+ $Modifier[[import]] $Namespace[[M]];
+ export module highlight;
+ $Modifier[[export]] void $Function_def_globalScope[[foo]]() {
+ }
+)cpp";
+ uint32_t ModifierMask = -1;
+ Annotations UseCpp(AnnotatedCode);
+
+ CDB.addFile("M.cppm", R"cpp(
+export module M;
+export struct TypeFromModule {};
+)cpp");
+
+
+ CDB.addFile("Use.cpp", UseCpp.code());
+
+ ModulesBuilder Builder(CDB);
+ auto Inputs = getInputs("Use.cpp", CDB);
+ Inputs.ModulesManager = &Builder;
+ Inputs.Opts.SkipPreambleBuild = true;
+
+ auto CI = buildCompilerInvocation(Inputs, DiagConsumer);
+ ASSERT_TRUE(CI);
+
+ auto Preamble =
+ buildPreamble(getFullPath("Use.cpp"), *CI, Inputs, /*StoreInMemory=*/true,
+ /*PeambleCallback=*/nullptr);
+ ASSERT_TRUE(Preamble);
+ EXPECT_EQ(Preamble->Preamble.getBounds().Size, 0u);
+
+ auto AST = ParsedAST::build(getFullPath("Use.cpp"), Inputs, std::move(CI), {},
+ Preamble);
+ auto Actual =
+ getSemanticHighlightings(AST.value(), /*IncludeInactiveRegionTokens=*/true);
+ for (auto &Token : Actual)
+ Token.Modifiers &= ModifierMask;
+
+ EXPECT_EQ(AnnotatedCode, annotate(UseCpp.code(), Actual));
+}
} // namespace
} // namespace clang::clangd
diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
index 2fdabb1462b46..f8b7c242be9ff 100644
--- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -1123,18 +1123,6 @@ sizeof...($TemplateParameter[[Elements]]);
)cpp"}},
~ScopeModifierMask);
- checkHighlightings(R"cpp(
- module;
- export module highlight;
- $Modifier[[export]] void $Function_def[[foo]]() {
- }
- )cpp",
- {{"imp.cxx", R"cpp(
- module;
- export module ABC;
- )cpp"}},
- ~ScopeModifierMask);
-
// A separate test for macros in headers.
checkHighlightings(R"cpp(
#include "imp.h"
diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp
index e2c2e3f000b66..ef29bc087b83c 100644
--- a/clang-tools-extra/clangd/unittests/TestTU.cpp
+++ b/clang-tools-extra/clangd/unittests/TestTU.cpp
@@ -63,9 +63,9 @@ ParseInputs TestTU::inputs(MockFS &FS) const {
// Put the file name at the end -- this allows the extra arg (-xc++) to
// override the language setting.
Argv.push_back(FullFilename);
-
auto Mangler = CommandMangler::forTests();
Mangler(Inputs.CompileCommand, FullFilename);
+
Inputs.CompileCommand.Filename = FullFilename;
Inputs.CompileCommand.Directory = testRoot();
Inputs.Contents = Code;
More information about the cfe-commits
mailing list