Exhaustively handle expressions in patterns

This commit is contained in:
Oli Scherer 2024-12-11 16:50:45 +00:00
parent 5df69191cb
commit c9365dd09f
36 changed files with 447 additions and 197 deletions

View File

@ -102,17 +102,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let kind = match &e.kind {
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
ExprKind::ConstBlock(c) => {
let c = self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
hir::ConstBlock {
def_id,
hir_id: this.lower_node_id(c.id),
body: this.lower_const_body(c.value.span, Some(&c.value)),
}
});
hir::ExprKind::ConstBlock(c)
}
ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(c)),
ExprKind::Repeat(expr, count) => {
let expr = self.lower_expr(expr);
let count = self.lower_array_length_to_const_arg(count);
@ -153,18 +143,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let ohs = self.lower_expr(ohs);
hir::ExprKind::Unary(op, ohs)
}
ExprKind::Lit(token_lit) => {
let lit_kind = match LitKind::from_token_lit(*token_lit) {
Ok(lit_kind) => lit_kind,
Err(err) => {
let guar =
report_lit_error(&self.tcx.sess.psess, err, *token_lit, e.span);
LitKind::Err(guar)
}
};
let lit = self.arena.alloc(respan(self.lower_span(e.span), lit_kind));
hir::ExprKind::Lit(lit)
}
ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)),
ExprKind::IncludedBytes(bytes) => {
let lit = self.arena.alloc(respan(
self.lower_span(e.span),
@ -403,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
hir::ConstBlock {
def_id,
hir_id: this.lower_node_id(c.id),
body: this.lower_const_body(c.value.span, Some(&c.value)),
}
})
}
pub(crate) fn lower_lit(
&mut self,
token_lit: &token::Lit,
span: Span,
) -> &'hir Spanned<LitKind> {
let lit_kind = match LitKind::from_token_lit(*token_lit) {
Ok(lit_kind) => lit_kind,
Err(err) => {
let guar = report_lit_error(&self.tcx.sess.psess, err, *token_lit, span);
LitKind::Err(guar)
}
};
self.arena.alloc(respan(self.lower_span(span), lit_kind))
}
fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
match u {
UnOp::Deref => hir::UnOp::Deref,

View File

@ -209,6 +209,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
fn visit_pat_expr(&mut self, expr: &'hir PatExpr<'hir>) {
self.insert(expr.span, expr.hir_id, Node::PatExpr(expr));
self.with_parent(expr.hir_id, |this| {
intravisit::walk_pat_expr(this, expr);
});
}
fn visit_pat_field(&mut self, field: &'hir PatField<'hir>) {
self.insert(field.span, field.hir_id, Node::PatField(field));
self.with_parent(field.hir_id, |this| {

View File

@ -35,6 +35,7 @@
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]

View File

@ -1,9 +1,12 @@
use std::sync::Arc;
use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_span::source_map::Spanned;
use rustc_middle::span_bug;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{Ident, Span};
use super::errors::{
@ -367,24 +370,54 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// }
// m!(S);
// ```
fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> {
match &expr.kind {
ExprKind::Lit(..)
| ExprKind::ConstBlock(..)
| ExprKind::IncludedBytes(..)
| ExprKind::Err(_)
| ExprKind::Dummy => {}
ExprKind::Path(..) if allow_paths => {}
ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
fn lower_expr_within_pat(
&mut self,
expr: &Expr,
allow_paths: bool,
) -> &'hir hir::PatExpr<'hir> {
let err = |guar| hir::PatExprKind::Lit {
lit: self.arena.alloc(respan(self.lower_span(expr.span), LitKind::Err(guar))),
negated: false,
};
let kind = match &expr.kind {
ExprKind::Lit(lit) => {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: false }
}
ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)),
ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit {
lit: self.arena.alloc(respan(
self.lower_span(expr.span),
LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked),
)),
negated: false,
},
ExprKind::Err(guar) => err(*guar),
ExprKind::Dummy => span_bug!(expr.span, "lowered ExprKind::Dummy"),
ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath(
expr.id,
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
)),
ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: true }
}
_ => {
let pattern_from_macro = expr.is_approximately_pattern();
let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {
span: expr.span,
pattern_from_macro_note: pattern_from_macro,
});
return self.arena.alloc(self.expr_err(expr.span, guar));
err(guar)
}
}
self.lower_expr(expr)
};
self.arena.alloc(hir::PatExpr {
hir_id: self.lower_node_id(expr.id),
span: expr.span,
kind,
})
}
}

View File

@ -1519,6 +1519,26 @@ impl fmt::Debug for DotDotPos {
}
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct PatExpr<'hir> {
pub hir_id: HirId,
pub span: Span,
pub kind: PatExprKind<'hir>,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum PatExprKind<'hir> {
Lit {
lit: &'hir Lit,
// FIXME: move this into `Lit` and handle negated literal expressions
// once instead of matching on unop neg expressions everywhere.
negated: bool,
},
ConstBlock(ConstBlock),
/// A path pattern for a unit struct/variant or a (maybe-associated) constant.
Path(QPath<'hir>),
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum PatKind<'hir> {
/// Represents a wildcard pattern (i.e., `_`).
@ -1564,13 +1584,13 @@ pub enum PatKind<'hir> {
Ref(&'hir Pat<'hir>, Mutability),
/// A literal.
Lit(&'hir Expr<'hir>),
Lit(&'hir PatExpr<'hir>),
/// A guard pattern (e.g., `x if guard(x)`).
Guard(&'hir Pat<'hir>, &'hir Expr<'hir>),
/// A range pattern (e.g., `1..=2` or `1..2`).
Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd),
Range(Option<&'hir PatExpr<'hir>>, Option<&'hir PatExpr<'hir>>, RangeEnd),
/// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`.
///
@ -4144,6 +4164,10 @@ pub enum Node<'hir> {
OpaqueTy(&'hir OpaqueTy<'hir>),
Pat(&'hir Pat<'hir>),
PatField(&'hir PatField<'hir>),
/// Needed as its own node with its own HirId for tracking
/// the unadjusted type of literals within patterns
/// (e.g. byte str literals not being of slice type).
PatExpr(&'hir PatExpr<'hir>),
Arm(&'hir Arm<'hir>),
Block(&'hir Block<'hir>),
LetStmt(&'hir LetStmt<'hir>),
@ -4200,6 +4224,7 @@ impl<'hir> Node<'hir> {
| Node::Block(..)
| Node::Ctor(..)
| Node::Pat(..)
| Node::PatExpr(..)
| Node::Arm(..)
| Node::LetStmt(..)
| Node::Crate(..)

View File

@ -342,6 +342,9 @@ pub trait Visitor<'v>: Sized {
fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result {
walk_pat_field(self, f)
}
fn visit_pat_expr(&mut self, expr: &'v PatExpr<'v>) -> Self::Result {
walk_pat_expr(self, expr)
}
fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result {
walk_anon_const(self, c)
}
@ -685,10 +688,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
try_visit!(visitor.visit_ident(ident));
visit_opt!(visitor, visit_pat, optional_subpattern);
}
PatKind::Lit(ref expression) => try_visit!(visitor.visit_expr(expression)),
PatKind::Lit(ref expression) => try_visit!(visitor.visit_pat_expr(expression)),
PatKind::Range(ref lower_bound, ref upper_bound, _) => {
visit_opt!(visitor, visit_expr, lower_bound);
visit_opt!(visitor, visit_expr, upper_bound);
visit_opt!(visitor, visit_pat_expr, lower_bound);
visit_opt!(visitor, visit_pat_expr, upper_bound);
}
PatKind::Never | PatKind::Wild | PatKind::Err(_) => (),
PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => {
@ -710,6 +713,15 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'
visitor.visit_pat(field.pat)
}
pub fn walk_pat_expr<'v, V: Visitor<'v>>(visitor: &mut V, expr: &'v PatExpr<'v>) -> V::Result {
try_visit!(visitor.visit_id(expr.hir_id));
match &expr.kind {
PatExprKind::Lit { .. } => V::Result::output(),
PatExprKind::ConstBlock(c) => visitor.visit_inline_const(c),
PatExprKind::Path(qpath) => visitor.visit_qpath(qpath, expr.hir_id, expr.span),
}
}
pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) -> V::Result {
try_visit!(visitor.visit_id(constant.hir_id));
visitor.visit_nested_body(constant.body)

View File

@ -2449,17 +2449,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ty::new_error(tcx, err)
}
hir::PatKind::Range(start, end, include_end) => {
let expr_to_const = |expr: &'tcx hir::Expr<'tcx>| -> ty::Const<'tcx> {
let (expr, neg) = match expr.kind {
hir::ExprKind::Unary(hir::UnOp::Neg, negated) => {
(negated, Some((expr.hir_id, expr.span)))
}
_ => (expr, None),
};
let (c, c_ty) = match &expr.kind {
hir::ExprKind::Lit(lit) => {
let expr_to_const = |expr: &'tcx hir::PatExpr<'tcx>| -> ty::Const<'tcx> {
let (c, c_ty) = match expr.kind {
hir::PatExprKind::Lit { lit, negated } => {
let lit_input =
LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() };
LitToConstInput { lit: &lit.node, ty, neg: negated };
let ct = match tcx.lit_to_const(lit_input) {
Ok(c) => c,
Err(LitToConstError::Reported(err)) => {
@ -2470,23 +2464,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
(ct, ty)
}
hir::ExprKind::Path(hir::QPath::Resolved(
hir::PatExprKind::Path(hir::QPath::Resolved(
_,
path @ &hir::Path {
res: Res::Def(DefKind::ConstParam, def_id),
..
},
)) => {
let _ = self.prohibit_generic_args(
match self.prohibit_generic_args(
path.segments.iter(),
GenericsArgsErrExtend::Param(def_id),
);
let ty = tcx
.type_of(def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
let ct = self.lower_const_param(def_id, expr.hir_id);
(ct, ty)
) {
Ok(()) => {
let ty = tcx
.type_of(def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
let ct = self.lower_const_param(def_id, expr.hir_id);
(ct, ty)
}
Err(guar) => (
ty::Const::new_error(tcx, guar),
Ty::new_error(tcx, guar),
),
}
}
_ => {
@ -2497,9 +2498,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
};
self.record_ty(expr.hir_id, c_ty, expr.span);
if let Some((id, span)) = neg {
self.record_ty(id, c_ty, span);
}
c
};

View File

@ -199,6 +199,7 @@ impl<'a> State<'a> {
Node::OpaqueTy(o) => self.print_opaque_ty(o),
Node::Pat(a) => self.print_pat(a),
Node::PatField(a) => self.print_patfield(a),
Node::PatExpr(a) => self.print_pat_expr(a),
Node::Arm(a) => self.print_arm(a),
Node::Infer(_) => self.word("_"),
Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident),
@ -1849,6 +1850,19 @@ impl<'a> State<'a> {
}
}
fn print_pat_expr(&mut self, expr: &hir::PatExpr<'_>) {
match &expr.kind {
hir::PatExprKind::Lit { lit, negated } => {
if *negated {
self.word("-");
}
self.print_literal(lit);
}
hir::PatExprKind::ConstBlock(c) => self.print_inline_const(c),
hir::PatExprKind::Path(qpath) => self.print_qpath(qpath, true),
}
}
fn print_pat(&mut self, pat: &hir::Pat<'_>) {
self.maybe_print_comment(pat.span.lo());
self.ann.pre(self, AnnNode::Pat(pat));
@ -1966,17 +1980,17 @@ impl<'a> State<'a> {
self.pclose();
}
}
PatKind::Lit(e) => self.print_expr(e),
PatKind::Lit(e) => self.print_pat_expr(e),
PatKind::Range(begin, end, end_kind) => {
if let Some(expr) = begin {
self.print_expr(expr);
self.print_pat_expr(expr);
}
match end_kind {
RangeEnd::Included => self.word("..."),
RangeEnd::Excluded => self.word(".."),
}
if let Some(expr) = end {
self.print_expr(expr);
self.print_pat_expr(expr);
}
}
PatKind::Slice(before, slice, after) => {

View File

@ -430,6 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| hir::Node::AssocItemConstraint(_)
| hir::Node::TraitRef(_)
| hir::Node::PatField(_)
| hir::Node::PatExpr(_)
| hir::Node::LetStmt(_)
| hir::Node::Synthetic
| hir::Node::Err(_)
@ -1796,7 +1797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn check_expr_const_block(
pub(super) fn check_expr_const_block(
&self,
block: &'tcx hir::ConstBlock,
expected: Expectation<'tcx>,

View File

@ -1039,6 +1039,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def_id,
span,
),
Res::Err => {
return (
Ty::new_error(
tcx,
tcx.dcx().span_delayed_bug(span, "could not resolve path {:?}"),
),
res,
);
}
_ => bug!("instantiate_value_path on {:?}", res),
};

View File

@ -170,6 +170,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
..
})
| hir::Node::PatExpr(&hir::PatExpr {
kind: hir::PatExprKind::Path(QPath::TypeRelative(rcvr, segment)),
span,
..
})
| hir::Node::Pat(&hir::Pat {
kind:
hir::PatKind::Path(QPath::TypeRelative(rcvr, segment))

View File

@ -30,6 +30,7 @@ use tracing::{debug, instrument, trace};
use ty::VariantDef;
use super::report_unexpected_variant_res;
use crate::expectation::Expectation;
use crate::gather_locals::DeclOrigin;
use crate::{FnCtxt, LoweredTy, errors};
@ -398,7 +399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`.
//
// Call `resolve_vars_if_possible` here for inline const blocks.
PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() {
PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
ty::Ref(..) => AdjustMode::Pass,
_ => AdjustMode::Peel,
},
@ -493,10 +494,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(expected, def_br, max_ref_mutbl)
}
fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
let ty = match &lt.kind {
rustc_hir::PatExprKind::Lit { lit, .. } => {
self.check_expr_lit(lit, Expectation::NoExpectation)
}
rustc_hir::PatExprKind::ConstBlock(c) => {
self.check_expr_const_block(c, Expectation::NoExpectation)
}
rustc_hir::PatExprKind::Path(qpath) => {
let (res, opt_ty, segments) =
self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span);
self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0
}
};
self.write_ty(lt.hir_id, ty);
ty
}
fn check_pat_lit(
&self,
span: Span,
lt: &hir::Expr<'tcx>,
lt: &hir::PatExpr<'tcx>,
expected: Ty<'tcx>,
ti: &TopInfo<'tcx>,
) -> Ty<'tcx> {
@ -507,7 +526,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Byte string patterns behave the same way as array patterns
// They can denote both statically and dynamically-sized byte arrays.
let mut pat_ty = ty;
if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind {
if let hir::PatExprKind::Lit {
lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, ..
} = lt.kind
{
let expected = self.structurally_resolve_type(span, expected);
if let ty::Ref(_, inner_ty, _) = *expected.kind()
&& self.try_structurally_resolve_type(span, inner_ty).is_slice()
@ -524,7 +546,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if self.tcx.features().string_deref_patterns()
&& let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind
&& let hir::PatExprKind::Lit {
lit: Spanned { node: ast::LitKind::Str(..), .. }, ..
} = lt.kind
{
let tcx = self.tcx;
let expected = self.resolve_vars_if_possible(expected);
@ -565,15 +589,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_pat_range(
&self,
span: Span,
lhs: Option<&'tcx hir::Expr<'tcx>>,
rhs: Option<&'tcx hir::Expr<'tcx>>,
lhs: Option<&'tcx hir::PatExpr<'tcx>>,
rhs: Option<&'tcx hir::PatExpr<'tcx>>,
expected: Ty<'tcx>,
ti: &TopInfo<'tcx>,
) -> Ty<'tcx> {
let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
let calc_side = |opt_expr: Option<&'tcx hir::PatExpr<'tcx>>| match opt_expr {
None => None,
Some(expr) => {
let ty = self.check_expr(expr);
let ty = self.check_pat_expr_unadjusted(expr);
// Check that the end-point is possibly of numeric or char type.
// The early check here is not for correctness, but rather better
// diagnostics (e.g. when `&str` is being matched, `expected` will
@ -1813,9 +1837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
} else if inexistent_fields.len() == 1 {
match pat_field.pat.kind {
PatKind::Lit(expr)
PatKind::Lit(_)
if !self.may_coerce(
self.typeck_results.borrow().expr_ty(expr),
self.typeck_results.borrow().node_type(pat_field.pat.hir_id),
self.field_ty(field.span, field_def, args),
) => {}
_ => {

View File

@ -147,15 +147,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
self.visit_body(body);
self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause);
}
hir::ExprKind::ConstBlock(anon_const) => {
let body = self.fcx.tcx.hir().body(anon_const.body);
self.visit_body(body);
}
_ => {}
}
intravisit::walk_expr(self, expr);
}
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
let body = self.fcx.tcx.hir().body(c.body);
self.visit_body(body);
}
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

View File

@ -246,6 +246,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}
}
fn visit_const_block(&mut self, span: Span, anon_const: &hir::ConstBlock) {
self.visit_node_id(span, anon_const.hir_id);
let body = self.tcx().hir().body(anon_const.body);
self.visit_body(body);
}
}
///////////////////////////////////////////////////////////////////////////
@ -275,11 +282,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
self.visit_field_id(e.hir_id);
}
hir::ExprKind::ConstBlock(anon_const) => {
self.visit_node_id(e.span, anon_const.hir_id);
let body = self.tcx().hir().body(anon_const.body);
self.visit_body(body);
hir::ExprKind::ConstBlock(ref anon_const) => {
self.visit_const_block(e.span, anon_const);
}
_ => {}
}
@ -335,6 +339,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
intravisit::walk_pat(self, p);
}
fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) {
self.visit_node_id(expr.span, expr.hir_id);
if let hir::PatExprKind::ConstBlock(c) = &expr.kind {
self.visit_const_block(expr.span, c);
}
intravisit::walk_pat_expr(self, expr);
}
fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
intravisit::walk_local(self, l);
let var_ty = self.fcx.local_ty(l.span, l.hir_id);

View File

@ -938,6 +938,7 @@ impl<'hir> Map<'hir> {
Node::OpaqueTy(op) => op.span,
Node::Pat(pat) => pat.span,
Node::PatField(field) => field.span,
Node::PatExpr(lit) => lit.span,
Node::Arm(arm) => arm.span,
Node::Block(block) => block.span,
Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)),
@ -1209,6 +1210,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
Node::OpaqueTy(_) => node_str("opaque type"),
Node::Pat(_) => node_str("pat"),
Node::PatField(_) => node_str("pattern field"),
Node::PatExpr(_) => node_str("pattern literal"),
Node::Param(_) => node_str("param"),
Node::Arm(_) => node_str("arm"),
Node::Block(_) => node_str("block"),

View File

@ -154,7 +154,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
fn lower_pattern_range_endpoint(
&mut self,
expr: Option<&'tcx hir::Expr<'tcx>>,
expr: Option<&'tcx hir::PatExpr<'tcx>>,
) -> Result<
(Option<PatRangeBoundary<'tcx>>, Option<Ascription<'tcx>>, Option<LocalDefId>),
ErrorGuaranteed,
@ -200,13 +200,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// This is only called when the range is already known to be malformed.
fn error_on_literal_overflow(
&self,
expr: Option<&'tcx hir::Expr<'tcx>>,
expr: Option<&'tcx hir::PatExpr<'tcx>>,
ty: Ty<'tcx>,
) -> Result<(), ErrorGuaranteed> {
use hir::{ExprKind, UnOp};
use rustc_ast::ast::LitKind;
let Some(mut expr) = expr else {
let Some(expr) = expr else {
return Ok(());
};
let span = expr.span;
@ -214,12 +213,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
// We need to inspect the original expression, because if we only inspect the output of
// `eval_bits`, an overflowed value has already been wrapped around.
// We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint.
let mut negated = false;
if let ExprKind::Unary(UnOp::Neg, sub_expr) = expr.kind {
negated = true;
expr = sub_expr;
}
let ExprKind::Lit(lit) = expr.kind else {
let hir::PatExprKind::Lit { lit, negated } = expr.kind else {
return Ok(());
};
let LitKind::Int(lit_val, _) = lit.node else {
@ -248,8 +242,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
fn lower_pattern_range(
&mut self,
lo_expr: Option<&'tcx hir::Expr<'tcx>>,
hi_expr: Option<&'tcx hir::Expr<'tcx>>,
lo_expr: Option<&'tcx hir::PatExpr<'tcx>>,
hi_expr: Option<&'tcx hir::PatExpr<'tcx>>,
end: RangeEnd,
ty: Ty<'tcx>,
span: Span,
@ -662,25 +656,18 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// The special case for negation exists to allow things like `-128_i8`
/// which would overflow if we tried to evaluate `128_i8` and then negate
/// afterwards.
fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> {
let (lit, neg) = match expr.kind {
hir::ExprKind::Path(ref qpath) => {
fn lower_lit(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> {
let (lit, neg) = match &expr.kind {
hir::PatExprKind::Path(qpath) => {
return self.lower_path(qpath, expr.hir_id, expr.span).kind;
}
hir::ExprKind::ConstBlock(ref anon_const) => {
hir::PatExprKind::ConstBlock(anon_const) => {
return self.lower_inline_const(anon_const, expr.hir_id, expr.span);
}
hir::ExprKind::Lit(ref lit) => (lit, false),
hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => {
let hir::ExprKind::Lit(ref lit) = expr.kind else {
span_bug!(expr.span, "not a literal: {:?}", expr);
};
(lit, true)
}
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
hir::PatExprKind::Lit { lit, negated } => (lit, *negated),
};
let ct_ty = self.typeck_results.expr_ty(expr);
let ct_ty = self.typeck_results.node_type(expr.hir_id);
let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg };
match self.tcx.at(expr.span).lit_to_const(lit_input) {
Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind,

View File

@ -3,8 +3,11 @@ use clippy_utils::numeric_literal;
use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr};
use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt};
use rustc_hir::{
Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Pat, PatExpr, PatExprKind, PatKind, Stmt, StmtKind,
StructTailExpr,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
@ -219,6 +222,22 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
walk_expr(self, expr);
}
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
match pat.kind {
PatKind::Lit(&PatExpr {
hir_id,
kind: PatExprKind::Lit { lit, .. },
..
}) => {
let ty = self.cx.typeck_results().node_type(hir_id);
self.check_lit(lit, ty, hir_id);
return;
},
_ => {},
}
walk_pat(self, pat)
}
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
match stmt.kind {
// we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric`

View File

@ -8,8 +8,8 @@ use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{
AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind,
ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath,
TraitItemRef, TyKind,
ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy,
QPath, TraitItemRef, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@ -163,7 +163,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
if let ExprKind::Let(lt) = expr.kind
&& match lt.pat.kind {
PatKind::Slice([], None, []) => true,
PatKind::Lit(lit) if is_empty_string(lit) => true,
PatKind::Lit(lit) => match lit.kind {
PatExprKind::Lit { lit, .. } => match lit.node {
LitKind::Str(lit, _) => lit.as_str().is_empty(),
_ => false,
},
_ => false,
},
_ => false,
}
&& !expr.span.from_expansion()

View File

@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator
use rustc_ast::LitKind::{Byte, Char};
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
{
let arg = peel_ref_operators(cx, arg);
let ty_sugg = get_ty_sugg(cx, arg, start);
let range = check_range(start, end);
let range = check_expr_range(start, end);
check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
}
}
@ -196,19 +196,34 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
}
}
fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
if let ExprKind::Lit(start_lit) = &start.kind
&& let ExprKind::Lit(end_lit) = &end.kind
{
match (&start_lit.node, &end_lit.node) {
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
(Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter,
(Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter,
(Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
_ => CharRange::Otherwise,
}
check_lit_range(start_lit, end_lit)
} else {
CharRange::Otherwise
}
}
fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange {
if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind
&& let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind
{
check_lit_range(start_lit, end_lit)
} else {
CharRange::Otherwise
}
}
fn check_lit_range(start_lit: &Lit, end_lit: &Lit) -> CharRange {
match (&start_lit.node, &end_lit.node) {
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
(Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter,
(Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter,
(Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
_ => CharRange::Otherwise,
}
}

View File

@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt;
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp};
use rustc_hir::{PatExpr, PatExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
@ -38,14 +38,13 @@ declare_clippy_lint! {
}
declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]);
fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> {
if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind {
expr_as_i128(expr).map(|num| -num)
} else if let ExprKind::Lit(lit) = expr.kind
fn expr_as_i128(expr: &PatExpr<'_>) -> Option<i128> {
if let PatExprKind::Lit { lit, negated } = expr.kind
&& let LitKind::Int(num, _) = lit.node
{
// Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now.
num.get().try_into().ok()
let n = i128::try_from(num.get()).ok()?;
Some(if negated { -n } else { n })
} else {
None
}
@ -58,7 +57,7 @@ struct Num {
}
impl Num {
fn new(expr: &Expr<'_>) -> Option<Self> {
fn new(expr: &PatExpr<'_>) -> Option<Self> {
Some(Self {
val: expr_as_i128(expr)?,
span: expr.span,

View File

@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet};
use clippy_utils::sugg::Sugg;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, PatKind};
use rustc_hir::{Arm, Expr, PatExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -22,7 +22,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
if arms.len() == 2 {
// no guards
let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
if let ExprKind::Lit(lit) = arm_bool.kind {
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
match lit.node {
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),

View File

@ -7,7 +7,7 @@ use rustc_arena::DroplessArena;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
use rustc_hir::{Arm, Expr, PatExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
@ -313,7 +313,7 @@ impl<'a> NormalizedPat<'a> {
},
PatKind::Lit(e) => match &e.kind {
// TODO: Handle negative integers. They're currently treated as a wild match.
ExprKind::Lit(lit) => match lit.node {
PatExprKind::Lit{ lit, negated: false } => match lit.node {
LitKind::Str(sym, _) => Self::LitStr(sym),
LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes),
LitKind::Byte(val) => Self::LitInt(val.into()),
@ -330,7 +330,7 @@ impl<'a> NormalizedPat<'a> {
let start = match start {
None => 0,
Some(e) => match &e.kind {
ExprKind::Lit(lit) => match lit.node {
PatExprKind::Lit { lit, negated: false } => match lit.node {
LitKind::Int(val, _) => val.get(),
LitKind::Char(val) => val.into(),
LitKind::Byte(val) => val.into(),
@ -342,7 +342,7 @@ impl<'a> NormalizedPat<'a> {
let (end, bounds) = match end {
None => (u128::MAX, RangeEnd::Included),
Some(e) => match &e.kind {
ExprKind::Lit(lit) => match lit.node {
PatExprKind::Lit { lit, negated: false } => match lit.node {
LitKind::Int(val, _) => (val.get(), bounds),
LitKind::Char(val) => (val.into(), bounds),
LitKind::Byte(val) => (val.into(), bounds),

View File

@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind};
use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;
@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(
};
for arm in arms {
if let PatKind::Lit(Expr {
kind: ExprKind::Lit(lit),
if let PatKind::Lit(PatExpr {
kind: PatExprKind::Lit { lit, negated: false },
..
}) = arm.pat.kind
&& let LitKind::Str(symbol, _) = lit.node

View File

@ -8,7 +8,7 @@ use clippy_utils::{
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath};
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;
@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
},
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
Node::Item(item) => {
if let ItemKind::Fn{ .. } = item.kind {
if let ItemKind::Fn { .. } = item.kind {
let output = cx
.tcx
.fn_sig(item.owner_id)
@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
});
},
// Example: `5 => 5`
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind {
(PatKind::Lit(pat_expr_expr), ExprKind::Lit(expr_spanned)) => {
if let PatExprKind::Lit {
lit: pat_spanned,
negated: false,
} = &pat_expr_expr.kind
{
return pat_spanned.node == expr_spanned.node;
}
},

View File

@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
let lhs_const = if let Some(lhs) = lhs {
ConstEvalCtxt::new(cx).eval(lhs)?
ConstEvalCtxt::new(cx).eval_pat_expr(lhs)?
} else {
let min_val_const = ty.numeric_min_val(cx.tcx)?;
mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
};
let rhs_const = if let Some(rhs) = rhs {
ConstEvalCtxt::new(cx).eval(rhs)?
ConstEvalCtxt::new(cx).eval_pat_expr(rhs)?
} else {
let max_val_const = ty.numeric_max_val(cx.tcx)?;
mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
@ -58,7 +58,9 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
}
if let PatKind::Lit(value) = pat.kind {
let value = ConstEvalCtxt::new(cx).eval_full_int(value)?;
let value = ConstEvalCtxt::new(cx)
.eval_pat_expr(value)?
.int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?;
return Some(SpannedRange {
span: pat.span,
node: (value, EndBound::Included(value)),

View File

@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, PatExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_span::{Span, Symbol, sym};
@ -75,7 +75,7 @@ fn find_match_true<'tcx>(
message: &'static str,
) {
if let PatKind::Lit(lit) = pat.kind
&& let ExprKind::Lit(lit) = lit.kind
&& let PatExprKind::Lit{ lit, negated: false } = lit.kind
&& let LitKind::Bool(pat_is_true) = lit.node
{
let mut applicability = Applicability::MachineApplicable;

View File

@ -9,7 +9,7 @@ use rustc_arena::DroplessArena;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{Visitor, walk_pat};
use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind};
use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind, PatExpr, PatExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef};
use rustc_span::{Span, sym};
@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
// scrutinee derives PartialEq and the pattern is a constant.
let pat_ref_count = match pat.kind {
// string literals are already a reference.
PatKind::Lit(Expr {
kind: ExprKind::Lit(lit),
PatKind::Lit(PatExpr {
kind: PatExprKind::Lit { lit, negated: false },
..
}) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1,
_ => pat_ref_count,

View File

@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr};
use itertools::Itertools;
use rustc_ast::{BinOpKind, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind};
use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<
return ControlFlow::Break(());
}
if arm.pat.walk_short(|pat| match pat.kind {
PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => {
PatKind::Lit(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => {
if let LitKind::Char(_) = lit.node {
set_char_spans.push(lit.span);
}

View File

@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind,
FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.expr(expr);
}
fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) {
let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind");
macro_rules! kind {
($($t:tt)*) => (kind(format_args!($($t)*)));
}
match lit.value.kind {
PatExprKind::Lit { lit, negated } => {
bind!(self, lit);
bind!(self, negated);
kind!("Lit{{ref {lit}, {negated} }}");
self.lit(lit);
},
PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
PatExprKind::Path(ref qpath) => {
bind!(self, qpath);
kind!("Path(ref {qpath})");
self.qpath(qpath);
},
}
}
fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind");
macro_rules! kind {
@ -721,13 +742,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
PatKind::Lit(lit_expr) => {
bind!(self, lit_expr);
kind!("Lit({lit_expr})");
self.expr(lit_expr);
self.pat_expr(lit_expr);
},
PatKind::Range(start, end, end_kind) => {
opt_bind!(self, start, end);
kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
start.if_some(|e| self.expr(e));
end.if_some(|e| self.expr(e));
start.if_some(|e| self.pat_expr(e));
end.if_some(|e| self.pat_expr(e));
},
PatKind::Slice(start, middle, end) => {
bind!(self, start, end);

View File

@ -4,7 +4,6 @@
//! executable MIR bodies, so we have to do this instead.
#![allow(clippy::float_cmp)]
use crate::macros::HirNode;
use crate::source::{SpanRangeExt, walk_span_to_context};
use crate::{clip, is_direct_expn_of, sext, unsext};
@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad};
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind};
use rustc_lexer::tokenize;
use rustc_lint::LateContext;
use rustc_middle::mir::ConstValue;
@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant<'tcx>> {
match &pat_expr.kind {
PatExprKind::Lit { lit, negated } => {
let ty = self.typeck.node_type_opt(pat_expr.hir_id);
let val = lit_to_mir_constant(&lit.node, ty);
if *negated {
self.constant_negate(&val, ty?)
} else {
Some(val)
}
}
PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value),
PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id),
}
}
fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option<Constant<'tcx>> {
let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() {
self.tcx.crate_name(def_id.krate) == sym::core
} else {
false
};
self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| {
let result = mir_to_const(self_.tcx, result)?;
// If source is already Constant we wouldn't want to override it with CoreConstant
self_.source.set(
if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
ConstantSource::CoreConstant
} else {
ConstantSource::Constant
},
);
Some(result)
})
}
/// Simple constant folding: Insert an expression, get a constant or none.
fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind {
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.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.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
self_.source.set(
if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
ConstantSource::CoreConstant
} else {
ConstantSource::Constant
},
);
Some(result)
})
},
ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(lit) => {
if is_direct_expn_of(e.span, "cfg").is_some() {

View File

@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName,
Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty,
TyKind,
Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
TraitBoundModifiers, Ty, TyKind,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::LateContext;
@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> {
li.name == ri.name && self.eq_pat(lp, rp)
}
fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool {
match (&left.kind, &right.kind) {
(
&PatExprKind::Lit {
lit: left,
negated: left_neg,
},
&PatExprKind::Lit {
lit: right,
negated: right_neg,
},
) => left_neg == right_neg && left.node == right.node,
(PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body),
(PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right),
(PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false,
}
}
/// Checks whether two patterns are the same.
fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) {
@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> {
eq
},
(PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r),
(&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
(&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_pat_expr(l, r),
(&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
(&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b))
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b))
both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& (li == ri)
},
(&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
@ -1073,6 +1091,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
}
pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) {
std::mem::discriminant(&lit.kind).hash(&mut self.s);
match &lit.kind {
PatExprKind::Lit { lit, negated } => {
lit.node.hash(&mut self.s);
negated.hash(&mut self.s);
},
PatExprKind::ConstBlock(c) => self.hash_body(c.body),
PatExprKind::Path(qpath) => self.hash_qpath(qpath),
}
}
pub fn hash_pat(&mut self, pat: &Pat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
},
PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat),
PatKind::Lit(expr) => self.hash_expr(expr),
PatKind::Lit(expr) => self.hash_pat_expr(expr),
PatKind::Or(pats) => {
for pat in pats {
self.hash_pat(pat);
@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
PatKind::Path(ref qpath) => self.hash_qpath(qpath),
PatKind::Range(s, e, i) => {
if let Some(s) = s {
self.hash_expr(s);
self.hash_pat_expr(s);
}
if let Some(e) = e {
self.hash_expr(e);
self.hash_pat_expr(e);
}
std::mem::discriminant(&i).hash(&mut self.s);
},

View File

@ -31,7 +31,7 @@ if let StmtKind::Let(local) = stmt.kind
if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind
&& let ExprKind::Let(let_expr) = cond.kind
&& let PatKind::Lit(lit_expr) = let_expr.pat.kind
&& let ExprKind::Lit(ref lit) = lit_expr.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.init.kind
&& match_qpath(qpath, &["a"])

View File

@ -77,7 +77,7 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While:
}
if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr)
&& let PatKind::Lit(lit_expr) = let_pat.kind
&& let ExprKind::Lit(ref lit) = lit_expr.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.kind
&& match_qpath(qpath, &["a"])

View File

@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind
&& let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node
&& arms.len() == 3
&& let PatKind::Lit(lit_expr) = arms[0].pat.kind
&& let ExprKind::Lit(ref lit1) = lit_expr.kind
&& let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind
&& let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node
&& arms[0].guard.is_none()
&& let ExprKind::Lit(ref lit2) = arms[0].body.kind
&& let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node
&& let PatKind::Lit(lit_expr1) = arms[1].pat.kind
&& let ExprKind::Lit(ref lit3) = lit_expr1.kind
&& let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind
&& let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node
&& arms[1].guard.is_none()
&& let ExprKind::Block(block, None) = arms[1].body.kind

View File

@ -24,7 +24,7 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
&& fields.len() == 1
&& fields[0].ident.as_str() == "field"
&& let PatKind::Lit(lit_expr) = fields[0].pat.kind
&& let ExprKind::Lit(ref lit) = lit_expr.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind
@ -37,7 +37,7 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
&& match_qpath(qpath, &["TestTuple"])
&& fields.len() == 1
&& let PatKind::Lit(lit_expr) = fields[0].kind
&& let ExprKind::Lit(ref lit) = lit_expr.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind

View File

@ -14,4 +14,4 @@ extern crate std;
fn main() ({ } as ())
fn foo((-(128 as i8) as i8)...(127 as i8): i8) ({ } as ())
fn foo(-128...127: i8) ({ } as ())