Skip to content

Commit

Permalink
[ClangFormat] Fix indent in child lines within a macro argument. (#82523
Browse files Browse the repository at this point in the history
)

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.
  • Loading branch information
r4nt committed Feb 23, 2024
1 parent bcf9826 commit ddb4450
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 73 deletions.
68 changes: 45 additions & 23 deletions clang/lib/Format/MacroCallReconstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void forEachToken(const UnwrappedLine &Line, const T &Call,
FormatToken *Parent = nullptr) {
bool First = true;
for (const auto &N : Line.Tokens) {
Call(N.Tok, Parent, First);
Call(N.Tok, Parent, First, Line.Level);
First = false;
for (const auto &Child : N.Children)
forEachToken(Child, Call, N.Tok);
Expand All @@ -44,26 +44,25 @@ MacroCallReconstructor::MacroCallReconstructor(
unsigned Level,
const llvm::DenseMap<FormatToken *, std::unique_ptr<UnwrappedLine>>
&ActiveExpansions)
: Level(Level), IdToReconstructed(ActiveExpansions) {
: Result(Level), IdToReconstructed(ActiveExpansions) {
Result.Tokens.push_back(std::make_unique<LineNode>());
ActiveReconstructedLines.push_back(&Result);
}

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

UnwrappedLine MacroCallReconstructor::takeResult() && {
finalize();
assert(Result.Tokens.size() == 1 &&
Result.Tokens.front()->Children.size() == 1);
UnwrappedLine Final =
createUnwrappedLine(*Result.Tokens.front()->Children.front(), Level);
UnwrappedLine Final = createUnwrappedLine(
*Result.Tokens.front()->Children.front(), Result.Level);
assert(!Final.Tokens.empty());
return Final;
}
Expand All @@ -72,7 +71,8 @@ UnwrappedLine MacroCallReconstructor::takeResult() && {
// ExpandedParent in the incoming unwrapped line. \p First specifies whether it
// is the first token in a given unwrapped line.
void MacroCallReconstructor::add(FormatToken *Token,
FormatToken *ExpandedParent, bool First) {
FormatToken *ExpandedParent, bool First,
unsigned Level) {
LLVM_DEBUG(
llvm::dbgs() << "MCR: Token: " << Token->TokenText << ", Parent: "
<< (ExpandedParent ? ExpandedParent->TokenText : "<null>")
Expand Down Expand Up @@ -102,7 +102,7 @@ void MacroCallReconstructor::add(FormatToken *Token,
First = true;
}

prepareParent(ExpandedParent, First);
prepareParent(ExpandedParent, First, Level);

if (Token->MacroCtx) {
// If this token was generated by a macro call, add the reconstructed
Expand All @@ -129,7 +129,7 @@ void MacroCallReconstructor::add(FormatToken *Token,
// is the parent of ActiveReconstructedLines.back() in the reconstructed
// unwrapped line.
void MacroCallReconstructor::prepareParent(FormatToken *ExpandedParent,
bool NewLine) {
bool NewLine, unsigned Level) {
LLVM_DEBUG({
llvm::dbgs() << "ParentMap:\n";
debugParentMap();
Expand Down Expand Up @@ -172,7 +172,7 @@ void MacroCallReconstructor::prepareParent(FormatToken *ExpandedParent,
}
assert(!ActiveReconstructedLines.empty());
ActiveReconstructedLines.back()->Tokens.back()->Children.push_back(
std::make_unique<ReconstructedLine>());
std::make_unique<ReconstructedLine>(Level));
ActiveReconstructedLines.push_back(
&*ActiveReconstructedLines.back()->Tokens.back()->Children.back());
} else if (parentLine().Tokens.back()->Tok != Parent) {
Expand Down Expand Up @@ -424,7 +424,8 @@ bool MacroCallReconstructor::processNextReconstructed() {
SpelledParentToReconstructedParent[MacroCallStructure.back()
.ParentLastToken] = Token;
appendToken(Token);
prepareParent(Token, /*NewLine=*/true);
prepareParent(Token, /*NewLine=*/true,
MacroCallStructure.back().Line->Level);
Token->MacroParent = true;
return false;
}
Expand All @@ -435,7 +436,8 @@ bool MacroCallReconstructor::processNextReconstructed() {
[MacroCallStructure.back().Line->Tokens.back()->Tok] = Token;
Token->MacroParent = true;
appendToken(Token, MacroCallStructure.back().Line);
prepareParent(Token, /*NewLine=*/true);
prepareParent(Token, /*NewLine=*/true,
MacroCallStructure.back().Line->Level);
return true;
}
if (Token->is(tok::r_paren)) {
Expand Down Expand Up @@ -509,16 +511,36 @@ MacroCallReconstructor::createUnwrappedLine(const ReconstructedLine &Line,
for (const auto &N : Line.Tokens) {
Result.Tokens.push_back(N->Tok);
UnwrappedLineNode &Current = Result.Tokens.back();
for (const auto &Child : N->Children) {
if (Child->Tokens.empty())
continue;
Current.Children.push_back(createUnwrappedLine(*Child, Level + 1));
}
if (Current.Children.size() == 1 &&
Current.Tok->isOneOf(tok::l_paren, tok::comma)) {
Result.Tokens.splice(Result.Tokens.end(),
Current.Children.front().Tokens);
Current.Children.clear();
auto NumChildren =
std::count_if(N->Children.begin(), N->Children.end(),
[](const auto &Child) { return !Child->Tokens.empty(); });
if (NumChildren == 1 && Current.Tok->isOneOf(tok::l_paren, tok::comma)) {
// If we only have one child, and the child is due to a macro expansion
// (either attached to a left parenthesis or comma), merge the child into
// the current line to prevent forced breaks for macro arguments.
auto *Child = std::find_if(
N->Children.begin(), N->Children.end(),
[](const auto &Child) { return !Child->Tokens.empty(); });
auto Line = createUnwrappedLine(**Child, Level);
Result.Tokens.splice(Result.Tokens.end(), Line.Tokens);
} else if (NumChildren > 0) {
// When there are multiple children with different indent, make sure that
// we indent them:
// 1. One level below the current line's level.
// 2. At the correct level relative to each other.
unsigned MinChildLevel =
std::min_element(N->Children.begin(), N->Children.end(),
[](const auto &E1, const auto &E2) {
return E1->Level < E2->Level;
})
->get()
->Level;
for (const auto &Child : N->Children) {
if (Child->Tokens.empty())
continue;
Current.Children.push_back(createUnwrappedLine(
*Child, Level + 1 + (Child->Level - MinChildLevel)));
}
}
}
return Result;
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Format/Macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,9 @@ class MacroCallReconstructor {
UnwrappedLine takeResult() &&;

private:
void add(FormatToken *Token, FormatToken *ExpandedParent, bool First);
void prepareParent(FormatToken *ExpandedParent, bool First);
void add(FormatToken *Token, FormatToken *ExpandedParent, bool First,
unsigned Level);
void prepareParent(FormatToken *ExpandedParent, bool First, unsigned Level);
FormatToken *getParentInResult(FormatToken *Parent);
void reconstruct(FormatToken *Token);
void startReconstruction(FormatToken *Token);
Expand Down Expand Up @@ -272,6 +273,8 @@ class MacroCallReconstructor {
// FIXME: Investigate changing UnwrappedLine to a pointer type and using it
// instead of rolling our own type.
struct ReconstructedLine {
explicit ReconstructedLine(unsigned Level) : Level(Level) {}
unsigned Level;
llvm::SmallVector<std::unique_ptr<LineNode>> Tokens;
};

Expand Down Expand Up @@ -373,9 +376,6 @@ class MacroCallReconstructor {
// \- )
llvm::SmallVector<MacroCallState> MacroCallStructure;

// Level the generated UnwrappedLine will be at.
const unsigned Level;

// Maps from identifier of the macro call to an unwrapped line containing
// all tokens of the macro call.
const llvm::DenseMap<FormatToken *, std::unique_ptr<UnwrappedLine>>
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ class ScopedDeclarationState {

} // end anonymous namespace

std::ostream &operator<<(std::ostream &Stream, const UnwrappedLine &Line) {
llvm::raw_os_ostream OS(Stream);
printLine(OS, Line);
return Stream;
}

class ScopedLineState {
public:
ScopedLineState(UnwrappedLineParser &Parser,
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/UnwrappedLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@ struct UnwrappedLineNode {
SmallVector<UnwrappedLine, 0> Children;
};

std::ostream &operator<<(std::ostream &Stream, const UnwrappedLine &Line);

} // end namespace format
} // end namespace clang

Expand Down
21 changes: 17 additions & 4 deletions clang/unittests/Format/FormatTestMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ TEST_F(FormatTestMacroExpansion, UnexpandConfiguredMacros) {
)",
Style);
verifyIncompleteFormat("ID3({, ID(a *b),\n"
" ;\n"
" ;\n"
" });",
Style);

Expand Down Expand Up @@ -131,9 +131,9 @@ ID(CALL(CALL(a * b)));
EXPECT_EQ(R"(
ID3(
{
CLASS
a *b;
};
CLASS
a *b;
};
},
ID(x *y);
,
Expand Down Expand Up @@ -287,6 +287,19 @@ TEST_F(FormatTestMacroExpansion,
Style);
}

TEST_F(FormatTestMacroExpansion, IndentChildrenWithinMacroCall) {
FormatStyle Style = getGoogleStyleWithColumns(22);
Style.Macros.push_back("MACRO(a, b)=a=(b)");
verifyFormat("void f() {\n"
" MACRO(a b, call([] {\n"
" if (expr) {\n"
" indent();\n"
" }\n"
" }));\n"
"}",
Style);
}

} // namespace
} // namespace test
} // namespace format
Expand Down

0 comments on commit ddb4450

Please sign in to comment.