Skip to content

Commit 2bd6974

Browse files
committed
[clang-format] Wrap lines for C# property accessors
Summary: Ensure that auto-implemented properties `{ get; private set }` are wrapped on to one line for C# code. Reviewers: MyDeveloperDay, krasimir Reviewed By: MyDeveloperDay, krasimir Subscribers: cfe-commits Tags: #clang-format, #clang Differential Revision: https://reviews.llvm.org/D75006
1 parent edae4be commit 2bd6974

File tree

2 files changed

+94
-8
lines changed

2 files changed

+94
-8
lines changed

clang/lib/Format/UnwrappedLineFormatter.cpp

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

295+
// Try to merge a CSharp property declaration like `{ get; private set }`.
296+
if (Style.isCSharp()) {
297+
unsigned CSPA = tryMergeCSharpPropertyAccessor(I, E, Limit);
298+
if (CSPA > 0)
299+
return CSPA;
300+
}
301+
295302
// Try to merge a function block with left brace unwrapped
296303
if (TheLine->Last->is(TT_FunctionLBrace) &&
297304
TheLine->First != TheLine->Last) {
@@ -421,6 +428,64 @@ class LineJoiner {
421428
return 0;
422429
}
423430

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

clang/unittests/Format/FormatTestCSharp.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -240,18 +240,12 @@ TEST_F(FormatTestCSharp, Attributes) {
240240

241241
verifyFormat("[TestMethod]\n"
242242
"public string Host\n"
243-
"{\n"
244-
" set;\n"
245-
" get;\n"
246-
"}");
243+
"{ set; get; }");
247244

248245
verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server "
249246
"listening on provided host\")]\n"
250247
"public string Host\n"
251-
"{\n"
252-
" set;\n"
253-
" get;\n"
254-
"}");
248+
"{ set; get; }");
255249

256250
verifyFormat(
257251
"[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n"
@@ -554,5 +548,32 @@ PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment
554548
Style);
555549
}
556550

551+
TEST_F(FormatTestCSharp, CSharpPropertyAccessors) {
552+
FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
553+
554+
verifyFormat("int Value { get }", Style);
555+
verifyFormat("int Value { get; }", Style);
556+
verifyFormat("int Value { internal get; }", Style);
557+
verifyFormat("int Value { get; } = 0", Style);
558+
verifyFormat("int Value { set }", Style);
559+
verifyFormat("int Value { set; }", Style);
560+
verifyFormat("int Value { internal set; }", Style);
561+
verifyFormat("int Value { set; } = 0", Style);
562+
verifyFormat("int Value { get; set }", Style);
563+
verifyFormat("int Value { set; get }", Style);
564+
verifyFormat("int Value { get; private set; }", Style);
565+
verifyFormat("int Value { get; set; }", Style);
566+
verifyFormat("int Value { get; set; } = 0", Style);
567+
verifyFormat("int Value { internal get; internal set; }", Style);
568+
569+
// Do not wrap expression body definitions.
570+
verifyFormat(R"(//
571+
public string Name {
572+
get => _name;
573+
set => _name = value;
574+
})",
575+
Style);
576+
}
577+
557578
} // namespace format
558579
} // end namespace clang

0 commit comments

Comments
 (0)