36
36
//! ```
37
37
38
38
use crate :: FnCtxt ;
39
+ use rustc_ast:: { LitKind , UnOp } ;
39
40
use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
40
41
use rustc_hir as hir;
41
42
use rustc_hir:: def_id:: DefId ;
42
43
use rustc_hir:: intravisit:: { self , Visitor } ;
43
- use rustc_hir:: Expr ;
44
+ use rustc_hir:: { Expr , ExprField , ExprKind , LangItem , QPath } ;
44
45
use rustc_hir_analysis:: hir_ty_lowering:: HirTyLowerer ;
45
46
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
46
47
use rustc_infer:: infer:: { Coercion , DefineOpaqueTypes , InferOk , InferResult } ;
@@ -57,6 +58,7 @@ use rustc_middle::ty::relate::RelateResult;
57
58
use rustc_middle:: ty:: visit:: TypeVisitableExt ;
58
59
use rustc_middle:: ty:: { self , GenericArgsRef , Ty , TyCtxt } ;
59
60
use rustc_session:: parse:: feature_err;
61
+ use rustc_span:: source_map:: Spanned ;
60
62
use rustc_span:: symbol:: sym;
61
63
use rustc_span:: DesugaringKind ;
62
64
use rustc_target:: spec:: abi:: Abi ;
@@ -1688,6 +1690,23 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
1688
1690
let hir:: ExprKind :: Loop ( _, _, _, loop_span) = expr. kind else {
1689
1691
return ;
1690
1692
} ;
1693
+
1694
+ let hir = tcx. hir ( ) ;
1695
+ let parent_node = tcx. hir_node ( hir. get_parent_item ( expr. hir_id ) . into ( ) ) ;
1696
+ let parent_block = if let Some ( body_id) = parent_node. body_id ( )
1697
+ && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1698
+ {
1699
+ Some ( block)
1700
+ } else {
1701
+ None
1702
+ } ;
1703
+
1704
+ if let Some ( block) = parent_block
1705
+ && Self :: loop_iterates_atleast_once ( block)
1706
+ {
1707
+ return ;
1708
+ }
1709
+
1691
1710
let mut span: MultiSpan = vec ! [ loop_span] . into ( ) ;
1692
1711
span. push_span_label ( loop_span, "this might have zero elements to iterate on" ) ;
1693
1712
const MAXITER : usize = 3 ;
@@ -1708,15 +1727,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
1708
1727
ret_exprs. len( ) - MAXITER
1709
1728
) ) ;
1710
1729
}
1711
- let hir = tcx. hir ( ) ;
1712
- let item = hir. get_parent_item ( expr. hir_id ) ;
1713
1730
let ret_msg = "return a value for the case when the loop has zero elements to iterate on" ;
1714
1731
let ret_ty_msg =
1715
1732
"otherwise consider changing the return type to account for that possibility" ;
1716
- let node = tcx. hir_node ( item. into ( ) ) ;
1717
- if let Some ( body_id) = node. body_id ( )
1718
- && let Some ( sig) = node. fn_sig ( )
1719
- && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1733
+ if let Some ( block) = parent_block
1734
+ && let Some ( sig) = parent_node. fn_sig ( )
1720
1735
&& !ty. is_never ( )
1721
1736
{
1722
1737
let indentation = if let None = block. expr
@@ -1781,6 +1796,97 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
1781
1796
}
1782
1797
}
1783
1798
1799
+ /// Returns `true` if the given `block` is a loop
1800
+ /// and it will definitely iterate at least once
1801
+ fn loop_iterates_atleast_once ( block : & hir:: Block < ' tcx > ) -> bool {
1802
+ // Check if `block` is a for loop and extract
1803
+ // the expr it iterate over as `iter_target`
1804
+ let Some ( Expr {
1805
+ kind :
1806
+ ExprKind :: DropTemps ( Expr {
1807
+ kind :
1808
+ ExprKind :: Match (
1809
+ Expr {
1810
+ kind :
1811
+ ExprKind :: Call (
1812
+ Expr {
1813
+ kind :
1814
+ ExprKind :: Path (
1815
+ QPath :: LangItem ( LangItem :: IntoIterIntoIter , ..) ,
1816
+ ..,
1817
+ ) ,
1818
+ ..
1819
+ } ,
1820
+ [ Expr { kind : iter_target, .. } ] ,
1821
+ ) ,
1822
+ ..
1823
+ } ,
1824
+ ..,
1825
+ ) ,
1826
+ ..
1827
+ } ) ,
1828
+ ..
1829
+ } ) = block. expr
1830
+ else {
1831
+ return false ; // Block is not a for loop
1832
+ } ;
1833
+
1834
+ // Peel away any ref if present
1835
+ let iter_target = match iter_target {
1836
+ ExprKind :: AddrOf ( .., deref) => deref. kind ,
1837
+ _ => * iter_target,
1838
+ } ;
1839
+
1840
+ // Computes value of a literal expr
1841
+ // Takes into account any enclosing neg unary expr if present
1842
+ fn get_literal < ' a > ( expr_kind : & ExprKind < ' a > ) -> Option < i128 > {
1843
+ match expr_kind {
1844
+ ExprKind :: Lit ( Spanned { node : LitKind :: Int ( lit, ..) , .. } ) => {
1845
+ i128:: try_from ( lit. get ( ) ) . ok ( )
1846
+ }
1847
+ ExprKind :: Unary (
1848
+ UnOp :: Neg ,
1849
+ Expr {
1850
+ kind : ExprKind :: Lit ( Spanned { node : LitKind :: Int ( lit, ..) , .. } ) , ..
1851
+ } ,
1852
+ ) => i128:: try_from ( lit. get ( ) ) . map ( |v| -1 * v) . ok ( ) ,
1853
+ _ => None ,
1854
+ }
1855
+ }
1856
+
1857
+ // Check if `iter_target` will be iterated over at least once.
1858
+ // We support only exclusive, inclusive and infinite range
1859
+ // literals and array literals
1860
+ match iter_target {
1861
+ // Exclusive range
1862
+ ExprKind :: Struct (
1863
+ QPath :: LangItem ( LangItem :: Range , ..) ,
1864
+ [
1865
+ ExprField { expr : Expr { kind : start, .. } , .. } ,
1866
+ ExprField { expr : Expr { kind : end, .. } , .. } ,
1867
+ ] ,
1868
+ ..,
1869
+ ) => match ( get_literal ( start) , get_literal ( end) ) {
1870
+ ( Some ( start) , Some ( end) ) => end > start,
1871
+ _ => false ,
1872
+ } ,
1873
+ // Inclusive range
1874
+ ExprKind :: Call (
1875
+ Expr {
1876
+ kind : ExprKind :: Path ( QPath :: LangItem ( LangItem :: RangeInclusiveNew , ..) ) , ..
1877
+ } ,
1878
+ [ Expr { kind : start, .. } , Expr { kind : end, .. } ] ,
1879
+ ) => match ( get_literal ( start) , get_literal ( end) ) {
1880
+ ( Some ( start) , Some ( end) ) => end >= start,
1881
+ _ => false ,
1882
+ } ,
1883
+ // Infinite range
1884
+ ExprKind :: Struct ( QPath :: LangItem ( LangItem :: RangeFrom , ..) , ..) => true ,
1885
+ ExprKind :: Array ( items) => items. len ( ) > 0 ,
1886
+ _ => false ,
1887
+ }
1888
+ }
1889
+
1784
1890
fn report_return_mismatched_types < ' a > (
1785
1891
& self ,
1786
1892
cause : & ObligationCause < ' tcx > ,
0 commit comments