1
Fork 0

Pull is_range_literal out into lowering

This commit is contained in:
varkor 2019-04-27 12:44:32 +01:00
parent 5b7baa53c9
commit 2cefd9987b
2 changed files with 64 additions and 66 deletions

View file

@ -5400,3 +5400,65 @@ fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body>) -> Vec<hir::BodyId> {
body_ids.sort_by_key(|b| bodies[b].value.span);
body_ids
}
/// This function checks if the specified expression is a built-in range literal.
/// (See: `LoweringContext::lower_expr()`).
pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
use hir::{Path, QPath, ExprKind, TyKind};
// We support `::std::ops::Range` and `::core::ops::Range` prefixes.
let is_range_path = |path: &Path| {
let mut segs = path.segments.iter().map(|seg| seg.ident.as_str());
if let (Some(root), Some(std_core), Some(ops), Some(range), None) =
(segs.next(), segs.next(), segs.next(), segs.next(), segs.next())
{
// "{{root}}" is the equivalent of `::` prefix in `Path`.
root == "{{root}}" && (std_core == "std" || std_core == "core")
&& ops == "ops" && range.starts_with("Range")
} else {
false
}
};
let span_is_range_literal = |span: &Span| {
// Check whether a span corresponding to a range expression
// is a range literal, rather than an explicit struct or `new()` call.
let source_map = sess.source_map();
let end_point = source_map.end_point(*span);
if let Ok(end_string) = source_map.span_to_snippet(end_point) {
!(end_string.ends_with("}") || end_string.ends_with(")"))
} else {
false
}
};
match expr.node {
// 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) && span_is_range_literal(&expr.span);
}
}
// `..` desugars to its struct path.
ExprKind::Path(QPath::Resolved(None, ref path)) => {
return is_range_path(&path) && span_is_range_literal(&expr.span);
}
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
ExprKind::Call(ref func, _) => {
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node {
if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node {
let call_to_new = segment.ident.as_str() == "new";
return is_range_path(&path) && span_is_range_literal(&expr.span) && call_to_new;
}
}
}
_ => {}
}
false
}

View file

@ -7,7 +7,7 @@ use syntax_pos::Span;
use rustc::hir;
use rustc::hir::def::Def;
use rustc::hir::Node;
use rustc::hir::print;
use rustc::hir::{print, lowering::is_range_literal};
use rustc::ty::{self, Ty, AssociatedItem};
use rustc::ty::adjustment::AllowTwoPhase;
use errors::{Applicability, DiagnosticBuilder};
@ -380,7 +380,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
hir::ExprKind::Cast(_, _) |
hir::ExprKind::Binary(_, _, _) => true,
// parenthesize borrows of range literals (Issue #54505)
_ if self.is_range_literal(expr) => true,
_ if is_range_literal(self.tcx.sess, expr) => true,
_ => false,
};
let sugg_expr = if needs_parens {
@ -479,70 +479,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
None
}
/// This function checks if the specified expression is a built-in range literal.
/// (See: `LoweringContext::lower_expr()` in `src/librustc/hir/lowering.rs`).
fn is_range_literal(&self, expr: &hir::Expr) -> bool {
use hir::{Path, QPath, ExprKind, TyKind};
// We support `::std::ops::Range` and `::core::ops::Range` prefixes
let is_range_path = |path: &Path| {
let mut segs = path.segments.iter()
.map(|seg| seg.ident.as_str());
if let (Some(root), Some(std_core), Some(ops), Some(range), None) =
(segs.next(), segs.next(), segs.next(), segs.next(), segs.next())
{
// "{{root}}" is the equivalent of `::` prefix in Path
root == "{{root}}" && (std_core == "std" || std_core == "core")
&& ops == "ops" && range.starts_with("Range")
} else {
false
}
};
let span_is_range_literal = |span: &Span| {
// Check whether a span corresponding to a range expression
// is a range literal, rather than an explicit struct or `new()` call.
let source_map = self.tcx.sess.source_map();
let end_point = source_map.end_point(*span);
if let Ok(end_string) = source_map.span_to_snippet(end_point) {
!(end_string.ends_with("}") || end_string.ends_with(")"))
} else {
false
}
};
match expr.node {
// All built-in range literals but `..=` and `..` desugar to Structs
ExprKind::Struct(ref qpath, _, _) => {
if let QPath::Resolved(None, ref path) = **qpath {
return is_range_path(&path) && span_is_range_literal(&expr.span);
}
}
// `..` desugars to its struct path
ExprKind::Path(QPath::Resolved(None, ref path)) => {
return is_range_path(&path) && span_is_range_literal(&expr.span);
}
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`
ExprKind::Call(ref func, _) => {
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node {
if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node {
let call_to_new = segment.ident.as_str() == "new";
return is_range_path(&path) && span_is_range_literal(&expr.span)
&& call_to_new;
}
}
}
_ => {}
}
false
}
pub fn check_for_cast(
&self,
err: &mut DiagnosticBuilder<'tcx>,