llvm / llvm/llvm-project

[clang-format] PointerAlignment: Left is ignored for braced condition declarations

Open Beginner friendly
#210,182 1 comment 0 reactions 0 assignees View on GitHub
clang-format
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## _EDIT: Changed from a code to documentation defect report_

## .clang-format
```
BasedOnStyle: LLVM
PointerAlignment: Left
```

## reproducer.cpp
```cpp
struct MyType {
explicit operator bool() const;
};

MyType* makeMyType();

void f() {
if (MyType* value{makeMyType()}) {
}
}
```

## Command
```
clang-format --style=file:.clang-format reproducer.cpp
```

## Expected output
```
if (MyType* value{makeMyType()}) {
```

## Actual output
```
if (MyType * value{makeMyType()}) {
```

## Reproduced with versions
- Ubuntu clang-format version 21.1.8 (6ubuntu1)
- Ubuntu clang-format version 22.1.2 (1ubuntu1)
- main HEAD: clang-format version 24.0.0git (ab35099249433e55ba586285c2087d0bd7f65e9c)

---

## Root cause — and why this is not really a `PointerAlignment` bug
In a condition/scoped declaration, `MyType * value` is grammatically ambiguous with the
binary expression `MyType * value` (pointer declaration vs. multiplication). clang-format's
annotator resolves keyword types (`int`, `auto`) as declarations but defaults
**user-defined** types to a binary operator, so the `*` is never a pointer token and
`PointerAlignment` legitimately does not apply. The symptom presents as
"`PointerAlignment` ignored," which is why this keeps getting filed against
`PointerAlignment`.

## `TypeNames` is the correct fix — but it is undiscoverable
I attempted a heuristic annotator fix myself; the effect was marginal and the instability
was not worth it. The maintainers' `TypeNames` option (clang-format 17) is the right
compromise:

> *"A `*`, `&`, or `&&` between a type name and another non-keyword identifier is annotated
> as a pointer or reference token instead of a binary operator."*

Adding `MyType` to `TypeNames` produces the expected output. The problem is purely
**discoverability**: nothing connects the observed `PointerAlignment` symptom to
`TypeNames`, so the same issue resurfaces repeatedly and is closed one-off by pointing at
the option.

## Evidence of resurfacing (same root cause, closed via `TypeNames`)
- #63846 — multiplication vs. pointer in `if` (`MYI * p`); resolved "add to `TypeNames`";
scaling concern raised; an algorithmic fix `70de684d` was reverted in `104cd749`
- #60146 — `PointerAlignment: Left` lost for scoped decls in `if`/`for`; resolved
"use `TypeNames`"; split into #109371
- #109371 — `if (MyClass * obj{...})`; closed *not planned*
- #41506 — `*` not applied when not directly attached to a type

## Suggested documentation improvements
1. **Cross-reference from `PointerAlignment` (and `ReferenceAlignment`) to `TypeNames`**,
explicitly naming this symptom: when a user-defined type precedes `*`/`&`/`&&` in a
declaration that is ambiguous with an expression (typically in `if`/`for`/scoped
declarations), the operator is annotated as binary and alignment is not applied — list
the type in `TypeNames`. This one link would prevent the recurring reports above.
2. **Address the "global whitelist doesn't scale / pollutes shared config" concern**
(raised in #63846) by documenting a scoping pattern: keep the `TypeNames` whitelist in a
**per-directory `.clang-format` using `BasedOnStyle: InheritParentConfig`**, confined to
the code that actually names those types, so the root/shared style stays clean. Working
example:
https://gitlab.com/misteffens/cxx-interface-tool/-/blob/main/src/InterfaceTool/.clang-format?ref_type=heads

With these two doc additions the behavior stays as designed, but the fix becomes findable
and adoptable without polluting global style configuration.

Contributor guide

Open the contributing guide

Research direction

Start with the .clang-format example and reproducer.cpp, then run clang-format with the shown command to confirm the behavior. Locate the PointerAlignment, ReferenceAlignment, and TypeNames documentation entries and add the requested cross-references plus the per-directory InheritParentConfig scoping guidance. Done means users can discover TypeNames from the alignment options and understand how to limit the whitelist.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
documentation
Issue type
Documentation
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.