Skip to content

Commit

Permalink
Auto merge of #54734 - pawroman:fix_range_borrowing_suggestion, r=varkor
Browse files Browse the repository at this point in the history
Fix range literals borrowing suggestions

Fixes #54505. The compiler issued incorrect range borrowing suggestions (missing `()` around borrows of range literals). This was not correct syntax (see the issue for an example).

With changes in this PR, this is fixed for all types of `Range` literals.

Thanks again to @varkor and @estebank for their invaluable help and guidance.

r? @varkor
  • Loading branch information
bors committed Oct 9, 2018
2 parents 96cafc5 + 1f7dafb commit eae47a4
Show file tree
Hide file tree
Showing 9 changed files with 664 additions and 3 deletions.
75 changes: 72 additions & 3 deletions src/librustc_typeck/check/demand.rs
Expand Up @@ -309,11 +309,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};
if self.can_coerce(ref_ty, expected) {
if let Ok(src) = cm.span_to_snippet(sp) {
let sugg_expr = match expr.node { // parenthesize if needed (Issue #46756)
let needs_parens = match expr.node {
// parenthesize if needed (Issue #46756)
hir::ExprKind::Cast(_, _) |
hir::ExprKind::Binary(_, _, _) => format!("({})", src),
_ => src,
hir::ExprKind::Binary(_, _, _) => true,
// parenthesize borrows of range literals (Issue #54505)
_ if self.is_range_literal(expr) => true,
_ => false,
};
let sugg_expr = if needs_parens {
format!("({})", src)
} else {
src
};

if let Some(sugg) = self.can_use_as_ref(expr) {
return Some(sugg);
}
Expand Down Expand Up @@ -374,6 +383,66 @@ 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(QPath::Resolved(None, ref path), _, _) |
// `..` 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>,
expr: &hir::Expr,
Expand Down
75 changes: 75 additions & 0 deletions src/test/ui/range/issue-54505-no-literals.fixed
@@ -0,0 +1,75 @@
// run-rustfix

// Regression test for changes introduced while fixing #54505

// This test uses non-literals for Ranges
// (expecting no parens with borrow suggestion)

use std::ops::RangeBounds;


// take a reference to any built-in range
fn take_range(_r: &impl RangeBounds<i8>) {}


fn main() {
take_range(&std::ops::Range { start: 0, end: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::Range { start: 0, end: 1 }

take_range(&::std::ops::Range { start: 0, end: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::Range { start: 0, end: 1 }

take_range(&std::ops::RangeFrom { start: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeFrom { start: 1 }

take_range(&::std::ops::RangeFrom { start: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeFrom { start: 1 }

take_range(&std::ops::RangeFull {});
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeFull {}

take_range(&::std::ops::RangeFull {});
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeFull {}

take_range(&std::ops::RangeInclusive::new(0, 1));
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeInclusive::new(0, 1)

take_range(&::std::ops::RangeInclusive::new(0, 1));
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeInclusive::new(0, 1)

take_range(&std::ops::RangeTo { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeTo { end: 5 }

take_range(&::std::ops::RangeTo { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeTo { end: 5 }

take_range(&std::ops::RangeToInclusive { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeToInclusive { end: 5 }

take_range(&::std::ops::RangeToInclusive { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeToInclusive { end: 5 }
}
75 changes: 75 additions & 0 deletions src/test/ui/range/issue-54505-no-literals.rs
@@ -0,0 +1,75 @@
// run-rustfix

// Regression test for changes introduced while fixing #54505

// This test uses non-literals for Ranges
// (expecting no parens with borrow suggestion)

use std::ops::RangeBounds;


// take a reference to any built-in range
fn take_range(_r: &impl RangeBounds<i8>) {}


fn main() {
take_range(std::ops::Range { start: 0, end: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::Range { start: 0, end: 1 }

take_range(::std::ops::Range { start: 0, end: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::Range { start: 0, end: 1 }

take_range(std::ops::RangeFrom { start: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeFrom { start: 1 }

take_range(::std::ops::RangeFrom { start: 1 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeFrom { start: 1 }

take_range(std::ops::RangeFull {});
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeFull {}

take_range(::std::ops::RangeFull {});
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeFull {}

take_range(std::ops::RangeInclusive::new(0, 1));
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeInclusive::new(0, 1)

take_range(::std::ops::RangeInclusive::new(0, 1));
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeInclusive::new(0, 1)

take_range(std::ops::RangeTo { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeTo { end: 5 }

take_range(::std::ops::RangeTo { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeTo { end: 5 }

take_range(std::ops::RangeToInclusive { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &std::ops::RangeToInclusive { end: 5 }

take_range(::std::ops::RangeToInclusive { end: 5 });
//~^ ERROR mismatched types [E0308]
//~| HELP consider borrowing here
//~| SUGGESTION &::std::ops::RangeToInclusive { end: 5 }
}
147 changes: 147 additions & 0 deletions src/test/ui/range/issue-54505-no-literals.stderr
@@ -0,0 +1,147 @@
error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:16:16
|
LL | take_range(std::ops::Range { start: 0, end: 1 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::Range`
| help: consider borrowing here: `&std::ops::Range { start: 0, end: 1 }`
|
= note: expected type `&_`
found type `std::ops::Range<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:21:16
|
LL | take_range(::std::ops::Range { start: 0, end: 1 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::Range`
| help: consider borrowing here: `&::std::ops::Range { start: 0, end: 1 }`
|
= note: expected type `&_`
found type `std::ops::Range<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:26:16
|
LL | take_range(std::ops::RangeFrom { start: 1 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeFrom`
| help: consider borrowing here: `&std::ops::RangeFrom { start: 1 }`
|
= note: expected type `&_`
found type `std::ops::RangeFrom<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:31:16
|
LL | take_range(::std::ops::RangeFrom { start: 1 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeFrom`
| help: consider borrowing here: `&::std::ops::RangeFrom { start: 1 }`
|
= note: expected type `&_`
found type `std::ops::RangeFrom<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:36:16
|
LL | take_range(std::ops::RangeFull {});
| ^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeFull`
| help: consider borrowing here: `&std::ops::RangeFull {}`
|
= note: expected type `&_`
found type `std::ops::RangeFull`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:41:16
|
LL | take_range(::std::ops::RangeFull {});
| ^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeFull`
| help: consider borrowing here: `&::std::ops::RangeFull {}`
|
= note: expected type `&_`
found type `std::ops::RangeFull`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:46:16
|
LL | take_range(std::ops::RangeInclusive::new(0, 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeInclusive`
| help: consider borrowing here: `&std::ops::RangeInclusive::new(0, 1)`
|
= note: expected type `&_`
found type `std::ops::RangeInclusive<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:51:16
|
LL | take_range(::std::ops::RangeInclusive::new(0, 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeInclusive`
| help: consider borrowing here: `&::std::ops::RangeInclusive::new(0, 1)`
|
= note: expected type `&_`
found type `std::ops::RangeInclusive<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:56:16
|
LL | take_range(std::ops::RangeTo { end: 5 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeTo`
| help: consider borrowing here: `&std::ops::RangeTo { end: 5 }`
|
= note: expected type `&_`
found type `std::ops::RangeTo<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:61:16
|
LL | take_range(::std::ops::RangeTo { end: 5 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeTo`
| help: consider borrowing here: `&::std::ops::RangeTo { end: 5 }`
|
= note: expected type `&_`
found type `std::ops::RangeTo<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:66:16
|
LL | take_range(std::ops::RangeToInclusive { end: 5 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeToInclusive`
| help: consider borrowing here: `&std::ops::RangeToInclusive { end: 5 }`
|
= note: expected type `&_`
found type `std::ops::RangeToInclusive<{integer}>`

error[E0308]: mismatched types
--> $DIR/issue-54505-no-literals.rs:71:16
|
LL | take_range(::std::ops::RangeToInclusive { end: 5 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected reference, found struct `std::ops::RangeToInclusive`
| help: consider borrowing here: `&::std::ops::RangeToInclusive { end: 5 }`
|
= note: expected type `&_`
found type `std::ops::RangeToInclusive<{integer}>`

error: aborting due to 12 previous errors

For more information about this error, try `rustc --explain E0308`.

0 comments on commit eae47a4

Please sign in to comment.