1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: numeric_literal:: NumericLiteral ;
3
3
use 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} ;
5
6
use if_chain:: if_chain;
6
7
use rustc_ast:: { LitFloatType , LitIntType , LitKind } ;
7
8
use rustc_errors:: Applicability ;
8
- use rustc_hir:: def:: Res ;
9
+ use rustc_hir:: def:: { DefKind , Res } ;
9
10
use rustc_hir:: { Expr , ExprKind , Lit , Node , Path , QPath , TyKind , UnOp } ;
10
11
use rustc_lint:: { LateContext , LintContext } ;
11
12
use rustc_middle:: lint:: in_external_macro;
12
13
use rustc_middle:: ty:: { self , FloatTy , InferTy , Ty } ;
14
+ use std:: ops:: ControlFlow ;
13
15
14
16
use super :: UNNECESSARY_CAST ;
15
17
@@ -59,7 +61,7 @@ pub(super) fn check<'tcx>(
59
61
}
60
62
}
61
63
62
- // skip cast of local to type alias
64
+ // skip cast of local that is a type alias
63
65
if let ExprKind :: Cast ( inner, ..) = expr. kind
64
66
&& let ExprKind :: Path ( qpath) = inner. kind
65
67
&& let QPath :: Resolved ( None , Path { res, .. } ) = qpath
@@ -83,6 +85,11 @@ pub(super) fn check<'tcx>(
83
85
}
84
86
}
85
87
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
+
86
93
// skip cast to non-primitive type
87
94
if_chain ! {
88
95
if let ExprKind :: Cast ( _, cast_to) = expr. kind;
@@ -223,3 +230,61 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
223
230
_ => 0 ,
224
231
}
225
232
}
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