Skip to content

Commit 8ea1896

Browse files
authored
[ty] followup: add-import action for reveal_type too (#21668)
1 parent e548ce1 commit 8ea1896

File tree

3 files changed

+142
-5
lines changed

3 files changed

+142
-5
lines changed

crates/ty_ide/src/code_action.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use ruff_diagnostics::Edit;
55
use ruff_text_size::TextRange;
66
use ty_project::Db;
77
use ty_python_semantic::create_suppression_fix;
8-
use ty_python_semantic::types::UNRESOLVED_REFERENCE;
8+
use ty_python_semantic::lint::LintId;
9+
use ty_python_semantic::types::{UNDEFINED_REVEAL, UNRESOLVED_REFERENCE};
910

1011
/// A `QuickFix` Code Action
1112
#[derive(Debug, Clone)]
@@ -28,12 +29,17 @@ pub fn code_actions(
2829

2930
let mut actions = Vec::new();
3031

31-
if lint_id.name() == UNRESOLVED_REFERENCE.name()
32+
// Suggest imports for unresolved references (often ideal)
33+
// TODO: suggest qualifying with an already imported symbol
34+
let is_unresolved_reference =
35+
lint_id == LintId::of(&UNRESOLVED_REFERENCE) || lint_id == LintId::of(&UNDEFINED_REVEAL);
36+
if is_unresolved_reference
3237
&& let Some(import_quick_fix) = create_import_symbol_quick_fix(db, file, diagnostic_range)
3338
{
3439
actions.extend(import_quick_fix);
3540
}
3641

42+
// Suggest just suppressing the lint (always a valid option, but never ideal)
3743
actions.push(QuickFix {
3844
title: format!("Ignore '{}' for this line", lint_id.name()),
3945
edits: create_suppression_fix(db, file, lint_id, diagnostic_range).into_edits(),

crates/ty_server/tests/e2e/code_actions.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,44 @@ x: Literal[1] = 1
132132
";
133133

134134
let ty_toml = SystemPath::new("ty.toml");
135-
let ty_toml_content = "\
136-
[rules]
137-
unused-ignore-comment = \"warn\"
135+
let ty_toml_content = "";
136+
137+
let mut server = TestServerBuilder::new()?
138+
.with_workspace(workspace_root, None)?
139+
.with_file(ty_toml, ty_toml_content)?
140+
.with_file(foo, foo_content)?
141+
.enable_pull_diagnostics(true)
142+
.build()
143+
.wait_until_workspaces_are_initialized();
144+
145+
server.open_text_document(foo, foo_content, 1);
146+
147+
// Wait for diagnostics to be computed.
148+
let diagnostics = server.document_diagnostic_request(foo, None);
149+
let range = full_range(foo_content);
150+
let code_action_params = code_actions_at(&server, diagnostics, foo, range);
151+
152+
// Get code actions
153+
let code_action_id = server.send_request::<CodeActionRequest>(code_action_params);
154+
let code_actions = server.await_response::<CodeActionRequest>(&code_action_id);
155+
156+
insta::assert_json_snapshot!(code_actions);
157+
158+
Ok(())
159+
}
160+
161+
// `Literal` is available from two places so we should suggest two possible imports
162+
#[test]
163+
fn code_action_undefined_reveal_type() -> Result<()> {
164+
let workspace_root = SystemPath::new("src");
165+
let foo = SystemPath::new("src/foo.py");
166+
let foo_content = "\
167+
reveal_type(1)
138168
";
139169

170+
let ty_toml = SystemPath::new("ty.toml");
171+
let ty_toml_content = "";
172+
140173
let mut server = TestServerBuilder::new()?
141174
.with_workspace(workspace_root, None)?
142175
.with_file(ty_toml, ty_toml_content)?
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
source: crates/ty_server/tests/e2e/code_actions.rs
3+
expression: code_actions
4+
---
5+
[
6+
{
7+
"title": "import typing.reveal_type",
8+
"kind": "quickfix",
9+
"diagnostics": [
10+
{
11+
"range": {
12+
"start": {
13+
"line": 0,
14+
"character": 0
15+
},
16+
"end": {
17+
"line": 0,
18+
"character": 11
19+
}
20+
},
21+
"severity": 2,
22+
"code": "undefined-reveal",
23+
"codeDescription": {
24+
"href": "https://ty.dev/rules#undefined-reveal"
25+
},
26+
"source": "ty",
27+
"message": "`reveal_type` used without importing it",
28+
"relatedInformation": []
29+
}
30+
],
31+
"edit": {
32+
"changes": {
33+
"file://<temp_dir>/src/foo.py": [
34+
{
35+
"range": {
36+
"start": {
37+
"line": 0,
38+
"character": 0
39+
},
40+
"end": {
41+
"line": 0,
42+
"character": 0
43+
}
44+
},
45+
"newText": "from typing import reveal_type\n"
46+
}
47+
]
48+
}
49+
},
50+
"isPreferred": true
51+
},
52+
{
53+
"title": "Ignore 'undefined-reveal' for this line",
54+
"kind": "quickfix",
55+
"diagnostics": [
56+
{
57+
"range": {
58+
"start": {
59+
"line": 0,
60+
"character": 0
61+
},
62+
"end": {
63+
"line": 0,
64+
"character": 11
65+
}
66+
},
67+
"severity": 2,
68+
"code": "undefined-reveal",
69+
"codeDescription": {
70+
"href": "https://ty.dev/rules#undefined-reveal"
71+
},
72+
"source": "ty",
73+
"message": "`reveal_type` used without importing it",
74+
"relatedInformation": []
75+
}
76+
],
77+
"edit": {
78+
"changes": {
79+
"file://<temp_dir>/src/foo.py": [
80+
{
81+
"range": {
82+
"start": {
83+
"line": 0,
84+
"character": 14
85+
},
86+
"end": {
87+
"line": 0,
88+
"character": 14
89+
}
90+
},
91+
"newText": " # ty:ignore[undefined-reveal]"
92+
}
93+
]
94+
}
95+
},
96+
"isPreferred": false
97+
}
98+
]

0 commit comments

Comments
 (0)