fix(diagnostics): flag a deleted package declaration on live edit - #323
Merged
Merged
Conversation
FileChangeHandler's debounced textDocument/didChange path builds its own semantic-diagnostics list independently of DocumentHandler's handle_file_opened/republish_open_file_diagnostics, and never called missing_package_diagnostic -- so deleting a file's `package` line via a live edit never surfaced the "Missing package declaration" warning, even though opening (or reopening) the same file correctly did. Root cause confirmed via full history search (git log -S across this file's entire history, including before its extraction from the monolithic workspace actor): this call has never existed here. Not a regression from a recent change removing it -- a gap present since the missing-package diagnostic feature's first commit, which only ever wired the two DocumentHandler call sites. Extracted the closure into compute_debounced_semantic_diagnostics so this list is independently unit-testable, and added the missing call. The two DocumentHandler call sites are untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Address the diagnostic-task error handling and ensure the test covers the production publish path.
Pull request overview
Fixes live-edit diagnostics so deleting a package declaration produces the missing-package warning.
Changes:
- Extracts debounced semantic diagnostic computation.
- Adds package diagnostics to the debounced path.
- Adds regression coverage for deleted package declarations.
File summaries
| File | Summary and review findings |
|---|---|
src/workspace/file_change_handler.rs |
Adds package diagnostics. Address the moderate join-error handling issue and rename pkg_diag to package_diagnostic. |
src/workspace/file_change_handler_tests.rs |
Adds regression coverage. Exercise the actual publish path and rename abbreviated locals (tmp, diags_before, diags_after). |
Review details
Suppressed comments (5)
src/workspace/file_change_handler.rs:292
pkg_diagis an abbreviated name for a newly introduced local and conflicts with the repository's no-abbreviations convention. Please use the fullpackage_diagnosticname so the diagnostic's purpose is explicit.
if let Some(pkg_diag) = missing_package_diagnostic(&text_lines, uri) {
diagnostics.push(pkg_diag);
src/workspace/file_change_handler.rs:293
- When the
index_contentblocking task fails, the caller above substitutesdiagnostics_textwith an empty string. This unconditional check then treats the failed read as an empty Kotlin/Java file and publishes a false “Missing package declaration” warning for any path from which a package can be derived, instead of preserving the prior diagnostics or skipping this publish. Keep the join-error state distinct from a successful cache-hit result and avoid computing package diagnostics when the task failed.
let text_lines: Vec<String> = diagnostics_text.lines().map(str::to_owned).collect();
if let Some(pkg_diag) = missing_package_diagnostic(&text_lines, uri) {
diagnostics.push(pkg_diag);
}
src/workspace/file_change_handler_tests.rs:188
tmpis a newly introduced abbreviated local name; use a descriptive name such astemporary_workspaceto follow the repository's no-abbreviations convention and make the fixture setup clearer.
let tmp = tempfile::tempdir().unwrap();
src/workspace/file_change_handler_tests.rs:207
diags_beforeand the matchingdiags_afterlocal use the abbreviateddiagsform, which is explicitly disallowed for new names by the repository's no-abbreviations convention. Rename them todiagnostics_beforeanddiagnostics_after(and update their uses).
let diags_before =
super::compute_debounced_semantic_diagnostics(&indexer, &uri, with_package, 0);
src/workspace/file_change_handler_tests.rs:198
- This test constructs the handler with
client: None, so the debounced task returns beforecompute_debounced_semantic_diagnosticsis invoked (the early return is infile_change_handler.rs:136). The later direct calls test the helper, but would still pass if the production closure stopped calling it; please exercise the actual publish path with an injectable/mock client or otherwise assert the task's semantic-diagnostics output.
let mut handler = FileChangeHandler::new(Arc::clone(&indexer), None);
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- src/workspace/file_change_handler.rs: rename pkg_diag to package_diagnostic (no-abbreviations convention). - Same file: fix a real false-positive the new package check could produce -- when the index_content spawn_blocking join fails, the caller substitutes an empty string for diagnostics_text. The other diagnostics degrade gracefully against that (an empty file has no call args/nullable dots/imports to flag), but missing_package_diagnostic does not -- an empty string genuinely has no package line, so it would publish a real, false warning for a file whose content was never actually re-read. Threaded a diagnostics_text_is_current flag through to gate the package check specifically. - Added a regression test for the false positive (confirmed real red without the gate, real green with it) and a doc-comment note arguing the remaining residual gap the review flagged (the closure calling compute_debounced_semantic_diagnostics could still be edited to stop calling it, and no test proves otherwise -- closing that needs a real/fake tower_lsp::Client this codebase has no precedent for; named explicitly rather than silently left unaddressed). - src/workspace/file_change_handler_tests.rs: renamed tmp, diags_before, diags_after to full words per the same convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The debounced
textDocument/didChangediagnostics path (FileChangeHandler)builds its own semantic-diagnostics list independently of
DocumentHandler'shandle_file_opened/republish_open_file_diagnostics, and never calledmissing_package_diagnostic— so deleting a file'spackageline via a liveedit never surfaced the "Missing package declaration" warning, even though
opening (or reopening) the same file correctly did.
Root cause, confirmed not guessed:
git log -S"missing_package_diagnostic"across this file's entire history (including before its extraction from the
monolithic workspace actor in
refactor(workspace): extract actor handlers (w5b)) returns zero hits. This call has never existed in this file. It's nota regression from a recent change removing it — the missing-package
diagnostic feature, when first added, only ever wired the two
DocumentHandlercall sites. Three near-identical diagnostic-list-buildingblocks exist across two files, and one of them was incomplete from day one.
Fix
Extracted the closure into
compute_debounced_semantic_diagnosticsso thislist is independently unit-testable (it wasn't reachable as a standalone unit
before — testing it required either mocking
tower_lsp::Clientor goingthrough the full async debounce pipeline with no way to inspect what got
published), then added the missing call. The two
DocumentHandlercall sitesare untouched.
Test plan
debounced_diagnostics_flag_a_deleted_package_declaration— real red before the fix (confirmed:
panicked ... got: []), realgreen after
cargo test— 1967 passed, 0 failed, 3 ignoredcargo clippy --all-targets -- -D warnings— cleancargo fmt -- --check— clean🤖 Generated with Claude Code