Jody Hagins
Jody Hagins

Reputation: 28339

Strange results with clang-format options AlwaysBreakAfterReturnType and AfterFunction

clang-format version 9.0.1, applied to C++ code.

The combination of AlwaysBreakAfterReturnType and AfterFunction is giving me strange results. The interaction with different options seems quite subtle, so I wonder what I need to do to get my desired result (which is coming).

First, the code being formatted.

int foo(int, double);

int bar(int, double) { return 42; }

template <typename T, typename U> XY<T, U> xy1(X, Y y) { return XY<T, U>{}; }

template <typename T, typename U> XY<T, U> xy2(X, Y) { return XY<T, U>{}; }

Note that the only difference between the functions xy1 and xy2 is that xy1 provides a name for tis last parameter, while xy2 does not name any of its paramteres.

Next, the contents of a minimal .clang-format file.

---
BasedOnStyle:  LLVM
AlwaysBreakAfterReturnType: TopLevelDefinitions

Running clang-format will format the original code as follows.

int foo(int, double);

int
bar(int, double) {
  return 42;
}

template <typename T, typename U>
XY<T, U>
xy1(X, Y y) {
  return XY<T, U>{};
}

template <typename T, typename U>
XY<T, U>
xy2(X, Y) {
  return XY<T, U>{};
}

However, I want the opening curly brace for the function on its own line, not packed just to the right of the function.

From the docs, I should set BraceWrapping/AfterFunction to true, resulting in the following .clang-format file.

---
BasedOnStyle:  LLVM
AlwaysBreakAfterReturnType: TopLevelDefinitions
BraceWrapping:
    AfterFunction:   true
BreakBeforeBraces: Custom

This results in the following formatting result.

int foo(int, double);

int
bar(int, double)
{
  return 42;
}

template <typename T, typename U>
XY<T, U>
xy1(X, Y y)
{
  return XY<T, U>{};
}

template <typename T, typename U> XY<T, U> xy2(X, Y) { return XY<T, U>{}; }

This is what I expect, except for the formatting of the function xy2, which is just nothing like I'd expect, and not even close to the formatting for xy1, which is exactly what I'd expect.

If I go a bit further, and force a break for the template, then I end up with this .clang-format file...

---
BasedOnStyle:  LLVM
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
    AfterFunction:   true
BreakBeforeBraces: Custom

which produces this formatted output.

int foo(int, double);

int
bar(int, double)
{
  return 42;
}

template <typename T, typename U>
XY<T, U>
xy1(X, Y y)
{
  return XY<T, U>{};
}

template <typename T, typename U>
XY<T, U> xy2(X, Y)
{
  return XY<T, U>{};
}

This is closer, but the formatting for xy2 completely ignores the request to break after the return type.

The subtle interactions between the different options is q bit confusing, and I have not been able to come up with a way to get what I want, which is basically for functions that have no explicit arguments to get formatted the same as those that have explicit arguments.

Again, note the subtle difference between xy1 and xy2. xy1 has at least one named parameter, while xy2 does not have any named parameters - but neither does bar.

Note also that the regular function bar does not have any named parameters and yet it is formatted correctly, while the function template xy2 is still not formatted properly.

What magic combination of options will cause xy2 to be formatted the same as xy1 and bar?

Thanks.

Upvotes: 4

Views: 763

Answers (2)

MyDeveloperDay
MyDeveloperDay

Reputation: 2670

This is resolved in clang-format v14 and above (maybe even 13)

Your issue was migrated to github during the recent move of the LLVM bug tracker

https://github.com/llvm/llvm-project/issues/44720

Upvotes: 0

Rumburak
Rumburak

Reputation: 3571

After experimenting a bit myself, I am pretty sure that there is no "magic combination". clang-format seems to have a problem with the missing parameter names.

I opened a bug report: https://bugs.llvm.org/show_bug.cgi?id=45375

In the meantime:

If you can use C++17, there is a workaround by using the [[maybe_unused]] attribute in the second template function:

template <typename T, typename U> 
XY<T, U> 
xy2([[maybe_unused]] X x, Y)
{
  return XY<T, U>{};
}

(using your last config)

Upvotes: 2

Related Questions