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.rs165
1 files changed, 165 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..c0edcd7d3
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
@@ -0,0 +1,165 @@
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//- minicore: option, result
53fn div(x: i32, y: i32) -> Option<i32> {
54 if y == 0 {
55 return None;
56 }
57 x / y$0
58}
59"#,
60 r#"
61fn div(x: i32, y: i32) -> Option<i32> {
62 if y == 0 {
63 return None;
64 }
65 Some(x / y)
66}
67"#,
68 );
69 }
70
71 #[test]
72 fn test_wrap_return_type() {
73 check_fix(
74 r#"
75//- minicore: option, result
76fn div(x: i32, y: i32) -> Result<i32, ()> {
77 if y == 0 {
78 return Err(());
79 }
80 x / y$0
81}
82"#,
83 r#"
84fn div(x: i32, y: i32) -> Result<i32, ()> {
85 if y == 0 {
86 return Err(());
87 }
88 Ok(x / y)
89}
90"#,
91 );
92 }
93
94 #[test]
95 fn test_wrap_return_type_handles_generic_functions() {
96 check_fix(
97 r#"
98//- minicore: option, result
99fn div<T>(x: T) -> Result<T, i32> {
100 if x == 0 {
101 return Err(7);
102 }
103 $0x
104}
105"#,
106 r#"
107fn div<T>(x: T) -> Result<T, i32> {
108 if x == 0 {
109 return Err(7);
110 }
111 Ok(x)
112}
113"#,
114 );
115 }
116
117 #[test]
118 fn test_wrap_return_type_handles_type_aliases() {
119 check_fix(
120 r#"
121//- minicore: option, result
122type MyResult<T> = Result<T, ()>;
123
124fn div(x: i32, y: i32) -> MyResult<i32> {
125 if y == 0 {
126 return Err(());
127 }
128 x $0/ y
129}
130"#,
131 r#"
132type MyResult<T> = Result<T, ()>;
133
134fn div(x: i32, y: i32) -> MyResult<i32> {
135 if y == 0 {
136 return Err(());
137 }
138 Ok(x / y)
139}
140"#,
141 );
142 }
143
144 #[test]
145 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
146 check_diagnostics(
147 r#"
148//- minicore: option, result
149fn foo() -> Result<(), i32> { 0 }
150"#,
151 );
152 }
153
154 #[test]
155 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
156 check_diagnostics(
157 r#"
158//- minicore: option, result
159enum SomeOtherEnum { Ok(i32), Err(String) }
160
161fn foo() -> SomeOtherEnum { 0 }
162"#,
163 );
164 }
165}