Skip to content

Commit ddb4450

Browse files
authored
[ClangFormat] Fix indent in child lines within a macro argument. (llvm#82523)
When reconstructing lines from a macro expansion, make sure that lines at different levels in the expanded code get indented correctly as part of the macro argument.
1 parent bcf9826 commit ddb4450

File tree

6 files changed

+163
-73
lines changed

6 files changed

+163
-73
lines changed

clang/lib/Format/MacroCallReconstructor.cpp

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void forEachToken(const UnwrappedLine &Line, const T &Call,
3333
FormatToken *Parent = nullptr) {
3434
bool First = true;
3535
for (const auto &N : Line.Tokens) {
36-
Call(N.Tok, Parent, First);
36+
Call(N.Tok, Parent, First, Line.Level);
3737
First = false;
3838
for (const auto &Child : N.Children)
3939
forEachToken(Child, Call, N.Tok);
@@ -44,26 +44,25 @@ MacroCallReconstructor::MacroCallReconstructor(
4444
unsigned Level,
4545
const llvm::DenseMap<FormatToken *, std::unique_ptr<UnwrappedLine>>
4646
&ActiveExpansions)
47-
: Level(Level), IdToReconstructed(ActiveExpansions) {
47+
: Result(Level), IdToReconstructed(ActiveExpansions) {
4848
Result.Tokens.push_back(std::make_unique<LineNode>());
4949
ActiveReconstructedLines.push_back(&Result);
5050
}
5151

5252
void MacroCallReconstructor::addLine(const UnwrappedLine &Line) {
5353
assert(State != Finalized);
5454
LLVM_DEBUG(llvm::dbgs() << "MCR: new line...\n");
55-
forEachToken(Line, [&](FormatToken *Token, FormatToken *Parent, bool First) {
56-
add(Token, Parent, First);
57-
});
55+
forEachToken(Line, [&](FormatToken *Token, FormatToken *Parent, bool First,
56+
unsigned Level) { add(Token, Parent, First, Level); });
5857
assert(InProgress || finished());
5958
}
6059

6160
UnwrappedLine MacroCallReconstructor::takeResult() && {
6261
finalize();
6362
assert(Result.Tokens.size() == 1 &&
6463
Result.Tokens.front()->Children.size() == 1);
65-
UnwrappedLine Final =
66-
createUnwrappedLine(*Result.Tokens.front()->Children.front(), Level);
64+
UnwrappedLine Final = createUnwrappedLine(
65+
*Result.Tokens.front()->Children.front(), Result.Level);
6766
assert(!Final.Tokens.empty());
6867
return Final;
6968
}
@@ -72,7 +71,8 @@ UnwrappedLine MacroCallReconstructor::takeResult() && {
7271
// ExpandedParent in the incoming unwrapped line. \p First specifies whether it
7372
// is the first token in a given unwrapped line.
7473
void MacroCallReconstructor::add(FormatToken *Token,
75-
FormatToken *ExpandedParent, bool First) {
74+
FormatToken *ExpandedParent, bool First,
75+
unsigned Level) {
7676
LLVM_DEBUG(
7777
llvm::dbgs() << "MCR: Token: " << Token->TokenText << ", Parent: "
7878
<< (ExpandedParent ? ExpandedParent->TokenText : "<null>")
@@ -102,7 +102,7 @@ void MacroCallReconstructor::add(FormatToken *Token,
102102
First = true;
103103
}
104104

105-
prepareParent(ExpandedParent, First);
105+
prepareParent(ExpandedParent, First, Level);
106106

107107
if (Token->MacroCtx) {
108108
// If this token was generated by a macro call, add the reconstructed
@@ -129,7 +129,7 @@ void MacroCallReconstructor::add(FormatToken *Token,
129129
// is the parent of ActiveReconstructedLines.back() in the reconstructed
130130
// unwrapped line.
131131
void MacroCallReconstructor::prepareParent(FormatToken *ExpandedParent,
132-
bool NewLine) {
132+
bool NewLine, unsigned Level) {
133133
LLVM_DEBUG({
134134
llvm::dbgs() << "ParentMap:\n";
135135
debugParentMap();
@@ -172,7 +172,7 @@ void MacroCallReconstructor::prepareParent(FormatToken *ExpandedParent,
172172
}
173173
assert(!ActiveReconstructedLines.empty());
174174
ActiveReconstructedLines.back()->Tokens.back()->Children.push_back(
175-
std::make_unique<ReconstructedLine>());
175+
std::make_unique<ReconstructedLine>(Level));
176176
ActiveReconstructedLines.push_back(
177177
&*ActiveReconstructedLines.back()->Tokens.back()->Children.back());
178178
} else if (parentLine().Tokens.back()->Tok != Parent) {
@@ -424,7 +424,8 @@ bool MacroCallReconstructor::processNextReconstructed() {
424424
SpelledParentToReconstructedParent[MacroCallStructure.back()
425425
.ParentLastToken] = Token;
426426
appendToken(Token);
427-
prepareParent(Token, /*NewLine=*/true);
427+
prepareParent(Token, /*NewLine=*/true,
428+
MacroCallStructure.back().Line->Level);
428429
Token->MacroParent = true;
429430
return false;
430431
}
@@ -435,7 +436,8 @@ bool MacroCallReconstructor::processNextReconstructed() {
435436
[MacroCallStructure.back().Line->Tokens.back()->Tok] = Token;
436437
Token->MacroParent = true;
437438
appendToken(Token, MacroCallStructure.back().Line);
438-
prepareParent(Token, /*NewLine=*/true);
439+
prepareParent(Token, /*NewLine=*/true,
440+
MacroCallStructure.back().Line->Level);
439441
return true;
440442
}
441443
if (Token->is(tok::r_paren)) {
@@ -509,16 +511,36 @@ MacroCallReconstructor::createUnwrappedLine(const ReconstructedLine &Line,
509511
for (const auto &N : Line.Tokens) {
510512
Result.Tokens.push_back(N->Tok);
511513
UnwrappedLineNode &Current = Result.Tokens.back();
512-
for (const auto &Child : N->Children) {
513-
if (Child->Tokens.empty())
514-
continue;
515-
Current.Children.push_back(createUnwrappedLine(*Child, Level + 1));
516-
}
517-
if (Current.Children.size() == 1 &&
518-
Current.Tok->isOneOf(tok::l_paren, tok::comma)) {
519-
Result.Tokens.splice(Result.Tokens.end(),
520-
Current.Children.front().Tokens);
521-
Current.Children.clear();
514+
auto NumChildren =
515+
std::count_if(N->Children.begin(), N->Children.end(),
516+
[](const auto &Child) { return !Child->Tokens.empty(); });
517+
if (NumChildren == 1 && Current.Tok->isOneOf(tok::l_paren, tok::comma)) {
518+
// If we only have one child, and the child is due to a macro expansion
519+
// (either attached to a left parenthesis or comma), merge the child into
520+
// the current line to prevent forced breaks for macro arguments.
521+
auto *Child = std::find_if(
522+
N->Children.begin(), N->Children.end(),
523+
[](const auto &Child) { return !Child->Tokens.empty(); });
524+
auto Line = createUnwrappedLine(**Child, Level);
525+
Result.Tokens.splice(Result.Tokens.end(), Line.Tokens);
526+
} else if (NumChildren > 0) {
527+
// When there are multiple children with different indent, make sure that
528+
// we indent them:
529+
// 1. One level below the current line's level.
530+
// 2. At the correct level relative to each other.
531+
unsigned MinChildLevel =
532+
std::min_element(N->Children.begin(), N->Children.end(),
533+
[](const auto &E1, const auto &E2) {
534+
return E1->Level < E2->Level;
535+
})
536+
->get()
537+
->Level;
538+
for (const auto &Child : N->Children) {
539+
if (Child->Tokens.empty())
540+
continue;
541+
Current.Children.push_back(createUnwrappedLine(
542+
*Child, Level + 1 + (Child->Level - MinChildLevel)));
543+
}
522544
}
523545
}
524546
return Result;

clang/lib/Format/Macros.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,9 @@ class MacroCallReconstructor {
231231
UnwrappedLine takeResult() &&;
232232

233233
private:
234-
void add(FormatToken *Token, FormatToken *ExpandedParent, bool First);
235-
void prepareParent(FormatToken *ExpandedParent, bool First);
234+
void add(FormatToken *Token, FormatToken *ExpandedParent, bool First,
235+
unsigned Level);
236+
void prepareParent(FormatToken *ExpandedParent, bool First, unsigned Level);
236237
FormatToken *getParentInResult(FormatToken *Parent);
237238
void reconstruct(FormatToken *Token);
238239
void startReconstruction(FormatToken *Token);
@@ -272,6 +273,8 @@ class MacroCallReconstructor {
272273
// FIXME: Investigate changing UnwrappedLine to a pointer type and using it
273274
// instead of rolling our own type.
274275
struct ReconstructedLine {
276+
explicit ReconstructedLine(unsigned Level) : Level(Level) {}
277+
unsigned Level;
275278
llvm::SmallVector<std::unique_ptr<LineNode>> Tokens;
276279
};
277280

@@ -373,9 +376,6 @@ class MacroCallReconstructor {
373376
// \- )
374377
llvm::SmallVector<MacroCallState> MacroCallStructure;
375378

376-
// Level the generated UnwrappedLine will be at.
377-
const unsigned Level;
378-
379379
// Maps from identifier of the macro call to an unwrapped line containing
380380
// all tokens of the macro call.
381381
const llvm::DenseMap<FormatToken *, std::unique_ptr<UnwrappedLine>>

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ class ScopedDeclarationState {
9090

9191
} // end anonymous namespace
9292

93+
std::ostream &operator<<(std::ostream &Stream, const UnwrappedLine &Line) {
94+
llvm::raw_os_ostream OS(Stream);
95+
printLine(OS, Line);
96+
return Stream;
97+
}
98+
9399
class ScopedLineState {
94100
public:
95101
ScopedLineState(UnwrappedLineParser &Parser,

clang/lib/Format/UnwrappedLineParser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ struct UnwrappedLineNode {
420420
SmallVector<UnwrappedLine, 0> Children;
421421
};
422422

423+
std::ostream &operator<<(std::ostream &Stream, const UnwrappedLine &Line);
424+
423425
} // end namespace format
424426
} // end namespace clang
425427

clang/unittests/Format/FormatTestMacroExpansion.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ TEST_F(FormatTestMacroExpansion, UnexpandConfiguredMacros) {
4848
)",
4949
Style);
5050
verifyIncompleteFormat("ID3({, ID(a *b),\n"
51-
" ;\n"
51+
" ;\n"
5252
" });",
5353
Style);
5454

@@ -131,9 +131,9 @@ ID(CALL(CALL(a * b)));
131131
EXPECT_EQ(R"(
132132
ID3(
133133
{
134-
CLASS
135-
a *b;
136-
};
134+
CLASS
135+
a *b;
136+
};
137137
},
138138
ID(x *y);
139139
,
@@ -287,6 +287,19 @@ TEST_F(FormatTestMacroExpansion,
287287
Style);
288288
}
289289

290+
TEST_F(FormatTestMacroExpansion, IndentChildrenWithinMacroCall) {
291+
FormatStyle Style = getGoogleStyleWithColumns(22);
292+
Style.Macros.push_back("MACRO(a, b)=a=(b)");
293+
verifyFormat("void f() {\n"
294+
" MACRO(a b, call([] {\n"
295+
" if (expr) {\n"
296+
" indent();\n"
297+
" }\n"
298+
" }));\n"
299+
"}",
300+
Style);
301+
}
302+
290303
} // namespace
291304
} // namespace test
292305
} // namespace format

0 commit comments

Comments
 (0)