Skip to content

Commit

Permalink
hir: simplify is_range_literal
Browse files Browse the repository at this point in the history
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 <mjjasper1@gmail.com>
Signed-off-by: David Wood <david@davidtw.co>
  • Loading branch information
davidtwco and matthewjasper committed Aug 16, 2020
1 parent 1e2f350 commit 664ecf1
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 49 deletions.
60 changes: 15 additions & 45 deletions src/librustc_hir/hir.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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)]
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_lint/types.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/demand.rs
Expand Up @@ -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 };
Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/range/range-1.stderr
Expand Up @@ -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<Idx> {
| --- 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

Expand Down

0 comments on commit 664ecf1

Please sign in to comment.