diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3143,6 +3143,38 @@ BasedOnStyle: llvm CanonicalDelimiter: 'cc' +**ReferenceAlignment** (``ReferenceAlignmentStyle``) + Reference alignment style (overrides ``PointerAlignment`` for + references). + + Possible values: + + * ``RAS_Pointer`` (in configuration: ``Pointer``) + Align reference like ``PointerAlignment``. + + * ``RAS_Left`` (in configuration: ``Left``) + Align reference to the left. + + .. code-block:: c++ + + int& a; + + * ``RAS_Right`` (in configuration: ``Right``) + Align reference to the right. + + .. code-block:: c++ + + int &a; + + * ``RAS_Middle`` (in configuration: ``Middle``) + Align reference in the middle. + + .. code-block:: c++ + + int & a; + + + **ReflowComments** (``bool``) If ``true``, clang-format will attempt to re-flow comments. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2644,7 +2644,7 @@ /// (counted relative to leading non-whitespace column). unsigned PenaltyIndentedWhitespace; - /// The ``&`` and ``*`` alignment style. + /// The ``&``, ``&&`` and ``*`` alignment style. enum PointerAlignmentStyle : unsigned char { /// Align pointer to the left. /// \code @@ -2739,6 +2739,31 @@ /// \endcode std::vector RawStringFormats; + /// \brief The ``&`` and ``&&`` alignment style. + enum ReferenceAlignmentStyle { + /// Align reference like ``PointerAlignment``. + RAS_Pointer, + /// Align reference to the left. + /// \code + /// int& a; + /// \endcode + RAS_Left, + /// Align reference to the right. + /// \code + /// int &a; + /// \endcode + RAS_Right, + /// Align reference in the middle. + /// \code + /// int & a; + /// \endcode + RAS_Middle + }; + + /// \brief Reference alignment style (overrides ``PointerAlignment`` for + /// references). + ReferenceAlignmentStyle ReferenceAlignment; + // clang-format off /// If ``true``, clang-format will attempt to re-flow comments. /// \code @@ -3370,6 +3395,7 @@ R.PenaltyBreakTemplateDeclaration && PointerAlignment == R.PointerAlignment && RawStringFormats == R.RawStringFormats && + ReferenceAlignment == R.ReferenceAlignment && ShortNamespaceLines == R.ShortNamespaceLines && SortIncludes == R.SortIncludes && SortJavaStaticImport == R.SortJavaStaticImport && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -398,6 +398,16 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::ReferenceAlignmentStyle &Value) { + IO.enumCase(Value, "Pointer", FormatStyle::RAS_Pointer); + IO.enumCase(Value, "Middle", FormatStyle::RAS_Middle); + IO.enumCase(Value, "Left", FormatStyle::RAS_Left); + IO.enumCase(Value, "Right", FormatStyle::RAS_Right); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, @@ -667,6 +677,7 @@ IO.mapOptional("PointerAlignment", Style.PointerAlignment); IO.mapOptional("PPIndentWidth", Style.PPIndentWidth); IO.mapOptional("RawStringFormats", Style.RawStringFormats); + IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment); IO.mapOptional("ReflowComments", Style.ReflowComments); IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines); IO.mapOptional("SortIncludes", Style.SortIncludes); @@ -1037,6 +1048,7 @@ LLVMStyle.ObjCSpaceAfterProperty = false; LLVMStyle.ObjCSpaceBeforeProtocolList = true; LLVMStyle.PointerAlignment = FormatStyle::PAS_Right; + LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer; LLVMStyle.ShortNamespaceLines = 1; LLVMStyle.SpacesBeforeTrailingComments = 1; LLVMStyle.Standard = FormatStyle::LS_Latest; @@ -1727,10 +1739,12 @@ Tok = Tok->Next; } } - if (Style.DerivePointerAlignment) + if (Style.DerivePointerAlignment) { Style.PointerAlignment = countVariableAlignments(AnnotatedLines) <= 0 ? FormatStyle::PAS_Left : FormatStyle::PAS_Right; + Style.ReferenceAlignment = FormatStyle::RAS_Pointer; + } if (Style.Standard == FormatStyle::LS_Auto) Style.Standard = hasCpp03IncompatibleFormat(AnnotatedLines) ? FormatStyle::LS_Latest diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h --- a/clang/lib/Format/TokenAnnotator.h +++ b/clang/lib/Format/TokenAnnotator.h @@ -189,6 +189,12 @@ void calculateUnbreakableTailLengths(AnnotatedLine &Line); + FormatStyle::PointerAlignmentStyle + getTokenReferenceAlignment(const FormatToken &PointerOrReference); + + FormatStyle::PointerAlignmentStyle + getTokenPointerOrReferenceAlignment(const FormatToken &PointerOrReference); + const FormatStyle &Style; const AdditionalKeywords &Keywords; 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 @@ -2887,16 +2887,17 @@ Style.SpaceAroundPointerQualifiers == FormatStyle::SAPQ_Both) && (Left.is(TT_AttributeParen) || Left.canBePointerOrReferenceQualifier())) return true; - return (Left.Tok.isLiteral() || - (!Left.isOneOf(TT_PointerOrReference, tok::l_paren) && - (Style.PointerAlignment != FormatStyle::PAS_Left || - (Line.IsMultiVariableDeclStmt && - (Left.NestingLevel == 0 || - (Left.NestingLevel == 1 && Line.First->is(tok::kw_for))))))); + return ( + Left.Tok.isLiteral() || + (!Left.isOneOf(TT_PointerOrReference, tok::l_paren) && + (getTokenPointerOrReferenceAlignment(Right) != FormatStyle::PAS_Left || + (Line.IsMultiVariableDeclStmt && + (Left.NestingLevel == 0 || + (Left.NestingLevel == 1 && Line.First->is(tok::kw_for))))))); } if (Right.is(TT_FunctionTypeLParen) && Left.isNot(tok::l_paren) && (!Left.is(TT_PointerOrReference) || - (Style.PointerAlignment != FormatStyle::PAS_Right && + (getTokenPointerOrReferenceAlignment(Left) != FormatStyle::PAS_Right && !Line.IsMultiVariableDeclStmt))) return true; if (Left.is(TT_PointerOrReference)) { @@ -2912,7 +2913,8 @@ (Right.is(tok::l_brace) && Right.is(BK_Block)) || (!Right.isOneOf(TT_PointerOrReference, TT_ArraySubscriptLSquare, tok::l_paren) && - (Style.PointerAlignment != FormatStyle::PAS_Right && + (getTokenPointerOrReferenceAlignment(Left) != + FormatStyle::PAS_Right && !Line.IsMultiVariableDeclStmt) && Left.Previous && !Left.Previous->isOneOf(tok::l_paren, tok::coloncolon, @@ -3077,7 +3079,7 @@ // Match const and volatile ref-qualifiers without any additional // qualifiers such as // void Fn() const &; - return Style.PointerAlignment != FormatStyle::PAS_Left; + return getTokenReferenceAlignment(Right) != FormatStyle::PAS_Left; return true; } @@ -3429,11 +3431,11 @@ // Space before TT_StructuredBindingLSquare. if (Right.is(TT_StructuredBindingLSquare)) return !Left.isOneOf(tok::amp, tok::ampamp) || - Style.PointerAlignment != FormatStyle::PAS_Right; + getTokenReferenceAlignment(Left) != FormatStyle::PAS_Right; // Space before & or && following a TT_StructuredBindingLSquare. if (Right.Next && Right.Next->is(TT_StructuredBindingLSquare) && Right.isOneOf(tok::amp, tok::ampamp)) - return Style.PointerAlignment != FormatStyle::PAS_Left; + return getTokenReferenceAlignment(Right) != FormatStyle::PAS_Left; if ((Right.is(TT_BinaryOperator) && !Left.is(tok::l_paren)) || (Left.isOneOf(TT_BinaryOperator, TT_ConditionalExpr) && !Right.is(tok::r_paren))) @@ -3929,7 +3931,8 @@ return !Right.is(tok::l_paren); if (Right.is(TT_PointerOrReference)) return Line.IsMultiVariableDeclStmt || - (Style.PointerAlignment == FormatStyle::PAS_Right && + (getTokenPointerOrReferenceAlignment(Right) == + FormatStyle::PAS_Right && (!Right.Next || Right.Next->isNot(TT_FunctionDeclarationName))); if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName) || Right.is(tok::kw_operator)) @@ -4144,5 +4147,39 @@ llvm::errs() << "----\n"; } +FormatStyle::PointerAlignmentStyle +TokenAnnotator::getTokenReferenceAlignment(const FormatToken &Reference) { + assert(Reference.isOneOf(tok::amp, tok::ampamp)); + switch (Style.ReferenceAlignment) { + case FormatStyle::RAS_Pointer: + return Style.PointerAlignment; + case FormatStyle::RAS_Left: + return FormatStyle::PAS_Left; + case FormatStyle::RAS_Right: + return FormatStyle::PAS_Right; + case FormatStyle::RAS_Middle: + return FormatStyle::PAS_Middle; + } +} + +FormatStyle::PointerAlignmentStyle +TokenAnnotator::getTokenPointerOrReferenceAlignment( + const FormatToken &PointerOrReference) { + if (PointerOrReference.isOneOf(tok::amp, tok::ampamp)) { + switch (Style.ReferenceAlignment) { + case FormatStyle::RAS_Pointer: + return Style.PointerAlignment; + case FormatStyle::RAS_Left: + return FormatStyle::PAS_Left; + case FormatStyle::RAS_Right: + return FormatStyle::PAS_Right; + case FormatStyle::RAS_Middle: + return FormatStyle::PAS_Middle; + } + } + assert(PointerOrReference.is(tok::star)); + return Style.PointerAlignment; +} + } // namespace format } // namespace clang diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -1230,6 +1230,98 @@ getLLVMStyleWithColumns(62)); } +TEST_F(FormatTest, SeparatePointerReferenceAlignment) { + FormatStyle Style = getLLVMStyle(); + // Check first the default LLVM style + // Style.PointerAlignment = FormatStyle::PAS_Right; + // Style.ReferenceAlignment = FormatStyle::RAS_Pointer; + verifyFormat("int *f1(int *a, int &b, int &&c);", Style); + verifyFormat("int &f2(int &&c, int *a, int &b);", Style); + verifyFormat("int &&f3(int &b, int &&c, int *a);", Style); + verifyFormat("int *f1(int &a) const &;", Style); + verifyFormat("int *f1(int &a) const & = 0;", Style); + verifyFormat("int *a = f1();", Style); + verifyFormat("int &b = f2();", Style); + verifyFormat("int &&c = f3();", Style); + + Style.AlignConsecutiveDeclarations = FormatStyle::ACS_Consecutive; + verifyFormat("Const unsigned int *c;\n" + "const unsigned int *d;\n" + "Const unsigned int &e;\n" + "const unsigned int &f;\n" + "const unsigned &&g;\n" + "Const unsigned h;", + Style); + + Style.PointerAlignment = FormatStyle::PAS_Left; + Style.ReferenceAlignment = FormatStyle::RAS_Pointer; + verifyFormat("int* f1(int* a, int& b, int&& c);", Style); + verifyFormat("int& f2(int&& c, int* a, int& b);", Style); + verifyFormat("int&& f3(int& b, int&& c, int* a);", Style); + verifyFormat("int* f1(int& a) const& = 0;", Style); + verifyFormat("int* a = f1();", Style); + verifyFormat("int& b = f2();", Style); + verifyFormat("int&& c = f3();", Style); + + Style.AlignConsecutiveDeclarations = FormatStyle::ACS_Consecutive; + verifyFormat("Const unsigned int* c;\n" + "const unsigned int* d;\n" + "Const unsigned int& e;\n" + "const unsigned int& f;\n" + "const unsigned&& g;\n" + "Const unsigned h;", + Style); + + Style.PointerAlignment = FormatStyle::PAS_Right; + Style.ReferenceAlignment = FormatStyle::RAS_Left; + verifyFormat("int *f1(int *a, int& b, int&& c);", Style); + verifyFormat("int& f2(int&& c, int *a, int& b);", Style); + verifyFormat("int&& f3(int& b, int&& c, int *a);", Style); + verifyFormat("int *a = f1();", Style); + verifyFormat("int& b = f2();", Style); + verifyFormat("int&& c = f3();", Style); + + Style.AlignConsecutiveDeclarations = FormatStyle::ACS_Consecutive; + verifyFormat("Const unsigned int *c;\n" + "const unsigned int *d;\n" + "Const unsigned int& e;\n" + "const unsigned int& f;\n" + "const unsigned g;\n" + "Const unsigned h;", + Style); + + Style.PointerAlignment = FormatStyle::PAS_Left; + Style.ReferenceAlignment = FormatStyle::RAS_Middle; + verifyFormat("int* f1(int* a, int & b, int && c);", Style); + verifyFormat("int & f2(int && c, int* a, int & b);", Style); + verifyFormat("int && f3(int & b, int && c, int* a);", Style); + verifyFormat("int* a = f1();", Style); + verifyFormat("int & b = f2();", Style); + verifyFormat("int && c = f3();", Style); + + Style.AlignConsecutiveDeclarations = FormatStyle::ACS_Consecutive; + verifyFormat("Const unsigned int* c;\n" + "const unsigned int* d;\n" + "Const unsigned int & e;\n" + "const unsigned int & f;\n" + "const unsigned && g;\n" + "Const unsigned h;", + Style); + + Style.PointerAlignment = FormatStyle::PAS_Middle; + Style.ReferenceAlignment = FormatStyle::RAS_Right; + verifyFormat("int * f1(int * a, int &b, int &&c);", Style); + verifyFormat("int &f2(int &&c, int * a, int &b);", Style); + verifyFormat("int &&f3(int &b, int &&c, int * a);", Style); + verifyFormat("int * a = f1();", Style); + verifyFormat("int &b = f2();", Style); + verifyFormat("int &&c = f3();", Style); + + // FIXME: we don't handle this yet, so output may be arbitrary until it's + // specifically handled + // verifyFormat("int Add2(BTree * &Root, char * szToAdd)", Style); +} + TEST_F(FormatTest, FormatsForLoop) { verifyFormat( "for (int VeryVeryLongLoopVariable = 0; VeryVeryLongLoopVariable < 10;\n" @@ -17031,6 +17123,15 @@ FormatStyle::PAS_Right); CHECK_PARSE("PointerAlignment: Middle", PointerAlignment, FormatStyle::PAS_Middle); + Style.ReferenceAlignment = FormatStyle::RAS_Middle; + CHECK_PARSE("ReferenceAlignment: Pointer", ReferenceAlignment, + FormatStyle::RAS_Pointer); + CHECK_PARSE("ReferenceAlignment: Left", ReferenceAlignment, + FormatStyle::RAS_Left); + CHECK_PARSE("ReferenceAlignment: Right", ReferenceAlignment, + FormatStyle::RAS_Right); + CHECK_PARSE("ReferenceAlignment: Middle", ReferenceAlignment, + FormatStyle::RAS_Middle); // For backward compatibility: CHECK_PARSE("PointerBindsToType: Left", PointerAlignment, FormatStyle::PAS_Left);