MyDeveloperDay updated this revision to Diff 266799.
MyDeveloperDay added a comment.

Minor change for the simpler review comments before refactoring some of the 
more involved ones


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69764/new/

https://reviews.llvm.org/D69764

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/CMakeLists.txt
  clang/lib/Format/EastWestConstFixer.cpp
  clang/lib/Format/EastWestConstFixer.h
  clang/lib/Format/Format.cpp
  clang/tools/clang-format/ClangFormat.cpp
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -13628,6 +13628,13 @@
   CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u);
   CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$");
 
+  Style.ConstPlacement = FormatStyle::CS_West;
+  CHECK_PARSE("ConstPlacement: Leave", ConstPlacement, FormatStyle::CS_Leave);
+  CHECK_PARSE("ConstPlacement: East", ConstPlacement, FormatStyle::CS_East);
+  CHECK_PARSE("ConstPlacement: West", ConstPlacement, FormatStyle::CS_West);
+  CHECK_PARSE("ConstPlacement: Right", ConstPlacement, FormatStyle::CS_East);
+  CHECK_PARSE("ConstPlacement: Left", ConstPlacement, FormatStyle::CS_West);
+
   Style.PointerAlignment = FormatStyle::PAS_Middle;
   CHECK_PARSE("PointerAlignment: Left", PointerAlignment,
               FormatStyle::PAS_Left);
@@ -16572,6 +16579,326 @@
                "}",
                Style);
 }
+
+TEST_F(FormatTest, EastWestConst) {
+  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, EastConst) {
+  FormatStyle Style = getLLVMStyle();
+  Style.ConstPlacement = FormatStyle::CS_East;
+
+  
+  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("volatile int const;", "volatile const int;", Style);
+  verifyFormat("volatile int const;", "const volatile int;", Style);
+  verifyFormat("int volatile const;", "const int volatile;", Style);
+  verifyFormat("volatile int const *restrict;", "volatile const int *restrict;",
+               Style);
+  verifyFormat("volatile int const *restrict;", "const volatile int *restrict;",
+               Style);
+  verifyFormat("int volatile const *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(FormatTest, WestConst) {
+  FormatStyle Style = getLLVMStyle();
+  Style.ConstPlacement = FormatStyle::CS_West;
+
+  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("volatile const int;", "volatile const int;", Style);
+  verifyFormat("const volatile int;", "const volatile int;", Style);
+  verifyFormat("const int volatile;", "const int volatile;", Style);
+
+  verifyFormat("volatile const int *restrict;", "volatile const int *restrict;",
+               Style);
+  verifyFormat("const volatile int *restrict;", "const volatile int *restrict;",
+               Style);
+  verifyFormat("const int volatile *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);
+
+  // don't adjust macros
+  verifyFormat("INTPTR const a;", "INTPTR const a;", Style);
+}
+
 } // namespace
 } // 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,12 @@
              "SortIncludes style flag"),
     cl::cat(ClangFormatCategory));
 
+static cl::opt<std::string> ConstPlacement(
+    "const-placement",
+    cl::desc("If set, overrides the const style behavior determined by the "
+             "ConstPlacement 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));
@@ -384,6 +391,14 @@
     return true;
   }
 
+  StringRef ConstAlignment = ConstPlacement;
+
+  FormatStyle->ConstPlacement =
+      StringSwitch<FormatStyle::ConstPlacementStyle>(ConstAlignment.lower())
+          .Cases("right", "east", FormatStyle::CS_East)
+          .Cases("left", "west", FormatStyle::CS_West)
+          .Default(FormatStyle->ConstPlacement);
+
   if (SortIncludes.getNumOccurrences() != 0)
     FormatStyle->SortIncludes = SortIncludes;
   unsigned CursorPosition = Cursor;
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -15,6 +15,7 @@
 #include "clang/Format/Format.h"
 #include "AffectedRangeManager.h"
 #include "ContinuationIndenter.h"
+#include "EastWestConstFixer.h"
 #include "FormatInternal.h"
 #include "FormatTokenLexer.h"
 #include "NamespaceEndCommentsFixer.h"
@@ -115,6 +116,17 @@
   }
 };
 
+template <> struct ScalarEnumerationTraits<FormatStyle::ConstPlacementStyle> {
+  static void enumeration(IO &IO, FormatStyle::ConstPlacementStyle &Value) {
+    IO.enumCase(Value, "Leave", FormatStyle::CS_Leave);
+    IO.enumCase(Value, "West", FormatStyle::CS_West);
+    IO.enumCase(Value, "East", FormatStyle::CS_East);
+
+    IO.enumCase(Value, "Left", FormatStyle::CS_West);
+    IO.enumCase(Value, "Right", FormatStyle::CS_East);
+  }
+};
+
 template <> struct ScalarEnumerationTraits<FormatStyle::ShortFunctionStyle> {
   static void enumeration(IO &IO, FormatStyle::ShortFunctionStyle &Value) {
     IO.enumCase(Value, "None", FormatStyle::SFS_None);
@@ -500,6 +512,7 @@
     IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals);
     IO.mapOptional("ColumnLimit", Style.ColumnLimit);
     IO.mapOptional("CommentPragmas", Style.CommentPragmas);
+    IO.mapOptional("ConstPlacement", Style.ConstPlacement);
     IO.mapOptional("CompactNamespaces", Style.CompactNamespaces);
     IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine",
                    Style.ConstructorInitializerAllOnOneLineOrOnePerLine);
@@ -857,6 +870,7 @@
   LLVMStyle.BreakStringLiterals = true;
   LLVMStyle.ColumnLimit = 80;
   LLVMStyle.CommentPragmas = "^ IWYU pragma:";
+  LLVMStyle.ConstPlacement = FormatStyle::CS_Leave;
   LLVMStyle.CompactNamespaces = false;
   LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false;
   LLVMStyle.ConstructorInitializerIndentWidth = 4;
@@ -1041,6 +1055,7 @@
     // taze:, triple slash directives (`/// <...`), tslint:, and @see, which is
     // commonly followed by overlong URLs.
     GoogleStyle.CommentPragmas = "(taze:|^/[ \t]*<|tslint:|@see)";
+    GoogleStyle.ConstPlacement = FormatStyle::CS_Leave;
     // TODO: enable once decided, in particular re disabling bin packing.
     // https://google.github.io/styleguide/jsguide.html#features-arrays-trailing-comma
     // GoogleStyle.InsertTrailingCommas = FormatStyle::TCS_Wrapped;
@@ -1095,6 +1110,7 @@
   //   _prepend that with a comment_ to prevent it" before changing behavior.
   ChromiumStyle.IncludeStyle.IncludeBlocks =
       tooling::IncludeStyle::IBS_Preserve;
+  ChromiumStyle.ConstPlacement = FormatStyle::CS_Leave;
 
   if (Language == FormatStyle::LK_Java) {
     ChromiumStyle.AllowShortIfStatementsOnASingleLine =
@@ -1156,6 +1172,7 @@
   MozillaStyle.PenaltyReturnTypeOnItsOwnLine = 200;
   MozillaStyle.PointerAlignment = FormatStyle::PAS_Left;
   MozillaStyle.SpaceAfterTemplateKeyword = false;
+  MozillaStyle.ConstPlacement = FormatStyle::CS_Leave;
   return MozillaStyle;
 }
 
@@ -1179,6 +1196,7 @@
   Style.PointerAlignment = FormatStyle::PAS_Left;
   Style.SpaceBeforeCpp11BracedList = true;
   Style.SpaceInEmptyBlock = true;
+  Style.ConstPlacement = FormatStyle::CS_Leave;
   return Style;
 }
 
@@ -1194,6 +1212,7 @@
   Style.FixNamespaceComments = false;
   Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
   Style.Standard = FormatStyle::LS_Cpp03;
+  Style.ConstPlacement = FormatStyle::CS_Leave;
   return Style;
 }
 
@@ -1224,6 +1243,7 @@
   Style.AllowShortLoopsOnASingleLine = false;
   Style.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;
   Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None;
+  Style.ConstPlacement = FormatStyle::CS_Leave;
   return Style;
 }
 
@@ -1232,6 +1252,7 @@
   NoStyle.DisableFormat = true;
   NoStyle.SortIncludes = false;
   NoStyle.SortUsingDeclarations = false;
+  NoStyle.ConstPlacement = FormatStyle::CS_Leave;
   return NoStyle;
 }
 
@@ -2599,6 +2620,11 @@
       });
   }
 
+  if (Style.isCpp() && Style.ConstPlacement != FormatStyle::CS_Leave)
+    Passes.emplace_back([&](const Environment &Env) {
+      return EastWestConstFixer(Env, Expanded).process();
+    });
+
   if (Style.Language == FormatStyle::LK_JavaScript &&
       Style.JavaScriptQuotes != FormatStyle::JSQS_Leave)
     Passes.emplace_back([&](const Environment &Env) {
Index: clang/lib/Format/EastWestConstFixer.h
===================================================================
--- /dev/null
+++ clang/lib/Format/EastWestConstFixer.h
@@ -0,0 +1,36 @@
+//===--- EastWestConstFixer.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 EastWestConstFixer, 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 EastWestConstFixer : public TokenAnalyzer {
+public:
+  EastWestConstFixer(const Environment &Env, const FormatStyle &Style);
+
+  std::pair<tooling::Replacements, unsigned>
+  analyze(TokenAnnotator &Annotator,
+          SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+          FormatTokenLexer &Tokens) override;
+};
+
+} // end namespace format
+} // end namespace clang
+
+#endif
Index: clang/lib/Format/EastWestConstFixer.cpp
===================================================================
--- /dev/null
+++ clang/lib/Format/EastWestConstFixer.cpp
@@ -0,0 +1,386 @@
+//===--- EastWestConstFixer.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 EastWestConstFixer, a TokenAnalyzer that
+/// enforces either east or west const depending on the style.
+///
+//===----------------------------------------------------------------------===//
+
+#include "EastWestConstFixer.h"
+#include "FormatToken.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Regex.h"
+
+#include <algorithm>
+
+namespace clang {
+namespace format {
+
+static void removeToken(const SourceManager &SourceMgr,
+                        tooling::Replacements &Fixes,
+                        const FormatToken *First) {
+  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+                                             First->Tok.getEndLoc());
+
+  auto Err = Fixes.add(tooling::Replacement(SourceMgr, Range, ""));
+
+  if (Err) {
+    llvm::errs() << "Error while removing const : "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+
+static void insertConstAfter(const SourceManager &SourceMgr,
+                             tooling::Replacements &Fixes,
+                             const FormatToken *First) {
+  FormatToken *Next = First->Next;
+  if (!Next) {
+    return;
+  }
+  auto Range = CharSourceRange::getCharRange(
+      Next->getStartOfNonWhitespace(), Next->Next->getStartOfNonWhitespace());
+
+  std::string NewText = " const ";
+  NewText += Next->TokenText;
+
+  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 insertConstBefore(const SourceManager &SourceMgr,
+                              tooling::Replacements &Fixes,
+                              const FormatToken *First) {
+  auto Range = CharSourceRange::getCharRange(
+      First->getStartOfNonWhitespace(), First->Next->getStartOfNonWhitespace());
+
+  std::string NewText = " const ";
+  NewText += First->TokenText;
+
+  auto Err = Fixes.add(tooling::Replacement(SourceMgr, Range, NewText));
+
+  if (Err) {
+    llvm::errs() << "Error while rearranging const : "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+
+static void swapTwoTokens(const SourceManager &SourceMgr,
+                          tooling::Replacements &Fixes,
+                          const FormatToken *First, const FormatToken *Second) {
+  // Change `const int` to be `int const`.
+  std::string NewText;
+  NewText += Second->TokenText;
+  NewText += " ";
+  NewText += First->TokenText;
+  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+                                             Second->Tok.getEndLoc());
+
+  auto Err = Fixes.add(tooling::Replacement(SourceMgr, Range, NewText));
+
+  if (Err) {
+    llvm::errs() << "Error while rearranging const : "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+
+static void swapThreeTokens(const SourceManager &SourceMgr,
+                            tooling::Replacements &Fixes,
+                            const FormatToken *First, const FormatToken *Second,
+                            const FormatToken *Third, bool Left) {
+  // e.g. Change `const unsigned char` to be `unsigned char const`.
+  std::string NewText;
+  if (Left) {
+    NewText += Third->TokenText;
+    NewText += " ";
+    NewText += First->TokenText;
+    NewText += " ";
+    NewText += Second->TokenText;
+  } else {
+    NewText += Second->TokenText;
+    NewText += " ";
+    NewText += Third->TokenText;
+    NewText += " ";
+    NewText += First->TokenText;
+  }
+
+  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+                                             Third->Tok.getEndLoc());
+
+  auto Err = Fixes.add(tooling::Replacement(SourceMgr, Range, NewText));
+
+  if (Err) {
+    llvm::errs() << "Error while rearranging const : "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+
+static void swapFourTokens(const SourceManager &SourceMgr,
+                           tooling::Replacements &Fixes,
+                           const FormatToken *First, const FormatToken *Second,
+                           const FormatToken *Third, const FormatToken *Fourth,
+                           bool Left) {
+  // e.g. Change `const unsigned long long` to be `unsigned long long const`.
+  std::string NewText;
+  if (Left) {
+    NewText += Fourth->TokenText;
+    NewText += " ";
+    NewText += First->TokenText;
+    NewText += " ";
+    NewText += Second->TokenText;
+    NewText += " ";
+    NewText += Third->TokenText;
+  } else {
+    NewText += Second->TokenText;
+    NewText += " ";
+    NewText += Third->TokenText;
+    NewText += " ";
+    NewText += Fourth->TokenText;
+    NewText += " ";
+    NewText += First->TokenText;
+  }
+
+  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+                                             Fourth->Tok.getEndLoc());
+
+  auto Err = Fixes.add(tooling::Replacement(SourceMgr, Range, NewText));
+
+  if (Err) {
+    llvm::errs() << "Error while rearranging const : "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+
+static bool isCVQualifierOrType(const FormatToken *Tok) {
+  return Tok && (Tok->isSimpleTypeSpecifier() ||
+                 Tok->isOneOf(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 *analyzeEast(const SourceManager &SourceMgr,
+                                const AdditionalKeywords &Keywords,
+                                tooling::Replacements &Fixes,
+                                FormatToken *Tok) {
+  // We only need to think about streams that begin with const.
+  if (!Tok->is(tok::kw_const)) {
+    return Tok;
+  }
+  // Don't concern yourself if nothing follows const.
+  if (!Tok->Next) {
+    return Tok;
+  }
+  if (isPossibleMacro(Tok->Next)) {
+    return Tok;
+  }
+  FormatToken *Const = Tok;
+
+  if (isCVQualifierOrType(Tok->Next) && isCVQualifierOrType(Tok->Next->Next) &&
+      isCVQualifierOrType(Tok->Next->Next->Next)) {
+    swapFourTokens(SourceMgr, Fixes, Tok, Tok->Next, Tok->Next->Next,
+                   Tok->Next->Next->Next,
+                   /*Left=*/false);
+    Tok = Tok->Next->Next->Next;
+  } else if (isCVQualifierOrType(Tok->Next) && Tok->Next->Next &&
+             isCVQualifierOrType(Tok->Next->Next)) {
+    // The unsigned/signed case  `const unsigned int` -> `unsigned int
+    // const`.
+    swapThreeTokens(SourceMgr, Fixes, Tok, Tok->Next, Tok->Next->Next,
+                    /*Left=*/false);
+    Tok = Tok->Next->Next;
+  } else if (Tok->Next->isSimpleTypeSpecifier() ||
+             Tok->Next->is(tok::kw_auto)) {
+    // The basic case `const int` -> `int const`
+    swapTwoTokens(SourceMgr, Fixes, Tok, Tok->Next);
+  } else if (Tok->startsSequence(tok::kw_const, 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)) {
+      // Remove the const.
+      insertConstAfter(SourceMgr, Fixes, EndTemplate);
+      removeToken(SourceMgr, Fixes, Tok);
+      return Tok;
+    }
+  } else if (Tok->startsSequence(tok::kw_const, 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;
+      }
+      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(tok::kw_const)) {
+        insertConstAfter(SourceMgr, Fixes, Next->Previous);
+        removeToken(SourceMgr, Fixes, Const);
+      }
+      return Next;
+    }
+  }
+
+  return Tok;
+}
+
+static FormatToken *analyzeWest(const SourceManager &SourceMgr,
+                                const AdditionalKeywords &Keywords,
+                                tooling::Replacements &Fixes,
+                                FormatToken *Tok) {
+  // if Tok is an identifier and possibly a macro then don't convert
+  if (isPossibleMacro(Tok)) {
+    return Tok;
+  }
+  if (isCVQualifierOrType(Tok) && isCVQualifierOrType(Tok->Next) &&
+      isCVQualifierOrType(Tok->Next->Next) &&
+      // `unsigned long long const` -> `const unsigned long long`.
+      Tok->Next->Next->Next && Tok->Next->Next->Next->is(tok::kw_const)) {
+    swapFourTokens(SourceMgr, Fixes, Tok, Tok->Next, Tok->Next,
+                   Tok->Next->Next->Next,
+                   /*Left=*/true);
+    Tok = Tok->Next->Next->Next;
+  } else if (isCVQualifierOrType(Tok) && isCVQualifierOrType(Tok->Next) &&
+             Tok->Next->Next && Tok->Next->Next->is(tok::kw_const)) {
+    // `unsigned int const` -> `const unsigned int`.
+    swapThreeTokens(SourceMgr, Fixes, Tok, Tok->Next, Tok->Next->Next,
+                    /*Left=*/true);
+    Tok = Tok->Next->Next;
+  } else if ((Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto)) &&
+             Tok->Next->is(tok::kw_const)) {
+    // The basic case  `int const` -> `const int`.
+    swapTwoTokens(SourceMgr, Fixes, Tok, Tok->Next);
+  } else if (Tok->startsSequence(tok::identifier, tok::kw_const)) {
+    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))) {
+        swapTwoTokens(SourceMgr, Fixes, 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(tok::kw_const)) {
+    swapTwoTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next);
+  }
+  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;
+      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(tok::kw_const)) {
+        // Remove the const.
+        removeToken(SourceMgr, Fixes, Next);
+        insertConstBefore(SourceMgr, Fixes, Tok);
+        return Next;
+      }
+    }
+    if (Next && Next->Next &&
+        Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) {
+      if (Next->is(tok::kw_const)) {
+        // Remove the const.
+        removeToken(SourceMgr, Fixes, Next);
+        insertConstBefore(SourceMgr, Fixes, Tok);
+        return Next;
+      }
+    }
+  }
+  return Tok;
+}
+
+EastWestConstFixer::EastWestConstFixer(const Environment &Env,
+                                       const FormatStyle &Style)
+    : TokenAnalyzer(Env, Style) {}
+
+std::pair<tooling::Replacements, unsigned>
+EastWestConstFixer::analyze(TokenAnnotator &Annotator,
+                            SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+                            FormatTokenLexer &Tokens) {
+  tooling::Replacements Fixes;
+  const AdditionalKeywords &Keywords = Tokens.getKeywords();
+  const SourceManager &SourceMgr = Env.getSourceManager();
+  AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
+
+  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.ConstPlacement == FormatStyle::CS_East) {
+        Tok = analyzeEast(SourceMgr, Keywords, Fixes, Tok);
+      } else if (Style.ConstPlacement == FormatStyle::CS_West) {
+        Tok = analyzeWest(SourceMgr, Keywords, Fixes, Tok);
+      }
+    }
+  }
+  return {Fixes, 0};
+}
+} // namespace format
+} // namespace clang
Index: clang/lib/Format/CMakeLists.txt
===================================================================
--- clang/lib/Format/CMakeLists.txt
+++ clang/lib/Format/CMakeLists.txt
@@ -14,6 +14,7 @@
   UnwrappedLineFormatter.cpp
   UnwrappedLineParser.cpp
   UsingDeclarationsSorter.cpp
+  EastWestConstFixer.cpp
   WhitespaceManager.cpp
 
   LINK_LIBS
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -1223,6 +1223,31 @@
   /// \endcode
   std::string CommentPragmas;
 
+  /// Different const alignment styles.
+  enum ConstPlacementStyle {
+    /// Don't change const to either East/Right const or West/Left const.
+    /// \code
+    ///    int const a;
+    ///    const int *a;
+    /// \endcode
+    CS_Leave,
+    /// Change type decorations to be West/Left const.
+    /// \code
+    ///    const int a;
+    ///    const int *a;
+    /// \endcode
+    CS_West,
+    /// Change type decorations to be East/Right const.
+    /// \code
+    ///    int const a;
+    ///    int const *a;
+    /// \endcode
+    CS_East
+  };
+
+  /// Different ways to arrange const.
+  ConstPlacementStyle ConstPlacement;
+
   /// Different ways to break inheritance list.
   enum BreakInheritanceListStyle {
     /// Break inheritance list before the colon and after the commas.
@@ -2317,6 +2342,7 @@
            BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations &&
            BreakStringLiterals == R.BreakStringLiterals &&
            ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas &&
+           ConstPlacement == R.ConstPlacement &&
            BreakInheritanceList == R.BreakInheritanceList &&
            ConstructorInitializerAllOnOneLineOrOnePerLine ==
                R.ConstructorInitializerAllOnOneLineOrOnePerLine &&
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -356,6 +356,10 @@
         foo();
       } while(1);
 
+- Option ``ConstPlacement`` has been added in order to auto-arrange the positioning of `const`
+  in variable and parameter declarations to be either ``Right/East`` const or ``Left/West`` 
+  const .
+
 libclang
 --------
 
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -1470,6 +1470,37 @@
     namespace Extra {
     }}}
 
+**ConstPlacement** (``ConstPlacementStyle``)
+  Different ways to arrange const.
+
+  Possible values:
+
+  * ``CS_Leave`` (in configuration: ``Leave``)
+    Don't change const to either East/Right const or West/Left const.
+
+    .. code-block:: c++
+
+       int const a;
+       const int *a;
+
+  * ``CS_West`` (in configuration: ``West``)
+    Change type decorations to be West/Left const.
+
+    .. code-block:: c++
+
+       const int a;
+       const int *a;
+
+  * ``CS_East`` (in configuration: ``East``)
+    Change type decorations to be East/Right const.
+
+    .. code-block:: c++
+
+       int const a;
+       int const *a;
+
+
+
 **ConstructorInitializerAllOnOneLineOrOnePerLine** (``bool``)
   If the constructor initializers don't fit on a line, put each
   initializer on its own line.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to