36
36
//! ```
37
37
38
38
use crate :: FnCtxt ;
39
+ use hir:: { Expr , ExprField , ExprKind , LangItem , QPath } ;
40
+ use rustc_ast:: { LitKind , UnOp } ;
39
41
use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
40
42
use rustc_hir as hir;
41
43
use rustc_hir:: def_id:: DefId ;
42
44
use rustc_hir:: intravisit:: { self , Visitor } ;
43
- use rustc_hir:: Expr ;
44
45
use rustc_hir_analysis:: astconv:: AstConv ;
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 , TypeAndMut } ;
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 ;
@@ -1694,6 +1696,23 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
1694
1696
let hir:: ExprKind :: Loop ( _, _, _, loop_span) = expr. kind else {
1695
1697
return ;
1696
1698
} ;
1699
+
1700
+ let hir = tcx. hir ( ) ;
1701
+ let parent_node = tcx. hir_node ( hir. get_parent_item ( expr. hir_id ) . into ( ) ) ;
1702
+ let parent_block = if let Some ( body_id) = parent_node. body_id ( )
1703
+ && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1704
+ {
1705
+ Some ( block)
1706
+ } else {
1707
+ None
1708
+ } ;
1709
+
1710
+ if let Some ( block) = parent_block
1711
+ && Self :: loop_iterates_atleast_once ( block)
1712
+ {
1713
+ return ;
1714
+ }
1715
+
1697
1716
let mut span: MultiSpan = vec ! [ loop_span] . into ( ) ;
1698
1717
span. push_span_label ( loop_span, "this might have zero elements to iterate on" ) ;
1699
1718
const MAXITER : usize = 3 ;
@@ -1714,15 +1733,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
1714
1733
ret_exprs. len( ) - MAXITER
1715
1734
) ) ;
1716
1735
}
1717
- let hir = tcx. hir ( ) ;
1718
- let item = hir. get_parent_item ( expr. hir_id ) ;
1719
1736
let ret_msg = "return a value for the case when the loop has zero elements to iterate on" ;
1720
1737
let ret_ty_msg =
1721
1738
"otherwise consider changing the return type to account for that possibility" ;
1722
- let node = tcx. hir_node ( item. into ( ) ) ;
1723
- if let Some ( body_id) = node. body_id ( )
1724
- && let Some ( sig) = node. fn_sig ( )
1725
- && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1739
+ if let Some ( block) = parent_block
1740
+ && let Some ( sig) = parent_node. fn_sig ( )
1726
1741
&& !ty. is_never ( )
1727
1742
{
1728
1743
let indentation = if let None = block. expr
@@ -1787,6 +1802,97 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
1787
1802
}
1788
1803
}
1789
1804
1805
+ /// Returns `true` if the given `block` is a loop
1806
+ /// and it will definitely iterate at least once
1807
+ fn loop_iterates_atleast_once ( block : & hir:: Block < ' tcx > ) -> bool {
1808
+ // Check if `block` is a for loop and extract
1809
+ // the expr it iterate over as `iter_target`
1810
+ let Some ( Expr {
1811
+ kind :
1812
+ ExprKind :: DropTemps ( Expr {
1813
+ kind :
1814
+ ExprKind :: Match (
1815
+ Expr {
1816
+ kind :
1817
+ ExprKind :: Call (
1818
+ Expr {
1819
+ kind :
1820
+ ExprKind :: Path (
1821
+ QPath :: LangItem ( LangItem :: IntoIterIntoIter , ..) ,
1822
+ ..,
1823
+ ) ,
1824
+ ..
1825
+ } ,
1826
+ [ Expr { kind : iter_target, .. } ] ,
1827
+ ) ,
1828
+ ..
1829
+ } ,
1830
+ ..,
1831
+ ) ,
1832
+ ..
1833
+ } ) ,
1834
+ ..
1835
+ } ) = block. expr
1836
+ else {
1837
+ return false ; // Block is not a for loop
1838
+ } ;
1839
+
1840
+ // Peel away any ref if present
1841
+ let iter_target = match iter_target {
1842
+ ExprKind :: AddrOf ( .., deref) => deref. kind ,
1843
+ _ => * iter_target,
1844
+ } ;
1845
+
1846
+ // Computes value of a literal expr
1847
+ // Takes into account any enclosing neg unary expr if present
1848
+ fn get_literal < ' a > ( expr_kind : & ExprKind < ' a > ) -> Option < i128 > {
1849
+ match expr_kind {
1850
+ ExprKind :: Lit ( Spanned { node : LitKind :: Int ( lit, ..) , .. } ) => {
1851
+ i128:: try_from ( lit. get ( ) ) . ok ( )
1852
+ }
1853
+ ExprKind :: Unary (
1854
+ UnOp :: Neg ,
1855
+ Expr {
1856
+ kind : ExprKind :: Lit ( Spanned { node : LitKind :: Int ( lit, ..) , .. } ) , ..
1857
+ } ,
1858
+ ) => i128:: try_from ( lit. get ( ) ) . map ( |v| -1 * v) . ok ( ) ,
1859
+ _ => None ,
1860
+ }
1861
+ }
1862
+
1863
+ // Check if `iter_target` will be iterated over at least once.
1864
+ // We support only exclusive, inclusive and infinite range
1865
+ // literals and array literals
1866
+ match iter_target {
1867
+ // Exclusive range
1868
+ ExprKind :: Struct (
1869
+ QPath :: LangItem ( LangItem :: Range , ..) ,
1870
+ [
1871
+ ExprField { expr : Expr { kind : start, .. } , .. } ,
1872
+ ExprField { expr : Expr { kind : end, .. } , .. } ,
1873
+ ] ,
1874
+ ..,
1875
+ ) => match ( get_literal ( start) , get_literal ( end) ) {
1876
+ ( Some ( start) , Some ( end) ) => end > start,
1877
+ _ => false ,
1878
+ } ,
1879
+ // Inclusive range
1880
+ ExprKind :: Call (
1881
+ Expr {
1882
+ kind : ExprKind :: Path ( QPath :: LangItem ( LangItem :: RangeInclusiveNew , ..) ) , ..
1883
+ } ,
1884
+ [ Expr { kind : start, .. } , Expr { kind : end, .. } ] ,
1885
+ ) => match ( get_literal ( start) , get_literal ( end) ) {
1886
+ ( Some ( start) , Some ( end) ) => end >= start,
1887
+ _ => false ,
1888
+ } ,
1889
+ // Infinite range
1890
+ ExprKind :: Struct ( QPath :: LangItem ( LangItem :: RangeFrom , ..) , ..) => true ,
1891
+ ExprKind :: Array ( items) => items. len ( ) > 0 ,
1892
+ _ => false ,
1893
+ }
1894
+ }
1895
+
1790
1896
fn report_return_mismatched_types < ' a > (
1791
1897
& self ,
1792
1898
cause : & ObligationCause < ' tcx > ,
0 commit comments