From 3bba8eb24c242418028da5dcd88164f964d5803f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 11 Dec 2024 16:50:45 +0000 Subject: [PATCH] Exhaustively handle expressions in patterns --- compiler/rustc_ast_lowering/src/expr.rs | 51 ++++++++-------- compiler/rustc_ast_lowering/src/index.rs | 8 +++ compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_lowering/src/pat.rs | 51 ++++++++++++---- compiler/rustc_hir/src/hir.rs | 29 ++++++++- compiler/rustc_hir/src/intravisit.rs | 18 +++++- .../src/hir_ty_lowering/mod.rs | 44 +++++++------- compiler/rustc_hir_pretty/src/lib.rs | 20 ++++++- compiler/rustc_hir_typeck/src/expr.rs | 3 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 9 +++ .../rustc_hir_typeck/src/method/suggest.rs | 5 ++ compiler/rustc_hir_typeck/src/pat.rs | 43 ++++++++++---- compiler/rustc_hir_typeck/src/upvar.rs | 9 +-- compiler/rustc_hir_typeck/src/writeback.rs | 22 +++++-- compiler/rustc_middle/src/hir/map/mod.rs | 2 + .../rustc_mir_build/src/thir/pattern/mod.rs | 37 ++++-------- src/tools/clippy/clippy_lints/src/len_zero.rs | 10 +++- .../clippy_lints/src/manual_is_ascii_check.rs | 37 ++++++++---- .../clippy_lints/src/manual_range_patterns.rs | 13 ++-- .../clippy_lints/src/matches/match_bool.rs | 4 +- .../src/matches/match_same_arms.rs | 8 +-- .../src/matches/match_str_case_mismatch.rs | 6 +- .../src/matches/needless_match.rs | 4 +- .../src/matches/overlapping_arms.rs | 6 +- .../src/matches/redundant_pattern_match.rs | 4 +- .../clippy_lints/src/matches/single_match.rs | 6 +- .../clippy_lints/src/string_patterns.rs | 4 +- .../clippy/clippy_lints/src/utils/author.rs | 29 +++++++-- src/tools/clippy/clippy_utils/src/consts.rs | 59 ++++++++++++------- .../clippy/clippy_utils/src/hir_utils.rs | 35 ++++++++--- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 2 +- 31 files changed, 393 insertions(+), 186 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 2ad0ff3200e74..fe8ccca072bfb 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -103,17 +103,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(c) => { - let c = self.with_new_scopes(c.value.span, |this| { - let def_id = this.local_def_id(c.id); - hir::ConstBlock { - def_id, - hir_id: this.lower_node_id(c.id), - body: this.lower_const_body(c.value.span, Some(&c.value)), - } - }); - hir::ExprKind::ConstBlock(c) - } + ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(c)), ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length_to_const_arg(count); @@ -154,18 +144,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let ohs = self.lower_expr(ohs); hir::ExprKind::Unary(op, ohs) } - ExprKind::Lit(token_lit) => { - let lit_kind = match LitKind::from_token_lit(*token_lit) { - Ok(lit_kind) => lit_kind, - Err(err) => { - let guar = - report_lit_error(&self.tcx.sess.psess, err, *token_lit, e.span); - LitKind::Err(guar) - } - }; - let lit = self.arena.alloc(respan(self.lower_span(e.span), lit_kind)); - hir::ExprKind::Lit(lit) - } + ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)), ExprKind::IncludedBytes(bytes) => { let lit = self.arena.alloc(respan( self.lower_span(e.span), @@ -396,6 +375,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 { + let lit_kind = match LitKind::from_token_lit(*token_lit) { + Ok(lit_kind) => lit_kind, + Err(err) => { + let guar = report_lit_error(&self.tcx.sess.psess, err, *token_lit, span); + LitKind::Err(guar) + } + }; + self.arena.alloc(respan(self.lower_span(span), lit_kind)) + } + fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { match u { UnOp::Deref => hir::UnOp::Deref, diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index c3ff7b4b89722..c92f149d24cae 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -209,6 +209,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_pat_lit(&mut self, lit: &'hir PatLit<'hir>) { + self.insert(lit.span, lit.hir_id, Node::PatLit(lit)); + + self.with_parent(lit.hir_id, |this| { + intravisit::walk_pat_lit(this, lit); + }); + } + fn visit_pat_field(&mut self, field: &'hir PatField<'hir>) { self.insert(field.span, field.hir_id, Node::PatField(field)); self.with_parent(field.hir_id, |this| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bac3f974ccae9..8784d729c7cb4 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -35,6 +35,7 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(rustdoc_internals)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index c4bae084a3f8c..25702e3c835cf 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -1,10 +1,13 @@ +use std::sync::Arc; + use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_middle::span_bug; use rustc_span::Span; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Spanned, respan}; use rustc_span::symbol::Ident; use super::errors::{ @@ -360,24 +363,46 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // } // m!(S); // ``` - fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> { - match &expr.kind { - ExprKind::Lit(..) - | ExprKind::ConstBlock(..) - | ExprKind::IncludedBytes(..) - | ExprKind::Err(_) - | ExprKind::Dummy => {} - ExprKind::Path(..) if allow_paths => {} - ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} + fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::PatLit<'hir> { + let err = |guar| hir::PatLitKind::Lit { + lit: self.arena.alloc(respan(self.lower_span(expr.span), LitKind::Err(guar))), + negated: false, + }; + let kind = match &expr.kind { + ExprKind::Lit(lit) => { + hir::PatLitKind::Lit { lit: self.lower_lit(lit, expr.span), negated: false } + } + ExprKind::ConstBlock(c) => hir::PatLitKind::ConstBlock(self.lower_const_block(c)), + ExprKind::IncludedBytes(bytes) => hir::PatLitKind::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::PatLitKind::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::PatLitKind::Lit { lit: self.lower_lit(lit, expr.span), negated: true } + } _ => { let pattern_from_macro = expr.is_approximately_pattern(); let guar = self.dcx().emit_err(ArbitraryExpressionInPattern { span: expr.span, pattern_from_macro_note: pattern_from_macro, }); - return self.arena.alloc(self.expr_err(expr.span, guar)); + err(guar) } - } - self.lower_expr(expr) + }; + self.arena.alloc(hir::PatLit { hir_id: self.lower_node_id(expr.id), span: expr.span, kind }) } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 365e4cbb55676..10fc150017842 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1270,6 +1270,26 @@ impl fmt::Debug for DotDotPos { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct PatLit<'hir> { + pub hir_id: HirId, + pub span: Span, + pub kind: PatLitKind<'hir>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum PatLitKind<'hir> { + Lit { + lit: &'hir Lit, + // FIXME: move this into `Lit` and handle negated literal expressions + // once instead of matching on unop neg expressions everywhere. + negated: bool, + }, + ConstBlock(ConstBlock), + /// A path pattern for a unit struct/variant or a (maybe-associated) constant. + Path(QPath<'hir>), +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum PatKind<'hir> { /// Represents a wildcard pattern (i.e., `_`). @@ -1315,10 +1335,10 @@ pub enum PatKind<'hir> { Ref(&'hir Pat<'hir>, Mutability), /// A literal. - Lit(&'hir Expr<'hir>), + Lit(&'hir PatLit<'hir>), /// A range pattern (e.g., `1..=2` or `1..2`). - Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd), + Range(Option<&'hir PatLit<'hir>>, Option<&'hir PatLit<'hir>>, RangeEnd), /// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`. /// @@ -3833,6 +3853,10 @@ pub enum Node<'hir> { OpaqueTy(&'hir OpaqueTy<'hir>), Pat(&'hir Pat<'hir>), PatField(&'hir PatField<'hir>), + /// Needed as its own node with its own HirId for tracking + /// the unadjusted type of literals within patterns + /// (e.g. byte str literals not being of slice type). + PatLit(&'hir PatLit<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), LetStmt(&'hir LetStmt<'hir>), @@ -3889,6 +3913,7 @@ impl<'hir> Node<'hir> { | Node::Block(..) | Node::Ctor(..) | Node::Pat(..) + | Node::PatLit(..) | Node::Arm(..) | Node::LetStmt(..) | Node::Crate(..) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9abb0870bf0ff..7a0b1a069b2a8 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -343,6 +343,9 @@ pub trait Visitor<'v>: Sized { fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result { walk_pat_field(self, f) } + fn visit_pat_lit(&mut self, lit: &'v PatLit<'v>) -> Self::Result { + walk_pat_lit(self, lit) + } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { walk_anon_const(self, c) } @@ -686,10 +689,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_pat, optional_subpattern); } - PatKind::Lit(ref expression) => try_visit!(visitor.visit_expr(expression)), + PatKind::Lit(lit) => try_visit!(visitor.visit_pat_lit(lit)), PatKind::Range(ref lower_bound, ref upper_bound, _) => { - visit_opt!(visitor, visit_expr, lower_bound); - visit_opt!(visitor, visit_expr, upper_bound); + visit_opt!(visitor, visit_pat_lit, lower_bound); + visit_opt!(visitor, visit_pat_lit, upper_bound); } PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => { @@ -707,6 +710,15 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<' visitor.visit_pat(field.pat) } +pub fn walk_pat_lit<'v, V: Visitor<'v>>(visitor: &mut V, lit: &'v PatLit<'v>) -> V::Result { + try_visit!(visitor.visit_id(lit.hir_id)); + match &lit.kind { + PatLitKind::Lit { .. } => V::Result::output(), + PatLitKind::ConstBlock(c) => visitor.visit_inline_const(c), + PatLitKind::Path(qpath) => visitor.visit_qpath(qpath, lit.hir_id, lit.span), + } +} + pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) -> V::Result { try_visit!(visitor.visit_id(constant.hir_id)); visitor.visit_nested_body(constant.body) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 09e46517cea8b..f79e4a6941f0a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2430,17 +2430,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_error(tcx, err) } hir::PatKind::Range(start, end, include_end) => { - let expr_to_const = |expr: &'tcx hir::Expr<'tcx>| -> ty::Const<'tcx> { - let (expr, neg) = match expr.kind { - hir::ExprKind::Unary(hir::UnOp::Neg, negated) => { - (negated, Some((expr.hir_id, expr.span))) - } - _ => (expr, None), - }; - let (c, c_ty) = match &expr.kind { - hir::ExprKind::Lit(lit) => { + let expr_to_const = |expr: &'tcx hir::PatLit<'tcx>| -> ty::Const<'tcx> { + let (c, c_ty) = match expr.kind { + hir::PatLitKind::Lit { lit, negated } => { let lit_input = - LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() }; + LitToConstInput { lit: &lit.node, ty, neg: negated }; let ct = match tcx.lit_to_const(lit_input) { Ok(c) => c, Err(LitToConstError::Reported(err)) => { @@ -2451,36 +2445,40 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (ct, ty) } - hir::ExprKind::Path(hir::QPath::Resolved( + hir::PatLitKind::Path(hir::QPath::Resolved( _, path @ &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }, )) => { - let _ = self.prohibit_generic_args( + match self.prohibit_generic_args( path.segments.iter(), GenericsArgsErrExtend::Param(def_id), - ); - let ty = tcx - .type_of(def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - let ct = self.lower_const_param(def_id, expr.hir_id); - (ct, ty) + ) { + Ok(()) => { + let ty = tcx + .type_of(def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + let ct = self.lower_const_param(def_id, expr.hir_id); + (ct, ty) + } + Err(guar) => ( + ty::Const::new_error(tcx, guar), + Ty::new_error(tcx, guar), + ), + } } _ => { let err = tcx .dcx() - .emit_err(crate::errors::NonConstRange { span: expr.span }); + .emit_err(crate::errors::NonConstRange { span: pat.span }); (ty::Const::new_error(tcx, err), Ty::new_error(tcx, err)) } }; self.record_ty(expr.hir_id, c_ty, expr.span); - if let Some((id, span)) = neg { - self.record_ty(id, c_ty, span); - } c }; diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a74e36c45c6ef..075391d0bcaa4 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -98,6 +98,7 @@ impl<'a> State<'a> { Node::OpaqueTy(o) => self.print_opaque_ty(o), Node::Pat(a) => self.print_pat(a), Node::PatField(a) => self.print_patfield(a), + Node::PatLit(a) => self.print_pat_lit(a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), @@ -1715,6 +1716,19 @@ impl<'a> State<'a> { } } + fn print_pat_lit(&mut self, lit: &hir::PatLit<'_>) { + match &lit.kind { + hir::PatLitKind::Lit { lit, negated } => { + if *negated { + self.word("-"); + } + self.print_literal(lit); + } + hir::PatLitKind::ConstBlock(c) => self.print_inline_const(c), + hir::PatLitKind::Path(qpath) => self.print_qpath(qpath, true), + } + } + fn print_pat(&mut self, pat: &hir::Pat<'_>) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); @@ -1832,17 +1846,17 @@ impl<'a> State<'a> { self.pclose(); } } - PatKind::Lit(e) => self.print_expr(e), + PatKind::Lit(e) => self.print_pat_lit(e), PatKind::Range(begin, end, end_kind) => { if let Some(expr) = begin { - self.print_expr(expr); + self.print_pat_lit(expr); } match end_kind { RangeEnd::Included => self.word("..."), RangeEnd::Excluded => self.word(".."), } if let Some(expr) = end { - self.print_expr(expr); + self.print_pat_lit(expr); } } PatKind::Slice(before, slice, after) => { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index c34739de967bf..9e4e285580b4b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -421,6 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::AssocItemConstraint(_) | hir::Node::TraitRef(_) | hir::Node::PatField(_) + | hir::Node::PatLit(_) | hir::Node::LetStmt(_) | hir::Node::Synthetic | hir::Node::Err(_) @@ -1693,7 +1694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_const_block( + pub(super) fn check_expr_const_block( &self, block: &'tcx hir::ConstBlock, expected: Expectation<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index a6276a2e94b6b..ee0125a40e9ac 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1017,6 +1017,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id, span, ), + Res::Err => { + return ( + Ty::new_error( + tcx, + tcx.dcx().span_delayed_bug(span, "could not resolve path {:?}"), + ), + res, + ); + } _ => bug!("instantiate_value_path on {:?}", res), }; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6b1a288510ac1..0148aebaf9b27 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -171,6 +171,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, .. }) + | hir::Node::PatLit(&hir::PatLit { + kind: hir::PatLitKind::Path(QPath::TypeRelative(rcvr, segment)), + span, + .. + }) | hir::Node::Pat(&hir::Pat { kind: hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index aa853d5e15457..e56c2658bdfab 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,6 +27,7 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; +use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -368,7 +369,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. // // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() { + PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_pat_lit_unadjusted(lt)).kind() { ty::Ref(..) => AdjustMode::Pass, _ => AdjustMode::Peel, }, @@ -461,10 +462,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, def_br, max_ref_mutbl) } + fn check_pat_lit_unadjusted(&self, lt: &'tcx hir::PatLit<'tcx>) -> Ty<'tcx> { + let ty = match <.kind { + rustc_hir::PatLitKind::Lit { lit, .. } => { + self.check_expr_lit(lit, Expectation::NoExpectation) + } + rustc_hir::PatLitKind::ConstBlock(c) => { + self.check_expr_const_block(c, Expectation::NoExpectation) + } + rustc_hir::PatLitKind::Path(qpath) => { + let (res, opt_ty, segments) = + self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span); + self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0 + } + }; + self.write_ty(lt.hir_id, ty); + ty + } + fn check_pat_lit( &self, span: Span, - lt: &hir::Expr<'tcx>, + lt: &hir::PatLit<'tcx>, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { @@ -475,7 +494,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically-sized byte arrays. let mut pat_ty = ty; - if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind { + if let hir::PatLitKind::Lit { + lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, .. + } = lt.kind + { let expected = self.structurally_resolve_type(span, expected); if let ty::Ref(_, inner_ty, _) = *expected.kind() && self.try_structurally_resolve_type(span, inner_ty).is_slice() @@ -492,7 +514,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if self.tcx.features().string_deref_patterns() - && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind + && let hir::PatLitKind::Lit { lit: Spanned { node: ast::LitKind::Str(..), .. }, .. } = + lt.kind { let tcx = self.tcx; let expected = self.resolve_vars_if_possible(expected); @@ -533,15 +556,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_range( &self, span: Span, - lhs: Option<&'tcx hir::Expr<'tcx>>, - rhs: Option<&'tcx hir::Expr<'tcx>>, + lhs: Option<&'tcx hir::PatLit<'tcx>>, + rhs: Option<&'tcx hir::PatLit<'tcx>>, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { - let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { + let calc_side = |opt_expr: Option<&'tcx hir::PatLit<'tcx>>| match opt_expr { None => None, Some(expr) => { - let ty = self.check_expr(expr); + let ty = self.check_pat_lit_unadjusted(expr); // Check that the end-point is possibly of numeric or char type. // The early check here is not for correctness, but rather better // diagnostics (e.g. when `&str` is being matched, `expected` will @@ -1780,9 +1803,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else if inexistent_fields.len() == 1 { match pat_field.pat.kind { - PatKind::Lit(expr) + PatKind::Lit(_) if !self.may_coerce( - self.typeck_results.borrow().expr_ty(expr), + self.typeck_results.borrow().node_type(pat_field.pat.hir_id), self.field_ty(field.span, field_def, args), ) => {} _ => { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index b0c020dd7cbed..cce758bfb3959 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -147,15 +147,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { self.visit_body(body); self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); } - hir::ExprKind::ConstBlock(anon_const) => { - let body = self.fcx.tcx.hir().body(anon_const.body); - self.visit_body(body); - } _ => {} } intravisit::walk_expr(self, expr); } + + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + let body = self.fcx.tcx.hir().body(c.body); + self.visit_body(body); + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index e17a68c869215..9bf604f5c1554 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -247,6 +247,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); + } } /////////////////////////////////////////////////////////////////////////// @@ -276,11 +283,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { self.visit_field_id(e.hir_id); } - hir::ExprKind::ConstBlock(anon_const) => { - self.visit_node_id(e.span, anon_const.hir_id); - - let body = self.tcx().hir().body(anon_const.body); - self.visit_body(body); + hir::ExprKind::ConstBlock(ref anon_const) => { + self.visit_const_block(e.span, anon_const); } _ => {} } @@ -336,6 +340,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_pat(self, p); } + fn visit_pat_lit(&mut self, lit: &'tcx hir::PatLit<'tcx>) { + self.visit_node_id(lit.span, lit.hir_id); + if let hir::PatLitKind::ConstBlock(c) = &lit.kind { + self.visit_const_block(lit.span, c); + } + intravisit::walk_pat_lit(self, lit); + } + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.hir_id); diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0c701c834f27e..667b3d1ccdf43 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -939,6 +939,7 @@ impl<'hir> Map<'hir> { Node::OpaqueTy(op) => op.span, Node::Pat(pat) => pat.span, Node::PatField(field) => field.span, + Node::PatLit(lit) => lit.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)), @@ -1210,6 +1211,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::OpaqueTy(_) => node_str("opaque type"), Node::Pat(_) => node_str("pat"), Node::PatField(_) => node_str("pattern field"), + Node::PatLit(_) => node_str("pattern literal"), Node::Param(_) => node_str("param"), Node::Arm(_) => node_str("arm"), Node::Block(_) => node_str("block"), diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2dbc8b7b57385..e01827125e375 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -140,7 +140,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range_endpoint( &mut self, - expr: Option<&'tcx hir::Expr<'tcx>>, + expr: Option<&'tcx hir::PatLit<'tcx>>, ) -> Result< (Option>, Option>, Option), ErrorGuaranteed, @@ -186,13 +186,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// This is only called when the range is already known to be malformed. fn error_on_literal_overflow( &self, - expr: Option<&'tcx hir::Expr<'tcx>>, + expr: Option<&'tcx hir::PatLit<'tcx>>, ty: Ty<'tcx>, ) -> Result<(), ErrorGuaranteed> { - use hir::{ExprKind, UnOp}; use rustc_ast::ast::LitKind; - let Some(mut expr) = expr else { + let Some(expr) = expr else { return Ok(()); }; let span = expr.span; @@ -200,12 +199,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // We need to inspect the original expression, because if we only inspect the output of // `eval_bits`, an overflowed value has already been wrapped around. // We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint. - let mut negated = false; - if let ExprKind::Unary(UnOp::Neg, sub_expr) = expr.kind { - negated = true; - expr = sub_expr; - } - let ExprKind::Lit(lit) = expr.kind else { + let hir::PatLitKind::Lit { lit, negated } = expr.kind else { return Ok(()); }; let LitKind::Int(lit_val, _) = lit.node else { @@ -234,8 +228,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range( &mut self, - lo_expr: Option<&'tcx hir::Expr<'tcx>>, - hi_expr: Option<&'tcx hir::Expr<'tcx>>, + lo_expr: Option<&'tcx hir::PatLit<'tcx>>, + hi_expr: Option<&'tcx hir::PatLit<'tcx>>, end: RangeEnd, ty: Ty<'tcx>, span: Span, @@ -644,25 +638,18 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// The special case for negation exists to allow things like `-128_i8` /// which would overflow if we tried to evaluate `128_i8` and then negate /// afterwards. - fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { - let (lit, neg) = match expr.kind { - hir::ExprKind::Path(ref qpath) => { + fn lower_lit(&mut self, expr: &'tcx hir::PatLit<'tcx>) -> PatKind<'tcx> { + let (lit, neg) = match &expr.kind { + hir::PatLitKind::Path(qpath) => { return self.lower_path(qpath, expr.hir_id, expr.span).kind; } - hir::ExprKind::ConstBlock(ref anon_const) => { + hir::PatLitKind::ConstBlock(anon_const) => { return self.lower_inline_const(anon_const, expr.hir_id, expr.span); } - hir::ExprKind::Lit(ref lit) => (lit, false), - hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => { - let hir::ExprKind::Lit(ref lit) = expr.kind else { - span_bug!(expr.span, "not a literal: {:?}", expr); - }; - (lit, true) - } - _ => span_bug!(expr.span, "not a literal: {:?}", expr), + hir::PatLitKind::Lit { lit, negated } => (lit, *negated), }; - let ct_ty = self.typeck_results.expr_ty(expr); + let ct_ty = self.typeck_results.node_type(expr.hir_id); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; match self.tcx.at(expr.span).lit_to_const(lit_input) { Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind, diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 3ea758e176f0b..cf612bf40dfa0 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, - TraitItemRef, TyKind, + TraitItemRef, TyKind, PatLitKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -163,7 +163,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { PatKind::Slice([], None, []) => true, - PatKind::Lit(lit) if is_empty_string(lit) => true, + PatKind::Lit(lit) => match lit.kind { + PatLitKind::Lit { lit, .. } => match lit.node { + LitKind::Str(lit, _) => lit.as_str().is_empty(), + _ => false, + }, + _ => false, + }, _ => false, } && !expr.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 3f01f3cf30aea..b5999f9275757 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; +use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatLit, PatLitKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { { let arg = peel_ref_operators(cx, arg); let ty_sugg = get_ty_sugg(cx, arg, start); - let range = check_range(start, end); + let range = check_expr_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } } @@ -196,19 +196,34 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { } } -fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { +fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { if let ExprKind::Lit(start_lit) = &start.kind && let ExprKind::Lit(end_lit) = &end.kind { - match (&start_lit.node, &end_lit.node) { - (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, - (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, - (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, - (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, - (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, - _ => CharRange::Otherwise, - } + check_lit_range(start_lit, end_lit) + } else { + CharRange::Otherwise + } +} + + +fn check_range(start: &PatLit<'_>, end: &PatLit<'_>) -> CharRange { + if let PatLitKind::Lit{ lit: start_lit, negated: false } = &start.kind + && let PatLitKind::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, + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index ffa3eacf3544f..bf324a9e5219e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; +use rustc_hir::{PatLit, PatLitKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -38,14 +38,13 @@ declare_clippy_lint! { } declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); -fn expr_as_i128(expr: &Expr<'_>) -> Option { - if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { - expr_as_i128(expr).map(|num| -num) - } else if let ExprKind::Lit(lit) = expr.kind +fn expr_as_i128(expr: &PatLit<'_>) -> Option { + if let PatLitKind::Lit { lit, negated } = expr.kind && let LitKind::Int(num, _) = lit.node { // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. - num.get().try_into().ok() + let n = i128::try_from(num.get()).ok()?; + Some(if negated { -n } else { n }) } else { None } @@ -58,7 +57,7 @@ struct Num { } impl Num { - fn new(expr: &Expr<'_>) -> Option { + fn new(expr: &PatLit<'_>) -> Option { Some(Self { val: expr_as_i128(expr)?, span: expr.span, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index 69105ff0d5c7a..d44ac6330431c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet}; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, PatLitKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -22,7 +22,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] if arms.len() == 2 { // no guards let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { - if let ExprKind::Lit(lit) = arm_bool.kind { + if let PatLitKind::Lit { lit, .. } = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 20984bc40caf3..6403afc9cc38c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, PatLitKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -315,7 +315,7 @@ impl<'a> NormalizedPat<'a> { }, PatKind::Lit(e) => match &e.kind { // TODO: Handle negative integers. They're currently treated as a wild match. - ExprKind::Lit(lit) => match lit.node { + PatLitKind::Lit{ lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), @@ -332,7 +332,7 @@ impl<'a> NormalizedPat<'a> { let start = match start { None => 0, Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatLitKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => val.get(), LitKind::Char(val) => val.into(), LitKind::Byte(val) => val.into(), @@ -344,7 +344,7 @@ impl<'a> NormalizedPat<'a> { let (end, bounds) = match end { None => (u128::MAX, RangeEnd::Included), Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatLitKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => (val.get(), bounds), LitKind::Char(val) => (val.into(), bounds), LitKind::Byte(val) => (val.into(), bounds), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 1267fc9d0a531..54727c8a044bc 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, PatLit, PatLitKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + if let PatKind::Lit(PatLit { + kind: PatLitKind::Lit { lit, negated: false }, .. }) = arm.pat.kind && let LitKind::Str(symbol, _) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 6f7d690264049..07c269bb5d4bc 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath, PatLitKind}; use rustc_lint::LateContext; use rustc_span::sym; @@ -188,7 +188,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }, // Example: `5 => 5` (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { - if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind { + if let PatLitKind::Lit { lit: pat_spanned, negated: false } = &pat_lit_expr.kind { return pat_spanned.node == expr_spanned.node; } }, diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 856311899f268..05d75637babc4 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = if let Some(lhs) = lhs { - ConstEvalCtxt::new(cx).eval(lhs)? + ConstEvalCtxt::new(cx).eval_pat_lit(lhs)? } else { let min_val_const = ty.numeric_min_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; let rhs_const = if let Some(rhs) = rhs { - ConstEvalCtxt::new(cx).eval(rhs)? + ConstEvalCtxt::new(cx).eval_pat_lit(rhs)? } else { let max_val_const = ty.numeric_max_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? @@ -58,7 +58,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) } if let PatKind::Lit(value) = pat.kind { - let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; + let value = ConstEvalCtxt::new(cx).eval_pat_lit(value)?.int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 264458a86ef49..52ff2eb1ff3ad 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, PatLitKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -75,7 +75,7 @@ fn find_match_true<'tcx>( message: &'static str, ) { if let PatKind::Lit(lit) = pat.kind - && let ExprKind::Lit(lit) = lit.kind + && let PatLitKind::Lit{ lit, negated: false } = lit.kind && let LitKind::Bool(pat_is_true) = lit.node { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 95a4bf6f60de3..33d3cd5e9a054 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind, PatLit, PatLitKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{Span, sym}; @@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { // string literals are already a reference. - PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + PatKind::Lit(PatLit { + kind: PatLitKind::Lit { lit, negated: false }, .. }) if lit.node.is_str() => pat_ref_count + 1, _ => pat_ref_count, diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 0d85b1b858a4f..c29bdbd8cf6ae 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatKind, PatLitKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< return ControlFlow::Break(()); } if arm.pat.walk_short(|pat| match pat.kind { - PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + PatKind::Lit(expr) if let PatLitKind::Lit { lit, negated: false } = expr.kind => { if let LitKind::Char(_) = lit.node { set_char_spans.push(lit.span); } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 311ed427cb910..2d26045652e0a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, - ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr, + ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr, PatLitKind }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -640,6 +640,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } + fn pat_lit(&self, lit: &Binding<&hir::PatLit<'_>>) { + let kind = |kind| chain!(self, "let PatLitKind::{kind} = {lit}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + match lit.value.kind { + PatLitKind::Lit { lit, negated } => { + bind!(self, lit); + bind!(self, negated); + kind!("Lit{{ref {lit}, {negated} }}"); + self.lit(lit); + } + PatLitKind::ConstBlock(_) => kind!("ConstBlock(_)"), + PatLitKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + } + } + } + fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { @@ -712,13 +733,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Lit(lit_expr) => { bind!(self, lit_expr); kind!("Lit({lit_expr})"); - self.expr(lit_expr); + self.pat_lit(lit_expr); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.expr(e)); - end.if_some(|e| self.expr(e)); + start.if_some(|e| self.pat_lit(e)); + end.if_some(|e| self.pat_lit(e)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 43ddf06730dd2..9e0b2048d2300 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -4,7 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use crate::macros::HirNode; use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatLit, PatLitKind}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + pub fn eval_pat_lit(&self, pat_lit: &PatLit<'_>) -> Option> { + match &pat_lit.kind { + PatLitKind::Lit { lit, negated } => { + let ty = self.typeck.node_type_opt(pat_lit.hir_id); + let val = lit_to_mir_constant(&lit.node, ty); + if *negated { + self.constant_negate(&val, ty?) + } else { + Some(val) + } + } + PatLitKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + PatLitKind::Path(qpath) => self.qpath(qpath, pat_lit.hir_id), + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core + } else { + false + }; + self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; + // If source is already Constant we wouldn't want to override it with CoreConstant + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); + Some(result) + }) + } + /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) - }, + ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 4be4340862d2a..541753f145623 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -10,7 +10,7 @@ use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty, - TyKind, StructTailExpr, + TyKind, StructTailExpr, PatLit, PatLitKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -484,6 +484,15 @@ impl HirEqInterExpr<'_, '_, '_> { li.name == ri.name && self.eq_pat(lp, rp) } + fn eq_pat_lit(&mut self, left: &PatLit<'_>, right: &PatLit<'_>) -> bool { + match (&left.kind, &right.kind) { + (&PatLitKind::Lit { lit: left, negated: left_neg }, &PatLitKind::Lit { lit: right, negated: right_neg}) => left_neg == right_neg && left.node == right.node, + (PatLitKind::ConstBlock(left), PatLitKind::ConstBlock(right)) => self.eq_body(left.body, right.body), + (PatLitKind::Path(left), PatLitKind::Path(right)) => self.eq_qpath(left, right), + (PatLitKind::Lit {..} | PatLitKind::ConstBlock(..) | PatLitKind::Path(..), _) => false, + } + } + /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { @@ -502,11 +511,11 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), - (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), + (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_pat_lit(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { - both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b)) - && both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b)) + both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_lit(a, b)) + && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_lit(a, b)) && (li == ri) }, (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), @@ -1062,6 +1071,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } + pub fn hash_pat_lit(&mut self, lit: &PatLit<'_>) { + std::mem::discriminant(&lit.kind).hash(&mut self.s); + match &lit.kind { + PatLitKind::Lit { lit, negated } => { + lit.node.hash(&mut self.s); + negated.hash(&mut self.s); + } + PatLitKind::ConstBlock(c) => self.hash_body(c.body), + PatLitKind::Path(qpath) => self.hash_qpath(qpath), + } + } + pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { @@ -1073,7 +1094,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), - PatKind::Lit(expr) => self.hash_expr(expr), + PatKind::Lit(expr) => self.hash_pat_lit(expr), PatKind::Or(pats) => { for pat in pats { self.hash_pat(pat); @@ -1082,10 +1103,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { - self.hash_expr(s); + self.hash_pat_lit(s); } if let Some(e) = e { - self.hash_expr(e); + self.hash_pat_lit(e); } std::mem::discriminant(&i).hash(&mut self.s); }, diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index c424b1afa3465..43aa93c83bd31 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -14,4 +14,4 @@ extern crate std; fn main() ({ } as ()) -fn foo((-(128 as i8) as i8)...(127 as i8): i8) ({ } as ()) +fn foo(-128...127: i8) ({ } as ())