Skip to content

Conversation

@funous
Copy link

@funous funous commented Nov 16, 2025

This change introduces an automatic fixing capability to clangd-tidy, allowing it to apply clang-tidy's suggested fixes, including complex cross-file refactorings like renames. Cross-file fixes are something not supported by clang-tidy, only by clangd via LSP.

This is implemented via two new flags:

  • --fix: Directly applies fixes to the source files.
  • --export-fixes <file>: Exports the fixes to a YAML file in the format used by clang-apply-replacements.

To support this complex, stateful interaction with clangd, the tool's architecture has been significantly refactored.

Event-Driven Architecture

The newly introduced complex LSP interactions are expressed using Flows. Their focus is to make it somewhat easier to reason about the asynchronous operations and state management involved in interacting with clangd. To support this, a new event-driven architecture has been introduced using an EventBus.

This new architecture brings several benefits:

  • Crash Recovery: The tool can now gracefully recover from clangd crashes, retrying incomplete operations without losing state.
  • Extensibility: The flow-based design makes it easier to add new features in the future.
  • Clarity: Complex interactions with the LSP server are now encapsulated within their respective flows, making the code easier to understand and maintain.

Fix Application Logic

To support cross-file fixes, which are not available through inline code actions, the tool now performs a more sophisticated dance with the LSP server:

  1. It waits for clangd's background indexing to complete to ensure it has a complete view of the project.
  2. It explicitly requests code actions for each diagnostic.
  3. It executes the returned commands (like clangd.applyRename).
  4. It handles the resulting workspace/applyEdit requests from the server, which may contain edits for multiple files.

Other Changes

  • Bump cattrs to >= 25.1.0 to support newly introduced Union types.
  • Python 3.10+ is now required. This is necessary for the cattrs version. Python 3.9 is already EOL.
  • A set of end-to-end tests has been added for the new fixing functionality, as well as the existing diagnostic collection functionality.
  • Use logging for warnings instead of printing directly to stderr.

Performance

This has been evaluated on a non-trivial C++ codebase (~50 files). Using just the --diagnostics mode, the performance impact is around 1% overhead compared to the previous version.

This change introduces an automatic fixing capability to `clangd-tidy`, allowing it to apply clang-tidy's suggested fixes, including complex cross-file refactorings like renames. Cross-file fixes are something not supported by clang-tidy, only by clangd via LSP.

This is implemented via two new flags:
- `--fix`: Directly applies fixes to the source files.
- `--export-fixes <file>`: Exports the fixes to a YAML file in the format used by `clang-apply-replacements`.

To support this complex, stateful interaction with `clangd`, the tool's architecture has been significantly refactored.

### Event-Driven Architecture

The newly introduced complex LSP interactions are expressed using `Flow`s. Their focus is to make it somewhat easier to reason about the asynchronous operations and state management involved in interacting with `clangd`. To support this, a new event-driven architecture has been introduced using an `EventBus`.

This new architecture brings several benefits:
- **Crash Recovery:** The tool can now gracefully recover from `clangd` crashes, retrying incomplete operations without losing state.
- **Extensibility:** The flow-based design makes it easier to add new features in the future.
- **Clarity:** Complex interactions with the LSP server are now encapsulated within their respective flows, making the code easier to understand and maintain.

### Fix Application Logic

To support cross-file fixes, which are not available through inline code actions, the tool now performs a more sophisticated dance with the LSP server:
1.  It waits for `clangd`'s background indexing to complete to ensure it has a complete view of the project.
2.  It explicitly requests code actions for each diagnostic.
3.  It executes the returned commands (like `clangd.applyRename`).
4.  It handles the resulting `workspace/applyEdit` requests from the server, which may contain edits for multiple files.

### Other Changes

- Bump `cattrs` to `>= 25.1.0` to support newly introduced `Union` types.
- **Python 3.10+ is now required.** This is necessary for the `cattrs` version. Python 3.9 is already EOL.
- A set of end-to-end tests has been added for the new fixing functionality, as well as the existing diagnostic collection functionality.
- Use `logging` for warnings instead of printing directly to stderr.

### Performance

This has been evaluated on a non-trivial C++ codebase (~50 files). Using just the `--diagnostics` mode, the performance impact is around 1% overhead compared to the previous version.
@funous funous mentioned this pull request Nov 16, 2025
@lljbash
Copy link
Owner

lljbash commented Nov 25, 2025

Thank you for the substantial PR, I really appreciate the effort. I will need some time to review it carefully.

By the way, I am curious whether any AI tools were involved in the process, just so I can better understand the context while reviewing.

@funous
Copy link
Author

funous commented Nov 26, 2025

Thank you for the substantial PR, I really appreciate the effort. I will need some time to review it carefully.

By the way, I am curious whether any AI tools were involved in the process, just so I can better understand the context while reviewing.

Thanks for looking into it! I am aware that it is a substantial change, so please do take the time to go through it properly.

It was done with significant involvement of AI tools, for both Agentic development and just simple code completion. It was however quite a long and iterative process with a lot of human involvement. The LLMs didn't get me as far as I'd like, the whole thing was restructured a few times during development before I reached this design. The biggest problem I had during this process was leftover dead code from previous iterations, I have manually checked the code based on code coverage obtained by running the new tests to avoid that.

If you have any questions about the design or decisions made, I am happy to answer them myself. Most of these have not been a result of vibe coding, I can provide more insight where more clarity is needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants