@@ -2104,6 +2104,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
21042104 ) ;
21052105 }
21062106 }
2107+
2108+ if let ( Some ( body_id) , Some ( ty:: subst:: GenericArgKind :: Type ( _) ) ) =
2109+ ( body_id, subst. map ( |subst| subst. unpack ( ) ) )
2110+ {
2111+ struct FindExprBySpan < ' hir > {
2112+ span : Span ,
2113+ result : Option < & ' hir hir:: Expr < ' hir > > ,
2114+ }
2115+
2116+ impl < ' v > hir:: intravisit:: Visitor < ' v > for FindExprBySpan < ' v > {
2117+ fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
2118+ if self . span == ex. span {
2119+ self . result = Some ( ex) ;
2120+ } else {
2121+ hir:: intravisit:: walk_expr ( self , ex) ;
2122+ }
2123+ }
2124+ }
2125+
2126+ let mut expr_finder = FindExprBySpan { span, result : None } ;
2127+
2128+ expr_finder. visit_expr ( & self . tcx . hir ( ) . body ( body_id) . value ) ;
2129+
2130+ if let Some ( hir:: Expr {
2131+ kind : hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) , .. }
2132+ ) = expr_finder. result
2133+ && let [
2134+ ..,
2135+ trait_path_segment @ hir:: PathSegment {
2136+ res : Some ( rustc_hir:: def:: Res :: Def ( rustc_hir:: def:: DefKind :: Trait , trait_id) ) ,
2137+ ..
2138+ } ,
2139+ hir:: PathSegment {
2140+ ident : assoc_item_name,
2141+ res : Some ( rustc_hir:: def:: Res :: Def ( _, item_id) ) ,
2142+ ..
2143+ }
2144+ ] = path. segments
2145+ && data. trait_ref . def_id == * trait_id
2146+ && self . tcx . trait_of_item ( item_id) == Some ( * trait_id)
2147+ && !self . is_tainted_by_errors ( )
2148+ {
2149+ let ( verb, noun) = match self . tcx . associated_item ( item_id) . kind {
2150+ ty:: AssocKind :: Const => ( "refer to the" , "constant" ) ,
2151+ ty:: AssocKind :: Fn => ( "call" , "function" ) ,
2152+ ty:: AssocKind :: Type => ( "refer to the" , "type" ) , // this is already covered by E0223, but this single match arm doesn't hurt here
2153+ } ;
2154+
2155+ // Replace the more general E0283 with a more specific error
2156+ err. cancel ( ) ;
2157+ err = self . tcx . sess . struct_span_err_with_code (
2158+ span,
2159+ & format ! (
2160+ "cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type" ,
2161+ ) ,
2162+ rustc_errors:: error_code!( E0790 ) ,
2163+ ) ;
2164+
2165+ if let Some ( local_def_id) = data. trait_ref . def_id . as_local ( )
2166+ && let Some ( hir:: Node :: Item ( hir:: Item { ident : trait_name, kind : hir:: ItemKind :: Trait ( _, _, _, _, trait_item_refs) , .. } ) ) = self . tcx . hir ( ) . find_by_def_id ( local_def_id)
2167+ && let Some ( method_ref) = trait_item_refs. iter ( ) . find ( |item_ref| item_ref. ident == * assoc_item_name) {
2168+ err. span_label ( method_ref. span , format ! ( "`{}::{}` defined here" , trait_name, assoc_item_name) ) ;
2169+ }
2170+
2171+ err. span_label ( span, format ! ( "cannot {verb} associated {noun} of trait" ) ) ;
2172+
2173+ let trait_impls = self . tcx . trait_impls_of ( data. trait_ref . def_id ) ;
2174+
2175+ if trait_impls. blanket_impls ( ) . is_empty ( )
2176+ && let Some ( ( impl_ty, _) ) = trait_impls. non_blanket_impls ( ) . iter ( ) . next ( )
2177+ && let Some ( impl_def_id) = impl_ty. def ( ) {
2178+ let message = if trait_impls. non_blanket_impls ( ) . len ( ) == 1 {
2179+ "use the fully-qualified path to the only available implementation" . to_string ( )
2180+ } else {
2181+ format ! (
2182+ "use a fully-qualified path to a specific available implementation ({} found)" ,
2183+ trait_impls. non_blanket_impls( ) . len( )
2184+ )
2185+ } ;
2186+
2187+ err. multipart_suggestion (
2188+ message,
2189+ vec ! [
2190+ ( trait_path_segment. ident. span. shrink_to_lo( ) , format!( "<{} as " , self . tcx. def_path( impl_def_id) . to_string_no_crate_verbose( ) ) ) ,
2191+ ( trait_path_segment. ident. span. shrink_to_hi( ) , format!( ">" ) )
2192+ ] ,
2193+ Applicability :: MaybeIncorrect
2194+ ) ;
2195+ }
2196+ }
2197+ } ;
2198+
21072199 err
21082200 }
21092201
0 commit comments