aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs')
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs229
1 files changed, 229 insertions, 0 deletions
diff --git a/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
new file mode 100644
index 000000000..63de54570
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
@@ -0,0 +1,229 @@
1use hir::db::AstDatabase;
2use ide_db::{assists::Assist, source_change::SourceChange};
3use syntax::AstNode;
4use text_edit::TextEdit;
5
6use crate::{fix, Diagnostic, DiagnosticsContext};
7
8// Diagnostic: missing-ok-or-some-in-tail-expr
9//
10// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
11// or if a block that should return `Option` returns a value not wrapped in `Some`.
12//
13// Example:
14//
15// ```rust
16// fn foo() -> Result<u8, ()> {
17// 10
18// }
19// ```
20pub(crate) fn missing_ok_or_some_in_tail_expr(
21 ctx: &DiagnosticsContext<'_>,
22 d: &hir::MissingOkOrSomeInTailExpr,
23) -> Diagnostic {
24 Diagnostic::new(
25 "missing-ok-or-some-in-tail-expr",
26 format!("wrap return expression in {}", d.required),
27 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
28 )
29 .with_fixes(fixes(ctx, d))
30}
31
32fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Option<Vec<Assist>> {
33 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
34 let tail_expr = d.expr.value.to_node(&root);
35 let tail_expr_range = tail_expr.syntax().text_range();
36 let replacement = format!("{}({})", d.required, tail_expr.syntax());
37 let edit = TextEdit::replace(tail_expr_range, replacement);
38 let source_change =
39 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
40 let name = if d.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
41 Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)])
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::tests::{check_diagnostics, check_fix};
47
48 #[test]
49 fn test_wrap_return_type_option() {
50 check_fix(
51 r#"
52//- /main.rs crate:main deps:core
53use core::option::Option::{self, Some, None};
54
55fn div(x: i32, y: i32) -> Option<i32> {
56 if y == 0 {
57 return None;
58 }
59 x / y$0
60}
61//- /core/lib.rs crate:core
62pub mod result {
63 pub enum Result<T, E> { Ok(T), Err(E) }
64}
65pub mod option {
66 pub enum Option<T> { Some(T), None }
67}
68"#,
69 r#"
70use core::option::Option::{self, Some, None};
71
72fn div(x: i32, y: i32) -> Option<i32> {
73 if y == 0 {
74 return None;
75 }
76 Some(x / y)
77}
78"#,
79 );
80 }
81
82 #[test]
83 fn test_wrap_return_type() {
84 check_fix(
85 r#"
86//- /main.rs crate:main deps:core
87use core::result::Result::{self, Ok, Err};
88
89fn div(x: i32, y: i32) -> Result<i32, ()> {
90 if y == 0 {
91 return Err(());
92 }
93 x / y$0
94}
95//- /core/lib.rs crate:core
96pub mod result {
97 pub enum Result<T, E> { Ok(T), Err(E) }
98}
99pub mod option {
100 pub enum Option<T> { Some(T), None }
101}
102"#,
103 r#"
104use core::result::Result::{self, Ok, Err};
105
106fn div(x: i32, y: i32) -> Result<i32, ()> {
107 if y == 0 {
108 return Err(());
109 }
110 Ok(x / y)
111}
112"#,
113 );
114 }
115
116 #[test]
117 fn test_wrap_return_type_handles_generic_functions() {
118 check_fix(
119 r#"
120//- /main.rs crate:main deps:core
121use core::result::Result::{self, Ok, Err};
122
123fn div<T>(x: T) -> Result<T, i32> {
124 if x == 0 {
125 return Err(7);
126 }
127 $0x
128}
129//- /core/lib.rs crate:core
130pub mod result {
131 pub enum Result<T, E> { Ok(T), Err(E) }
132}
133pub mod option {
134 pub enum Option<T> { Some(T), None }
135}
136"#,
137 r#"
138use core::result::Result::{self, Ok, Err};
139
140fn div<T>(x: T) -> Result<T, i32> {
141 if x == 0 {
142 return Err(7);
143 }
144 Ok(x)
145}
146"#,
147 );
148 }
149
150 #[test]
151 fn test_wrap_return_type_handles_type_aliases() {
152 check_fix(
153 r#"
154//- /main.rs crate:main deps:core
155use core::result::Result::{self, Ok, Err};
156
157type MyResult<T> = Result<T, ()>;
158
159fn div(x: i32, y: i32) -> MyResult<i32> {
160 if y == 0 {
161 return Err(());
162 }
163 x $0/ y
164}
165//- /core/lib.rs crate:core
166pub mod result {
167 pub enum Result<T, E> { Ok(T), Err(E) }
168}
169pub mod option {
170 pub enum Option<T> { Some(T), None }
171}
172"#,
173 r#"
174use core::result::Result::{self, Ok, Err};
175
176type MyResult<T> = Result<T, ()>;
177
178fn div(x: i32, y: i32) -> MyResult<i32> {
179 if y == 0 {
180 return Err(());
181 }
182 Ok(x / y)
183}
184"#,
185 );
186 }
187
188 #[test]
189 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
190 check_diagnostics(
191 r#"
192//- /main.rs crate:main deps:core
193use core::result::Result::{self, Ok, Err};
194
195fn foo() -> Result<(), i32> { 0 }
196
197//- /core/lib.rs crate:core
198pub mod result {
199 pub enum Result<T, E> { Ok(T), Err(E) }
200}
201pub mod option {
202 pub enum Option<T> { Some(T), None }
203}
204"#,
205 );
206 }
207
208 #[test]
209 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
210 check_diagnostics(
211 r#"
212//- /main.rs crate:main deps:core
213use core::result::Result::{self, Ok, Err};
214
215enum SomeOtherEnum { Ok(i32), Err(String) }
216
217fn foo() -> SomeOtherEnum { 0 }
218
219//- /core/lib.rs crate:core
220pub mod result {
221 pub enum Result<T, E> { Ok(T), Err(E) }
222}
223pub mod option {
224 pub enum Option<T> { Some(T), None }
225}
226"#,
227 );
228 }
229}