Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
};
pub use hir_ty::diagnostics::{
- IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
+ IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
NoSuchField, RemoveThisSemicolon,
};
macro_rules! __known_path {
(core::iter::IntoIterator) => {};
(core::result::Result) => {};
+ (core::option::Option) => {};
(core::ops::Range) => {};
(core::ops::RangeFrom) => {};
(core::ops::RangeFull) => {};
future,
result,
boxed,
+ option,
// Components of known path (type name)
Iterator,
IntoIterator,
Ok,
Future,
Result,
+ Option,
Output,
Target,
Box,
}
}
-// Diagnostic: missing-ok-in-tail-expr
+// Diagnostic: missing-ok-or-some-in-tail-expr
//
-// This diagnostic is triggered if block that should return `Result` returns a value not wrapped in `Ok`.
+// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
+// or if a block that should return `Option` returns a value not wrapped in `Some`.
//
// Example:
//
// }
// ```
#[derive(Debug)]
-pub struct MissingOkInTailExpr {
+pub struct MissingOkOrSomeInTailExpr {
pub file: HirFileId,
pub expr: AstPtr<ast::Expr>,
+ // `Some` or `Ok` depending on whether the return type is Result or Option
+ pub required: String,
}
-impl Diagnostic for MissingOkInTailExpr {
+impl Diagnostic for MissingOkOrSomeInTailExpr {
fn code(&self) -> DiagnosticCode {
- DiagnosticCode("missing-ok-in-tail-expr")
+ DiagnosticCode("missing-ok-or-some-in-tail-expr")
}
fn message(&self) -> String {
- "wrap return expression in Ok".to_string()
+ format!("wrap return expression in {}", self.required)
}
fn display_source(&self) -> InFile<SyntaxNodePtr> {
InFile { file_id: self.file, value: self.expr.clone().into() }
db::HirDatabase,
diagnostics::{
match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
- MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
+ MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, MissingPatFields,
RemoveThisSemicolon,
},
utils::variant_data,
};
let core_result_path = path![core::result::Result];
+ let core_option_path = path![core::option::Option];
let resolver = self.owner.resolver(db.upcast());
let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
Some(it) => it,
_ => return,
};
+ let core_option_enum = match resolver.resolve_known_enum(db.upcast(), &core_option_path) {
+ Some(it) => it,
+ _ => return,
+ };
let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
- let params = match &mismatch.expected {
+ let core_option_ctor = TypeCtor::Adt(AdtId::EnumId(core_option_enum));
+
+ let (params, required) = match &mismatch.expected {
Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
- parameters
- }
+ (parameters, "Ok".to_string())
+ },
+ Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_option_ctor => {
+ (parameters, "Some".to_string())
+ },
_ => return,
};
- if params.len() == 2 && params[0] == mismatch.actual {
+ if params.len() > 0 && params[0] == mismatch.actual {
let (_, source_map) = db.body_with_source_map(self.owner.into());
if let Ok(source_ptr) = source_map.expr_syntax(id) {
self.sink
- .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value });
+ .push(MissingOkOrSomeInTailExpr { file: source_ptr.file_id, expr: source_ptr.value, required });
}
}
}
.on::<hir::diagnostics::MissingFields, _>(|d| {
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
})
- .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
+ .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
})
.on::<hir::diagnostics::NoSuchField, _>(|d| {
expect.assert_debug_eq(&diagnostics)
}
+ #[test]
+ fn test_wrap_return_type_option() {
+ check_fix(
+ r#"
+//- /main.rs crate:main deps:core
+use core::option::Option::{self, Some, None};
+
+fn div(x: i32, y: i32) -> Option<i32> {
+ if y == 0 {
+ return None;
+ }
+ x / y<|>
+}
+//- /core/lib.rs crate:core
+pub mod result {
+ pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+ pub enum Option<T> { Some(T), None }
+}
+"#,
+ r#"
+use core::option::Option::{self, Some, None};
+
+fn div(x: i32, y: i32) -> Option<i32> {
+ if y == 0 {
+ return None;
+ }
+ Some(x / y)
+}
+"#,
+ );
+ }
+
#[test]
fn test_wrap_return_type() {
check_fix(
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
+pub mod option {
+ pub enum Option<T> { Some(T), None }
+}
"#,
r#"
use core::result::Result::{self, Ok, Err};
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
+pub mod option {
+ pub enum Option<T> { Some(T), None }
+}
"#,
r#"
use core::result::Result::{self, Ok, Err};
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
+pub mod option {
+ pub enum Option<T> { Some(T), None }
+}
"#,
r#"
use core::result::Result::{self, Ok, Err};
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
+pub mod option {
+ pub enum Option<T> { Some(T), None }
+}
"#,
);
}
#[test]
- fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
+ fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
check_no_diagnostics(
r#"
//- /main.rs crate:main deps:core
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
+pub mod option {
+ pub enum Option<T> { Some(T), None }
+}
"#,
);
}
use hir::{
db::AstDatabase,
diagnostics::{
- Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
+ Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField,
RemoveThisSemicolon, UnresolvedModule,
},
HasSource, HirDisplay, InFile, Semantics, VariantDef,
}
}
-impl DiagnosticWithFix for MissingOkInTailExpr {
+impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
let root = sema.db.parse_or_expand(self.file)?;
let tail_expr = self.expr.to_node(&root);
let tail_expr_range = tail_expr.syntax().text_range();
- let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
- let source_change =
- SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
- Some(Fix::new("Wrap with ok", source_change, tail_expr_range))
+ let replacement = format!("{}({})", self.required, tail_expr.syntax());
+ let edit = TextEdit::replace(tail_expr_range, replacement);
+ let source_change = SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
+ let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
+ Some(Fix::new(name, source_change, tail_expr_range))
}
}