1
Fork 0

Detect missing ; on methods with return type ()

- Point out the origin of a type requirement when it is the return type
  of a method
- Point out possibly missing semicolon when the return type is () and
  the implicit return makes sense as a statement
- Suggest changing the return type of methods with default return type
- Don't suggest changing the return type on fn main()
- Don't suggest changing the return type on impl fn
This commit is contained in:
Esteban Küber 2017-06-24 00:57:39 -07:00
parent 8e11189cbb
commit c023856b38
17 changed files with 320 additions and 64 deletions

View file

@ -594,8 +594,12 @@ impl<'hir> Map<'hir> {
/// last good node id we found. Note that reaching the crate root (id == 0),
/// is not an error, since items in the crate module have the crate root as
/// parent.
fn walk_parent_nodes<F>(&self, start_id: NodeId, found: F) -> Result<NodeId, NodeId>
where F: Fn(&Node<'hir>) -> bool
fn walk_parent_nodes<F, F2>(&self,
start_id: NodeId,
found: F,
bail_early: F2)
-> Result<NodeId, NodeId>
where F: Fn(&Node<'hir>) -> bool, F2: Fn(&Node<'hir>) -> bool
{
let mut id = start_id;
loop {
@ -616,6 +620,8 @@ impl<'hir> Map<'hir> {
Some(ref node) => {
if found(node) {
return Ok(parent_node);
} else if bail_early(node) {
return Err(parent_node);
}
}
None => {
@ -626,6 +632,34 @@ impl<'hir> Map<'hir> {
}
}
pub fn get_return_block(&self, id: NodeId) -> Option<NodeId> {
let match_fn = |node: &Node| {
match *node {
NodeItem(_) |
NodeForeignItem(_) |
NodeTraitItem(_) |
NodeImplItem(_) => true,
_ => false,
}
};
let match_non_returning_block = |node: &Node| {
match *node {
NodeExpr(ref expr) => {
match expr.node {
ExprWhile(..) | ExprLoop(..) => true,
_ => false,
}
}
_ => false,
}
};
match self.walk_parent_nodes(id, match_fn, match_non_returning_block) {
Ok(id) => Some(id),
Err(_) => None,
}
}
/// Retrieve the NodeId for `id`'s parent item, or `id` itself if no
/// parent item is in this map. The "parent item" is the closest parent node
/// in the AST which is recorded by the map and is an item, either an item
@ -637,7 +671,7 @@ impl<'hir> Map<'hir> {
NodeTraitItem(_) |
NodeImplItem(_) => true,
_ => false,
}) {
}, |_| false) {
Ok(id) => id,
Err(id) => id,
}
@ -649,7 +683,7 @@ impl<'hir> Map<'hir> {
let id = match self.walk_parent_nodes(id, |node| match *node {
NodeItem(&Item { node: Item_::ItemMod(_), .. }) => true,
_ => false,
}) {
}, |_| false) {
Ok(id) => id,
Err(id) => id,
};
@ -668,7 +702,7 @@ impl<'hir> Map<'hir> {
NodeImplItem(_) |
NodeBlock(_) => true,
_ => false,
}) {
}, |_| false) {
Ok(id) => Some(id),
Err(_) => None,
}

View file

@ -1088,7 +1088,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ObligationCauseCode::VariableType(_) => {
err.note("all local variables must have a statically known size");
}
ObligationCauseCode::ReturnType => {
ObligationCauseCode::SizedReturnType => {
err.note("the return type of a function must have a \
statically known size");
}
@ -1133,6 +1133,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
but not on the corresponding trait method",
predicate));
}
ObligationCauseCode::ReturnType(_) |
ObligationCauseCode::BlockTailExpression(_) => (),
}
}

View file

@ -118,27 +118,32 @@ pub enum ObligationCauseCode<'tcx> {
/// Obligation incurred due to an object cast.
ObjectCastObligation(/* Object type */ Ty<'tcx>),
/// Various cases where expressions must be sized/copy/etc:
AssignmentLhsSized, // L = X implies that L is Sized
StructInitializerSized, // S { ... } must be Sized
VariableType(ast::NodeId), // Type of each variable must be Sized
ReturnType, // Return type must be Sized
RepeatVec, // [T,..n] --> T must be Copy
// Various cases where expressions must be sized/copy/etc:
/// L = X implies that L is Sized
AssignmentLhsSized,
/// S { ... } must be Sized
StructInitializerSized,
/// Type of each variable must be Sized
VariableType(ast::NodeId),
/// Return type must be Sized
SizedReturnType,
/// [T,..n] --> T must be Copy
RepeatVec,
// Types of fields (other than the last) in a struct must be sized.
/// Types of fields (other than the last) in a struct must be sized.
FieldSized,
// Constant expressions must be sized.
/// Constant expressions must be sized.
ConstSized,
// static items must have `Sync` type
/// static items must have `Sync` type
SharedStatic,
BuiltinDerivedObligation(DerivedObligationCause<'tcx>),
ImplDerivedObligation(DerivedObligationCause<'tcx>),
// error derived when matching traits/impls; see ObligationCause for more details
/// error derived when matching traits/impls; see ObligationCause for more details
CompareImplMethodObligation {
item_name: ast::Name,
impl_item_def_id: DefId,
@ -146,37 +151,43 @@ pub enum ObligationCauseCode<'tcx> {
lint_id: Option<ast::NodeId>,
},
// Checking that this expression can be assigned where it needs to be
/// Checking that this expression can be assigned where it needs to be
// FIXME(eddyb) #11161 is the original Expr required?
ExprAssignable,
// Computing common supertype in the arms of a match expression
/// Computing common supertype in the arms of a match expression
MatchExpressionArm { arm_span: Span,
source: hir::MatchSource },
// Computing common supertype in an if expression
/// Computing common supertype in an if expression
IfExpression,
// Computing common supertype of an if expression with no else counter-part
/// Computing common supertype of an if expression with no else counter-part
IfExpressionWithNoElse,
// `where a == b`
/// `where a == b`
EquatePredicate,
// `main` has wrong type
/// `main` has wrong type
MainFunctionType,
// `start` has wrong type
/// `start` has wrong type
StartFunctionType,
// intrinsic has wrong type
/// intrinsic has wrong type
IntrinsicType,
// method receiver
/// method receiver
MethodReceiver,
// `return` with no expression
/// `return` with no expression
ReturnNoExpression,
/// `return` with an expression
ReturnType(ast::NodeId),
/// Block implicit return
BlockTailExpression(ast::NodeId),
}
#[derive(Clone, Debug, PartialEq, Eq)]

View file

@ -191,7 +191,8 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
super::AssignmentLhsSized => Some(super::AssignmentLhsSized),
super::StructInitializerSized => Some(super::StructInitializerSized),
super::VariableType(id) => Some(super::VariableType(id)),
super::ReturnType => Some(super::ReturnType),
super::ReturnType(id) => Some(super::ReturnType(id)),
super::SizedReturnType => Some(super::SizedReturnType),
super::RepeatVec => Some(super::RepeatVec),
super::FieldSized => Some(super::FieldSized),
super::ConstSized => Some(super::ConstSized),
@ -213,34 +214,19 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
lint_id: lint_id,
})
}
super::ExprAssignable => {
Some(super::ExprAssignable)
}
super::ExprAssignable => Some(super::ExprAssignable),
super::MatchExpressionArm { arm_span, source } => {
Some(super::MatchExpressionArm { arm_span: arm_span,
source: source })
}
super::IfExpression => {
Some(super::IfExpression)
}
super::IfExpressionWithNoElse => {
Some(super::IfExpressionWithNoElse)
}
super::EquatePredicate => {
Some(super::EquatePredicate)
}
super::MainFunctionType => {
Some(super::MainFunctionType)
}
super::StartFunctionType => {
Some(super::StartFunctionType)
}
super::IntrinsicType => {
Some(super::IntrinsicType)
}
super::MethodReceiver => {
Some(super::MethodReceiver)
}
super::IfExpression => Some(super::IfExpression),
super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
super::EquatePredicate => Some(super::EquatePredicate),
super::MainFunctionType => Some(super::MainFunctionType),
super::StartFunctionType => Some(super::StartFunctionType),
super::IntrinsicType => Some(super::IntrinsicType),
super::MethodReceiver => Some(super::MethodReceiver),
super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)),
}
}
}
@ -492,12 +478,14 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
super::AssignmentLhsSized |
super::StructInitializerSized |
super::VariableType(_) |
super::ReturnType |
super::ReturnType(_) |
super::SizedReturnType |
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized |
super::ConstSized |
super::SharedStatic |
super::BlockTailExpression(_) |
super::CompareImplMethodObligation { .. } => self.clone(),
super::ProjectionWf(proj) => super::ProjectionWf(proj.fold_with(folder)),
@ -537,12 +525,14 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
super::AssignmentLhsSized |
super::StructInitializerSized |
super::VariableType(_) |
super::ReturnType |
super::ReturnType(_) |
super::SizedReturnType |
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized |
super::ConstSized |
super::SharedStatic |
super::BlockTailExpression(_) |
super::CompareImplMethodObligation { .. } => false,
super::ProjectionWf(proj) => proj.visit_with(visitor),

View file

@ -1162,6 +1162,24 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
"`return;` in a function whose return type is not `()`");
db.span_label(cause.span, "return type is not ()");
}
ObligationCauseCode::BlockTailExpression(blk_id) => {
db = fcx.report_mismatched_types(cause, expected, found, err);
let expr = expression.unwrap_or_else(|| {
span_bug!(cause.span,
"supposed to be part of a block tail expression, but the \
expression is empty");
});
fcx.suggest_mismatched_types_on_tail(&mut db, expr,
expected, found,
cause.span, blk_id);
}
ObligationCauseCode::ReturnType(ret_id) => {
db = fcx.report_mismatched_types(cause, expected, found, err);
if let Some((fn_decl, _)) = fcx.get_fn_decl(ret_id) {
fcx.point_to_type_requirement(&mut db, &fn_decl, expected);
}
}
_ => {
db = fcx.report_mismatched_types(cause, expected, found, err);
}

View file

@ -73,15 +73,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
if let Some(mut err) = self.demand_coerce_diag(expr, checked_ty, expected) {
err.emit();
}
}
// Checks that the type of `expr` can be coerced to `expected`.
//
// NB: This code relies on `self.diverges` to be accurate. In
// particular, assignments to `!` will be permitted if the
// diverges flag is currently "always".
pub fn demand_coerce(&self,
expr: &hir::Expr,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>) {
pub fn demand_coerce_diag(&self,
expr: &hir::Expr,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
let expected = self.resolve_type_vars_with_obligations(expected);
if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
@ -105,8 +111,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.get_best_match(&suggestions).join("\n")));
}
}
err.emit();
return Some(err);
}
None
}
fn format_method_suggestion(&self, method: &AssociatedItem) -> String {

View file

@ -124,6 +124,7 @@ use syntax_pos::{self, BytePos, Span};
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::hir::map::Node;
use rustc::hir::{self, PatKind};
use rustc::middle::lang_items;
use rustc_back::slice;
@ -985,7 +986,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
*fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
let ret_ty = fn_sig.output();
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::SizedReturnType);
let ret_ty = fcx.instantiate_anon_types(&ret_ty);
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
fn_sig = fcx.tcx.mk_fn_sig(
@ -1908,7 +1909,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Require that the predicate holds for the concrete type.
let cause = traits::ObligationCause::new(span, self.body_id,
traits::ReturnType);
traits::SizedReturnType);
self.register_predicate(traits::Obligation::new(cause,
self.param_env,
predicate));
@ -2848,10 +2849,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
"check_return_expr called outside fn body"));
let ret_ty = ret_coercion.borrow().expected_ty();
let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone());
ret_coercion.borrow_mut()
.coerce(self,
&self.misc(return_expr.span),
&self.cause(return_expr.span,
ObligationCauseCode::ReturnType(return_expr.id)),
return_expr,
return_expr_ty,
self.diverges.get());
@ -4170,8 +4172,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut coerce = ctxt.coerce.as_mut().unwrap();
if let Some(tail_expr_ty) = tail_expr_ty {
let tail_expr = tail_expr.unwrap();
let cause = self.cause(tail_expr.span,
ObligationCauseCode::BlockTailExpression(blk.id));
coerce.coerce(self,
&self.misc(tail_expr.span),
&cause,
tail_expr,
tail_expr_ty,
self.diverges.get());
@ -4210,6 +4214,148 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ty
}
/// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and wether it is
/// `fn main` if it is a method, `None` otherwise.
pub fn get_fn_decl(&self, blk_id: ast::NodeId) -> Option<(hir::FnDecl, bool)> {
// Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
// `while` before reaching it, as block tail returns are not available in them.
if let Some(fn_id) = self.tcx.hir.get_return_block(blk_id) {
let parent = self.tcx.hir.get(fn_id);
if let Node::NodeItem(&hir::Item {
name, node: hir::ItemFn(ref decl, ..), ..
}) = parent {
decl.clone().and_then(|decl| {
// This is less than ideal, it will not present the return type span on any
// method called `main`, regardless of wether it is actually the entry point.
Some((decl, name == Symbol::intern("main")))
})
} else if let Node::NodeTraitItem(&hir::TraitItem {
node: hir::TraitItemKind::Method(hir::MethodSig {
ref decl, ..
}, ..), ..
}) = parent {
decl.clone().and_then(|decl| {
Some((decl, false))
})
} else {
None
}
} else {
None
}
}
/// On implicit return expressions with mismatched types, provide the following suggestions:
///
/// - Point out the method's return type as the reason for the expected type
/// - Possible missing semicolon
/// - Possible missing return type if the return type is the default, and not `fn main()`
pub fn suggest_mismatched_types_on_tail(&self,
err: &mut DiagnosticBuilder<'tcx>,
expression: &'gcx hir::Expr,
expected: Ty<'tcx>,
found: Ty<'tcx>,
cause_span: Span,
blk_id: ast::NodeId) {
self.suggest_missing_semicolon(err, expression, expected, cause_span);
if let Some((fn_decl, is_main)) = self.get_fn_decl(blk_id) {
self.point_to_type_requirement(err, &fn_decl, expected);
// `fn main()` must return `()`, do not suggest changing return type
if !is_main {
self.suggest_missing_return_type(err, &fn_decl, found);
}
}
}
pub fn point_to_type_requirement(&self,
err: &mut DiagnosticBuilder<'tcx>,
fn_decl: &hir::FnDecl,
ty: Ty<'tcx>) {
let msg = if let &hir::FnDecl {
output: hir::FunctionRetTy::DefaultReturn(_), ..
} = fn_decl {
"default "
} else {
""
};
let ty = self.resolve_type_vars_if_possible(&ty);
if ty.to_string().len() < 10 {
err.span_label(fn_decl.output.span(),
format!("expected `{}` because of this {}return type", ty, msg));
} else {
err.span_label(fn_decl.output.span(),
format!("expected because of this {}return type", msg));
}
}
/// A common error is to forget to add a semicolon at the end of a block:
///
/// ```
/// fn foo() {
/// bar_that_returns_u32()
/// }
/// ```
///
/// This routine checks if the return expression in a block would make sense on its own as a
/// statement and the return type has been left as defaultor has been specified as `()`. If so,
/// it suggests adding a semicolon.
fn suggest_missing_semicolon(&self,
err: &mut DiagnosticBuilder<'tcx>,
expression: &'gcx hir::Expr,
expected: Ty<'tcx>,
cause_span: Span) {
if expected.is_nil() {
// `BlockTailExpression` only relevant if the tail expr would be
// useful on its own.
match expression.node {
hir::ExprCall(..) |
hir::ExprMethodCall(..) |
hir::ExprIf(..) |
hir::ExprWhile(..) |
hir::ExprLoop(..) |
hir::ExprMatch(..) |
hir::ExprBlock(..) => {
let sp = cause_span.next_point();
err.span_suggestion(sp,
"did you mean to add a semicolon here?",
";".to_string());
}
_ => (),
}
}
}
/// A possible error is to forget to add a return type that is needed:
///
/// ```
/// fn foo() {
/// bar_that_returns_u32()
/// }
/// ```
///
/// This routine checks if the return type is left as default, the method is not part of an
/// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
/// type.
fn suggest_missing_return_type(&self,
err: &mut DiagnosticBuilder<'tcx>,
fn_decl: &hir::FnDecl,
ty: Ty<'tcx>) {
// Only recommend changing the return type for methods that
// haven't set a return type at all (and aren't `fn main()` or an impl).
if let &hir::FnDecl {
output: hir::FunctionRetTy::DefaultReturn(span), ..
} = fn_decl {
err.span_suggestion(span,
"possibly return type missing here?",
format!("-> {} ", ty));
}
}
/// A common error is to add an extra semicolon:
///
/// ```

View file

@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/issue-13624.rs:17:5
|
16 | pub fn get_enum_struct_variant() -> () {
| -- expected `()` because of this return type
17 | Enum::EnumStructVariant { x: 1, y: 2, z: 3 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `a::Enum`
|

View file

@ -1,6 +1,11 @@
error[E0308]: mismatched types
--> $DIR/issue-20862.rs:12:5
|
11 | fn foo(x: i32) {
| -
| |
| help: possibly return type missing here? `-> [closure@$DIR/issue-20862.rs:12:5: 12:14 x:_] `
| expected `()` because of this default return type
12 | |y| x + y
| ^^^^^^^^^ expected (), found closure
|

View file

@ -11,6 +11,9 @@ error[E0277]: the trait bound `{integer}: Scalar` is not satisfied
error[E0308]: mismatched types
--> $DIR/issue-22645.rs:25:3
|
23 | fn main() {
| - expected `()` because of this default return type
24 | let b = Bob + 3.5;
25 | b + 3 //~ ERROR E0277
| ^^^^^ expected (), found struct `Bob`
|

View file

@ -7,6 +7,11 @@ error[E0599]: no method named `b` found for type `&Self` in the current scope
error[E0308]: mismatched types
--> $DIR/issue-3563.rs:13:9
|
12 | fn a(&self) {
| -
| |
| help: possibly return type missing here? `-> [closure@$DIR/issue-3563.rs:13:9: 13:20 self:_] `
| expected `()` because of this default return type
13 | || self.b()
| ^^^^^^^^^^^ expected (), found closure
|

View file

@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/issue-5500.rs:12:5
|
11 | fn main() {
| - expected `()` because of this default return type
12 | &panic!()
| ^^^^^^^^^ expected (), found reference
|

View file

@ -1,11 +1,17 @@
error[E0308]: mismatched types
--> $DIR/unexpected-return-on-unit.rs:19:5
|
18 | fn bar() {
| - expected `()` because of this default return type
19 | foo()
| ^^^^^ expected (), found usize
|
= note: expected type `()`
found type `usize`
help: did you mean to add a semicolon here?
| foo();
help: possibly return type missing here?
| fn bar() -> usize {
error: aborting due to previous error(s)

View file

@ -1,6 +1,9 @@
error[E0308]: mismatched types
--> $DIR/equality.rs:25:5
|
21 | fn two(x: bool) -> impl Foo {
| -------- expected `i32` because of this return type
...
25 | 0_u32
| ^^^^^ expected i32, found u32
|

View file

@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/abridged.rs:26:5
|
25 | fn a() -> Foo {
| --- expected `Foo` because of this return type
26 | Some(Foo { bar: 1 })
| ^^^^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::option::Option`
|
@ -10,6 +12,8 @@ error[E0308]: mismatched types
error[E0308]: mismatched types
--> $DIR/abridged.rs:30:5
|
29 | fn a2() -> Foo {
| --- expected `Foo` because of this return type
30 | Ok(Foo { bar: 1})
| ^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::result::Result`
|
@ -19,6 +23,8 @@ error[E0308]: mismatched types
error[E0308]: mismatched types
--> $DIR/abridged.rs:34:5
|
33 | fn b() -> Option<Foo> {
| ----------- expected because of this return type
34 | Foo { bar: 1 }
| ^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `Foo`
|
@ -28,6 +34,8 @@ error[E0308]: mismatched types
error[E0308]: mismatched types
--> $DIR/abridged.rs:38:5
|
37 | fn c() -> Result<Foo, Bar> {
| ---------------- expected because of this return type
38 | Foo { bar: 1 }
| ^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `Foo`
|
@ -37,6 +45,8 @@ error[E0308]: mismatched types
error[E0308]: mismatched types
--> $DIR/abridged.rs:42:5
|
41 | fn d() -> X<X<String, String>, String> {
| ---------------------------- expected because of this return type
42 | / X {
43 | | x: X {
44 | | x: "".to_string(),
@ -52,6 +62,8 @@ error[E0308]: mismatched types
error[E0308]: mismatched types
--> $DIR/abridged.rs:52:5
|
51 | fn e() -> X<X<String, String>, String> {
| ---------------------------- expected because of this return type
52 | / X {
53 | | x: X {
54 | | x: "".to_string(),

View file

@ -1,6 +1,11 @@
error[E0308]: mismatched types
--> $DIR/issue-19109.rs:14:5
|
13 | fn function(t: &mut Trait) {
| -
| |
| help: possibly return type missing here? `-> *mut Trait `
| expected `()` because of this default return type
14 | t as *mut Trait
| ^^^^^^^^^^^^^^^ expected (), found *-ptr
|

View file

@ -34,8 +34,13 @@ error[E0425]: cannot find function `is_directory` in this scope
error[E0308]: mismatched types
--> $DIR/token-error-correct-3.rs:25:13
|
20 | -> io::Result<bool> {
| ---------------- expected `()` because of this return type
...
25 | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: did you mean to add a semicolon here? `;`
| |
| expected (), found enum `std::result::Result`
|
= note: expected type `()`
found type `std::result::Result<bool, std::io::Error>`