Skip to content

feat(code-action): add remove unused formal parameter quickfix#778

Open
takeokunn wants to merge 2 commits into
mainfrom
feature/add-code-action-12
Open

feat(code-action): add remove unused formal parameter quickfix#778
takeokunn wants to merge 2 commits into
mainfrom
feature/add-code-action-12

Conversation

@takeokunn

Copy link
Copy Markdown
Collaborator

Summary

Add code action to remove unused formal parameters from lambda expressions.

Example Transformation:

Before After
{x, y}: x {x}: x
{x, y, z}: x + z {x, z}: x + z
{x ? 1, y}: y {y}: y
{x, y, ...}: y {y, ...}: y

Changes

File Description
nixd/lib/Controller/CodeActions/RemoveUnusedFormal.h Header file for remove-unused-formal action
nixd/lib/Controller/CodeActions/RemoveUnusedFormal.cpp Implementation with comma handling logic
nixd/lib/Controller/CodeAction.cpp Register the action (requires PM, Diagnostics)
nixd/lib/meson.build Add source to build

Behavior

Action offered when:

  • Cursor is on an unused formal parameter
  • Diagnostic DK_UnusedDefLambdaNoArg_Formal or DK_UnusedDefLambdaWithArg_Formal exists

Action NOT offered when:

  • Formal is used in the lambda body
  • Cursor is on ellipsis (...)

Handles comma placement:

  1. First formal {x, y}: → remove x and next formal's leading comma
  2. Non-first formal {x, y}: → remove y (comma is part of its range)

Test Plan

  • All 6 remove-unused-formal tests pass
  • Build succeeds
  • Tests cover:
    • basic.md: Remove last formal
    • first-formal.md: Remove first formal (special comma handling)
    • middle-formal.md: Remove middle formal
    • with-default.md: Remove formal with default value
    • with-ellipsis.md: Remove formal when ellipsis present
    • all-used-negative.md: Negative - all formals used (no action offered)

Related

@inclyc inclyc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel these changes might be better implemented in libnixf, as seen in draft PR #755. Is there a specific reason for choosing the current implementation over that one? Because we already landed #777 ?

@takeokunn

Copy link
Copy Markdown
Collaborator Author

@inclyc
I was aware that Sema::removeFormal() already exists in libnixf.
I intentionally chose the Controller-layer approach to stay consistent with the other code actions (especially #777/AddToFormals) and to avoid touching libnixf in this PR.

That said, I agree that attaching the fix directly to the diagnostic in VariableLookup.cpp would be the more natural fit for this case, since the Formals context is already available at diagnostic emission time — unlike AddToFormals, which needs ParentMap to find the enclosing lambda.

What would you prefer?

  1. Rework this PR to attach the fix in libnixf (using Sema::removeFormal()), keeping only the LSP conversion in the Controller
  2. Keep the current approach for now and refactor later

@inclyc

inclyc commented Feb 7, 2026

Copy link
Copy Markdown
Member

I believe this PR (#778) should be refactored to be primarily based on diagnostic information in libnixf, while #777 can remain as it is.

The reasons are:

(1) The existing structure based on fix-it hints is very prevalent and common in libnixf. Rather than implementing it as a standalone code action, I think it is more natural to utilize diagnostic information for the fix.

(2) On the other hand, if a formal parameter is unused, automatically removing it feels natural and provides a unique solution. In contrast, "AddToFormals" appears to be "one of several possible solutions," which makes it more suitable to be implemented as a separate Code Action.

Furthermore, considering "technical perfectionism," open-source communities generally don't share the same sense of "urgency" found in commercial software. If you're willing, I would also prefer to maintain a more robust project rather than leaving this as technical debt indefinitely. Implementing this specific task within Code Actions feels somewhat "awkward" and counter-intuitive.


この PR (#778) は libnixf の診断情報(diagnostics)をベースにする形にリファクタリングすべきですが、#777 は現状維持でよいと考えています。

理由は以下の通りです:

(1) libnixf において、既存の fix-it hints に基づく構造は非常に一般的で広く使われています。独立した Code Action として実装するよりも、診断情報を利用して修正を行う方が自然だと考えます。

(2) 一方で、形式引数が**未使用(unused)**である場合、それを自動的に削除することは自然であり、かつ解決策も一意です。それに対して "AddToFormals" は「複数の解決策のうちの一つ」という側面が強いため、個別の Code Action として記述する方が適しています。

また、「技術的なこだわり(技術潔癖)」という面から見ても、オープンソースコミュニティは通常、商用ソフトウェアのような「急ぎの対応」を追求する場ではないと考えています。もし許されるのであれば、これを技術負債として放置するのではなく、より堅牢なプロジェクトとして維持していきたいです。この機能を Code Actions の中で実装し続けるのは、どこか「不自然(拧巴)」で無理があると感じています。

@takeokunn

Copy link
Copy Markdown
Collaborator Author

@inclyc
Thanks for the detailed feedback.
I'll refactor this PR to use libnixf instead.

@takeokunn takeokunn force-pushed the feature/add-code-action-12 branch 2 times, most recently from ea556ea to 8558171 Compare February 14, 2026 05:11
…eters

Attach fix-it hints to unused formal parameter diagnostics in
VariableLookup, using Sema::removeFormal() to generate TextEdits.
The Controller simply converts these to LSP code actions.

Handles comma placement for first, middle, and last formals,
as well as formals with default values and ellipsis.
@takeokunn takeokunn force-pushed the feature/add-code-action-12 branch from 8558171 to 14e726b Compare February 14, 2026 05:14
@takeokunn

Copy link
Copy Markdown
Collaborator Author

@inclyc
Refactored the code to use libnixf. Ready for review!

@takeokunn takeokunn requested a review from inclyc February 14, 2026 05:25
@takeokunn takeokunn added enhancement New feature or request nixd:controller labels Feb 14, 2026
class Fix {
std::vector<TextEdit> Edits;
std::string Message;
bool Preferred = false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since libnixf stores fixes in a std::vector and most diagnostics currently yield only a single solution, adding an is_preferred boolean field makes sense primarily as a tie-breaker for multi-fix scenarios. I agree with this addition for LSP alignment (CodeAction.isPreferred), but we should carefully define its interaction with the existing diagnostic structure—specifically how to handle cases where multiple fixes (or none) are marked as preferred to ensure consistent client behavior.

One design detail to consider: if a diagnostic provides only a single fix(), should we mark it as is_preferred by default? In my view, we should be cautious—isPreferred in LSP usually signals that the fix can be safely auto-applied (e.g., via "Fix All"). If a fix has side effects, it might be better to keep it false even if it's the only option. What are your thoughts on the default behavior for single-fix scenarios?


libnixf では現在、fix を std::vector で管理しており、ほとんどの Diagnostic が単一の解決策しか提供していないことを考えると、is_preferred という bool フィールドの導入は、主に複数の候補が存在する場合の優先順位付けとして機能すると理解しています。LSP の仕様(CodeAction.isPreferred)に準拠させるという点では賛成ですが、既存の Diagnostic 構造との論理的な関係については慎重に定義すべきだと考えています。具体的には、複数の fix が preferred とマークされている場合、あるいは一つもマークされていない場合に、クライアント側で一貫した挙動を保証するための設計が必要です。

設計上の詳細について一点確認させてください。ある Diagnostic に対して fix() が一つしかない場合、それをデフォルトで is_preferred とすべきでしょうか?個人的には、少し慎重になる必要があると考えています。LSP における isPreferred は通常、その修正が(「Fix All」などを通じて)安全に自動適用できることを意味します。たとえ解決策が一つしかなくても、副作用があるような修正の場合は false のままにしておく方が適切かもしれません。単一 fix の場合のデフォルト挙動について、どのようにお考えでしょうか?

@Malix-Labs

Copy link
Copy Markdown
Contributor

Also see #796

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

Labels

enhancement New feature or request nixd:controller

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants