Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -741,8 +741,7 @@ enum { A, B } myEnum; false: - enum - { + enum { A, B } myEnum; @@ -3201,6 +3200,73 @@ +**QualifierAlignment** (``QualifierAlignmentStyle``) + Different ways to arrange const/volatile qualifiers. + + Possible values: + + * ``QAS_Leave`` (in configuration: ``Leave``) + Don't change specifiers/qualifier to either Left or Right alignment + + .. code-block:: c++ + + int const a; + const int *a; + + * ``QAS_Left`` (in configuration: ``Left``) + Change specifiers/qualifiers to be Left aligned. + + .. code-block:: c++ + + const int a; + const int *a; + + * ``QAS_Right`` (in configuration: ``Right``) + Change specifiers/qualifiers to be Right aligned. + + .. code-block:: c++ + + int const a; + int const *a; + + * ``QAS_Custom`` (in configuration: ``Custom``) + Change specifiers/qualifiers to be aligned based on QualfierOrder. + With: + + .. code-block:: yaml + + QualifierOrder: ['inline', 'static' , '', 'const'] + + + .. code-block:: c++ + + + int const a; + int const *a; + + + +**QualifierOrder** (``std::vector``) + The Order in which the qualifiers appear. + Order is a an array can contain any of the following + + * const + * inline + * static + * constexpr + * volatile + * restrict + * type + + Note: it MUST contain 'type'. + Items to the left of type will be aligned in the order supplied. + Items to the right of type will be aligned in the order supplied. + + + .. code-block:: yaml + + QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ] + **RawStringFormats** (``std::vector``) Defines hints for detecting supported languages code blocks in raw strings. Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -156,6 +156,16 @@ - Option ``AllowShortEnumsOnASingleLine: false`` has been improved, it now correctly places the opening brace according to ``BraceWrapping.AfterEnum``. +- Option ``QualifierAligment`` has been added in order to auto-arrange the + positioning of specifiers/qualifiers + `const` `volatile` `static` `inline` `constexpr` `restrict` + in variable and parameter declarations to be either ``Right`` aligned + or ``Left`` aligned or ``Custom`` using ``QualifierOrder``. + +- Option ``QualifierOrder`` has been added to allow the order + `const` `volatile` `static` `inline` `constexpr` `restrict` + to be controlled relative to the ``. + libclang -------- Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -40,7 +40,11 @@ Success = 0, Error, Unsuitable, - BinPackTrailingCommaConflict + BinPackTrailingCommaConflict, + InvalidQualifierSpecified, + DuplicateQualifierSpecified, + MissingQualifierType, + MissingQualifierOrder }; class ParseErrorCategory final : public std::error_category { public: @@ -1831,6 +1835,63 @@ /// \endcode std::string CommentPragmas; + /// Different const/volatile qualifier alignment styles. + enum QualifierAlignmentStyle { + /// Don't change specifiers/qualifier to either Left or Right alignment + /// \code + /// int const a; + /// const int *a; + /// \endcode + QAS_Leave, + /// Change specifiers/qualifiers to be Left aligned. + /// \code + /// const int a; + /// const int *a; + /// \endcode + QAS_Left, + /// Change specifiers/qualifiers to be Right aligned. + /// \code + /// int const a; + /// int const *a; + /// \endcode + QAS_Right, + /// Change specifiers/qualifiers to be aligned based on QualfierOrder. + /// With: + /// \code{.yaml} + /// QualifierOrder: ['inline', 'static' , '', 'const'] + /// \endcode + /// + /// \code + /// + /// int const a; + /// int const *a; + /// \endcode + QAS_Custom + }; + + /// Different ways to arrange const/volatile qualifiers. + QualifierAlignmentStyle QualifierAlignment; + + /// The Order in which the qualifiers appear. + /// Order is a an array can contain any of the following + /// + /// * const + /// * inline + /// * static + /// * constexpr + /// * volatile + /// * restrict + /// * type + /// + /// Note: it MUST contain 'type'. + /// Items to the left of type will be aligned in the order supplied. + /// Items to the right of type will be aligned in the order supplied. + /// + /// \code{.yaml} + /// QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ] + /// \endcode + std::vector QualifierOrder; + /// Different ways to break inheritance list. enum BreakInheritanceListStyle : unsigned char { /// Break inheritance list before the colon and after the commas. @@ -3422,6 +3483,8 @@ BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakStringLiterals == R.BreakStringLiterals && ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas && + QualifierAlignment == R.QualifierAlignment && + QualifierOrder == R.QualifierOrder && BreakInheritanceList == R.BreakInheritanceList && ConstructorInitializerAllOnOneLineOrOnePerLine == R.ConstructorInitializerAllOnOneLineOrOnePerLine && Index: clang/lib/Format/CMakeLists.txt =================================================================== --- clang/lib/Format/CMakeLists.txt +++ clang/lib/Format/CMakeLists.txt @@ -15,6 +15,7 @@ UnwrappedLineFormatter.cpp UnwrappedLineParser.cpp UsingDeclarationsSorter.cpp + QualifierAlignmentFixer.cpp WhitespaceManager.cpp LINK_LIBS Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -19,6 +19,7 @@ #include "FormatInternal.h" #include "FormatTokenLexer.h" #include "NamespaceEndCommentsFixer.h" +#include "QualifierAlignmentFixer.h" #include "SortJavaScriptImports.h" #include "TokenAnalyzer.h" #include "TokenAnnotator.h" @@ -126,6 +127,16 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::QualifierAlignmentStyle &Value) { + IO.enumCase(Value, "Leave", FormatStyle::QAS_Leave); + IO.enumCase(Value, "Left", FormatStyle::QAS_Left); + IO.enumCase(Value, "Right", FormatStyle::QAS_Right); + IO.enumCase(Value, "Custom", FormatStyle::QAS_Custom); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::ShortFunctionStyle &Value) { IO.enumCase(Value, "None", FormatStyle::SFS_None); @@ -632,6 +643,22 @@ IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("QualifierAlignment", Style.QualifierAlignment); + + // Default Order for Left/Right based Qualifier alignment. + if (Style.QualifierAlignment == FormatStyle::QAS_Right) { + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("type"); + Style.QualifierOrder.push_back("const"); + Style.QualifierOrder.push_back("volatile"); + } else if (Style.QualifierAlignment == FormatStyle::QAS_Left) { + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("const"); + Style.QualifierOrder.push_back("volatile"); + Style.QualifierOrder.push_back("type"); + } + IO.mapOptional("QualifierOrder", Style.QualifierOrder); + IO.mapOptional("CompactNamespaces", Style.CompactNamespaces); IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine", Style.ConstructorInitializerAllOnOneLineOrOnePerLine); @@ -863,6 +890,14 @@ return "Unsuitable"; case ParseError::BinPackTrailingCommaConflict: return "trailing comma insertion cannot be used with bin packing"; + case ParseError::InvalidQualifierSpecified: + return "Invalid qualifier specified in QualifierOrder"; + case ParseError::DuplicateQualifierSpecified: + return "Duplicate qualifier specified in QualfierOrder"; + case ParseError::MissingQualifierType: + return "Missing type in QualfierOrder"; + case ParseError::MissingQualifierOrder: + return "Missing QualfierOrder"; } llvm_unreachable("unexpected parse error"); } @@ -1038,6 +1073,16 @@ LLVMStyle.ConstructorInitializerIndentWidth = 4; LLVMStyle.ContinuationIndentWidth = 4; LLVMStyle.Cpp11BracedListStyle = true; + + // Off by default Qualifier ordering + LLVMStyle.QualifierAlignment = FormatStyle::QAS_Leave; + // Default 'Left' qualifier order but off by default. + LLVMStyle.QualifierOrder.push_back("inline"); + LLVMStyle.QualifierOrder.push_back("static"); + LLVMStyle.QualifierOrder.push_back("const"); + LLVMStyle.QualifierOrder.push_back("volatile"); + LLVMStyle.QualifierOrder.push_back("type"); + LLVMStyle.DeriveLineEnding = true; LLVMStyle.DerivePointerAlignment = false; LLVMStyle.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; @@ -1471,6 +1516,32 @@ return true; } +ParseError validateQualifierOrder(FormatStyle *Style) { + // If its empty then it means don't do anything. + if (Style->QualifierOrder.empty()) + return ParseError::MissingQualifierOrder; + + // Ensure the list contains only currently valid qualifiers. + for (const auto &Qualifier : Style->QualifierOrder) { + if (Qualifier == "type") + continue; + auto token = QualifierAlignmentFixer::getTokenFromQualifier(Qualifier); + if (token == tok::identifier) + return ParseError::InvalidQualifierSpecified; + } + // Ensure the list is unqiue (no duplicates). + std::set UniqueQualifiers(Style->QualifierOrder.begin(), + Style->QualifierOrder.end()); + if (Style->QualifierOrder.size() != UniqueQualifiers.size()) + return ParseError::DuplicateQualifierSpecified; + + auto type = std::find(Style->QualifierOrder.begin(), + Style->QualifierOrder.end(), "type"); + if (type == Style->QualifierOrder.end()) + return ParseError::MissingQualifierType; + return ParseError::Success; +} + std::error_code parseConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, bool AllowUnknownOptions, llvm::SourceMgr::DiagHandlerTy DiagHandler, @@ -1532,6 +1603,8 @@ // See comment on FormatStyle::TSC_Wrapped. return make_error_code(ParseError::BinPackTrailingCommaConflict); } + if (Style->QualifierAlignment != FormatStyle::QAS_Leave) + return make_error_code(validateQualifierOrder(Style)); return make_error_code(ParseError::Success); } @@ -2816,6 +2889,34 @@ } namespace internal { + +void PrepareLeftRightOrdering(const std::vector &Order, + std::vector &LeftOrder, + std::vector &RightOrder) { + + // Depending on the position of type in the order you need + // To iterate forward or backward through the order list as qualifier + // can push through each other. + auto type = std::find(Order.begin(), Order.end(), "type"); + // The Order list must define the position of "type" to signify + assert(type != Order.end() && "QualifierOrder must contain type"); + // Split the Order list by type and reverse the left side + bool left = true; + for (const auto &s : Order) { + if (s == "type") { + left = false; + continue; + } + if (left) { + LeftOrder.push_back(s); + } else { + RightOrder.push_back(s); + } + } + // Reverse the order for left aligned items. + std::reverse(LeftOrder.begin(), LeftOrder.end()); +} + std::pair reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, unsigned FirstStartColumn, @@ -2865,6 +2966,28 @@ }); } + if (Style.isCpp() && Style.QualifierAlignment != FormatStyle::QAS_Leave) { + std::vector LeftOrder; + std::vector RightOrder; + PrepareLeftRightOrdering(Style.QualifierOrder, LeftOrder, RightOrder); + + // Handle the left and right Alignment Seperately + for (const auto &Qualifier : LeftOrder) { + Passes.emplace_back([&, Qualifier](const Environment &Env) { + return QualifierAlignmentFixer(Env, Expanded, Qualifier, + FormatStyle::QAS_Left) + .process(); + }); + } + for (const auto &Qualifier : RightOrder) { + Passes.emplace_back([&, Qualifier](const Environment &Env) { + return QualifierAlignmentFixer(Env, Expanded, Qualifier, + FormatStyle::QAS_Right) + .process(); + }); + } + } + if (Style.Language == FormatStyle::LK_JavaScript && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave) Passes.emplace_back([&](const Environment &Env) { Index: clang/lib/Format/QualifierAlignmentFixer.h =================================================================== --- /dev/null +++ clang/lib/Format/QualifierAlignmentFixer.h @@ -0,0 +1,44 @@ +//===--- QualifierAlignmentFixer.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares QualifierAlignmentFixer, a TokenAnalyzer that +/// enforces either east or west const depending on the style. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_FORMAT_QUALIFIERALIGNMENTFIXER_H +#define LLVM_CLANG_LIB_FORMAT_QUALIFIERALIGNMENTFIXER_H + +#include "TokenAnalyzer.h" + +namespace clang { +namespace format { + +class QualifierAlignmentFixer : public TokenAnalyzer { + std::string Qualifier; + FormatStyle::QualifierAlignmentStyle Alignment; + +public: + QualifierAlignmentFixer( + const Environment &Env, const FormatStyle &Style, + const std::string &Qualifier, + const FormatStyle::QualifierAlignmentStyle &Alignment); + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override; + + static tok::TokenKind getTokenFromQualifier(const std::string &Qualifier); +}; + +} // end namespace format +} // end namespace clang + +#endif Index: clang/lib/Format/QualifierAlignmentFixer.cpp =================================================================== --- /dev/null +++ clang/lib/Format/QualifierAlignmentFixer.cpp @@ -0,0 +1,333 @@ +//===--- QualifierAlignmentFixer.cpp ---------------------------*- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements QualifierAlignmentFixer, a TokenAnalyzer that +/// enforces either east or west const depending on the style. +/// +//===----------------------------------------------------------------------===// + +#include "QualifierAlignmentFixer.h" +#include "FormatToken.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Regex.h" + +#include + +namespace clang { +namespace format { + +static void replaceToken(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const CharSourceRange &Range, std::string NewText) { + auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); + auto Err = Fixes.add(Replacement); + + if (Err) + llvm::errs() << "Error while rearranging qualifier : " + << llvm::toString(std::move(Err)) << "\n"; +} + +static void removeToken(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *First) { + auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), + First->Tok.getEndLoc()); + replaceToken(SourceMgr, Fixes, Range, ""); +} + +static void insertQualifierAfter(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *First, + const std::string &Qualifier) { + FormatToken *Next = First->Next; + if (!Next) + return; + auto Range = CharSourceRange::getCharRange( + Next->getStartOfNonWhitespace(), Next->Next->getStartOfNonWhitespace()); + + std::string NewText = " " + Qualifier + " "; + NewText += Next->TokenText; + + replaceToken(SourceMgr, Fixes, Range, NewText); +} + +static void insertQualifierBefore(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *First, + const std::string &Qualifier) { + auto Range = CharSourceRange::getCharRange( + First->getStartOfNonWhitespace(), First->Next->getStartOfNonWhitespace()); + + std::string NewText = " " + Qualifier + " "; + NewText += First->TokenText; + + replaceToken(SourceMgr, Fixes, Range, NewText); +} + +static void rotateTokens(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, const FormatToken *First, + const FormatToken *Last, bool Left) { + auto *End = Last; + auto *Begin = First; + if (!Left) { + End = Last->Next; + Begin = First->Next; + } + + std::string NewText; + // If we are rotating to the left we move the Last token to the front. + if (Left) { + NewText += Last->TokenText; + NewText += " "; + } + + // Then move through the other tokens. + auto *Tok = Begin; + while (Tok != End) { + if (!NewText.empty()) + NewText += " "; + + NewText += Tok->TokenText; + Tok = Tok->Next; + } + + // If we are rotating to the right we move the first token to the back. + if (!Left) { + NewText += " "; + NewText += First->TokenText; + } + + auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), + Last->Tok.getEndLoc()); + + replaceToken(SourceMgr, Fixes, Range, NewText); +} + +static bool isQualifierOrType(const FormatToken *Tok) { + return Tok && (Tok->isSimpleTypeSpecifier() || + Tok->isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_auto, + tok::kw_static, tok::kw_inline, tok::kw_constexpr, + tok::kw_restrict)); +} + +// If a token is an identifier and it's upper case, it could +// be a macro and hence we need to be able to ignore it. +static bool isPossibleMacro(const FormatToken *Tok) { + if (!Tok) + return false; + if (!Tok->is(tok::identifier)) + return false; + if (Tok->TokenText.upper() == Tok->TokenText.str()) + return true; + return false; +} + +static FormatToken *analyzeRight(const SourceManager &SourceMgr, + const AdditionalKeywords &Keywords, + tooling::Replacements &Fixes, FormatToken *Tok, + const std::string &Qualifier, + tok::TokenKind QualifierType) { + // We only need to think about streams that begin with const. + if (!Tok->is(QualifierType)) + return Tok; + // Don't concern yourself if nothing follows const. + if (!Tok->Next) + return Tok; + if (isPossibleMacro(Tok->Next)) + return Tok; + FormatToken *Const = Tok; + + FormatToken *Qual = Tok->Next; + FormatToken *LastQual = Qual; + while (Qual && isQualifierOrType(Qual)) { + LastQual = Qual; + Qual = Qual->Next; + } + if (LastQual && Qual != LastQual) { + rotateTokens(SourceMgr, Fixes, Const, LastQual, /*Left=*/false); + Tok = LastQual; + } else if (Tok->startsSequence(QualifierType, tok::identifier, + TT_TemplateOpener)) { + // Read from the TemplateOpener to + // TemplateCloser as in const ArrayRef a; const ArrayRef &a; + FormatToken *EndTemplate = Tok->Next->Next->MatchingParen; + if (EndTemplate) { + // Move to the end of any template class members e.g. + // `Foo::iterator`. + if (EndTemplate->startsSequence(TT_TemplateCloser, tok::coloncolon, + tok::identifier)) + EndTemplate = EndTemplate->Next->Next; + } + if (EndTemplate && EndTemplate->Next && + !EndTemplate->Next->isOneOf(tok::equal, tok::l_paren)) { + // Remove the const. + insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier); + removeToken(SourceMgr, Fixes, Tok); + return Tok; + } + } else if (Tok->startsSequence(QualifierType, tok::identifier)) { + FormatToken *Next = Tok->Next; + // The case `const Foo` -> `Foo const` + // The case `const Foo *` -> `Foo const *` + // The case `const Foo &` -> `Foo const &` + // The case `const Foo &&` -> `Foo const &&` + // The case `const std::Foo &&` -> `std::Foo const &&` + // The case `const std::Foo &&` -> `std::Foo const &&` + while (Next && Next->isOneOf(tok::identifier, tok::coloncolon)) { + Next = Next->Next; + } + if (Next && Next->is(TT_TemplateOpener)) { + Next = Next->MatchingParen; + // Move to the end of any template class members e.g. + // `Foo::iterator`. + if (Next && Next->startsSequence(TT_TemplateCloser, tok::coloncolon, + tok::identifier)) { + Next = Next->Next->Next; + return Tok; + } + assert(Next && "Missing template opener"); + Next = Next->Next; + } + if (Next && Next->isOneOf(tok::star, tok::amp, tok::ampamp) && + !Tok->Next->isOneOf(Keywords.kw_override, Keywords.kw_final)) { + if (Next->Previous && !Next->Previous->is(QualifierType)) { + insertQualifierAfter(SourceMgr, Fixes, Next->Previous, Qualifier); + removeToken(SourceMgr, Fixes, Const); + } + return Next; + } + } + + return Tok; +} + +static FormatToken *analyzeLeft(const SourceManager &SourceMgr, + const AdditionalKeywords &Keywords, + tooling::Replacements &Fixes, FormatToken *Tok, + const std::string &Qualifier, + tok::TokenKind QualifierType) { + // if Tok is an identifier and possibly a macro then don't convert + if (isPossibleMacro(Tok)) + return Tok; + + FormatToken *Qual = Tok; + FormatToken *LastQual = Qual; + while (Qual && isQualifierOrType(Qual)) { + LastQual = Qual; + Qual = Qual->Next; + if (Qual && Qual->is(QualifierType)) + break; + } + if (LastQual && Qual != LastQual && Qual->is(QualifierType)) { + rotateTokens(SourceMgr, Fixes, Tok, Qual, /*Left=*/true); + Tok = Qual->Next; + } else if (Tok->startsSequence(tok::identifier, QualifierType)) { + if (Tok->Next->Next && Tok->Next->Next->isOneOf(tok::identifier, tok::star, + tok::amp, tok::ampamp)) { + // Don't swap `::iterator const` to `::const iterator`. + if (!Tok->Previous || + (Tok->Previous && !Tok->Previous->is(tok::coloncolon))) + rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true); + } + } + if (Tok->is(TT_TemplateOpener) && Tok->Next && + (Tok->Next->is(tok::identifier) || Tok->Next->isSimpleTypeSpecifier()) && + Tok->Next->Next && Tok->Next->Next->is(QualifierType)) { + rotateTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next, /*Left=*/true); + } + if (Tok->startsSequence(tok::identifier) && Tok->Next) { + if (Tok->Previous && + Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)) { + return Tok; + } + FormatToken *Next = Tok->Next; + // The case `std::Foo const` -> `const std::Foo &&` + while (Next && Next->isOneOf(tok::identifier, tok::coloncolon)) + Next = Next->Next; + if (Next && Next->Previous && + Next->Previous->startsSequence(tok::identifier, TT_TemplateOpener)) { + // Read from to the end of the TemplateOpener to + // TemplateCloser const ArrayRef a; const ArrayRef &a; + assert(Next->MatchingParen && "Missing template closer"); + Next = Next->MatchingParen->Next; + + // Move to the end of any template class members e.g. + // `Foo::iterator`. + if (Next && Next->startsSequence(tok::coloncolon, tok::identifier)) + Next = Next->Next->Next; + if (Next && Next->is(QualifierType)) { + // Remove the const. + removeToken(SourceMgr, Fixes, Next); + insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier); + return Next; + } + } + if (Next && Next->Next && + Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) { + if (Next->is(QualifierType)) { + // Remove the const. + removeToken(SourceMgr, Fixes, Next); + insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier); + return Next; + } + } + } + return Tok; +} + +tok::TokenKind +QualifierAlignmentFixer::getTokenFromQualifier(const std::string &Qualifier) { + // don't let 'type' be an indentifier steal typeof token + return llvm::StringSwitch(Qualifier) + .Case("type", tok::kw_typeof) + .Case("const", tok::kw_const) + .Case("volatile", tok::kw_volatile) + .Case("static", tok::kw_static) + .Case("inline", tok::kw_inline) + .Case("constexpr", tok::kw_constexpr) + .Case("restrict", tok::kw_restrict) + .Default(tok::identifier); +} + +QualifierAlignmentFixer::QualifierAlignmentFixer( + const Environment &Env, const FormatStyle &Style, + const std::string &Qualifier, + const FormatStyle::QualifierAlignmentStyle &Alignment) + : TokenAnalyzer(Env, Style), Qualifier(Qualifier), Alignment(Alignment) {} + +std::pair QualifierAlignmentFixer::analyze( + TokenAnnotator &Annotator, SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) { + tooling::Replacements Fixes; + const AdditionalKeywords &Keywords = Tokens.getKeywords(); + const SourceManager &SourceMgr = Env.getSourceManager(); + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + + tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); + assert(QualifierToken != tok::identifier && "Unrecognised Qualifier"); + + for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { + FormatToken *First = AnnotatedLines[I]->First; + const auto *Last = AnnotatedLines[I]->Last; + + for (auto *Tok = First; Tok && Tok != Last && Tok->Next; Tok = Tok->Next) { + if (Tok->is(tok::comment)) + continue; + if (Alignment == FormatStyle::QAS_Right) + Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, + QualifierToken); + else if (Alignment == FormatStyle::QAS_Left) + Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, + QualifierToken); + } + } + return {Fixes, 0}; +} +} // namespace format +} // namespace clang Index: clang/tools/clang-format/ClangFormat.cpp =================================================================== --- clang/tools/clang-format/ClangFormat.cpp +++ clang/tools/clang-format/ClangFormat.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/Version.h" #include "clang/Format/Format.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" @@ -104,6 +105,13 @@ "SortIncludes style flag"), cl::cat(ClangFormatCategory)); +static cl::opt QualifierAlignment( + "qualifier-aligment", + cl::desc( + "If set, overrides the cvqualifier style behavior determined by the " + "QualifierAlignment style flag"), + cl::init(""), cl::cat(ClangFormatCategory)); + static cl::opt Verbose("verbose", cl::desc("If set, shows the list of processed files"), cl::cat(ClangFormatCategory)); @@ -402,6 +410,14 @@ return true; } + StringRef ConstAlignment = QualifierAlignment; + + FormatStyle->QualifierAlignment = + StringSwitch(ConstAlignment.lower()) + .Case("right", FormatStyle::QAS_Right) + .Case("left", FormatStyle::QAS_Left) + .Default(FormatStyle->QualifierAlignment); + if (SortIncludes.getNumOccurrences() != 0) { if (SortIncludes) FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive; Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -18180,6 +18180,10 @@ EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" +#define FAIL_PARSE(TEXT, FIELD, VALUE) \ + EXPECT_NE(0, parseConfiguration(TEXT, &Style).value()); \ + EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" + TEST_F(FormatTest, ParsesConfigurationBools) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_Cpp; @@ -18277,6 +18281,26 @@ CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u); CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$"); + Style.QualifierAlignment = FormatStyle::QAS_Right; + CHECK_PARSE("QualifierAlignment: Leave", QualifierAlignment, + FormatStyle::QAS_Leave); + CHECK_PARSE("QualifierAlignment: Right", QualifierAlignment, + FormatStyle::QAS_Right); + CHECK_PARSE("QualifierAlignment: Left", QualifierAlignment, + FormatStyle::QAS_Left); + CHECK_PARSE("QualifierAlignment: Custom", QualifierAlignment, + FormatStyle::QAS_Custom); + + Style.QualifierOrder.clear(); + CHECK_PARSE("QualifierOrder: [ const, volatile, type ]", QualifierOrder, + std::vector({"const", "volatile", "type"})); + Style.QualifierOrder.clear(); + CHECK_PARSE("QualifierOrder: [const, type]", QualifierOrder, + std::vector({"const", "type"})); + Style.QualifierOrder.clear(); + CHECK_PARSE("QualifierOrder: [volatile, type]", QualifierOrder, + std::vector({"volatile", "type"})); + Style.AlignConsecutiveAssignments = FormatStyle::ACS_Consecutive; CHECK_PARSE("AlignConsecutiveAssignments: None", AlignConsecutiveAssignments, FormatStyle::ACS_None); @@ -18918,8 +18942,6 @@ EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language); } -#undef CHECK_PARSE - TEST_F(FormatTest, UsesLanguageForBasedOnStyle) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_JavaScript; @@ -22220,6 +22242,494 @@ "}"; EXPECT_EQ(Code, format(Code, Style)); } + +TEST_F(FormatTest, LeftRightQualifier) { + FormatStyle Style = getLLVMStyle(); + + // keep the const style unaltered + verifyFormat("const int a;", Style); + verifyFormat("const int *a;", Style); + verifyFormat("const int &a;", Style); + verifyFormat("const int &&a;", Style); + verifyFormat("int const b;", Style); + verifyFormat("int const *b;", Style); + verifyFormat("int const &b;", Style); + verifyFormat("int const &&b;", Style); + verifyFormat("int const *b const;", Style); + verifyFormat("int *const c;", Style); + + verifyFormat("const Foo a;", Style); + verifyFormat("const Foo *a;", Style); + verifyFormat("const Foo &a;", Style); + verifyFormat("const Foo &&a;", Style); + verifyFormat("Foo const b;", Style); + verifyFormat("Foo const *b;", Style); + verifyFormat("Foo const &b;", Style); + verifyFormat("Foo const &&b;", Style); + verifyFormat("Foo const *b const;", Style); + + verifyFormat("LLVM_NODISCARD const int &Foo();", Style); + verifyFormat("LLVM_NODISCARD int const &Foo();", Style); + + verifyFormat("volatile const int *restrict;", Style); + verifyFormat("const volatile int *restrict;", Style); + verifyFormat("const int volatile *restrict;", Style); +} + +TEST_F(FormatTest, RightQualifier) { + FormatStyle Style = getLLVMStyle(); + Style.QualifierAlignment = FormatStyle::QAS_Right; + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("type"); + Style.QualifierOrder.push_back("const"); + Style.QualifierOrder.push_back("volatile"); + + verifyFormat("int const a;", Style); + verifyFormat("int const *a;", Style); + verifyFormat("int const &a;", Style); + verifyFormat("int const &&a;", Style); + verifyFormat("int const b;", Style); + verifyFormat("int const *b;", Style); + verifyFormat("int const &b;", Style); + verifyFormat("int const &&b;", Style); + verifyFormat("int const *b const;", Style); + verifyFormat("int *const c;", Style); + + verifyFormat("Foo const a;", Style); + verifyFormat("Foo const *a;", Style); + verifyFormat("Foo const &a;", Style); + verifyFormat("Foo const &&a;", Style); + verifyFormat("Foo const b;", Style); + verifyFormat("Foo const *b;", Style); + verifyFormat("Foo const &b;", Style); + verifyFormat("Foo const &&b;", Style); + verifyFormat("Foo const *b const;", Style); + verifyFormat("Foo *const b;", Style); + verifyFormat("Foo const *const b;", Style); + verifyFormat("auto const v = get_value();", Style); + verifyFormat("long long const &a;", Style); + verifyFormat("unsigned char const *a;", Style); + verifyFormat("int main(int const argc, char const *const *const argv)", + Style); + + verifyFormat("LLVM_NODISCARD int const &Foo();", Style); + verifyFormat("SourceRange getSourceRange() const override LLVM_READONLY", + Style); + verifyFormat("void foo() const override;", Style); + verifyFormat("void foo() const override LLVM_READONLY;", Style); + verifyFormat("void foo() const final;", Style); + verifyFormat("void foo() const final LLVM_READONLY;", Style); + verifyFormat("void foo() const LLVM_READONLY;", Style); + + verifyFormat( + "template explicit Action(Action const &action);", + Style); + verifyFormat( + "template explicit Action(Action const &action);", + "template explicit Action(const Action& action);", + Style); + verifyFormat( + "template explicit Action(Action const &action);", + "template \nexplicit Action(const Action& action);", + Style); + + verifyFormat("int const a;", "const int a;", Style); + verifyFormat("int const *a;", "const int *a;", Style); + verifyFormat("int const &a;", "const int &a;", Style); + verifyFormat("foo(int const &a)", "foo(const int &a)", Style); + verifyFormat("unsigned char *a;", "unsigned char *a;", Style); + verifyFormat("unsigned char const *a;", "const unsigned char *a;", Style); + verifyFormat("vector args1", + "vector args1", Style); + verifyFormat("unsigned int const &get_nu() const", + "const unsigned int &get_nu() const", Style); + verifyFormat("Foo const &a", "const Foo &a", Style); + verifyFormat("Foo::iterator const &a", "const Foo::iterator &a", + Style); + + verifyFormat("Foo(int a, " + "unsigned b, // c-style args\n" + " Bar const &c);", + "Foo(int a, " + "unsigned b, // c-style args\n" + " const Bar &c);", + Style); + + verifyFormat("int const volatile;", "volatile const int;", Style); + verifyFormat("int const volatile;", "const volatile int;", Style); + verifyFormat("int const volatile;", "const int volatile;", Style); + verifyFormat("int const volatile *restrict;", "volatile const int *restrict;", + Style); + verifyFormat("int const volatile *restrict;", "const volatile int *restrict;", + Style); + verifyFormat("int const volatile *restrict;", "const int volatile *restrict;", + Style); + + verifyFormat("static int const bat;", "static const int bat;", Style); + verifyFormat("static int const bat;", "static int const bat;", Style); + + verifyFormat("int const Foo::bat = 0;", "const int Foo::bat = 0;", + Style); + verifyFormat("int const Foo::bat = 0;", "int const Foo::bat = 0;", + Style); + verifyFormat("void fn(Foo const &i);", "void fn(const Foo &i);", Style); + verifyFormat("int const Foo::fn() {", "int const Foo::fn() {", + Style); + verifyFormat("Foo> const *p;", "const Foo> *p;", Style); + verifyFormat( + "Foo> const *p = const_cast> const *>(&ffi);", + "const Foo> *p = const_cast> *>(&ffi);", + Style); + + verifyFormat("void fn(Foo const &i);", "void fn(const Foo &i);", Style); + verifyFormat("void fns(ns::S const &s);", "void fns(const ns::S &s);", Style); + verifyFormat("void fn(ns::Foo const &i);", "void fn(const ns::Foo &i);", + Style); + verifyFormat("void fns(ns::ns2::S const &s);", + "void fns(const ns::ns2::S &s);", Style); + verifyFormat("void fn(ns::Foo> const &i);", + "void fn(const ns::Foo> &i);", Style); + verifyFormat("void fn(ns::ns2::Foo> const &i);", + "void fn(const ns::ns2::Foo> &i);", Style); + verifyFormat("void fn(ns::ns2::Foo> const &i);", + "void fn(const ns::ns2::Foo> &i);", Style); + + verifyFormat("LocalScope const *Scope = nullptr;", + "const LocalScope* Scope = nullptr;", Style); + verifyFormat("struct DOTGraphTraits", + "struct DOTGraphTraits", Style); + + verifyFormat( + "bool tools::addXRayRuntime(ToolChain const &TC, ArgList const &Args) {", + "bool tools::addXRayRuntime(const ToolChain&TC, const ArgList &Args) {", + Style); + verifyFormat("Foo const> P;", "Foo> P;", Style); + verifyFormat("Foo const> P;\n", "Foo> P;\n", Style); + verifyFormat("Foo const> P;\n#if 0\n#else\n#endif", + "Foo> P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("auto const i = 0;", "const auto i = 0;", Style); + verifyFormat("auto const &ir = i;", "const auto &ir = i;", Style); + verifyFormat("auto const *ip = &i;", "const auto *ip = &i;", Style); + + verifyFormat("Foo const> P;\n#if 0\n#else\n#endif", + "Foo> P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("Bar const> P;\n#if 0\n#else\n#endif", + "Bar const> P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("Baz const> P;\n#if 0\n#else\n#endif", + "Baz> P;\n#if 0\n#else\n#endif", Style); + + // verifyFormat("#if 0\nBoo const> P;\n#else\n#endif", + // "#if 0\nBoo> P;\n#else\n#endif", Style); + + verifyFormat("int const P;\n#if 0\n#else\n#endif", + "const int P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("unsigned long const a;", "const unsigned long a;", Style); + verifyFormat("unsigned long long const a;", "const unsigned long long a;", + Style); + + // don't adjust macros + verifyFormat("const INTPTR a;", "const INTPTR a;", Style); +} + +TEST_F(FormatTest, LeftQualifier) { + FormatStyle Style = getLLVMStyle(); + Style.QualifierAlignment = FormatStyle::QAS_Left; + + verifyFormat("const int a;", Style); + verifyFormat("const int *a;", Style); + verifyFormat("const int &a;", Style); + verifyFormat("const int &&a;", Style); + verifyFormat("const int b;", Style); + verifyFormat("const int *b;", Style); + verifyFormat("const int &b;", Style); + verifyFormat("const int &&b;", Style); + verifyFormat("const int *b const;", Style); + verifyFormat("int *const c;", Style); + + verifyFormat("const Foo a;", Style); + verifyFormat("const Foo *a;", Style); + verifyFormat("const Foo &a;", Style); + verifyFormat("const Foo &&a;", Style); + verifyFormat("const Foo b;", Style); + verifyFormat("const Foo *b;", Style); + verifyFormat("const Foo &b;", Style); + verifyFormat("const Foo &&b;", Style); + verifyFormat("const Foo *b const;", Style); + verifyFormat("Foo *const b;", Style); + verifyFormat("const Foo *const b;", Style); + + verifyFormat("LLVM_NODISCARD const int &Foo();", Style); + + verifyFormat("const char a[];", Style); + verifyFormat("const auto v = get_value();", Style); + verifyFormat("const long long &a;", Style); + verifyFormat("const unsigned char *a;", Style); + verifyFormat("const unsigned char *a;", "unsigned char const *a;", Style); + verifyFormat("const Foo &a", "Foo const &a", Style); + verifyFormat("const Foo::iterator &a", "Foo::iterator const &a", + Style); + + verifyFormat("const int a;", "int const a;", Style); + verifyFormat("const int *a;", "int const *a;", Style); + verifyFormat("const int &a;", "int const &a;", Style); + verifyFormat("foo(const int &a)", "foo(int const &a)", Style); + verifyFormat("unsigned char *a;", "unsigned char *a;", Style); + verifyFormat("const unsigned int &get_nu() const", + "unsigned int const &get_nu() const", Style); + + verifyFormat("const volatile int;", "volatile const int;", Style); + verifyFormat("const volatile int;", "const volatile int;", Style); + verifyFormat("const volatile int;", "const int volatile;", Style); + + verifyFormat("const volatile int *restrict;", "volatile const int *restrict;", + Style); + verifyFormat("const volatile int *restrict;", "const volatile int *restrict;", + Style); + verifyFormat("const volatile int *restrict;", "const int volatile *restrict;", + Style); + + verifyFormat("SourceRange getSourceRange() const override LLVM_READONLY;", + Style); + + verifyFormat("void foo() const override;", Style); + verifyFormat("void foo() const override LLVM_READONLY;", Style); + verifyFormat("void foo() const final;", Style); + verifyFormat("void foo() const final LLVM_READONLY;", Style); + verifyFormat("void foo() const LLVM_READONLY;", Style); + + verifyFormat( + "template explicit Action(const Action &action);", + Style); + verifyFormat( + "template explicit Action(const Action &action);", + "template explicit Action(Action const &action);", + Style); + + verifyFormat("static const int bat;", "static const int bat;", Style); + verifyFormat("static const int bat;", "static int const bat;", Style); + + verifyFormat("static const int Foo::bat = 0;", + "static const int Foo::bat = 0;", Style); + verifyFormat("static const int Foo::bat = 0;", + "static int const Foo::bat = 0;", Style); + + verifyFormat("void fn(const Foo &i);"); + + verifyFormat("const int Foo::bat = 0;", "const int Foo::bat = 0;", + Style); + verifyFormat("const int Foo::bat = 0;", "int const Foo::bat = 0;", + Style); + verifyFormat("void fn(const Foo &i);", "void fn( Foo const &i);", + Style); + verifyFormat("const int Foo::fn() {", "int const Foo::fn() {", + Style); + verifyFormat("const Foo> *p;", "Foo> const *p;", Style); + verifyFormat( + "const Foo> *p = const_cast> *>(&ffi);", + "const Foo> *p = const_cast> const *>(&ffi);", + Style); + + verifyFormat("void fn(const Foo &i);", "void fn(Foo const &i);", Style); + verifyFormat("void fns(const ns::S &s);", "void fns(ns::S const &s);", Style); + verifyFormat("void fn(const ns::Foo &i);", "void fn(ns::Foo const &i);", + Style); + verifyFormat("void fns(const ns::ns2::S &s);", + "void fns(ns::ns2::S const &s);", Style); + verifyFormat("void fn(const ns::Foo> &i);", + "void fn(ns::Foo> const &i);", Style); + verifyFormat("void fn(const ns::ns2::Foo> &i);", + "void fn(ns::ns2::Foo> const &i);", Style); + verifyFormat("void fn(const ns::ns2::Foo> &i);", + "void fn(ns::ns2::Foo> const &i);", Style); + + verifyFormat("const auto i = 0;", "auto const i = 0;", Style); + verifyFormat("const auto &ir = i;", "auto const &ir = i;", Style); + verifyFormat("const auto *ip = &i;", "auto const *ip = &i;", Style); + + verifyFormat("Foo> P;\n#if 0\n#else\n#endif", + "Foo const> P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("Foo> P;\n#if 0\n#else\n#endif", + "Foo> P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("const int P;\n#if 0\n#else\n#endif", + "int const P;\n#if 0\n#else\n#endif", Style); + + verifyFormat("const unsigned long a;", "unsigned long const a;", Style); + verifyFormat("const unsigned long long a;", "unsigned long long const a;", + Style); + + verifyFormat("const long long unsigned a;", "long const long unsigned a;", + Style); + + verifyFormat("const std::Foo", "const std::Foo", Style); + verifyFormat("const std::Foo<>", "const std::Foo<>", Style); + verifyFormat("const std::Foo < int", "const std::Foo", "const std::Foo", Style); + + // don't adjust macros + verifyFormat("INTPTR const a;", "INTPTR const a;", Style); +} + +TEST_F(FormatTest, ConstVolatileQualifiersOrder) { + FormatStyle Style = getLLVMStyle(); + Style.QualifierAlignment = FormatStyle::QAS_Left; + + // the Default + EXPECT_EQ(Style.QualifierOrder.size(), 5); + EXPECT_EQ(Style.QualifierOrder[0], "inline"); + EXPECT_EQ(Style.QualifierOrder[1], "static"); + EXPECT_EQ(Style.QualifierOrder[2], "const"); + EXPECT_EQ(Style.QualifierOrder[3], "volatile"); + EXPECT_EQ(Style.QualifierOrder[4], "type"); + + verifyFormat("const volatile int a;", "const volatile int a;", Style); + verifyFormat("const volatile int a;", "volatile const int a;", Style); + verifyFormat("const volatile int a;", "int const volatile a;", Style); + verifyFormat("const volatile int a;", "int volatile const a;", Style); + verifyFormat("const volatile int a;", "const int volatile a;", Style); + + Style.QualifierAlignment = FormatStyle::QAS_Right; + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("type"); + Style.QualifierOrder.push_back("const"); + Style.QualifierOrder.push_back("volatile"); + + verifyFormat("int const volatile a;", "const volatile int a;", Style); + verifyFormat("int const volatile a;", "volatile const int a;", Style); + verifyFormat("int const volatile a;", "int const volatile a;", Style); + verifyFormat("int const volatile a;", "int volatile const a;", Style); + verifyFormat("int const volatile a;", "const int volatile a;", Style); + + Style.QualifierAlignment = FormatStyle::QAS_Left; + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("volatile"); + Style.QualifierOrder.push_back("const"); + Style.QualifierOrder.push_back("type"); + + verifyFormat("volatile const int a;", "const volatile int a;", Style); + verifyFormat("volatile const int a;", "volatile const int a;", Style); + verifyFormat("volatile const int a;", "int const volatile a;", Style); + verifyFormat("volatile const int a;", "int volatile const a;", Style); + verifyFormat("volatile const int a;", "const int volatile a;", Style); + + Style.QualifierAlignment = FormatStyle::QAS_Right; + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("type"); + Style.QualifierOrder.push_back("volatile"); + Style.QualifierOrder.push_back("const"); + + verifyFormat("int volatile const a;", "const volatile int a;", Style); + verifyFormat("int volatile const a;", "volatile const int a;", Style); + verifyFormat("int volatile const a;", "int const volatile a;", Style); + verifyFormat("int volatile const a;", "int volatile const a;", Style); + verifyFormat("int volatile const a;", "const int volatile a;", Style); + + Style.QualifierAlignment = FormatStyle::QAS_Custom; + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("type"); + Style.QualifierOrder.push_back("volatile"); + Style.QualifierOrder.push_back("const"); + + verifyFormat("int volatile const a;", "const volatile int a;", Style); + verifyFormat("int volatile const a;", "volatile const int a;", Style); + verifyFormat("int volatile const a;", "int const volatile a;", Style); + verifyFormat("int volatile const a;", "int volatile const a;", Style); + verifyFormat("int volatile const a;", "const int volatile a;", Style); +} + +TEST_F(FormatTest, FailQualifierInvalidConfiguration) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("QualifierAlignment: Custom\n" + "QualifierOrder: [const, volatile, apples, type]", + QualifierOrder, + std::vector({"const", "volatile", "apples", "type"})); +} + +TEST_F(FormatTest, FailQualifierDuplicateConfiguration) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("QualifierAlignment: Custom\n" + "QualifierOrder: [const, volatile, const, type]", + QualifierOrder, + std::vector({"const", "volatile", "const", "type"})); +} + +TEST_F(FormatTest, FailQualifierMissingType) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("QualifierAlignment: Custom\n" + "QualifierOrder: [const, volatile ]", + QualifierOrder, + std::vector({ + "const", + "volatile", + })); +} + +TEST_F(FormatTest, FailQualifierEmptyOrder) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("QualifierAlignment: Custom\nQualifierOrder: []", QualifierOrder, + std::vector({})); +} + +TEST_F(FormatTest, FailQualifierMissingOrder) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("QualifierAlignment: Custom", QualifierOrder, + std::vector()); +} + +TEST_F(FormatTest, QualifierLeft) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + CHECK_PARSE("QualifierAlignment: Left", QualifierOrder, + std::vector({"const", "volatile", "type"})); +} + +TEST_F(FormatTest, QualifierRight) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + CHECK_PARSE("QualifierAlignment: Right", QualifierOrder, + std::vector({"type", "const", "volatile"})); +} + +TEST_F(FormatTest, QualifiersCustomOrder) { + FormatStyle Style = getLLVMStyle(); + Style.QualifierAlignment = FormatStyle::QAS_Left; + Style.QualifierOrder.clear(); + Style.QualifierOrder.push_back("inline"); + Style.QualifierOrder.push_back("constexpr"); + Style.QualifierOrder.push_back("static"); + Style.QualifierOrder.push_back("const"); + Style.QualifierOrder.push_back("volatile"); + Style.QualifierOrder.push_back("type"); + + verifyFormat("const volatile int a;", "const volatile int a;", Style); + verifyFormat("const volatile int a;", "volatile const int a;", Style); + verifyFormat("const volatile int a;", "int const volatile a;", Style); + verifyFormat("const volatile int a;", "int volatile const a;", Style); + verifyFormat("const volatile int a;", "const int volatile a;", Style); + + verifyFormat("static const volatile int a;", "const static int volatile a;", + Style); + verifyFormat("inline static const volatile int a;", + "const static inline int volatile a;", Style); + + verifyFormat("constexpr static int a;", "static constexpr int a;", Style); + verifyFormat("constexpr static int A;", "static constexpr int A;", Style); + verifyFormat("constexpr static int Bar;", "static constexpr int Bar;", Style); + verifyFormat("constexpr static LPINT Bar;", "static constexpr LPINT Bar;", + Style); + verifyFormat("const const int a;", "const int const a;", Style); +} + } // namespace } // namespace format } // namespace clang