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