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()
. PathLookup
s 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::Ty
s are associated with a specific span of code, ty::Ty
s are not. For more information, please see rustc_hir::Ty
vs ty::Ty
from the rustc
Dev Guide.