[clang] [clang-tools-extra] [clangd] Support #embed highlighting in clangd (PR #212036)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jul 25 09:48:00 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangd
Author: Yihan Wang (yronglin)
<details>
<summary>Changes</summary>
This PR add `#embed` highlighting support for clangd.
<img width="412" height="142" alt="image" src="https://github.com/user-attachments/assets/e6a1a677-4c37-461a-8512-541f44327238" />
<img width="731" height="242" alt="image" src="https://github.com/user-attachments/assets/7f9339b9-d7be-4349-a6bd-b8b7e09cd036" />
---
Full diff: https://github.com/llvm/llvm-project/pull/212036.diff
15 Files Affected:
- (modified) clang-tools-extra/clangd/CollectMacros.cpp (+20)
- (modified) clang-tools-extra/clangd/CollectMacros.h (+6)
- (modified) clang-tools-extra/clangd/SemanticHighlighting.cpp (+8)
- (modified) clang-tools-extra/clangd/SemanticHighlighting.h (+1)
- (added) clang-tools-extra/clangd/test/semantic-tokens-embed.test (+72)
- (modified) clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp (+17)
- (modified) clang/lib/Frontend/DependencyFile.cpp (+2-1)
- (modified) clang/lib/Frontend/PrintPreprocessedOutput.cpp (+5-3)
- (modified) clang/lib/Lex/PPDirectives.cpp (+12-3)
- (modified) clang/lib/Tooling/Syntax/Tokens.cpp (+30)
- (added) clang/test/CoverageMapping/embed.c (+28)
- (modified) clang/test/Preprocessor/embed_file_not_found_quote.c (+6-1)
- (modified) clang/unittests/Lex/PPCallbacksTest.cpp (+22-7)
- (modified) clang/unittests/Lex/PPDependencyDirectivesTest.cpp (+2-1)
- (modified) clang/unittests/Tooling/Syntax/TokensTest.cpp (+30)
``````````diff
diff --git a/clang-tools-extra/clangd/CollectMacros.cpp b/clang-tools-extra/clangd/CollectMacros.cpp
index 1e7d765f0b6f1..d03dec4a07df4 100644
--- a/clang-tools-extra/clangd/CollectMacros.cpp
+++ b/clang-tools-extra/clangd/CollectMacros.cpp
@@ -11,6 +11,7 @@
#include "Protocol.h"
#include "SourceCode.h"
#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Syntax/Tokens.h"
#include "llvm/ADT/STLExtras.h"
#include <cstddef>
@@ -95,6 +96,25 @@ void CollectMainFileMacros::Defined(const Token &MacroName,
/*InConditionalDirective=*/true);
}
+void CollectMainFileMacros::EmbedDirective(SourceLocation HashLoc, StringRef,
+ bool, OptionalFileEntryRef,
+ const LexEmbedParametersResult &) {
+ if (!InMainFile)
+ return;
+ auto Embed = Lexer::findNextToken(HashLoc, SM, PP.getLangOpts());
+ if (!Embed)
+ return;
+
+ SourceLocation HashEnd =
+ Lexer::getLocForEndOfToken(HashLoc, 0, SM, PP.getLangOpts());
+ if (HashEnd.isValid())
+ Out.EmbedDirectiveTokens.push_back(
+ halfOpenToRange(SM, CharSourceRange::getCharRange(HashLoc, HashEnd)));
+ Out.EmbedDirectiveTokens.push_back(
+ halfOpenToRange(SM, CharSourceRange::getCharRange(Embed->getLocation(),
+ Embed->getEndLoc())));
+}
+
void CollectMainFileMacros::SourceRangeSkipped(SourceRange R,
SourceLocation EndifLoc) {
if (!InMainFile)
diff --git a/clang-tools-extra/clangd/CollectMacros.h b/clang-tools-extra/clangd/CollectMacros.h
index 20a3fc24d759c..9ef8791d4e435 100644
--- a/clang-tools-extra/clangd/CollectMacros.h
+++ b/clang-tools-extra/clangd/CollectMacros.h
@@ -42,6 +42,8 @@ struct MainFileMacros {
// reference to an undefined macro. Store them separately, e.g. for semantic
// highlighting.
std::vector<MacroOccurrence> UnknownMacros;
+ // Ranges of the `#` and `embed` tokens in active #embed directives.
+ std::vector<Range> EmbedDirectiveTokens;
// Ranges skipped by the preprocessor due to being inactive.
std::vector<Range> SkippedRanges;
};
@@ -81,6 +83,10 @@ class CollectMainFileMacros : public PPCallbacks {
void Defined(const Token &MacroName, const MacroDefinition &MD,
SourceRange Range) override;
+ void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled,
+ OptionalFileEntryRef File,
+ const LexEmbedParametersResult &Params) override;
+
void SourceRangeSkipped(SourceRange R, SourceLocation EndifLoc) override;
// Called when the AST build is done to disable further recording
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index d2d110b72011b..106931db30cd5 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -1116,6 +1116,9 @@ getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens) {
for (const auto &M : AST.getMacros().UnknownMacros)
AddMacro(M);
+ for (const Range &R : AST.getMacros().EmbedDirectiveTokens)
+ Builder.addToken(R, HighlightingKind::Keyword);
+
return std::move(Builder).collect(AST);
}
@@ -1169,6 +1172,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) {
return OS << "Bracket";
case HighlightingKind::Label:
return OS << "Label";
+ case HighlightingKind::Keyword:
+ return OS << "Keyword";
case HighlightingKind::InactiveCode:
return OS << "InactiveCode";
}
@@ -1200,6 +1205,7 @@ highlightingKindFromString(llvm::StringRef Name) {
{"Modifier", HighlightingKind::Modifier},
{"Operator", HighlightingKind::Operator},
{"Bracket", HighlightingKind::Bracket},
+ {"Keyword", HighlightingKind::Keyword},
{"InactiveCode", HighlightingKind::InactiveCode},
};
@@ -1370,6 +1376,8 @@ llvm::StringRef toSemanticTokenType(HighlightingKind Kind) {
return "bracket";
case HighlightingKind::Label:
return "label";
+ case HighlightingKind::Keyword:
+ return "keyword";
case HighlightingKind::InactiveCode:
return "comment";
}
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h
index 59d742b83ee52..828779bebbaa0 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.h
+++ b/clang-tools-extra/clangd/SemanticHighlighting.h
@@ -53,6 +53,7 @@ enum class HighlightingKind {
Operator,
Bracket,
Label,
+ Keyword,
// This one is different from the other kinds as it's a line style
// rather than a token style.
diff --git a/clang-tools-extra/clangd/test/semantic-tokens-embed.test b/clang-tools-extra/clangd/test/semantic-tokens-embed.test
new file mode 100644
index 0000000000000..da2c4af3ec09d
--- /dev/null
+++ b/clang-tools-extra/clangd/test/semantic-tokens-embed.test
@@ -0,0 +1,72 @@
+# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{
+ "capabilities":{"textDocument":{"semanticTokens":{"dynamicRegistration":true}}},
+ "initializationOptions":{"fallbackFlags":["-std=c23"]}
+}}
+# CHECK: "label",
+# CHECK-NEXT: "keyword",
+# CHECK-NEXT: "comment"
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+ "uri":"test:///foo.c",
+ "languageId":"c",
+ "text":"#define P 1\nint data[] = {\n#embed \"foo.c\" prefix(P,) suffix(,P)\n};\nint after;"
+}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/semanticTokens/full","params":{
+ "textDocument":{"uri":"test:///foo.c"}
+}}
+# CHECK: "id": 1,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": {
+# CHECK-NEXT: "data": [
+# P macro definition.
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 8,
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 19,
+# CHECK-NEXT: 131073,
+# data declaration.
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 4,
+# CHECK-NEXT: 4,
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 131075,
+# # in embed directive.
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 24,
+# CHECK-NEXT: 0,
+# embed directive keyword.
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 5,
+# CHECK-NEXT: 24,
+# CHECK-NEXT: 0,
+# P in prefix().
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 21,
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 19,
+# CHECK-NEXT: 131072,
+# P in suffix().
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 12,
+# CHECK-NEXT: 1,
+# CHECK-NEXT: 19,
+# CHECK-NEXT: 131072,
+# after declaration.
+# CHECK-NEXT: 2,
+# CHECK-NEXT: 4,
+# CHECK-NEXT: 5,
+# CHECK-NEXT: 0,
+# CHECK-NEXT: 131075
+# CHECK-NEXT: ],
+# CHECK-NEXT: "resultId": "1"
+# CHECK-NEXT: }
+---
+{"jsonrpc":"2.0","id":2,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
index a7ebd146c297b..4eca7b8a6e8fb 100644
--- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -1156,6 +1156,23 @@ sizeof...($TemplateParameter[[Elements]]);
~ScopeModifierMask, {"-isystemSystemSDK/"});
}
+TEST(SemanticHighlighting, EmbedDirectives) {
+ checkHighlightings(
+ R"cpp(
+ /*error-ok*/
+ int $Variable_def[[data]][] = {
+ $Keyword[[#]]$Keyword[[embed]] "data.bin" suffix(,)
+ $Keyword[[#]] /**/ $Keyword[[embed]] "empty.bin"
+ $Keyword[[#]]$Keyword[[embed]] "missing.bin"
+ };
+#if 0
+$InactiveCode[[#embed "inactive.bin"]]
+#endif
+ )cpp",
+ {{"data.bin", "a"}, {"empty.bin", ""}}, ~ScopeModifierMask,
+ {"-std=c23", "-xc"});
+}
+
TEST(SemanticHighlighting, NoCrash) {
// Testcases where we are just testing that computation of the
// semantic tokens does not trigger a crash.
diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp
index 00f4a54269cfa..7c005c954aba2 100644
--- a/clang/lib/Frontend/DependencyFile.cpp
+++ b/clang/lib/Frontend/DependencyFile.cpp
@@ -66,7 +66,8 @@ struct DepCollectorPPCallbacks : public PPCallbacks {
void EmbedDirective(SourceLocation, StringRef, bool,
OptionalFileEntryRef File,
const LexEmbedParametersResult &) override {
- assert(File && "expected to only be called when the file is found");
+ if (!File)
+ return;
StringRef FileName =
llvm::sys::path::remove_leading_dotslash(File->getName());
DepCollector.maybeAddDependency(FileName,
diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
index 02266882c4c4a..ba21df36d7675 100644
--- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -465,17 +465,19 @@ void PrintPPOutputPPCallbacks::EmbedDirective(
*OS << " prefix(";
PrintToks(Params.MaybePrefixParam->Tokens);
*OS << ")";
- NumToksToSkip += Params.MaybePrefixParam->Tokens.size();
+ if (File)
+ NumToksToSkip += Params.MaybePrefixParam->Tokens.size();
}
if (Params.MaybeSuffixParam) {
*OS << " suffix(";
PrintToks(Params.MaybeSuffixParam->Tokens);
*OS << ")";
- NumToksToSkip += Params.MaybeSuffixParam->Tokens.size();
+ if (File)
+ NumToksToSkip += Params.MaybeSuffixParam->Tokens.size();
}
// We may need to skip the annotation token.
- if (SkipAnnotToks)
+ if (File && SkipAnnotToks)
NumToksToSkip++;
*OS << " /* clang -E -dE */";
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index c161f6a03593e..c522d3f5f1c2e 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -4056,7 +4056,9 @@ void Preprocessor::HandleEmbedDirectiveImpl(
size_t TokCount = Toks.size();
auto NewToks = std::make_unique<Token[]>(TokCount);
llvm::copy(Toks, NewToks.get());
- EnterTokenStream(std::move(NewToks), TokCount, true, true);
+ EnterTokenStream(std::move(NewToks), TokCount,
+ /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
}
return;
}
@@ -4089,7 +4091,9 @@ void Preprocessor::HandleEmbedDirectiveImpl(
}
assert(CurIdx == TotalNumToks && "Calculated the incorrect number of tokens");
- EnterTokenStream(std::move(Toks), TotalNumToks, true, true);
+ EnterTokenStream(std::move(Toks), TotalNumToks,
+ /*DisableMacroExpansion=*/true,
+ /*IsReinject=*/false);
}
void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc,
@@ -4145,7 +4149,12 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc,
this->LookupEmbedFile(Filename, isAngled, /*OpenFile=*/true);
if (!MaybeFileRef) {
// could not find file
- if (Callbacks && Callbacks->EmbedFileNotFound(Filename)) {
+ bool SuppressDiagnostic =
+ Callbacks && Callbacks->EmbedFileNotFound(Filename);
+ if (Callbacks)
+ Callbacks->EmbedDirective(HashLoc, Filename, isAngled, std::nullopt,
+ *Params);
+ if (SuppressDiagnostic) {
return;
}
Diag(FilenameTok, diag::err_pp_file_not_found) << Filename;
diff --git a/clang/lib/Tooling/Syntax/Tokens.cpp b/clang/lib/Tooling/Syntax/Tokens.cpp
index 927333fda18bb..cf7f09a848c3e 100644
--- a/clang/lib/Tooling/Syntax/Tokens.cpp
+++ b/clang/lib/Tooling/Syntax/Tokens.cpp
@@ -15,6 +15,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/PPEmbedParameters.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/ArrayRef.h"
@@ -658,9 +659,36 @@ class TokenCollector::CollectPPExpansions : public PPCallbacks {
Collector->Expansions[Range.getBegin()] = Range.getEnd();
LastExpansionEnd = Range.getEnd();
}
+
+ void EmbedDirective(SourceLocation, StringRef, bool,
+ OptionalFileEntryRef File,
+ const LexEmbedParametersResult &Params) override {
+ if (!Collector || !File)
+ return;
+ auto Record = [&](const auto &Param) {
+ if (!Param)
+ return;
+ for (const clang::Token &T : Param->Tokens)
+ ++EmbedTokensToIgnore[T.getLocation()];
+ };
+ Record(Params.MaybeIfEmptyParam);
+ Record(Params.MaybePrefixParam);
+ Record(Params.MaybeSuffixParam);
+ }
+
+ bool consumeIgnoredEmbedToken(const clang::Token &T) {
+ auto It = EmbedTokensToIgnore.find(T.getLocation());
+ if (It == EmbedTokensToIgnore.end())
+ return false;
+ if (--It->second == 0)
+ EmbedTokensToIgnore.erase(It);
+ return true;
+ }
+
// FIXME: handle directives like #pragma, #include, etc.
private:
TokenCollector *Collector;
+ llvm::DenseMap<SourceLocation, unsigned> EmbedTokensToIgnore;
/// Used to detect recursive macro expansions.
SourceLocation LastExpansionEnd;
};
@@ -681,6 +709,8 @@ class TokenCollector::CollectPPExpansions : public PPCallbacks {
TokenCollector::TokenCollector(Preprocessor &PP) : PP(PP) {
// Collect the expanded token stream during preprocessing.
PP.setTokenWatcher([this](const clang::Token &T) {
+ if (Collector->consumeIgnoredEmbedToken(T))
+ return;
if (T.is(tok::annot_module_name)) {
auto &SM = this->PP.getSourceManager();
StringRef Text = Lexer::getSourceText(
diff --git a/clang/test/CoverageMapping/embed.c b/clang/test/CoverageMapping/embed.c
new file mode 100644
index 0000000000000..5c23b30a987cd
--- /dev/null
+++ b/clang/test/CoverageMapping/embed.c
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -std=c23 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s
+
+int found(void) {
+ int data[] = {
+#embed "Inputs/ends_a_scope_only" limit(1) suffix(, 2,) prefix(1,)
+// found comment
+
+ 3
+ };
+ return data[0];
+}
+
+int empty(void) {
+ int data[] = {
+#embed "Inputs/ends_a_scope_only" limit(0) if_empty(1,)
+// empty comment
+
+ 2
+ };
+ return data[0];
+}
+
+// CHECK-LABEL: found:
+// CHECK-NEXT: File 0, 3:17 -> 11:2 = #0
+// CHECK-NEXT: Skipped,File 0, 6:1 -> 7:1 = 0
+// CHECK-LABEL: empty:
+// CHECK-NEXT: File 0, 13:17 -> 21:2 = #0
+// CHECK-NEXT: Skipped,File 0, 16:1 -> 17:1 = 0
diff --git a/clang/test/Preprocessor/embed_file_not_found_quote.c b/clang/test/Preprocessor/embed_file_not_found_quote.c
index bf9c62b55c99e..4d9f740364fe0 100644
--- a/clang/test/Preprocessor/embed_file_not_found_quote.c
+++ b/clang/test/Preprocessor/embed_file_not_found_quote.c
@@ -1,4 +1,9 @@
// RUN: %clang_cc1 -std=c23 %s -E -verify
+// RUN: %clang_cc1 -std=c23 %s -E -dE -verify | FileCheck %s
+// expected-error at +1 {{'nfejfNejAKFe' file not found}}
#embed "nfejfNejAKFe"
-// expected-error at -1 {{'nfejfNejAKFe' file not found}}
+int after;
+
+// CHECK: #embed "nfejfNejAKFe" /* clang -E -dE */
+// CHECK-NEXT: int after;
diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp b/clang/unittests/Lex/PPCallbacksTest.cpp
index 9533fbc776e6e..e2240d98a3652 100644
--- a/clang/unittests/Lex/PPCallbacksTest.cpp
+++ b/clang/unittests/Lex/PPCallbacksTest.cpp
@@ -473,7 +473,8 @@ TEST_F(PPCallbacksTest, EmbedFileNotFoundChained) {
llvm::MemoryBuffer::getMemBuffer(SourceText);
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
- unsigned int NumCalls = 0;
+ unsigned int NumNotFoundCalls = 0;
+ unsigned int NumDirectiveCalls = 0;
HeaderSearchOptions HSOpts;
TrivialModuleLoader ModLoader;
PreprocessorOptions PPOpts;
@@ -488,26 +489,40 @@ TEST_F(PPCallbacksTest, EmbedFileNotFoundChained) {
class EmbedFileNotFoundCallbacks : public PPCallbacks {
public:
- unsigned int &NumCalls;
+ unsigned int &NumNotFoundCalls;
+ unsigned int &NumDirectiveCalls;
- EmbedFileNotFoundCallbacks(unsigned int &NumCalls) : NumCalls(NumCalls) {}
+ EmbedFileNotFoundCallbacks(unsigned int &NumNotFoundCalls,
+ unsigned int &NumDirectiveCalls)
+ : NumNotFoundCalls(NumNotFoundCalls),
+ NumDirectiveCalls(NumDirectiveCalls) {}
bool EmbedFileNotFound(StringRef FileName) override {
- NumCalls++;
+ NumNotFoundCalls++;
return true;
}
+
+ void EmbedDirective(SourceLocation, StringRef, bool,
+ OptionalFileEntryRef File,
+ const LexEmbedParametersResult &) override {
+ EXPECT_FALSE(File);
+ NumDirectiveCalls++;
+ }
};
// Add two instances of `EmbedFileNotFoundCallbacks` to ensure the
// preprocessor is using an instance of `PPChainedCallbaks`.
- PP.addPPCallbacks(std::make_unique<EmbedFileNotFoundCallbacks>(NumCalls));
- PP.addPPCallbacks(std::make_unique<EmbedFileNotFoundCallbacks>(NumCalls));
+ PP.addPPCallbacks(std::make_unique<EmbedFileNotFoundCallbacks>(
+ NumNotFoundCalls, NumDirectiveCalls));
+ PP.addPPCallbacks(std::make_unique<EmbedFileNotFoundCallbacks>(
+ NumNotFoundCalls, NumDirectiveCalls));
// Lex source text.
PP.EnterMainSourceFile();
PP.LexTokensUntilEOF();
- ASSERT_EQ(2u, NumCalls);
+ ASSERT_EQ(2u, NumNotFoundCalls);
+ ASSERT_EQ(2u, NumDirectiveCalls);
ASSERT_EQ(0u, DiagConsumer->getNumErrors());
}
diff --git a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
index 6beb66d1a36f5..bd7187326e253 100644
--- a/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
+++ b/clang/unittests/Lex/PPDependencyDirectivesTest.cpp
@@ -80,7 +80,8 @@ class EmbedCollector : public PPCallbacks {
void EmbedDirective(SourceLocation, StringRef, bool,
OptionalFileEntryRef File,
const LexEmbedParametersResult &) override {
- assert(File && "expected to only be called when the file is found");
+ if (!File)
+ return;
StringRef Filename =
llvm::sys::path::remove_leading_dotslash(File->getName());
EmbeddedFiles.push_back(Filename);
diff --git a/clang/unittests/Tooling/Syntax/TokensTest.cpp b/clang/unittests/Tooling/Syntax/TokensTest.cpp
index 6418cc8f87d9d..68f0e53bf7512 100644
--- a/clang/unittests/Tooling/Syntax/TokensTest.cpp
+++ b/clang/unittests/Tooling/Syntax/TokensTest.cpp
@@ -1201,4 +1201,34 @@ TEST_F(TokenCollectorTest, CXX20ModuleImportPartition) {
Kind(tok::annot_module_name), Kind(tok::semi),
Kind(tok::eof)));
}
+
+TEST_F(TokenCollectorTest, CXX26Embed) {
+ LangStandard = "-std=c++26";
+ addFile("./data.bin", "a");
+ recordTokens(R"cpp(
+ #define ONE 1
+ #define TWO 2
+ struct S {
+ unsigned char data[3] = {
+ #embed "data.bin" suffix(, TWO) prefix(ONE,)
+ };
+ unsigned char empty[2] = {
+ #embed "data.bin" limit(0) if_empty(ONE, TWO)
+ };
+ };
+ )cpp");
+
+ EXPECT_THAT(
+ Buffer.expandedTokens(),
+ ElementsAre(
+ Kind(tok::kw_struct), Kind(tok::identifier), Kind(tok::l_brace),
+ Kind(tok::kw_unsigned), Kind(tok::kw_char), Kind(tok::identifier),
+ Kind(tok::l_square), Kind(tok::numeric_constant), Kind(tok::r_square),
+ Kind(tok::equal), Kind(tok::l_brace), Kind(tok::r_brace),
+ Kind(tok::semi), Kind(tok::kw_unsigned), Kind(tok::kw_char),
+ Kind(tok::identifier), Kind(tok::l_square),
+ Kind(tok::numeric_constant), Kind(tok::r_square), Kind(tok::equal),
+ Kind(tok::l_brace), Kind(tok::r_brace), Kind(tok::semi),
+ Kind(tok::r_brace), Kind(tok::semi), Kind(tok::eof)));
+}
} // namespace
``````````
</details>
https://github.com/llvm/llvm-project/pull/212036
More information about the cfe-commits
mailing list