llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.09k stars 12k forks source link

clang-format mis-indents lambdas with sufficiently long signatures #59051

Open brevzin opened 1 year ago

brevzin commented 1 year ago

My .clang-format looks like this:

BasedOnStyle: llvm

AccessModifierOffset: -4
AlignEscapedNewlines: Right
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: true
AllowShortLambdasOnASingleLine: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
BraceWrapping:
  BeforeElse: true
  BeforeCatch: true
  BeforeLambdaBody: false
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
IndentWidth: 4
LambdaBodyIndentation: Signature
NamespaceIndentation: None
PenaltyBreakAssignment: 60
PenaltyBreakBeforeFirstCallParameter: 175
PointerAlignment: Left
QualifierAlignment: Custom
QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile']
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpacesBeforeTrailingComments: 2
SpacesInSquareBrackets: false

which produces this indendation:

template <typename T>
struct Optional {};

namespace abcd {
struct SufficientlyLongName {};
}  // namespace abcd

struct X {};

struct C {
    auto format(X const& x, auto& ctx const) {
        auto some_very_long_name =
            [&]() -> Optional<abcd::SufficientlyLongName const&> {
            if (true) {
                return {};
            }
            else {
                return {};
            }
        };
        return 42;
    }
};

The body of the lambda should be indented with respect to the signature of the lambda, that doesn't happen in this case - which looks weird and there doesn't seem to be any option to fix it. The desired indentation would be:

struct C {
    auto format(X const& x, auto& ctx const) {
        auto some_very_long_name =
            [&]() -> Optional<abcd::SufficientlyLongName const&> {
                if (true) {
                    return {};
                }
                else {
                    return {};
                }
            };
        return 42;
    }
};

With the }; of the lambda also indented.

llvmbot commented 1 year ago

@llvm/issue-subscribers-clang-format

rymiel commented 1 year ago

This appears to be an interaction between the continuation indent and regular indents, which is perhaps better observed by using an IndentWidth smaller than the ContinuationIndentWidth, such as default LLVM:

struct C {
  auto format(X const &x, auto &ctx const) {
    auto some_very_long_name =
        [&]() -> Optional<abcd::SufficientlyLongName const &> {
      if (true) {
        return {};
      } else {
        return {};
      }
    };
    return 42;
  }
};

The lambda is broken to the next line as a continuation, but the contents of it act as if the lambda was never broken to a continuation line.

I'm not familiar with clang-format indentation logic, but poking around a bit, it seems this isn't easy to patch because scopes are indented almost independently of each other. This property manifests with anything that introduces a new scope, such as this if case:

auto f() {
  if (a_long_name_for_something + another_long_name ==
      stuff_stuff_stuff) {
    operation();
//  | ^ odd gap going left
  }
};

It's just a lot more noticeable on lambdas but I do agree it looks like an issue (just one that I don't know how to fix)

Perhaps as a workaround you could increase the ContinuationIndentWidth in your config?