How do I get clang-format to align arguments in nested function calls by the root instead?

Viewed 438

Using this minimal .clang-format:

BasedOnStyle: LLVM
IndentWidth: 4
---
Language: Cpp
ColumnLimit: 120
UseTab: Never
AlignAfterOpenBracket: DontAlign
ContinuationIndentWidth: 8

I end up with code formatted like this:

#include <algorithm>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> vec;
    vec.erase(std::remove_if(vec.begin(), vec.end(),
                      [](const std::string &) {
                          std::string x = "I'm here just to expand this block to keep it from collapsing";
                          return false;
                      }),
            vec.end());
    // the third argument, while invalid, doesn't affect clang-format.
    // It exists to force the lambda to break to the next line. If it
    // doesn't break, it ends up with slightly different, but still acceptable
    // formatting. I need it on a new line to demonstrate the difference between
    // a single function call, and calls to a function in the list of function
    // params
    std::remove_if(vec.begin(), vec.end(), imasuperlonginvalididentifiertopushthisblockfaaaarofftotheleft,
            [](const std::string &) {
                std::string x = "I'm here just to expand this block to keep it from collapsing";
                return false;
            });
}

My problem here is with the first block.

First of all, notice the second block. It indents the lambda, a wrapped arg, by 8 spaces. I'm fine with this. The first block, however, is bad:

Image showing the first block misaligning with indent guidelines

Here's a numbered indent to show the problem:

vec.erase(std::remove_if(vec.begin(), vec.end(),
          12345678[](const std::string &) {
                      std::string x = "I'm here just to expand this block to keep it from collapsing";
                      return false;
                  }),
        vec.end());

Note how the indent is aligned to the start of std::remove_if. Because vec.erase( doesn't work out as 8, this means the entire block gets misaligned. I can deal with it for arguments, but the effects on multi-line lambda is a problem. This is how I want it to format:

vec.erase(std::remove_if(vec.begin(), vec.end(),
                [](const std::string &) {
                    std::string x = "I'm here just to expand this block to keep it from collapsing";
                    return false;
                }),
        vec.end());

And numbered:

vec.erase(std::remove_if(vec.begin(), vec.end(),
1234567812345678[](const std::string &) {
                    std::string x = "I'm here just to expand this block to keep it from collapsing";
                    return false;
                  }),
        vec.end());

And an image:

Image showing the desired format with indent guidelines

While writing this question, I've been a bit back and forth, because I'm not sure when it triggers. I cut down the column width to force std::remove_if to wrap:

std::remove(
        std::remove_if( ...
                [](...) { ...

it's suddenly fine, because remove_if has been aligned to an 8 block, which means the arguments are indented by 8 or 16 as well. However, with this code:

    randomFunctionCall(functionCall2(9,
            std::string{"I need args so I made some up. "
                        "clang-format should allow them"},
            42, anotherlongidentifier, weeeeeeeeeee));

the arguments for the inner function arguments are fine. Well, kinda. They indent by 8 instead of 16, but I can live with that. (For the record, 16 comes from a continuation indent of 8, two levels deep, or 2 * 8 = 16). Now, watch what happens if I add another argument to the randomFunctionCall instead of adding to functionCall2:

randomFunctionCall(functionCall2(9,
                           std::string{"I need args so I made some up. "
                                       "clang-format should allow them"},
                           42, anotherlongidentifier, weeeeeeeeeee),
        randoarg);

The inner arguments align to the function call + 8 instead of the root function call + 16. So the misalignment happens, at least as far as I can tell, when there's a function call that takes a function call with arguments, but also has additional arguments beyond the function call.

When I have nested function calls (func(func2(severalArgsWithLongEnoughNamesToWrap), nonfuncarg)) with arguments, how do I get clang format to consider indentation from the start of the line, rather than from the start of the current function call? I'm aware I can use // clang-format off/on to locally enforce a non-standard style, but I would prefer not to.

I'm using Clang and clang-format 11.

0 Answers
Related