Skip to content

Commit 06e88c3

Browse files
committed
Auto merge of #123125 - gurry:122561-bad-note-non-zero-loop-iters-2, r=estebank
Remove suggestion about iteration count in coerce Fixes #122561 The iteration count-centric suggestion was implemented in PR #100094, but it was based on the wrong assumption that the type mismatch error depends on the number of times the loop iterates. As it turns out, that is not true (see this comment for details: #122679 (comment)) This PR attempts to remedy the situation by changing the suggestion from the one centered on iteration count to a simple suggestion to add a return value. It should also fix #100285 by simply making it redundant.
2 parents 02f7806 + 6289ed8 commit 06e88c3

13 files changed

+545
-205
lines changed

Diff for: compiler/rustc_hir_typeck/src/coercion.rs

+17-138
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,9 @@
3737
3838
use crate::errors::SuggestBoxingForReturnImplTrait;
3939
use crate::FnCtxt;
40-
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
40+
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag};
4141
use rustc_hir as hir;
4242
use rustc_hir::def_id::{DefId, LocalDefId};
43-
use rustc_hir::intravisit::{self, Visitor};
44-
use rustc_hir::Expr;
4543
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4644
use rustc_infer::infer::type_variable::TypeVariableOrigin;
4745
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -93,22 +91,6 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
9391

9492
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
9593

96-
struct CollectRetsVisitor<'tcx> {
97-
ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
98-
}
99-
100-
impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
101-
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
102-
match expr.kind {
103-
hir::ExprKind::Ret(_) => self.ret_exprs.push(expr),
104-
// `return` in closures does not return from the outer function
105-
hir::ExprKind::Closure(_) => return,
106-
_ => {}
107-
}
108-
intravisit::walk_expr(self, expr);
109-
}
110-
}
111-
11294
/// Coercing a mutable reference to an immutable works, while
11395
/// coercing `&T` to `&mut T` should be forbidden.
11496
fn coerce_mutbls<'tcx>(
@@ -1597,7 +1579,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15971579

15981580
let mut err;
15991581
let mut unsized_return = false;
1600-
let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
16011582
match *cause.code() {
16021583
ObligationCauseCode::ReturnNoExpression => {
16031584
err = struct_span_code_err!(
@@ -1632,11 +1613,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16321613
if !fcx.tcx.features().unsized_locals {
16331614
unsized_return = self.is_return_ty_definitely_unsized(fcx);
16341615
}
1635-
if let Some(expression) = expression
1636-
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind
1637-
{
1638-
intravisit::walk_block(&mut visitor, loop_blk);
1639-
}
16401616
}
16411617
ObligationCauseCode::ReturnValue(id) => {
16421618
err = self.report_return_mismatched_types(
@@ -1737,6 +1713,22 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17371713
augment_error(&mut err);
17381714

17391715
if let Some(expr) = expression {
1716+
if let hir::ExprKind::Loop(
1717+
_,
1718+
_,
1719+
loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop),
1720+
_,
1721+
) = expr.kind
1722+
{
1723+
let loop_type = if loop_src == hir::LoopSource::While {
1724+
"`while` loops"
1725+
} else {
1726+
"`for` loops"
1727+
};
1728+
1729+
err.note(format!("{loop_type} evaluate to unit type `()`"));
1730+
}
1731+
17401732
fcx.emit_coerce_suggestions(
17411733
&mut err,
17421734
expr,
@@ -1745,15 +1737,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17451737
None,
17461738
Some(coercion_error),
17471739
);
1748-
if visitor.ret_exprs.len() > 0 {
1749-
self.note_unreachable_loop_return(
1750-
&mut err,
1751-
fcx.tcx,
1752-
&expr,
1753-
&visitor.ret_exprs,
1754-
expected,
1755-
);
1756-
}
17571740
}
17581741

17591742
let reported = err.emit_unless(unsized_return);
@@ -1827,110 +1810,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
18271810
);
18281811
}
18291812

1830-
fn note_unreachable_loop_return(
1831-
&self,
1832-
err: &mut Diag<'_>,
1833-
tcx: TyCtxt<'tcx>,
1834-
expr: &hir::Expr<'tcx>,
1835-
ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
1836-
ty: Ty<'tcx>,
1837-
) {
1838-
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else {
1839-
return;
1840-
};
1841-
let mut span: MultiSpan = vec![loop_span].into();
1842-
span.push_span_label(loop_span, "this might have zero elements to iterate on");
1843-
const MAXITER: usize = 3;
1844-
let iter = ret_exprs.iter().take(MAXITER);
1845-
for ret_expr in iter {
1846-
span.push_span_label(
1847-
ret_expr.span,
1848-
"if the loop doesn't execute, this value would never get returned",
1849-
);
1850-
}
1851-
err.span_note(
1852-
span,
1853-
"the function expects a value to always be returned, but loops might run zero times",
1854-
);
1855-
if MAXITER < ret_exprs.len() {
1856-
err.note(format!(
1857-
"if the loop doesn't execute, {} other values would never get returned",
1858-
ret_exprs.len() - MAXITER
1859-
));
1860-
}
1861-
let hir = tcx.hir();
1862-
let item = hir.get_parent_item(expr.hir_id);
1863-
let ret_msg = "return a value for the case when the loop has zero elements to iterate on";
1864-
let ret_ty_msg =
1865-
"otherwise consider changing the return type to account for that possibility";
1866-
let node = tcx.hir_node(item.into());
1867-
if let Some(body_id) = node.body_id()
1868-
&& let Some(sig) = node.fn_sig()
1869-
&& let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind
1870-
&& !ty.is_never()
1871-
{
1872-
let indentation = if let None = block.expr
1873-
&& let [.., last] = &block.stmts
1874-
{
1875-
tcx.sess.source_map().indentation_before(last.span).unwrap_or_else(String::new)
1876-
} else if let Some(expr) = block.expr {
1877-
tcx.sess.source_map().indentation_before(expr.span).unwrap_or_else(String::new)
1878-
} else {
1879-
String::new()
1880-
};
1881-
if let None = block.expr
1882-
&& let [.., last] = &block.stmts
1883-
{
1884-
err.span_suggestion_verbose(
1885-
last.span.shrink_to_hi(),
1886-
ret_msg,
1887-
format!("\n{indentation}/* `{ty}` value */"),
1888-
Applicability::MaybeIncorrect,
1889-
);
1890-
} else if let Some(expr) = block.expr {
1891-
err.span_suggestion_verbose(
1892-
expr.span.shrink_to_hi(),
1893-
ret_msg,
1894-
format!("\n{indentation}/* `{ty}` value */"),
1895-
Applicability::MaybeIncorrect,
1896-
);
1897-
}
1898-
let mut sugg = match sig.decl.output {
1899-
hir::FnRetTy::DefaultReturn(span) => {
1900-
vec![(span, " -> Option<()>".to_string())]
1901-
}
1902-
hir::FnRetTy::Return(ty) => {
1903-
vec![
1904-
(ty.span.shrink_to_lo(), "Option<".to_string()),
1905-
(ty.span.shrink_to_hi(), ">".to_string()),
1906-
]
1907-
}
1908-
};
1909-
for ret_expr in ret_exprs {
1910-
match ret_expr.kind {
1911-
hir::ExprKind::Ret(Some(expr)) => {
1912-
sugg.push((expr.span.shrink_to_lo(), "Some(".to_string()));
1913-
sugg.push((expr.span.shrink_to_hi(), ")".to_string()));
1914-
}
1915-
hir::ExprKind::Ret(None) => {
1916-
sugg.push((ret_expr.span.shrink_to_hi(), " Some(())".to_string()));
1917-
}
1918-
_ => {}
1919-
}
1920-
}
1921-
if let None = block.expr
1922-
&& let [.., last] = &block.stmts
1923-
{
1924-
sugg.push((last.span.shrink_to_hi(), format!("\n{indentation}None")));
1925-
} else if let Some(expr) = block.expr {
1926-
sugg.push((expr.span.shrink_to_hi(), format!("\n{indentation}None")));
1927-
}
1928-
err.multipart_suggestion(ret_ty_msg, sugg, Applicability::MaybeIncorrect);
1929-
} else {
1930-
err.help(format!("{ret_msg}, {ret_ty_msg}"));
1931-
}
1932-
}
1933-
19341813
fn report_return_mismatched_types<'a>(
19351814
&self,
19361815
cause: &ObligationCause<'tcx>,

Diff for: compiler/rustc_hir_typeck/src/demand.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5757
|| self.suggest_into(err, expr, expr_ty, expected)
5858
|| self.suggest_floating_point_literal(err, expr, expected)
5959
|| self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
60-
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
60+
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty)
61+
|| self.suggest_returning_value_after_loop(err, expr, expected);
6162

6263
if !suggested {
6364
self.note_source_of_type_mismatch_constraint(

Diff for: compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+87-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use rustc_hir::def::Res;
1818
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
1919
use rustc_hir::lang_items::LangItem;
2020
use rustc_hir::{
21-
CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node,
22-
Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
21+
Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId,
22+
Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
2323
};
2424
use rustc_hir_analysis::collect::suggest_impl_trait;
2525
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@@ -1942,6 +1942,91 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19421942
false
19431943
}
19441944

1945+
// If the expr is a while or for loop and is the tail expr of its
1946+
// enclosing body suggest returning a value right after it
1947+
pub fn suggest_returning_value_after_loop(
1948+
&self,
1949+
err: &mut Diag<'_>,
1950+
expr: &hir::Expr<'tcx>,
1951+
expected: Ty<'tcx>,
1952+
) -> bool {
1953+
let hir = self.tcx.hir();
1954+
let enclosing_scope =
1955+
hir.get_enclosing_scope(expr.hir_id).map(|hir_id| self.tcx.hir_node(hir_id));
1956+
1957+
// Get tail expr of the enclosing block or body
1958+
let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
1959+
&& expr.is_some()
1960+
{
1961+
*expr
1962+
} else {
1963+
let body_def_id = hir.enclosing_body_owner(expr.hir_id);
1964+
let body_id = hir.body_owned_by(body_def_id);
1965+
let body = hir.body(body_id);
1966+
1967+
// Get tail expr of the body
1968+
match body.value.kind {
1969+
// Regular function body etc.
1970+
hir::ExprKind::Block(block, _) => block.expr,
1971+
// Anon const body (there's no block in this case)
1972+
hir::ExprKind::DropTemps(expr) => Some(expr),
1973+
_ => None,
1974+
}
1975+
};
1976+
1977+
let Some(tail_expr) = tail_expr else {
1978+
return false; // Body doesn't have a tail expr we can compare with
1979+
};
1980+
1981+
// Get the loop expr within the tail expr
1982+
let loop_expr_in_tail = match expr.kind {
1983+
hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
1984+
hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
1985+
match tail_expr.peel_drop_temps() {
1986+
Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
1987+
_ => return false, // Not really a for loop
1988+
}
1989+
}
1990+
_ => return false, // Not a while or a for loop
1991+
};
1992+
1993+
// If the expr is the loop expr in the tail
1994+
// then make the suggestion
1995+
if expr.hir_id == loop_expr_in_tail.hir_id {
1996+
let span = expr.span;
1997+
1998+
let (msg, suggestion) = if expected.is_never() {
1999+
(
2000+
"consider adding a diverging expression here",
2001+
"`loop {}` or `panic!(\"...\")`".to_string(),
2002+
)
2003+
} else {
2004+
("consider returning a value here", format!("`{expected}` value"))
2005+
};
2006+
2007+
let src_map = self.tcx.sess.source_map();
2008+
let suggestion = if src_map.is_multiline(expr.span) {
2009+
let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2010+
format!("\n{indentation}/* {suggestion} */")
2011+
} else {
2012+
// If the entire expr is on a single line
2013+
// put the suggestion also on the same line
2014+
format!(" /* {suggestion} */")
2015+
};
2016+
2017+
err.span_suggestion_verbose(
2018+
span.shrink_to_hi(),
2019+
msg,
2020+
suggestion,
2021+
Applicability::MaybeIncorrect,
2022+
);
2023+
2024+
true
2025+
} else {
2026+
false
2027+
}
2028+
}
2029+
19452030
/// If the expected type is an enum (Issue #55250) with any variants whose
19462031
/// sole field is of the found type, suggest such variants. (Issue #42764)
19472032
pub(crate) fn suggest_compatible_variants(

0 commit comments

Comments
 (0)