From 9e27f38354e850346f5f4e895e44ad3346546d9c Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Sun, 18 Oct 2020 16:05:36 +0100 Subject: [PATCH] [clang-format] Add a SpaceAroundPointerQualifiers style option Some projects (e.g. FreeBSD) align pointers to the right but expect a space between the '*' and any pointer qualifiers such as const. To handle these cases this patch adds a new config option SpaceAroundPointerQualifiers that can be used to configure whether spaces need to be added before/after pointer qualifiers. PointerAlignment = Right SpaceAroundPointerQualifiers = Default/After: void *const *x = NULL; SpaceAroundPointerQualifiers = Before/Both void * const *x = NULL; PointerAlignment = Left SpaceAroundPointerQualifiers = Default/Before: void* const* x = NULL; SpaceAroundPointerQualifiers = After/Both void* const * x = NULL; PointerAlignment = Middle SpaceAroundPointerQualifiers = Default/Before/After/Both: void * const * x = NULL; Reviewed By: MyDeveloperDay Differential Revision: https://reviews.llvm.org/D88227 --- clang/docs/ClangFormatStyleOptions.rst | 40 ++++++++++++++++ clang/include/clang/Format/Format.h | 33 +++++++++++++ clang/lib/Format/Format.cpp | 14 ++++++ clang/lib/Format/TokenAnnotator.cpp | 16 ++++++- clang/unittests/Format/FormatTest.cpp | 84 ++++++++++++++++++++++++++++++++++ 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 0c3ed9f..ccc59cd 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2463,6 +2463,46 @@ the configuration (without a prefix: ``Auto``). 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 index 40d5184..587e588 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2078,6 +2078,38 @@ struct FormatStyle { /// \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 @@ struct FormatStyle { 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 index f2215e2..1c566c9 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -348,6 +348,17 @@ template <> struct ScalarEnumerationTraits { }; 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, FormatStyle::SpaceBeforeParensOptions &Value) { @@ -598,6 +609,8 @@ template <> struct MappingTraits { 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 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { 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 index 2fa3b28..66a8cac 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2859,6 +2859,13 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, 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 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, (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 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, 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 index 7686e25..1bdd706 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12104,6 +12104,80 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeColon) { 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 @@ TEST_F(FormatTest, ParsesConfiguration) { 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); -- 2.7.4