diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3280,13 +3280,6 @@ return true; } - // Put multiple C# attributes on a new line. - if (Style.isCSharp() && - ((Left.is(TT_AttributeSquare) && Left.is(tok::r_square)) || - (Left.is(tok::r_square) && Right.is(TT_AttributeSquare) && - Right.is(tok::l_square)))) - return true; - // Put multiple Java annotation on a new line. if ((Style.Language == FormatStyle::LK_Java || Style.Language == FormatStyle::LK_JavaScript) && diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -125,6 +125,7 @@ bool parseObjCProtocol(); void parseJavaScriptEs6ImportExport(); void parseStatementMacro(); + void parseCSharpAttribute(); bool tryToParseLambda(); bool tryToParseLambdaIntroducer(); void tryToParseJSFunction(); diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -323,6 +323,20 @@ addUnwrappedLine(); } +void UnwrappedLineParser::parseCSharpAttribute() { + do { + switch (FormatTok->Tok.getKind()) { + case tok::r_square: + nextToken(); + addUnwrappedLine(); + return; + default: + nextToken(); + break; + } + } while (!eof()); +} + void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { bool SwitchLabelEncountered = false; do { @@ -381,6 +395,13 @@ SwitchLabelEncountered = true; parseStructuralElement(); break; + case tok::l_square: + if (Style.isCSharp()) { + nextToken(); + parseCSharpAttribute(); + break; + } + LLVM_FALLTHROUGH; default: parseStructuralElement(); break; diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -258,6 +258,21 @@ "// The const char* returned by hello_world must not be deleted.\n" "private static extern IntPtr HelloFromCpp();)"); + // Class attributes go on their own line and do not affect layout of + // interfaces. Line wrapping decisions previously caused each interface to be + // on its own line. + verifyFormat("[SomeAttribute]\n" + "[SomeOtherAttribute]\n" + "public class A : IShape, IAnimal, IVehicle\n" + "{\n" + " int X;\n" + "}"); + + // Attributes in a method declaration do not cause line wrapping. + verifyFormat("void MethodA([In][Out] ref double x)\n" + "{\n" + "}"); + // Unwrappable lines go on a line of their own. // 'target:' is not treated as a label. // Modify Style to enforce a column limit.