Skip to content

Commit 8c3daed

Browse files
aketchum15144026
andauthored
[clangd] Implement simple folding for preprocessor branches (#140959)
This pull request builds on #121449 by sr-tream with a unit test and a bug fix. --------- Co-authored-by: Ruihua Dong <[email protected]>
1 parent 0346e57 commit 8c3daed

File tree

4 files changed

+128
-9
lines changed

4 files changed

+128
-9
lines changed

clang-tools-extra/clangd/SemanticSelection.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,8 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) {
175175
return collectFoldingRanges(SyntaxTree, TM);
176176
}
177177

178-
// FIXME( usaxena95): Collect PP conditional regions, includes and other code
179-
// regions (e.g. public/private/protected sections of classes, control flow
180-
// statement bodies).
178+
// FIXME( usaxena95): Collect includes and other code regions (e.g.
179+
// public/private/protected sections of classes, control flow statement bodies).
181180
// Related issue: https://github.com/clangd/clangd/issues/310
182181
llvm::Expected<std::vector<FoldingRange>>
183182
getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
@@ -186,12 +185,6 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
186185
auto DirectiveStructure = DirectiveTree::parse(OrigStream);
187186
chooseConditionalBranches(DirectiveStructure, OrigStream);
188187

189-
// FIXME: Provide ranges in the disabled-PP regions as well.
190-
auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream);
191-
192-
auto ParseableStream = cook(Preprocessed, genericLangOpts());
193-
pairBrackets(ParseableStream);
194-
195188
std::vector<FoldingRange> Result;
196189
auto AddFoldingRange = [&](Position Start, Position End,
197190
llvm::StringLiteral Kind) {
@@ -220,7 +213,32 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
220213
auto EndPosition = [&](const Token &T) {
221214
return offsetToPosition(Code, EndOffset(T));
222215
};
216+
217+
// Preprocessor directives
218+
auto PPRanges = pairDirectiveRanges(DirectiveStructure, OrigStream);
219+
for (const auto &R : PPRanges) {
220+
auto BTok = OrigStream.tokens()[R.Begin];
221+
auto ETok = OrigStream.tokens()[R.End];
222+
if (ETok.Kind == tok::eof)
223+
continue;
224+
if (BTok.Line >= ETok.Line)
225+
continue;
226+
227+
Position Start = EndPosition(BTok);
228+
Position End = StartPosition(ETok);
229+
if (LineFoldingOnly)
230+
End.line--;
231+
AddFoldingRange(Start, End, FoldingRange::REGION_KIND);
232+
}
233+
234+
// FIXME: Provide ranges in the disabled-PP regions as well.
235+
auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream);
236+
237+
auto ParseableStream = cook(Preprocessed, genericLangOpts());
238+
pairBrackets(ParseableStream);
239+
223240
auto Tokens = ParseableStream.tokens();
241+
224242
// Brackets.
225243
for (const auto &Tok : Tokens) {
226244
if (auto *Paired = Tok.pair()) {
@@ -240,6 +258,7 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
240258
return OriginalToken(T).Length >= 2 &&
241259
Code.substr(StartOffset(T), 2) == "/*";
242260
};
261+
243262
// Multi-line comments.
244263
for (auto *T = Tokens.begin(); T != Tokens.end();) {
245264
if (T->Kind != tok::comment) {

clang-tools-extra/clangd/support/DirectiveTree.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,5 +356,62 @@ TokenStream DirectiveTree::stripDirectives(const TokenStream &In) const {
356356
return Out;
357357
}
358358

359+
namespace {
360+
class RangePairer {
361+
std::vector<Token::Range> &Ranges;
362+
363+
public:
364+
RangePairer(std::vector<Token::Range> &Ranges) : Ranges(Ranges) {}
365+
366+
void walk(const DirectiveTree &T) {
367+
for (const auto &C : T.Chunks)
368+
std::visit(*this, C);
369+
}
370+
371+
void operator()(const DirectiveTree::Code &C) {}
372+
373+
void operator()(const DirectiveTree::Directive &) {}
374+
375+
void operator()(const DirectiveTree::Conditional &C) {
376+
Token::Range Range;
377+
Token::Index Last;
378+
auto First = true;
379+
for (const auto &[Directive, _] : C.Branches) {
380+
if (First) {
381+
First = false;
382+
} else {
383+
Range = {Last, Directive.Tokens.Begin};
384+
Ranges.push_back(Range);
385+
}
386+
Last = Directive.Tokens.Begin;
387+
}
388+
389+
if (C.End.Kind != tok::pp_not_keyword) {
390+
Range = {Last, C.End.Tokens.Begin};
391+
Ranges.push_back(Range);
392+
}
393+
394+
for (const auto &[_, SubTree] : C.Branches)
395+
walk(SubTree);
396+
}
397+
};
398+
} // namespace
399+
400+
std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree,
401+
const TokenStream &Code) {
402+
std::vector<Token::Range> Ranges;
403+
RangePairer(Ranges).walk(Tree);
404+
405+
// Transform paired ranges to start with last token in its logical line
406+
for (auto &R : Ranges) {
407+
const Token *Tok = &Code.tokens()[R.Begin + 1];
408+
while (Tok->Kind != tok::eof && !Tok->flag(LexFlags::StartsPPLine))
409+
++Tok;
410+
Tok = Tok - 1;
411+
R.Begin = Tok->OriginalIndex;
412+
}
413+
return Ranges;
414+
}
415+
359416
} // namespace clangd
360417
} // namespace clang

clang-tools-extra/clangd/support/DirectiveTree.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &,
124124
/// The choices are stored in Conditional::Taken nodes.
125125
void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code);
126126

127+
/// Pairs preprocessor conditional directives and computes their token ranges.
128+
std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree,
129+
const TokenStream &Code);
130+
127131
} // namespace clangd
128132
} // namespace clang
129133

clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,45 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
371371
//[[ foo
372372
/* bar */]]
373373
)cpp",
374+
R"cpp(
375+
//Ignore non-conditional directives
376+
#define A 1
377+
378+
void func() {[[
379+
int Variable = 100;
380+
381+
#ifdef FOO[[
382+
Variable = 1;
383+
#if 1[[
384+
Variable = 4;
385+
]]#endif
386+
]]#else[[
387+
Variable = 2;
388+
//handle nested directives
389+
#if 1[[
390+
Variable = 3;
391+
]]#endif
392+
]]#endif
393+
394+
395+
]]}
396+
)cpp",
397+
R"cpp(
398+
int Variable = 0;
399+
#if defined(WALDO)
400+
Variable = 1;
401+
#
402+
)cpp",
403+
R"cpp(
404+
int Variable = 0;
405+
#if defined(WALDO)[[
406+
Variable = 1;
407+
]]#elif 1[[
408+
Variable = 2;
409+
]]#else
410+
Variable = 3;
411+
#
412+
)cpp",
374413
};
375414
for (const char *Test : Tests) {
376415
auto T = Annotations(Test);

0 commit comments

Comments
 (0)