Work with Types
Note
This document assumes you are working with
ty::Ty, notrustc_hir::Ty, unless explicitly stated otherwise.
Getting the Type of an Expression
It is possible to get the type of an expression (Expr) through TypeckResults:
#![allow(unused)] fn main() { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { let ty = cx.typeck_results().expr_ty(expr); } }
Usually you want to use expr_ty_adjusted() instead.
Peeling References
Often you have the type that may be behind one or more references, such as &&str or *mut [T], but you need to extract the underlying type (str and [T] in this case). You can do this by "peeling" references:
#![allow(unused)] fn main() { let peeled_ty = ty.peel_refs(); }
See Ty::peel_refs() for more information.
Getting the Adjusted Type of an Expression
The Rust compiler occasionally makes adjustments to types in order to support automatic dereferencing and type coercion. TypeckResults::expr_ty() ignores these adjustments, returning the type the user wrote. Usually this isn't desired, as you want to see the final coerced type, in which case you should use TypeckResults::expr_ty_adjusted() instead:
#![allow(unused)] fn main() { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { let ty = cx.typeck_results().expr_ty_adjusted(expr); } }
For example, you may be writing a lint to check when a user calls str::contains(). In order to catch the most cases, you want to also check for method calls on types that dereference into str, such as String and Box<str>. expr_ty_adjusted() lets you treat String and Box<str> as str:
#![allow(unused)] fn main() { // `expr_ty()` is `&str`, `expr_ty_adjusted()` is `&str`. let a = "Hello, world!".contains("Hello"); // `expr_ty()` is `String`, `expr_ty_adjusted()` is `&str`. let b = String::from("Hello, world!").contains("Hello"); // `expr_ty()` is `Box<&str>`, `expr_ty_adjusted()` is `&str`. let c = Box::new("Hello, world!").contains("Hello"); }
For more information, see Adjustment, Type coercions, and Method lookup.
Checking for a Specific Type
Often you have a Ty, and want to check if it matches a specific hardcoded type, such as Bevy's App. You can check if a type matches a specific path using PathLookup::matches_ty(). PathLookups used by bevy_lint are placed in the paths module:
#![allow(unused)] fn main() { // Import the `PathLookup` for Bevy's `App` type. use crate::paths::APP; // Returns true if `ty` is an `App`. if APP.matches_ty(cx, ty) { // ... } }
Getting ty::Ty from rustc_hir::Ty
Often you'll have an rustc_hir::Ty, but you need ty::Ty. This is a process known as lowering, and it is accomplished through the ty_from_hir_ty() function:
#![allow(unused)] fn main() { use clippy_utils::ty::ty_from_hir_ty; fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &rustc_hir::Ty<'tcx, AmbigArg>) { let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()); } }
Also note that this conversion is one-directional and cannot be easily reversed. While rustc_hir::Tys are associated with a specific span of code, ty::Tys are not. For more information, please see rustc_hir::Ty vs ty::Ty from the rustc Dev Guide.