From fa6ae4c82872d4cd325072400a04e386f4004dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 24 Nov 2017 14:47:40 -0800 Subject: [PATCH] Suggest using slice when encountering `let x = ""[..];` --- src/librustc/hir/mod.rs | 17 +++++++ src/librustc/traits/error_reporting.rs | 29 ++++++++++++ .../ui/suggestions/str-array-assignment.rs | 17 +++++++ .../suggestions/str-array-assignment.stderr | 44 +++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 src/test/ui/suggestions/str-array-assignment.rs create mode 100644 src/test/ui/suggestions/str-array-assignment.stderr diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 39ec33eef1f..986f1b9e419 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -244,6 +244,23 @@ impl Path { pub fn is_global(&self) -> bool { !self.segments.is_empty() && self.segments[0].name == keywords::CrateRoot.name() } + + /// Wether this path is any of `::std::ops::{Range, RangeTo, RangeFrom}`. + pub fn is_range(&self) -> bool { + let mut base = ["{{root}}", "std", "ops"].iter().map(|p| p.to_string()).collect::>(); + let range_paths = ["Range", "RangeTo", "RangeFrom"]; + let segments = self.segments.iter() + .map(|segment| format!("{}", segment.name)) + .collect::>(); + for path in &range_paths { + base.push(path.to_string()); + if base == segments { + return true; + } + base.pop(); + } + false + } } impl fmt::Debug for Path { diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 46ec2be4a1f..a8a8b20012b 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -581,6 +581,8 @@ pub fn report_selection_error(&self, trait_ref.self_ty())); } + self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); + // Try to report a help message if !trait_ref.has_infer_types() && self.predicate_can_apply(obligation.param_env, trait_ref) { @@ -821,6 +823,33 @@ pub fn report_selection_error(&self, err.emit(); } + /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a + /// suggestion to borrow the initializer in order to use have a slice instead. + fn suggest_borrow_on_unsized_slice(&self, + code: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx>) { + if let &ObligationCauseCode::VariableType(node_id) = code { + let parent_node = self.tcx.hir.get_parent_node(node_id); + if let Some(hir::map::NodeLocal(ref local)) = self.tcx.hir.find(parent_node) { + if let Some(ref expr) = local.init { + if let hir::ExprIndex(_, ref index) = expr.node { + if let hir::ExprStruct(hir::QPath::Resolved(None, ref path), + ..) = index.node { + if let (Ok(snippet), true) = ( + self.tcx.sess.codemap().span_to_snippet(expr.span), + path.is_range() + ) { + err.span_suggestion(expr.span, + "consider a slice instead", + format!("&{}", snippet)); + } + } + } + } + } + } + } + fn report_arg_count_mismatch( &self, span: Span, diff --git a/src/test/ui/suggestions/str-array-assignment.rs b/src/test/ui/suggestions/str-array-assignment.rs new file mode 100644 index 00000000000..523e7bea622 --- /dev/null +++ b/src/test/ui/suggestions/str-array-assignment.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let s = "abc"; + let t = if true { s[..2] } else { s }; + let u: &str = if true { s[..2] } else { s }; + let v = s[..2]; + let w: &str = s[..2]; +} diff --git a/src/test/ui/suggestions/str-array-assignment.stderr b/src/test/ui/suggestions/str-array-assignment.stderr new file mode 100644 index 00000000000..225dfbd98fd --- /dev/null +++ b/src/test/ui/suggestions/str-array-assignment.stderr @@ -0,0 +1,44 @@ +error[E0308]: if and else have incompatible types + --> $DIR/str-array-assignment.rs:13:11 + | +13 | let t = if true { s[..2] } else { s }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found &str + | + = note: expected type `str` + found type `&str` + +error[E0308]: mismatched types + --> $DIR/str-array-assignment.rs:14:27 + | +11 | fn main() { + | - expected `()` because of default return type +... +14 | let u: &str = if true { s[..2] } else { s }; + | ^^^^^^ expected &str, found str + | + = note: expected type `&str` + found type `str` + +error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied + --> $DIR/str-array-assignment.rs:15:7 + | +15 | let v = s[..2]; + | ^ ------ help: consider a slice instead: `&s[..2]` + | | + | `str` does not have a constant size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: all local variables must have a statically known size + +error[E0308]: mismatched types + --> $DIR/str-array-assignment.rs:16:17 + | +16 | let w: &str = s[..2]; + | ^^^^^^ expected &str, found str + | + = note: expected type `&str` + found type `str` + = help: try with `&s[..2]` + +error: aborting due to 4 previous errors +