From 664ecf1085b1bab9d7444eb54dccfeeabd99446e Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 4 Aug 2020 14:18:11 +0100 Subject: [PATCH] hir: simplify `is_range_literal` This commit simplifies `is_range_literal` by checking for `QPath::LangItem` containing range-related lang items, rather than using a heuristic. Co-authored-by: Matthew Jasper Signed-off-by: David Wood --- src/librustc_hir/hir.rs | 60 ++++++++--------------------- src/librustc_lint/types.rs | 4 +- src/librustc_typeck/check/demand.rs | 2 +- src/test/ui/range/range-1.stderr | 6 ++- 4 files changed, 23 insertions(+), 49 deletions(-) diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index e1ec60bcc0a2c..bfcb506f1326f 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -13,7 +13,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_macros::HashStable_Generic; use rustc_span::def_id::LocalDefId; -use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -1495,58 +1495,28 @@ impl Expr<'_> { /// Checks if the specified expression is a built-in range literal. /// (See: `LoweringContext::lower_expr()`). -/// -/// FIXME(#60607): This function is a hack. If and when we have `QPath::Lang(...)`, -/// we can use that instead as simpler, more reliable mechanism, as opposed to using `SourceMap`. -pub fn is_range_literal(sm: &SourceMap, expr: &Expr<'_>) -> bool { - // Returns whether the given path represents a (desugared) range, - // either in std or core, i.e. has either a `::std::ops::Range` or - // `::core::ops::Range` prefix. - fn is_range_path(path: &Path<'_>) -> bool { - let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect(); - let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect(); - - // "{{root}}" is the equivalent of `::` prefix in `Path`. - if let ["{{root}}", std_core, "ops", range] = segs.as_slice() { - (*std_core == "std" || *std_core == "core") && range.starts_with("Range") - } else { - false - } - }; - - // Check whether a span corresponding to a range expression is a - // range literal, rather than an explicit struct or `new()` call. - fn is_lit(sm: &SourceMap, span: &Span) -> bool { - sm.span_to_snippet(*span).map(|range_src| range_src.contains("..")).unwrap_or(false) - }; - +pub fn is_range_literal(expr: &Expr<'_>) -> bool { match expr.kind { // All built-in range literals but `..=` and `..` desugar to `Struct`s. - ExprKind::Struct(ref qpath, _, _) => { - if let QPath::Resolved(None, ref path) = **qpath { - return is_range_path(&path) && is_lit(sm, &expr.span); - } - } - - // `..` desugars to its struct path. - ExprKind::Path(QPath::Resolved(None, ref path)) => { - return is_range_path(&path) && is_lit(sm, &expr.span); - } + ExprKind::Struct(ref qpath, _, _) => matches!( + **qpath, + QPath::LangItem( + LangItem::Range + | LangItem::RangeTo + | LangItem::RangeFrom + | LangItem::RangeFull + | LangItem::RangeToInclusive, + _, + ) + ), // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. ExprKind::Call(ref func, _) => { - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind { - if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind { - let new_call = segment.ident.name == sym::new; - return is_range_path(&path) && is_lit(sm, &expr.span) && new_call; - } - } + matches!(func.kind, ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, _))) } - _ => {} + _ => false, } - - false } #[derive(Debug, HashStable_Generic)] diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 5891abcfd9cd1..a1c9b05a684dd 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -258,7 +258,7 @@ fn lint_int_literal<'tcx>( let par_id = cx.tcx.hir().get_parent_node(e.hir_id); if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) { if let hir::ExprKind::Struct(..) = par_e.kind { - if is_range_literal(cx.sess().source_map(), par_e) + if is_range_literal(par_e) && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str()) { // The overflowing literal lint was overridden. @@ -317,7 +317,7 @@ fn lint_uint_literal<'tcx>( return; } } - hir::ExprKind::Struct(..) if is_range_literal(cx.sess().source_map(), par_e) => { + hir::ExprKind::Struct(..) if is_range_literal(par_e) => { let t = t.name_str(); if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) { // The overflowing literal lint was overridden. diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 4ea76a4a9e2ab..ad97dbe63d8b3 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -485,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parenthesize if needed (Issue #46756) hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(self.tcx.sess.source_map(), expr) => true, + _ if is_range_literal(expr) => true, _ => false, }; let sugg_expr = if needs_parens { format!("({})", src) } else { src }; diff --git a/src/test/ui/range/range-1.stderr b/src/test/ui/range/range-1.stderr index e179feba7a799..11cb72fa2b6f8 100644 --- a/src/test/ui/range/range-1.stderr +++ b/src/test/ui/range/range-1.stderr @@ -17,9 +17,13 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compi | LL | let range = *arr..; | ^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/ops/range.rs:LL:COL + | +LL | pub struct RangeFrom { + | --- required by this bound in `std::ops::RangeFrom` | = help: the trait `std::marker::Sized` is not implemented for `[{integer}]` - = note: required by `std::ops::RangeFrom` error: aborting due to 3 previous errors