Summary
clang-format 23 produces lines that exceed ColumnLimit when formatting a
function-like macro body containing a call with an argument preceded by the
## token-paste operator. There are several oddities observed; they perhaps
all have the same root cause.
Environment
- Good:
clang-format version 22.1.0-rc1
- Bad:
clang-format version 23.1.0-rc1
--style='{BasedOnStyle: LLVM, IndentWidth: 4, ContinuationIndentWidth: 8, ColumnLimit: 80, AlignEscapedNewlines: Right}'
Manifestation 1: refusal to insert a line break immediately before the ##-pasted argument
Input:
#define M(f, ...) \
auto f = call(firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, ##__VA_ARGS__);
v22 (correct — breaks before ##__VA_ARGS__, max column 80):
#define M(f, ...) \
auto f = call(firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, \
##__VA_ARGS__);
v23 (buggy — refuses to break before ##__VA_ARGS__; the last line is 87 columns):
#define M(f, ...) \
auto f = call( \
firstArgumentThatIsQuiteLongEnoughToForceAWrapHere11111111, ##__VA_ARGS__);
Manifestation 2 — ## in nested logic causes oddities two ways
Here the ##-pasted argument is not the last argument, the call is nested several blocks deep, and its argument list spans multiple lines.
Input (format with the style above):
#define CHECK(flagName, expr, ...) \
({ \
if ((false)) { \
if (expr) {} \
} \
if (unlikely(!(expr))) { \
if (someLongContainerName.contains(SomeEnumTypeName::flagName)) { \
someLongContainerName.add(SomeEnumTypeName::flagName); \
} else { \
reportFunction(std::source_location::current(), "some diagnostic message string here " "(" #flagName "): " #expr, ##__VA_ARGS__, extra); \
} \
} \
(void) 0; \
})
v22 (correct — aligned continuation characters):
#define CHECK(flagName, expr, ...) \
({ \
if ((false)) { \
if (expr) { \
} \
} \
if (unlikely(!(expr))) { \
if (someLongContainerName.contains(SomeEnumTypeName::flagName)) { \
someLongContainerName.add(SomeEnumTypeName::flagName); \
} else { \
reportFunction(std::source_location::current(), \
"some diagnostic message string here " \
"(" #flagName "): " #expr, \
##__VA_ARGS__, extra); \
} \
} \
(void)0; \
})
v23 (buggy - line is not broken at all):
#define CHECK(flagName, expr, ...) \
({ \
if ((false)) { \
if (expr) { \
} \
} \
if (unlikely(!(expr))) { \
if (someLongContainerName.contains(SomeEnumTypeName::flagName)) { \
someLongContainerName.add(SomeEnumTypeName::flagName); \
} else { \
reportFunction(std::source_location::current(), "some diagnostic message string here " "(" #flagName "): " #expr, ##__VA_ARGS__, extra); \
} \
} \
(void)0; \
})
And if we send v22's output to v23, we get a jagged edge of the continuation character:
#define CHECK(flagName, expr, ...) \
({ \
if ((false)) { \
if (expr) { \
} \
} \
if (unlikely(!(expr))) { \
if (someLongContainerName.contains(SomeEnumTypeName::flagName)) { \
someLongContainerName.add(SomeEnumTypeName::flagName); \
} else { \
reportFunction(std::source_location::current(), \
"some diagnostic message string here " \
"(" #flagName "): " #expr, \
##__VA_ARGS__, extra); \
} \
} \
(void)0; \
})
Removing just the ## (##__VA_ARGS__ → __VA_ARGS__) makes v23 wrap the call
correctly with a max column of 80, identical to v22.
Summary
clang-format 23 produces lines that exceed
ColumnLimitwhen formatting afunction-like macro body containing a call with an argument preceded by the
##token-paste operator. There are several oddities observed; they perhapsall have the same root cause.
Environment
clang-format version 22.1.0-rc1clang-format version 23.1.0-rc1Manifestation 1: refusal to insert a line break immediately before the
##-pasted argumentInput:
v22 (correct — breaks before
##__VA_ARGS__, max column 80):v23 (buggy — refuses to break before
##__VA_ARGS__; the last line is 87 columns):Manifestation 2 —
##in nested logic causes oddities two waysHere the
##-pasted argument is not the last argument, the call is nested several blocks deep, and its argument list spans multiple lines.Input (format with the style above):
v22 (correct — aligned continuation characters):
v23 (buggy - line is not broken at all):
And if we send v22's output to v23, we get a jagged edge of the continuation character:
Removing just the
##(##__VA_ARGS__→__VA_ARGS__) makes v23 wrap the callcorrectly with a max column of 80, identical to v22.