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:
bors 2024-08-08 14:42:42 +00:00
commit b1e87922c1
54 changed files with 395 additions and 386 deletions

View file

@ -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)

View file

@ -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;
},

View file

@ -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)),
};

View file

@ -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

View file

@ -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

View file

@ -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"##