Auto merge of #13200 - Jarcho:constant_no_typeck, r=Alexendoo
Don't use `LateContext` in the constant evaluator This also changes the interface to require explicitly creating the context. `constant` could be added back in, but the others are probably not worth it. A couple of bugs have been fixed. The wrong `TypeckResults` was used once when evaluating a constant, and the wrong `ParamEnv` was used by some callers (there wasn't a way to use the correct one). changelog: none
This commit is contained in:
commit
b1e87922c1
54 changed files with 395 additions and 386 deletions
|
|
@ -14,12 +14,13 @@ use rustc_lexer::tokenize;
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::{alloc_range, Scalar};
|
||||
use rustc_middle::mir::ConstValue;
|
||||
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy};
|
||||
use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
use rustc_target::abi::Size;
|
||||
use std::cell::Cell;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter;
|
||||
|
|
@ -263,10 +264,10 @@ impl<'tcx> Constant<'tcx> {
|
|||
}
|
||||
|
||||
/// Returns the integer value or `None` if `self` or `val_type` is not integer type.
|
||||
pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
|
||||
pub fn int_value(&self, tcx: TyCtxt<'_>, val_type: Ty<'_>) -> Option<FullInt> {
|
||||
if let Constant::Int(const_int) = *self {
|
||||
match *val_type.kind() {
|
||||
ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
|
||||
ty::Int(ity) => Some(FullInt::S(sext(tcx, const_int, ity))),
|
||||
ty::Uint(_) => Some(FullInt::U(const_int)),
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -322,6 +323,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan
|
|||
}
|
||||
|
||||
/// The source of a constant value.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ConstantSource {
|
||||
/// The value is determined solely from the expression.
|
||||
Local,
|
||||
|
|
@ -331,54 +333,11 @@ pub enum ConstantSource {
|
|||
CoreConstant,
|
||||
}
|
||||
impl ConstantSource {
|
||||
pub fn is_local(&self) -> bool {
|
||||
pub fn is_local(self) -> bool {
|
||||
matches!(self, Self::Local)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to check whether the expression is a constant representing an empty slice, str, array,
|
||||
/// etc…
|
||||
pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> {
|
||||
ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e)
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression as a constant.
|
||||
pub fn constant<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<Constant<'tcx>> {
|
||||
ConstEvalLateContext::new(lcx, typeck_results).expr(e)
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression as a constant.
|
||||
pub fn constant_with_source<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<(Constant<'tcx>, ConstantSource)> {
|
||||
let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results);
|
||||
let res = ctxt.expr(e);
|
||||
res.map(|x| (x, ctxt.source))
|
||||
}
|
||||
|
||||
/// Attempts to evaluate an expression only if its value is not dependent on other items.
|
||||
pub fn constant_simple<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<Constant<'tcx>> {
|
||||
constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c))
|
||||
}
|
||||
|
||||
pub fn constant_full_int<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
typeck_results: &ty::TypeckResults<'tcx>,
|
||||
e: &Expr<'_>,
|
||||
) -> Option<FullInt> {
|
||||
constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq)]
|
||||
pub enum FullInt {
|
||||
S(i128),
|
||||
|
|
@ -417,44 +376,87 @@ impl Ord for FullInt {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ConstEvalLateContext<'a, 'tcx> {
|
||||
lcx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
source: ConstantSource,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
/// The context required to evaluate a constant expression.
|
||||
///
|
||||
/// This is currently limited to constant folding and reading the value of named constants.
|
||||
pub struct ConstEvalCtxt<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
source: Cell<ConstantSource>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self {
|
||||
impl<'tcx> ConstEvalCtxt<'tcx> {
|
||||
/// Creates the evaluation context from the lint context. This requires the lint context to be
|
||||
/// in a body (i.e. `cx.enclosing_body.is_some()`).
|
||||
pub fn new(cx: &LateContext<'tcx>) -> Self {
|
||||
Self {
|
||||
lcx,
|
||||
typeck_results,
|
||||
param_env: lcx.param_env,
|
||||
source: ConstantSource::Local,
|
||||
args: List::empty(),
|
||||
tcx: cx.tcx,
|
||||
param_env: cx.param_env,
|
||||
typeck: cx.typeck_results(),
|
||||
source: Cell::new(ConstantSource::Local),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an evaluation context.
|
||||
pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self {
|
||||
Self {
|
||||
tcx,
|
||||
param_env,
|
||||
typeck,
|
||||
source: Cell::new(ConstantSource::Local),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression and returns both the value and whether it's dependant on
|
||||
/// other items.
|
||||
pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> {
|
||||
self.source.set(ConstantSource::Local);
|
||||
self.expr(e).map(|c| (c, self.source.get()))
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression.
|
||||
pub fn eval(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
self.expr(e)
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression without accessing other items.
|
||||
pub fn eval_simple(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
match self.eval_with_source(e) {
|
||||
Some((x, ConstantSource::Local)) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression as an integer without accessing other items.
|
||||
pub fn eval_full_int(&self, e: &Expr<'_>) -> Option<FullInt> {
|
||||
match self.eval_with_source(e) {
|
||||
Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple constant folding: Insert an expression, get a constant or none.
|
||||
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
match e.kind {
|
||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value),
|
||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
|
||||
ExprKind::DropTemps(e) => self.expr(e),
|
||||
ExprKind::Path(ref qpath) => {
|
||||
let is_core_crate = if let Some(def_id) = self.lcx.qpath_res(qpath, e.hir_id()).opt_def_id() {
|
||||
self.lcx.tcx.crate_name(def_id.krate) == sym::core
|
||||
let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() {
|
||||
self.tcx.crate_name(def_id.krate) == sym::core
|
||||
} else {
|
||||
false
|
||||
};
|
||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
|
||||
let result = mir_to_const(this.lcx, result)?;
|
||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
|
||||
let result = mir_to_const(self_.tcx, result)?;
|
||||
// If source is already Constant we wouldn't want to override it with CoreConstant
|
||||
this.source = if is_core_crate && !matches!(this.source, ConstantSource::Constant) {
|
||||
ConstantSource::CoreConstant
|
||||
} else {
|
||||
ConstantSource::Constant
|
||||
};
|
||||
self_.source.set(
|
||||
if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
|
||||
ConstantSource::CoreConstant
|
||||
} else {
|
||||
ConstantSource::Constant
|
||||
},
|
||||
);
|
||||
Some(result)
|
||||
})
|
||||
},
|
||||
|
|
@ -463,21 +465,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
if is_direct_expn_of(e.span, "cfg").is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
|
||||
Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e)))
|
||||
}
|
||||
},
|
||||
ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
|
||||
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
|
||||
ExprKind::Repeat(value, _) => {
|
||||
let n = match self.typeck_results.expr_ty(e).kind() {
|
||||
ty::Array(_, n) => n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)?,
|
||||
let n = match self.typeck.expr_ty(e).kind() {
|
||||
ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
|
||||
_ => span_bug!(e.span, "typeck error"),
|
||||
};
|
||||
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
|
||||
},
|
||||
ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
|
||||
UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
|
||||
UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
|
||||
UnOp::Not => self.constant_not(&o, self.typeck.expr_ty(e)),
|
||||
UnOp::Neg => self.constant_negate(&o, self.typeck.expr_ty(e)),
|
||||
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
|
||||
}),
|
||||
ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
|
||||
|
|
@ -486,21 +488,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
// We only handle a few const functions for now.
|
||||
if args.is_empty()
|
||||
&& let ExprKind::Path(qpath) = &callee.kind
|
||||
&& let res = self.typeck_results.qpath_res(qpath, callee.hir_id)
|
||||
&& let Some(def_id) = res.opt_def_id()
|
||||
&& let def_path = self.lcx.get_def_path(def_id)
|
||||
&& let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::<Vec<_>>()
|
||||
&& let ["core", "num", int_impl, "max_value"] = *def_path
|
||||
&& let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id()
|
||||
{
|
||||
let value = match int_impl {
|
||||
"<impl i8>" => i8::MAX as u128,
|
||||
"<impl i16>" => i16::MAX as u128,
|
||||
"<impl i32>" => i32::MAX as u128,
|
||||
"<impl i64>" => i64::MAX as u128,
|
||||
"<impl i128>" => i128::MAX as u128,
|
||||
_ => return None,
|
||||
};
|
||||
Some(Constant::Int(value))
|
||||
match self.tcx.get_diagnostic_name(did) {
|
||||
Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)),
|
||||
Some(sym::i16_legacy_fn_max_value) => Some(Constant::Int(i16::MAX as u128)),
|
||||
Some(sym::i32_legacy_fn_max_value) => Some(Constant::Int(i32::MAX as u128)),
|
||||
Some(sym::i64_legacy_fn_max_value) => Some(Constant::Int(i64::MAX as u128)),
|
||||
Some(sym::i128_legacy_fn_max_value) => Some(Constant::Int(i128::MAX as u128)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -512,9 +509,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
|
||||
&& let ty::Adt(adt_def, _) = constant.ty().kind()
|
||||
&& adt_def.is_struct()
|
||||
&& let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field)
|
||||
&& let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field)
|
||||
{
|
||||
mir_to_const(self.lcx, desired_field)
|
||||
mir_to_const(self.tcx, desired_field)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
|
|
@ -526,21 +523,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
/// Simple constant folding to determine if an expression is an empty slice, str, array, …
|
||||
/// `None` will be returned if the constness cannot be determined, or if the resolution
|
||||
/// leaves the local crate.
|
||||
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
|
||||
pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option<bool> {
|
||||
match e.kind {
|
||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value),
|
||||
ExprKind::DropTemps(e) => self.expr_is_empty(e),
|
||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value),
|
||||
ExprKind::DropTemps(e) => self.eval_is_empty(e),
|
||||
ExprKind::Path(ref qpath) => {
|
||||
if !self
|
||||
.typeck_results
|
||||
.typeck
|
||||
.qpath_res(qpath, e.hir_id)
|
||||
.opt_def_id()
|
||||
.is_some_and(DefId::is_local)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
|
||||
mir_is_empty(this.lcx, result)
|
||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
|
||||
mir_is_empty(self_.tcx, result)
|
||||
})
|
||||
},
|
||||
ExprKind::Lit(lit) => {
|
||||
|
|
@ -556,8 +553,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
},
|
||||
ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
|
||||
ExprKind::Repeat(..) => {
|
||||
if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() {
|
||||
Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0)
|
||||
if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() {
|
||||
Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0)
|
||||
} else {
|
||||
span_bug!(e.span, "typeck error");
|
||||
}
|
||||
|
|
@ -574,8 +571,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
Int(value) => {
|
||||
let value = !value;
|
||||
match *ty.kind() {
|
||||
ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
|
||||
ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
|
||||
ty::Int(ity) => Some(Int(unsext(self.tcx, value as i128, ity))),
|
||||
ty::Uint(ity) => Some(Int(clip(self.tcx, value, ity))),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
|
|
@ -590,7 +587,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
let ty::Int(ity) = *ty.kind() else { return None };
|
||||
let (min, _) = ity.min_max()?;
|
||||
// sign extend
|
||||
let value = sext(self.lcx.tcx, value, ity);
|
||||
let value = sext(self.tcx, value, ity);
|
||||
|
||||
// Applying unary - to the most negative value of any signed integer type panics.
|
||||
if value == min {
|
||||
|
|
@ -599,7 +596,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
|
||||
let value = value.checked_neg()?;
|
||||
// clear unused bits
|
||||
Some(Int(unsext(self.lcx.tcx, value, ity)))
|
||||
Some(Int(unsext(self.tcx, value, ity)))
|
||||
},
|
||||
F32(f) => Some(F32(-f)),
|
||||
F64(f) => Some(F64(-f)),
|
||||
|
|
@ -609,21 +606,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
|
||||
/// Create `Some(Vec![..])` of all constants, unless there is any
|
||||
/// non-constant part.
|
||||
fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
|
||||
fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
|
||||
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
|
||||
}
|
||||
|
||||
/// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
|
||||
fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
|
||||
fn fetch_path_and_apply<T, F>(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option<T>,
|
||||
F: FnOnce(&Self, mir::Const<'tcx>) -> Option<T>,
|
||||
{
|
||||
let res = self.typeck_results.qpath_res(qpath, id);
|
||||
let res = self.typeck.qpath_res(qpath, id);
|
||||
match res {
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
|
||||
// Check if this constant is based on `cfg!(..)`,
|
||||
// which is NOT constant for our purposes.
|
||||
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
|
||||
if let Some(node) = self.tcx.hir().get_if_local(def_id)
|
||||
&& let Node::Item(Item {
|
||||
kind: ItemKind::Const(.., body_id),
|
||||
..
|
||||
|
|
@ -632,20 +629,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
kind: ExprKind::Lit(_),
|
||||
span,
|
||||
..
|
||||
}) = self.lcx.tcx.hir_node(body_id.hir_id)
|
||||
}) = self.tcx.hir_node(body_id.hir_id)
|
||||
&& is_direct_expn_of(*span, "cfg").is_some()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let args = self.typeck_results.node_args(id);
|
||||
let args = if self.args.is_empty() {
|
||||
args
|
||||
} else {
|
||||
EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args)
|
||||
};
|
||||
let args = self.typeck.node_args(id);
|
||||
let result = self
|
||||
.lcx
|
||||
.tcx
|
||||
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
|
||||
.ok()
|
||||
|
|
@ -656,7 +647,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
let lhs = self.expr(lhs);
|
||||
let index = self.expr(index);
|
||||
|
||||
|
|
@ -685,8 +676,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A block can only yield a constant if it only has one constant expression.
|
||||
fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> {
|
||||
/// A block can only yield a constant if it has exactly one constant expression.
|
||||
fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> {
|
||||
if block.stmts.is_empty()
|
||||
&& let Some(expr) = block.expr
|
||||
{
|
||||
|
|
@ -696,7 +687,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
|
||||
&& let expr_lo = expr_span.lo()
|
||||
&& expr_lo >= span.lo
|
||||
&& let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx)
|
||||
&& let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx)
|
||||
&& let Some(src) = src.as_str()
|
||||
{
|
||||
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
|
||||
|
|
@ -705,11 +696,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
.filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
|
||||
.eq([OpenBrace])
|
||||
{
|
||||
self.source = ConstantSource::Constant;
|
||||
self.source.set(ConstantSource::Constant);
|
||||
}
|
||||
} else {
|
||||
// Unable to access the source. Assume a non-local dependency.
|
||||
self.source = ConstantSource::Constant;
|
||||
self.source.set(ConstantSource::Constant);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -719,7 +710,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
|
||||
fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
|
||||
if let Some(Constant::Bool(b)) = self.expr(cond) {
|
||||
if b {
|
||||
self.expr(then)
|
||||
|
|
@ -731,16 +722,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||
let l = self.expr(left)?;
|
||||
let r = self.expr(right);
|
||||
match (l, r) {
|
||||
(Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
|
||||
(Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck.expr_ty_opt(left)?.kind() {
|
||||
ty::Int(ity) => {
|
||||
let (ty_min_value, _) = ity.min_max()?;
|
||||
let bits = ity.bits();
|
||||
let l = sext(self.lcx.tcx, l, ity);
|
||||
let r = sext(self.lcx.tcx, r, ity);
|
||||
let l = sext(self.tcx, l, ity);
|
||||
let r = sext(self.tcx, r, ity);
|
||||
|
||||
// Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
|
||||
// the right-hand argument is -1 always panics, even with overflow-checks disabled
|
||||
|
|
@ -751,7 +742,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
|
||||
let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
|
||||
match op.node {
|
||||
// When +, * or binary - create a value greater than the maximum value, or less than
|
||||
// the minimum value that can be stored, it panics.
|
||||
|
|
@ -845,7 +836,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
|
||||
pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
|
||||
let mir::Const::Val(val, _) = result else {
|
||||
// We only work on evaluated consts.
|
||||
return None;
|
||||
|
|
@ -863,13 +854,13 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
|
|||
_ => None,
|
||||
},
|
||||
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
|
||||
let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
|
||||
let data = val.try_get_slice_bytes_for_diagnostics(tcx)?;
|
||||
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
|
||||
},
|
||||
(_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
|
||||
(ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
|
||||
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let len = len.try_to_target_usize(lcx.tcx)?;
|
||||
let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let len = len.try_to_target_usize(tcx)?;
|
||||
let ty::Float(flt) = sub_type.kind() else {
|
||||
return None;
|
||||
};
|
||||
|
|
@ -877,7 +868,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
|
|||
let mut res = Vec::new();
|
||||
for idx in 0..len {
|
||||
let range = alloc_range(offset + size * idx, size);
|
||||
let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?;
|
||||
let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?;
|
||||
res.push(match flt {
|
||||
FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)),
|
||||
FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)),
|
||||
|
|
@ -891,7 +882,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
|
|||
}
|
||||
}
|
||||
|
||||
fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
|
||||
fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
|
||||
let mir::Const::Val(val, _) = result else {
|
||||
// We only work on evaluated consts.
|
||||
return None;
|
||||
|
|
@ -902,26 +893,26 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
|
|||
if let ConstValue::Indirect { alloc_id, offset } = val {
|
||||
// Get the length from the slice, using the same formula as
|
||||
// [`ConstValue::try_get_slice_bytes_for_diagnostics`].
|
||||
let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let ptr_size = lcx.tcx.data_layout.pointer_size;
|
||||
let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let ptr_size = tcx.data_layout.pointer_size;
|
||||
if a.size() < offset + 2 * ptr_size {
|
||||
// (partially) dangling reference
|
||||
return None;
|
||||
}
|
||||
let len = a
|
||||
.read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false)
|
||||
.read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false)
|
||||
.ok()?
|
||||
.to_target_usize(&lcx.tcx)
|
||||
.to_target_usize(&tcx)
|
||||
.ok()?;
|
||||
Some(len == 0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0),
|
||||
ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0),
|
||||
_ => None,
|
||||
},
|
||||
(ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0),
|
||||
(ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0),
|
||||
(ConstValue::ZeroSized, _) => Some(true),
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -929,12 +920,12 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
|
|||
|
||||
fn field_of_struct<'tcx>(
|
||||
adt_def: ty::AdtDef<'tcx>,
|
||||
lcx: &LateContext<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
result: mir::Const<'tcx>,
|
||||
field: &Ident,
|
||||
) -> Option<mir::Const<'tcx>> {
|
||||
if let mir::Const::Val(result, ty) = result
|
||||
&& let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty)
|
||||
&& let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty)
|
||||
&& let Some(dc_variant) = dc.variant
|
||||
&& let Some(variant) = adt_def.variants().get(dc_variant)
|
||||
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
//! - or-fun-call
|
||||
//! - option-if-let-else
|
||||
|
||||
use crate::consts::{constant, FullInt};
|
||||
use crate::consts::{ConstEvalCtxt, FullInt};
|
||||
use crate::ty::{all_predicates_of, is_copy};
|
||||
use crate::visitors::is_const_evaluatable;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
|
@ -206,7 +206,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
},
|
||||
|
||||
// `-i32::MIN` panics with overflow checks
|
||||
ExprKind::Unary(UnOp::Neg, right) if constant(self.cx, self.cx.typeck_results(), right).is_none() => {
|
||||
ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => {
|
||||
self.eagerness |= NoChange;
|
||||
},
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
// Thus, we would realistically only delay the lint.
|
||||
ExprKind::Binary(op, _, right)
|
||||
if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr)
|
||||
&& constant(self.cx, self.cx.typeck_results(), right).is_none() =>
|
||||
&& ConstEvalCtxt::new(self.cx).eval(right).is_none() =>
|
||||
{
|
||||
self.eagerness |= NoChange;
|
||||
},
|
||||
|
|
@ -240,9 +240,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
ExprKind::Binary(op, left, right)
|
||||
if matches!(op.node, BinOpKind::Div | BinOpKind::Rem)
|
||||
&& let right_ty = self.cx.typeck_results().expr_ty(right)
|
||||
&& let left = constant(self.cx, self.cx.typeck_results(), left)
|
||||
&& let right = constant(self.cx, self.cx.typeck_results(), right)
|
||||
.and_then(|c| c.int_value(self.cx, right_ty))
|
||||
&& let ecx = ConstEvalCtxt::new(self.cx)
|
||||
&& let left = ecx.eval(left)
|
||||
&& let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty))
|
||||
&& matches!(
|
||||
(left, right),
|
||||
// `1 / x`: x might be zero
|
||||
|
|
@ -261,8 +261,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
ExprKind::Binary(op, left, right)
|
||||
if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul)
|
||||
&& !self.cx.typeck_results().expr_ty(e).is_floating_point()
|
||||
&& (constant(self.cx, self.cx.typeck_results(), left).is_none()
|
||||
|| constant(self.cx, self.cx.typeck_results(), right).is_none()) =>
|
||||
&& let ecx = ConstEvalCtxt::new(self.cx)
|
||||
&& (ecx.eval(left).is_none() || ecx.eval(right).is_none()) =>
|
||||
{
|
||||
self.eagerness |= NoChange;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#![deny(clippy::missing_docs_in_private_items)]
|
||||
|
||||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::consts::{ConstEvalCtxt, Constant};
|
||||
use crate::ty::is_type_diagnostic_item;
|
||||
use crate::{is_expn_of, match_def_path, paths};
|
||||
|
||||
|
|
@ -471,7 +471,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
|
|||
return Some(VecInitKind::Default);
|
||||
} else if name.ident.name.as_str() == "with_capacity" {
|
||||
let arg = args.first()?;
|
||||
return match constant_simple(cx, cx.typeck_results(), arg) {
|
||||
return match ConstEvalCtxt::new(cx).eval_simple(arg) {
|
||||
Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
|
||||
_ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::consts::constant_simple;
|
||||
use crate::consts::ConstEvalCtxt;
|
||||
use crate::macros::macro_backtrace;
|
||||
use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt};
|
||||
use crate::tokenize_with_text;
|
||||
|
|
@ -255,8 +255,8 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
|
||||
&& typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
|
||||
&& let (Some(l), Some(r)) = (
|
||||
constant_simple(self.inner.cx, typeck_lhs, left),
|
||||
constant_simple(self.inner.cx, typeck_rhs, right),
|
||||
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left),
|
||||
ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right),
|
||||
)
|
||||
&& l == r
|
||||
{
|
||||
|
|
@ -714,9 +714,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn hash_expr(&mut self, e: &Expr<'_>) {
|
||||
let simple_const = self
|
||||
.maybe_typeck_results
|
||||
.and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
|
||||
let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
|
||||
ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e)
|
||||
});
|
||||
|
||||
// const hashing may result in the same hash as some unrelated node, so add a sort of
|
||||
// discriminant depending on which path we're choosing next
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ use rustc_span::{sym, Span};
|
|||
use rustc_target::abi::Integer;
|
||||
use visitors::Visitable;
|
||||
|
||||
use crate::consts::{constant, mir_to_const, Constant};
|
||||
use crate::consts::{mir_to_const, ConstEvalCtxt, Constant};
|
||||
use crate::higher::Range;
|
||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||
use crate::visitors::for_each_expr_without_closures;
|
||||
|
|
@ -1581,8 +1581,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
|
|||
if let rustc_ty::Adt(_, subst) = ty.kind()
|
||||
&& let bnd_ty = subst.type_at(0)
|
||||
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
|
||||
&& let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, cx.tcx))
|
||||
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
|
||||
&& let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx))
|
||||
&& let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
|
||||
{
|
||||
start_const == min_const
|
||||
} else {
|
||||
|
|
@ -1594,8 +1594,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
|
|||
if let rustc_ty::Adt(_, subst) = ty.kind()
|
||||
&& let bnd_ty = subst.type_at(0)
|
||||
&& let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
|
||||
&& let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, cx.tcx))
|
||||
&& let Some(end_const) = constant(cx, cx.typeck_results(), end)
|
||||
&& let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx))
|
||||
&& let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
|
||||
{
|
||||
end_const == max_const
|
||||
} else {
|
||||
|
|
@ -1626,7 +1626,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
|
|||
return true;
|
||||
}
|
||||
let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
|
||||
if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
|
||||
if let Some(Constant::Int(v)) =
|
||||
ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e)
|
||||
{
|
||||
return value == v;
|
||||
}
|
||||
false
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ use rustc_ast::{LitKind, StrStyle};
|
|||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_lint::{EarlyContext, LateContext};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::{original_sp, SourceMap};
|
||||
use rustc_span::{
|
||||
|
|
@ -17,6 +18,30 @@ use std::borrow::Cow;
|
|||
use std::fmt;
|
||||
use std::ops::Range;
|
||||
|
||||
pub trait HasSession {
|
||||
fn sess(&self) -> &Session;
|
||||
}
|
||||
impl HasSession for Session {
|
||||
fn sess(&self) -> &Session {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl HasSession for TyCtxt<'_> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
}
|
||||
impl HasSession for EarlyContext<'_> {
|
||||
fn sess(&self) -> &Session {
|
||||
::rustc_lint::LintContext::sess(self)
|
||||
}
|
||||
}
|
||||
impl HasSession for LateContext<'_> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.tcx.sess()
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion of a value into the range portion of a `Span`.
|
||||
pub trait SpanRange: Sized {
|
||||
fn into_range(self) -> Range<BytePos>;
|
||||
|
|
@ -71,19 +96,19 @@ impl IntoSpan for Range<BytePos> {
|
|||
pub trait SpanRangeExt: SpanRange {
|
||||
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span
|
||||
/// extends through multiple files, or is malformed.
|
||||
fn get_source_text(self, cx: &impl LintContext) -> Option<SourceFileRange> {
|
||||
fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> {
|
||||
get_source_text(cx.sess().source_map(), self.into_range())
|
||||
}
|
||||
|
||||
/// Calls the given function with the source text referenced and returns the value. Returns
|
||||
/// `None` if the source text cannot be retrieved.
|
||||
fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
|
||||
fn with_source_text<T>(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
|
||||
with_source_text(cx.sess().source_map(), self.into_range(), f)
|
||||
}
|
||||
|
||||
/// Checks if the referenced source text satisfies the given predicate. Returns `false` if the
|
||||
/// source text cannot be retrieved.
|
||||
fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
|
||||
fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
|
||||
self.with_source_text(cx, pred).unwrap_or(false)
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +116,7 @@ pub trait SpanRangeExt: SpanRange {
|
|||
/// and returns the value. Returns `None` if the source text cannot be retrieved.
|
||||
fn with_source_text_and_range<T>(
|
||||
self,
|
||||
cx: &impl LintContext,
|
||||
cx: &impl HasSession,
|
||||
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
|
||||
) -> Option<T> {
|
||||
with_source_text_and_range(cx.sess().source_map(), self.into_range(), f)
|
||||
|
|
@ -104,30 +129,30 @@ pub trait SpanRangeExt: SpanRange {
|
|||
/// The new range must reside within the same source file.
|
||||
fn map_range(
|
||||
self,
|
||||
cx: &impl LintContext,
|
||||
cx: &impl HasSession,
|
||||
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
|
||||
) -> Option<Range<BytePos>> {
|
||||
map_range(cx.sess().source_map(), self.into_range(), f)
|
||||
}
|
||||
|
||||
/// Extends the range to include all preceding whitespace characters.
|
||||
fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> {
|
||||
fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
|
||||
with_leading_whitespace(cx.sess().source_map(), self.into_range())
|
||||
}
|
||||
|
||||
/// Trims the leading whitespace from the range.
|
||||
fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> {
|
||||
fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
|
||||
trim_start(cx.sess().source_map(), self.into_range())
|
||||
}
|
||||
|
||||
/// Writes the referenced source text to the given writer. Will return `Err` if the source text
|
||||
/// could not be retrieved.
|
||||
fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result {
|
||||
fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
|
||||
write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
|
||||
}
|
||||
|
||||
/// Extracts the referenced source text as an owned string.
|
||||
fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> {
|
||||
fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
|
||||
self.with_source_text(cx, ToOwned::to_owned)
|
||||
}
|
||||
}
|
||||
|
|
@ -227,15 +252,15 @@ impl SourceFileRange {
|
|||
}
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||
pub fn expr_block<T: LintContext>(
|
||||
cx: &T,
|
||||
pub fn expr_block(
|
||||
sess: &impl HasSession,
|
||||
expr: &Expr<'_>,
|
||||
outer: SyntaxContext,
|
||||
default: &str,
|
||||
indent_relative_to: Option<Span>,
|
||||
app: &mut Applicability,
|
||||
) -> String {
|
||||
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
|
||||
let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app);
|
||||
if !from_macro
|
||||
&& let ExprKind::Block(block, _) = expr.kind
|
||||
&& block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
|
|
@ -260,13 +285,13 @@ pub fn expr_block<T: LintContext>(
|
|||
/// let x = ();
|
||||
/// // ^^^^^^^^^^
|
||||
/// ```
|
||||
pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
|
||||
first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
|
||||
pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span {
|
||||
first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
|
||||
}
|
||||
|
||||
fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
|
||||
let line_span = line_span(cx, span);
|
||||
snippet_opt(cx, line_span).and_then(|snip| {
|
||||
fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option<BytePos> {
|
||||
let line_span = line_span(sess, span);
|
||||
snippet_opt(sess, line_span).and_then(|snip| {
|
||||
snip.find(|c: char| !c.is_whitespace())
|
||||
.map(|pos| line_span.lo() + BytePos::from_usize(pos))
|
||||
})
|
||||
|
|
@ -281,9 +306,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
|
|||
/// let x = ();
|
||||
/// // ^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
|
||||
fn line_span(sess: &impl HasSession, span: Span) -> Span {
|
||||
let span = original_sp(span, DUMMY_SP);
|
||||
let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap();
|
||||
let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap();
|
||||
let line_start = sf.lines()[line];
|
||||
let line_start = sf.absolute_position(line_start);
|
||||
span.with_lo(line_start)
|
||||
|
|
@ -297,13 +322,13 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
|
|||
/// let x = ();
|
||||
/// // ^^ -- will return 4
|
||||
/// ```
|
||||
pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
|
||||
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
|
||||
pub fn indent_of(sess: &impl HasSession, span: Span) -> Option<usize> {
|
||||
snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
|
||||
}
|
||||
|
||||
/// Gets a snippet of the indentation of the line of a span
|
||||
pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
snippet_opt(cx, line_span(cx, span)).map(|mut s| {
|
||||
pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> {
|
||||
snippet_opt(sess, line_span(sess, span)).map(|mut s| {
|
||||
let len = s.len() - s.trim_start().len();
|
||||
s.truncate(len);
|
||||
s
|
||||
|
|
@ -315,8 +340,8 @@ pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
|||
// sources that the user has no control over.
|
||||
// For some reason these attributes don't have any expansion info on them, so
|
||||
// we have to check it this way until there is a better way.
|
||||
pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
|
||||
if let Some(snippet) = snippet_opt(cx, span) {
|
||||
pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
|
||||
if let Some(snippet) = snippet_opt(sess, span) {
|
||||
if snippet.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -407,8 +432,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
|
|||
/// snippet(cx, span1, "..") // -> "value"
|
||||
/// snippet(cx, span2, "..") // -> "Vec::new()"
|
||||
/// ```
|
||||
pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
|
||||
pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from)
|
||||
}
|
||||
|
||||
/// Same as [`snippet`], but it adapts the applicability level by following rules:
|
||||
|
|
@ -417,13 +442,13 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<
|
|||
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
|
||||
/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
|
||||
/// `HasPlaceholders`
|
||||
pub fn snippet_with_applicability<'a, T: LintContext>(
|
||||
cx: &T,
|
||||
pub fn snippet_with_applicability<'a>(
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
snippet_with_applicability_sess(cx.sess(), span, default, applicability)
|
||||
snippet_with_applicability_sess(sess.sess(), span, default, applicability)
|
||||
}
|
||||
|
||||
fn snippet_with_applicability_sess<'a>(
|
||||
|
|
@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>(
|
|||
if *applicability != Applicability::Unspecified && span.from_expansion() {
|
||||
*applicability = Applicability::MaybeIncorrect;
|
||||
}
|
||||
snippet_opt_sess(sess, span).map_or_else(
|
||||
snippet_opt(sess, span).map_or_else(
|
||||
|| {
|
||||
if *applicability == Applicability::MachineApplicable {
|
||||
*applicability = Applicability::HasPlaceholders;
|
||||
|
|
@ -447,12 +472,8 @@ fn snippet_with_applicability_sess<'a>(
|
|||
}
|
||||
|
||||
/// Converts a span to a code snippet. Returns `None` if not available.
|
||||
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
|
||||
snippet_opt_sess(cx.sess(), span)
|
||||
}
|
||||
|
||||
fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
|
||||
sess.source_map().span_to_snippet(span).ok()
|
||||
pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> {
|
||||
sess.sess().source_map().span_to_snippet(span).ok()
|
||||
}
|
||||
|
||||
/// Converts a span (from a block) to a code snippet if available, otherwise use default.
|
||||
|
|
@ -489,41 +510,41 @@ fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
|
|||
/// } // aligned with `if`
|
||||
/// ```
|
||||
/// Note that the first line of the snippet always has 0 indentation.
|
||||
pub fn snippet_block<'a, T: LintContext>(
|
||||
cx: &T,
|
||||
pub fn snippet_block<'a>(
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
) -> Cow<'a, str> {
|
||||
let snip = snippet(cx, span, default);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
|
||||
let snip = snippet(sess, span, default);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
reindent_multiline(snip, true, indent)
|
||||
}
|
||||
|
||||
/// Same as `snippet_block`, but adapts the applicability level by the rules of
|
||||
/// `snippet_with_applicability`.
|
||||
pub fn snippet_block_with_applicability<'a>(
|
||||
cx: &impl LintContext,
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
let snip = snippet_with_applicability(cx, span, default, applicability);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
|
||||
let snip = snippet_with_applicability(sess, span, default, applicability);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
reindent_multiline(snip, true, indent)
|
||||
}
|
||||
|
||||
pub fn snippet_block_with_context<'a>(
|
||||
cx: &impl LintContext,
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
app: &mut Applicability,
|
||||
) -> (Cow<'a, str>, bool) {
|
||||
let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
|
||||
let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
|
||||
(reindent_multiline(snip, true, indent), from_macro)
|
||||
}
|
||||
|
||||
|
|
@ -537,13 +558,13 @@ pub fn snippet_block_with_context<'a>(
|
|||
///
|
||||
/// This will also return whether or not the snippet is a macro call.
|
||||
pub fn snippet_with_context<'a>(
|
||||
cx: &impl LintContext,
|
||||
sess: &impl HasSession,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> (Cow<'a, str>, bool) {
|
||||
snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
|
||||
snippet_with_context_sess(sess.sess(), span, outer, default, applicability)
|
||||
}
|
||||
|
||||
fn snippet_with_context_sess<'a>(
|
||||
|
|
@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
|
|||
/// writeln!(o, "") -> writeln!(o, "")
|
||||
/// ^^ ^^^^
|
||||
/// ```
|
||||
pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
|
||||
let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true);
|
||||
pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span {
|
||||
let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true);
|
||||
extended.with_lo(extended.lo() - BytePos(1))
|
||||
}
|
||||
|
||||
/// Converts `expr` to a `char` literal if it's a `str` literal containing a single
|
||||
/// character (or a single byte with `ascii_only`)
|
||||
pub fn str_literal_to_char_literal(
|
||||
cx: &LateContext<'_>,
|
||||
sess: &impl HasSession,
|
||||
expr: &Expr<'_>,
|
||||
applicability: &mut Applicability,
|
||||
ascii_only: bool,
|
||||
|
|
@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal(
|
|||
}
|
||||
&& len == 1
|
||||
{
|
||||
let snip = snippet_with_applicability(cx, expr.span, string, applicability);
|
||||
let snip = snippet_with_applicability(sess, expr.span, string, applicability);
|
||||
let ch = if let StrStyle::Raw(nhash) = style {
|
||||
let nhash = nhash as usize;
|
||||
// for raw string: r##"a"##
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue