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.
&lt;img width="412" height="142" alt="image" 
src="https://github.com/user-attachments/assets/e6a1a677-4c37-461a-8512-541f44327238";
 /&gt;
&lt;img width="731" height="242" alt="image" 
src="https://github.com/user-attachments/assets/7f9339b9-d7be-4349-a6bd-b8b7e09cd036";
 /&gt;


---
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@+1 {{'nfejfNejAKFe' file not found}}
 #embed "nfejfNejAKFe"
-// expected-error@-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
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to