Skip to content

Commit af2d3f0

Browse files
authored
Rollup merge of rust-lang#60330 - varkor:range-endpoint-overflow-lint, r=estebank
Suggest using an inclusive range instead of an exclusive range when the endpoint overflows by 1 Fixes rust-lang#47213.
2 parents 24da0e5 + b7711bf commit af2d3f0

19 files changed

+551
-399
lines changed

src/librustc/hir/lowering.rs

+62
Original file line numberDiff line numberDiff line change
@@ -5400,3 +5400,65 @@ fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body>) -> Vec<hir::BodyId> {
54005400
body_ids.sort_by_key(|b| bodies[b].value.span);
54015401
body_ids
54025402
}
5403+
5404+
/// Checks if the specified expression is a built-in range literal.
5405+
/// (See: `LoweringContext::lower_expr()`).
5406+
pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
5407+
use hir::{Path, QPath, ExprKind, TyKind};
5408+
5409+
// Returns whether the given path represents a (desugared) range,
5410+
// either in std or core, i.e. has either a `::std::ops::Range` or
5411+
// `::core::ops::Range` prefix.
5412+
fn is_range_path(path: &Path) -> bool {
5413+
let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.as_str().to_string()).collect();
5414+
let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect();
5415+
5416+
// "{{root}}" is the equivalent of `::` prefix in `Path`.
5417+
if let ["{{root}}", std_core, "ops", range] = segs.as_slice() {
5418+
(*std_core == "std" || *std_core == "core") && range.starts_with("Range")
5419+
} else {
5420+
false
5421+
}
5422+
};
5423+
5424+
// Check whether a span corresponding to a range expression is a
5425+
// range literal, rather than an explicit struct or `new()` call.
5426+
fn is_lit(sess: &Session, span: &Span) -> bool {
5427+
let source_map = sess.source_map();
5428+
let end_point = source_map.end_point(*span);
5429+
5430+
if let Ok(end_string) = source_map.span_to_snippet(end_point) {
5431+
!(end_string.ends_with("}") || end_string.ends_with(")"))
5432+
} else {
5433+
false
5434+
}
5435+
};
5436+
5437+
match expr.node {
5438+
// All built-in range literals but `..=` and `..` desugar to `Struct`s.
5439+
ExprKind::Struct(ref qpath, _, _) => {
5440+
if let QPath::Resolved(None, ref path) = **qpath {
5441+
return is_range_path(&path) && is_lit(sess, &expr.span);
5442+
}
5443+
}
5444+
5445+
// `..` desugars to its struct path.
5446+
ExprKind::Path(QPath::Resolved(None, ref path)) => {
5447+
return is_range_path(&path) && is_lit(sess, &expr.span);
5448+
}
5449+
5450+
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
5451+
ExprKind::Call(ref func, _) => {
5452+
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node {
5453+
if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node {
5454+
let new_call = segment.ident.as_str() == "new";
5455+
return is_range_path(&path) && is_lit(sess, &expr.span) && new_call;
5456+
}
5457+
}
5458+
}
5459+
5460+
_ => {}
5461+
}
5462+
5463+
false
5464+
}

0 commit comments

Comments
 (0)