[clang-format] Spurious spaces around template brackets when callee is UDL operator (`operator""_x<...>()`)
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Hi! I ran into this issue using clang-format 17, where it was treating variadic user-defined literals (UDLs) strangely, adding extra spaces. I had Claude help me explore the behavior across different versions. Here's what we found.
First, this is a regression: clang-format 14, 15, and 16 all format the construct correctly. It looks like it got partially fixed in clang 19, but is still "half present" even in the latest release.
## Minimal reproducer
Input (`--style=LLVM`, but style-independent):
```cpp
template auto f() { return operator""_mag(); }
```
### Actual output
clang-format **17.0.6** and **18.1.8**:
```cpp
template auto f() { return operator""_mag < Cs... > (); }
```
clang-format **19.1.7** through **22.1.8** (latest):
```cpp
template auto f() { return operator""_mag < Cs...>(); }
```
(19+ stopped spacing the closing `>`, but still inserts a spurious space before `<`.)
### Expected output
The pre-17 behavior, unchanged from an ordinary template call:
```cpp
template auto f() { return operator""_mag(); }
```
### Control (correct in all versions)
Replacing the UDL operator with a normal function name is formatted correctly everywhere, confirming the operator-literal name is what triggers the bug:
```cpp
template auto f() { return g(); } // stays g()
```
## Version bisection
| clang-format | Output | Status |
|---|---|---|
| 14.0.6 | `operator""_mag()` | correct |
| 15.0.7 | `operator""_mag()` | correct |
| 16.0.6 | `operator""_mag()` | correct |
| 17.0.6 | `operator""_mag < Cs... > ()` | **regressed (first bad)** |
| 18.1.8 | `operator""_mag < Cs... > ()` | broken |
| 19.1.7 | `operator""_mag < Cs...>()` | partially fixed, still wrong |
| 20.1.8 | `operator""_mag < Cs...>()` | partially fixed, still wrong |
| 21.1.8 | `operator""_mag < Cs...>()` | partially fixed, still wrong |
| 22.1.8 | `operator""_mag < Cs...>()` | partially fixed, still wrong |
(Binaries from the PyPI `clang-format` wheels.)
## Notes
This looks related to the "unnecessary spaces around template brackets" family of regressions (#110968, #112487, #123144), but those cover other triggers (`conditional_t<...>{}`, binary operators inside the argument list). None of them involve a user-defined-literal operator as the template-id name, and the `operator""` case remains unfixed on the latest release. The distinguishing feature here is purely the `operator""` name preceding the `<...>()`.
Contributor guide
Assessment
This issue has not been assessed yet.