11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: numeric_literal:: NumericLiteral ;
33use clippy_utils:: source:: snippet_opt;
4- use clippy_utils:: { get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local} ;
4+ use clippy_utils:: visitors:: { for_each_expr, Visitable } ;
5+ use clippy_utils:: { get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local} ;
56use if_chain:: if_chain;
67use rustc_ast:: { LitFloatType , LitIntType , LitKind } ;
78use rustc_errors:: Applicability ;
8- use rustc_hir:: def:: Res ;
9+ use rustc_hir:: def:: { DefKind , Res } ;
910use rustc_hir:: { Expr , ExprKind , Lit , Node , Path , QPath , TyKind , UnOp } ;
1011use rustc_lint:: { LateContext , LintContext } ;
1112use rustc_middle:: lint:: in_external_macro;
1213use rustc_middle:: ty:: { self , FloatTy , InferTy , Ty } ;
14+ use std:: ops:: ControlFlow ;
1315
1416use super :: UNNECESSARY_CAST ;
1517
@@ -59,7 +61,7 @@ pub(super) fn check<'tcx>(
5961 }
6062 }
6163
62- // skip cast of local to type alias
64+ // skip cast of local that is a type alias
6365 if let ExprKind :: Cast ( inner, ..) = expr. kind
6466 && let ExprKind :: Path ( qpath) = inner. kind
6567 && let QPath :: Resolved ( None , Path { res, .. } ) = qpath
@@ -83,6 +85,11 @@ pub(super) fn check<'tcx>(
8385 }
8486 }
8587
88+ // skip cast of fn call that returns type alias
89+ if let ExprKind :: Cast ( inner, ..) = expr. kind && is_cast_from_ty_alias ( cx, inner, cast_from) {
90+ return false ;
91+ }
92+
8693 // skip cast to non-primitive type
8794 if_chain ! {
8895 if let ExprKind :: Cast ( _, cast_to) = expr. kind;
@@ -223,3 +230,61 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
223230 _ => 0 ,
224231 }
225232}
233+
234+ /// Finds whether an `Expr` returns a type alias.
235+ ///
236+ /// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
237+ /// dark path reimplementing this (or something similar).
238+ fn is_cast_from_ty_alias < ' tcx > ( cx : & LateContext < ' tcx > , expr : impl Visitable < ' tcx > , cast_from : Ty < ' tcx > ) -> bool {
239+ for_each_expr ( expr, |expr| {
240+ // Calls are a `Path`, and usage of locals are a `Path`. So, this checks
241+ // - call() as i32
242+ // - local as i32
243+ if let ExprKind :: Path ( qpath) = expr. kind {
244+ let res = cx. qpath_res ( & qpath, expr. hir_id ) ;
245+ // Function call
246+ if let Res :: Def ( DefKind :: Fn , def_id) = res {
247+ let Some ( snippet) = snippet_opt ( cx, cx. tcx . def_span ( def_id) ) else {
248+ return ControlFlow :: Continue ( ( ) ) ;
249+ } ;
250+ // This is the worst part of this entire function. This is the only way I know of to
251+ // check whether a function returns a type alias. Sure, you can get the return type
252+ // from a function in the current crate as an hir ty, but how do you get it for
253+ // external functions?? Simple: It's impossible. So, we check whether a part of the
254+ // function's declaration snippet is exactly equal to the `Ty`. That way, we can
255+ // see whether it's a type alias.
256+ //
257+ // Will this work for more complex types? Probably not!
258+ if !snippet
259+ . split ( "->" )
260+ . skip ( 0 )
261+ . map ( |s| {
262+ s. trim ( ) == cast_from. to_string ( )
263+ || s. split ( "where" ) . any ( |ty| ty. trim ( ) == cast_from. to_string ( ) )
264+ } )
265+ . any ( |a| a)
266+ {
267+ return ControlFlow :: Break ( ( ) ) ;
268+ }
269+ // Local usage
270+ } else if let Res :: Local ( hir_id) = res
271+ && let Some ( parent) = get_parent_node ( cx. tcx , hir_id)
272+ && let Node :: Local ( l) = parent
273+ {
274+ if let Some ( e) = l. init && is_cast_from_ty_alias ( cx, e, cast_from) {
275+ return ControlFlow :: Break :: < ( ) > ( ( ) ) ;
276+ }
277+
278+ if let Some ( ty) = l. ty
279+ && let TyKind :: Path ( qpath) = ty. kind
280+ && is_ty_alias ( & qpath)
281+ {
282+ return ControlFlow :: Break :: < ( ) > ( ( ) ) ;
283+ }
284+ }
285+ }
286+
287+ ControlFlow :: Continue ( ( ) )
288+ } )
289+ . is_some ( )
290+ }
0 commit comments