Skip to content

Commit 2f9fc8d

Browse files
committed
[clang-format] Handle C# property accessors when parsing lines
Summary: Improve C# `{ get; set; } = default;` formatting by handling it in the UnwrappedLineParser rather than trying to merge lines later. Remove old logic to merge lines. Update tests as formatting output has changed (as intended). Reviewers: krasimir, MyDeveloperDay Reviewed By: krasimir Subscribers: cfe-commits Tags: #clang-format, #clang Differential Revision: https://reviews.llvm.org/D78642
1 parent c9e6b70 commit 2f9fc8d

File tree

4 files changed

+73
-70
lines changed

4 files changed

+73
-70
lines changed

clang/lib/Format/UnwrappedLineFormatter.cpp

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,6 @@ class LineJoiner {
294294
}
295295
}
296296

297-
// Try to merge a CSharp property declaration like `{ get; private set }`.
298-
if (Style.isCSharp()) {
299-
unsigned CSPA = tryMergeCSharpPropertyAccessor(I, E, Limit);
300-
if (CSPA > 0)
301-
return CSPA;
302-
}
303-
304297
// Try to merge a function block with left brace unwrapped
305298
if (TheLine->Last->is(TT_FunctionLBrace) &&
306299
TheLine->First != TheLine->Last) {
@@ -430,64 +423,6 @@ class LineJoiner {
430423
return 0;
431424
}
432425

433-
// true for lines of the form [access-modifier] {get,set} [;]
434-
bool isMergeablePropertyAccessor(const AnnotatedLine *Line) {
435-
auto *Tok = Line->First;
436-
if (!Tok)
437-
return false;
438-
439-
if (Tok->isOneOf(tok::kw_public, tok::kw_protected, tok::kw_private,
440-
Keywords.kw_internal))
441-
Tok = Tok->Next;
442-
443-
if (!Tok || (Tok->TokenText != "get" && Tok->TokenText != "set"))
444-
return false;
445-
446-
if (!Tok->Next || Tok->Next->is(tok::semi))
447-
return true;
448-
449-
return false;
450-
}
451-
452-
unsigned tryMergeCSharpPropertyAccessor(
453-
SmallVectorImpl<AnnotatedLine *>::const_iterator I,
454-
SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned /*Limit*/) {
455-
456-
auto CurrentLine = I;
457-
// Does line start with `{`
458-
if (!(*CurrentLine)->Last || (*CurrentLine)->Last->isNot(TT_FunctionLBrace))
459-
return 0;
460-
++CurrentLine;
461-
462-
unsigned MergedLines = 0;
463-
bool HasGetOrSet = false;
464-
while (CurrentLine != E) {
465-
bool LineIsGetOrSet = isMergeablePropertyAccessor(*CurrentLine);
466-
HasGetOrSet = HasGetOrSet || LineIsGetOrSet;
467-
if (LineIsGetOrSet) {
468-
++CurrentLine;
469-
++MergedLines;
470-
continue;
471-
}
472-
auto *Tok = (*CurrentLine)->First;
473-
if (Tok && Tok->is(tok::r_brace)) {
474-
++CurrentLine;
475-
++MergedLines;
476-
// See if the next line is a default value so that we can merge `{ get;
477-
// set } = 0`
478-
if (CurrentLine != E && (*CurrentLine)->First &&
479-
(*CurrentLine)->First->is(tok::equal)) {
480-
++MergedLines;
481-
}
482-
break;
483-
}
484-
// Not a '}' or a get/set line so do not merege lines.
485-
return 0;
486-
}
487-
488-
return HasGetOrSet ? MergedLines : 0;
489-
}
490-
491426
unsigned
492427
tryMergeSimplePPDirective(SmallVectorImpl<AnnotatedLine *>::const_iterator I,
493428
SmallVectorImpl<AnnotatedLine *>::const_iterator E,

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1334,7 +1334,7 @@ void UnwrappedLineParser::parseStructuralElement() {
13341334
parseChildBlock();
13351335
break;
13361336
case tok::l_brace:
1337-
if (!tryToParseBracedList()) {
1337+
if (!tryToParsePropertyAccessor() && !tryToParseBracedList()) {
13381338
// A block outside of parentheses must be the last part of a
13391339
// structural element.
13401340
// FIXME: Figure out cases where this is not true, and add projections
@@ -1487,6 +1487,75 @@ void UnwrappedLineParser::parseStructuralElement() {
14871487
} while (!eof());
14881488
}
14891489

1490+
bool UnwrappedLineParser::tryToParsePropertyAccessor() {
1491+
assert(FormatTok->is(tok::l_brace));
1492+
if (!Style.isCSharp())
1493+
return false;
1494+
// See if it's a property accessor.
1495+
if (FormatTok->Previous->isNot(tok::identifier))
1496+
return false;
1497+
1498+
// Try to parse the property accessor braces and contents:
1499+
// `{ get; set; } = new MyType(defaultValue);`
1500+
// ^^^^^^^^^^^^^
1501+
//
1502+
// Record the current tokenPosition so that we can advance and
1503+
// reset the current token. `Next` is not set yet so we need
1504+
// another way to advance along the token stream.
1505+
unsigned int StoredPosition = Tokens->getPosition();
1506+
FormatToken *Tok = Tokens->getNextToken();
1507+
1508+
bool HasGetOrSet = false;
1509+
while (!eof()) {
1510+
if (Tok->isOneOf(tok::semi, tok::kw_public, tok::kw_private,
1511+
tok::kw_protected, Keywords.kw_internal, Keywords.kw_get,
1512+
Keywords.kw_set)) {
1513+
if (Tok->isOneOf(Keywords.kw_get, Keywords.kw_set))
1514+
HasGetOrSet = true;
1515+
Tok = Tokens->getNextToken();
1516+
continue;
1517+
}
1518+
if (Tok->is(tok::r_brace))
1519+
break;
1520+
Tokens->setPosition(StoredPosition);
1521+
return false;
1522+
}
1523+
1524+
if (!HasGetOrSet) {
1525+
Tokens->setPosition(StoredPosition);
1526+
return false;
1527+
}
1528+
1529+
Tokens->setPosition(StoredPosition);
1530+
while (FormatTok->isNot(tok::r_brace)) {
1531+
nextToken();
1532+
}
1533+
1534+
// Try to parse (optional) assignment to default value:
1535+
// `{ get; set; } = new MyType(defaultValue);`
1536+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
1537+
// There may be some very complicated expressions inside default value
1538+
// assignment, the simple parse block below will not handle them.
1539+
// The parse block below would need extending to handle opening parens etc.
1540+
StoredPosition = Tokens->getPosition();
1541+
Tok = Tokens->getNextToken();
1542+
bool NextTokenIsEqual = Tok->is(tok::equal);
1543+
Tokens->setPosition(StoredPosition);
1544+
1545+
if (NextTokenIsEqual) {
1546+
do {
1547+
nextToken();
1548+
if (FormatTok->is(tok::semi))
1549+
break;
1550+
} while (!eof());
1551+
}
1552+
1553+
// Add an unwrapped line for the whole property accessor.
1554+
nextToken();
1555+
addUnwrappedLine();
1556+
return true;
1557+
}
1558+
14901559
bool UnwrappedLineParser::tryToParseLambda() {
14911560
if (!Style.isCpp()) {
14921561
nextToken();

clang/lib/Format/UnwrappedLineParser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class UnwrappedLineParser {
132132
void parseCSharpGenericTypeConstraint();
133133
bool tryToParseLambda();
134134
bool tryToParseLambdaIntroducer();
135+
bool tryToParsePropertyAccessor();
135136
void tryToParseJSFunction();
136137
void addUnwrappedLine();
137138
bool eof() const;

clang/unittests/Format/FormatTestCSharp.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,11 @@ TEST_F(FormatTestCSharp, Attributes) {
245245
"}");
246246

247247
verifyFormat("[TestMethod]\n"
248-
"public string Host\n"
249-
"{ set; get; }");
248+
"public string Host { set; get; }");
250249

251250
verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server "
252251
"listening on provided host\")]\n"
253-
"public string Host\n"
254-
"{ set; get; }");
252+
"public string Host { set; get; }");
255253

256254
verifyFormat(
257255
"[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n"

0 commit comments

Comments
 (0)