https://github.com/aketchum15 updated 
https://github.com/llvm/llvm-project/pull/140959

>From f6abf494febe7becb42dd24071a3309c2d4b66d5 Mon Sep 17 00:00:00 2001
From: Ruihua Dong <dongruihua....@alibaba-inc.com>
Date: Fri, 17 Jan 2025 12:52:19 +0500
Subject: [PATCH 1/6] [clangd] Implement simple folding of preprocessor
 branches

Extract directive branches information from DirectiveTree, fold branches
that don't end with eof.

Fixes https://github.com/clangd/clangd/issues/1661
---
 .../clangd/SemanticSelection.cpp              | 18 +++++++
 .../clangd/support/DirectiveTree.cpp          | 54 +++++++++++++++++++
 .../clangd/support/DirectiveTree.h            |  4 ++
 3 files changed, 76 insertions(+)

diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp 
b/clang-tools-extra/clangd/SemanticSelection.cpp
index dd7116e619e6d..57e7ff8b97c65 100644
--- a/clang-tools-extra/clangd/SemanticSelection.cpp
+++ b/clang-tools-extra/clangd/SemanticSelection.cpp
@@ -220,6 +220,24 @@ getFoldingRanges(const std::string &Code, bool 
LineFoldingOnly) {
   auto EndPosition = [&](const Token &T) {
     return offsetToPosition(Code, EndOffset(T));
   };
+
+  // Preprocessor directives
+  auto PPRanges = pairDirectiveRanges(DirectiveStructure, OrigStream);
+  for (const auto &R : PPRanges) {
+    auto BTok = OrigStream.tokens()[R.Begin];
+    auto ETok = OrigStream.tokens()[R.End];
+    if (ETok.Kind == tok::eof)
+      continue;
+    if (BTok.Line >= ETok.Line)
+      continue;
+
+    Position Start = EndPosition(BTok);
+    Position End = StartPosition(ETok);
+    if (LineFoldingOnly)
+      End.line--;
+    AddFoldingRange(Start, End, FoldingRange::REGION_KIND);
+  }
+
   auto Tokens = ParseableStream.tokens();
   // Brackets.
   for (const auto &Tok : Tokens) {
diff --git a/clang-tools-extra/clangd/support/DirectiveTree.cpp 
b/clang-tools-extra/clangd/support/DirectiveTree.cpp
index 7ea08add7a107..25ef1dcffd750 100644
--- a/clang-tools-extra/clangd/support/DirectiveTree.cpp
+++ b/clang-tools-extra/clangd/support/DirectiveTree.cpp
@@ -356,5 +356,59 @@ TokenStream DirectiveTree::stripDirectives(const 
TokenStream &In) const {
   return Out;
 }
 
+namespace {
+class RangePairer {
+  std::vector<Token::Range> &Ranges;
+
+public:
+  RangePairer(std::vector<Token::Range> &Ranges) : Ranges(Ranges) {}
+
+  void walk(const DirectiveTree &T) {
+    for (const auto &C : T.Chunks)
+      std::visit(*this, C);
+  }
+
+  void operator()(const DirectiveTree::Code &C) {}
+
+  void operator()(const DirectiveTree::Directive &) {}
+
+  void operator()(const DirectiveTree::Conditional &C) {
+    Token::Range Range;
+    Token::Index Last;
+    auto First = true;
+    for (const auto &B : C.Branches) {
+      if (First) {
+        First = false;
+      } else {
+        Range = {Last, B.first.Tokens.Begin};
+        Ranges.push_back(Range);
+      }
+      Last = B.first.Tokens.Begin;
+    }
+    Range = {Last, C.End.Tokens.Begin};
+    Ranges.push_back(Range);
+
+    for (const auto &B : C.Branches)
+      walk(B.second);
+  }
+};
+} // namespace
+
+std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree,
+                                              const TokenStream &Code) {
+  std::vector<Token::Range> Ranges;
+  RangePairer(Ranges).walk(Tree);
+
+  // Transform paired ranges to start with last token in its logical line
+  for (auto &R : Ranges) {
+    const Token *Tok = &Code.tokens()[R.Begin + 1];
+    while (Tok->Kind != tok::eof && !Tok->flag(LexFlags::StartsPPLine))
+      ++Tok;
+    Tok = Tok - 1;
+    R.Begin = Tok->OriginalIndex;
+  }
+  return Ranges;
+}
+
 } // namespace clangd
 } // namespace clang
diff --git a/clang-tools-extra/clangd/support/DirectiveTree.h 
b/clang-tools-extra/clangd/support/DirectiveTree.h
index 34f5a888863f2..373af322bca0c 100644
--- a/clang-tools-extra/clangd/support/DirectiveTree.h
+++ b/clang-tools-extra/clangd/support/DirectiveTree.h
@@ -124,6 +124,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &,
 /// The choices are stored in Conditional::Taken nodes.
 void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code);
 
+/// Pairs preprocessor conditional directives and computes their token ranges.
+std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree,
+                                              const TokenStream &Code);
+
 } // namespace clangd
 } // namespace clang
 

>From 6ddc0539ffe3a654dd310b9e91fea6a487000a8f Mon Sep 17 00:00:00 2001
From: Adam Ketchum <ketc...@purdue.edu>
Date: Wed, 21 May 2025 16:17:57 -0400
Subject: [PATCH 2/6] Add unit test for preprocessor directive folding

---
 .../unittests/SemanticSelectionTests.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp 
b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
index 7faef6f95d8f9..b4249590c7f66 100644
--- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
@@ -370,6 +370,26 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
         //[[ foo
         /* bar */]]
       )cpp",
+      R"cpp(
+        //Ignore non-conditional directives
+        #define A 1
+
+        void func() {[[
+          int Variable = 100;
+
+          #ifdef FOO[[
+            Variable = 1;
+          ]]#else[[
+            Variable = 2;
+            //handle nested directives
+            #if 1[[
+              Variable = 3;
+            ]]#endif
+          ]]#endif
+
+
+          ]]}
+      )cpp",
   };
   for (const char *Test : Tests) {
     auto T = Annotations(Test);

>From 265f452340aa451606cb1c40469a13e44bed3a30 Mon Sep 17 00:00:00 2001
From: Adam Ketchum <ketc...@purdue.edu>
Date: Tue, 5 Aug 2025 21:11:46 -0400
Subject: [PATCH 3/6] Update to test nested pp regions in inactive regions and
 incomplete pp regions

---
 .../unittests/SemanticSelectionTests.cpp      | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp 
b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
index b4249590c7f66..ec6ef3bed54e9 100644
--- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
@@ -379,6 +379,9 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
 
           #ifdef FOO[[
             Variable = 1;
+            #if 1[[
+                Variable = 4;
+            ]]#endif
           ]]#else[[
             Variable = 2;
             //handle nested directives
@@ -390,6 +393,22 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
 
           ]]}
       )cpp",
+      R"cpp(
+        int Variable = 0;
+        #if defined(WALDO)
+            Variable = 1;
+        #
+      )cpp",
+      R"cpp(
+        int Variable = 0;
+        #if defined(WALDO)[[
+            Variable = 1;
+        ]]#elif 1[[
+            Variable = 2;
+        ]]#else
+            Variable = 3;
+        #
+      )cpp",
   };
   for (const char *Test : Tests) {
     auto T = Annotations(Test);
@@ -400,6 +419,12 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
   }
 }
 
+#if defined(WALDO)
+#elif 1
+#else
+#endif
+
+
 TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
   const char *Tests[] = {
       R"cpp(

>From 34d8cdf40b1c010c3a50a6ab1aca05152c9e23e8 Mon Sep 17 00:00:00 2001
From: Adam Ketchum <ketc...@purdue.edu>
Date: Tue, 5 Aug 2025 21:13:34 -0400
Subject: [PATCH 4/6] Fix creating invalid rangs for incomplete PP regions

---
 clang-tools-extra/clangd/support/DirectiveTree.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clangd/support/DirectiveTree.cpp 
b/clang-tools-extra/clangd/support/DirectiveTree.cpp
index 25ef1dcffd750..89cfa6a365150 100644
--- a/clang-tools-extra/clangd/support/DirectiveTree.cpp
+++ b/clang-tools-extra/clangd/support/DirectiveTree.cpp
@@ -385,8 +385,11 @@ class RangePairer {
       }
       Last = B.first.Tokens.Begin;
     }
-    Range = {Last, C.End.Tokens.Begin};
-    Ranges.push_back(Range);
+
+    if (C.End.Kind != tok::pp_not_keyword) {
+        Range = {Last, C.End.Tokens.Begin};
+        Ranges.push_back(Range);
+    }
 
     for (const auto &B : C.Branches)
       walk(B.second);

>From 9aa17b4e7dda37315308948b2a3adf1cf6a1f8dd Mon Sep 17 00:00:00 2001
From: Adam Ketchum <ketc...@purdue.edu>
Date: Tue, 5 Aug 2025 21:15:18 -0400
Subject: [PATCH 5/6] formatting changes and updating FIXME comment

---
 clang-tools-extra/clangd/SemanticSelection.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp 
b/clang-tools-extra/clangd/SemanticSelection.cpp
index 57e7ff8b97c65..33b829c7744e8 100644
--- a/clang-tools-extra/clangd/SemanticSelection.cpp
+++ b/clang-tools-extra/clangd/SemanticSelection.cpp
@@ -175,9 +175,8 @@ llvm::Expected<std::vector<FoldingRange>> 
getFoldingRanges(ParsedAST &AST) {
   return collectFoldingRanges(SyntaxTree, TM);
 }
 
-// FIXME( usaxena95): Collect PP conditional regions, includes and other code
-// regions (e.g. public/private/protected sections of classes, control flow
-// statement bodies).
+// FIXME( usaxena95): Collect includes and other code regions (e.g. 
+// public/private/protected sections of classes, control flow statement 
bodies).
 // Related issue: https://github.com/clangd/clangd/issues/310
 llvm::Expected<std::vector<FoldingRange>>
 getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
@@ -186,12 +185,6 @@ getFoldingRanges(const std::string &Code, bool 
LineFoldingOnly) {
   auto DirectiveStructure = DirectiveTree::parse(OrigStream);
   chooseConditionalBranches(DirectiveStructure, OrigStream);
 
-  // FIXME: Provide ranges in the disabled-PP regions as well.
-  auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream);
-
-  auto ParseableStream = cook(Preprocessed, genericLangOpts());
-  pairBrackets(ParseableStream);
-
   std::vector<FoldingRange> Result;
   auto AddFoldingRange = [&](Position Start, Position End,
                              llvm::StringLiteral Kind) {
@@ -238,7 +231,13 @@ getFoldingRanges(const std::string &Code, bool 
LineFoldingOnly) {
     AddFoldingRange(Start, End, FoldingRange::REGION_KIND);
   }
 
+  auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream);
+
+  auto ParseableStream = cook(Preprocessed, genericLangOpts());
+  pairBrackets(ParseableStream);
+
   auto Tokens = ParseableStream.tokens();
+
   // Brackets.
   for (const auto &Tok : Tokens) {
     if (auto *Paired = Tok.pair()) {
@@ -258,6 +257,7 @@ getFoldingRanges(const std::string &Code, bool 
LineFoldingOnly) {
     return OriginalToken(T).Length >= 2 &&
            Code.substr(StartOffset(T), 2) == "/*";
   };
+
   // Multi-line comments.
   for (auto *T = Tokens.begin(); T != Tokens.end();) {
     if (T->Kind != tok::comment) {

>From 0b63c7de2db8cb660cd36d581e176de0618ed9c0 Mon Sep 17 00:00:00 2001
From: Adam Ketchum <ketc...@purdue.edu>
Date: Tue, 5 Aug 2025 21:58:42 -0400
Subject: [PATCH 6/6] remove scratchpad code

---
 .../clangd/unittests/SemanticSelectionTests.cpp             | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp 
b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
index ec6ef3bed54e9..b4d67b778a70d 100644
--- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
@@ -419,12 +419,6 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
   }
 }
 
-#if defined(WALDO)
-#elif 1
-#else
-#endif
-
-
 TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
   const char *Tests[] = {
       R"cpp(

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to