Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -425,6 +425,11 @@ A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed. +**BreakBeforeInhertianceDelimiter** (``boolt``) + If ``true``, in the class inheritance expression clang-format will + break before operands ``:`` and ``,`` only if there is multiple + inheritance. + **ConstructorInitializerAllOnOneLineOrOnePerLine** (``bool``) If the constructor initializers don't fit on a line, put each initializer on its own line. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -303,6 +303,11 @@ /// which should not be split into lines or otherwise changed. std::string CommentPragmas; + /// \brief If ``true``, in the class inheritance expression clang-format will + /// break before operands ``:`` and ``,`` only if there is multiple + /// inheritance. + bool BreakBeforeInhertianceDelimiter; + /// \brief If the constructor initializers don't fit on a line, put each /// initializer on its own line. bool ConstructorInitializerAllOnOneLineOrOnePerLine; @@ -669,6 +674,8 @@ BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakStringLiterals == R.BreakStringLiterals && ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas && + BreakBeforeInhertianceDelimiter == + R.BreakBeforeInhertianceDelimiter && ConstructorInitializerAllOnOneLineOrOnePerLine == R.ConstructorInitializerAllOnOneLineOrOnePerLine && ConstructorInitializerIndentWidth == Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -57,8 +57,10 @@ Style.BreakConstructorInitializersBeforeComma) return true; return Previous.is(tok::comma) && !Current.isTrailingComment() && - (Previous.isNot(TT_CtorInitializerComma) || - !Style.BreakConstructorInitializersBeforeComma); + ((Previous.isNot(TT_CtorInitializerComma) || + !Style.BreakConstructorInitializersBeforeComma) && + (Previous.isNot(TT_InheritanceComma) || + !Style.BreakBeforeInhertianceDelimiter)); } ContinuationIndenter::ContinuationIndenter(const FormatStyle &Style, @@ -741,6 +743,8 @@ return State.FirstIndent + Style.ConstructorInitializerIndentWidth; if (NextNonComment->is(TT_CtorInitializerComma)) return State.Stack.back().Indent; + if (NextNonComment->isOneOf(TT_InheritanceColon, TT_InheritanceComma)) + return State.FirstIndent + Style.ContinuationIndentWidth; if (Previous.is(tok::r_paren) && !Current.isBinaryOperator() && !Current.isOneOf(tok::colon, tok::comment)) return ContinuationIndent; @@ -810,6 +814,9 @@ State.Stack.back().AvoidBinPacking = true; State.Stack.back().BreakBeforeParameter = false; } + if (Current.is(TT_InheritanceColon)) + State.Stack.back().Indent = + State.FirstIndent + Style.ContinuationIndentWidth; if (Current.isOneOf(TT_BinaryOperator, TT_ConditionalExpr) && Newline) State.Stack.back().NestedBlockIndent = State.Column + Current.ColumnWidth + 1; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -520,6 +520,7 @@ false, false, false, false, false}; LLVMStyle.BreakAfterJavaFieldAnnotations = false; LLVMStyle.BreakConstructorInitializersBeforeComma = false; + LLVMStyle.BreakBeforeInhertianceDelimiter = false; LLVMStyle.BreakStringLiterals = true; LLVMStyle.ColumnLimit = 80; LLVMStyle.CommentPragmas = "^ IWYU pragma:"; @@ -673,6 +674,7 @@ MozillaStyle.BinPackArguments = false; MozillaStyle.BreakBeforeBraces = FormatStyle::BS_Mozilla; MozillaStyle.BreakConstructorInitializersBeforeComma = true; + MozillaStyle.BreakBeforeInhertianceDelimiter = true; MozillaStyle.ConstructorInitializerIndentWidth = 2; MozillaStyle.ContinuationIndentWidth = 2; MozillaStyle.Cpp11BracedListStyle = false; Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -48,6 +48,7 @@ TYPE(FunctionTypeLParen) \ TYPE(ImplicitStringLiteral) \ TYPE(InheritanceColon) \ + TYPE(InheritanceComma) \ TYPE(InlineASMBrace) \ TYPE(InlineASMColon) \ TYPE(JavaAnnotation) \ Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -676,6 +676,8 @@ case tok::comma: if (Contexts.back().InCtorInitializer) Tok->Type = TT_CtorInitializerComma; + else if (Contexts.back().InInhertiance) + Tok->Type = TT_InheritanceComma; else if (Contexts.back().FirstStartOfName && (Contexts.size() == 1 || Line.startsWith(tok::kw_for))) { Contexts.back().FirstStartOfName->PartOfMultiVariableDeclStmt = true; @@ -945,6 +947,7 @@ bool CanBeExpression = true; bool InTemplateArgument = false; bool InCtorInitializer = false; + bool InInhertiance = false; bool CaretFound = false; bool IsForEachMacro = false; }; @@ -1004,6 +1007,10 @@ Current.Previous->is(TT_CtorInitializerColon)) { Contexts.back().IsExpression = true; Contexts.back().InCtorInitializer = true; + } else if (Current.Previous && + Current.Previous->is(TT_InheritanceColon)) { + Contexts.back().IsExpression = true; + Contexts.back().InInhertiance = true; } else if (Current.isOneOf(tok::r_paren, tok::greater, tok::comma)) { for (FormatToken *Previous = Current.Previous; Previous && Previous->isOneOf(tok::star, tok::amp); @@ -2388,6 +2395,15 @@ !Tok.isOneOf(TT_ObjCBlockLBrace, TT_DictLiteral); } +// Determine if the next token from the closing scope is an inheritance token +static bool hasMultipleInheritance(const FormatToken &Tok) { + // Look for a column of type TT_InheritanceComma untill we find it or + // we find a declaration termination + for (const FormatToken* nextTok = Tok.Next; nextTok; nextTok = nextTok->Next) + if (nextTok->is(TT_InheritanceComma)) return true; + return false; +} + bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, const FormatToken &Right) { const FormatToken &Left = *Right.Previous; @@ -2471,6 +2487,11 @@ Style.BreakConstructorInitializersBeforeComma && !Style.ConstructorInitializerAllOnOneLineOrOnePerLine) return true; + //Break only if we have multiple inheritance + if (Style.BreakBeforeInhertianceDelimiter && + ((Right.is(TT_InheritanceColon) && hasMultipleInheritance(Right)) || + Right.is(TT_InheritanceComma))) + return true; if (Right.is(tok::string_literal) && Right.TokenText.startswith("R\"")) // Raw string literals are special wrt. line breaks. The author has made a // deliberate choice and might have aligned the contents of the string @@ -2648,6 +2669,12 @@ if (Right.is(TT_CtorInitializerComma) && Style.BreakConstructorInitializersBeforeComma) return true; + if (Left.is(TT_InheritanceComma) && + Style.BreakBeforeInhertianceDelimiter) + return false; + if (Right.is(TT_InheritanceComma) && + Style.BreakBeforeInhertianceDelimiter) + return true; if ((Left.is(tok::greater) && Right.is(tok::greater)) || (Left.is(tok::less) && Right.is(tok::less))) return false; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -1020,6 +1020,29 @@ verifyFormat("class ::A::B {};"); } +TEST_F(FormatTest, BreakBeforeInhertianceDelimiter) { + FormatStyle StyleWithInheritanceBreak = getLLVMStyle(); + LocalStyle.BreakBeforeInhertianceDelimiter = true; + + verifyFormat("class MyClass : public X {}", LocalStyle); + verifyFormat("class MyClass\n" + " : public X\n" + " , public Y // some difficult comment\n" + " , public Z {}", + StyleWithInheritanceBreak); + verifyFormat("class MyClass" + " : public X\n" + " , public Y // some difficult comment\n" + " , public Z\n", + StyleWithInheritanceBreak); + verifyFormat("class MyClass\n" + " : public X // When deriving from more than one class, put " + "each on its own\n" + " // line.\n" + " , public Y {}", + StyleWithInheritanceBreak); +} + TEST_F(FormatTest, FormatsVariableDeclarationsAfterStructOrClass) { verifyFormat("class A {\n} a, b;"); verifyFormat("struct A {\n} a, b;"); @@ -8468,6 +8491,7 @@ CHECK_PARSE_BOOL(BreakBeforeTernaryOperators); CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeComma); CHECK_PARSE_BOOL(BreakStringLiterals); + CHECK_PARSE_BOOL(BreakBeforeInhertianceDelimiter) CHECK_PARSE_BOOL(ConstructorInitializerAllOnOneLineOrOnePerLine); CHECK_PARSE_BOOL(DerivePointerAlignment); CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");