mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-13 02:17:41 +00:00
Auto merge of #42850 - estebank:unwanted-return-rotj, r=nikomatsakis
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 - Suggest removal of semicolon (instead of being help)
This commit is contained in:
commit
69c65d2961
@ -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,56 @@ impl<'hir> Map<'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the NodeId for `id`'s enclosing method, unless there's a
|
||||
/// `while` or `loop` before reacing it, as block tail returns are not
|
||||
/// available in them.
|
||||
///
|
||||
/// ```
|
||||
/// fn foo(x: usize) -> bool {
|
||||
/// if x == 1 {
|
||||
/// true // `get_return_block` gets passed the `id` corresponding
|
||||
/// } else { // to this, it will return `foo`'s `NodeId`.
|
||||
/// false
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// fn foo(x: usize) -> bool {
|
||||
/// loop {
|
||||
/// true // `get_return_block` gets passed the `id` corresponding
|
||||
/// } // to this, it will return `None`.
|
||||
/// false
|
||||
/// }
|
||||
/// ```
|
||||
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 +693,7 @@ impl<'hir> Map<'hir> {
|
||||
NodeTraitItem(_) |
|
||||
NodeImplItem(_) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
}, |_| false) {
|
||||
Ok(id) => id,
|
||||
Err(id) => id,
|
||||
}
|
||||
@ -649,7 +705,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 +724,7 @@ impl<'hir> Map<'hir> {
|
||||
NodeImplItem(_) |
|
||||
NodeBlock(_) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
}, |_| false) {
|
||||
Ok(id) => Some(id),
|
||||
Err(_) => None,
|
||||
}
|
||||
|
@ -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(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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),
|
||||
|
@ -481,6 +481,18 @@ impl<'tcx> TyS<'tcx> {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_suggestable(&self) -> bool {
|
||||
match self.sty {
|
||||
TypeVariants::TyAnon(..) |
|
||||
TypeVariants::TyFnDef(..) |
|
||||
TypeVariants::TyFnPtr(..) |
|
||||
TypeVariants::TyDynamic(..) |
|
||||
TypeVariants::TyClosure(..) |
|
||||
TypeVariants::TyProjection(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ty::TyS<'tcx> {
|
||||
|
@ -47,7 +47,12 @@ impl Emitter for EmitterWriter {
|
||||
// don't display multiline suggestions as labels
|
||||
sugg.substitution_parts[0].substitutions[0].find('\n').is_none() {
|
||||
let substitution = &sugg.substitution_parts[0].substitutions[0];
|
||||
let msg = format!("help: {} `{}`", sugg.msg, substitution);
|
||||
let msg = if substitution.len() == 0 {
|
||||
// This substitution is only removal, don't show it
|
||||
format!("help: {}", sugg.msg)
|
||||
} else {
|
||||
format!("help: {} `{}`", sugg.msg, substitution)
|
||||
};
|
||||
primary_span.push_span_label(sugg.substitution_spans().next().unwrap(), msg);
|
||||
} else {
|
||||
// if there are multiple suggestions, print them all in full
|
||||
|
@ -1168,6 +1168,18 @@ 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);
|
||||
}
|
||||
_ => {
|
||||
db = fcx.report_mismatched_types(cause, expected, found, err);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
@ -977,7 +978,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(
|
||||
@ -1900,7 +1901,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));
|
||||
@ -2839,10 +2840,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());
|
||||
@ -4161,8 +4163,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());
|
||||
@ -4201,6 +4205,130 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
ty
|
||||
}
|
||||
|
||||
/// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and whether 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 whether 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) {
|
||||
// `fn main()` must return `()`, do not suggest changing return type
|
||||
if !is_main {
|
||||
self.suggest_missing_return_type(err, &fn_decl, found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
if ty.is_suggestable() {
|
||||
err.span_suggestion(span,
|
||||
"possibly return type missing here?",
|
||||
format!("-> {} ", ty));
|
||||
} else {
|
||||
err.span_label(span, "possibly return type missing here?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A common error is to add an extra semicolon:
|
||||
///
|
||||
/// ```
|
||||
@ -4236,7 +4364,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
hi: original_span.hi,
|
||||
ctxt: original_span.ctxt,
|
||||
};
|
||||
err.span_help(span_semi, "consider removing this semicolon:");
|
||||
err.span_suggestion(span_semi, "consider removing this semicolon", "".to_string());
|
||||
}
|
||||
|
||||
// Instantiates the given path, which must refer to an item with the given
|
||||
|
@ -0,0 +1,11 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/block-must-not-have-result-do.rs:13:9
|
||||
|
|
||||
13 | true //~ ERROR mismatched types
|
||||
| ^^^^ expected (), found bool
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `bool`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
@ -0,0 +1,11 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/block-must-not-have-result-res.rs:15:9
|
||||
|
|
||||
15 | true //~ ERROR mismatched types
|
||||
| ^^^^ expected (), found bool
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `bool`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
@ -0,0 +1,11 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/block-must-not-have-result-while.rs:13:9
|
||||
|
|
||||
13 | true //~ ERROR mismatched types
|
||||
| ^^^^ expected (), found bool
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `bool`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
30
src/test/ui/block-result/consider-removing-last-semi.stderr
Normal file
30
src/test/ui/block-result/consider-removing-last-semi.stderr
Normal file
@ -0,0 +1,30 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/consider-removing-last-semi.rs:11:18
|
||||
|
|
||||
11 | fn f() -> String { //~ ERROR mismatched types
|
||||
| __________________^
|
||||
12 | | 0u8;
|
||||
13 | | "bla".to_string(); //~ HELP consider removing this semicolon
|
||||
| | - help: consider removing this semicolon
|
||||
14 | | }
|
||||
| |_^ expected struct `std::string::String`, found ()
|
||||
|
|
||||
= note: expected type `std::string::String`
|
||||
found type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/consider-removing-last-semi.rs:16:18
|
||||
|
|
||||
16 | fn g() -> String { //~ ERROR mismatched types
|
||||
| __________________^
|
||||
17 | | "this won't work".to_string();
|
||||
18 | | "removeme".to_string(); //~ HELP consider removing this semicolon
|
||||
| | - help: consider removing this semicolon
|
||||
19 | | }
|
||||
| |_^ expected struct `std::string::String`, found ()
|
||||
|
|
||||
= note: expected type `std::string::String`
|
||||
found type `()`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
17
src/test/ui/block-result/issue-11714.stderr
Normal file
17
src/test/ui/block-result/issue-11714.stderr
Normal file
@ -0,0 +1,17 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-11714.rs:11:18
|
||||
|
|
||||
11 | fn blah() -> i32 { //~ ERROR mismatched types
|
||||
| __________________^
|
||||
12 | | 1
|
||||
13 | |
|
||||
14 | | ; //~ HELP consider removing this semicolon:
|
||||
| | - help: consider removing this semicolon
|
||||
15 | | }
|
||||
| |_^ expected i32, found ()
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
33
src/test/ui/block-result/issue-13428.stderr
Normal file
33
src/test/ui/block-result/issue-13428.stderr
Normal file
@ -0,0 +1,33 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-13428.rs:13:20
|
||||
|
|
||||
13 | fn foo() -> String { //~ ERROR mismatched types
|
||||
| ____________________^
|
||||
14 | | format!("Hello {}",
|
||||
15 | | "world")
|
||||
16 | | // Put the trailing semicolon on its own line to test that the
|
||||
17 | | // note message gets the offending semicolon exactly
|
||||
18 | | ; //~ HELP consider removing this semicolon
|
||||
| | - help: consider removing this semicolon
|
||||
19 | | }
|
||||
| |_^ expected struct `std::string::String`, found ()
|
||||
|
|
||||
= note: expected type `std::string::String`
|
||||
found type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-13428.rs:21:20
|
||||
|
|
||||
21 | fn bar() -> String { //~ ERROR mismatched types
|
||||
| ____________________^
|
||||
22 | | "foobar".to_string()
|
||||
23 | | ; //~ HELP consider removing this semicolon
|
||||
| | - help: consider removing this semicolon
|
||||
24 | | }
|
||||
| |_^ expected struct `std::string::String`, found ()
|
||||
|
|
||||
= note: expected type `std::string::String`
|
||||
found type `()`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
20
src/test/ui/block-result/issue-13624.stderr
Normal file
20
src/test/ui/block-result/issue-13624.stderr
Normal file
@ -0,0 +1,20 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-13624.rs:17:5
|
||||
|
|
||||
17 | Enum::EnumStructVariant { x: 1, y: 2, z: 3 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `a::Enum`
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `a::Enum`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-13624.rs:32:9
|
||||
|
|
||||
32 | a::Enum::EnumStructVariant { x, y, z } => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `a::Enum`
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `a::Enum`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
19
src/test/ui/block-result/issue-20862.stderr
Normal file
19
src/test/ui/block-result/issue-20862.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-20862.rs:12:5
|
||||
|
|
||||
11 | fn foo(x: i32) {
|
||||
| - possibly return type missing here?
|
||||
12 | |y| x + y
|
||||
| ^^^^^^^^^ expected (), found closure
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `[closure@$DIR/issue-20862.rs:12:5: 12:14 x:_]`
|
||||
|
||||
error[E0618]: expected function, found `()`
|
||||
--> $DIR/issue-20862.rs:17:13
|
||||
|
|
||||
17 | let x = foo(5)(2);
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
21
src/test/ui/block-result/issue-22645.stderr
Normal file
21
src/test/ui/block-result/issue-22645.stderr
Normal file
@ -0,0 +1,21 @@
|
||||
error[E0277]: the trait bound `{integer}: Scalar` is not satisfied
|
||||
--> $DIR/issue-22645.rs:25:5
|
||||
|
|
||||
25 | b + 3 //~ ERROR E0277
|
||||
| ^ the trait `Scalar` is not implemented for `{integer}`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<f64 as Scalar>
|
||||
= note: required because of the requirements on the impl of `std::ops::Add<{integer}>` for `Bob`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-22645.rs:25:3
|
||||
|
|
||||
25 | b + 3 //~ ERROR E0277
|
||||
| ^^^^^ expected (), found struct `Bob`
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `Bob`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
19
src/test/ui/block-result/issue-3563.stderr
Normal file
19
src/test/ui/block-result/issue-3563.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0599]: no method named `b` found for type `&Self` in the current scope
|
||||
--> $DIR/issue-3563.rs:13:17
|
||||
|
|
||||
13 | || self.b()
|
||||
| ^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-3563.rs:13:9
|
||||
|
|
||||
12 | fn a(&self) {
|
||||
| - possibly return type missing here?
|
||||
13 | || self.b()
|
||||
| ^^^^^^^^^^^ expected (), found closure
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `[closure@$DIR/issue-3563.rs:13:9: 13:20 self:_]`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
11
src/test/ui/block-result/issue-5500.stderr
Normal file
11
src/test/ui/block-result/issue-5500.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-5500.rs:12:5
|
||||
|
|
||||
12 | &panic!()
|
||||
| ^^^^^^^^^ expected (), found reference
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `&_`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
24
src/test/ui/block-result/unexpected-return-on-unit.rs
Normal file
24
src/test/ui/block-result/unexpected-return-on-unit.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test that we do some basic error correcton in the tokeniser (and don't spew
|
||||
// too many bogus errors).
|
||||
|
||||
fn foo() -> usize {
|
||||
3
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
foo()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
bar()
|
||||
}
|
15
src/test/ui/block-result/unexpected-return-on-unit.stderr
Normal file
15
src/test/ui/block-result/unexpected-return-on-unit.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/unexpected-return-on-unit.rs:19:5
|
||||
|
|
||||
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)
|
||||
|
@ -4,16 +4,12 @@ error[E0308]: mismatched types
|
||||
13 | fn plus_one(x: i32) -> i32 {
|
||||
| ____________________________^
|
||||
14 | | x + 1;
|
||||
| | - help: consider removing this semicolon
|
||||
15 | | }
|
||||
| |_^ expected i32, found ()
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
help: consider removing this semicolon:
|
||||
--> $DIR/coercion-missing-tail-expected-type.rs:14:10
|
||||
|
|
||||
14 | x + 1;
|
||||
| ^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/coercion-missing-tail-expected-type.rs:17:29
|
||||
@ -21,16 +17,12 @@ error[E0308]: mismatched types
|
||||
17 | fn foo() -> Result<u8, u64> {
|
||||
| _____________________________^
|
||||
18 | | Ok(1);
|
||||
| | - help: consider removing this semicolon
|
||||
19 | | }
|
||||
| |_^ expected enum `std::result::Result`, found ()
|
||||
|
|
||||
= note: expected type `std::result::Result<u8, u64>`
|
||||
found type `()`
|
||||
help: consider removing this semicolon:
|
||||
--> $DIR/coercion-missing-tail-expected-type.rs:18:10
|
||||
|
|
||||
18 | Ok(1);
|
||||
| ^
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
||||
|
11
src/test/ui/mismatched_types/for-loop-has-unit-body.stderr
Normal file
11
src/test/ui/mismatched_types/for-loop-has-unit-body.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/for-loop-has-unit-body.rs:13:9
|
||||
|
|
||||
13 | x //~ ERROR mismatched types
|
||||
| ^ expected (), found integral variable
|
||||
|
|
||||
= note: expected type `()`
|
||||
found type `{integer}`
|
||||
|
||||
error: aborting due to previous error(s)
|
||||
|
@ -1,6 +1,8 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-19109.rs:14:5
|
||||
|
|
||||
13 | fn function(t: &mut Trait) {
|
||||
| - help: possibly return type missing here? `-> *mut Trait `
|
||||
14 | t as *mut Trait
|
||||
| ^^^^^^^^^^^^^^^ expected (), found *-ptr
|
||||
|
|
||||
|
@ -35,7 +35,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/token-error-correct-3.rs:25:13
|
||||
|
|
||||
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>`
|
||||
|
Loading…
Reference in New Issue
Block a user