@@ -6,7 +6,7 @@ use crate::{Module, ModuleKind, ModuleOrUniformRoot};
6
6
use crate :: { PathResult , PathSource , Segment } ;
7
7
use rustc_hir:: def:: Namespace :: { self , * } ;
8
8
9
- use rustc_ast:: visit:: { FnCtxt , FnKind , LifetimeCtxt } ;
9
+ use rustc_ast:: visit:: { walk_ty , FnCtxt , FnKind , LifetimeCtxt , Visitor } ;
10
10
use rustc_ast:: {
11
11
self as ast, AssocItemKind , Expr , ExprKind , GenericParam , GenericParamKind , Item , ItemKind ,
12
12
MethodCall , NodeId , Path , Ty , TyKind , DUMMY_NODE_ID ,
@@ -2612,6 +2612,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
2612
2612
. collect ( ) ;
2613
2613
debug ! ( ?in_scope_lifetimes) ;
2614
2614
2615
+ let mut maybe_static = false ;
2615
2616
debug ! ( ?function_param_lifetimes) ;
2616
2617
if let Some ( ( param_lifetimes, params) ) = & function_param_lifetimes {
2617
2618
let elided_len = param_lifetimes. len ( ) ;
@@ -2650,9 +2651,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
2650
2651
2651
2652
if num_params == 0 {
2652
2653
err. help (
2653
- "this function's return type contains a borrowed value, \
2654
- but there is no value for it to be borrowed from",
2654
+ "this function's return type contains a borrowed value, but there is no value \
2655
+ for it to be borrowed from",
2655
2656
) ;
2657
+ maybe_static = true ;
2656
2658
if in_scope_lifetimes. is_empty ( ) {
2657
2659
in_scope_lifetimes = vec ! [ (
2658
2660
Ident :: with_dummy_span( kw:: StaticLifetime ) ,
@@ -2661,10 +2663,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
2661
2663
}
2662
2664
} else if elided_len == 0 {
2663
2665
err. help (
2664
- "this function's return type contains a borrowed value with \
2665
- an elided lifetime, but the lifetime cannot be derived from \
2666
- the arguments",
2666
+ "this function's return type contains a borrowed value with an elided \
2667
+ lifetime, but the lifetime cannot be derived from the arguments",
2667
2668
) ;
2669
+ maybe_static = true ;
2668
2670
if in_scope_lifetimes. is_empty ( ) {
2669
2671
in_scope_lifetimes = vec ! [ (
2670
2672
Ident :: with_dummy_span( kw:: StaticLifetime ) ,
@@ -2673,13 +2675,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
2673
2675
}
2674
2676
} else if num_params == 1 {
2675
2677
err. help ( format ! (
2676
- "this function's return type contains a borrowed value, \
2677
- but the signature does not say which {m} it is borrowed from"
2678
+ "this function's return type contains a borrowed value, but the signature does \
2679
+ not say which {m} it is borrowed from",
2678
2680
) ) ;
2679
2681
} else {
2680
2682
err. help ( format ! (
2681
- "this function's return type contains a borrowed value, \
2682
- but the signature does not say whether it is borrowed from {m}"
2683
+ "this function's return type contains a borrowed value, but the signature does \
2684
+ not say whether it is borrowed from {m}",
2683
2685
) ) ;
2684
2686
}
2685
2687
}
@@ -2744,11 +2746,107 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
2744
2746
) ;
2745
2747
}
2746
2748
1 => {
2749
+ let post = if maybe_static {
2750
+ let owned = if let [ lt] = & lifetime_refs[ ..]
2751
+ && lt. kind != MissingLifetimeKind :: Ampersand
2752
+ {
2753
+ ", or if you will only have owned values"
2754
+ } else {
2755
+ ""
2756
+ } ;
2757
+ format ! (
2758
+ ", but this is uncommon unless you're returning a borrowed value from a \
2759
+ `const` or a `static`{owned}",
2760
+ )
2761
+ } else {
2762
+ String :: new ( )
2763
+ } ;
2747
2764
err. multipart_suggestion_verbose (
2748
- format ! ( "consider using the `{existing_name}` lifetime" ) ,
2765
+ format ! ( "consider using the `{existing_name}` lifetime{post} " ) ,
2749
2766
spans_suggs,
2750
2767
Applicability :: MaybeIncorrect ,
2751
2768
) ;
2769
+ if maybe_static {
2770
+ // FIXME: what follows are general suggestions, but we'd want to perform some
2771
+ // minimal flow analysis to provide more accurate suggestions. For example, if
2772
+ // we identified that the return expression references only one argument, we
2773
+ // would suggest borrowing only that argument, and we'd skip the prior
2774
+ // "use `'static`" suggestion entirely.
2775
+ if let [ lt] = & lifetime_refs[ ..] && lt. kind == MissingLifetimeKind :: Ampersand {
2776
+ let pre = if let Some ( ( kind, _span) ) =
2777
+ self . diagnostic_metadata . current_function
2778
+ && let FnKind :: Fn ( _, _, sig, _, _, _) = kind
2779
+ && !sig. decl . inputs . is_empty ( )
2780
+ && let sugg = sig
2781
+ . decl
2782
+ . inputs
2783
+ . iter ( )
2784
+ . filter_map ( |param| {
2785
+ if param. ty . span . contains ( lt. span ) {
2786
+ // We don't want to suggest `fn elision(_: &fn() -> &i32)`
2787
+ // when we have `fn elision(_: fn() -> &i32)`
2788
+ None
2789
+ } else if let TyKind :: CVarArgs = param. ty . kind {
2790
+ // Don't suggest `&...` for ffi fn with varargs
2791
+ None
2792
+ } else {
2793
+ Some ( ( param. ty . span . shrink_to_lo ( ) , "&" . to_string ( ) ) )
2794
+ }
2795
+ } )
2796
+ . collect :: < Vec < _ > > ( )
2797
+ && !sugg. is_empty ( )
2798
+ {
2799
+
2800
+ let ( the, s) = if sig. decl . inputs . len ( ) == 1 {
2801
+ ( "the" , "" )
2802
+ } else {
2803
+ ( "one of the" , "s" )
2804
+ } ;
2805
+ err. multipart_suggestion_verbose (
2806
+ format ! (
2807
+ "instead, you are more likely to want to change {the} \
2808
+ argument{s} to be borrowed...",
2809
+ ) ,
2810
+ sugg,
2811
+ Applicability :: MaybeIncorrect ,
2812
+ ) ;
2813
+ "...or alternatively,"
2814
+ } else {
2815
+ "instead, you are more likely"
2816
+ } ;
2817
+ let mut sugg = vec ! [ ( lt. span, String :: new( ) ) ] ;
2818
+ if let Some ( ( kind, _span) ) =
2819
+ self . diagnostic_metadata . current_function
2820
+ && let FnKind :: Fn ( _, _, sig, _, _, _) = kind
2821
+ && let ast:: FnRetTy :: Ty ( ty) = & sig. decl . output
2822
+ {
2823
+ let mut lt_finder = LifetimeFinder { lifetime : lt. span , found : None } ;
2824
+ lt_finder. visit_ty ( & ty) ;
2825
+
2826
+ if let Some ( ty) = lt_finder. found {
2827
+ if let TyKind :: Path ( None , Path { segments, .. } ) = & ty. kind
2828
+ && segments. len ( ) == 1
2829
+ && segments[ 0 ] . ident . name == sym:: str
2830
+ {
2831
+ // Don't suggest `-> str`, suggest `-> String`.
2832
+ sugg = vec ! [ ( lt. span. with_hi( ty. span. hi( ) ) , "String" . to_string( ) ) ] ;
2833
+ }
2834
+ if let TyKind :: Slice ( inner_ty) = & ty. kind {
2835
+ // Don't suggest `-> [T]`, suggest `-> Vec<T>`.
2836
+ sugg = vec ! [
2837
+ ( lt. span. with_hi( inner_ty. span. lo( ) ) , "Vec<" . to_string( ) ) ,
2838
+ ( ty. span. with_lo( inner_ty. span. hi( ) ) , ">" . to_string( ) ) ,
2839
+ ] ;
2840
+ }
2841
+ }
2842
+ } ;
2843
+ err. multipart_suggestion_verbose (
2844
+ format ! ( "{pre} to want to return an owned value" ) ,
2845
+ sugg,
2846
+ Applicability :: MaybeIncorrect ,
2847
+ ) ;
2848
+ }
2849
+ }
2752
2850
2753
2851
// Record as using the suggested resolution.
2754
2852
let ( _, ( _, res) ) = in_scope_lifetimes[ 0 ] ;
@@ -2778,7 +2876,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
2778
2876
fn mk_where_bound_predicate (
2779
2877
path : & Path ,
2780
2878
poly_trait_ref : & ast:: PolyTraitRef ,
2781
- ty : & ast :: Ty ,
2879
+ ty : & Ty ,
2782
2880
) -> Option < ast:: WhereBoundPredicate > {
2783
2881
use rustc_span:: DUMMY_SP ;
2784
2882
let modified_segments = {
@@ -2855,6 +2953,22 @@ pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: I
2855
2953
err. emit ( ) ;
2856
2954
}
2857
2955
2956
+ struct LifetimeFinder < ' ast > {
2957
+ lifetime : Span ,
2958
+ found : Option < & ' ast Ty > ,
2959
+ }
2960
+
2961
+ impl < ' ast > Visitor < ' ast > for LifetimeFinder < ' ast > {
2962
+ fn visit_ty ( & mut self , t : & ' ast Ty ) {
2963
+ if t. span . lo ( ) == self . lifetime . lo ( )
2964
+ && let TyKind :: Ref ( _, mut_ty) = & t. kind
2965
+ {
2966
+ self . found = Some ( & mut_ty. ty ) ;
2967
+ }
2968
+ walk_ty ( self , t)
2969
+ }
2970
+ }
2971
+
2858
2972
/// Shadowing involving a label is only a warning for historical reasons.
2859
2973
//FIXME: make this a proper lint.
2860
2974
pub ( super ) fn signal_label_shadowing ( sess : & Session , orig : Span , shadower : Ident ) {
0 commit comments