1
Fork 0

Rollup merge of #134228 - oli-obk:pat-lit-path, r=compiler-errors

Exhaustively handle expressions in patterns

We currently have this invariant in HIR that a `PatKind::Lit` or a `PatKind::Range` only contains

* `ExprKind::Lit`
* `ExprKind::UnOp(Neg, ExprKind::Lit)`
* `ExprKind::Path`
* `ExprKind::ConstBlock`

So I made `PatKind::Lit` and `PatKind::Range` stop containing `Expr`, and instead created a `PatLit` type whose `kind` enum only contains those variants.

The only place code got more complicated was in clippy, as it couldn't share as much anymore with `Expr` handling

It may be interesting on merging `ExprKind::{Path,Lit,ConstBlock}` in the future and using the same `PatLit` type (under a new name).

Then it should also be easier to eliminate any and all `UnOp(Neg, Lit) | Lit` matching that we have across the compiler. Some day we should fold the negation into the literal itself and just store it on the numeric literals
This commit is contained in:
Matthias Krüger 2025-01-08 18:21:00 +01:00 committed by GitHub
commit f92a5ed5b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 523 additions and 273 deletions

View file

@ -623,7 +623,7 @@ impl Pat {
PatKind::Wild PatKind::Wild
| PatKind::Rest | PatKind::Rest
| PatKind::Never | PatKind::Never
| PatKind::Lit(_) | PatKind::Expr(_)
| PatKind::Range(..) | PatKind::Range(..)
| PatKind::Ident(..) | PatKind::Ident(..)
| PatKind::Path(..) | PatKind::Path(..)
@ -801,8 +801,8 @@ pub enum PatKind {
/// A reference pattern (e.g., `&mut (a, b)`). /// A reference pattern (e.g., `&mut (a, b)`).
Ref(P<Pat>, Mutability), Ref(P<Pat>, Mutability),
/// A literal. /// A literal, const block or path.
Lit(P<Expr>), Expr(P<Expr>),
/// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>), Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>),

View file

@ -1512,7 +1512,7 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
vis.visit_ident(ident); vis.visit_ident(ident);
visit_opt(sub, |sub| vis.visit_pat(sub)); visit_opt(sub, |sub| vis.visit_pat(sub));
} }
PatKind::Lit(e) => vis.visit_expr(e), PatKind::Expr(e) => vis.visit_expr(e),
PatKind::TupleStruct(qself, path, elems) => { PatKind::TupleStruct(qself, path, elems) => {
vis.visit_qself(qself); vis.visit_qself(qself);
vis.visit_path(path); vis.visit_path(path);

View file

@ -680,7 +680,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ident(ident));
visit_opt!(visitor, visit_pat, optional_subpattern); visit_opt!(visitor, visit_pat, optional_subpattern);
} }
PatKind::Lit(expression) => try_visit!(visitor.visit_expr(expression)), PatKind::Expr(expression) => try_visit!(visitor.visit_expr(expression)),
PatKind::Range(lower_bound, upper_bound, _end) => { PatKind::Range(lower_bound, upper_bound, _end) => {
visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, lower_bound);
visit_opt!(visitor, visit_expr, upper_bound); visit_opt!(visitor, visit_expr, upper_bound);

View file

@ -102,17 +102,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let kind = match &e.kind { let kind = match &e.kind {
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
ExprKind::ConstBlock(c) => { ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(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::Repeat(expr, count) => { ExprKind::Repeat(expr, count) => {
let expr = self.lower_expr(expr); let expr = self.lower_expr(expr);
let count = self.lower_array_length_to_const_arg(count); let count = self.lower_array_length_to_const_arg(count);
@ -153,18 +143,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let ohs = self.lower_expr(ohs); let ohs = self.lower_expr(ohs);
hir::ExprKind::Unary(op, ohs) hir::ExprKind::Unary(op, ohs)
} }
ExprKind::Lit(token_lit) => { ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)),
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::IncludedBytes(bytes) => { ExprKind::IncludedBytes(bytes) => {
let lit = self.arena.alloc(respan( let lit = self.arena.alloc(respan(
self.lower_span(e.span), 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 { fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
match u { match u {
UnOp::Deref => hir::UnOp::Deref, 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>) { fn visit_pat_field(&mut self, field: &'hir PatField<'hir>) {
self.insert(field.span, field.hir_id, Node::PatField(field)); self.insert(field.span, field.hir_id, Node::PatField(field));
self.with_parent(field.hir_id, |this| { self.with_parent(field.hir_id, |this| {

View file

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

View file

@ -1,9 +1,12 @@
use std::sync::Arc;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::*; use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::Res; 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 rustc_span::{Ident, Span};
use super::errors::{ use super::errors::{
@ -35,8 +38,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
lower_sub, lower_sub,
); );
} }
PatKind::Lit(e) => { PatKind::Expr(e) => {
break hir::PatKind::Lit(self.lower_expr_within_pat(e, false)); break hir::PatKind::Expr(self.lower_expr_within_pat(e, false));
} }
PatKind::TupleStruct(qself, path, pats) => { PatKind::TupleStruct(qself, path, pats) => {
let qpath = self.lower_qpath( let qpath = self.lower_qpath(
@ -367,24 +370,54 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// } // }
// m!(S); // m!(S);
// ``` // ```
fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> { fn lower_expr_within_pat(
match &expr.kind { &mut self,
ExprKind::Lit(..) expr: &Expr,
| ExprKind::ConstBlock(..) allow_paths: bool,
| ExprKind::IncludedBytes(..) ) -> &'hir hir::PatExpr<'hir> {
| ExprKind::Err(_) let err = |guar| hir::PatExprKind::Lit {
| ExprKind::Dummy => {} lit: self.arena.alloc(respan(self.lower_span(expr.span), LitKind::Err(guar))),
ExprKind::Path(..) if allow_paths => {} negated: false,
ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} };
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 pattern_from_macro = expr.is_approximately_pattern();
let guar = self.dcx().emit_err(ArbitraryExpressionInPattern { let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {
span: expr.span, span: expr.span,
pattern_from_macro_note: pattern_from_macro, 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

@ -1701,7 +1701,7 @@ impl<'a> State<'a> {
self.print_pat(inner); self.print_pat(inner);
} }
} }
PatKind::Lit(e) => self.print_expr(e, FixupContext::default()), PatKind::Expr(e) => self.print_expr(e, FixupContext::default()),
PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => { PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
if let Some(e) = begin { if let Some(e) = begin {
self.print_expr(e, FixupContext::default()); self.print_expr(e, FixupContext::default());

View file

@ -522,7 +522,7 @@ impl MacResult for MacEager {
return Some(P(ast::Pat { return Some(P(ast::Pat {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
span: e.span, span: e.span,
kind: PatKind::Lit(e), kind: PatKind::Expr(e),
tokens: None, tokens: None,
})); }));
} }

View file

@ -486,7 +486,7 @@ impl<'a> ExtCtxt<'a> {
self.pat(span, PatKind::Wild) self.pat(span, PatKind::Wild)
} }
pub fn pat_lit(&self, span: Span, expr: P<ast::Expr>) -> P<ast::Pat> { pub fn pat_lit(&self, span: Span, expr: P<ast::Expr>) -> P<ast::Pat> {
self.pat(span, PatKind::Lit(expr)) self.pat(span, PatKind::Expr(expr))
} }
pub fn pat_ident(&self, span: Span, ident: Ident) -> P<ast::Pat> { pub fn pat_ident(&self, span: Span, ident: Ident) -> P<ast::Pat> {
self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE) self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE)

View file

@ -1386,7 +1386,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*; use PatKind::*;
match self.kind { match self.kind {
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true,
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
@ -1413,7 +1413,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*; use PatKind::*;
match self.kind { match self.kind {
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {}
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
@ -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)] #[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum PatKind<'hir> { pub enum PatKind<'hir> {
/// Represents a wildcard pattern (i.e., `_`). /// Represents a wildcard pattern (i.e., `_`).
@ -1563,14 +1583,14 @@ pub enum PatKind<'hir> {
/// A reference pattern (e.g., `&mut (a, b)`). /// A reference pattern (e.g., `&mut (a, b)`).
Ref(&'hir Pat<'hir>, Mutability), Ref(&'hir Pat<'hir>, Mutability),
/// A literal. /// A literal, const block or path.
Lit(&'hir Expr<'hir>), Expr(&'hir PatExpr<'hir>),
/// A guard pattern (e.g., `x if guard(x)`). /// A guard pattern (e.g., `x if guard(x)`).
Guard(&'hir Pat<'hir>, &'hir Expr<'hir>), Guard(&'hir Pat<'hir>, &'hir Expr<'hir>),
/// A range pattern (e.g., `1..=2` or `1..2`). /// 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)?]`. /// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`.
/// ///
@ -4144,6 +4164,10 @@ pub enum Node<'hir> {
OpaqueTy(&'hir OpaqueTy<'hir>), OpaqueTy(&'hir OpaqueTy<'hir>),
Pat(&'hir Pat<'hir>), Pat(&'hir Pat<'hir>),
PatField(&'hir PatField<'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>), Arm(&'hir Arm<'hir>),
Block(&'hir Block<'hir>), Block(&'hir Block<'hir>),
LetStmt(&'hir LetStmt<'hir>), LetStmt(&'hir LetStmt<'hir>),
@ -4200,6 +4224,7 @@ impl<'hir> Node<'hir> {
| Node::Block(..) | Node::Block(..)
| Node::Ctor(..) | Node::Ctor(..)
| Node::Pat(..) | Node::Pat(..)
| Node::PatExpr(..)
| Node::Arm(..) | Node::Arm(..)
| Node::LetStmt(..) | Node::LetStmt(..)
| Node::Crate(..) | 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 { fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result {
walk_pat_field(self, f) 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 { fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result {
walk_anon_const(self, c) 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)); try_visit!(visitor.visit_ident(ident));
visit_opt!(visitor, visit_pat, optional_subpattern); visit_opt!(visitor, visit_pat, optional_subpattern);
} }
PatKind::Lit(ref expression) => try_visit!(visitor.visit_expr(expression)), PatKind::Expr(ref expression) => try_visit!(visitor.visit_pat_expr(expression)),
PatKind::Range(ref lower_bound, ref upper_bound, _) => { PatKind::Range(ref lower_bound, ref upper_bound, _) => {
visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_pat_expr, lower_bound);
visit_opt!(visitor, visit_expr, upper_bound); visit_opt!(visitor, visit_pat_expr, upper_bound);
} }
PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), PatKind::Never | PatKind::Wild | PatKind::Err(_) => (),
PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => { 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) 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 { 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)); try_visit!(visitor.visit_id(constant.hir_id));
visitor.visit_nested_body(constant.body) visitor.visit_nested_body(constant.body)

View file

@ -704,7 +704,7 @@ fn resolve_local<'tcx>(
| PatKind::Wild | PatKind::Wild
| PatKind::Never | PatKind::Never
| PatKind::Path(_) | PatKind::Path(_)
| PatKind::Lit(_) | PatKind::Expr(_)
| PatKind::Range(_, _, _) | PatKind::Range(_, _, _)
| PatKind::Err(_) => false, | PatKind::Err(_) => false,
} }

View file

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

View file

@ -199,6 +199,7 @@ impl<'a> State<'a> {
Node::OpaqueTy(o) => self.print_opaque_ty(o), Node::OpaqueTy(o) => self.print_opaque_ty(o),
Node::Pat(a) => self.print_pat(a), Node::Pat(a) => self.print_pat(a),
Node::PatField(a) => self.print_patfield(a), Node::PatField(a) => self.print_patfield(a),
Node::PatExpr(a) => self.print_pat_expr(a),
Node::Arm(a) => self.print_arm(a), Node::Arm(a) => self.print_arm(a),
Node::Infer(_) => self.word("_"), Node::Infer(_) => self.word("_"),
Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), 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<'_>) { fn print_pat(&mut self, pat: &hir::Pat<'_>) {
self.maybe_print_comment(pat.span.lo()); self.maybe_print_comment(pat.span.lo());
self.ann.pre(self, AnnNode::Pat(pat)); self.ann.pre(self, AnnNode::Pat(pat));
@ -1966,17 +1980,17 @@ impl<'a> State<'a> {
self.pclose(); self.pclose();
} }
} }
PatKind::Lit(e) => self.print_expr(e), PatKind::Expr(e) => self.print_pat_expr(e),
PatKind::Range(begin, end, end_kind) => { PatKind::Range(begin, end, end_kind) => {
if let Some(expr) = begin { if let Some(expr) = begin {
self.print_expr(expr); self.print_pat_expr(expr);
} }
match end_kind { match end_kind {
RangeEnd::Included => self.word("..."), RangeEnd::Included => self.word("..."),
RangeEnd::Excluded => self.word(".."), RangeEnd::Excluded => self.word(".."),
} }
if let Some(expr) = end { if let Some(expr) = end {
self.print_expr(expr); self.print_pat_expr(expr);
} }
} }
PatKind::Slice(before, slice, after) => { PatKind::Slice(before, slice, after) => {

View file

@ -430,6 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| hir::Node::AssocItemConstraint(_) | hir::Node::AssocItemConstraint(_)
| hir::Node::TraitRef(_) | hir::Node::TraitRef(_)
| hir::Node::PatField(_) | hir::Node::PatField(_)
| hir::Node::PatExpr(_)
| hir::Node::LetStmt(_) | hir::Node::LetStmt(_)
| hir::Node::Synthetic | hir::Node::Synthetic
| hir::Node::Err(_) | hir::Node::Err(_)
@ -484,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| hir::PatKind::Box(_) | hir::PatKind::Box(_)
| hir::PatKind::Ref(_, _) | hir::PatKind::Ref(_, _)
| hir::PatKind::Deref(_) | hir::PatKind::Deref(_)
| hir::PatKind::Lit(_) | hir::PatKind::Expr(_)
| hir::PatKind::Range(_, _, _) | hir::PatKind::Range(_, _, _)
| hir::PatKind::Slice(_, _, _) | hir::PatKind::Slice(_, _, _)
| hir::PatKind::Err(_) => true, | hir::PatKind::Err(_) => true,
@ -837,7 +838,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We always require that the type provided as the value for // We always require that the type provided as the value for
// a type parameter outlives the moment of instantiation. // a type parameter outlives the moment of instantiation.
let args = self.typeck_results.borrow().node_args(expr.hir_id); let args = self.typeck_results.borrow().node_args(expr.hir_id);
self.add_wf_bounds(args, expr); self.add_wf_bounds(args, expr.span);
ty ty
} }
@ -1796,7 +1797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
fn check_expr_const_block( pub(super) fn check_expr_const_block(
&self, &self,
block: &'tcx hir::ConstBlock, block: &'tcx hir::ConstBlock,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,

View file

@ -595,7 +595,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
let place_ty = place.place.ty(); let place_ty = place.place.ty();
needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span); needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span);
} }
PatKind::Lit(_) | PatKind::Range(..) => { PatKind::Expr(_) | PatKind::Range(..) => {
// If the PatKind is a Lit or a Range then we want // If the PatKind is a Lit or a Range then we want
// to borrow discr. // to borrow discr.
needs_to_be_read = true; needs_to_be_read = true;
@ -1803,7 +1803,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
PatKind::Path(_) PatKind::Path(_)
| PatKind::Binding(.., None) | PatKind::Binding(.., None)
| PatKind::Lit(..) | PatKind::Expr(..)
| PatKind::Range(..) | PatKind::Range(..)
| PatKind::Never | PatKind::Never
| PatKind::Wild | PatKind::Wild

View file

@ -577,11 +577,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
/// Registers obligations that all `args` are well-formed. /// Registers obligations that all `args` are well-formed.
pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, expr: &hir::Expr<'_>) { pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, span: Span) {
for arg in args.iter().filter(|arg| { for arg in args.iter().filter(|arg| {
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
}) { }) {
self.register_wf_obligation(arg, expr.span, ObligationCauseCode::WellFormed(None)); self.register_wf_obligation(arg, span, ObligationCauseCode::WellFormed(None));
} }
} }
@ -1039,6 +1039,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def_id, def_id,
span, 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), _ => bug!("instantiate_value_path on {:?}", res),
}; };

View file

@ -611,7 +611,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// this is a projection from a trait reference, so we have to // this is a projection from a trait reference, so we have to
// make sure that the trait reference inputs are well-formed. // make sure that the trait reference inputs are well-formed.
self.add_wf_bounds(all_args, self.call_expr); self.add_wf_bounds(all_args, self.call_expr.span);
// the function type must also be well-formed (this is not // the function type must also be well-formed (this is not
// implied by the args being well-formed because of inherent // implied by the args being well-formed because of inherent

View file

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

View file

@ -30,6 +30,7 @@ use tracing::{debug, instrument, trace};
use ty::VariantDef; use ty::VariantDef;
use super::report_unexpected_variant_res; use super::report_unexpected_variant_res;
use crate::expectation::Expectation;
use crate::gather_locals::DeclOrigin; use crate::gather_locals::DeclOrigin;
use crate::{FnCtxt, LoweredTy, errors}; use crate::{FnCtxt, LoweredTy, errors};
@ -270,7 +271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PatKind::Wild | PatKind::Err(_) => expected, PatKind::Wild | PatKind::Err(_) => expected,
// We allow any type here; we ensure that the type is uninhabited during match checking. // We allow any type here; we ensure that the type is uninhabited during match checking.
PatKind::Never => expected, PatKind::Never => expected,
PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
PatKind::Binding(ba, var_id, ident, sub) => { PatKind::Binding(ba, var_id, ident, sub) => {
self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
@ -279,7 +280,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
} }
PatKind::Path(ref qpath) => { PatKind::Path(ref qpath) => {
self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) self.check_pat_path(pat.hir_id, pat.span, qpath, path_res.unwrap(), expected, ti)
} }
PatKind::Struct(ref qpath, fields, has_rest_pat) => { PatKind::Struct(ref qpath, fields, has_rest_pat) => {
self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
@ -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" {}`. // 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. // 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::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
ty::Ref(..) => AdjustMode::Pass, ty::Ref(..) => AdjustMode::Pass,
_ => AdjustMode::Peel, _ => AdjustMode::Peel,
}, },
@ -493,10 +494,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(expected, def_br, max_ref_mutbl) (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( fn check_pat_lit(
&self, &self,
span: Span, span: Span,
lt: &hir::Expr<'tcx>, lt: &hir::PatExpr<'tcx>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
ti: &TopInfo<'tcx>, ti: &TopInfo<'tcx>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
@ -507,7 +526,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Byte string patterns behave the same way as array patterns // Byte string patterns behave the same way as array patterns
// They can denote both statically and dynamically-sized byte arrays. // They can denote both statically and dynamically-sized byte arrays.
let mut pat_ty = ty; 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); let expected = self.structurally_resolve_type(span, expected);
if let ty::Ref(_, inner_ty, _) = *expected.kind() if let ty::Ref(_, inner_ty, _) = *expected.kind()
&& self.try_structurally_resolve_type(span, inner_ty).is_slice() && 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() 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 tcx = self.tcx;
let expected = self.resolve_vars_if_possible(expected); let expected = self.resolve_vars_if_possible(expected);
@ -565,15 +589,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_pat_range( fn check_pat_range(
&self, &self,
span: Span, span: Span,
lhs: Option<&'tcx hir::Expr<'tcx>>, lhs: Option<&'tcx hir::PatExpr<'tcx>>,
rhs: Option<&'tcx hir::Expr<'tcx>>, rhs: Option<&'tcx hir::PatExpr<'tcx>>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
ti: &TopInfo<'tcx>, ti: &TopInfo<'tcx>,
) -> Ty<'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, None => None,
Some(expr) => { 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. // Check that the end-point is possibly of numeric or char type.
// The early check here is not for correctness, but rather better // The early check here is not for correctness, but rather better
// diagnostics (e.g. when `&str` is being matched, `expected` will // diagnostics (e.g. when `&str` is being matched, `expected` will
@ -919,7 +943,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| PatKind::Box(..) | PatKind::Box(..)
| PatKind::Deref(_) | PatKind::Deref(_)
| PatKind::Ref(..) | PatKind::Ref(..)
| PatKind::Lit(..) | PatKind::Expr(..)
| PatKind::Range(..) | PatKind::Range(..)
| PatKind::Err(_) => break 'block None, | PatKind::Err(_) => break 'block None,
}, },
@ -1053,7 +1077,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_pat_path( fn check_pat_path(
&self, &self,
pat: &Pat<'tcx>, hir_id: HirId,
span: Span,
qpath: &hir::QPath<'_>, qpath: &hir::QPath<'_>,
path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]), path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
expected: Ty<'tcx>, expected: Ty<'tcx>,
@ -1072,8 +1097,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => {
let expected = "unit struct, unit variant or constant"; let expected = "unit struct, unit variant or constant";
let e = let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected);
report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0533, expected);
return Ty::new_error(tcx, e); return Ty::new_error(tcx, e);
} }
Res::SelfCtor(def_id) => { Res::SelfCtor(def_id) => {
@ -1088,7 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
res, res,
None, None,
qpath, qpath,
pat.span, span,
E0533, E0533,
"unit struct", "unit struct",
); );
@ -1107,11 +1131,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Type-check the path. // Type-check the path.
let (pat_ty, pat_res) = let (pat_ty, pat_res) =
self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); self.instantiate_value_path(segments, opt_ty, res, span, span, hir_id);
if let Err(err) = if let Err(err) =
self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty)
{ {
self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments); self.emit_bad_pat_path(err, hir_id, span, res, pat_res, pat_ty, segments);
} }
pat_ty pat_ty
} }
@ -1154,13 +1178,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn emit_bad_pat_path( fn emit_bad_pat_path(
&self, &self,
mut e: Diag<'_>, mut e: Diag<'_>,
pat: &hir::Pat<'tcx>, hir_id: HirId,
pat_span: Span,
res: Res, res: Res,
pat_res: Res, pat_res: Res,
pat_ty: Ty<'tcx>, pat_ty: Ty<'tcx>,
segments: &'tcx [hir::PathSegment<'tcx>], segments: &'tcx [hir::PathSegment<'tcx>],
) { ) {
let pat_span = pat.span;
if let Some(span) = self.tcx.hir().res_span(pat_res) { if let Some(span) = self.tcx.hir().res_span(pat_res) {
e.span_label(span, format!("{} defined here", res.descr())); e.span_label(span, format!("{} defined here", res.descr()));
if let [hir::PathSegment { ident, .. }] = &*segments { if let [hir::PathSegment { ident, .. }] = &*segments {
@ -1173,7 +1197,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
res.descr(), res.descr(),
), ),
); );
match self.tcx.parent_hir_node(pat.hir_id) { match self.tcx.parent_hir_node(hir_id) {
hir::Node::PatField(..) => { hir::Node::PatField(..) => {
e.span_suggestion_verbose( e.span_suggestion_verbose(
ident.span.shrink_to_hi(), ident.span.shrink_to_hi(),
@ -1813,9 +1837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} else if inexistent_fields.len() == 1 { } else if inexistent_fields.len() == 1 {
match pat_field.pat.kind { match pat_field.pat.kind {
PatKind::Lit(expr) PatKind::Expr(_)
if !self.may_coerce( 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), 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.visit_body(body);
self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); 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); 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> { 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(..) => { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
self.visit_field_id(e.hir_id); self.visit_field_id(e.hir_id);
} }
hir::ExprKind::ConstBlock(anon_const) => { hir::ExprKind::ConstBlock(ref anon_const) => {
self.visit_node_id(e.span, anon_const.hir_id); self.visit_const_block(e.span, anon_const);
let body = self.tcx().hir().body(anon_const.body);
self.visit_body(body);
} }
_ => {} _ => {}
} }
@ -335,6 +339,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
intravisit::walk_pat(self, p); 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>) { fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
intravisit::walk_local(self, l); intravisit::walk_local(self, l);
let var_ty = self.fcx.local_ty(l.span, l.hir_id); let var_ty = self.fcx.local_ty(l.span, l.hir_id);

View file

@ -1220,7 +1220,7 @@ impl EarlyLintPass for UnusedParens {
// Do not lint on `(..)` as that will result in the other arms being useless. // Do not lint on `(..)` as that will result in the other arms being useless.
Paren(_) Paren(_)
// The other cases do not contain sub-patterns. // The other cases do not contain sub-patterns.
| Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {},
// These are list-like patterns; parens can always be removed. // These are list-like patterns; parens can always be removed.
TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
self.check_unused_parens_pat(cx, p, false, false, keep_space); self.check_unused_parens_pat(cx, p, false, false, keep_space);

View file

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

View file

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

View file

@ -656,14 +656,14 @@ impl<'a> Parser<'a> {
fn visit_pat(&mut self, p: &'a Pat) -> Self::Result { fn visit_pat(&mut self, p: &'a Pat) -> Self::Result {
match &p.kind { match &p.kind {
// Base expression // Base expression
PatKind::Err(_) | PatKind::Lit(_) => { PatKind::Err(_) | PatKind::Expr(_) => {
self.maybe_add_suggestions_then_emit(p.span, p.span, false) self.maybe_add_suggestions_then_emit(p.span, p.span, false)
} }
// Sub-patterns // Sub-patterns
// FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`) // FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`)
PatKind::Box(subpat) | PatKind::Ref(subpat, _) PatKind::Box(subpat) | PatKind::Ref(subpat, _)
if matches!(subpat.kind, PatKind::Err(_) | PatKind::Lit(_)) => if matches!(subpat.kind, PatKind::Err(_) | PatKind::Expr(_)) =>
{ {
self.maybe_add_suggestions_then_emit(subpat.span, p.span, false) self.maybe_add_suggestions_then_emit(subpat.span, p.span, false)
} }
@ -766,7 +766,7 @@ impl<'a> Parser<'a> {
if let Some(re) = self.parse_range_end() { if let Some(re) = self.parse_range_end() {
self.parse_pat_range_begin_with(const_expr, re)? self.parse_pat_range_begin_with(const_expr, re)?
} else { } else {
PatKind::Lit(const_expr) PatKind::Expr(const_expr)
} }
} else if self.is_builtin() { } else if self.is_builtin() {
self.parse_pat_builtin()? self.parse_pat_builtin()?
@ -833,7 +833,7 @@ impl<'a> Parser<'a> {
.struct_span_err(self_.token.span, msg) .struct_span_err(self_.token.span, msg)
.with_span_label(self_.token.span, format!("expected {expected}")) .with_span_label(self_.token.span, format!("expected {expected}"))
}); });
PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit))) PatKind::Expr(self.mk_expr(lo, ExprKind::Lit(lit)))
} else { } else {
// Try to parse everything else as literal with optional minus // Try to parse everything else as literal with optional minus
match self.parse_literal_maybe_minus() { match self.parse_literal_maybe_minus() {
@ -845,7 +845,7 @@ impl<'a> Parser<'a> {
match self.parse_range_end() { match self.parse_range_end() {
Some(form) => self.parse_pat_range_begin_with(begin, form)?, Some(form) => self.parse_pat_range_begin_with(begin, form)?,
None => PatKind::Lit(begin), None => PatKind::Expr(begin),
} }
} }
Err(err) => return self.fatal_unexpected_non_pat(err, expected), Err(err) => return self.fatal_unexpected_non_pat(err, expected),
@ -989,7 +989,7 @@ impl<'a> Parser<'a> {
match &pat.kind { match &pat.kind {
// recover ranges with parentheses around the `(start)..` // recover ranges with parentheses around the `(start)..`
PatKind::Lit(begin) PatKind::Expr(begin)
if self.may_recover() if self.may_recover()
&& let Some(form) = self.parse_range_end() => && let Some(form) = self.parse_range_end() =>
{ {

View file

@ -304,7 +304,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
Box, Box,
Deref, Deref,
Ref, Ref,
Lit, Expr,
Guard, Guard,
Range, Range,
Slice, Slice,
@ -587,7 +587,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Box, Box,
Deref, Deref,
Ref, Ref,
Lit, Expr,
Range, Range,
Slice, Slice,
Rest, Rest,

View file

@ -314,9 +314,9 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
PatKind::Box(p) => return name_from_pat(p), PatKind::Box(p) => return name_from_pat(p),
PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)), PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)),
PatKind::Ref(p, _) => return name_from_pat(p), PatKind::Ref(p, _) => return name_from_pat(p),
PatKind::Lit(..) => { PatKind::Expr(..) => {
warn!( warn!(
"tried to get argument name from PatKind::Lit, which is silly in function arguments" "tried to get argument name from PatKind::Expr, which is silly in function arguments"
); );
return Symbol::intern("()"); return Symbol::intern("()");
} }

View file

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

View file

@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Path(_) | PatKind::Lit(_) => true, PatKind::Path(_) | PatKind::Expr(_) => true,
} }
} }

View file

@ -8,8 +8,8 @@ use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{ use rustc_hir::{
AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind,
ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy,
TraitItemRef, TyKind, QPath, TraitItemRef, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; 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 if let ExprKind::Let(lt) = expr.kind
&& match lt.pat.kind { && match lt.pat.kind {
PatKind::Slice([], None, []) => true, PatKind::Slice([], None, []) => true,
PatKind::Lit(lit) if is_empty_string(lit) => true, PatKind::Expr(lit) => match lit.kind {
PatExprKind::Lit { lit, .. } => match lit.node {
LitKind::Str(lit, _) => lit.as_str().is_empty(),
_ => false,
},
_ => false,
},
_ => false, _ => false,
} }
&& !expr.span.from_expansion() && !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::LitKind::{Byte, Char};
use rustc_ast::ast::RangeLimits; use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability; 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_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
{ {
let arg = peel_ref_operators(cx, arg); let arg = peel_ref_operators(cx, arg);
let ty_sugg = get_ty_sugg(cx, arg, start); 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); 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 if let ExprKind::Lit(start_lit) = &start.kind
&& let ExprKind::Lit(end_lit) = &end.kind && let ExprKind::Lit(end_lit) = &end.kind
{ {
match (&start_lit.node, &end_lit.node) { check_lit_range(start_lit, end_lit)
(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,
}
} else { } else {
CharRange::Otherwise 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_ast::LitKind;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; 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_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
@ -38,14 +38,13 @@ declare_clippy_lint! {
} }
declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]);
fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> { fn expr_as_i128(expr: &PatExpr<'_>) -> Option<i128> {
if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { if let PatExprKind::Lit { lit, negated } = expr.kind
expr_as_i128(expr).map(|num| -num)
} else if let ExprKind::Lit(lit) = expr.kind
&& let LitKind::Int(num, _) = lit.node && let LitKind::Int(num, _) = lit.node
{ {
// Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. // 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 { } else {
None None
} }
@ -58,7 +57,7 @@ struct Num {
} }
impl Num { impl Num {
fn new(expr: &Expr<'_>) -> Option<Self> { fn new(expr: &PatExpr<'_>) -> Option<Self> {
Some(Self { Some(Self {
val: expr_as_i128(expr)?, val: expr_as_i128(expr)?,
span: expr.span, span: expr.span,
@ -90,7 +89,7 @@ impl LateLintPass<'_> for ManualRangePatterns {
let mut ranges_found = Vec::new(); let mut ranges_found = Vec::new();
for pat in pats { for pat in pats {
if let PatKind::Lit(lit) = pat.kind if let PatKind::Expr(lit) = pat.kind
&& let Some(num) = Num::new(lit) && let Some(num) = Num::new(lit)
{ {
numbers_found.insert(num.val); numbers_found.insert(num.val);

View file

@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use rustc_ast::LitKind; use rustc_ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_hir::{Arm, Expr, PatExprKind, PatKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty; use rustc_middle::ty;
@ -21,8 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
move |diag| { move |diag| {
if arms.len() == 2 { if arms.len() == 2 {
// no guards // no guards
let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { let exprs = if let PatKind::Expr(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 { match lit.node {
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
LitKind::Bool(false) => Some((arms[1].body, arms[0].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_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd};
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty; use rustc_middle::ty;
@ -311,9 +311,9 @@ impl<'a> NormalizedPat<'a> {
); );
Self::Tuple(None, pats) Self::Tuple(None, pats)
}, },
PatKind::Lit(e) => match &e.kind { PatKind::Expr(e) => match &e.kind {
// TODO: Handle negative integers. They're currently treated as a wild match. // 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::Str(sym, _) => Self::LitStr(sym),
LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes),
LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Byte(val) => Self::LitInt(val.into()),
@ -330,7 +330,7 @@ impl<'a> NormalizedPat<'a> {
let start = match start { let start = match start {
None => 0, None => 0,
Some(e) => match &e.kind { 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::Int(val, _) => val.get(),
LitKind::Char(val) => val.into(), LitKind::Char(val) => val.into(),
LitKind::Byte(val) => val.into(), LitKind::Byte(val) => val.into(),
@ -342,7 +342,7 @@ impl<'a> NormalizedPat<'a> {
let (end, bounds) = match end { let (end, bounds) = match end {
None => (u128::MAX, RangeEnd::Included), None => (u128::MAX, RangeEnd::Included),
Some(e) => match &e.kind { 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::Int(val, _) => (val.get(), bounds),
LitKind::Char(val) => (val.into(), bounds), LitKind::Char(val) => (val.into(), bounds),
LitKind::Byte(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_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr}; 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_lint::LateContext;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_span::Span; use rustc_span::Span;
@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(
}; };
for arm in arms { for arm in arms {
if let PatKind::Lit(Expr { if let PatKind::Expr(PatExpr {
kind: ExprKind::Lit(lit), kind: PatExprKind::Lit { lit, negated: false },
.. ..
}) = arm.pat.kind }) = arm.pat.kind
&& let LitKind::Str(symbol, _) = lit.node && let LitKind::Str(symbol, _) = lit.node

View file

@ -8,7 +8,7 @@ use clippy_utils::{
}; };
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone; 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_lint::LateContext;
use rustc_span::sym; 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` // compare match_expr ty with RetTy in `fn foo() -> RetTy`
Node::Item(item) => { Node::Item(item) => {
if let ItemKind::Fn{ .. } = item.kind { if let ItemKind::Fn { .. } = item.kind {
let output = cx let output = cx
.tcx .tcx
.fn_sig(item.owner_id) .fn_sig(item.owner_id)
@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
}); });
}, },
// Example: `5 => 5` // Example: `5 => 5`
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { (PatKind::Expr(pat_expr_expr), ExprKind::Lit(expr_spanned)) => {
if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind { if let PatExprKind::Lit {
lit: pat_spanned,
negated: false,
} = &pat_expr_expr.kind
{
return pat_spanned.node == expr_spanned.node; 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 Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
let lhs_const = if let Some(lhs) = lhs { let lhs_const = if let Some(lhs) = lhs {
ConstEvalCtxt::new(cx).eval(lhs)? ConstEvalCtxt::new(cx).eval_pat_expr(lhs)?
} else { } else {
let min_val_const = ty.numeric_min_val(cx.tcx)?; 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))? mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
}; };
let rhs_const = if let Some(rhs) = rhs { let rhs_const = if let Some(rhs) = rhs {
ConstEvalCtxt::new(cx).eval(rhs)? ConstEvalCtxt::new(cx).eval_pat_expr(rhs)?
} else { } else {
let max_val_const = ty.numeric_max_val(cx.tcx)?; 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))? mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
@ -57,8 +57,10 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
}); });
} }
if let PatKind::Lit(value) = pat.kind { if let PatKind::Expr(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 { return Some(SpannedRange {
span: pat.span, span: pat.span,
node: (value, EndBound::Included(value)), node: (value, EndBound::Included(value)),

View file

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

View file

@ -9,7 +9,7 @@ use rustc_arena::DroplessArena;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{Visitor, walk_pat}; 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, PatExpr, PatExprKind, PatKind, QPath, StmtKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef};
use rustc_span::{Span, sym}; use rustc_span::{Span, sym};
@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
} }
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind
&& let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
&& let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
// scrutinee derives PartialEq and the pattern is a constant. // scrutinee derives PartialEq and the pattern is a constant.
let pat_ref_count = match pat.kind { let pat_ref_count = match pat.kind {
// string literals are already a reference. // string literals are already a reference.
PatKind::Lit(Expr { PatKind::Expr(PatExpr {
kind: ExprKind::Lit(lit), kind: PatExprKind::Lit { lit, negated: false },
.. ..
}) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1,
_ => pat_ref_count, _ => pat_ref_count,
@ -384,7 +384,7 @@ impl<'a> PatState<'a> {
PatKind::Wild PatKind::Wild
| PatKind::Binding(_, _, _, None) | PatKind::Binding(_, _, _, None)
| PatKind::Lit(_) | PatKind::Expr(_)
| PatKind::Range(..) | PatKind::Range(..)
| PatKind::Path(_) | PatKind::Path(_)
| PatKind::Never | PatKind::Never

View file

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

View file

@ -92,7 +92,7 @@ impl EarlyLintPass for UnnestedOrPatterns {
} }
fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
// This is a leaf pattern, so cloning is unprofitable. // This is a leaf pattern, so cloning is unprofitable.
return; return;
} }
@ -228,7 +228,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
// Therefore they are not some form of constructor `C`, // Therefore they are not some form of constructor `C`,
// with which a pattern `C(p_0)` may be formed, // with which a pattern `C(p_0)` may be formed,
// which we would want to join with other `C(p_j)`s. // which we would want to join with other `C(p_j)`s.
Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) Ident(.., None) | Expr(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_)
// Skip immutable refs, as grouping them saves few characters, // Skip immutable refs, as grouping them saves few characters,
// and almost always requires adding parens (increasing noisiness). // and almost always requires adding parens (increasing noisiness).
// In the case of only two patterns, replacement adds net characters. // In the case of only two patterns, replacement adds net characters.

View file

@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{ use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, 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_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.expr(expr); 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<'_>>) { fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind");
macro_rules! kind { macro_rules! kind {
@ -717,17 +738,17 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Guard({pat}, {cond})"); kind!("Guard({pat}, {cond})");
self.pat(pat); self.pat(pat);
self.expr(cond); self.expr(cond);
} },
PatKind::Lit(lit_expr) => { PatKind::Expr(lit_expr) => {
bind!(self, lit_expr); bind!(self, lit_expr);
kind!("Lit({lit_expr})"); kind!("Expr({lit_expr})");
self.expr(lit_expr); self.pat_expr(lit_expr);
}, },
PatKind::Range(start, end, end_kind) => { PatKind::Range(start, end, end_kind) => {
opt_bind!(self, start, end); opt_bind!(self, start, end);
kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
start.if_some(|e| self.expr(e)); start.if_some(|e| self.pat_expr(e));
end.if_some(|e| self.expr(e)); end.if_some(|e| self.pat_expr(e));
}, },
PatKind::Slice(start, middle, end) => { PatKind::Slice(start, middle, end) => {
bind!(self, start, end); bind!(self, start, end);

View file

@ -36,7 +36,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
(Paren(l), _) => eq_pat(l, r), (Paren(l), _) => eq_pat(l, r),
(_, Paren(r)) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r),
(Wild, Wild) | (Rest, Rest) => true, (Wild, Wild) | (Rest, Rest) => true,
(Lit(l), Lit(r)) => eq_expr(l, r), (Expr(l), Expr(r)) => eq_expr(l, r),
(Ident(b1, i1, s1), Ident(b2, i2, s2)) => { (Ident(b1, i1, s1), Ident(b2, i2, s2)) => {
b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat)
}, },

View file

@ -4,7 +4,6 @@
//! executable MIR bodies, so we have to do this instead. //! executable MIR bodies, so we have to do this instead.
#![allow(clippy::float_cmp)] #![allow(clippy::float_cmp)]
use crate::macros::HirNode;
use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::source::{SpanRangeExt, walk_span_to_context};
use crate::{clip, is_direct_expn_of, sext, unsext}; 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_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res}; 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_lexer::tokenize;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::ConstValue; 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. /// Simple constant folding: Insert an expression, get a constant or none.
fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind { match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
ExprKind::DropTemps(e) => self.expr(e), ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id),
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::Block(block, _) => self.block(block), ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(lit) => { ExprKind::Lit(lit) => {
if is_direct_expn_of(e.span, "cfg").is_some() { 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::{ use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName,
Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
TyKind, TraitBoundModifiers, Ty, TyKind,
}; };
use rustc_lexer::{TokenKind, tokenize}; use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> {
li.name == ri.name && self.eq_pat(lp, rp) 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. /// Checks whether two patterns are the same.
fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) { match (&left.kind, &right.kind) {
@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> {
eq eq
}, },
(PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r),
(&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), (&PatKind::Expr(l), &PatKind::Expr(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::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)) => { (&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(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_expr(a, b)) && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& (li == ri) && (li == ri)
}, },
(&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), (&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); // 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<'_>) { pub fn hash_pat(&mut self, pat: &Pat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s); std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind { match pat.kind {
@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
} }
}, },
PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat),
PatKind::Lit(expr) => self.hash_expr(expr), PatKind::Expr(expr) => self.hash_pat_expr(expr),
PatKind::Or(pats) => { PatKind::Or(pats) => {
for pat in pats { for pat in pats {
self.hash_pat(pat); self.hash_pat(pat);
@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Path(ref qpath) => self.hash_qpath(qpath),
PatKind::Range(s, e, i) => { PatKind::Range(s, e, i) => {
if let Some(s) = s { if let Some(s) = s {
self.hash_expr(s); self.hash_pat_expr(s);
} }
if let Some(e) = e { if let Some(e) = e {
self.hash_expr(e); self.hash_pat_expr(e);
} }
std::mem::discriminant(&i).hash(&mut self.s); std::mem::discriminant(&i).hash(&mut self.s);
}, },

View file

@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
}, },
} }
}, },
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
} }
} }

View file

@ -30,8 +30,8 @@ if let StmtKind::Let(local) = stmt.kind
} }
if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind
&& let ExprKind::Let(let_expr) = cond.kind && let ExprKind::Let(let_expr) = cond.kind
&& let PatKind::Lit(lit_expr) = let_expr.pat.kind && let PatKind::Expr(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 LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.init.kind && let ExprKind::Path(ref qpath) = let_expr.init.kind
&& match_qpath(qpath, &["a"]) && match_qpath(qpath, &["a"])

View file

@ -76,8 +76,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While:
// report your lint here // report your lint here
} }
if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) 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 PatKind::Expr(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 LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.kind && let ExprKind::Path(ref qpath) = let_expr.kind
&& match_qpath(qpath, &["a"]) && match_qpath(qpath, &["a"])

View file

@ -4,14 +4,14 @@ if let StmtKind::Let(local) = stmt.kind
&& let ExprKind::Lit(ref lit) = scrutinee.kind && let ExprKind::Lit(ref lit) = scrutinee.kind
&& let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node
&& arms.len() == 3 && arms.len() == 3
&& let PatKind::Lit(lit_expr) = arms[0].pat.kind && let PatKind::Expr(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 && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node
&& arms[0].guard.is_none() && arms[0].guard.is_none()
&& let ExprKind::Lit(ref lit2) = arms[0].body.kind && let ExprKind::Lit(ref lit2) = arms[0].body.kind
&& let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node
&& let PatKind::Lit(lit_expr1) = arms[1].pat.kind && let PatKind::Expr(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 && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node
&& arms[1].guard.is_none() && arms[1].guard.is_none()
&& let ExprKind::Block(block, None) = arms[1].body.kind && let ExprKind::Block(block, None) = arms[1].body.kind

View file

@ -23,8 +23,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
&& match_qpath(qpath, &["Test"]) && match_qpath(qpath, &["Test"])
&& fields.len() == 1 && fields.len() == 1
&& fields[0].ident.as_str() == "field" && fields[0].ident.as_str() == "field"
&& let PatKind::Lit(lit_expr) = fields[0].pat.kind && let PatKind::Expr(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 && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none() && arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind && let ExprKind::Block(block, None) = arm.body.kind
@ -36,8 +36,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
&& match_qpath(qpath, &["TestTuple"]) && match_qpath(qpath, &["TestTuple"])
&& fields.len() == 1 && fields.len() == 1
&& let PatKind::Lit(lit_expr) = fields[0].kind && let PatKind::Expr(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 && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none() && arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind && let ExprKind::Block(block, None) = arm.body.kind

View file

@ -42,7 +42,7 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
| ast::PatKind::Never | ast::PatKind::Never
| ast::PatKind::Wild | ast::PatKind::Wild
| ast::PatKind::Err(_) | ast::PatKind::Err(_)
| ast::PatKind::Lit(_) => true, | ast::PatKind::Expr(_) => true,
ast::PatKind::Ident(_, _, ref pat) => pat.is_none(), ast::PatKind::Ident(_, _, ref pat) => pat.is_none(),
ast::PatKind::Struct(..) ast::PatKind::Struct(..)
| ast::PatKind::MacCall(..) | ast::PatKind::MacCall(..)
@ -293,7 +293,7 @@ impl Rewrite for Pat {
let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?;
rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape)
} }
PatKind::Lit(ref expr) => expr.rewrite_result(context, shape), PatKind::Expr(ref expr) => expr.rewrite_result(context, shape),
PatKind::Slice(ref slice_pat) PatKind::Slice(ref slice_pat)
if context.config.style_edition() <= StyleEdition::Edition2021 => if context.config.style_edition() <= StyleEdition::Edition2021 =>
{ {
@ -530,7 +530,7 @@ pub(crate) fn can_be_overflowed_pat(
ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => { ast::PatKind::Ref(ref p, _) | ast::PatKind::Box(ref p) => {
can_be_overflowed_pat(context, &TuplePatField::Pat(p), len) can_be_overflowed_pat(context, &TuplePatField::Pat(p), len)
} }
ast::PatKind::Lit(ref expr) => can_be_overflowed_expr(context, expr, len), ast::PatKind::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
_ => false, _ => false,
}, },
TuplePatField::Dotdot(..) => false, TuplePatField::Dotdot(..) => false,

View file

@ -569,7 +569,7 @@ fn test_pat() {
c1!(pat, [ &pat ], "&pat"); c1!(pat, [ &pat ], "&pat");
c1!(pat, [ &mut pat ], "&mut pat"); c1!(pat, [ &mut pat ], "&mut pat");
// PatKind::Lit // PatKind::Expr
c1!(pat, [ 1_000_i8 ], "1_000_i8"); c1!(pat, [ 1_000_i8 ], "1_000_i8");
// PatKind::Range // PatKind::Range

View file

@ -6,7 +6,7 @@ macro_rules! enum_number {
fn foo(value: i32) -> Option<$name> { fn foo(value: i32) -> Option<$name> {
match value { match value {
$( $value => Some($name::$variant), )* // PatKind::Lit $( $value => Some($name::$variant), )* // PatKind::Expr
$( $value ..= 42 => Some($name::$variant), )* // PatKind::Range $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range
_ => None _ => None
} }

View file

@ -651,8 +651,8 @@ mod patterns {
let &mut pat; let &mut pat;
} }
/// PatKind::Lit /// PatKind::Expr
fn pat_lit() { fn pat_expr() {
let 1_000_i8; let 1_000_i8;
let -""; let -"";
} }

View file

@ -567,8 +567,8 @@ mod patterns {
fn pat_deref() { let deref!(pat); } fn pat_deref() { let deref!(pat); }
/// PatKind::Ref /// PatKind::Ref
fn pat_ref() { let &pat; let &mut pat; } fn pat_ref() { let &pat; let &mut pat; }
/// PatKind::Lit /// PatKind::Expr
fn pat_lit() { let 1_000_i8; let -""; } fn pat_expr() { let 1_000_i8; let -""; }
/// PatKind::Range /// PatKind::Range
fn pat_range() { let ..1; let 0..; let 0..1; let 0..=1; let -2..=-1; } fn pat_range() { let ..1; let 0..; let 0..1; let 0..=1; let -2..=-1; }
/// PatKind::Slice /// PatKind::Slice

View file

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