Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -2098,6 +2098,40 @@ const char* x = "veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString"; +**CVQualifierAlignment** (``CVQualifierAlignmentStyle``) + Different ways to arrange const/volatile qualifiers. + + Possible values: + + * ``CVQAS_Leave`` (in configuration: ``Leave``) + Don't change cvqualifier to either Left or Right alignment + + .. code-block:: c++ + + int const a; + const int *a; + + * ``CVQAS_Left`` (in configuration: ``Left``) + Change cvqualifiers to be Left aligned. + + .. code-block:: c++ + + const int a; + const int *a; + + * ``CVQAS_Right`` (in configuration: ``Right``) + Change cvqualifiers to be Right aligned. + + .. code-block:: c++ + + int const a; + int const *a; + + + +**CVQualifierOrder** (``std::vector``) + The Order in which the qualifiers appear. + **ColumnLimit** (``unsigned``) The column limit. Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -156,6 +156,12 @@ - Option ``AllowShortEnumsOnASingleLine: false`` has been improved, it now correctly places the opening brace according to ``BraceWrapping.AfterEnum``. +- Option ``CVQualifierAligment`` has been added in order to auto-arrange the positioning of cv qualifiers `const` and `volatile` + in variable and parameter declarations to be either ``Right`` aligned or ``Left`` aligned. + +- Option ``CVQualifierOrder`` has been added to allow the order of `const` and + `volatile` to be controlled relative to each other + libclang -------- Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -40,7 +40,8 @@ Success = 0, Error, Unsuitable, - BinPackTrailingCommaConflict + BinPackTrailingCommaConflict, + InvalidCVQualifierOrder }; class ParseErrorCategory final : public std::error_category { public: @@ -1831,6 +1832,34 @@ /// \endcode std::string CommentPragmas; + /// Different const/volatile qualifier alignment styles. + enum CVQualifierAlignmentStyle { + /// Don't change cvqualifier to either Left or Right alignment + /// \code + /// int const a; + /// const int *a; + /// \endcode + CVQAS_Leave, + /// Change cvqualifiers to be Left aligned. + /// \code + /// const int a; + /// const int *a; + /// \endcode + CVQAS_Left, + /// Change cvqualifiers to be Right aligned. + /// \code + /// int const a; + /// int const *a; + /// \endcode + CVQAS_Right + }; + + /// Different ways to arrange const/volatile qualifiers. + CVQualifierAlignmentStyle CVQualifierAlignment; + + /// The Order in which the qualifiers appear. + std::vector CVQualifierOrder; + /// Different ways to break inheritance list. enum BreakInheritanceListStyle : unsigned char { /// Break inheritance list before the colon and after the commas. @@ -3422,6 +3451,8 @@ BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakStringLiterals == R.BreakStringLiterals && ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas && + CVQualifierAlignment == R.CVQualifierAlignment && + CVQualifierOrder == R.CVQualifierOrder && 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 + LeftRightConstFixer.cpp WhitespaceManager.cpp LINK_LIBS Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -18,6 +18,7 @@ #include "ContinuationIndenter.h" #include "FormatInternal.h" #include "FormatTokenLexer.h" +#include "LeftRightConstFixer.h" #include "NamespaceEndCommentsFixer.h" #include "SortJavaScriptImports.h" #include "TokenAnalyzer.h" @@ -126,6 +127,16 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::CVQualifierAlignmentStyle &Value) { + IO.enumCase(Value, "Leave", FormatStyle::CVQAS_Leave); + IO.enumCase(Value, "Left", FormatStyle::CVQAS_Left); + IO.enumCase(Value, "Right", FormatStyle::CVQAS_Right); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::ShortFunctionStyle &Value) { IO.enumCase(Value, "None", FormatStyle::SFS_None); @@ -632,6 +643,8 @@ IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("CVQualifierAlignment", Style.CVQualifierAlignment); + IO.mapOptional("CVQualifierOrder", Style.CVQualifierOrder); IO.mapOptional("CompactNamespaces", Style.CompactNamespaces); IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine", Style.ConstructorInitializerAllOnOneLineOrOnePerLine); @@ -863,6 +876,8 @@ return "Unsuitable"; case ParseError::BinPackTrailingCommaConflict: return "trailing comma insertion cannot be used with bin packing"; + case ParseError::InvalidCVQualifierOrder: + return "Invalid CVQualifierOrder"; } llvm_unreachable("unexpected parse error"); } @@ -1033,6 +1048,7 @@ LLVMStyle.BreakStringLiterals = true; LLVMStyle.ColumnLimit = 80; LLVMStyle.CommentPragmas = "^ IWYU pragma:"; + LLVMStyle.CVQualifierAlignment = FormatStyle::CVQAS_Leave; LLVMStyle.CompactNamespaces = false; LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; LLVMStyle.ConstructorInitializerIndentWidth = 4; @@ -1129,6 +1145,8 @@ LLVMStyle.WhitespaceSensitiveMacros.push_back("BOOST_PP_STRINGIZE"); LLVMStyle.WhitespaceSensitiveMacros.push_back("NS_SWIFT_NAME"); LLVMStyle.WhitespaceSensitiveMacros.push_back("CF_SWIFT_NAME"); + LLVMStyle.CVQualifierOrder.push_back("const"); + LLVMStyle.CVQualifierOrder.push_back("volatile"); // Defaults that differ when not C++. if (Language == FormatStyle::LK_TableGen) { @@ -1471,6 +1489,23 @@ return true; } +bool isValidCVQualifierOrder(FormatStyle *Style) { + // If its empty then it means don't do anything. + if (Style->CVQualifierOrder.empty()) { + return true; + } + // Ensure the list contains only currently valid qualifiers. + for (const auto &CVQualifier : Style->CVQualifierOrder) { + auto token = LeftRightConstFixer::getTokenFromCVQualifier(CVQualifier); + if (token == tok::identifier) + return false; + } + // Ensure the list is unqiue (no duplicates). + std::set UniqueQualifiers(Style->CVQualifierOrder.begin(), + Style->CVQualifierOrder.end()); + return Style->CVQualifierOrder.size() == UniqueQualifiers.size(); +} + std::error_code parseConfiguration(llvm::MemoryBufferRef Config, FormatStyle *Style, bool AllowUnknownOptions, llvm::SourceMgr::DiagHandlerTy DiagHandler, @@ -1532,6 +1567,9 @@ // See comment on FormatStyle::TSC_Wrapped. return make_error_code(ParseError::BinPackTrailingCommaConflict); } + if (!Style->CVQualifierOrder.empty() && !isValidCVQualifierOrder(Style)) { + return make_error_code(ParseError::InvalidCVQualifierOrder); + } return make_error_code(ParseError::Success); } @@ -2865,6 +2903,24 @@ }); } + if (Style.isCpp() && Style.CVQualifierAlignment != FormatStyle::CVQAS_Leave) { + // Depending on the placement style of left or right you need + // To iterate forward or backward though the order list as qualifier + // can push though each other. + // Copy the very small list and reverse the order if left aligned. + auto Order = Style.CVQualifierOrder; + if (Style.CVQualifierAlignment == FormatStyle::CVQAS_Left) + std::reverse(Order.begin(), Order.end()); + + // Need to pass the literal into the constructor or the string + // will be out of scope at construction. + for (const auto &CVQualifier : Order) { + Passes.emplace_back([&, CVQualifier](const Environment &Env) { + return LeftRightConstFixer(Env, Expanded, CVQualifier).process(); + }); + } + } + if (Style.Language == FormatStyle::LK_JavaScript && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave) Passes.emplace_back([&](const Environment &Env) { Index: clang/lib/Format/LeftRightConstFixer.h =================================================================== --- /dev/null +++ clang/lib/Format/LeftRightConstFixer.h @@ -0,0 +1,42 @@ +//===--- LeftRightConstFixer.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 LeftRightConstFixer, a TokenAnalyzer that +/// enforces either east or west const depending on the style. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_FORMAT_EASTWESTCONSTFIXER_H +#define LLVM_CLANG_LIB_FORMAT_EASTWESTCONSTFIXER_H + +#include "TokenAnalyzer.h" + +namespace clang { +namespace format { + +class LeftRightConstFixer : public TokenAnalyzer { + std::string CVQualifier; + +public: + LeftRightConstFixer(const Environment &Env, const FormatStyle &Style, + const std::string &CVQualifier); + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override; + + static tok::TokenKind + LeftRightConstFixer::getTokenFromCVQualifier(const std::string &CVQualifier); +}; + +} // end namespace format +} // end namespace clang + +#endif Index: clang/lib/Format/LeftRightConstFixer.cpp =================================================================== --- /dev/null +++ clang/lib/Format/LeftRightConstFixer.cpp @@ -0,0 +1,348 @@ +//===--- LeftRightConstFixer.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 LeftRightConstFixer, a TokenAnalyzer that +/// enforces either east or west const depending on the style. +/// +//===----------------------------------------------------------------------===// + +#include "LeftRightConstFixer.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 const : " + << 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 insertConstAfter(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *First, + const std::string &CVQualifier) { + FormatToken *Next = First->Next; + if (!Next) { + return; + } + auto Range = CharSourceRange::getCharRange( + Next->getStartOfNonWhitespace(), Next->Next->getStartOfNonWhitespace()); + + std::string NewText = " " + CVQualifier + " "; + NewText += Next->TokenText; + + replaceToken(SourceMgr, Fixes, Range, NewText); +} + +static void insertConstBefore(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *First, + const std::string &CVQualifier) { + auto Range = CharSourceRange::getCharRange( + First->getStartOfNonWhitespace(), First->Next->getStartOfNonWhitespace()); + + std::string NewText = " " + CVQualifier + " "; + 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 isCVQualifierOrType(const FormatToken *Tok) { + return Tok && (Tok->isSimpleTypeSpecifier() || + Tok->isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_auto)); +} + +// 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 &CVQualifier, + tok::TokenKind CVQualifierType) { + // We only need to think about streams that begin with const. + if (!Tok->is(CVQualifierType)) { + 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 *Qualifier = Tok->Next; + FormatToken *LastQualifier = Qualifier; + while (Qualifier && isCVQualifierOrType(Qualifier)) { + LastQualifier = Qualifier; + Qualifier = Qualifier->Next; + } + if (LastQualifier && Qualifier != LastQualifier) { + rotateTokens(SourceMgr, Fixes, Const, LastQualifier, /*Left=*/false); + Tok = LastQualifier; + } else if (Tok->startsSequence(CVQualifierType, 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. + insertConstAfter(SourceMgr, Fixes, EndTemplate, CVQualifier); + removeToken(SourceMgr, Fixes, Tok); + return Tok; + } + } else if (Tok->startsSequence(CVQualifierType, 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; + } + 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(CVQualifierType)) { + insertConstAfter(SourceMgr, Fixes, Next->Previous, CVQualifier); + 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 &CVQualifier, + tok::TokenKind CVQualifierType) { + // if Tok is an identifier and possibly a macro then don't convert + if (isPossibleMacro(Tok)) { + return Tok; + } + + FormatToken *Qualifier = Tok; + FormatToken *LastQualifier = Qualifier; + while (Qualifier && isCVQualifierOrType(Qualifier)) { + LastQualifier = Qualifier; + Qualifier = Qualifier->Next; + if (Qualifier && Qualifier->is(CVQualifierType)) { + break; + } + } + if (LastQualifier && Qualifier != LastQualifier && + Qualifier->is(CVQualifierType)) { + rotateTokens(SourceMgr, Fixes, Tok, Qualifier, /*Left=*/true); + Tok = Qualifier->Next; + } else if (Tok->startsSequence(tok::identifier, CVQualifierType)) { + 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(CVQualifierType)) { + 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; + 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(CVQualifierType)) { + // Remove the const. + removeToken(SourceMgr, Fixes, Next); + insertConstBefore(SourceMgr, Fixes, Tok, CVQualifier); + return Next; + } + } + if (Next && Next->Next && + Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) { + if (Next->is(CVQualifierType)) { + // Remove the const. + removeToken(SourceMgr, Fixes, Next); + insertConstBefore(SourceMgr, Fixes, Tok, CVQualifier); + return Next; + } + } + } + return Tok; +} + +tok::TokenKind +LeftRightConstFixer::getTokenFromCVQualifier(const std::string &CVQualifier) { + return llvm::StringSwitch(CVQualifier) + .Case("const", tok::kw_const) + .Case("volatile", tok::kw_volatile) + // FIXME add support for aligning other qualifiers +#if 0 + .Case("static", tok::kw_static) + .Case("inline", tok::kw_inline) + .Case("constexpr", tok::kw_constexpr) + .Case("restrict", tok::kw_restrict) + .Case("", tok::kw_typeof) +#endif + .Default(tok::identifier); +} + +LeftRightConstFixer::LeftRightConstFixer(const Environment &Env, + const FormatStyle &Style, + const std::string &CVQualifier) + : TokenAnalyzer(Env, Style), CVQualifier(CVQualifier) {} + +std::pair +LeftRightConstFixer::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 CVQualifierToken = getTokenFromCVQualifier(CVQualifier); + assert(CVQualifierToken != tok::identifier && "Unrecognised CVQualifier"); + + 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 (Style.CVQualifierAlignment == FormatStyle::CVQAS_Right) { + Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, CVQualifier, + CVQualifierToken); + } else if (Style.CVQualifierAlignment == FormatStyle::CVQAS_Left) { + Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, CVQualifier, + CVQualifierToken); + } + } + } + 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 CVQualifierAlignment( + "cvqualifier-aligment", + cl::desc( + "If set, overrides the cvqualifier style behavior determined by the " + "CVQualifierAlignment 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,15 @@ return true; } + StringRef ConstAlignment = CVQualifierAlignment; + + FormatStyle->CVQualifierAlignment = + StringSwitch( + ConstAlignment.lower()) + .Case("right", FormatStyle::CVQAS_Right) + .Case("left", FormatStyle::CVQAS_Left) + .Default(FormatStyle->CVQualifierAlignment); + 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,11 @@ EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" +#define FAIL_PARSE(TEXT, FIELD, VALUE) \ + EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!"; \ + 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 +18282,24 @@ CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u); CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$"); + Style.CVQualifierAlignment = FormatStyle::CVQAS_Right; + CHECK_PARSE("CVQualifierAlignment: Leave", CVQualifierAlignment, + FormatStyle::CVQAS_Leave); + CHECK_PARSE("CVQualifierAlignment: Right", CVQualifierAlignment, + FormatStyle::CVQAS_Right); + CHECK_PARSE("CVQualifierAlignment: Left", CVQualifierAlignment, + FormatStyle::CVQAS_Left); + + CHECK_PARSE("CVQualifierOrder: [ const, volatile]", CVQualifierOrder, + std::vector({"const", "volatile"})); + Style.CVQualifierOrder.clear(); + CHECK_PARSE("CVQualifierOrder: [const]", CVQualifierOrder, + std::vector{"const"}); + CHECK_PARSE("CVQualifierOrder: [volatile]", CVQualifierOrder, + std::vector{"volatile"}); + CHECK_PARSE("CVQualifierOrder: [const, volatile]", CVQualifierOrder, + std::vector({"const", "volatile"})); + Style.AlignConsecutiveAssignments = FormatStyle::ACS_Consecutive; CHECK_PARSE("AlignConsecutiveAssignments: None", AlignConsecutiveAssignments, FormatStyle::ACS_None); @@ -22220,6 +22243,387 @@ "}"; EXPECT_EQ(Code, format(Code, Style)); } + +TEST_F(FormatTest, LeftRightConst) { + 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, RightConst) { + FormatStyle Style = getLLVMStyle(); + Style.CVQualifierAlignment = FormatStyle::CVQAS_Right; + + 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, LeftConst) { + FormatStyle Style = getLLVMStyle(); + Style.CVQualifierAlignment = FormatStyle::CVQAS_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); + + // don't adjust macros + verifyFormat("INTPTR const a;", "INTPTR const a;", Style); +} + +TEST_F(FormatTest, ConstVolatileCVQualifiersOrder) { + FormatStyle Style = getLLVMStyle(); + Style.CVQualifierAlignment = FormatStyle::CVQAS_Left; + + // the Default + EXPECT_EQ(Style.CVQualifierOrder.size(), 2); + EXPECT_EQ(Style.CVQualifierOrder[0], "const"); + EXPECT_EQ(Style.CVQualifierOrder[1], "volatile"); + + 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.CVQualifierAlignment = FormatStyle::CVQAS_Right; + + 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.CVQualifierAlignment = FormatStyle::CVQAS_Left; + Style.CVQualifierOrder.clear(); + Style.CVQualifierOrder.push_back("volatile"); + Style.CVQualifierOrder.push_back("const"); + + 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.CVQualifierAlignment = FormatStyle::CVQAS_Right; + + 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, FailCVInvalidConfiguration) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("CVQualifierOrder: [const, volatile, apples]", CVQualifierOrder, + std::vector({"const", "volatile", "apples"})); +} + +TEST_F(FormatTest, FailCVDuplicateConfiguration) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + FAIL_PARSE("CVQualifierOrder: [const, volatile, const]", CVQualifierOrder, + std::vector({"const", "volatile", "const"})); +} + } // namespace } // namespace format } // namespace clang