Remove QPath::LangItem from ranges

This commit is contained in:
Cameron Steffen
2025-08-17 07:43:01 -05:00
parent 9c98533491
commit 7cbff63411
33 changed files with 158 additions and 218 deletions

View File

@@ -1484,7 +1484,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
let e1 = self.lower_expr_mut(e1);
let e2 = self.lower_expr_mut(e2);
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span);
let fn_path = self.make_lang_item_qpath(hir::LangItem::RangeInclusiveNew, span, None);
let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path)));
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
}
@@ -1570,7 +1570,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::ExprKind::Struct(
self.arena.alloc(hir::QPath::LangItem(lang_item, span)),
self.arena.alloc(self.make_lang_item_qpath(lang_item, span, None)),
fields,
hir::StructTailExpr::None,
)

View File

@@ -23,7 +23,9 @@ use rustc_index::IndexVec;
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Spanned;
use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use rustc_span::{
BytePos, DUMMY_SP, DesugaringKind, ErrorGuaranteed, Ident, Span, Symbol, kw, sym,
};
use rustc_target::asm::InlineAsmRegOrRegClass;
use smallvec::SmallVec;
use thin_vec::ThinVec;
@@ -2600,102 +2602,21 @@ impl Expr<'_> {
) => path1.res == path2.res,
(
ExprKind::Struct(
QPath::LangItem(LangItem::RangeTo, _),
[val1],
&QPath::Resolved(None, &Path { res: Res::Def(_, path1_def_id), .. }),
args1,
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeTo, _),
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusive, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusive, _),
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusiveCopy, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeToInclusiveCopy, _),
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFrom, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFrom, _),
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFromCopy, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFromCopy, _),
[val2],
StructTailExpr::None,
),
) => val1.expr.equivalent_for_indexing(val2.expr),
(
ExprKind::Struct(
QPath::LangItem(LangItem::Range, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::Range, _),
[val2, val4],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeCopy, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeCopy, _),
[val2, val4],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeInclusiveCopy, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeInclusiveCopy, _),
[val2, val4],
&QPath::Resolved(None, &Path { res: Res::Def(_, path2_def_id), .. }),
args2,
StructTailExpr::None,
),
) => {
val1.expr.equivalent_for_indexing(val2.expr)
&& val3.expr.equivalent_for_indexing(val4.expr)
path2_def_id == path1_def_id
&& is_range_literal(self)
&& is_range_literal(other)
&& std::iter::zip(args1, args2)
.all(|(a, b)| a.expr.equivalent_for_indexing(b.expr))
}
_ => false,
}
@@ -2713,30 +2634,29 @@ impl Expr<'_> {
/// Checks if the specified expression is a built-in range literal.
/// (See: `LoweringContext::lower_expr()`).
pub fn is_range_literal(expr: &Expr<'_>) -> bool {
match expr.kind {
// All built-in range literals but `..=` and `..` desugar to `Struct`s.
ExprKind::Struct(ref qpath, _, _) => matches!(
**qpath,
QPath::LangItem(
LangItem::Range
| LangItem::RangeTo
| LangItem::RangeFrom
| LangItem::RangeFull
| LangItem::RangeToInclusive
| LangItem::RangeCopy
| LangItem::RangeFromCopy
| LangItem::RangeInclusiveCopy
| LangItem::RangeToInclusiveCopy,
..
)
),
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
ExprKind::Call(ref func, _) => {
matches!(func.kind, ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)))
}
_ => false,
if let ExprKind::Struct(QPath::Resolved(None, path), _, StructTailExpr::None) = expr.kind
&& let [.., segment] = path.segments
&& let sym::RangeFrom
| sym::RangeFull
| sym::Range
| sym::RangeToInclusive
| sym::RangeTo
| sym::RangeFromCopy
| sym::RangeCopy
| sym::RangeInclusiveCopy
| sym::RangeToInclusiveCopy = segment.ident.name
&& expr.span.is_desugaring(DesugaringKind::RangeExpr)
{
true
} else if let ExprKind::Call(func, _) = &expr.kind
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
&& let [.., segment] = path.segments
&& let sym::range_inclusive_new = segment.ident.name
&& expr.span.is_desugaring(DesugaringKind::RangeExpr)
{
true
} else {
false
}
}

View File

@@ -19,7 +19,7 @@ use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, HirId, QPath, find_attr};
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
use rustc_hir_analysis::NoVariantNamed;
use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _};
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin};
@@ -2506,9 +2506,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
args: GenericArgsRef<'tcx>,
mut err: Diag<'_>,
) {
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) =
last_expr_field.expr.kind
if is_range_literal(last_expr_field.expr)
&& let ExprKind::Struct(&qpath, [range_start, range_end], _) = last_expr_field.expr.kind
&& self.tcx.qpath_is_lang_item(qpath, LangItem::Range)
&& let variant_field =
variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident)
&& let range_def_id = self.tcx.lang_items().range_struct()

View File

@@ -1032,8 +1032,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// inherent impl, we need to record the
// `T` for posterity (see `UserSelfTy` for
// details).
let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw;
user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty });
// Generated desugaring code may have a path without a self.
user_self_ty = self_ty.map(|self_ty| UserSelfTy {
impl_def_id: container_id,
self_ty: self_ty.raw,
});
}
}
}

View File

@@ -8,7 +8,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, lis
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath, is_range_literal};
use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants};
use rustc_index::IndexVec;
@@ -2052,8 +2052,9 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> {
fn detect_dotdot(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'tcx>) {
if let ty::Adt(adt, _) = ty.kind()
&& self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull)
&& let hir::ExprKind::Struct(hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], _) =
expr.kind
&& is_range_literal(expr)
&& let hir::ExprKind::Struct(&path, [], _) = expr.kind
&& self.tcx().qpath_is_lang_item(path, hir::LangItem::RangeFull)
{
// We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax
// from default field values, which is not supported on tuples.

View File

@@ -11,7 +11,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{
self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind,
GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind,
WherePredicateKind, expr_needs_parens,
WherePredicateKind, expr_needs_parens, is_range_literal,
};
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_hir_analysis::suggest_impl_trait;
@@ -1664,7 +1664,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return false;
}
match expr.kind {
ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
ExprKind::Struct(&qpath, [start, end], _)
if is_range_literal(expr)
&& self.tcx.qpath_is_lang_item(qpath, LangItem::Range) =>
{
err.span_suggestion_verbose(
start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()),
"remove the unnecessary `.` operator for a floating point literal",
@@ -1673,24 +1676,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
true
}
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
ExprKind::Struct(&qpath, [arg], _)
if is_range_literal(expr)
&& let Some(qpath @ (LangItem::RangeFrom | LangItem::RangeTo)) =
self.tcx.qpath_lang_item(qpath) =>
{
let range_span = expr.span.parent_callsite().unwrap();
err.span_suggestion_verbose(
range_span.with_lo(start.expr.span.hi()),
"remove the unnecessary `.` operator for a floating point literal",
'.',
Applicability::MaybeIncorrect,
);
true
}
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
let range_span = expr.span.parent_callsite().unwrap();
err.span_suggestion_verbose(
range_span.until(end.expr.span),
"remove the unnecessary `.` operator and add an integer part for a floating point literal",
"0.",
Applicability::MaybeIncorrect,
);
match qpath {
LangItem::RangeFrom => {
err.span_suggestion_verbose(
range_span.with_lo(arg.expr.span.hi()),
"remove the unnecessary `.` operator for a floating point literal",
'.',
Applicability::MaybeIncorrect,
);
}
_ => {
err.span_suggestion_verbose(
range_span.until(arg.expr.span),
"remove the unnecessary `.` operator and add an integer part for a floating point literal",
"0.",
Applicability::MaybeIncorrect,
);
}
}
true
}
ExprKind::Lit(Spanned {
@@ -3497,11 +3506,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !hir::is_range_literal(expr) {
return;
}
let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
expr.kind
else {
let hir::ExprKind::Struct(&qpath, [start, end], _) = expr.kind else {
return;
};
if !self.tcx.qpath_is_lang_item(qpath, LangItem::Range) {
return;
}
if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) {
// Ignore `Foo { field: a..Default::default() }`
return;

View File

@@ -19,7 +19,9 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{self as hir, ExprKind, HirId, Node, PathSegment, QPath, find_attr};
use rustc_hir::{
self as hir, ExprKind, HirId, Node, PathSegment, QPath, find_attr, is_range_literal,
};
use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin};
use rustc_middle::bug;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type};
@@ -2412,22 +2414,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let SelfSource::MethodCall(expr) = source {
for (_, parent) in tcx.hir_parent_iter(expr.hir_id).take(5) {
if let Node::Expr(parent_expr) = parent {
if !is_range_literal(parent_expr) {
continue;
}
let lang_item = match parent_expr.kind {
ExprKind::Struct(qpath, _, _) => match *qpath {
QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range),
QPath::LangItem(LangItem::RangeCopy, ..) => Some(LangItem::RangeCopy),
QPath::LangItem(LangItem::RangeInclusiveCopy, ..) => {
Some(LangItem::RangeInclusiveCopy)
}
QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo),
QPath::LangItem(LangItem::RangeToInclusive, ..) => {
Some(LangItem::RangeToInclusive)
}
ExprKind::Struct(qpath, _, _) => match tcx.qpath_lang_item(*qpath) {
Some(
lang_item @ (LangItem::Range
| LangItem::RangeCopy
| LangItem::RangeInclusiveCopy
| LangItem::RangeTo
| LangItem::RangeToInclusive),
) => Some(lang_item),
_ => None,
},
ExprKind::Call(func, _) => match func.kind {
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => {
ExprKind::Path(qpath)
if tcx.qpath_is_lang_item(qpath, LangItem::RangeInclusiveNew) =>
{
Some(LangItem::RangeInclusiveStruct)
}
_ => None,

View File

@@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
let note = "the suggestion might not be applicable in constant blocks";
let ty = cx.typeck_results().expr_ty(array).peel_refs();
let allowed_in_tests = self.allow_indexing_slicing_in_tests && is_in_test(cx.tcx, expr.hir_id);
if let Some(range) = higher::Range::hir(index) {
if let Some(range) = higher::Range::hir(cx, index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.kind() {
let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) {

View File

@@ -178,7 +178,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
Finite
}
},
ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(),
ExprKind::Struct(..) => higher::Range::hir(cx, expr).is_some_and(|r| r.end.is_none()).into(),
_ => Finite,
}
}

View File

@@ -131,7 +131,7 @@ fn check_index_usage<'tcx>(
fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
match node {
Node::Expr(expr) if higher::Range::hir(expr).is_some() => {},
Node::Expr(expr) if higher::Range::hir(cx, expr).is_some() => {},
Node::ExprField(_) => {},
Node::Expr(expr) => return Some(expr),
_ => break,

View File

@@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(
end: Some(end),
limits,
span: _,
}) = higher::Range::hir(arg)
}) = higher::Range::hir(cx, arg)
// the var must be a single name
&& let PatKind::Binding(_, canonical_id, _, _) = pat.kind
{

View File

@@ -32,7 +32,7 @@ pub(super) fn check<'tcx>(
end: Some(end),
limits: RangeLimits::HalfOpen,
span: _,
}) = higher::Range::hir(arg)
}) = higher::Range::hir(cx, arg)
&& let ExprKind::Lit(Spanned {
node: LitKind::Int(Pu128(0), _),
..

View File

@@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
start: Some(start),
end: Some(end),
..
}) = higher::Range::hir(arg)
}) = higher::Range::hir(cx, arg)
&& let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end))
&& (mut_id_start.is_some() || mut_id_end.is_some())
{

View File

@@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(
ref end,
limits,
span,
}) = higher::Range::hir(arg)
}) = higher::Range::hir(cx, arg)
// the var must be a single name
&& let PatKind::Binding(_, canonical_id, ident, _) = pat.kind
{

View File

@@ -90,7 +90,7 @@ pub(super) fn check<'tcx>(
arg_snip = format!("({arg_snip})").into();
}
if clippy_utils::higher::Range::hir(arg_expression).is_some() {
if clippy_utils::higher::Range::hir(cx, arg_expression).is_some() {
let range_expr = snippet(cx, arg_expression.span, "?").to_string();
let sugg = snippet(cx, arg_expression.span, "..");

View File

@@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
end: Some(end),
limits: RangeLimits::Closed,
span: _,
}) = higher::Range::hir(receiver)
}) = higher::Range::hir(cx, receiver)
&& !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_))
{
let arg = peel_ref_operators(cx, arg);

View File

@@ -207,7 +207,7 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
ExprKind::Index(arr, range, _) => match arr.kind {
ExprKind::Array([]) => is_range_literal(range),
ExprKind::Array(_) => {
let Some(range) = clippy_utils::higher::Range::hir(range) else {
let Some(range) = clippy_utils::higher::Range::hir(cx, range) else {
return false;
};
range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0))

View File

@@ -237,7 +237,7 @@ fn find_stripping<'tcx>(
if is_ref_str(self.cx, ex)
&& let unref = peel_ref(ex)
&& let ExprKind::Index(indexed, index, _) = &unref.kind
&& let Some(higher::Range { start, end, .. }) = higher::Range::hir(index)
&& let Some(higher::Range { start, end, .. }) = higher::Range::hir(self.cx, index)
&& let ExprKind::Path(path) = &indexed.kind
&& self.cx.qpath_res(path, ex.hir_id) == self.target
{

View File

@@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
end: None,
limits: ast::RangeLimits::HalfOpen,
span: _,
}) = higher::Range::hir(index_expr)
}) = higher::Range::hir(cx, index_expr)
&& let hir::ExprKind::Lit(start_lit) = &start_expr.kind
&& let ast::LitKind::Int(start_idx, _) = start_lit.node
{

View File

@@ -66,7 +66,7 @@ pub(super) fn check(
method_name_span: Span,
) {
let mut applicability = Applicability::MaybeIncorrect;
if let Some(range) = higher::Range::hir(receiver)
if let Some(range) = higher::Range::hir(cx, receiver)
&& let ExprKind::Closure(Closure { body, .. }) = arg.kind
&& let body_hir = cx.tcx.hir_body(*body)
&& let Body {

View File

@@ -11,7 +11,7 @@ use super::RANGE_ZIP_WITH_LEN;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator)
// range expression in `.zip()` call: `0..x.len()`
&& let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg)
&& let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(cx, zip_arg)
&& is_integer_const(cx, start, 0)
// `.len()` call
&& let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind

View File

@@ -220,14 +220,14 @@ impl<'hir> IndexEntry<'hir> {
///
/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
/// for `..=5` this returns `Some(5)`
fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
fn upper_index_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<usize> {
if let ExprKind::Lit(lit) = &expr.kind
&& let LitKind::Int(Pu128(index), _) = lit.node
{
Some(index as usize)
} else if let Some(higher::Range {
end: Some(end), limits, ..
}) = higher::Range::hir(expr)
}) = higher::Range::hir(cx, expr)
&& let ExprKind::Lit(lit) = &end.kind
&& let LitKind::Int(Pu128(index @ 1..), _) = lit.node
{
@@ -244,7 +244,7 @@ fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
if let ExprKind::Index(slice, index_lit, _) = expr.kind
&& cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice()
&& let Some(index) = upper_index_expr(index_lit)
&& let Some(index) = upper_index_expr(cx, index_lit)
{
let hash = hash_expr(cx, slice);

View File

@@ -74,7 +74,7 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) {
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(cx, expr) {
if let Some(start) = start {
check_for_parens(cx, start, true);
}

View File

@@ -503,7 +503,7 @@ fn check_range_switch<'tcx>(
msg: &'static str,
operator: &str,
) {
if let Some(range) = higher::Range::hir(expr)
if let Some(range) = higher::Range::hir(cx, expr)
&& let higher::Range {
start,
end: Some(end),
@@ -571,7 +571,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
end: Some(end),
limits,
span,
}) = higher::Range::hir(expr)
}) = higher::Range::hir(cx, expr)
&& let ty = cx.typeck_results().expr_ty(start)
&& let ty::Int(_) | ty::Uint(_) = ty.kind()
&& let ecx = ConstEvalCtxt::new(cx)

View File

@@ -6,7 +6,7 @@ use clippy_utils::ty::implements_trait;
use clippy_utils::{is_no_std_crate, sym};
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use std::fmt::{self, Display, Formatter};
@@ -86,12 +86,12 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
return;
};
let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind
let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind
else {
return;
};
if matches!(lang_item, LangItem::Range)
if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range)
&& let ty = cx.typeck_results().expr_ty(start.expr)
&& let Some(snippet) = span.get_source_text(cx)
// `is_from_proc_macro` will skip any `vec![]`. Let's not!

View File

@@ -6,7 +6,7 @@ use clippy_utils::{
};
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath};
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
@@ -266,7 +266,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
&& expressions[0].1.is_empty()
// Check for slicer
&& let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind
&& let ExprKind::Struct(&qpath, _, _) = right.kind
&& cx.tcx.qpath_is_lang_item(qpath, LangItem::Range)
{
let mut applicability = Applicability::MachineApplicable;
let string_expression = &expressions[0].0;

View File

@@ -215,14 +215,12 @@ pub struct Range<'a> {
impl<'a> Range<'a> {
/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
#[expect(clippy::similar_names)]
pub fn hir(expr: &'a Expr<'_>) -> Option<Range<'a>> {
pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<Range<'a>> {
let span = expr.range_span()?;
match expr.kind {
ExprKind::Call(path, [arg1, arg2])
if matches!(
path.kind,
ExprKind::Path(QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..))
) =>
if let ExprKind::Path(qpath) = path.kind
&& cx.tcx.qpath_is_lang_item(qpath, hir::LangItem::RangeInclusiveNew) =>
{
Some(Range {
start: Some(arg1),
@@ -231,14 +229,14 @@ impl<'a> Range<'a> {
span,
})
},
ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
(QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
ExprKind::Struct(&qpath, fields, StructTailExpr::None) => match (cx.tcx.qpath_lang_item(qpath)?, fields) {
(hir::LangItem::RangeFull, []) => Some(Range {
start: None,
end: None,
limits: ast::RangeLimits::HalfOpen,
span,
}),
(QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => {
(hir::LangItem::RangeFrom, [field]) if field.ident.name == sym::start => {
Some(Range {
start: Some(field.expr),
end: None,
@@ -246,7 +244,7 @@ impl<'a> Range<'a> {
span,
})
},
(QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => {
(hir::LangItem::Range, [field1, field2]) => {
let (start, end) = match (field1.ident.name, field2.ident.name) {
(sym::start, sym::end) => (field1.expr, field2.expr),
(sym::end, sym::start) => (field2.expr, field1.expr),
@@ -259,7 +257,7 @@ impl<'a> Range<'a> {
span,
})
},
(QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => {
(hir::LangItem::RangeToInclusive, [field]) if field.ident.name == sym::end => {
Some(Range {
start: None,
end: Some(field.expr),
@@ -267,7 +265,7 @@ impl<'a> Range<'a> {
span,
})
},
(QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range {
(hir::LangItem::RangeTo, [field]) if field.ident.name == sym::end => Some(Range {
start: None,
end: Some(field.expr),
limits: ast::RangeLimits::HalfOpen,

View File

@@ -1282,7 +1282,7 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
/// If the given `Expr` is not some kind of range, the function returns `false`.
pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
if let Some(Range { start, end, limits, .. }) = Range::hir(expr) {
if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) {
let start_is_none_or_min = start.is_none_or(|start| {
if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0)

View File

@@ -59,7 +59,7 @@ impl<'a> Sugg<'a> {
pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
let ctxt = expr.span.ctxt();
let get_snippet = |span| snippet_with_context(cx, span, ctxt, "", &mut Applicability::Unspecified).0;
snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet))
snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(cx, expr, get_snippet))
}
/// Convenience function around `hir_opt` for suggestions with a default
@@ -115,7 +115,7 @@ impl<'a> Sugg<'a> {
Box::new(Self::hir_with_context(cx, inner, ctxt, default, applicability)),
)
} else {
Self::hir_from_snippet(expr, |span| {
Self::hir_from_snippet(cx, expr, |span| {
snippet_with_context(cx, span, ctxt, default, applicability).0
})
}
@@ -127,8 +127,8 @@ impl<'a> Sugg<'a> {
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
/// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self {
if let Some(range) = higher::Range::hir(expr) {
fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self {
if let Some(range) = higher::Range::hir(cx, expr) {
let op = AssocOp::Range(range.limits);
let start = range.start.map_or("".into(), |expr| get_snippet(expr.span));
let end = range.end.map_or("".into(), |expr| get_snippet(expr.span));
@@ -166,7 +166,7 @@ impl<'a> Sugg<'a> {
| ExprKind::Use(..)
| ExprKind::Err(_)
| ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)),
ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet),
ExprKind::DropTemps(inner) => Self::hir_from_snippet(cx, inner, get_snippet),
ExprKind::Assign(lhs, rhs, _) => {
Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
},

View File

@@ -2,7 +2,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind
&& name.as_str() == "y"
&& let ExprKind::Struct(qpath, fields, None) = arg.kind
&& matches!(qpath, QPath::LangItem(LangItem::Range, _))
&& let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
&& paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed
&& fields.len() == 2
&& fields[0].ident.as_str() == "start"
&& let ExprKind::Lit(ref lit) = fields[0].expr.kind
@@ -23,7 +24,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
&& let PatKind::Wild = pat.kind
&& let ExprKind::Struct(qpath, fields, None) = arg.kind
&& matches!(qpath, QPath::LangItem(LangItem::Range, _))
&& let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
&& paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed
&& fields.len() == 2
&& fields[0].ident.as_str() == "start"
&& let ExprKind::Lit(ref lit) = fields[0].expr.kind
@@ -43,7 +45,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr)
&& let PatKind::Wild = pat.kind
&& let ExprKind::Struct(qpath, fields, None) = arg.kind
&& matches!(qpath, QPath::LangItem(LangItem::Range, _))
&& let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
&& paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed
&& fields.len() == 2
&& fields[0].ident.as_str() == "start"
&& let ExprKind::Lit(ref lit) = fields[0].expr.kind

View File

@@ -2,7 +2,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind
&& name.as_str() == "i"
&& let ExprKind::Struct(qpath, fields, None) = arg.kind
&& matches!(qpath, QPath::LangItem(LangItem::Range, _))
&& let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
&& paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed
&& fields.len() == 2
&& fields[0].ident.as_str() == "start"
&& let ExprKind::Lit(ref lit) = fields[0].expr.kind

View File

@@ -29,11 +29,9 @@ impl Foo<'_> {
fn d<'a>(&self, x: &'a u32) { }
// FIXME: impl Traits printed as just `/*impl Trait*/`, ugh
fn iter1<'a>(&self)
-> /*impl Trait*/ { #[lang = "Range"] { start: 0, end: 1 } }
fn iter1<'a>(&self) -> /*impl Trait*/ { Range { start: 0, end: 1 } }
fn iter2(&self)
-> /*impl Trait*/ { #[lang = "Range"] { start: 0, end: 1 } }
fn iter2(&self) -> /*impl Trait*/ { Range { start: 0, end: 1 } }
}
fn a(x: Foo<'_>) { }

View File

@@ -273,14 +273,14 @@ mod expressions {
/// ExprKind::Range
fn expr_range() {
let (lo, hi);
#[lang = "RangeFull"] { };
#[lang = "RangeTo"] { end: hi };
#[lang = "RangeFrom"] { start: lo };
#[lang = "Range"] { start: lo, end: hi };
#[lang = "Range"] { start: lo, end: hi };
#[lang = "RangeToInclusive"] { end: hi };
#[lang = "range_inclusive_new"](lo, hi);
#[lang = "range_inclusive_new"](-2, -1);
RangeFull { };
RangeTo { end: hi };
RangeFrom { start: lo };
Range { start: lo, end: hi };
Range { start: lo, end: hi };
RangeToInclusive { end: hi };
range_inclusive_new(lo, hi);
range_inclusive_new(-2, -1);
}
/// ExprKind::Underscore