Skip to content

Format Cpp2

Johel Ernesto Guerrero Peña edited this page Oct 9, 2024 · 13 revisions

Build instructions

See https://clang.llvm.org/get_started.html.

git clone --depth 1 --branch format-cpp2 https://github.com/JohelEGP/llvm-project.git
cmake \
  -B format-cpp2 \
  -S llvm-project/llvm \
  -G Ninja \
  -D CMAKE_BUILD_TYPE=Debug \
  -D CMAKE_BUILD_WITH_INSTALL_RPATH=ON \
  -D LLVM_BUILD_TOOLS=OFF \
  -D LLVM_ENABLE_DIA_SDK=OFF \
  -D LLVM_ENABLE_IDE=ON \
  -D LLVM_ENABLE_LIBPFM=OFF \
  -D LLVM_ENABLE_PIC=OFF \
  -D LLVM_ENABLE_PROJECTS="clang" \
  -D LLVM_ENABLE_RUNTIMES="" \
  -D LLVM_INCLUDE_BENCHMARKS=OFF \
  -D LLVM_INCLUDE_EXAMPLES=OFF \
  -D LLVM_INCLUDE_TESTS=OFF \
  -D LLVM_PARALLEL_COMPILE_JOBS=4 \
  -D LLVM_PARALLEL_LINK_JOBS=4 \
  -D LLVM_TARGETS_TO_BUILD=X86 \
  -D LLVM_VERSION_SUFFIX=""
cmake --build format-cpp2 --target clang-format -j4

Usage instructions

Just use Clang Format normally. There's no opt-in.

TODO

  • Add a GitHub Action to build and upload a portable clang-format binary.
  • Test CMake target FormatTests.
  • Add tests for Cpp2 to CMake target FormatTests.
  • Post the branch to https://reviews.llvm.org/.
  • Review all the style options for Cpp2.
  • Handle parsing failure more gracefully.

Sample

Here's my formatted Cpp2 test file and .clang-format configuration. Be warned, my configuration is themed around stuffing a lot into a single line.

Configuration
AccessModifierOffset: -2
AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
  Enabled: true
  AcrossEmptyLines: false
  AcrossComments: false
  AlignCompound: true
  PadOperators: false
AlignConsecutiveBitFields: Consecutive
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: None
AlignConsecutiveShortCaseStatements:
  Enabled: true
  AcrossEmptyLines: true
  AcrossComments: true
  AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
  Kind: Always
  OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Always
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: true
BinPackParameters: BinPack
BitFieldColonSpacing: Both
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Never
BreakAfterReturnType: Automatic
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: Allowed
BreakBeforeTernaryOperators: true
BreakBinaryOperations: RespectPrecedence
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: No
ColumnLimit: 120
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: Always
FixNamespaceComments: true
IncludeBlocks: Preserve
IncludeCategories:
  - Regex:      '<(experimental/)?[a-z_]+>' # C++ standard library
    Priority:   1
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: true
IntegerLiteralSeparator:
  Binary: 0
  Decimal: 0
  Hex: 0
KeepEmptyLines:
  AtEndOfFile: false
  AtStartOfBlock: false
  AtStartOfFile: false
LambdaBodyIndentation: Signature
Language: Cpp
LineEnding: LF
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PackConstructorInitializers: NextLine
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Custom
QualifierOrder: ["static", "friend", "inline", "constexpr", "const", "volatile", "type"]
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RequiresClausePosition: SingleLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 0
SkipMacroDefinitionBody: false
SortIncludes: CaseInsensitive
SortUsingDeclarations: LexicographicNumeric
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: true
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInLineCommentPrefix:
  Minimum: 1
  Maximum: 1
SpacesInParens: Never
SpacesInSquareBrackets: false
Standard: Latest
StatementMacros: ["BOOST_GEOMETRY_REGISTER_POINT_2D"]
TabWidth: 2
TypeNames: []
UseCRLF: false
UseTab: Never
Formatted Cpp2 test file
// Clang Format (and some highlighting) test cases.

// Cpp1 and Cpp2 next to each other
// to show degree of consistent formatting.

int x                = 0;
const int x          = 0;
const int* x         = 0;
int* const x         = 0;
const int* const x   = 0;
x: i32               = 0;
x: const i32         = 0;
x: * const i32       = 0;
x: const * i32       = 0;
x: const * const i32 = 0;

auto x        = 0;
auto* x       = 0;
const auto x  = 0;
const auto* x = 0;
x: _          = 0;
x := 0;
x: * _       = 0;
x: *         = 0;
x: const _   = 0;
x: const     = 0;
x: * const _ = 0;
x: * const   = 0;

auto x = "a"
         "b";
auto x = u8R"(a)"
         u8R"(b)";
x := "a"
     "b";
x := u8R"(a)"
     u8R"(b)";

void f() { }
f: () = { }

void f() { int x = 0; }
f: () = { x: i32 = 0; }

int f() { return 0; }
f: () -> i32 = { return 0; }
f: () -> i32 = 0;

auto f(const auto& x) { return x; }
f: (in x: _) -> _ = { return x; }
f: (in x: _, ) -> _ = { return x; }
f: (x) -> _ = x;
f: (x, ) -> _ = x;
f: (in_ref x) -> _ = x;

void f(const int) { }
void f(const int x) { }
f: (:i32)   = { } // Pending [grammar][].
f: (x: i32) = { }

void f(int) { }
f: (copy: i32) = { } // Pending [grammar][].

void f(buffer& x) { }
f: (inout x: buffer) = { }
f: (out x: buffer)   = { }

int&& f(int&&);
f: (move: i32) -> move i32; // Pending [grammar][].

auto&& f(uncvref_same<int>&& x) { return FWD(x); }
f: (forward x: i32) -> forward _     = forward x; // Pending [hsutter/cppfront#408][].
f: (forward x: i32) -> forward_ref _ = x;

void f(std::regular auto) { }
f: (copy _: _ is std::regular) = { }

void f() { std::regular auto x = 0; }
f: () = { x: _ is std::regular = 0; }

void f(int = 0) { }
f: (copy _: int = 0) = { }

void f() {
  g();
  g();
}
f: () = {
  g();
  g();
}

class t { };
class t final { };
t: type       = { }
t: final type = { }

class t {
  int x = 0;
};
t: type = {
  x: i32 = 0;
}

class t : base {
  t() : base{""} { }
};
t: type = {
  this: base = "";
}

class t {
private:
  void f() const { }
};
t: type = {
  private f: (this) = { }
}

class t {
private:
  void f() const { }

private:
  void f() const { }
};
t: type = {
  private f: (this) = { }
  private f: (this) = { }
}

class t {
  void f() const { g(); }
};
t: type = {
  f: (this) = g();
  f: (this) = { g(); }
}

class t {
  static void f() {
    g();
    g();
  }
};
t: type = {
  f: () = {
    g();
    g();
  }
}

class t {
  [[nodiscard]] bool operator==(const t&) const = default;
};
t: type = {
  operator==: (this, that) -> bool;
}

class t {
  virtual ~t() { }
  virtual void f() const = 0;
};
t: type = {
  operator=: (virtual move this) = { }
  f: (virtual this);
}
t: @interface type = {
  f: (this);
}

class t {
  int x = 0;
  [[nodiscard]] int get() const { return x; }
  void set(const int y) { x = y; }
};
t: type = {
  x: i32             = 0;
  get: (this) -> i32 = x;
  set: (inout this, y: i32) = x = y;
}

namespace ns { }
ns: namespace = { }

namespace ns {
int x = 0;
} // namespace ns
ns: namespace = {
x: i32 = 0;
} // namespace ns

namespace ns {
int x = 0;
void f() { }
} // namespace ns
ns: namespace = {
x: i32 = 0;
f: ()  = { }
} // namespace ns

namespace a {
namespace b {
  int x = 0;
} // namespace b
} // namespace a
a: namespace = {
b: namespace = {
  x: i32 = 0;
} // namespace b
} // namespace a

using t       = int;
namespace ns  = a::b;
const auto& x = y;
t: type       == i32;
ns: namespace == a::b;
x :== y;

void f() {
  [] { };
}
f: () = {
  :() = { };
}

void f() {
  [] { }();
  [] { return 0; }();
  [] { g(); }();
}
f: () = {
  :()      = { }();
  :() -> _ = { return 0; }();
  :() -> _ = 0;();
  :()      = { g(); }();
  :()      = g();();
}

void f() {
  [] {
    g();
    g()
  };
  [] {
    g();
    g()
  }();
}
f: () = {
  :() = {
    g();
    g();
  };
  :() = {
    g();
    g();
  }();
}

void f() {
  [] { [] { }; };
  [] { }([] { });
}
f: () = {
  :() = { :() = { }; };
  :() = { }(:() = { });
}
f: () = :() = { :() = { }; };
f: () = :() = { }(:() = { });

f: () = {
  // _primary-expression_.
  (:() = 0);
  (:() = 0;);
  (:() = 0;());
  (:() = 0, );
  (:() = 0;, );
  (:() = 0;(), );
  :int = 0;

  // _postfix-expression_.
  :int  = 0;();
  :()   = 0;();
  g(:() = 0);
  g(:() = 0;);
  g(:() = 0;());
  g(:() = 0, a);
  g(:() = 0;, a);
  g(:() = 0;(), a);
  g(:() = 0, );
  g(:() = 0;, );
  g(:() = 0;(), );
  g(:() = 0, a, );
  x[:() = 0];
  x[:() = 0;];
  x[:() = 0;()];
  x[:() = 0, a];
  x[:() = 0;, a];
  x[:() = 0;(), a];
  x[:() = 0, ];
  x[:() = 0;, ];
  x[:() = 0;(), ];
  x[:() = 0, a, ];
  x*;
  x&;
  x~;
  x$; // clang-format off
  x *;
  x &;
  x ~;
  x $; // clang-format on
  x*&~$;
  x$~&*;
  x.y;
  x..y;
  x ..< y;
  x ..= y;

  // _prefix-expression_.
  move x;  // Pending [hsutter/cppfront#408][].
  +move x; // Pending [hsutter/cppfront#408][].

  // _is-as-expression_.
  x is t;
  x is 0;
  x as t;
  is is * (); // Pending [grammar][].
  is is is;
  (is) is (is);
  (is) is (is)&;
  (is) is (is&);
  is is (); // Pending [grammar][].
  is as is;
  is as * ();   // Pending [grammar][].
  (is) as * (); // Pending [grammar][].
  :()      = 0; as t;
  :()      = 0; is t;
  x is :() = 0;
  x is :() = 0;();

  // Chained comparisons.
  min <= x < max;
  a == b == c;

  // _postfix-operator_ symbols as unary/binary.
  x * x;
  x* * x;
  x * 0;
  x* * 0;
  x * (x);
  x* * (x);
  x & x;
  x& & x;
  x & 0;
  x& & 0;
  x & (x);
  x& & (x);
  x~(x);
  x~~(x);
  x$(x);
}

void f() {
  if (a || b) g();
  if (a || b) { g(); }
}
f: () = {
  if (a || b) { g(); }
  if a || b { g(); }
  (x := 0) if a || b { g(); }
  (x := 0, ) if a || b { g(); }
}

void f() {
  if (a || b) g();
  else x = 0;

  if (a || b) {
    g();
  } else {
    x = 0;
  }
}
f: () = {
  if (a || b) {
    g();
  } else {
    x = 0;
  }

  if a || b {
    g();
  } else {
    x = 0;
  }
  (x := 0)
    if a || b {
      g();
    } else {
      x = 0;
    }
}

void f() {
  if (a || b) g();
  else if (true) x = 0;
  else x = 1;

  if (a || b) {
    g();
  } else if (true) {
    x = 0;
  } else {
    x = 1;
  }
}
f: () = {
  if (a || b) {
    g();
  } else if (true) {
    x = 0;
  } else {
    x = 1;
  }

  if a || b {
    g();
  } else if true {
    x = 0;
  } else {
    x = 1;
  }
}

void f() {
  if constexpr (a || b) g();
  else if constexpr (true) x = 0;
  else x = 1;

  if constexpr (a || b) {
    g();
  } else if constexpr (true) {
    x = 0;
  } else {
    x = 1;
  }
}
f: () = {
  if constexpr (a || b) {
    g();
  } else if constexpr (true) {
    x = 0;
  } else {
    x = 1;
  }

  if constexpr a || b {
    g();
  } else if constexpr true {
    x = 0;
  } else {
    x = 1;
  }
}

namespace ns {
using std::string;
using namespace std::chrono_literals;
} // namespace ns
ns: namespace = {
using std::string;
using std::chrono_literals::_;
} // namespace ns

f: () = {
  inspect x { is _ = g(); }
  (x := 0) inspect x { is _ = g(); }
  inspect constexpr x { y: is _ = g(); }
  inspect x {
    is t = g();
    is _ = g();
  }
  (x := 0)
    inspect x {
      is t = g();
      is _ = g();
    }
  inspect x {
    is t = {
      g();
      g();
    }
    is _ = g();
  }
  (inspect x -> i32 { is _ = 0; });
  (inspect x -> i32 {
    is i32 = 0;
    is _   = 1;
  });
  std::cout << inspect x -> i32 { is _ = 0; } << '\n';
  std::cout << inspect x -> i32 {
    is i32 = 0;
    is _   = 1;
  } << '\n';
}

f: () = {
  return (move x);
  return move x; // Pending [hsutter/cppfront#408][].
  return :int = 0;
  return :()  = 0;();
  return x&;
  (x := 0) return x&;

  break;
  break pre;
  continue;
  continue pre;
  (x := 0) continue pre;
}

void f() {
  while (a || b) g();
  while (a || b) { g(); }
}
f: () = {
  while (a || b) { g(); }
  while a || b { g(); }
  (x := 0) while a || b { g(); }
}

void f() {
  while (a || b) {
    g();
    g();
  }
}
f: () = {
  while (a || b) {
    g();
    g();
  }
  while a || b {
    g();
    g();
  }
}

void f() {
  while (a || b) {
    g();
    ++x;
  }
}
f: () = {
  while (a || b) next x++ { g(); }
  while a || b next x++ { g(); }
  while (a || b) next x++ {
    g();
    g();
  }
  while a || b next x++ {
    g();
    g();
  }
  (x := 0)
    while a || b next x++ {
      g();
      g();
    }
}

void f() {
  do { g(); } while (a || b);
}
f: () = {
  do { g(); } while (a || b);
  do { g(); } while a || b;
  (x := 0) do { g(); } while a || b;
}

void f() {
  do {
    g();
    g();
  } while (a || b);
}
f: () = {
  do {
    g();
    g();
  } while (a || b);
  do {
    g();
    g();
  } while a || b;
}

f: () = {
  do { g(); } while (a || b) next x++;
  do { g(); } while a || b next x++;
  do {
    g();
    g();
  } while (a || b) next x++;
  do {
    g();
    g();
  } while a || b next x++;
  (x := 0)
    do {
      g();
      g();
    } while a || b next x++;
}

void f() {
  for (const auto& e : r) g(e);
  for (const auto& e : r) { g(e); }
  for (const auto& x = 0; const auto& e : r) { g(e); }
  for (const auto& : std::views::iota(17, 30)) { }
  for (const auto& : std::ranges::subrange(v.begin(), v.end())) { }
}
f: () = {
  for r do (e) g(e);
  for r do (e) { g(e); }
  (x := 0) for r do (e) { g(e); }
  for 17 ..= 29 do (_) { }
  for v.begin() ..< v.end() do (_) { }
}

void f() {
  for (const auto& e : r) {
    g(e);
    ++x;
  }
}
f: () = {
  for r next x++ do (e) g(e);
  for r next x++ do (e) { g(e); }
  for r next x++ do (e) {
    g(e);
    g(e);
  }
  (x := 0)
    for r next x++ do (e) {
      g(e);
      g(e);
    }
}

void f() {
pre:
  while (a || b) g();
pre:
  do { g(); } while (a || b);
pre:
  for (const auto& e : r) g(e);
}
f: () = {
pre:
  while a || b { g(); }
pre:
  do { g(); } while a || b;
pre:
  for r do (e) g(e);
  (x := 0)
  pre:
    for r do (e) g(e);
}

void f() {
  { g(); }
  {
    g();
    g();
  }
}
f: () = {
  { g(); }
  (x := 0) { g(); }
  {
    g();
    g();
  }
  (x := 0) {
    g();
    g();
  }
}

f: () = {
  (x := 0) (x := 0) g();
  (x := 0) (:() = 0;());
  (x := 0) :int = 0;();
  (x := 0)
    :() = {
      g();
      g();
    };
  (x := 0) g(:() = 0;(), a);
  (x := 0) x$~&*;
  (x := 0) move x;       // Pending [hsutter/cppfront#408][].
  (x := 0) (is) as * (); // Pending [grammar][].
}

void f() { assert(a || b); }
f: () = { assert(a || b); }
f: () = { assert(a || b, ); }

void f() {
  [] { assert(a || b); };
}
f: () = {
  :() = { assert(a || b); };
}

f: () = {
  assert(a || b);
  assert(a || b);
}
f: () = {
  assert(:() -> _ = true;());
  assert(:() -> _ = { return true; }());
}

f: () = { assert(a || b, "!a && !b"); }
f: () = { assert(a || b, "!a && !b", ); }
f: () = { assert(a || b, "!(a)$ && !(b)$"); }

void f() noexcept { }
f: () throws = { }

void f() requires a { }
void f() requires a || b { }
void f() requires(a || b) { }
f: () requires a       = { }
f: () requires a || b  = { }
f: () requires(a || b) = { }

void f() requires([] { g(); }()) { }
f: () requires :() = { g(); }() = { }
f: () requires :() = g();() = { }

void f() requires([] {
  g();
  g();
}()) { }
f: () requires :() = {
  g();
  g();
}() = { }

void f() //
  requires a || b {
  g();
}
f: () //
  requires a || b = {
  g();
}

f: () pre(true)                          = { }
f: () pre(:() -> _ = { })                = { }
f: () pre(:() -> _ = true;())            = { }
f: () pre(:() -> _ = { return true; }()) = { }
f: ()
  pre(true)
  post(true)
= { }
f: ()
  pre(:() = {
    g();
    g();
  }())
= { }

f: @meta ()    = { }
f: @meta<T> () = { }
f: @meta<T, > () = { }
f: @meta <T: type> () = { }
f: @meta <T: type, > ()  = { }
f: @meta <T, T: type> () = { }

f: (x, move y: t) throws -> void = g();
f: <T, V: _> (x, move y: t) throws -> void requires a || b                 = g();
f: <T, V: _> (x, move y: t) throws -> void pre(true) requires a || b       = g();
f: <T, V: _> (x, move y: t) throws -> void pre(true, ) requires a || b     = g();
f: <T, V: _> (x, move y: t) throws -> void pre(true) requires a || b       = { g(); }
f: @meta <T, V: _> (x, move y: t) throws -> void pre(true) requires a || b = { g(); }
f: @meta <T, V: _> (x, move y: t) throws -> void pre(true) requires a || b = {
  g();
  g();
}

x := $R"()";

using t = void();
using t = void (*)();
using t = int (*)();
t: type == ();          // Pending [grammar][].
t: type == * ();        // Pending [grammar][].
t: type == * () -> i32; // Pending [grammar][].

using t = auto (*)() -> auto (*)() -> void;
using t = auto (*)() -> auto (*)(int) -> auto (*)() -> int;
t: type == * () -> * ();                    // Pending [grammar][].
t: type == * () -> * (:i32) -> * () -> i32; // Pending [grammar][].

// Adapted from <https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators>.
g: () = { // clang-format off
         f:   (:i32) -> * (:i32) -> std::string;  // Pending [grammar][].
  assert(f is (:i32) -> * (:i32) -> std::string); // Pending [grammar][].
  //          /         |  |
  //         /          |  |
  assert(f(42)    is    * (:i32) -> std::string); // Pending [grammar][].
  //             ______/   |
  //            /          |
  assert((f(42)*)   is    (:i32) -> std::string); // Pending [grammar][].
  //                ______/
  //               /
  assert((f(42)*)(1)      is        std::string); // Pending [grammar][].
} // clang-format on

I<8> x;
x: I<8>;

I<[] { }> x;
I<[] { }()> x;
x: I<:() = { }>;
x: I<:() = { }()>;

template<class T> const I<8>* x;
template<class T> const I<8>* const x;
x: <T> * const I<8>;
x: <T> const * const I<8>;

template<I<[] { }> V> int x;
x: <V: I<:() = { }>> i32;

I<[] { return 0; }()> x;
x: I<:() -> _ = { return 0; }()>;
x: I<:() -> _ = 0;()>;

template<I<[] { return 0; }()> V> int x;
x: <V: I<:() -> _ = { return 0; }()>> i32;

I<8> f(int l, I<8> r);
f: (l: i32, r: I<8>) -> I<8>;

I<8> (*f)(int l, I<8> r);
f: * (l: i32, r: I<8>) -> I<8>;

std::tuple<int> f();
f: () -> (a: i32);
f: () -> (a: i32, );

std::tuple<int, auto (*)(int)->void (*)()> f();
f: () -> std::tuple<i32, * (:i32) -> * ()>;
f: () -> (a: i32, b: * (:i32) -> * ());

f := :() = x*$;

// f: <T> (x: T<N() <<= 1>) = { }
// f: <T> (x: T<N() >>= 1>) = { }
// f: <V: T<N() <<= 1>> (x) = { }
// f: <V: T<N() >>= 1>> (x) = { }

x := y$;

x := 0;
x = 0;

x  := 0;
xx := 0;

xx = 0;
x := 0;

x: _ = y;
xx: _ == y;
x: _  == y;
x  :== y;
xx :== y;

zero :== int_c<0>;

f5: <T> (_: std::vector<T>) = { }

f1: () = { v0 := 0; }
f2: () = { v0 := cp(0); }
f4: (inout v0: i8, v1: * i8) = {
  v0&* = 1;
  v1* *= 1;
}

cp: <T> (x: T) -> T = x;

t0: <v0: _> type == i8;
t0: <v0: _> final type    = { }
t0: <v0: _> requires true = 0;

main: () -> int = {
  f3();
  // next(u8(255));
  v0: i8 = 1;
  f4(v0, v0&);
  v: std::vector<int> = ();
  f5(v);
  return next(-v0);
}

x: long long;

// Interpolated raw string literal.
x := $R"()";
x := $R"(x)";
x := $R"~()~";
x := u8$R"~(x)~";

// Clang Format regression tests.

// // Not yet fixed.

// x := :() -> forward () throws = f0$*;

f: () = {
  dark_grey  :== :sf::Color  = (0x64, 0x64, 0x64);
  light_grey :== :sf::Color = (0x90, 0x90, 0x90);

  x  : =    f ( : ( )  =   {
     if true { }
 },  z) ;
}

// f: () = {
//   a
// }

jegp: namespace = {
qty: namespace = {
  unit_symbol: type = {
    operator(): <N> (this, forward number: N) -> quantity<U*.default_name, U, std::remove_reference_t<N>> = (number, this);
  }
} // namespace qty
} // namespace jegp

f: () = {
  fffffffffff(:jegp__ui__rectangle = (
            jegp__ui__vectorrrrr(200000000000000.x()),
     jegp__ui__vectorrrrr(128000000000000.x().x())));

  (l   := (:jegp::ui::vector   = (1.px().wide(), 1.px().tall())).from(jegp::ui::window()),
     r := (:jegp::ui::vector = (2.px().wide(), 2.px().tall())).from(jegp::ui::window()))
    std::cout << distance(l, r) << '\n';
}

fffffffffffffffffffffffffffffffffffffffffff: (this) -> _ = :tttttttttttttttttttttttttttttttttttttt =
  vvvvvvvvvvvvvvvvvvv;

export renumber: <NewNumber, Name, Unit, Number> (q: quantity<Name, Unit, Number>) -> quantity<Name, Unit, NewNumber>
  requires std::convertible_to<Number, NewNumber> = :quantity = (q.name, forward q.number as Number, q.unit);

// f: () = {
//   static_assert(false, x{} + "");
// }

// x := 0.std::views::iota();

// f: () = {
//   t.add_member($R"(
// (value())$
// for vec do (i)
//   if (i % 2 == 0) { count++; }
// return count;
//   )");
// }

// Breaks into two adjacent *string-literal*s.
// f: () = {
//   loop_constraints.add("std::invocable<decltype((arg)$), "
//                        "~~~~decltype(std::declval<std::ranges::range_reference_t<R>>().std::as_const())>");
// }

// f: () = {
//   g := :() 0;
//   (x := 0) _ = x;
// }

f: () = {
  require_arguments := :(s: step, n: int) (require&$*)(
    s.arguments.empty() == (n == 0), ("`(s.name())$` needs (n)$ argument(s)").error());
}

// (..., _ = args);

empty2: @ ::noop type = { }

// // Fixed.

x: function_ref<(_: i32) -> i32>;

x := 0..<n;

test_function: type == () -> void;

f: <T: type = my_type> () = { }

f: () = { return $R"(
  )"; }

f: (_) a;

x :== f.operator()<i32>();

_ := :() = {
  x;
  assert(true);
};

f: () = _ = : ::x = 0;

to_vector_ref: <V> (inout v: V) -> _ requires is_specialization_of<V, cartesian_vector>() = //
  :geo::vector2d = (v.x.number&, v.y.number&);

export to: <Unit> (forward q: quantity) -> _ requires std::derived_from<Unit, qty::unit> = //
  :quantity = (q.name, cpp2::unsafe_narrow<Number>(forward q.number), :Unit = ());

f: () = {
  is_close :=
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx || yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;

  visibleArea :==
    sf::FloatRect(:sf::Vector2f = (), sf::Vector2f(event.size.width as float, event.size.height as float));

  if full() { }

  (x := 0)
    //
    _ = x;
}

t: @struct type = { }
or: @and type   = { }

f: () = {
  for args next (:() = 0) do (arg) std::cout << arg;
  // for args next :()  = 0 do (arg) std::cout << arg;
}

t: type = {
  f: (inout this, x) -> i32 = 42;
}

x: Type = (a, 0);

f: () = {
  v0<:int = 0>;
  assert(x is () -> void); // When not formatted.
  operator==(this, this);
  operator[](this, this);
  x  := t<* day>;
  (x := 0) (move x);
  // (x: t<* t>      = g("('(')$")) (move x);
  :<move V: _> () = { };
}

x := (:int = 0);

t: type = {
  operator is: (this, that) -> bool;
  operator[]: (inout this, x: i32) = { } // When further indented.
}

f: () = {
  :() pre(1)            = 0;
  :() pre(1)            = { };
  :() pre(1) requires 1 = { };
  :() pre(1)
  = :() = {
    x;
    x;
  };
}

f: () = {
  x * x;
  x & x;
  x * as;
  x * (0);
}

f: () = {
  (move x);
  (:int = 0);
  (x := 0) (move x);
  (move x);
  ();
}

f: () -> std::tuple<i32, * (:i32) -> * ()>; // `->*` is split into `->` `*`.

f: () = {
  :int = 0;
  +x;
  :int = 0;
  -x;
  g();
  ();
}

f: () = {
  for r do (e) g(e);
}

f: (is: * ()) = {
  assert(is is is);
  assert((is) is (is));
  assert(is as * ());
  assert((is) as * ());
}

I<[] {
  auto y = 0;
  return y < z;
}()>
  x = 0;

x: I<:() -> _ = {
  y := 0;
  return y < z;
}()> = 0;

int_c: <V: int> const std::integral_constant<int, V> = ();

x := (type);

f3: () = {
  v0: i8 = 0;
  v1: i8 = i8(0);
  v2: i8 = cp(:i8 = 0);
  v3: i8          = cp(0 as i8);
  v4: t0<:i8 = 0> = 0;
  v5 := v0&;
  v6 := v5*;
}

// Syntax highlighting test cases.

// Highlighted interpolation (I want them formatted, too).
x := "Next is (current++)$\n";
x := "(inspect 1->i32{is _=2;})$";
x := "(\"0\")$\n"; // Known limitation.

// Type-only contexts.
x: i32 = 0;
x: int = 0;
x: day = Wednesday;
x: type == i32;
x: type == day;
x := x as day;
f: () -> day = { }

// Types.
x := t<i32>;
x := t<* day>;
x := i32(0);

// `_` wildcard.
// // Anonymous declarations.
f: <_> (_) = { _: std::lock = mutex; }
_: namespace = {
// // Using namespace.
using std::chrono_literals::_;
} // namespace _
// // Deduced types.
x: _         = 0;
f: (:_) -> _ = {
  // // Placeholder.
  x := x is _;
  // // Discard.
  x := _ = 0;
  // // Just an identifier.
  f(_);
}

f: () = {
  // Keyword, declaration, type, identifier.
  (move move: move = move) { } // Pending [hsutter/cppfront#408][].
}

int x                = 0;
const int* const x   = 0;
x: i32               = 0;
x: const * const i32 = 0; // clang-format off
 x : i32 = 0 ;
x:i32=0;
x:const*const i32=0; // clang-format on

x: char       = 0;
x: _schar     = 0;
x: _uchar     = 0;
x: short      = 0;
x: ushort     = 0;
x: int        = 0;
x: uint       = 0;
x: signed     = 0;
x: unsigned   = 0;
x: long       = 0;
x: ulong      = 0;
x: longlong   = 0;
x: ulonglong  = 0;
x: float      = 0;
x: double     = 0;
x: longdouble = 0;

long int x  = 0;
x: long int = 0;

::day x        = 0;
ns::day x      = 0;
::t::day x     = 0;
t<0>::day x    = 0;
::t<0>::day x  = 0;
x: ::day       = 0;
x: ns::day     = 0;
x: ::t::day    = 0;
x: t<0>::day   = 0;
x: ::t<0>::day = 0;

export day x  = 0;
export x: day = 0; // clang-format off
 export x: day = 0; // clang-format on
exportx: day  = 0;

x: @x i32              = 0;
x: @x::x i32           = 0;
x: @x.x i32            = 0;
x: @x<T> i32           = 0;
x: @x<T>::x i32        = 0;
x: @x<T>.x i32         = 0;
x: @x <T: type> i32    = 0;
x: @x<T> <T: type> i32 = 0; // clang-format off
x:@x@x i32=0;
 x : @ x @ x i32 = 0; // clang-format on

template<class T> int x = 0;
template<class> day x   = 0;
x: <T: type> i32        = 0;
x: <T> i32              = 0;
x: <:type> day          = 0; // clang-format off
x:<T,T>i32=0;
 x : < T , T > i32 = 0; // clang-format on

template<auto V> int x        = 0;
template<auto> int x          = 0;
template<const auto& V> day x = 0;
template<const auto&> day x   = 0;
x: <V: _> i32                 = 0;
x: <:_> i32                   = 0;
x: <:__> i32                  = 0;
x: <in V: _> day              = 0;
x: <in: _> day                = 0;
x: <inV: _> day               = 0;

void f() { }
void f(const auto& x) { }
void f(const auto&) { }
void f(auto) { }
void f(const auto) { }
void f(const day x) { }
void f(const day) { }
f: ()              = { }
f: (in x: _)       = { }
f: (in x)          = { }
f: (x)             = { }
f: (in: _)         = { }
f: (:_)            = { }
f: (copy: _)       = { }
f: (copy: const _) = { }
f: (in x: day)     = { }
f: (x: day)        = { }
f: (in: day)       = { }
f: (:day)          = { } // clang-format off
f:(x,y)={}
 f : ( in x : _ , : day ) = { }
f: (in :_)         = { } // clang-format on
f: (inx: _)        = { }
f: (inx)           = { }
f: (:const_)       = { }

void f() noexcept { }
f: () throws = { } // clang-format off
f:()throws={} // clang-format on
_: throws_   = { }

int f() { }
day f() { }
auto f() { }
auto&& f() { }
auto f() -> int { }
auto f() -> day { }
auto f() -> auto { }
auto f() -> auto&& { }
f: () -> i32 { }
f: () -> day { }
f: () -> _ { }
f: () -> forward _ { } // clang-format off
f:()->i32{}
f:()->forward _{}
f:()->forward_{} // clang-format on

auto f() -> move { }
auto f() -> ns::move { }
auto f() -> ::ns::move { }
f: () -> ::move { }
f: () -> ns::move { }
f: () -> ::ns::move { }

f: () pre(true)                       = { }
f: () pre(true, "")                   = { }
f: () pre<::x<0>>(true)               = { }
f: () pre<bounds_safety>(true)        = { }
f: () pre<bounds_safety, audit>(true) = { }

void f() requires true { }
f: () requires true = { }
_: requires_        = { }

struct t { };
struct t final { };
struct t : base {
  int x = 0;
  day x;
  bool operator==(const t&) const = default;
  void operator[](int x) { }
  virtual void f() const = 0;
};
t: type       = { }
t: final type = { }
t: type       = {
  x: i32 = 0;
  x: day;
  this: base;
  operator==: (this, that) -> bool;
  operator[]: (inout this, x_: i32) = {
    x      = x_;
    this.x = x_;
    operator==(this, this);
    this.operator==(this);
  }
  f: (virtual in this); // clang-format off
  x:i32=0;
  x :day ;
  this :base ;
  operator == :( this ,that )->bool ;
  operator [ ] :( inout this ,x :i32 )={} // clang-format on
  f: (in this_, that_);
  f: (virtual_);
  f: (inthis);
  f: (virtualthis);
  f: (virtualinthis);
}
t: type = {
  operator is: (this, that) -> bool;
}
_: final_ = { }
_: type_  = { }

namespace ns { }
ns: namespace = { }
// clang-format off
ns :namespace={} // clang-format on
_: namespace_ = { }

x = 0.0e+0f;
x := 0.0e+0f;
x = 0x0'0u;
x := 0x0'0u;
x = 0b0'0;
x := 0b0'0;
x = 0'0;
x := 0'0;
x = '\0';
x := '\0';

x = nullptr;
x := nullptr;
x = false;
x := false;

x = u8"";
x := u8"";
x = "(x)$";
x := "\(x)$";
x := "(x)$";
x := "(x)$(x)$";
x := "@(x)$@(x)$@";
x = R"()";
x := R"()";
x = u8R"()";
x := u8R"()";
x := $R"()";
x := u8$R"()";
// Highlight interpolation also in non-interpolated raw string literal as it's most likely used for reflection.
x = R"((x)$)";
x := $R"(\(x)$)";
x := R"(\(x)$)";
x := R"((x)$)";
x := R"((x)$(x)$)";
x := R"(@(x)$@(x)$@)";
x := "(:()->_=0;())$";
x := "(inspect 1->i32{is _=2;})$";

x = x = x.x = x::x = x<0> = x<0>.x = x<0>::x;
x := x = x.x = x::x = x<0> = x<0>.x = x<0>::x;

x = (0);
x := (0);

x := :int     = 0;
x := (:int    = 0);
x := (:int    = 0;);
x := :() -> _ = 0;();
x := :() -> _ = 0;(x, :int = 0);
x := :() -> _ = 0;(x, :int = 0;);

x = *x  = &x;
x := x* = x&;

x = x[0](0).x;
x := x[0](0).x;

x = std::iota(0, n);
x := 0 ..< n;
x := 0 ..= n;
x = std::ranges::subrange(v.begin(), --v.end());
x := v.begin() ..< v.end()--;
x := v.begin() ..= v.end()--;

x       = +x;
x := +x = move x = +move x;

x = x * x + x << x <=> x < x == x & x ^ x | x && x || x;
x := x * x + x << x <=> x < x == x & x ^ x | x && x || x;

struct t {
  struct t { };
  struct t {
    int x = 0;
    void f() { }
  };
};
t: type = {
  t: type = { }
  t: type = {
    x: i32 = 0;
    f: ()  = { }
  }
}

namespace ns {
int x = 0;
void f() { }
struct t {
  int x = 0;
  void f() { }
};
} // namespace ns
ns: namespace = {
x: i32  = 0;
f: ()   = { }
t: type = {
  x: i32 = 0;
  f: ()  = { }
}
} // namespace ns

namespace ns {
int x = 0;
namespace ns {
  void f() { }
  struct t {
    int x = 0;
    struct t {
      void f() {
        [] { };
      }
    };
  };
} // namespace ns
} // namespace ns
ns: namespace = {
x: i32 = 0;
ns: namespace = {
  f: ()   = { }
  t: type = {
    x: i32  = 0;
    t: type = {
      f: () = {
        :() = { };
      }
    }
  }
} // namespace ns
} // namespace ns

// Cpp1 colons.
int x = a ? b : c;
int x = a ? // clang-format off
          b : c; // clang-format on
void f() {
  switch (x) {
  case x:
  default:
  label:
    goto label;
  }
  for (auto x : r) { }
}
enum t : int;
class t : base {
public:
  unsigned x : 1;
  t() : x{} { }
  t() requires x : x{} { }
  t() // clang-format off
  requires x : x{} { } // clang-format on
  t() noexcept : x{} { }
  t() try : x{} {
  } catch (...) { }
  t() // clang-format off
  try : x{} { // clang-format on
  } catch (...) { }
};
module a:b;
module: private;
int x = a::b;
// Cpp2 colons.
f: () = {
  // Declaration colons.
pre:
  while true { }
pre: //
  while true { }
  x := 0;
  x: i32 = 0;
  :i32   = 0;
  x :== y;
  x: _ == y;
  f: ()          = { }
  :()            = { };
  :(in x: i32)   = { };
  :(x: i32)      = { };
  :(in: i32)     = { }; // Pending [grammar][].
  :(:i32)        = { }; // Pending [grammar][].
  t: <V: _> type = {
    x: i32;
    this: u;
    operator=: ();
    operator==: ();
    operator(): ();
    operator[]: ();
  }
  inspect x { y: is _ = g(); }
  a::b;
}

struct t {
  void f(override) override; // Declaration, keyword.
  void f(final) final;       // Declaration, keyword.
};
f: () = {
  // Declaration followed by keyword.

  :(implicit, implicit this) = { };
  :(override, override this) = { };
  :(final, final this)       = { };
  :(final, final in this)    = { };

  :(throws) throws = { };

  f: (pre) pre(1)       = { }
  f: (post) post(1)     = { }
  f: (assert) assert(1) = { }

  t: <type> type            = { }
  t: <final> final type     = { }
  t: <type> type requires 1 = { }
  t: <type> type == i32;
  :<type: type> () = { };
}

f: () = {
  move;                   // Identifier.
  -move;                  // Identifier.
  move*;                  // Identifier.
  x is move;              // Type.
  x + move x;             // Keyword. // Pending [hsutter/cppfront#408][].
  x + move;               // Identifier.
  move x;                 // Keyword. // Pending [hsutter/cppfront#408][].
  move move x;            // Keywords. // Pending [hsutter/cppfront#408][].
  move: i8;               // Declaration.
  x: move;                // Type.
  f(move);                // Identifier.
  x[move];                // Identifier.
  x[move x];              // Keyword.
  f(move x, move x);      // Keywords.
  :(move, move) = { };    // Declarations.
  :(move: i8)      = { }; // Keyword. // Pending [grammar][].
  :(move x)        = { }; // Keyword.
  :(move x: i8)    = { }; // Keyword.
  :(move move)     = { }; // Keyword, declaration.
  :(move move: i8) = { }; // Keyword, declaration.
  :(:move)         = { }; // Type.
  :(move: move)    = { }; // Keyword, type. // Pending [hsutter/cppfront#408][].
  :() -> move i8   = { }; // Keyword.
  :() -> ::move    = { }; // Type.
  return move x;          // Keyword. // Pending [hsutter/cppfront#408][].
  :<move V: _> () = { };  // Keyword. // Pending [hsutter/cppfront#425][].

  (move move: move = move) { } // Keyword, declaration, type, identifier. // Pending [hsutter/cppfront#408][].
}

void f() {
  x and y;  // Keyword.
  new int;  // Keyword.
  class t;  // Keyword.
  struct t; // Keyword.
}
f: () = {
  // Reclaimed identifiers.
  f(and);
  f(::new);
  f(class);
  f(struct);

  // Special identifiers.
  x := new<i32>();
  x := unique.new<i32>();
  x := shared.new<i32>();
  _: finally = finally(:()          = { });
  _: finally<std::function<void()>> = finally<std::function<void()>>();
  _: cpp1_ref<int>                  = cpp1_ref(x);
  _: cpp1_ref                       = cpp1_ref<int>(x);
  _: cpp1_rvalue_ref<int>           = cpp1_rvalue_ref(x);
  _: cpp1_rvalue_ref                = cpp1_rvalue_ref<int>(x);
  unsafe_narrow<int>(0.0);
  unsafe_cast<derived>(base);
  _: type_of(0) = type_of(0)(0);
  assert<bounds_safety>(min <= x < max);

  // Normal identifiers.
  x := ::new<i32>();
  x := ::unique.new<i32>();
  x := shared.old<i32>();
  _: ::finally              = ::finally();
  _: ::cpp1_ref             = ::cpp1_ref<int>;
  _: ::cpp1_rvalue_ref<int> = ::cpp1_rvalue_ref(x);
  _                         = ::unsafe_narrow;
  _                         = ::unsafe_cast;
  _                         = ::type_of;
  _: type_of<u>             = 0;
  assert<my_bounds_safety>(min <= x < max);
  casset<testing>("reverse side");
}

// Syntax highlighting regression tests.

// // Not yet fixed.

f: <T: type = my_type> () = { }
x: function_ref<(_: i32) -> i32>;
// clang-format off
// x: t<y>= 0;
t: @struct<T: type> type = { } // clang-format on
i: (x) (x);
s :== "x(:t=0:t)$x";
s :== "x(:t=0;:t)$x";
s :== "x(:t=0;++:t)$x";
main: (args) = {
  // for args next :() = 0 do (arg) std::cout << arg;
  assert<testing, testing>();
}

// sfml_argument_list: (mf: cpp2::meta::function_declaration) -> std::string =
//   "(" + mf.get_parameters().drop(1).map(sfml_argument).intersperse("(',')$ ").flatten().to<std::string>() + ")";

// // Fixed.

f: () = { static_assert(true); }
c := 'X';
c := '?';
s: (f, g) :(x) f$(x, g$(x));
b: (f, g) :(x) f$(g$(x));
_ := x is () -> void;
a: span<const int>;
i: (x) x;
s :== "x(:t=0)$x";
s :== "x(x:t)$x";
s :== "x(x&:t)$x";
s :== "x(x...:t)$x";
s :== R"(x(+"": <10.2f)$x)";
s :== "x(x.price(): <10.2f)$x";
f: (x...: int) = ... && g(x...);
a: unit<"">;
x := (inspect y -> int {
  is (0) = 1;
  is _   = 2;
});
// clang-format off
x := (0)is 0; // clang-format on
t: type = {
  operatoris: ();
}
x := "()$ ()";

// [grammar]: https://github.com/hsutter/cppfront/issues/387
// [hsutter/cppfront#408]: https://github.com/hsutter/cppfront/issues/408
// [hsutter/cppfront#425]: https://github.com/hsutter/cppfront/issues/425
Clone this wiki locally