Skip to content

Unused keys: Handles false positives for child nodes#721

Open
davidwessman wants to merge 3 commits intomainfrom
713-unused-false-positive
Open

Unused keys: Handles false positives for child nodes#721
davidwessman wants to merge 3 commits intomainfrom
713-unused-false-positive

Conversation

@davidwessman
Copy link
Copy Markdown
Collaborator

- When a parent node is used as the key, it causes false positives for
  its children.
- Fixes #713
@davidwessman davidwessman force-pushed the 713-unused-false-positive branch from 73af62b to 14fab54 Compare April 11, 2026 12:39
@davidwessman davidwessman changed the title Unused keys: Handles false positives for leaf Unused keys: Handles false positives for child nodes Apr 11, 2026
@davidwessman davidwessman requested review from Copilot and glebm April 11, 2026 12:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes false positives in i18n-tasks unused where using a parent key (e.g. t(:section)) could cause nested child keys under the same root to be reported as unused (Issue #713).

Changes:

  • Updates unused-key filtering to treat a key as used when any ancestor key is used.
  • Adds an RSpec regression test reproducing the shadowing case from #713.
  • Adds an entry to the Unreleased changelog.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
lib/i18n/tasks/unused_keys.rb Adjusts unused key selection to account for ancestor/descendant “used” relationships.
spec/unused_keys_spec.rb Adds a regression spec ensuring nested keys aren’t flagged unused when the root key is used.
CHANGES.md Documents the behavior change in the Unreleased section.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/i18n/tasks/unused_keys.rb Outdated
Comment thread CHANGES.md Outdated
Comment thread spec/unused_keys_spec.rb
Comment on lines +36 to +37
TestCodebase.in_test_app_dir { ex.call }
TestCodebase.teardown
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

TestCodebase.teardown won’t run if the example raises (e.g., a failing expectation), which can leave files behind and potentially affect subsequent specs. Wrap the in_test_app_dir { ex.call } call in an ensure (or use an after hook) so teardown is guaranteed to execute.

Suggested change
TestCodebase.in_test_app_dir { ex.call }
TestCodebase.teardown
begin
TestCodebase.in_test_app_dir { ex.call }
ensure
TestCodebase.teardown
end

Copilot uses AI. Check for mistakes.
davidwessman and others added 2 commits April 11, 2026 14:53
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 15 to 21
def unused_tree(locale: base_locale, strict: nil)
used_key_names = used_tree(strict: true).key_names
used_key_names = used_tree(strict: true).key_names.to_set
collapse_plural_nodes!(data[locale].select_keys do |key, _node|
!ignore_key?(key, :unused) &&
(strict || !used_in_expr?(key)) &&
!used_key_names.include?(depluralize_key(key, locale))
!key_used?(depluralize_key(key, locale), used_key_names)
end)
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

key_used? makes any descendant key count as used when an ancestor key is used. This avoids the unused false positive, but it also changes the semantics of unused (it will now hide genuinely-unused descendants whenever a parent/ancestor is referenced) and does not address the underlying used-keys tree collision where inserting a leaf after its children can replace the subtree (see lib/i18n/tasks/data/tree/siblings.rb#set, which removes an existing node on leaf insert). Consider fixing the collision at the tree-building layer (merge leaf occurrences into an existing node instead of replacing it) so used_tree and other features relying on it remain accurate, and keep unused behavior narrowly scoped to that fix if possible.

Copilot uses AI. Check for mistakes.
Comment thread spec/unused_keys_spec.rb
Comment on lines +41 to +43
unused_key_names = task.unused_keys.key_names(root: true)
expect(unused_key_names).not_to include("en.section.item.title")
expect(unused_key_names).not_to include("en.section.item.subtitle")
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This spec only asserts that nested keys are not reported as unused; with the current implementation that can pass even if used_tree still drops nested keys when a leaf key is encountered later in the scan (because descendants are treated as used via their ancestor). To ensure the original regression is fully covered (and to protect used_tree/find output), consider also asserting that task.used_tree(strict: true).key_names includes the nested keys.

Suggested change
unused_key_names = task.unused_keys.key_names(root: true)
expect(unused_key_names).not_to include("en.section.item.title")
expect(unused_key_names).not_to include("en.section.item.subtitle")
unused_key_names = task.unused_keys.key_names(root: true)
used_key_names = task.used_tree(strict: true).key_names
expect(unused_key_names).not_to include("en.section.item.title")
expect(unused_key_names).not_to include("en.section.item.subtitle")
expect(used_key_names).to include("en.section.item.title")
expect(used_key_names).to include("en.section.item.subtitle")

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unused false positive when a leaf key and nested keys share the same root

2 participants