MyDeveloperDay updated this revision to Diff 365765.
MyDeveloperDay added a comment.
- Remove the "CV" from the options
- Add support for ordering static/inline/const/volatile around the type,
allowing for mixing both left and right alignment at the same time
QualifierAlignment: Custom
QualifierOrder: [ "inline", "static", "volatile", "type", const ]
- Add "Custom" format to allow use of QualifierOrder (may want to change the
`Qualifier` name as its now both Qualifiers and Specifiers)
- Add Parse errors and test to check for unsupported specifiers/qualifiers,
duplicates,missing 'type' field and empty Order lists
- Add Defaults for Left and Right Alignment (may need to discuss suitable
defaults).
- Removal of excess braces (if only clang-format could do that for us!!)
NOTE: Known issue:
Despite the clang-format file being already format correctly, I seem to be
getting replacements: (e.g.)
For the following example
inline volatile int const c;
with the .clang-format file of:
---
Language: Cpp
BasedOnStyle: LLVM
QualifierAlignment: Custom
QualifierOrder: [ inline, static, volatile, type, const ]
I get the following:
$ clang-format -n test1.cpp
test1.cpp:1:1: warning: code should be clang-formatted
[-Wclang-format-violations]
inline volatile int const c;
^
I believe caused by the existence of replacements that effectively do nothing.
$ clang-format test1.cpp --output-replacements-xml
<?xml version='1.0'?>
<replacements xml:space='preserve' incomplete_format='false'>
<replacement offset='0' length='15'>inline volatile</replacement>
</replacements>
Its likely I am switching the order from:
inline volatile int const c;
to
volatile inline int const c;
and then back to
inline volatile int const c;
Which I think likely becomes a single `inline volatile` replacement. This
could in theory be a corner case for tooling::Replacements but I need to dig in
more, its more likely an artefact of the reversing the `LeftOrder` rather
keeping the Left order and not pushing specifiers though other specifiers that
are more `LeftMost` combined with each specifier/qualifier being handled in its
own pass.
I'll look into that next, but wanted to park the current implementation as
functionally this is a significant step closer.
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/Format.cpp
clang/lib/Format/QualifierAlignmentFixer.cpp
clang/lib/Format/QualifierAlignmentFixer.h
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
@@ -18180,6 +18180,10 @@
EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \
EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!"
+#define FAIL_PARSE(TEXT, FIELD, VALUE) \
+ EXPECT_NE(0, parseConfiguration(TEXT, &Style).value()); \
+ EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!"
+
TEST_F(FormatTest, ParsesConfigurationBools) {
FormatStyle Style = {};
Style.Language = FormatStyle::LK_Cpp;
@@ -18277,6 +18281,26 @@
CHECK_PARSE("ContinuationIndentWidth: 11", ContinuationIndentWidth, 11u);
CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$");
+ Style.QualifierAlignment = FormatStyle::QAS_Right;
+ CHECK_PARSE("QualifierAlignment: Leave", QualifierAlignment,
+ FormatStyle::QAS_Leave);
+ CHECK_PARSE("QualifierAlignment: Right", QualifierAlignment,
+ FormatStyle::QAS_Right);
+ CHECK_PARSE("QualifierAlignment: Left", QualifierAlignment,
+ FormatStyle::QAS_Left);
+ CHECK_PARSE("QualifierAlignment: Custom", QualifierAlignment,
+ FormatStyle::QAS_Custom);
+
+ Style.QualifierOrder.clear();
+ CHECK_PARSE("QualifierOrder: [ const, volatile, type ]", QualifierOrder,
+ std::vector<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);
@@ -18918,8 +18942,6 @@
EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language);
}
-#undef CHECK_PARSE
-
TEST_F(FormatTest, UsesLanguageForBasedOnStyle) {
FormatStyle Style = {};
Style.Language = FormatStyle::LK_JavaScript;
@@ -22220,6 +22242,494 @@
"}";
EXPECT_EQ(Code, format(Code, Style));
}
+
+TEST_F(FormatTest, LeftRightQualifier) {
+ FormatStyle Style = getLLVMStyle();
+
+ // keep the const style unaltered
+ verifyFormat("const int a;", Style);
+ verifyFormat("const int *a;", Style);
+ verifyFormat("const int &a;", Style);
+ verifyFormat("const int &&a;", Style);
+ verifyFormat("int const b;", Style);
+ verifyFormat("int const *b;", Style);
+ verifyFormat("int const &b;", Style);
+ verifyFormat("int const &&b;", Style);
+ verifyFormat("int const *b const;", Style);
+ verifyFormat("int *const c;", Style);
+
+ verifyFormat("const Foo a;", Style);
+ verifyFormat("const Foo *a;", Style);
+ verifyFormat("const Foo &a;", Style);
+ verifyFormat("const Foo &&a;", Style);
+ verifyFormat("Foo const b;", Style);
+ verifyFormat("Foo const *b;", Style);
+ verifyFormat("Foo const &b;", Style);
+ verifyFormat("Foo const &&b;", Style);
+ verifyFormat("Foo const *b const;", Style);
+
+ verifyFormat("LLVM_NODISCARD const int &Foo();", Style);
+ verifyFormat("LLVM_NODISCARD int const &Foo();", Style);
+
+ verifyFormat("volatile const int *restrict;", Style);
+ verifyFormat("const volatile int *restrict;", Style);
+ verifyFormat("const int volatile *restrict;", Style);
+}
+
+TEST_F(FormatTest, RightQualifier) {
+ FormatStyle Style = getLLVMStyle();
+ Style.QualifierAlignment = FormatStyle::QAS_Right;
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("type");
+ Style.QualifierOrder.push_back("const");
+ Style.QualifierOrder.push_back("volatile");
+
+ verifyFormat("int const a;", Style);
+ verifyFormat("int const *a;", Style);
+ verifyFormat("int const &a;", Style);
+ verifyFormat("int const &&a;", Style);
+ verifyFormat("int const b;", Style);
+ verifyFormat("int const *b;", Style);
+ verifyFormat("int const &b;", Style);
+ verifyFormat("int const &&b;", Style);
+ verifyFormat("int const *b const;", Style);
+ verifyFormat("int *const c;", Style);
+
+ verifyFormat("Foo const a;", Style);
+ verifyFormat("Foo const *a;", Style);
+ verifyFormat("Foo const &a;", Style);
+ verifyFormat("Foo const &&a;", Style);
+ verifyFormat("Foo const b;", Style);
+ verifyFormat("Foo const *b;", Style);
+ verifyFormat("Foo const &b;", Style);
+ verifyFormat("Foo const &&b;", Style);
+ verifyFormat("Foo const *b const;", Style);
+ verifyFormat("Foo *const b;", Style);
+ verifyFormat("Foo const *const b;", Style);
+ verifyFormat("auto const v = get_value();", Style);
+ verifyFormat("long long const &a;", Style);
+ verifyFormat("unsigned char const *a;", Style);
+ verifyFormat("int main(int const argc, char const *const *const argv)",
+ Style);
+
+ verifyFormat("LLVM_NODISCARD int const &Foo();", Style);
+ verifyFormat("SourceRange getSourceRange() const override LLVM_READONLY",
+ Style);
+ verifyFormat("void foo() const override;", Style);
+ verifyFormat("void foo() const override LLVM_READONLY;", Style);
+ verifyFormat("void foo() const final;", Style);
+ verifyFormat("void foo() const final LLVM_READONLY;", Style);
+ verifyFormat("void foo() const LLVM_READONLY;", Style);
+
+ verifyFormat(
+ "template <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(FormatTest, LeftQualifier) {
+ FormatStyle Style = getLLVMStyle();
+ Style.QualifierAlignment = FormatStyle::QAS_Left;
+
+ verifyFormat("const int a;", Style);
+ verifyFormat("const int *a;", Style);
+ verifyFormat("const int &a;", Style);
+ verifyFormat("const int &&a;", Style);
+ verifyFormat("const int b;", Style);
+ verifyFormat("const int *b;", Style);
+ verifyFormat("const int &b;", Style);
+ verifyFormat("const int &&b;", Style);
+ verifyFormat("const int *b const;", Style);
+ verifyFormat("int *const c;", Style);
+
+ verifyFormat("const Foo a;", Style);
+ verifyFormat("const Foo *a;", Style);
+ verifyFormat("const Foo &a;", Style);
+ verifyFormat("const Foo &&a;", Style);
+ verifyFormat("const Foo b;", Style);
+ verifyFormat("const Foo *b;", Style);
+ verifyFormat("const Foo &b;", Style);
+ verifyFormat("const Foo &&b;", Style);
+ verifyFormat("const Foo *b const;", Style);
+ verifyFormat("Foo *const b;", Style);
+ verifyFormat("const Foo *const b;", Style);
+
+ verifyFormat("LLVM_NODISCARD const int &Foo();", Style);
+
+ verifyFormat("const char a[];", Style);
+ verifyFormat("const auto v = get_value();", Style);
+ verifyFormat("const long long &a;", Style);
+ verifyFormat("const unsigned char *a;", Style);
+ verifyFormat("const unsigned char *a;", "unsigned char const *a;", Style);
+ verifyFormat("const Foo<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(FormatTest, ConstVolatileQualifiersOrder) {
+ FormatStyle Style = getLLVMStyle();
+ Style.QualifierAlignment = FormatStyle::QAS_Left;
+
+ // the Default
+ EXPECT_EQ(Style.QualifierOrder.size(), 5);
+ EXPECT_EQ(Style.QualifierOrder[0], "inline");
+ EXPECT_EQ(Style.QualifierOrder[1], "static");
+ EXPECT_EQ(Style.QualifierOrder[2], "const");
+ EXPECT_EQ(Style.QualifierOrder[3], "volatile");
+ EXPECT_EQ(Style.QualifierOrder[4], "type");
+
+ verifyFormat("const volatile int a;", "const volatile int a;", Style);
+ verifyFormat("const volatile int a;", "volatile const int a;", Style);
+ verifyFormat("const volatile int a;", "int const volatile a;", Style);
+ verifyFormat("const volatile int a;", "int volatile const a;", Style);
+ verifyFormat("const volatile int a;", "const int volatile a;", Style);
+
+ Style.QualifierAlignment = FormatStyle::QAS_Right;
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("type");
+ Style.QualifierOrder.push_back("const");
+ Style.QualifierOrder.push_back("volatile");
+
+ verifyFormat("int const volatile a;", "const volatile int a;", Style);
+ verifyFormat("int const volatile a;", "volatile const int a;", Style);
+ verifyFormat("int const volatile a;", "int const volatile a;", Style);
+ verifyFormat("int const volatile a;", "int volatile const a;", Style);
+ verifyFormat("int const volatile a;", "const int volatile a;", Style);
+
+ Style.QualifierAlignment = FormatStyle::QAS_Left;
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("volatile");
+ Style.QualifierOrder.push_back("const");
+ Style.QualifierOrder.push_back("type");
+
+ verifyFormat("volatile const int a;", "const volatile int a;", Style);
+ verifyFormat("volatile const int a;", "volatile const int a;", Style);
+ verifyFormat("volatile const int a;", "int const volatile a;", Style);
+ verifyFormat("volatile const int a;", "int volatile const a;", Style);
+ verifyFormat("volatile const int a;", "const int volatile a;", Style);
+
+ Style.QualifierAlignment = FormatStyle::QAS_Right;
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("type");
+ Style.QualifierOrder.push_back("volatile");
+ Style.QualifierOrder.push_back("const");
+
+ verifyFormat("int volatile const a;", "const volatile int a;", Style);
+ verifyFormat("int volatile const a;", "volatile const int a;", Style);
+ verifyFormat("int volatile const a;", "int const volatile a;", Style);
+ verifyFormat("int volatile const a;", "int volatile const a;", Style);
+ verifyFormat("int volatile const a;", "const int volatile a;", Style);
+
+ Style.QualifierAlignment = FormatStyle::QAS_Custom;
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("type");
+ Style.QualifierOrder.push_back("volatile");
+ Style.QualifierOrder.push_back("const");
+
+ verifyFormat("int volatile const a;", "const volatile int a;", Style);
+ verifyFormat("int volatile const a;", "volatile const int a;", Style);
+ verifyFormat("int volatile const a;", "int const volatile a;", Style);
+ verifyFormat("int volatile const a;", "int volatile const a;", Style);
+ verifyFormat("int volatile const a;", "const int volatile a;", Style);
+}
+
+TEST_F(FormatTest, FailQualifierInvalidConfiguration) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ FAIL_PARSE("QualifierAlignment: Custom\n"
+ "QualifierOrder: [const, volatile, apples, type]",
+ QualifierOrder,
+ std::vector<std::string>({"const", "volatile", "apples", "type"}));
+}
+
+TEST_F(FormatTest, FailQualifierDuplicateConfiguration) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ FAIL_PARSE("QualifierAlignment: Custom\n"
+ "QualifierOrder: [const, volatile, const, type]",
+ QualifierOrder,
+ std::vector<std::string>({"const", "volatile", "const", "type"}));
+}
+
+TEST_F(FormatTest, FailQualifierMissingType) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ FAIL_PARSE("QualifierAlignment: Custom\n"
+ "QualifierOrder: [const, volatile ]",
+ QualifierOrder,
+ std::vector<std::string>({
+ "const",
+ "volatile",
+ }));
+}
+
+TEST_F(FormatTest, FailQualifierEmptyOrder) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ FAIL_PARSE("QualifierAlignment: Custom\nQualifierOrder: []", QualifierOrder,
+ std::vector<std::string>({}));
+}
+
+TEST_F(FormatTest, FailQualifierMissingOrder) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ FAIL_PARSE("QualifierAlignment: Custom", QualifierOrder,
+ std::vector<std::string>());
+}
+
+TEST_F(FormatTest, QualifierLeft) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ CHECK_PARSE("QualifierAlignment: Left", QualifierOrder,
+ std::vector<std::string>({"const", "volatile", "type"}));
+}
+
+TEST_F(FormatTest, QualifierRight) {
+ FormatStyle Style = {};
+ Style.Language = FormatStyle::LK_Cpp;
+ CHECK_PARSE("QualifierAlignment: Right", QualifierOrder,
+ std::vector<std::string>({"type", "const", "volatile"}));
+}
+
+TEST_F(FormatTest, QualifiersCustomOrder) {
+ FormatStyle Style = getLLVMStyle();
+ Style.QualifierAlignment = FormatStyle::QAS_Left;
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("inline");
+ Style.QualifierOrder.push_back("constexpr");
+ Style.QualifierOrder.push_back("static");
+ Style.QualifierOrder.push_back("const");
+ Style.QualifierOrder.push_back("volatile");
+ Style.QualifierOrder.push_back("type");
+
+ verifyFormat("const volatile int a;", "const volatile int a;", Style);
+ verifyFormat("const volatile int a;", "volatile const int a;", Style);
+ verifyFormat("const volatile int a;", "int const volatile a;", Style);
+ verifyFormat("const volatile int a;", "int volatile const a;", Style);
+ verifyFormat("const volatile int a;", "const int volatile a;", Style);
+
+ verifyFormat("static const volatile int a;", "const static int volatile a;",
+ Style);
+ verifyFormat("inline static const volatile int a;",
+ "const static inline int volatile a;", Style);
+
+ verifyFormat("constexpr static int a;", "static constexpr int a;", Style);
+ verifyFormat("constexpr static int A;", "static constexpr int A;", Style);
+ verifyFormat("constexpr static int Bar;", "static constexpr int Bar;", Style);
+ verifyFormat("constexpr static LPINT Bar;", "static constexpr LPINT Bar;",
+ Style);
+ verifyFormat("const const int a;", "const int const a;", Style);
+}
+
} // namespace
} // namespace format
} // namespace clang
Index: clang/tools/clang-format/ClangFormat.cpp
===================================================================
--- clang/tools/clang-format/ClangFormat.cpp
+++ clang/tools/clang-format/ClangFormat.cpp
@@ -19,6 +19,7 @@
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
@@ -104,6 +105,13 @@
"SortIncludes style flag"),
cl::cat(ClangFormatCategory));
+static cl::opt<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 @@
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: clang/lib/Format/QualifierAlignmentFixer.h
===================================================================
--- /dev/null
+++ clang/lib/Format/QualifierAlignmentFixer.h
@@ -0,0 +1,44 @@
+//===--- QualifierAlignmentFixer.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file declares QualifierAlignmentFixer, a TokenAnalyzer that
+/// enforces either east or west const depending on the style.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_FORMAT_QUALIFIERALIGNMENTFIXER_H
+#define LLVM_CLANG_LIB_FORMAT_QUALIFIERALIGNMENTFIXER_H
+
+#include "TokenAnalyzer.h"
+
+namespace clang {
+namespace format {
+
+class QualifierAlignmentFixer : public TokenAnalyzer {
+ std::string Qualifier;
+ FormatStyle::QualifierAlignmentStyle Alignment;
+
+public:
+ QualifierAlignmentFixer(
+ const Environment &Env, const FormatStyle &Style,
+ const std::string &Qualifier,
+ const FormatStyle::QualifierAlignmentStyle &Alignment);
+
+ std::pair<tooling::Replacements, unsigned>
+ analyze(TokenAnnotator &Annotator,
+ SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+ FormatTokenLexer &Tokens) override;
+
+ static tok::TokenKind getTokenFromQualifier(const std::string &Qualifier);
+};
+
+} // end namespace format
+} // end namespace clang
+
+#endif
Index: clang/lib/Format/QualifierAlignmentFixer.cpp
===================================================================
--- /dev/null
+++ clang/lib/Format/QualifierAlignmentFixer.cpp
@@ -0,0 +1,333 @@
+//===--- QualifierAlignmentFixer.cpp ---------------------------*- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements QualifierAlignmentFixer, a TokenAnalyzer that
+/// enforces either east or west const depending on the style.
+///
+//===----------------------------------------------------------------------===//
+
+#include "QualifierAlignmentFixer.h"
+#include "FormatToken.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Regex.h"
+
+#include <algorithm>
+
+namespace clang {
+namespace format {
+
+static void replaceToken(const SourceManager &SourceMgr,
+ tooling::Replacements &Fixes,
+ const CharSourceRange &Range, std::string NewText) {
+ auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
+ auto Err = Fixes.add(Replacement);
+
+ if (Err)
+ llvm::errs() << "Error while rearranging qualifier : "
+ << llvm::toString(std::move(Err)) << "\n";
+}
+
+static void removeToken(const SourceManager &SourceMgr,
+ tooling::Replacements &Fixes,
+ const FormatToken *First) {
+ auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+ First->Tok.getEndLoc());
+ replaceToken(SourceMgr, Fixes, Range, "");
+}
+
+static void insertQualifierAfter(const SourceManager &SourceMgr,
+ tooling::Replacements &Fixes,
+ const FormatToken *First,
+ const std::string &Qualifier) {
+ FormatToken *Next = First->Next;
+ if (!Next)
+ return;
+ auto Range = CharSourceRange::getCharRange(
+ Next->getStartOfNonWhitespace(), Next->Next->getStartOfNonWhitespace());
+
+ std::string NewText = " " + Qualifier + " ";
+ NewText += Next->TokenText;
+
+ replaceToken(SourceMgr, Fixes, Range, NewText);
+}
+
+static void insertQualifierBefore(const SourceManager &SourceMgr,
+ tooling::Replacements &Fixes,
+ const FormatToken *First,
+ const std::string &Qualifier) {
+ auto Range = CharSourceRange::getCharRange(
+ First->getStartOfNonWhitespace(), First->Next->getStartOfNonWhitespace());
+
+ std::string NewText = " " + Qualifier + " ";
+ NewText += First->TokenText;
+
+ replaceToken(SourceMgr, Fixes, Range, NewText);
+}
+
+static void rotateTokens(const SourceManager &SourceMgr,
+ tooling::Replacements &Fixes, const FormatToken *First,
+ const FormatToken *Last, bool Left) {
+ auto *End = Last;
+ auto *Begin = First;
+ if (!Left) {
+ End = Last->Next;
+ Begin = First->Next;
+ }
+
+ std::string NewText;
+ // If we are rotating to the left we move the Last token to the front.
+ if (Left) {
+ NewText += Last->TokenText;
+ NewText += " ";
+ }
+
+ // Then move through the other tokens.
+ auto *Tok = Begin;
+ while (Tok != End) {
+ if (!NewText.empty())
+ NewText += " ";
+
+ NewText += Tok->TokenText;
+ Tok = Tok->Next;
+ }
+
+ // If we are rotating to the right we move the first token to the back.
+ if (!Left) {
+ NewText += " ";
+ NewText += First->TokenText;
+ }
+
+ auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
+ Last->Tok.getEndLoc());
+
+ replaceToken(SourceMgr, Fixes, Range, NewText);
+}
+
+static bool isQualifierOrType(const FormatToken *Tok) {
+ return Tok && (Tok->isSimpleTypeSpecifier() ||
+ Tok->isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_auto,
+ tok::kw_static, tok::kw_inline, tok::kw_constexpr,
+ tok::kw_restrict));
+}
+
+// If a token is an identifier and it's upper case, it could
+// be a macro and hence we need to be able to ignore it.
+static bool isPossibleMacro(const FormatToken *Tok) {
+ if (!Tok)
+ return false;
+ if (!Tok->is(tok::identifier))
+ return false;
+ if (Tok->TokenText.upper() == Tok->TokenText.str())
+ return true;
+ return false;
+}
+
+static FormatToken *analyzeRight(const SourceManager &SourceMgr,
+ const AdditionalKeywords &Keywords,
+ tooling::Replacements &Fixes, FormatToken *Tok,
+ const std::string &Qualifier,
+ tok::TokenKind QualifierType) {
+ // We only need to think about streams that begin with const.
+ if (!Tok->is(QualifierType))
+ return Tok;
+ // Don't concern yourself if nothing follows const.
+ if (!Tok->Next)
+ return Tok;
+ if (isPossibleMacro(Tok->Next))
+ return Tok;
+ FormatToken *Const = Tok;
+
+ FormatToken *Qual = Tok->Next;
+ FormatToken *LastQual = Qual;
+ while (Qual && isQualifierOrType(Qual)) {
+ LastQual = Qual;
+ Qual = Qual->Next;
+ }
+ if (LastQual && Qual != LastQual) {
+ rotateTokens(SourceMgr, Fixes, Const, LastQual, /*Left=*/false);
+ Tok = LastQual;
+ } else if (Tok->startsSequence(QualifierType, tok::identifier,
+ TT_TemplateOpener)) {
+ // Read from the TemplateOpener to
+ // TemplateCloser as in const ArrayRef<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.
+ insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier);
+ removeToken(SourceMgr, Fixes, Tok);
+ return Tok;
+ }
+ } else if (Tok->startsSequence(QualifierType, tok::identifier)) {
+ FormatToken *Next = Tok->Next;
+ // The case `const Foo` -> `Foo const`
+ // The case `const Foo *` -> `Foo const *`
+ // The case `const Foo &` -> `Foo const &`
+ // The case `const Foo &&` -> `Foo const &&`
+ // The case `const std::Foo &&` -> `std::Foo const &&`
+ // The case `const std::Foo<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, Const);
+ }
+ return Next;
+ }
+ }
+
+ return Tok;
+}
+
+static FormatToken *analyzeLeft(const SourceManager &SourceMgr,
+ const AdditionalKeywords &Keywords,
+ tooling::Replacements &Fixes, FormatToken *Tok,
+ const std::string &Qualifier,
+ tok::TokenKind QualifierType) {
+ // if Tok is an identifier and possibly a macro then don't convert
+ if (isPossibleMacro(Tok))
+ return Tok;
+
+ FormatToken *Qual = Tok;
+ FormatToken *LastQual = Qual;
+ while (Qual && isQualifierOrType(Qual)) {
+ LastQual = Qual;
+ Qual = Qual->Next;
+ if (Qual && Qual->is(QualifierType))
+ break;
+ }
+ if (LastQual && Qual != LastQual && Qual->is(QualifierType)) {
+ rotateTokens(SourceMgr, Fixes, Tok, Qual, /*Left=*/true);
+ Tok = Qual->Next;
+ } else if (Tok->startsSequence(tok::identifier, QualifierType)) {
+ if (Tok->Next->Next && Tok->Next->Next->isOneOf(tok::identifier, tok::star,
+ tok::amp, tok::ampamp)) {
+ // Don't swap `::iterator const` to `::const iterator`.
+ if (!Tok->Previous ||
+ (Tok->Previous && !Tok->Previous->is(tok::coloncolon)))
+ rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true);
+ }
+ }
+ if (Tok->is(TT_TemplateOpener) && Tok->Next &&
+ (Tok->Next->is(tok::identifier) || Tok->Next->isSimpleTypeSpecifier()) &&
+ Tok->Next->Next && Tok->Next->Next->is(QualifierType)) {
+ rotateTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next, /*Left=*/true);
+ }
+ if (Tok->startsSequence(tok::identifier) && Tok->Next) {
+ if (Tok->Previous &&
+ Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)) {
+ return Tok;
+ }
+ FormatToken *Next = Tok->Next;
+ // The case `std::Foo<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.
+ removeToken(SourceMgr, Fixes, Next);
+ insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
+ return Next;
+ }
+ }
+ if (Next && Next->Next &&
+ Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) {
+ if (Next->is(QualifierType)) {
+ // Remove the const.
+ removeToken(SourceMgr, Fixes, Next);
+ insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
+ return Next;
+ }
+ }
+ }
+ return Tok;
+}
+
+tok::TokenKind
+QualifierAlignmentFixer::getTokenFromQualifier(const std::string &Qualifier) {
+ // don't let 'type' be an indentifier steal typeof token
+ return llvm::StringSwitch<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);
+}
+
+QualifierAlignmentFixer::QualifierAlignmentFixer(
+ const Environment &Env, const FormatStyle &Style,
+ const std::string &Qualifier,
+ const FormatStyle::QualifierAlignmentStyle &Alignment)
+ : TokenAnalyzer(Env, Style), Qualifier(Qualifier), Alignment(Alignment) {}
+
+std::pair<tooling::Replacements, unsigned> QualifierAlignmentFixer::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 (Alignment == FormatStyle::QAS_Right)
+ Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier,
+ QualifierToken);
+ else if (Alignment == FormatStyle::QAS_Left)
+ Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier,
+ QualifierToken);
+ }
+ }
+ return {Fixes, 0};
+}
+} // namespace format
+} // namespace clang
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -19,6 +19,7 @@
#include "FormatInternal.h"
#include "FormatTokenLexer.h"
#include "NamespaceEndCommentsFixer.h"
+#include "QualifierAlignmentFixer.h"
#include "SortJavaScriptImports.h"
#include "TokenAnalyzer.h"
#include "TokenAnnotator.h"
@@ -126,6 +127,16 @@
}
};
+template <>
+struct ScalarEnumerationTraits<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);
@@ -632,6 +643,22 @@
IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals);
IO.mapOptional("ColumnLimit", Style.ColumnLimit);
IO.mapOptional("CommentPragmas", Style.CommentPragmas);
+ IO.mapOptional("QualifierAlignment", Style.QualifierAlignment);
+
+ // Default Order for Left/Right based Qualifier alignment.
+ if (Style.QualifierAlignment == FormatStyle::QAS_Right) {
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("type");
+ Style.QualifierOrder.push_back("const");
+ Style.QualifierOrder.push_back("volatile");
+ } else if (Style.QualifierAlignment == FormatStyle::QAS_Left) {
+ Style.QualifierOrder.clear();
+ Style.QualifierOrder.push_back("const");
+ Style.QualifierOrder.push_back("volatile");
+ Style.QualifierOrder.push_back("type");
+ }
+ IO.mapOptional("QualifierOrder", Style.QualifierOrder);
+
IO.mapOptional("CompactNamespaces", Style.CompactNamespaces);
IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine",
Style.ConstructorInitializerAllOnOneLineOrOnePerLine);
@@ -863,6 +890,14 @@
return "Unsuitable";
case ParseError::BinPackTrailingCommaConflict:
return "trailing comma insertion cannot be used with bin packing";
+ case ParseError::InvalidQualifierSpecified:
+ return "Invalid qualifier specified in QualifierOrder";
+ case ParseError::DuplicateQualifierSpecified:
+ return "Duplicate qualifier specified in QualfierOrder";
+ case ParseError::MissingQualifierType:
+ return "Missing type in QualfierOrder";
+ case ParseError::MissingQualifierOrder:
+ return "Missing QualfierOrder";
}
llvm_unreachable("unexpected parse error");
}
@@ -1038,6 +1073,16 @@
LLVMStyle.ConstructorInitializerIndentWidth = 4;
LLVMStyle.ContinuationIndentWidth = 4;
LLVMStyle.Cpp11BracedListStyle = true;
+
+ // Off by default Qualifier ordering
+ LLVMStyle.QualifierAlignment = FormatStyle::QAS_Leave;
+ // Default 'Left' qualifier order but off by default.
+ LLVMStyle.QualifierOrder.push_back("inline");
+ LLVMStyle.QualifierOrder.push_back("static");
+ LLVMStyle.QualifierOrder.push_back("const");
+ LLVMStyle.QualifierOrder.push_back("volatile");
+ LLVMStyle.QualifierOrder.push_back("type");
+
LLVMStyle.DeriveLineEnding = true;
LLVMStyle.DerivePointerAlignment = false;
LLVMStyle.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never;
@@ -1471,6 +1516,32 @@
return true;
}
+ParseError validateQualifierOrder(FormatStyle *Style) {
+ // If its empty then it means don't do anything.
+ if (Style->QualifierOrder.empty())
+ return ParseError::MissingQualifierOrder;
+
+ // Ensure the list contains only currently valid qualifiers.
+ for (const auto &Qualifier : Style->QualifierOrder) {
+ if (Qualifier == "type")
+ continue;
+ auto token = QualifierAlignmentFixer::getTokenFromQualifier(Qualifier);
+ if (token == tok::identifier)
+ return ParseError::InvalidQualifierSpecified;
+ }
+ // Ensure the list is unqiue (no duplicates).
+ std::set<std::string> UniqueQualifiers(Style->QualifierOrder.begin(),
+ Style->QualifierOrder.end());
+ if (Style->QualifierOrder.size() != UniqueQualifiers.size())
+ return ParseError::DuplicateQualifierSpecified;
+
+ auto type = std::find(Style->QualifierOrder.begin(),
+ Style->QualifierOrder.end(), "type");
+ if (type == Style->QualifierOrder.end())
+ return ParseError::MissingQualifierType;
+ return ParseError::Success;
+}
+
std::error_code parseConfiguration(llvm::MemoryBufferRef Config,
FormatStyle *Style, bool AllowUnknownOptions,
llvm::SourceMgr::DiagHandlerTy DiagHandler,
@@ -1532,6 +1603,8 @@
// See comment on FormatStyle::TSC_Wrapped.
return make_error_code(ParseError::BinPackTrailingCommaConflict);
}
+ if (Style->QualifierAlignment != FormatStyle::QAS_Leave)
+ return make_error_code(validateQualifierOrder(Style));
return make_error_code(ParseError::Success);
}
@@ -2816,6 +2889,34 @@
}
namespace internal {
+
+void PrepareLeftRightOrdering(const std::vector<std::string> &Order,
+ std::vector<std::string> &LeftOrder,
+ std::vector<std::string> &RightOrder) {
+
+ // Depending on the position of type in the order you need
+ // To iterate forward or backward through the order list as qualifier
+ // can push through each other.
+ auto type = std::find(Order.begin(), Order.end(), "type");
+ // The Order list must define the position of "type" to signify
+ assert(type != Order.end() && "QualifierOrder must contain type");
+ // Split the Order list by type and reverse the left side
+ bool left = true;
+ for (const auto &s : Order) {
+ if (s == "type") {
+ left = false;
+ continue;
+ }
+ if (left) {
+ LeftOrder.push_back(s);
+ } else {
+ RightOrder.push_back(s);
+ }
+ }
+ // Reverse the order for left aligned items.
+ std::reverse(LeftOrder.begin(), LeftOrder.end());
+}
+
std::pair<tooling::Replacements, unsigned>
reformat(const FormatStyle &Style, StringRef Code,
ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
@@ -2865,6 +2966,28 @@
});
}
+ if (Style.isCpp() && Style.QualifierAlignment != FormatStyle::QAS_Leave) {
+ std::vector<std::string> LeftOrder;
+ std::vector<std::string> RightOrder;
+ PrepareLeftRightOrdering(Style.QualifierOrder, LeftOrder, RightOrder);
+
+ // Handle the left and right Alignment Seperately
+ for (const auto &Qualifier : LeftOrder) {
+ Passes.emplace_back([&, Qualifier](const Environment &Env) {
+ return QualifierAlignmentFixer(Env, Expanded, Qualifier,
+ FormatStyle::QAS_Left)
+ .process();
+ });
+ }
+ for (const auto &Qualifier : RightOrder) {
+ Passes.emplace_back([&, Qualifier](const Environment &Env) {
+ return QualifierAlignmentFixer(Env, Expanded, Qualifier,
+ FormatStyle::QAS_Right)
+ .process();
+ });
+ }
+ }
+
if (Style.Language == FormatStyle::LK_JavaScript &&
Style.JavaScriptQuotes != FormatStyle::JSQS_Leave)
Passes.emplace_back([&](const Environment &Env) {
Index: clang/lib/Format/CMakeLists.txt
===================================================================
--- clang/lib/Format/CMakeLists.txt
+++ clang/lib/Format/CMakeLists.txt
@@ -15,6 +15,7 @@
UnwrappedLineFormatter.cpp
UnwrappedLineParser.cpp
UsingDeclarationsSorter.cpp
+ QualifierAlignmentFixer.cpp
WhitespaceManager.cpp
LINK_LIBS
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -40,7 +40,11 @@
Success = 0,
Error,
Unsuitable,
- BinPackTrailingCommaConflict
+ BinPackTrailingCommaConflict,
+ InvalidQualifierSpecified,
+ DuplicateQualifierSpecified,
+ MissingQualifierType,
+ MissingQualifierOrder
};
class ParseErrorCategory final : public std::error_category {
public:
@@ -1831,6 +1835,63 @@
/// \endcode
std::string CommentPragmas;
+ /// Different const/volatile qualifier alignment styles.
+ enum QualifierAlignmentStyle {
+ /// Don't change specifiers/qualifier to either Left or Right alignment
+ /// \code
+ /// int const a;
+ /// const int *a;
+ /// \endcode
+ QAS_Leave,
+ /// Change specifiers/qualifiers to be Left aligned.
+ /// \code
+ /// const int a;
+ /// const int *a;
+ /// \endcode
+ QAS_Left,
+ /// Change specifiers/qualifiers to be Right aligned.
+ /// \code
+ /// int const a;
+ /// int const *a;
+ /// \endcode
+ QAS_Right,
+ /// Change specifiers/qualifiers to be aligned based on QualfierOrder.
+ /// With:
+ /// \code{.yaml}
+ /// QualifierOrder: ['inline', 'static' , '<type>', 'const']
+ /// \endcode
+ ///
+ /// \code
+ ///
+ /// int const a;
+ /// int const *a;
+ /// \endcode
+ QAS_Custom
+ };
+
+ /// Different ways to arrange const/volatile qualifiers.
+ QualifierAlignmentStyle QualifierAlignment;
+
+ /// The Order in which the qualifiers appear.
+ /// Order is a an array can contain any of the following
+ ///
+ /// * const
+ /// * inline
+ /// * static
+ /// * constexpr
+ /// * volatile
+ /// * restrict
+ /// * type
+ ///
+ /// Note: it MUST contain 'type'.
+ /// Items to the left of type will be aligned in the order supplied.
+ /// Items to the right of type will be aligned in the order supplied.
+ ///
+ /// \code{.yaml}
+ /// QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ]
+ /// \endcode
+ std::vector<std::string> QualifierOrder;
+
/// Different ways to break inheritance list.
enum BreakInheritanceListStyle : unsigned char {
/// Break inheritance list before the colon and after the commas.
@@ -3422,6 +3483,8 @@
BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations &&
BreakStringLiterals == R.BreakStringLiterals &&
ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas &&
+ QualifierAlignment == R.QualifierAlignment &&
+ QualifierOrder == R.QualifierOrder &&
BreakInheritanceList == R.BreakInheritanceList &&
ConstructorInitializerAllOnOneLineOrOnePerLine ==
R.ConstructorInitializerAllOnOneLineOrOnePerLine &&
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -156,6 +156,16 @@
- Option ``AllowShortEnumsOnASingleLine: false`` has been improved, it now
correctly places the opening brace according to ``BraceWrapping.AfterEnum``.
+- Option ``QualifierAligment`` has been added in order to auto-arrange the
+ positioning of specifiers/qualifiers
+ `const` `volatile` `static` `inline` `constexpr` `restrict`
+ in variable and parameter declarations to be either ``Right`` aligned
+ or ``Left`` aligned or ``Custom`` using ``QualifierOrder``.
+
+- Option ``QualifierOrder`` has been added to allow the order
+ `const` `volatile` `static` `inline` `constexpr` `restrict`
+ to be controlled relative to the `<type>`.
+
libclang
--------
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -741,8 +741,7 @@
enum { A, B } myEnum;
false:
- enum
- {
+ enum {
A,
B
} myEnum;
@@ -3201,6 +3200,73 @@
+**QualifierAlignment** (``QualifierAlignmentStyle``)
+ Different ways to arrange const/volatile qualifiers.
+
+ Possible values:
+
+ * ``QAS_Leave`` (in configuration: ``Leave``)
+ Don't change specifiers/qualifier to either Left or Right alignment
+
+ .. code-block:: c++
+
+ int const a;
+ const int *a;
+
+ * ``QAS_Left`` (in configuration: ``Left``)
+ Change specifiers/qualifiers to be Left aligned.
+
+ .. code-block:: c++
+
+ const int a;
+ const int *a;
+
+ * ``QAS_Right`` (in configuration: ``Right``)
+ Change specifiers/qualifiers to be Right aligned.
+
+ .. code-block:: c++
+
+ int const a;
+ int const *a;
+
+ * ``QAS_Custom`` (in configuration: ``Custom``)
+ Change specifiers/qualifiers to be aligned based on QualfierOrder.
+ With:
+
+ .. code-block:: yaml
+
+ QualifierOrder: ['inline', 'static' , '<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.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits