|
5 | 5 | use rustc_ast::ast::Mutability;
|
6 | 6 | use rustc_data_structures::fx::FxHashMap;
|
7 | 7 | use rustc_hir as hir;
|
| 8 | +use rustc_hir::def::{CtorKind, DefKind, Res}; |
8 | 9 | use rustc_hir::def_id::DefId;
|
9 |
| -use rustc_hir::{TyKind, Unsafety}; |
| 10 | +use rustc_hir::{Expr, TyKind, Unsafety}; |
10 | 11 | use rustc_infer::infer::TyCtxtInferExt;
|
11 | 12 | use rustc_lint::LateContext;
|
12 |
| -use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; |
13 |
| -use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy}; |
| 13 | +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; |
| 14 | +use rustc_middle::ty::{ |
| 15 | + self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, |
| 16 | +}; |
14 | 17 | use rustc_span::symbol::Ident;
|
15 | 18 | use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
16 | 19 | use rustc_trait_selection::infer::InferCtxtExt;
|
17 | 20 | use rustc_trait_selection::traits::query::normalize::AtExt;
|
18 | 21 | use std::iter;
|
19 | 22 |
|
20 |
| -use crate::{match_def_path, must_use_attr}; |
| 23 | +use crate::{expr_path_res, match_def_path, must_use_attr}; |
21 | 24 |
|
22 | 25 | // Checks if the given type implements copy.
|
23 | 26 | pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
@@ -410,3 +413,105 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
|
410 | 413 | })
|
411 | 414 | .flatten()
|
412 | 415 | }
|
| 416 | + |
| 417 | +/// A signature for a function like type. |
| 418 | +#[derive(Clone, Copy)] |
| 419 | +pub enum ExprFnSig<'tcx> { |
| 420 | + Sig(Binder<'tcx, FnSig<'tcx>>), |
| 421 | + Closure(Binder<'tcx, FnSig<'tcx>>), |
| 422 | + Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>), |
| 423 | +} |
| 424 | +impl<'tcx> ExprFnSig<'tcx> { |
| 425 | + /// Gets the argument type at the given offset. |
| 426 | + pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> { |
| 427 | + match self { |
| 428 | + Self::Sig(sig) => sig.input(i), |
| 429 | + Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_element_ty(i).unwrap()), |
| 430 | + Self::Trait(inputs, _) => inputs.map_bound(|ty| ty.tuple_element_ty(i).unwrap()), |
| 431 | + } |
| 432 | + } |
| 433 | + |
| 434 | + /// Gets the result type, if one could be found. Note that the result type of a trait may not be |
| 435 | + /// specified. |
| 436 | + pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> { |
| 437 | + match self { |
| 438 | + Self::Sig(sig) | Self::Closure(sig) => Some(sig.output()), |
| 439 | + Self::Trait(_, output) => output, |
| 440 | + } |
| 441 | + } |
| 442 | +} |
| 443 | + |
| 444 | +/// If the expression is function like, get the signature for it. |
| 445 | +pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> { |
| 446 | + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) { |
| 447 | + Some(ExprFnSig::Sig(cx.tcx.fn_sig(id))) |
| 448 | + } else { |
| 449 | + let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); |
| 450 | + match *ty.kind() { |
| 451 | + ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())), |
| 452 | + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))), |
| 453 | + ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)), |
| 454 | + ty::Dynamic(bounds, _) => { |
| 455 | + let lang_items = cx.tcx.lang_items(); |
| 456 | + match bounds.principal() { |
| 457 | + Some(bound) |
| 458 | + if Some(bound.def_id()) == lang_items.fn_trait() |
| 459 | + || Some(bound.def_id()) == lang_items.fn_once_trait() |
| 460 | + || Some(bound.def_id()) == lang_items.fn_mut_trait() => |
| 461 | + { |
| 462 | + let output = bounds |
| 463 | + .projection_bounds() |
| 464 | + .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id())) |
| 465 | + .map(|p| p.map_bound(|p| p.ty)); |
| 466 | + Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output)) |
| 467 | + }, |
| 468 | + _ => None, |
| 469 | + } |
| 470 | + }, |
| 471 | + ty::Param(_) | ty::Projection(..) => { |
| 472 | + let mut inputs = None; |
| 473 | + let mut output = None; |
| 474 | + let lang_items = cx.tcx.lang_items(); |
| 475 | + |
| 476 | + for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) { |
| 477 | + let mut is_input = false; |
| 478 | + if let Some(ty) = pred |
| 479 | + .kind() |
| 480 | + .map_bound(|pred| match pred { |
| 481 | + PredicateKind::Trait(p) |
| 482 | + if (lang_items.fn_trait() == Some(p.def_id()) |
| 483 | + || lang_items.fn_mut_trait() == Some(p.def_id()) |
| 484 | + || lang_items.fn_once_trait() == Some(p.def_id())) |
| 485 | + && p.self_ty() == ty => |
| 486 | + { |
| 487 | + is_input = true; |
| 488 | + Some(p.trait_ref.substs.type_at(1)) |
| 489 | + }, |
| 490 | + PredicateKind::Projection(p) |
| 491 | + if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() |
| 492 | + && p.projection_ty.self_ty() == ty => |
| 493 | + { |
| 494 | + is_input = false; |
| 495 | + Some(p.ty) |
| 496 | + }, |
| 497 | + _ => None, |
| 498 | + }) |
| 499 | + .transpose() |
| 500 | + { |
| 501 | + if is_input && inputs.is_none() { |
| 502 | + inputs = Some(ty); |
| 503 | + } else if !is_input && output.is_none() { |
| 504 | + output = Some(ty); |
| 505 | + } else { |
| 506 | + // Multiple different fn trait impls. Is this even allowed? |
| 507 | + return None; |
| 508 | + } |
| 509 | + } |
| 510 | + } |
| 511 | + |
| 512 | + inputs.map(|ty| ExprFnSig::Trait(ty, output)) |
| 513 | + }, |
| 514 | + _ => None, |
| 515 | + } |
| 516 | + } |
| 517 | +} |
0 commit comments