Skip to content

Commit b2dbe6e

Browse files
committed
Add fix to wrap return expression in Some
1 parent 981a0d7 commit b2dbe6e

File tree

7 files changed

+87
-21
lines changed

7 files changed

+87
-21
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ pub use hir_expand::diagnostics::{
44
Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
55
};
66
pub use hir_ty::diagnostics::{
7-
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
7+
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
88
NoSuchField, RemoveThisSemicolon,
99
};

crates/hir_def/src/path.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ pub use hir_expand::name as __name;
305305
macro_rules! __known_path {
306306
(core::iter::IntoIterator) => {};
307307
(core::result::Result) => {};
308+
(core::option::Option) => {};
308309
(core::ops::Range) => {};
309310
(core::ops::RangeFrom) => {};
310311
(core::ops::RangeFull) => {};

crates/hir_expand/src/name.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ pub mod known {
164164
future,
165165
result,
166166
boxed,
167+
option,
167168
// Components of known path (type name)
168169
Iterator,
169170
IntoIterator,
@@ -172,6 +173,7 @@ pub mod known {
172173
Ok,
173174
Future,
174175
Result,
176+
Option,
175177
Output,
176178
Target,
177179
Box,

crates/hir_ty/src/diagnostics.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,10 @@ impl Diagnostic for MissingMatchArms {
186186
}
187187
}
188188

189-
// Diagnostic: missing-ok-in-tail-expr
189+
// Diagnostic: missing-ok-or-some-in-tail-expr
190190
//
191-
// This diagnostic is triggered if block that should return `Result` returns a value not wrapped in `Ok`.
191+
// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
192+
// or if a block that should return `Option` returns a value not wrapped in `Some`.
192193
//
193194
// Example:
194195
//
@@ -198,17 +199,19 @@ impl Diagnostic for MissingMatchArms {
198199
// }
199200
// ```
200201
#[derive(Debug)]
201-
pub struct MissingOkInTailExpr {
202+
pub struct MissingOkOrSomeInTailExpr {
202203
pub file: HirFileId,
203204
pub expr: AstPtr<ast::Expr>,
205+
// `Some` or `Ok` depending on whether the return type is Result or Option
206+
pub required: String,
204207
}
205208

206-
impl Diagnostic for MissingOkInTailExpr {
209+
impl Diagnostic for MissingOkOrSomeInTailExpr {
207210
fn code(&self) -> DiagnosticCode {
208-
DiagnosticCode("missing-ok-in-tail-expr")
211+
DiagnosticCode("missing-ok-or-some-in-tail-expr")
209212
}
210213
fn message(&self) -> String {
211-
"wrap return expression in Ok".to_string()
214+
format!("wrap return expression in {}", self.required)
212215
}
213216
fn display_source(&self) -> InFile<SyntaxNodePtr> {
214217
InFile { file_id: self.file, value: self.expr.clone().into() }

crates/hir_ty/src/diagnostics/expr.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
db::HirDatabase,
1212
diagnostics::{
1313
match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
14-
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
14+
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, MissingPatFields,
1515
RemoveThisSemicolon,
1616
},
1717
utils::variant_data,
@@ -306,27 +306,37 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
306306
};
307307

308308
let core_result_path = path![core::result::Result];
309+
let core_option_path = path![core::option::Option];
309310

310311
let resolver = self.owner.resolver(db.upcast());
311312
let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
312313
Some(it) => it,
313314
_ => return,
314315
};
316+
let core_option_enum = match resolver.resolve_known_enum(db.upcast(), &core_option_path) {
317+
Some(it) => it,
318+
_ => return,
319+
};
315320

316321
let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
317-
let params = match &mismatch.expected {
322+
let core_option_ctor = TypeCtor::Adt(AdtId::EnumId(core_option_enum));
323+
324+
let (params, required) = match &mismatch.expected {
318325
Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
319-
parameters
320-
}
326+
(parameters, "Ok".to_string())
327+
},
328+
Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_option_ctor => {
329+
(parameters, "Some".to_string())
330+
},
321331
_ => return,
322332
};
323333

324-
if params.len() == 2 && params[0] == mismatch.actual {
334+
if params.len() > 0 && params[0] == mismatch.actual {
325335
let (_, source_map) = db.body_with_source_map(self.owner.into());
326336

327337
if let Ok(source_ptr) = source_map.expr_syntax(id) {
328338
self.sink
329-
.push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value });
339+
.push(MissingOkOrSomeInTailExpr { file: source_ptr.file_id, expr: source_ptr.value, required });
330340
}
331341
}
332342
}

crates/ide/src/diagnostics.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub(crate) fn diagnostics(
125125
.on::<hir::diagnostics::MissingFields, _>(|d| {
126126
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
127127
})
128-
.on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
128+
.on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
129129
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
130130
})
131131
.on::<hir::diagnostics::NoSuchField, _>(|d| {
@@ -304,6 +304,40 @@ mod tests {
304304
expect.assert_debug_eq(&diagnostics)
305305
}
306306

307+
#[test]
308+
fn test_wrap_return_type_option() {
309+
check_fix(
310+
r#"
311+
//- /main.rs crate:main deps:core
312+
use core::option::Option::{self, Some, None};
313+
314+
fn div(x: i32, y: i32) -> Option<i32> {
315+
if y == 0 {
316+
return None;
317+
}
318+
x / y<|>
319+
}
320+
//- /core/lib.rs crate:core
321+
pub mod result {
322+
pub enum Result<T, E> { Ok(T), Err(E) }
323+
}
324+
pub mod option {
325+
pub enum Option<T> { Some(T), None }
326+
}
327+
"#,
328+
r#"
329+
use core::option::Option::{self, Some, None};
330+
331+
fn div(x: i32, y: i32) -> Option<i32> {
332+
if y == 0 {
333+
return None;
334+
}
335+
Some(x / y)
336+
}
337+
"#,
338+
);
339+
}
340+
307341
#[test]
308342
fn test_wrap_return_type() {
309343
check_fix(
@@ -321,6 +355,9 @@ fn div(x: i32, y: i32) -> Result<i32, ()> {
321355
pub mod result {
322356
pub enum Result<T, E> { Ok(T), Err(E) }
323357
}
358+
pub mod option {
359+
pub enum Option<T> { Some(T), None }
360+
}
324361
"#,
325362
r#"
326363
use core::result::Result::{self, Ok, Err};
@@ -352,6 +389,9 @@ fn div<T>(x: T) -> Result<T, i32> {
352389
pub mod result {
353390
pub enum Result<T, E> { Ok(T), Err(E) }
354391
}
392+
pub mod option {
393+
pub enum Option<T> { Some(T), None }
394+
}
355395
"#,
356396
r#"
357397
use core::result::Result::{self, Ok, Err};
@@ -385,6 +425,9 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
385425
pub mod result {
386426
pub enum Result<T, E> { Ok(T), Err(E) }
387427
}
428+
pub mod option {
429+
pub enum Option<T> { Some(T), None }
430+
}
388431
"#,
389432
r#"
390433
use core::result::Result::{self, Ok, Err};
@@ -414,12 +457,15 @@ fn foo() -> Result<(), i32> { 0 }
414457
pub mod result {
415458
pub enum Result<T, E> { Ok(T), Err(E) }
416459
}
460+
pub mod option {
461+
pub enum Option<T> { Some(T), None }
462+
}
417463
"#,
418464
);
419465
}
420466

421467
#[test]
422-
fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
468+
fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
423469
check_no_diagnostics(
424470
r#"
425471
//- /main.rs crate:main deps:core
@@ -433,6 +479,9 @@ fn foo() -> SomeOtherEnum { 0 }
433479
pub mod result {
434480
pub enum Result<T, E> { Ok(T), Err(E) }
435481
}
482+
pub mod option {
483+
pub enum Option<T> { Some(T), None }
484+
}
436485
"#,
437486
);
438487
}

crates/ide/src/diagnostics/fixes.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use hir::{
44
db::AstDatabase,
55
diagnostics::{
6-
Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
6+
Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField,
77
RemoveThisSemicolon, UnresolvedModule,
88
},
99
HasSource, HirDisplay, InFile, Semantics, VariantDef,
@@ -94,15 +94,16 @@ impl DiagnosticWithFix for MissingFields {
9494
}
9595
}
9696

97-
impl DiagnosticWithFix for MissingOkInTailExpr {
97+
impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
9898
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
9999
let root = sema.db.parse_or_expand(self.file)?;
100100
let tail_expr = self.expr.to_node(&root);
101101
let tail_expr_range = tail_expr.syntax().text_range();
102-
let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
103-
let source_change =
104-
SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
105-
Some(Fix::new("Wrap with ok", source_change, tail_expr_range))
102+
let replacement = format!("{}({})", self.required, tail_expr.syntax());
103+
let edit = TextEdit::replace(tail_expr_range, replacement);
104+
let source_change = SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
105+
let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
106+
Some(Fix::new(name, source_change, tail_expr_range))
106107
}
107108
}
108109

0 commit comments

Comments
 (0)