[clang-format] Add Left/Right Const fixer capability
authormydeveloperday <mydeveloperday@gmail.com>
Thu, 23 Sep 2021 19:00:33 +0000 (20:00 +0100)
committermydeveloperday <mydeveloperday@gmail.com>
Thu, 23 Sep 2021 19:00:33 +0000 (20:00 +0100)
Developers these days seem to argue over east vs west const like they used to argue over tabs vs whitespace or the various bracing style. These previous arguments were mainly eliminated with tools like `clang-format` that allowed those rules to become part of your style guide. Anyone who has been using clang-format in a large team over the last couple of years knows that we don't have those religious arguments any more, and code reviews are more productive.

https://www.youtube.com/watch?v=fv--IKZFVO8
https://mariusbancila.ro/blog/2018/11/23/join-the-east-const-revolution/
https://www.youtube.com/watch?v=z6s6bacI424

The purpose of this revision is to try to do the same for the East/West const discussion. Move the debate into the style guide and leave it there!

In addition to the new `ConstStyle: Right` or `ConstStyle: Left` there is an additional command-line argument `--const-style=left/right` which would allow an individual developer to switch the source back and forth to their own style for editing, and back to the committed style before commit. (you could imagine an IDE might offer such a switch)

The revision works by implementing a separate pass of the Annotated lines much like the SortIncludes and then create replacements for constant type declarations.

Differential Revision: https://reviews.llvm.org/D69764

12 files changed:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/docs/tools/dump_format_style.py
clang/include/clang/Format/Format.h
clang/lib/Format/CMakeLists.txt
clang/lib/Format/Format.cpp
clang/lib/Format/QualifierAlignmentFixer.cpp [new file with mode: 0644]
clang/lib/Format/QualifierAlignmentFixer.h [new file with mode: 0644]
clang/tools/clang-format/ClangFormat.cpp
clang/unittests/Format/CMakeLists.txt
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/QualifierFixerTest.cpp [new file with mode: 0755]

index 677d498..77dfd64 100644 (file)
@@ -3225,6 +3225,78 @@ the configuration (without a prefix: ``Auto``).
 
 
 
+**QualifierAlignment** (``QualifierAlignmentStyle``)
+  Different ways to arrange const/volatile qualifiers.
+
+  .. warning:: 
+
+   ``QualifierAlignment`` COULD lead to incorrect code generation.
+
+  Possible values:
+
+  * ``QAS_Leave`` (in configuration: ``Leave``)
+    Don't change specifiers/qualifier to either Left or Right alignment
+    (default)
+
+    .. 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' , '<type>', 'const']
+
+
+    .. code-block:: c++
+
+
+       int const a;
+       int const *a;
+
+
+
+**QualifierOrder** (``std::vector<std::string>``)
+  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<RawStringFormat>``)
   Defines hints for detecting supported languages code blocks in raw
   strings.
index 4c2bf04..7ad6804 100644 (file)
@@ -189,6 +189,16 @@ clang-format
 - 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 `type`.
+
 libclang
 --------
 
index fd58370..050cba2 100755 (executable)
@@ -117,6 +117,14 @@ def clean_comment_line(line):
   endcode_match = re.match(r'^/// +\\endcode$', line)
   if endcode_match:
     return ''
+
+  match = re.match(r'^/// \\warning$', line)
+  if match:
+    return '\n.. warning:: \n\n'
+
+  endwarning_match = re.match(r'^/// +\\endwarning$', line)
+  if endwarning_match:
+    return ''
   return line[4:] + '\n'
 
 def read_options(header):
index 7be21d1..cced2c0 100644 (file)
@@ -40,7 +40,11 @@ enum class ParseError {
   Success = 0,
   Error,
   Unsuitable,
-  BinPackTrailingCommaConflict
+  BinPackTrailingCommaConflict,
+  InvalidQualifierSpecified,
+  DuplicateQualifierSpecified,
+  MissingQualifierType,
+  MissingQualifierOrder
 };
 class ParseErrorCategory final : public std::error_category {
 public:
@@ -1819,6 +1823,67 @@ struct FormatStyle {
   /// \endcode
   std::string CommentPragmas;
 
+  /// Different const/volatile qualifier alignment styles.
+  enum QualifierAlignmentStyle {
+    /// Don't change specifiers/qualifier to either Left or Right alignment
+    /// (default)
+    /// \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' , '<type>', 'const']
+    /// \endcode
+    ///
+    /// \code
+    ///
+    ///    int const a;
+    ///    int const *a;
+    /// \endcode
+    QAS_Custom
+  };
+
+  /// Different ways to arrange const/volatile qualifiers.
+  /// \warning
+  ///  ``QualifierAlignment`` COULD lead to incorrect code generation.
+  /// \endwarning
+  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<std::string> QualifierOrder;
+
   /// Different ways to break inheritance list.
   enum BreakInheritanceListStyle : unsigned char {
     /// Break inheritance list before the colon and after the commas.
@@ -3496,6 +3561,8 @@ struct FormatStyle {
            PenaltyBreakTemplateDeclaration ==
                R.PenaltyBreakTemplateDeclaration &&
            PointerAlignment == R.PointerAlignment &&
+           QualifierAlignment == R.QualifierAlignment &&
+           QualifierOrder == R.QualifierOrder &&
            RawStringFormats == R.RawStringFormats &&
            ReferenceAlignment == R.ReferenceAlignment &&
            ShortNamespaceLines == R.ShortNamespaceLines &&
index ec1522d..4ff6a53 100644 (file)
@@ -9,6 +9,7 @@ add_clang_library(clangFormat
   FormatTokenLexer.cpp
   MacroExpander.cpp
   NamespaceEndCommentsFixer.cpp
+  QualifierAlignmentFixer.cpp
   SortJavaScriptImports.cpp
   TokenAnalyzer.cpp
   TokenAnnotator.cpp
index 580f32b..16f3906 100644 (file)
@@ -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<FormatStyle::ShortBlockStyle> {
   }
 };
 
+template <>
+struct ScalarEnumerationTraits<FormatStyle::QualifierAlignmentStyle> {
+  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<FormatStyle::ShortFunctionStyle> {
   static void enumeration(IO &IO, FormatStyle::ShortFunctionStyle &Value) {
     IO.enumCase(Value, "None", FormatStyle::SFS_None);
@@ -641,6 +652,17 @@ template <> struct MappingTraits<FormatStyle> {
     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 = {"type", "const", "volatile"};
+    } else if (Style.QualifierAlignment == FormatStyle::QAS_Left) {
+      Style.QualifierOrder = {"const", "volatile", "type"};
+    } else if (Style.QualifierAlignment == FormatStyle::QAS_Custom) {
+      IO.mapOptional("QualifierOrder", Style.QualifierOrder);
+    }
+
     IO.mapOptional("CompactNamespaces", Style.CompactNamespaces);
     IO.mapOptional("ConstructorInitializerIndentWidth",
                    Style.ConstructorInitializerIndentWidth);
@@ -905,6 +927,14 @@ std::string ParseErrorCategory::message(int EV) const {
     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");
 }
@@ -1078,6 +1108,10 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.ConstructorInitializerIndentWidth = 4;
   LLVMStyle.ContinuationIndentWidth = 4;
   LLVMStyle.Cpp11BracedListStyle = true;
+
+  // Off by default Qualifier ordering
+  LLVMStyle.QualifierAlignment = FormatStyle::QAS_Leave;
+
   LLVMStyle.DeriveLineEnding = true;
   LLVMStyle.DerivePointerAlignment = false;
   LLVMStyle.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never;
@@ -1512,6 +1546,37 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language,
   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 =
+        LeftRightQualifierAlignmentFixer::getTokenFromQualifier(Qualifier);
+    if (token == tok::identifier)
+      return ParseError::InvalidQualifierSpecified;
+  }
+  // Ensure the list is unqiue (no duplicates).
+  std::set<std::string> UniqueQualifiers(Style->QualifierOrder.begin(),
+                                         Style->QualifierOrder.end());
+  if (Style->QualifierOrder.size() != UniqueQualifiers.size()) {
+    LLVM_DEBUG(llvm::dbgs()
+               << "Duplicate Qualifiers " << Style->QualifierOrder.size()
+               << " vs " << UniqueQualifiers.size() << "\n");
+    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,
@@ -1573,6 +1638,8 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config,
     // 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);
 }
 
@@ -2894,6 +2961,15 @@ reformat(const FormatStyle &Style, StringRef Code,
       AnalyzerPass;
   SmallVector<AnalyzerPass, 4> Passes;
 
+  if (Style.isCpp() && Style.QualifierAlignment != FormatStyle::QAS_Leave) {
+    Passes.emplace_back([&](const Environment &Env) {
+      return QualifierAlignmentFixer(Env, Expanded, Code, Ranges,
+                                     FirstStartColumn, NextStartColumn,
+                                     LastStartColumn, FileName)
+          .process();
+    });
+  }
+
   if (Style.Language == FormatStyle::LK_Cpp) {
     if (Style.FixNamespaceComments)
       Passes.emplace_back([&](const Environment &Env) {
diff --git a/clang/lib/Format/QualifierAlignmentFixer.cpp b/clang/lib/Format/QualifierAlignmentFixer.cpp
new file mode 100644 (file)
index 0000000..69d0b44
--- /dev/null
@@ -0,0 +1,455 @@
+//===--- LeftRightQualifierAlignmentFixer.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 LeftRightQualifierAlignmentFixer, a TokenAnalyzer that
+/// enforces either left or right const depending on the style.
+///
+//===----------------------------------------------------------------------===//
+
+#include "QualifierAlignmentFixer.h"
+#include "FormatToken.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Regex.h"
+
+#include <algorithm>
+
+#define DEBUG_TYPE "format-qualifier-alignment-fixer"
+
+namespace clang {
+namespace format {
+
+QualifierAlignmentFixer::QualifierAlignmentFixer(
+    const Environment &Env, const FormatStyle &Style, StringRef &Code,
+    ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
+    unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName)
+    : TokenAnalyzer(Env, Style), Code(Code), Ranges(Ranges),
+      FirstStartColumn(FirstStartColumn), NextStartColumn(NextStartColumn),
+      LastStartColumn(LastStartColumn), FileName(FileName) {
+  std::vector<std::string> LeftOrder;
+  std::vector<std::string> RightOrder;
+  std::vector<tok::TokenKind> ConfiguredQualifierTokens;
+  PrepareLeftRightOrdering(Style.QualifierOrder, LeftOrder, RightOrder,
+                           ConfiguredQualifierTokens);
+
+  // Handle the left and right Alignment Seperately
+  for (const auto &Qualifier : LeftOrder) {
+    Passes.emplace_back(
+        [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
+          return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
+                                                  ConfiguredQualifierTokens,
+                                                  /*RightAlign=*/false)
+              .process();
+        });
+  }
+  for (const auto &Qualifier : RightOrder) {
+    Passes.emplace_back(
+        [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
+          return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
+                                                  ConfiguredQualifierTokens,
+                                                  /*RightAlign=*/true)
+              .process();
+        });
+  }
+}
+
+std::pair<tooling::Replacements, unsigned> QualifierAlignmentFixer::analyze(
+    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+    FormatTokenLexer &Tokens) {
+
+  auto Env =
+      std::make_unique<Environment>(Code, FileName, Ranges, FirstStartColumn,
+                                    NextStartColumn, LastStartColumn);
+  llvm::Optional<std::string> CurrentCode = None;
+  tooling::Replacements Fixes;
+  unsigned Penalty = 0;
+  for (size_t I = 0, E = Passes.size(); I < E; ++I) {
+    std::pair<tooling::Replacements, unsigned> PassFixes = Passes[I](*Env);
+    auto NewCode = applyAllReplacements(
+        CurrentCode ? StringRef(*CurrentCode) : Code, PassFixes.first);
+    if (NewCode) {
+      Fixes = Fixes.merge(PassFixes.first);
+      Penalty += PassFixes.second;
+      if (I + 1 < E) {
+        CurrentCode = std::move(*NewCode);
+        Env = std::make_unique<Environment>(
+            *CurrentCode, FileName,
+            tooling::calculateRangesAfterReplacements(Fixes, Ranges),
+            FirstStartColumn, NextStartColumn, LastStartColumn);
+      }
+    }
+  }
+  return {Fixes, 0};
+}
+
+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->Tok.getEndLoc());
+
+  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->Tok.getEndLoc());
+
+  std::string NewText = " " + Qualifier + " ";
+  NewText += First->TokenText;
+
+  replaceToken(SourceMgr, Fixes, Range, NewText);
+}
+
+static bool endsWithSpace(const std::string &s) {
+  if (s.empty()) {
+    return false;
+  }
+  return isspace(s.back());
+}
+
+static bool startsWithSpace(const std::string &s) {
+  if (s.empty()) {
+    return false;
+  }
+  return isspace(s.front());
+}
+
+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() && !endsWithSpace(NewText)) {
+      NewText += " ";
+    }
+
+    NewText += Tok->TokenText;
+    Tok = Tok->Next;
+  }
+
+  // If we are rotating to the right we move the first token to the back.
+  if (!Left) {
+    if (!NewText.empty() && !startsWithSpace(NewText)) {
+      NewText += " ";
+    }
+    NewText += First->TokenText;
+  }
+
+  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+                                             Last->Tok.getEndLoc());
+
+  replaceToken(SourceMgr, Fixes, Range, NewText);
+}
+
+FormatToken *LeftRightQualifierAlignmentFixer::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 a qualifier.
+  if (!Tok->is(QualifierType))
+    return Tok;
+  // Don't concern yourself if nothing follows the qualifier.
+  if (!Tok->Next)
+    return Tok;
+  if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok->Next))
+    return Tok;
+
+  FormatToken *Qual = Tok->Next;
+  FormatToken *LastQual = Qual;
+  while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
+    LastQual = Qual;
+    Qual = Qual->Next;
+  }
+  if (LastQual && Qual != LastQual) {
+    rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
+    Tok = LastQual;
+  } else if (Tok->startsSequence(QualifierType, tok::identifier,
+                                 TT_TemplateOpener)) {
+    // Read from the TemplateOpener to
+    // TemplateCloser as in const ArrayRef<int> a; const ArrayRef<int> &a;
+    FormatToken *EndTemplate = Tok->Next->Next->MatchingParen;
+    if (EndTemplate) {
+      // Move to the end of any template class members e.g.
+      // `Foo<int>::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)) {
+      insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier);
+      // Remove the 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<T> &&` -> `std::Foo<T> 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<int>::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, Tok);
+      }
+      return Next;
+    }
+  }
+
+  return Tok;
+}
+
+FormatToken *LeftRightQualifierAlignmentFixer::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 (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok))
+    return Tok;
+
+  FormatToken *Qual = Tok;
+  FormatToken *LastQual = Qual;
+  while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
+    LastQual = Qual;
+    Qual = Qual->Next;
+    if (Qual && Qual->is(QualifierType))
+      break;
+  }
+
+  if (!Qual) {
+    return Tok;
+  }
+
+  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);
+        Tok = Tok->Next;
+      }
+    }
+  }
+  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<T> const` -> `const std::Foo<T> &&`
+    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<int> a; const ArrayRef<int> &a;
+      assert(Next->MatchingParen && "Missing template closer");
+      Next = Next->MatchingParen->Next;
+
+      // Move to the end of any template class members e.g.
+      // `Foo<int>::iterator`.
+      if (Next && Next->startsSequence(tok::coloncolon, tok::identifier))
+        Next = Next->Next->Next;
+      if (Next && Next->is(QualifierType)) {
+        // Remove the const.
+        insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
+        removeToken(SourceMgr, Fixes, Next);
+        return Next;
+      }
+    }
+    if (Next && Next->Next &&
+        Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) {
+      if (Next->is(QualifierType)) {
+        // Remove the qualifier.
+        insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
+        removeToken(SourceMgr, Fixes, Next);
+        return Next;
+      }
+    }
+  }
+  return Tok;
+}
+
+tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier(
+    const std::string &Qualifier) {
+  // don't let 'type' be an indentifier steal typeof token
+  return llvm::StringSwitch<tok::TokenKind>(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);
+}
+
+LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer(
+    const Environment &Env, const FormatStyle &Style,
+    const std::string &Qualifier,
+    const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign)
+    : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign),
+      ConfiguredQualifierTokens(QualifierTokens) {}
+
+std::pair<tooling::Replacements, unsigned>
+LeftRightQualifierAlignmentFixer::analyze(
+    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &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 (RightAlign)
+        Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier,
+                           QualifierToken);
+      else
+        Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier,
+                          QualifierToken);
+    }
+  }
+  return {Fixes, 0};
+}
+
+void QualifierAlignmentFixer::PrepareLeftRightOrdering(
+    const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder,
+    std::vector<std::string> &RightOrder,
+    std::vector<tok::TokenKind> &Qualifiers) {
+
+  // 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;
+    }
+
+    tok::TokenKind QualifierToken =
+        LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s);
+    if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) {
+      Qualifiers.push_back(QualifierToken);
+    }
+
+    if (left)
+      // Reverse the order for left aligned items.
+      LeftOrder.insert(LeftOrder.begin(), s);
+    else
+      RightOrder.push_back(s);
+  }
+}
+
+bool LeftRightQualifierAlignmentFixer::isQualifierOrType(
+    const FormatToken *Tok, const std::vector<tok::TokenKind> &specifiedTypes) {
+  return Tok && (Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto) ||
+                 (std::find(specifiedTypes.begin(), specifiedTypes.end(),
+                            Tok->Tok.getKind()) != specifiedTypes.end()));
+}
+
+// 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.
+bool LeftRightQualifierAlignmentFixer::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;
+}
+
+} // namespace format
+} // namespace clang
diff --git a/clang/lib/Format/QualifierAlignmentFixer.h b/clang/lib/Format/QualifierAlignmentFixer.h
new file mode 100644 (file)
index 0000000..7abd256
--- /dev/null
@@ -0,0 +1,98 @@
+//===--- LeftRightQualifierAlignmentFixer.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 LeftRightQualifierAlignmentFixer, 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 {
+
+typedef std::function<std::pair<tooling::Replacements, unsigned>(
+    const Environment &)>
+    AnalyzerPass;
+
+class QualifierAlignmentFixer : public TokenAnalyzer {
+  // Left to Right ordering requires multiple passes
+  SmallVector<AnalyzerPass, 8> Passes;
+  StringRef &Code;
+  ArrayRef<tooling::Range> Ranges;
+  unsigned FirstStartColumn;
+  unsigned NextStartColumn;
+  unsigned LastStartColumn;
+  StringRef FileName;
+
+public:
+  QualifierAlignmentFixer(const Environment &Env, const FormatStyle &Style,
+                          StringRef &Code, ArrayRef<tooling::Range> Ranges,
+                          unsigned FirstStartColumn, unsigned NextStartColumn,
+                          unsigned LastStartColumn, StringRef FileName);
+
+  std::pair<tooling::Replacements, unsigned>
+  analyze(TokenAnnotator &Annotator,
+          SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+          FormatTokenLexer &Tokens) override;
+
+  static void PrepareLeftRightOrdering(const std::vector<std::string> &Order,
+                                       std::vector<std::string> &LeftOrder,
+                                       std::vector<std::string> &RightOrder,
+                                       std::vector<tok::TokenKind> &Qualifiers);
+};
+
+class LeftRightQualifierAlignmentFixer : public TokenAnalyzer {
+  std::string Qualifier;
+  bool RightAlign;
+  SmallVector<tok::TokenKind, 8> QualifierTokens;
+  std::vector<tok::TokenKind> ConfiguredQualifierTokens;
+
+public:
+  LeftRightQualifierAlignmentFixer(
+      const Environment &Env, const FormatStyle &Style,
+      const std::string &Qualifier,
+      const std::vector<tok::TokenKind> &ConfiguredQualifierTokens,
+      bool RightAlign);
+
+  std::pair<tooling::Replacements, unsigned>
+  analyze(TokenAnnotator &Annotator,
+          SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+          FormatTokenLexer &Tokens) override;
+
+  static tok::TokenKind getTokenFromQualifier(const std::string &Qualifier);
+
+  FormatToken *analyzeRight(const SourceManager &SourceMgr,
+                            const AdditionalKeywords &Keywords,
+                            tooling::Replacements &Fixes, FormatToken *Tok,
+                            const std::string &Qualifier,
+                            tok::TokenKind QualifierType);
+
+  FormatToken *analyzeLeft(const SourceManager &SourceMgr,
+                           const AdditionalKeywords &Keywords,
+                           tooling::Replacements &Fixes, FormatToken *Tok,
+                           const std::string &Qualifier,
+                           tok::TokenKind QualifierType);
+
+  // is the Token a simple or qualifier type
+  static bool isQualifierOrType(const FormatToken *Tok,
+                                const std::vector<tok::TokenKind> &Qualifiers);
+
+  // is the Token likely a Macro
+  static bool isPossibleMacro(const FormatToken *Tok);
+};
+
+} // end namespace format
+} // end namespace clang
+
+#endif
index 144e87f..16d76a6 100644 (file)
@@ -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 @@ static cl::opt<bool> SortIncludes(
              "SortIncludes style flag"),
     cl::cat(ClangFormatCategory));
 
+static cl::opt<std::string> 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<bool>
     Verbose("verbose", cl::desc("If set, shows the list of processed files"),
             cl::cat(ClangFormatCategory));
@@ -402,6 +410,14 @@ static bool format(StringRef FileName) {
     return true;
   }
 
+  StringRef ConstAlignment = QualifierAlignment;
+
+  FormatStyle->QualifierAlignment =
+      StringSwitch<FormatStyle::QualifierAlignmentStyle>(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 5175c49..4707580 100644 (file)
@@ -18,6 +18,7 @@ add_clang_unittest(FormatTests
   FormatTestTextProto.cpp
   MacroExpanderTest.cpp
   NamespaceEndCommentsFixerTest.cpp
+  QualifierFixerTest.cpp
   SortImportsTestJS.cpp
   SortImportsTestJava.cpp
   SortIncludesTest.cpp
index 80fa21d..1c0db59 100644 (file)
@@ -18325,6 +18325,26 @@ TEST_F(FormatTest, ParsesConfiguration) {
   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<std::string>({"const", "volatile", "type"}));
+  Style.QualifierOrder.clear();
+  CHECK_PARSE("QualifierOrder: [const, type]", QualifierOrder,
+              std::vector<std::string>({"const", "type"}));
+  Style.QualifierOrder.clear();
+  CHECK_PARSE("QualifierOrder: [volatile, type]", QualifierOrder,
+              std::vector<std::string>({"volatile", "type"}));
+
   Style.AlignConsecutiveAssignments = FormatStyle::ACS_Consecutive;
   CHECK_PARSE("AlignConsecutiveAssignments: None", AlignConsecutiveAssignments,
               FormatStyle::ACS_None);
@@ -18992,8 +19012,6 @@ TEST_F(FormatTest, ParsesConfigurationWithLanguages) {
   EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language);
 }
 
-#undef CHECK_PARSE
-
 TEST_F(FormatTest, UsesLanguageForBasedOnStyle) {
   FormatStyle Style = {};
   Style.Language = FormatStyle::LK_JavaScript;
@@ -22294,6 +22312,7 @@ TEST_F(FormatTest, LimitlessStringsAndComments) {
       "}";
   EXPECT_EQ(Code, format(Code, Style));
 }
+
 } // namespace
 } // namespace format
 } // namespace clang
diff --git a/clang/unittests/Format/QualifierFixerTest.cpp b/clang/unittests/Format/QualifierFixerTest.cpp
new file mode 100755 (executable)
index 0000000..1bb1792
--- /dev/null
@@ -0,0 +1,810 @@
+//===- unittest/Format/QualifierFixerTest.cpp - Formatting unit tests -----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Format/Format.h"
+
+#include "FormatTestUtils.h"
+#include "TestLexer.h"
+#include "gtest/gtest.h"
+
+#include "../../lib/Format/QualifierAlignmentFixer.h"
+
+#define DEBUG_TYPE "format-qualifier-fixer-test"
+
+using testing::ScopedTrace;
+
+namespace clang {
+namespace format {
+namespace {
+
+#define CHECK_PARSE(TEXT, FIELD, VALUE)                                        \
+  EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!";          \
+  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!"
+
+class QualifierFixerTest : public ::testing::Test {
+protected:
+  enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete, SC_DoNotCheck };
+
+  TokenList annotate(llvm::StringRef Code,
+                     const FormatStyle &Style = getLLVMStyle()) {
+    return TestLexer(Allocator, Buffers, Style).annotate(Code);
+  }
+  llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;
+  std::vector<std::unique_ptr<llvm::MemoryBuffer>> Buffers;
+
+  std::string format(llvm::StringRef Code,
+                     const FormatStyle &Style = getLLVMStyle(),
+                     StatusCheck CheckComplete = SC_ExpectComplete) {
+    LLVM_DEBUG(llvm::errs() << "---\n");
+    LLVM_DEBUG(llvm::errs() << Code << "\n\n");
+    std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
+    FormattingAttemptStatus Status;
+    tooling::Replacements Replaces =
+        reformat(Style, Code, Ranges, "<stdin>", &Status);
+    if (CheckComplete != SC_DoNotCheck) {
+      bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete;
+      EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete)
+          << Code << "\n\n";
+    }
+    ReplacementCount = Replaces.size();
+    auto Result = applyAllReplacements(Code, Replaces);
+    EXPECT_TRUE(static_cast<bool>(Result));
+    LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+    return *Result;
+  }
+
+  FormatStyle getStyleWithColumns(FormatStyle Style, unsigned ColumnLimit) {
+    Style.ColumnLimit = ColumnLimit;
+    return Style;
+  }
+
+  FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) {
+    return getStyleWithColumns(getLLVMStyle(), ColumnLimit);
+  }
+
+  void _verifyFormat(const char *File, int Line, llvm::StringRef Expected,
+                     llvm::StringRef Code,
+                     const FormatStyle &Style = getLLVMStyle()) {
+    ScopedTrace t(File, Line, ::testing::Message() << Code.str());
+    EXPECT_EQ(Expected.str(), format(Expected, Style))
+        << "Expected code is not stable";
+    EXPECT_EQ(Expected.str(), format(Code, Style));
+    if (Style.Language == FormatStyle::LK_Cpp) {
+      // Objective-C++ is a superset of C++, so everything checked for C++
+      // needs to be checked for Objective-C++ as well.
+      FormatStyle ObjCStyle = Style;
+      ObjCStyle.Language = FormatStyle::LK_ObjC;
+      EXPECT_EQ(Expected.str(), format(test::messUp(Code), ObjCStyle));
+    }
+  }
+
+  void _verifyFormat(const char *File, int Line, llvm::StringRef Code,
+                     const FormatStyle &Style = getLLVMStyle()) {
+    _verifyFormat(File, Line, Code, test::messUp(Code), Style);
+  }
+
+  void _verifyIncompleteFormat(const char *File, int Line, llvm::StringRef Code,
+                               const FormatStyle &Style = getLLVMStyle()) {
+    ScopedTrace t(File, Line, ::testing::Message() << Code.str());
+    EXPECT_EQ(Code.str(),
+              format(test::messUp(Code), Style, SC_ExpectIncomplete));
+  }
+
+  void _verifyIndependentOfContext(const char *File, int Line,
+                                   llvm::StringRef Text,
+                                   const FormatStyle &Style = getLLVMStyle()) {
+    _verifyFormat(File, Line, Text, Style);
+    _verifyFormat(File, Line, llvm::Twine("void f() { " + Text + " }").str(),
+                  Style);
+  }
+
+  /// \brief Verify that clang-format does not crash on the given input.
+  void verifyNoCrash(llvm::StringRef Code,
+                     const FormatStyle &Style = getLLVMStyle()) {
+    format(Code, Style, SC_DoNotCheck);
+  }
+
+  int ReplacementCount;
+};
+
+#define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__)
+
+} // namespace
+
+TEST_F(QualifierFixerTest, RotateTokens) {
+  // TODO add test
+  EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("const"),
+            tok::kw_const);
+  EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("volatile"),
+            tok::kw_volatile);
+  EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("inline"),
+            tok::kw_inline);
+  EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("static"),
+            tok::kw_static);
+  EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("restrict"),
+            tok::kw_restrict);
+}
+
+TEST_F(QualifierFixerTest, FailQualifierInvalidConfiguration) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  FAIL_PARSE("QualifierAlignment: Custom\n"
+             "QualifierOrder: [const, volatile, apples, type]",
+             QualifierOrder,
+             std::vector<std::string>({"const", "volatile", "apples", "type"}));
+}
+
+TEST_F(QualifierFixerTest, FailQualifierDuplicateConfiguration) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  FAIL_PARSE("QualifierAlignment: Custom\n"
+             "QualifierOrder: [const, volatile, const, type]",
+             QualifierOrder,
+             std::vector<std::string>({"const", "volatile", "const", "type"}));
+}
+
+TEST_F(QualifierFixerTest, FailQualifierMissingType) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  FAIL_PARSE("QualifierAlignment: Custom\n"
+             "QualifierOrder: [const, volatile ]",
+             QualifierOrder,
+             std::vector<std::string>({
+                 "const",
+                 "volatile",
+             }));
+}
+
+TEST_F(QualifierFixerTest, FailQualifierEmptyOrder) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  FAIL_PARSE("QualifierAlignment: Custom\nQualifierOrder: []", QualifierOrder,
+             std::vector<std::string>({}));
+}
+
+TEST_F(QualifierFixerTest, FailQualifierMissingOrder) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  FAIL_PARSE("QualifierAlignment: Custom", QualifierOrder,
+             std::vector<std::string>());
+}
+
+TEST_F(QualifierFixerTest, QualifierLeft) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  CHECK_PARSE("QualifierAlignment: Left", QualifierOrder,
+              std::vector<std::string>({"const", "volatile", "type"}));
+}
+
+TEST_F(QualifierFixerTest, QualifierRight) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_Cpp;
+  CHECK_PARSE("QualifierAlignment: Right", QualifierOrder,
+              std::vector<std::string>({"type", "const", "volatile"}));
+}
+
+TEST_F(QualifierFixerTest, QualifiersCustomOrder) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"inline", "constexpr", "static",
+                          "const",  "volatile",  "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);
+}
+
+TEST_F(QualifierFixerTest, 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(QualifierFixerTest, RightQualifier) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Right;
+  Style.QualifierOrder = {"type", "const", "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 <typename Func> explicit Action(Action<Func> const &action);",
+      Style);
+  verifyFormat(
+      "template <typename Func> explicit Action(Action<Func> const &action);",
+      "template <typename Func> explicit Action(const Action<Func>& action);",
+      Style);
+  verifyFormat(
+      "template <typename Func> explicit Action(Action<Func> const &action);",
+      "template <typename Func>\nexplicit Action(const Action<Func>& 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<int, int const, int &, int const &> args1",
+               "vector<int, const int, int &, const int &> args1", Style);
+  verifyFormat("unsigned int const &get_nu() const",
+               "const unsigned int &get_nu() const", Style);
+  verifyFormat("Foo<int> const &a", "const Foo<int> &a", Style);
+  verifyFormat("Foo<int>::iterator const &a", "const Foo<int>::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<int>::bat = 0;", "const int Foo<int>::bat = 0;",
+               Style);
+  verifyFormat("int const Foo<int>::bat = 0;", "int const Foo<int>::bat = 0;",
+               Style);
+  verifyFormat("void fn(Foo<T> const &i);", "void fn(const Foo<T> &i);", Style);
+  verifyFormat("int const Foo<int>::fn() {", "int const Foo<int>::fn() {",
+               Style);
+  verifyFormat("Foo<Foo<int>> const *p;", "const Foo<Foo<int>> *p;", Style);
+  verifyFormat(
+      "Foo<Foo<int>> const *p = const_cast<Foo<Foo<int>> const *>(&ffi);",
+      "const Foo<Foo<int>> *p = const_cast<const Foo<Foo<int>> *>(&ffi);",
+      Style);
+
+  verifyFormat("void fn(Foo<T> const &i);", "void fn(const Foo<T> &i);", Style);
+  verifyFormat("void fns(ns::S const &s);", "void fns(const ns::S &s);", Style);
+  verifyFormat("void fn(ns::Foo<T> const &i);", "void fn(const ns::Foo<T> &i);",
+               Style);
+  verifyFormat("void fns(ns::ns2::S const &s);",
+               "void fns(const ns::ns2::S &s);", Style);
+  verifyFormat("void fn(ns::Foo<Bar<T>> const &i);",
+               "void fn(const ns::Foo<Bar<T>> &i);", Style);
+  verifyFormat("void fn(ns::ns2::Foo<Bar<T>> const &i);",
+               "void fn(const ns::ns2::Foo<Bar<T>> &i);", Style);
+  verifyFormat("void fn(ns::ns2::Foo<Bar<T, U>> const &i);",
+               "void fn(const ns::ns2::Foo<Bar<T, U>> &i);", Style);
+
+  verifyFormat("LocalScope const *Scope = nullptr;",
+               "const LocalScope* Scope = nullptr;", Style);
+  verifyFormat("struct DOTGraphTraits<Stmt const *>",
+               "struct DOTGraphTraits<const Stmt *>", Style);
+
+  verifyFormat(
+      "bool tools::addXRayRuntime(ToolChain const &TC, ArgList const &Args) {",
+      "bool tools::addXRayRuntime(const ToolChain&TC, const ArgList &Args) {",
+      Style);
+  verifyFormat("Foo<Foo<int> const> P;", "Foo<const Foo<int>> P;", Style);
+  verifyFormat("Foo<Foo<int> const> P;\n", "Foo<const Foo<int>> P;\n", Style);
+  verifyFormat("Foo<Foo<int> const> P;\n#if 0\n#else\n#endif",
+               "Foo<const Foo<int>> 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<Foo<int> const> P;\n#if 0\n#else\n#endif",
+               "Foo<const Foo<int>> P;\n#if 0\n#else\n#endif", Style);
+
+  verifyFormat("Bar<Bar<int const> const> P;\n#if 0\n#else\n#endif",
+               "Bar<Bar<const int> const> P;\n#if 0\n#else\n#endif", Style);
+
+  verifyFormat("Baz<Baz<int const> const> P;\n#if 0\n#else\n#endif",
+               "Baz<const Baz<const int>> P;\n#if 0\n#else\n#endif", Style);
+
+  // verifyFormat("#if 0\nBoo<Boo<int const> const> P;\n#else\n#endif",
+  //             "#if 0\nBoo<const Boo<const int>> 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(QualifierFixerTest, LeftQualifier) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"inline", "static", "const", "volatile", "type"};
+
+  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<int> &a", "Foo<int> const &a", Style);
+  verifyFormat("const Foo<int>::iterator &a", "Foo<int>::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 <typename Func> explicit Action(const Action<Func> &action);",
+      Style);
+  verifyFormat(
+      "template <typename Func> explicit Action(const Action<Func> &action);",
+      "template <typename Func> explicit Action(Action<Func> 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<int>::bat = 0;",
+               "static const int Foo<int>::bat = 0;", Style);
+  verifyFormat("static const int Foo<int>::bat = 0;",
+               "static int const Foo<int>::bat = 0;", Style);
+
+  verifyFormat("void fn(const Foo<T> &i);");
+
+  verifyFormat("const int Foo<int>::bat = 0;", "const int Foo<int>::bat = 0;",
+               Style);
+  verifyFormat("const int Foo<int>::bat = 0;", "int const Foo<int>::bat = 0;",
+               Style);
+  verifyFormat("void fn(const Foo<T> &i);", "void fn( Foo<T> const &i);",
+               Style);
+  verifyFormat("const int Foo<int>::fn() {", "int const Foo<int>::fn() {",
+               Style);
+  verifyFormat("const Foo<Foo<int>> *p;", "Foo<Foo<int>> const *p;", Style);
+  verifyFormat(
+      "const Foo<Foo<int>> *p = const_cast<const Foo<Foo<int>> *>(&ffi);",
+      "const Foo<Foo<int>> *p = const_cast<Foo<Foo<int>> const *>(&ffi);",
+      Style);
+
+  verifyFormat("void fn(const Foo<T> &i);", "void fn(Foo<T> const &i);", Style);
+  verifyFormat("void fns(const ns::S &s);", "void fns(ns::S const &s);", Style);
+  verifyFormat("void fn(const ns::Foo<T> &i);", "void fn(ns::Foo<T> 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<Bar<T>> &i);",
+               "void fn(ns::Foo<Bar<T>> const &i);", Style);
+  verifyFormat("void fn(const ns::ns2::Foo<Bar<T>> &i);",
+               "void fn(ns::ns2::Foo<Bar<T>> const &i);", Style);
+  verifyFormat("void fn(const ns::ns2::Foo<Bar<T, U>> &i);",
+               "void fn(ns::ns2::Foo<Bar<T, U>> 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<const Foo<int>> P;\n#if 0\n#else\n#endif",
+               "Foo<Foo<int> const> P;\n#if 0\n#else\n#endif", Style);
+
+  verifyFormat("Foo<Foo<const int>> P;\n#if 0\n#else\n#endif",
+               "Foo<Foo<int const>> 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<int", Style);
+  verifyFormat("const std::Foo<int>", "const std::Foo<int>", Style);
+
+  // don't adjust macros
+  verifyFormat("INTPTR const a;", "INTPTR const a;", Style);
+}
+
+TEST_F(QualifierFixerTest, ConstVolatileQualifiersOrder) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"inline", "static", "const", "volatile", "type"};
+
+  // The Default
+  EXPECT_EQ(Style.QualifierOrder.size(), 5);
+
+  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 = {"type", "const", "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 = {"volatile", "const", "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 = {"type", "volatile", "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 = {"type", "volatile", "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(QualifierFixerTest, InlineStatics) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"inline", "static", "const", "volatile", "type"};
+  EXPECT_EQ(Style.QualifierOrder.size(), 5);
+
+  verifyFormat("inline static const volatile int a;",
+               "const inline static volatile int a;", Style);
+  verifyFormat("inline static const volatile int a;",
+               "volatile inline static const int a;", Style);
+  verifyFormat("inline static const volatile int a;",
+               "int const inline static  volatile a;", Style);
+  verifyFormat("inline static const volatile int a;",
+               "int volatile inline static  const a;", Style);
+  verifyFormat("inline static const volatile int a;",
+               "const int inline static  volatile a;", Style);
+}
+
+TEST_F(QualifierFixerTest, AmpEqual) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Custom;
+  Style.QualifierOrder = {"static", "type", "const"};
+  EXPECT_EQ(Style.QualifierOrder.size(), 3);
+
+  verifyFormat("foo(std::string const & = std::string()) const",
+               "foo(const std::string & = std::string()) const", Style);
+  verifyFormat("foo(std::string const & = std::string())",
+               "foo(const std::string & = std::string())", Style);
+}
+
+TEST_F(QualifierFixerTest, MoveConstBeyondTypeSmall) {
+
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Custom;
+  Style.QualifierOrder = {"type", "const"};
+  EXPECT_EQ(Style.QualifierOrder.size(), 2);
+
+  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("int const &a;", "const int &a;", Style);
+  verifyFormat("int const &&a;", "const int &&a;", Style);
+}
+
+TEST_F(QualifierFixerTest, MoveConstBeforeTypeSmall) {
+
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Custom;
+  Style.QualifierOrder = {"const", "type"};
+  EXPECT_EQ(Style.QualifierOrder.size(), 2);
+
+  verifyFormat("const int a;", "int const a;", Style);
+  verifyFormat("const int *a;", "int const *a;", Style);
+  verifyFormat("const int *a const;", "int const *a const;", Style);
+
+  verifyFormat("const int a = foo();", "int const a = foo();", Style);
+  verifyFormat("const int *a = foo();", "int const *a = foo();", Style);
+  verifyFormat("const int *a const = foo();", "int const *a const = foo();",
+               Style);
+
+  verifyFormat("const auto a = foo();", "auto const a = foo();", Style);
+  verifyFormat("const auto *a = foo();", "auto const *a = foo();", Style);
+  verifyFormat("const auto *a const = foo();", "auto const *a const = foo();",
+               Style);
+}
+
+TEST_F(QualifierFixerTest, MoveConstBeyondType) {
+
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Custom;
+  Style.QualifierOrder = {"static", "inline", "type", "const", "volatile"};
+  EXPECT_EQ(Style.QualifierOrder.size(), 5);
+
+  verifyFormat("static inline int const volatile a;",
+               "const inline static volatile int a;", Style);
+  verifyFormat("static inline int const volatile a;",
+               "volatile inline static const int a;", Style);
+  verifyFormat("static inline int const volatile a;",
+               "int const inline static  volatile a;", Style);
+  verifyFormat("static inline int const volatile a;",
+               "int volatile inline static  const a;", Style);
+  verifyFormat("static inline int const volatile a;",
+               "const int inline static  volatile a;", Style);
+
+  verifyFormat("static inline int const volatile *a const;",
+               "const int inline static  volatile *a const;", Style);
+}
+
+TEST_F(QualifierFixerTest, PrepareLeftRightOrdering) {
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Custom;
+  Style.QualifierOrder = {"static", "inline", "type", "const", "volatile"};
+
+  std::vector<std::string> Left;
+  std::vector<std::string> Right;
+  std::vector<tok::TokenKind> ConfiguredTokens;
+  QualifierAlignmentFixer::PrepareLeftRightOrdering(Style.QualifierOrder, Left,
+                                                    Right, ConfiguredTokens);
+
+  EXPECT_EQ(Left.size(), 2);
+  EXPECT_EQ(Right.size(), 2);
+
+  std::vector<std::string> LeftResult = {"inline", "static"};
+  std::vector<std::string> RightResult = {"const", "volatile"};
+  EXPECT_EQ(Left, LeftResult);
+  EXPECT_EQ(Right, RightResult);
+}
+
+TEST_F(QualifierFixerTest, IsQualifierType) {
+
+  std::vector<tok::TokenKind> ConfiguredTokens;
+  ConfiguredTokens.push_back(tok::kw_const);
+  ConfiguredTokens.push_back(tok::kw_static);
+  ConfiguredTokens.push_back(tok::kw_inline);
+  ConfiguredTokens.push_back(tok::kw_restrict);
+  ConfiguredTokens.push_back(tok::kw_constexpr);
+
+  auto Tokens =
+      annotate("const static inline auto restrict int double long constexpr");
+
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[0], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[1], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[2], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[3], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[4], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[5], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[6], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[7], ConfiguredTokens));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      Tokens[8], ConfiguredTokens));
+
+  auto NotTokens = annotate("for while do Foo Bar ");
+
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      NotTokens[0], ConfiguredTokens));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      NotTokens[1], ConfiguredTokens));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      NotTokens[2], ConfiguredTokens));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      NotTokens[3], ConfiguredTokens));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      NotTokens[4], ConfiguredTokens));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isQualifierOrType(
+      NotTokens[5], ConfiguredTokens));
+}
+
+TEST_F(QualifierFixerTest, IsMacro) {
+
+  auto Tokens = annotate("INT INTPR Foo int");
+
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[0]));
+  EXPECT_TRUE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[1]));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[2]));
+  EXPECT_FALSE(LeftRightQualifierAlignmentFixer::isPossibleMacro(Tokens[3]));
+}
+
+TEST_F(QualifierFixerTest, OverlappingQualifier) {
+
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"const", "type"};
+
+  verifyFormat("Foo(const Bar &name);", "Foo(Bar const &name);", Style);
+}
+
+TEST_F(QualifierFixerTest, DontPushQualifierThroughNonSpecifiedTypes) {
+
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"const", "type"};
+
+  verifyFormat("inline static const int a;", Style);
+
+  Style.QualifierOrder = {"static", "const", "type"};
+
+  verifyFormat("inline static const int a;", Style);
+  verifyFormat("static inline const int a;", "static inline const int a;",
+               Style);
+
+  verifyFormat("static const int a;", "const static int a;", Style);
+}
+
+TEST_F(QualifierFixerTest, UnsignedQualifier) {
+
+  FormatStyle Style = getLLVMStyle();
+  Style.QualifierAlignment = FormatStyle::QAS_Left;
+  Style.QualifierOrder = {"const", "type"};
+
+  verifyFormat("Foo(const unsigned char *bytes)",
+               "Foo(unsigned const char *bytes)", Style);
+
+  Style.QualifierAlignment = FormatStyle::QAS_Right;
+  Style.QualifierOrder = {"type", "const"};
+
+  verifyFormat("Foo(unsigned char const *bytes)",
+               "Foo(unsigned const char *bytes)", Style);
+}
+
+} // namespace format
+} // namespace clang