diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2463,6 +2463,46 @@ true: false: template void foo(); vs. template void foo(); +**SpaceAroundPointerQualifiers** (``SpaceAroundPointerQualifiersStyle``) + Defines in which cases to put a space before or after pointer qualifiers + + Possible values: + + * ``SAPQ_Default`` (in configuration: ``Default``) + Don't ensure spaces around pointer qualifiers and use PointerAlignment + instead. + + .. code-block:: c++ + + PointerAlignment: Left PointerAlignment: Right + void* const* x = NULL; vs. void *const *x = NULL; + + * ``SAPQ_Before`` (in configuration: ``Before``) + Ensure that there is a space before pointer qualifiers. + + .. code-block:: c++ + + PointerAlignment: Left PointerAlignment: Right + void* const* x = NULL; vs. void * const *x = NULL; + + * ``SAPQ_After`` (in configuration: ``After``) + Ensure that there is a space after pointer qualifiers. + + .. code-block:: c++ + + PointerAlignment: Left PointerAlignment: Right + void* const * x = NULL; vs. void *const *x = NULL; + + * ``SAPQ_Both`` (in configuration: ``Both``) + Ensure that there is a space both before and after pointer qualifiers. + + .. code-block:: c++ + + PointerAlignment: Left PointerAlignment: Right + void* const * x = NULL; vs. void * const *x = NULL; + + + **SpaceBeforeAssignmentOperators** (``bool``) If ``false``, spaces will be removed before assignment operators. 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 @@ -2078,6 +2078,38 @@ /// \endcode bool SpaceAfterTemplateKeyword; + /// Different ways to put a space before opening parentheses. + enum SpaceAroundPointerQualifiersStyle { + /// Don't ensure spaces around pointer qualifiers and use PointerAlignment + /// instead. + /// \code + /// PointerAlignment: Left PointerAlignment: Right + /// void* const* x = NULL; vs. void *const *x = NULL; + /// \endcode + SAPQ_Default, + /// Ensure that there is a space before pointer qualifiers. + /// \code + /// PointerAlignment: Left PointerAlignment: Right + /// void* const* x = NULL; vs. void * const *x = NULL; + /// \endcode + SAPQ_Before, + /// Ensure that there is a space after pointer qualifiers. + /// \code + /// PointerAlignment: Left PointerAlignment: Right + /// void* const * x = NULL; vs. void *const *x = NULL; + /// \endcode + SAPQ_After, + /// Ensure that there is a space both before and after pointer qualifiers. + /// \code + /// PointerAlignment: Left PointerAlignment: Right + /// void* const * x = NULL; vs. void * const *x = NULL; + /// \endcode + SAPQ_Both, + }; + + /// Defines in which cases to put a space before or after pointer qualifiers + SpaceAroundPointerQualifiersStyle SpaceAroundPointerQualifiers; + /// If ``false``, spaces will be removed before assignment operators. /// \code /// true: false: @@ -2470,6 +2502,7 @@ R.SpaceBeforeCtorInitializerColon && SpaceBeforeInheritanceColon == R.SpaceBeforeInheritanceColon && SpaceBeforeParens == R.SpaceBeforeParens && + SpaceAroundPointerQualifiers == R.SpaceAroundPointerQualifiers && SpaceBeforeRangeBasedForLoopColon == R.SpaceBeforeRangeBasedForLoopColon && SpaceInEmptyBlock == R.SpaceInEmptyBlock && 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 @@ -347,6 +347,17 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void + enumeration(IO &IO, FormatStyle::SpaceAroundPointerQualifiersStyle &Value) { + IO.enumCase(Value, "Default", FormatStyle::SAPQ_Default); + IO.enumCase(Value, "Before", FormatStyle::SAPQ_Before); + IO.enumCase(Value, "After", FormatStyle::SAPQ_After); + IO.enumCase(Value, "Both", FormatStyle::SAPQ_Both); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, @@ -598,6 +609,8 @@ IO.mapOptional("SpaceBeforeInheritanceColon", Style.SpaceBeforeInheritanceColon); IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens); + IO.mapOptional("SpaceAroundPointerQualifiers", + Style.SpaceAroundPointerQualifiers); IO.mapOptional("SpaceBeforeRangeBasedForLoopColon", Style.SpaceBeforeRangeBasedForLoopColon); IO.mapOptional("SpaceInEmptyBlock", Style.SpaceInEmptyBlock); @@ -935,6 +948,7 @@ LLVMStyle.SpaceAfterCStyleCast = false; LLVMStyle.SpaceAfterLogicalNot = false; LLVMStyle.SpaceAfterTemplateKeyword = true; + LLVMStyle.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Default; LLVMStyle.SpaceBeforeCtorInitializerColon = true; LLVMStyle.SpaceBeforeInheritanceColon = true; LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; 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 @@ -2859,6 +2859,13 @@ if (!TokenBeforeMatchingParen || !Left.is(TT_TypeDeclarationParen)) return true; } + // Add a space if the previous token is a pointer qualifer or the closing + // parenthesis of __attribute__(()) expression and the style requires spaces + // after pointer qualifiers. + if ((Style.SpaceAroundPointerQualifiers == FormatStyle::SAPQ_After || + 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 || @@ -2871,7 +2878,13 @@ (Style.PointerAlignment != FormatStyle::PAS_Right && !Line.IsMultiVariableDeclStmt))) return true; - if (Left.is(TT_PointerOrReference)) + if (Left.is(TT_PointerOrReference)) { + // Add a space if the next token is a pointer qualifer and the style + // requires spaces before pointer qualifiers. + if ((Style.SpaceAroundPointerQualifiers == FormatStyle::SAPQ_Before || + Style.SpaceAroundPointerQualifiers == FormatStyle::SAPQ_Both) && + Right.canBePointerOrReferenceQualifier()) + return true; return Right.Tok.isLiteral() || Right.is(TT_BlockComment) || (Right.isOneOf(Keywords.kw_override, Keywords.kw_final) && !Right.is(TT_StartOfName)) || @@ -2883,6 +2896,7 @@ Left.Previous && !Left.Previous->isOneOf(tok::l_paren, tok::coloncolon, tok::l_square)); + } // Ensure right pointer alignement with ellipsis e.g. int *...P if (Left.is(tok::ellipsis) && Left.Previous && Left.Previous->isOneOf(tok::star, tok::amp, tok::ampamp)) 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 @@ -12104,6 +12104,80 @@ NoSpaceStyle); } +TEST_F(FormatTest, ConfigurableSpaceAroundPointerQualifiers) { + FormatStyle Style = getLLVMStyle(); + + Style.PointerAlignment = FormatStyle::PAS_Left; + Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Default; + verifyFormat("void* const* x = NULL;", Style); + +#define verifyQualifierSpaces(Code, Pointers, Qualifiers) \ + do { \ + Style.PointerAlignment = FormatStyle::Pointers; \ + Style.SpaceAroundPointerQualifiers = FormatStyle::Qualifiers; \ + verifyFormat(Code, Style); \ + } while (false) + + verifyQualifierSpaces("void* const* x = NULL;", PAS_Left, SAPQ_Default); + verifyQualifierSpaces("void *const *x = NULL;", PAS_Right, SAPQ_Default); + verifyQualifierSpaces("void * const * x = NULL;", PAS_Middle, SAPQ_Default); + + verifyQualifierSpaces("void* const* x = NULL;", PAS_Left, SAPQ_Before); + verifyQualifierSpaces("void * const *x = NULL;", PAS_Right, SAPQ_Before); + verifyQualifierSpaces("void * const * x = NULL;", PAS_Middle, SAPQ_Before); + + verifyQualifierSpaces("void* const * x = NULL;", PAS_Left, SAPQ_After); + verifyQualifierSpaces("void *const *x = NULL;", PAS_Right, SAPQ_After); + verifyQualifierSpaces("void * const * x = NULL;", PAS_Middle, SAPQ_After); + + verifyQualifierSpaces("void* const * x = NULL;", PAS_Left, SAPQ_Both); + verifyQualifierSpaces("void * const *x = NULL;", PAS_Right, SAPQ_Both); + verifyQualifierSpaces("void * const * x = NULL;", PAS_Middle, SAPQ_Both); + +#undef verifyQualifierSpaces + + FormatStyle Spaces = getLLVMStyle(); + Spaces.AttributeMacros.push_back("qualified"); + Spaces.PointerAlignment = FormatStyle::PAS_Right; + Spaces.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Default; + verifyFormat("SomeType *volatile *a = NULL;", Spaces); + verifyFormat("SomeType *__attribute__((attr)) *a = NULL;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + Spaces.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Before; + verifyFormat("SomeType * volatile *a = NULL;", Spaces); + verifyFormat("SomeType * __attribute__((attr)) *a = NULL;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + + // Check that SAPQ_Before doesn't result in extra spaces for PAS_Left. + Spaces.PointerAlignment = FormatStyle::PAS_Left; + Spaces.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Before; + verifyFormat("SomeType* volatile* a = NULL;", Spaces); + verifyFormat("SomeType* __attribute__((attr))* a = NULL;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + // However, setting it to SAPQ_After should add spaces after __attribute, etc. + Spaces.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_After; + verifyFormat("SomeType* volatile * a = NULL;", Spaces); + verifyFormat("SomeType* __attribute__((attr)) * a = NULL;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + + // PAS_Middle should not have any noticeable changes even for SAPQ_Both + Spaces.PointerAlignment = FormatStyle::PAS_Middle; + Spaces.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_After; + verifyFormat("SomeType * volatile * a = NULL;", Spaces); + verifyFormat("SomeType * __attribute__((attr)) * a = NULL;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); + verifyFormat("std::vector x;", Spaces); +} + TEST_F(FormatTest, AlignConsecutiveMacros) { FormatStyle Style = getLLVMStyle(); Style.AlignConsecutiveAssignments = true; @@ -14194,6 +14268,16 @@ CHECK_PARSE("AllowShortFunctionsOnASingleLine: true", AllowShortFunctionsOnASingleLine, FormatStyle::SFS_All); + Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both; + CHECK_PARSE("SpaceAroundPointerQualifiers: Default", + SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Default); + CHECK_PARSE("SpaceAroundPointerQualifiers: Before", + SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Before); + CHECK_PARSE("SpaceAroundPointerQualifiers: After", + SpaceAroundPointerQualifiers, FormatStyle::SAPQ_After); + CHECK_PARSE("SpaceAroundPointerQualifiers: Both", + SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Both); + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; CHECK_PARSE("SpaceBeforeParens: Never", SpaceBeforeParens, FormatStyle::SBPO_Never);