From fbefb5df6a017561314286345b83c45c0a526dfb Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Sun, 5 Jul 2026 21:17:43 -0400 Subject: [PATCH] Softens unused import diagnostic to "may be unused" instead of "is unused", per #3949 Fixes #3949 Documentation for IDE-only unused-import hint added to IDE-features.mdx Avoided error-kinds.mdx because it's only an IDE hint --- pyrefly/lib/lsp/non_wasm/server.rs | 2 +- pyrefly/lib/test/lsp/diagnostic.rs | 12 ++++++------ pyrefly/lib/test/lsp/lsp_interaction/diagnostic.rs | 8 ++++---- website/docs/IDE-features.mdx | 3 +++ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pyrefly/lib/lsp/non_wasm/server.rs b/pyrefly/lib/lsp/non_wasm/server.rs index 78bba6936f..f8b7ff0d69 100644 --- a/pyrefly/lib/lsp/non_wasm/server.rs +++ b/pyrefly/lib/lsp/non_wasm/server.rs @@ -5370,7 +5370,7 @@ impl Server { range: lsp_range, severity: Some(DiagnosticSeverity::HINT), source: Some("Pyrefly".to_owned()), - message: format!("Import `{}` is unused", unused.name.as_str()).into(), + message: format!("Import `{}` may be unused", unused.name.as_str()).into(), code: Some(NumberOrString::String("unused-import".to_owned())), code_description: None, related_information: None, diff --git a/pyrefly/lib/test/lsp/diagnostic.rs b/pyrefly/lib/test/lsp/diagnostic.rs index 7dcbca8004..4ca08437ea 100644 --- a/pyrefly/lib/test/lsp/diagnostic.rs +++ b/pyrefly/lib/test/lsp/diagnostic.rs @@ -23,7 +23,7 @@ fn get_unused_import_diagnostics(state: &State, handle: &Handle) -> String { if i > 0 { report.push_str(", "); } - report.push_str(&format!("Import `{}` is unused", unused.name.as_str())); + report.push_str(&format!("Import `{}` may be unused", unused.name.as_str())); } report } else { @@ -76,7 +76,7 @@ def foo() -> str: let (handles, state) = mk_multi_file_state(&[("main", code)], Require::Exports, true); let handle = handles.get("main").unwrap(); let report = get_unused_import_diagnostics(&state, handle); - assert_eq!(report, "Import `os` is unused"); + assert_eq!(report, "Import `os` may be unused"); } #[test] @@ -104,7 +104,7 @@ def foo() -> str: let (handles, state) = mk_multi_file_state(&[("main", code)], Require::Exports, true); let handle = handles.get("main").unwrap(); let report = get_unused_import_diagnostics(&state, handle); - assert_eq!(report, "Import `os` is unused"); + assert_eq!(report, "Import `os` may be unused"); } #[test] @@ -132,7 +132,7 @@ def process(items: List[str]): let (handles, state) = mk_multi_file_state(&[("main", code)], Require::Exports, true); let handle = handles.get("main").unwrap(); let report = get_unused_import_diagnostics(&state, handle); - assert_eq!(report, "Import `Dict` is unused"); + assert_eq!(report, "Import `Dict` may be unused"); } #[test] @@ -384,7 +384,7 @@ import os as operating_system let (handles, state) = mk_multi_file_state(&[("main", code)], Require::Exports, true); let handle = handles.get("main").unwrap(); let report = get_unused_import_diagnostics(&state, handle); - assert_eq!(report, "Import `operating_system` is unused"); + assert_eq!(report, "Import `operating_system` may be unused"); } #[test] @@ -396,5 +396,5 @@ from math import tau as my_tau let (handles, state) = mk_multi_file_state(&[("main", code)], Require::Exports, true); let handle = handles.get("main").unwrap(); let report = get_unused_import_diagnostics(&state, handle); - assert_eq!(report, "Import `my_tau` is unused"); + assert_eq!(report, "Import `my_tau` may be unused"); } diff --git a/pyrefly/lib/test/lsp/lsp_interaction/diagnostic.rs b/pyrefly/lib/test/lsp/lsp_interaction/diagnostic.rs index 4cf3c09fd2..2d7d2c066d 100644 --- a/pyrefly/lib/test/lsp/lsp_interaction/diagnostic.rs +++ b/pyrefly/lib/test/lsp/lsp_interaction/diagnostic.rs @@ -879,7 +879,7 @@ fn test_unused_import_diagnostic() { "items": [ { "code": "unused-import", - "message": "Import `os` is unused", + "message": "Import `os` may be unused", "range": { "start": {"line": 6, "character": 7}, "end": {"line": 6, "character": 9} @@ -928,7 +928,7 @@ fn test_unused_from_import_diagnostic() { "items": [ { "code": "unused-import", - "message": "Import `Dict` is unused", + "message": "Import `Dict` may be unused", "range": { "start": {"line": 6, "character": 19}, "end": {"line": 6, "character": 23} @@ -1613,7 +1613,7 @@ fn test_untyped_import_diagnostic_does_not_show_non_recommended_packages() { "items": [ { "code": "unused-import", - "message": "Import `boto3` is unused", + "message": "Import `boto3` may be unused", "range": { "start": {"line": 5, "character": 7}, "end": {"line": 5, "character": 12} @@ -1730,7 +1730,7 @@ fn test_untyped_import_diagnostic_shows_error_for_recommended_packages() { }, { "code": "unused-import", - "message": "Import `django` is unused", + "message": "Import `django` may be unused", "range": { "start": {"line": 5, "character": 7}, "end": {"line": 5, "character": 13} diff --git a/website/docs/IDE-features.mdx b/website/docs/IDE-features.mdx index a183f5b31f..7a950496e7 100644 --- a/website/docs/IDE-features.mdx +++ b/website/docs/IDE-features.mdx @@ -437,6 +437,9 @@ Type errors and warnings from Pyrefly’s checker appear in the diagnostics pane preload="metadata" /> +Pyrefly also greys out unused imports and unused local variables directly in the editor. These are IDE-only hints and do not appear in the CLI. +Because Pyrefly does not model import side effects, an import used only for its side effects, for example a plugin that registers accessors or methods on an existing class, such as `pyjanitor` extending `pandas.DataFrame`, may be greyed even though it is required at runtime. Thus the hint is "may be unused" rather than "is unused." + --- ### [Semantic tokens](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens)