Check that we don't treat any type but a range type as a range

This commit is contained in:
Oliver Schneider 2018-05-08 17:16:01 +02:00
parent e456241f18
commit c6e35eae53
No known key found for this signature in database
GPG key ID: 1D5CB4FC597C3004
6 changed files with 46 additions and 12 deletions

View file

@ -73,7 +73,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
} }
// Index is a constant range // Index is a constant range
if let Some(range) = higher::range(index) { if let Some(range) = higher::range(cx, index) {
if let Some((start, end)) = to_const_range(cx, range, size) { if let Some((start, end)) = to_const_range(cx, range, size) {
if start > size || end > size { if start > size || end > size {
utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "range is out of bounds"); utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "range is out of bounds");
@ -83,7 +83,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
} }
} }
if let Some(range) = higher::range(index) { if let Some(range) = higher::range(cx, index) {
// Full ranges are always valid // Full ranges are always valid
if range.start.is_none() && range.end.is_none() { if range.start.is_none() && range.end.is_none() {
return; return;

View file

@ -167,7 +167,7 @@ fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
} else { } else {
Finite Finite
}, },
ExprStruct(..) => higher::range(expr) ExprStruct(..) => higher::range(cx, expr)
.map_or(false, |r| r.end.is_none()) .map_or(false, |r| r.end.is_none())
.into(), .into(),
_ => Finite, _ => Finite,

View file

@ -888,7 +888,7 @@ fn detect_manual_memcpy<'a, 'tcx>(
start: Some(start), start: Some(start),
ref end, ref end,
limits, limits,
}) = higher::range(arg) }) = higher::range(cx, arg)
{ {
// the var must be a single name // the var must be a single name
if let PatKind::Binding(_, canonical_id, _, _) = pat.node { if let PatKind::Binding(_, canonical_id, _, _) = pat.node {
@ -982,7 +982,7 @@ fn check_for_loop_range<'a, 'tcx>(
start: Some(start), start: Some(start),
ref end, ref end,
limits, limits,
}) = higher::range(arg) }) = higher::range(cx, arg)
{ {
// the var must be a single name // the var must be a single name
if let PatKind::Binding(_, canonical_id, ref ident, _) = pat.node { if let PatKind::Binding(_, canonical_id, ref ident, _) = pat.node {
@ -1118,7 +1118,7 @@ fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx
start: Some(start), start: Some(start),
end: Some(end), end: Some(end),
limits, limits,
}) = higher::range(arg) }) = higher::range(cx, arg)
{ {
// ...and both sides are compile-time constant integers... // ...and both sides are compile-time constant integers...
if let Some((start_idx, _)) = constant(cx, start) { if let Some((start_idx, _)) = constant(cx, start) {
@ -1456,7 +1456,7 @@ fn check_for_mut_range_bound(cx: &LateContext, arg: &Expr, body: &Expr) {
start: Some(start), start: Some(start),
end: Some(end), end: Some(end),
.. ..
}) = higher::range(arg) }) = higher::range(cx, arg)
{ {
let mut_ids = vec![ let mut_ids = vec![
check_for_mutability(cx, start), check_for_mutability(cx, start),

View file

@ -110,7 +110,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let ExprMethodCall(ref iter_path, _, ref iter_args ) = *iter; if let ExprMethodCall(ref iter_path, _, ref iter_args ) = *iter;
if iter_path.name == "iter"; if iter_path.name == "iter";
// range expression in .zip() call: 0..x.len() // range expression in .zip() call: 0..x.len()
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg);
if is_integer_literal(start, 0); if is_integer_literal(start, 0);
// .len() call // .len() call
if let ExprMethodCall(ref len_path, _, ref len_args) = end.node; if let ExprMethodCall(ref len_path, _, ref len_args) = end.node;
@ -132,7 +132,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// exclusive range plus one: x..(y+1) // exclusive range plus one: x..(y+1)
if_chain! { if_chain! {
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::HalfOpen }) = higher::range(expr); if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::HalfOpen }) = higher::range(cx, expr);
if let Some(y) = y_plus_one(end); if let Some(y) = y_plus_one(end);
then { then {
span_lint_and_then( span_lint_and_then(
@ -153,7 +153,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// inclusive range minus one: x..=(y-1) // inclusive range minus one: x..=(y-1)
if_chain! { if_chain! {
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr); if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr);
if let Some(y) = y_minus_one(end); if let Some(y) = y_minus_one(end);
then { then {
span_lint_and_then( span_lint_and_then(

View file

@ -3,7 +3,7 @@
#![deny(missing_docs_in_private_items)] #![deny(missing_docs_in_private_items)]
use rustc::hir; use rustc::{hir, ty};
use rustc::lint::LateContext; use rustc::lint::LateContext;
use syntax::ast; use syntax::ast;
use utils::{is_expn_of, match_def_path, match_qpath, opt_def_id, paths, resolve_node}; use utils::{is_expn_of, match_def_path, match_qpath, opt_def_id, paths, resolve_node};
@ -44,7 +44,36 @@ pub struct Range<'a> {
} }
/// Higher a `hir` range to something similar to `ast::ExprKind::Range`. /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
pub fn range(expr: &hir::Expr) -> Option<Range> { pub fn range<'a, 'b, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'b hir::Expr) -> Option<Range<'b>> {
let def_path = match cx.tables.expr_ty(expr).sty {
ty::TyAdt(def, _) => cx.tcx.def_path(def.did),
_ => return None,
};
// sanity checks for std::ops::RangeXXXX
if def_path.data.len() != 3 {
return None;
}
if def_path.data.get(0)?.data.as_interned_str() != "ops" {
return None;
}
if def_path.data.get(1)?.data.as_interned_str() != "range" {
return None;
}
let type_name = def_path.data.get(2)?.data.as_interned_str();
let range_types = [
"RangeFrom",
"RangeFull",
"RangeInclusive",
"Range",
"RangeTo",
"RangeToInclusive",
];
if !range_types.contains(&&*type_name.as_str()) {
return None;
}
/// Find the field named `name` in the field. Always return `Some` for /// Find the field named `name` in the field. Always return `Some` for
/// convenience. /// convenience.
fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> { fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> {

View file

@ -0,0 +1,5 @@
pub fn f(new: fn()) {
new();
}
fn main() {}