From d2ff829433222f563425f3f7b13d20002e0708fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 30 Nov 2018 15:12:41 +0100 Subject: [PATCH 01/16] Update variances_of --- src/librustc/arena.rs | 8 ++++++++ src/librustc/query/mod.rs | 4 ++-- src/librustc/ty/mod.rs | 8 ++------ src/librustc/ty/relate.rs | 4 ++-- src/librustc_metadata/cstore_impl.rs | 2 +- src/librustc_typeck/variance/mod.rs | 8 ++++---- src/librustc_typeck/variance/solve.rs | 21 ++++++++++----------- 7 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs index e9751a23f1218..c4435070ba6c7 100644 --- a/src/librustc/arena.rs +++ b/src/librustc/arena.rs @@ -102,6 +102,14 @@ impl<'tcx> Arena<'tcx> { } } + #[inline] + pub fn alloc_slice(&self, value: &[T]) -> &mut [T] { + if value.len() == 0 { + return &mut [] + } + self.dropless.alloc_slice(value) + } + pub fn alloc_from_iter< T: ArenaAllocatable, I: IntoIterator diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 1861420b408b6..fbe79925a88db 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -245,13 +245,13 @@ rustc_queries! { /// Get a map with the variance of every item; use `item_variance` /// instead. - query crate_variances(_: CrateNum) -> Lrc { + query crate_variances(_: CrateNum) -> Lrc> { desc { "computing the variances for items in this crate" } } /// Maps from def-id of a type or region parameter to its /// (inferred) variance. - query variances_of(_: DefId) -> Lrc> {} + query variances_of(_: DefId) -> &'tcx [ty::Variance] {} } TypeChecking { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 67be228d232e1..0b862db89a6b6 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -330,15 +330,11 @@ pub enum Variance { /// `tcx.variances_of()` to get the variance for a *particular* /// item. #[derive(HashStable)] -pub struct CrateVariancesMap { +pub struct CrateVariancesMap<'tcx> { /// For each item with generics, maps to a vector of the variance /// of its generics. If an item has no generics, it will have no /// entry. - pub variances: FxHashMap>>, - - /// An empty vector, useful for cloning. - #[stable_hasher(ignore)] - pub empty_variance: Lrc>, + pub variances: FxHashMap, } impl Variance { diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 810bd10c8f4f7..601226182ed62 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -60,7 +60,7 @@ pub trait TypeRelation<'a, 'gcx: 'a+'tcx, 'tcx: 'a> : Sized { b_subst); let opt_variances = self.tcx().variances_of(item_def_id); - relate_substs(self, Some(&opt_variances), a_subst, b_subst) + relate_substs(self, Some(opt_variances), a_subst, b_subst) } /// Switch variance for the purpose of relating `a` and `b`. @@ -122,7 +122,7 @@ impl<'tcx> Relate<'tcx> for ty::TypeAndMut<'tcx> { } pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R, - variances: Option<&Vec>, + variances: Option<&[ty::Variance]>, a_subst: SubstsRef<'tcx>, b_subst: SubstsRef<'tcx>) -> RelateResult<'tcx, SubstsRef<'tcx>> diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 1a1b933ccf311..483fd64245b0b 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -106,7 +106,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, let _ = cdata; tcx.calculate_dtor(def_id, &mut |_,_| Ok(())) } - variances_of => { Lrc::new(cdata.get_item_variances(def_id.index)) } + variances_of => { tcx.arena.alloc_from_iter(cdata.get_item_variances(def_id.index)) } associated_item_def_ids => { let mut result = vec![]; cdata.each_child_of_item(def_id.index, diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs index 9b9a6bace96b1..88ee1d79f5435 100644 --- a/src/librustc_typeck/variance/mod.rs +++ b/src/librustc_typeck/variance/mod.rs @@ -36,7 +36,7 @@ pub fn provide(providers: &mut Providers<'_>) { } fn crate_variances<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) - -> Lrc { + -> Lrc> { assert_eq!(crate_num, LOCAL_CRATE); let mut arena = arena::TypedArena::default(); let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); @@ -45,7 +45,7 @@ fn crate_variances<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) } fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId) - -> Lrc> { + -> &'tcx [ty::Variance] { let id = tcx.hir().as_local_hir_id(item_def_id).expect("expected local def-id"); let unsupported = || { // Variance not relevant. @@ -88,6 +88,6 @@ fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId) let crate_map = tcx.crate_variances(LOCAL_CRATE); crate_map.variances.get(&item_def_id) - .unwrap_or(&crate_map.empty_variance) - .clone() + .map(|p| *p) + .unwrap_or(&[]) } diff --git a/src/librustc_typeck/variance/solve.rs b/src/librustc_typeck/variance/solve.rs index 51a1d088ddccb..8edf3c52ccc22 100644 --- a/src/librustc_typeck/variance/solve.rs +++ b/src/librustc_typeck/variance/solve.rs @@ -8,7 +8,6 @@ use rustc::hir::def_id::DefId; use rustc::ty; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use super::constraints::*; use super::terms::*; @@ -23,7 +22,9 @@ struct SolveContext<'a, 'tcx: 'a> { solutions: Vec, } -pub fn solve_constraints(constraints_cx: ConstraintContext<'_, '_>) -> ty::CrateVariancesMap { +pub fn solve_constraints<'tcx>( + constraints_cx: ConstraintContext<'_, 'tcx> +) -> ty::CrateVariancesMap<'tcx> { let ConstraintContext { terms_cx, constraints, .. } = constraints_cx; let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()]; @@ -41,9 +42,8 @@ pub fn solve_constraints(constraints_cx: ConstraintContext<'_, '_>) -> ty::Crate }; solutions_cx.solve(); let variances = solutions_cx.create_map(); - let empty_variance = Lrc::new(Vec::new()); - ty::CrateVariancesMap { variances, empty_variance } + ty::CrateVariancesMap { variances } } impl<'a, 'tcx> SolveContext<'a, 'tcx> { @@ -78,7 +78,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { } } - fn enforce_const_invariance(&self, generics: &ty::Generics, variances: &mut Vec) { + fn enforce_const_invariance(&self, generics: &ty::Generics, variances: &mut [ty::Variance]) { let tcx = self.terms_cx.tcx; // Make all const parameters invariant. @@ -94,7 +94,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { } } - fn create_map(&self) -> FxHashMap>> { + fn create_map(&self) -> FxHashMap { let tcx = self.terms_cx.tcx; let solutions = &self.solutions; @@ -103,22 +103,21 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { let generics = tcx.generics_of(def_id); let count = generics.count(); - let mut variances = solutions[start..(start + count)].to_vec(); - debug!("id={} variances={:?}", id, variances); + let variances = tcx.arena.alloc_slice(&solutions[start..(start + count)]); // Const parameters are always invariant. - self.enforce_const_invariance(generics, &mut variances); + self.enforce_const_invariance(generics, variances); // Functions are permitted to have unused generic parameters: make those invariant. if let ty::FnDef(..) = tcx.type_of(def_id).sty { - for variance in &mut variances { + for variance in variances.iter_mut() { if *variance == ty::Bivariant { *variance = ty::Invariant; } } } - (def_id, Lrc::new(variances)) + (def_id, &*variances) }).collect() } From a962274903e7947c5847dbaffbe6c554748e29b9 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 26 Apr 2019 22:14:52 +0100 Subject: [PATCH 02/16] Search for incompatible universes in borrow errors If we have a borrow that has to live for `'static` we need to check for any regions in incompatible universes when trying to find the cause. --- .../nll/region_infer/error_reporting/mod.rs | 7 +++-- .../ui/nll/local-outlives-static-via-hrtb.rs | 26 +++++++++++++++++++ .../nll/local-outlives-static-via-hrtb.stderr | 26 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/nll/local-outlives-static-via-hrtb.rs create mode 100644 src/test/ui/nll/local-outlives-static-via-hrtb.stderr diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index abb30d042ca4c..00e81ee049183 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -674,8 +674,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { borrow_region: RegionVid, outlived_region: RegionVid, ) -> (ConstraintCategory, bool, Span, Option) { - let (category, from_closure, span) = - self.best_blame_constraint(mir, borrow_region, |r| r == outlived_region); + let (category, from_closure, span) = self.best_blame_constraint( + mir, + borrow_region, + |r| self.provides_universal_region(r, borrow_region, outlived_region) + ); let outlived_fr_name = self.give_region_a_name(infcx, mir, upvars, mir_def_id, outlived_region, &mut 1); (category, from_closure, span, outlived_fr_name) diff --git a/src/test/ui/nll/local-outlives-static-via-hrtb.rs b/src/test/ui/nll/local-outlives-static-via-hrtb.rs new file mode 100644 index 0000000000000..5f1f9b3a7f229 --- /dev/null +++ b/src/test/ui/nll/local-outlives-static-via-hrtb.rs @@ -0,0 +1,26 @@ +// Test that we handle the case when a local variable is borrowed for `'static` +// due to an outlives constraint involving a region in an incompatible universe + +pub trait Outlives<'this> {} + +impl<'this, T> Outlives<'this> for T where T: 'this {} +trait Reference { + type AssociatedType; +} + +impl<'a, T: 'a> Reference for &'a T { + type AssociatedType = &'a (); +} + +fn assert_static_via_hrtb(_: G) where for<'a> G: Outlives<'a> {} + +fn assert_static_via_hrtb_with_assoc_type(_: &'_ T) +where + for<'a> &'a T: Reference, +{} + +fn main() { + let local = 0; + assert_static_via_hrtb(&local); //~ ERROR `local` does not live long enough + assert_static_via_hrtb_with_assoc_type(&&local); //~ ERROR `local` does not live long enough +} diff --git a/src/test/ui/nll/local-outlives-static-via-hrtb.stderr b/src/test/ui/nll/local-outlives-static-via-hrtb.stderr new file mode 100644 index 0000000000000..61009da49ffed --- /dev/null +++ b/src/test/ui/nll/local-outlives-static-via-hrtb.stderr @@ -0,0 +1,26 @@ +error[E0597]: `local` does not live long enough + --> $DIR/local-outlives-static-via-hrtb.rs:24:28 + | +LL | assert_static_via_hrtb(&local); + | -----------------------^^^^^^- + | | | + | | borrowed value does not live long enough + | argument requires that `local` is borrowed for `'static` +LL | assert_static_via_hrtb_with_assoc_type(&&local); +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/local-outlives-static-via-hrtb.rs:25:45 + | +LL | assert_static_via_hrtb_with_assoc_type(&&local); + | ----------------------------------------^^^^^^- + | | | + | | borrowed value does not live long enough + | argument requires that `local` is borrowed for `'static` +LL | } + | - `local` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. From abf73a6aebbe505694392736e543502c240841db Mon Sep 17 00:00:00 2001 From: Tim Diekmann Date: Mon, 29 Apr 2019 02:55:43 +0200 Subject: [PATCH 03/16] Support ZSTs in DispatchFromDyn --- src/librustc_typeck/coherence/builtin.rs | 15 +++--- src/test/run-pass/dispatch_from_dyn_zst.rs | 49 +++++++++++++++++++ .../ui/invalid_dispatch_from_dyn_impls.stderr | 2 +- 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/test/run-pass/dispatch_from_dyn_zst.rs diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index c3b08569d2f1f..dc5b461ac1429 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -223,19 +223,22 @@ fn visit_implementation_of_dispatch_from_dyn<'a, 'tcx>( let fields = &def_a.non_enum_variant().fields; let coerced_fields = fields.iter().filter_map(|field| { - if tcx.type_of(field.did).is_phantom_data() { - // ignore PhantomData fields - return None - } - let ty_a = field.ty(tcx, substs_a); let ty_b = field.ty(tcx, substs_b); + + if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { + if layout.is_zst() { + // ignore ZST fields + return None; + } + } + if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { if ok.obligations.is_empty() { create_err( "the trait `DispatchFromDyn` may only be implemented \ for structs containing the field being coerced, \ - `PhantomData` fields, and nothing else" + ZST fields, and nothing else" ).note( &format!( "extra field `{}` of type `{}` is not allowed", diff --git a/src/test/run-pass/dispatch_from_dyn_zst.rs b/src/test/run-pass/dispatch_from_dyn_zst.rs new file mode 100644 index 0000000000000..a2181336e00f5 --- /dev/null +++ b/src/test/run-pass/dispatch_from_dyn_zst.rs @@ -0,0 +1,49 @@ +#![feature(unsize, dispatch_from_dyn, never_type)] + +#![allow(dead_code)] + +use std::{ + ops::DispatchFromDyn, + marker::{Unsize, PhantomData}, +}; + +struct Zst; +struct NestedZst(PhantomData<()>, Zst); + + +struct WithUnit(Box, ()); +impl DispatchFromDyn> for WithUnit + where T: Unsize {} + +struct WithPhantom(Box, PhantomData<()>); +impl DispatchFromDyn> for WithPhantom + where T: Unsize {} + +struct WithNever(Box, !); +impl DispatchFromDyn> for WithNever + where T: Unsize {} + +struct WithZst(Box, Zst); +impl DispatchFromDyn> for WithZst + where T: Unsize {} + +struct WithNestedZst(Box, NestedZst); +impl DispatchFromDyn> for WithNestedZst + where T: Unsize {} + + +struct Generic(Box, A); +impl DispatchFromDyn> for Generic + where T: Unsize {} +impl DispatchFromDyn>> + for Generic> + where T: Unsize {} +impl DispatchFromDyn> for Generic + where T: Unsize {} +impl DispatchFromDyn> for Generic + where T: Unsize {} +impl DispatchFromDyn> for Generic + where T: Unsize {} + + +fn main() {} diff --git a/src/test/ui/invalid_dispatch_from_dyn_impls.stderr b/src/test/ui/invalid_dispatch_from_dyn_impls.stderr index bd016466300d7..624f35d062cc5 100644 --- a/src/test/ui/invalid_dispatch_from_dyn_impls.stderr +++ b/src/test/ui/invalid_dispatch_from_dyn_impls.stderr @@ -1,4 +1,4 @@ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, `PhantomData` fields, and nothing else +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields, and nothing else --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1 | LL | / impl DispatchFromDyn> for WrapperWithExtraField From 484b304eb4b363eb31c2c35480a527b78929423f Mon Sep 17 00:00:00 2001 From: lzutao Date: Mon, 29 Apr 2019 14:05:48 +0700 Subject: [PATCH 04/16] build-gcc: Create missing cc symlink --- src/ci/docker/dist-x86_64-linux/build-gcc.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ci/docker/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/dist-x86_64-linux/build-gcc.sh index 7f6e94d326ddf..ddc2066537cef 100755 --- a/src/ci/docker/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/dist-x86_64-linux/build-gcc.sh @@ -32,6 +32,7 @@ hide_output ../gcc-$GCC/configure \ --enable-languages=c,c++ hide_output make -j10 hide_output make install +ln -s gcc /rustroot/bin/cc cd .. rm -rf gcc-build From a0e112ba52bbf90f958cf51b6fb39970c8a5c8b2 Mon Sep 17 00:00:00 2001 From: YOSHIOKA Takuma Date: Tue, 30 Apr 2019 15:52:07 +0900 Subject: [PATCH 05/16] Implement `BorrowMut` for `String` Closes rust-lang/rfcs#1282. --- src/liballoc/str.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index e5d4e1c533c7b..f66ff894ae865 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -28,7 +28,7 @@ // It's cleaner to just turn off the unused_imports warning than to fix them. #![allow(unused_imports)] -use core::borrow::Borrow; +use core::borrow::{Borrow, BorrowMut}; use core::str::pattern::{Pattern, Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; use core::ptr; @@ -190,6 +190,14 @@ impl Borrow for String { } } +#[stable(feature = "string_borrow_mut", since = "1.36.0")] +impl BorrowMut for String { + #[inline] + fn borrow_mut(&mut self) -> &mut str { + &mut self[..] + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for str { type Owned = String; From 1b679e74f050bc3ff40452e4d4c82d3fc4b03cf4 Mon Sep 17 00:00:00 2001 From: Tim Diekmann Date: Tue, 30 Apr 2019 14:38:17 +0200 Subject: [PATCH 06/16] Only allow ZSTs with 1 byte alignment --- src/librustc_typeck/coherence/builtin.rs | 6 +++--- src/test/ui/invalid_dispatch_from_dyn_impls.rs | 9 +++++++++ .../ui/invalid_dispatch_from_dyn_impls.stderr | 15 +++++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index dc5b461ac1429..1be0248727d01 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -227,8 +227,8 @@ fn visit_implementation_of_dispatch_from_dyn<'a, 'tcx>( let ty_b = field.ty(tcx, substs_b); if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { - if layout.is_zst() { - // ignore ZST fields + if layout.is_zst() && layout.details.align.abi.bytes() == 1 { + // ignore ZST fields with alignment of 1 byte return None; } } @@ -238,7 +238,7 @@ fn visit_implementation_of_dispatch_from_dyn<'a, 'tcx>( create_err( "the trait `DispatchFromDyn` may only be implemented \ for structs containing the field being coerced, \ - ZST fields, and nothing else" + ZST fields with 1 byte alignment, and nothing else" ).note( &format!( "extra field `{}` of type `{}` is not allowed", diff --git a/src/test/ui/invalid_dispatch_from_dyn_impls.rs b/src/test/ui/invalid_dispatch_from_dyn_impls.rs index c4716893fbc54..b7bc766fbe020 100644 --- a/src/test/ui/invalid_dispatch_from_dyn_impls.rs +++ b/src/test/ui/invalid_dispatch_from_dyn_impls.rs @@ -39,4 +39,13 @@ where T: Unsize, {} //~^^^ ERROR [E0378] +#[repr(align(64))] +struct OverAlignedZst; +struct OverAligned(Box, OverAlignedZst); + +impl DispatchFromDyn> for OverAligned + where + T: Unsize, +{} //~^^^ ERROR [E0378] + fn main() {} diff --git a/src/test/ui/invalid_dispatch_from_dyn_impls.stderr b/src/test/ui/invalid_dispatch_from_dyn_impls.stderr index 624f35d062cc5..6d62d4fd0711d 100644 --- a/src/test/ui/invalid_dispatch_from_dyn_impls.stderr +++ b/src/test/ui/invalid_dispatch_from_dyn_impls.stderr @@ -1,4 +1,4 @@ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields, and nothing else +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1 | LL | / impl DispatchFromDyn> for WrapperWithExtraField @@ -36,6 +36,17 @@ LL | | T: Unsize, LL | | {} | |__^ -error: aborting due to 4 previous errors +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else + --> $DIR/invalid_dispatch_from_dyn_impls.rs:46:1 + | +LL | / impl DispatchFromDyn> for OverAligned +LL | | where +LL | | T: Unsize, +LL | | {} + | |__^ + | + = note: extra field `1` of type `OverAlignedZst` is not allowed + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0378`. From d58cb934cb78b1f11c2fa44c469621c10a8359fd Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 30 Apr 2019 17:46:59 +0200 Subject: [PATCH 07/16] Rename hir::ExprKind::Use to ::DropTemps and improve docs. --- src/librustc/cfg/construct.rs | 2 +- src/librustc/hir/intravisit.rs | 2 +- src/librustc/hir/lowering.rs | 18 +++++++++++------- src/librustc/hir/mod.rs | 14 ++++++++------ src/librustc/hir/print.rs | 2 +- src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/middle/liveness.rs | 6 +++--- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc/middle/region.rs | 4 ++-- src/librustc_mir/hair/cx/expr.rs | 2 +- src/librustc_passes/rvalue_promotion.rs | 2 +- src/librustc_typeck/check/mod.rs | 2 +- 12 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 8a4594fe0e89e..2592af7d4ad5a 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -369,7 +369,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { hir::ExprKind::AddrOf(_, ref e) | hir::ExprKind::Cast(ref e, _) | hir::ExprKind::Type(ref e, _) | - hir::ExprKind::Use(ref e) | + hir::ExprKind::DropTemps(ref e) | hir::ExprKind::Unary(_, ref e) | hir::ExprKind::Field(ref e, _) | hir::ExprKind::Yield(ref e) | diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 3d727f7cd9128..0c73d97394fda 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -1029,7 +1029,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(subexpression); visitor.visit_ty(typ) } - ExprKind::Use(ref subexpression) => { + ExprKind::DropTemps(ref subexpression) => { visitor.visit_expr(subexpression); } ExprKind::If(ref head_expression, ref if_block, ref optional_else) => { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index df455a725c5ba..42597be2c6f91 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -4671,7 +4671,7 @@ impl<'a> LoweringContext<'a> { // The construct was introduced in #21984. // FIXME(60253): Is this still necessary? // Also, add the attributes to the outer returned expr node. - return self.expr_use(head_sp, match_expr, e.attrs.clone()) + return self.expr_drop_temps(head_sp, match_expr, e.attrs.clone()) } // Desugar `ExprKind::Try` @@ -5030,15 +5030,19 @@ impl<'a> LoweringContext<'a> { ) } - /// Wrap the given `expr` in `hir::ExprKind::Use`. + /// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`. /// - /// In terms of drop order, it has the same effect as - /// wrapping `expr` in `{ let _t = $expr; _t }` but - /// should provide better compile-time performance. + /// In terms of drop order, it has the same effect as wrapping `expr` in + /// `{ let _t = $expr; _t }` but should provide better compile-time performance. /// /// The drop order can be important in e.g. `if expr { .. }`. - fn expr_use(&mut self, span: Span, expr: P, attrs: ThinVec) -> hir::Expr { - self.expr(span, hir::ExprKind::Use(expr), attrs) + fn expr_drop_temps( + &mut self, + span: Span, + expr: P, + attrs: ThinVec + ) -> hir::Expr { + self.expr(span, hir::ExprKind::DropTemps(expr), attrs) } fn expr_match( diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 8418658ab6878..5a2807ac93d85 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1366,7 +1366,7 @@ impl Expr { ExprKind::Unary(..) => ExprPrecedence::Unary, ExprKind::Lit(_) => ExprPrecedence::Lit, ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, - ExprKind::Use(ref expr, ..) => expr.precedence(), + ExprKind::DropTemps(ref expr, ..) => expr.precedence(), ExprKind::If(..) => ExprPrecedence::If, ExprKind::While(..) => ExprPrecedence::While, ExprKind::Loop(..) => ExprPrecedence::Loop, @@ -1438,7 +1438,7 @@ impl Expr { ExprKind::Binary(..) | ExprKind::Yield(..) | ExprKind::Cast(..) | - ExprKind::Use(..) | + ExprKind::DropTemps(..) | ExprKind::Err => { false } @@ -1488,10 +1488,12 @@ pub enum ExprKind { Cast(P, P), /// A type reference (e.g., `Foo`). Type(P, P), - /// Semantically equivalent to `{ let _t = expr; _t }`. - /// Maps directly to `hair::ExprKind::Use`. - /// Only exists to tweak the drop order in HIR. - Use(P), + /// Wraps the expression in a terminating scope. + /// This makes it semantically equivalent to `{ let _t = expr; _t }`. + /// + /// This construct only exists to tweak the drop order in HIR lowering. + /// An example of that is the desugaring of `for` loops. + DropTemps(P), /// An `if` block, with an optional else block. /// /// I.e., `if { } else { }`. diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 06225364f6c70..54816316f0bf5 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1388,7 +1388,7 @@ impl<'a> State<'a> { self.word_space(":")?; self.print_type(&ty)?; } - hir::ExprKind::Use(ref init) => { + hir::ExprKind::DropTemps(ref init) => { // Print `{`: self.cbox(indent_unit)?; self.ibox(0)?; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 6900be55769e1..ebc80c272135b 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -520,7 +520,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { self.consume_expr(&base); } - hir::ExprKind::Use(ref expr) => { + hir::ExprKind::DropTemps(ref expr) => { self.consume_expr(&expr); } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 966bec8381ae7..2ae53a5df3a86 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -521,7 +521,7 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) { hir::ExprKind::Binary(..) | hir::ExprKind::AddrOf(..) | hir::ExprKind::Cast(..) | - hir::ExprKind::Use(..) | + hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(_) | @@ -1222,7 +1222,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprKind::AddrOf(_, ref e) | hir::ExprKind::Cast(ref e, _) | hir::ExprKind::Type(ref e, _) | - hir::ExprKind::Use(ref e) | + hir::ExprKind::DropTemps(ref e) | hir::ExprKind::Unary(_, ref e) | hir::ExprKind::Yield(ref e) | hir::ExprKind::Repeat(ref e, _) => { @@ -1526,7 +1526,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) { hir::ExprKind::Match(..) | hir::ExprKind::While(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Index(..) | hir::ExprKind::Field(..) | hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) | - hir::ExprKind::Cast(..) | hir::ExprKind::Use(..) | hir::ExprKind::Unary(..) | + hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) | hir::ExprKind::Lit(_) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 6ca1e0ea84ac0..b6df7f2326a49 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -677,7 +677,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) | hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Unary(..) | hir::ExprKind::Yield(..) | - hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::Use(..) | + hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::If(..) | hir::ExprKind::Binary(..) | hir::ExprKind::While(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) | diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index f9491a1e8f4e6..2b88f273adce4 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -908,8 +908,8 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr: visitor.cx.var_parent = visitor.cx.parent; } - hir::ExprKind::Use(ref expr) => { - // `Use(expr)` does not denote a conditional scope. + hir::ExprKind::DropTemps(ref expr) => { + // `DropTemps(expr)` does not denote a conditional scope. // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`. terminating(expr.hir_id.local_id); } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index e54a24f4df197..07701952b3221 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -759,7 +759,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } } - hir::ExprKind::Use(ref source) => { + hir::ExprKind::DropTemps(ref source) => { ExprKind::Use { source: source.to_ref() } } hir::ExprKind::Box(ref value) => { diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index ce54035fe67db..881f63e994d59 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -437,7 +437,7 @@ fn check_expr_kind<'a, 'tcx>( hir::ExprKind::AddrOf(_, ref expr) | hir::ExprKind::Repeat(ref expr, _) | hir::ExprKind::Type(ref expr, _) | - hir::ExprKind::Use(ref expr) => { + hir::ExprKind::DropTemps(ref expr) => { v.check_expr(&expr) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f11638478923f..f6c263b98551f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4538,7 +4538,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_expr_eq_type(&e, ty); ty } - ExprKind::Use(ref e) => { + ExprKind::DropTemps(ref e) => { self.check_expr_with_expectation(e, expected) } ExprKind::Array(ref args) => { From 2cefd9987b36488a3d6f0e2b350a8abd84cfce25 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 27 Apr 2019 12:44:32 +0100 Subject: [PATCH 08/16] Pull `is_range_literal` out into `lowering` --- src/librustc/hir/lowering.rs | 62 ++++++++++++++++++++++++++ src/librustc_typeck/check/demand.rs | 68 +---------------------------- 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index df455a725c5ba..b84ee579a10a5 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -5400,3 +5400,65 @@ fn body_ids(bodies: &BTreeMap) -> Vec { 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 +} diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 689996ccb25a9..8ae66a96c763a 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -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>, From 5ec2f5e87ee5b56c1b4d78b5443558645fb2b2a1 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 27 Apr 2019 12:44:51 +0100 Subject: [PATCH 09/16] Suggest using an inclusive range for an overflowing endpoint --- src/librustc_lint/types.rs | 76 ++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index d3223c6edb809..989eb350442da 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] -use rustc::hir::Node; +use rustc::hir::{ExprKind, Node}; +use rustc::hir::lowering::is_range_literal; use rustc::ty::subst::SubstsRef; use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc::ty::layout::{self, IntegerExt, LayoutOf, VariantIdx}; @@ -129,21 +130,66 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { if lit_val < min || lit_val > max { let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id); if let Node::Expr(parent_expr) = cx.tcx.hir().get_by_hir_id(parent_id) { - if let hir::ExprKind::Cast(..) = parent_expr.node { - if let ty::Char = cx.tables.expr_ty(parent_expr).sty { - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - parent_expr.span, - "only u8 can be cast into char"); - err.span_suggestion( - parent_expr.span, - &"use a char literal instead", - format!("'\\u{{{:X}}}'", lit_val), - Applicability::MachineApplicable - ); - err.emit(); - return + match parent_expr.node { + hir::ExprKind::Cast(..) => { + if let ty::Char = cx.tables.expr_ty(parent_expr).sty { + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + parent_expr.span, + "only u8 can be cast into char", + ); + err.span_suggestion( + parent_expr.span, + &"use a char literal instead", + format!("'\\u{{{:X}}}'", lit_val), + Applicability::MachineApplicable, + ); + err.emit(); + return; + } + } + hir::ExprKind::Struct(..) + if is_range_literal(cx.sess(), parent_expr) => { + // We only want to handle exclusive (`..`) ranges, + // which are represented as `ExprKind::Struct`. + if let ExprKind::Struct(_, eps, _) = &parent_expr.node { + debug_assert_eq!(eps.len(), 2); + // We can suggest using an inclusive range + // (`..=`) instead only if it is the `end` that is + // overflowing and only by 1. + if eps[1].expr.hir_id == e.hir_id + && lit_val - 1 == max + { + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + parent_expr.span, + &format!( + "range endpoint is out of range \ + for {:?}", + t, + ), + ); + if let Ok(start) = cx.sess().source_map() + .span_to_snippet(eps[0].span) + { + let suggestion = format!( + "{}..={}", + start, + lit_val - 1, + ); + err.span_suggestion( + parent_expr.span, + &"use an inclusive range instead", + suggestion, + Applicability::MachineApplicable, + ); + err.emit(); + return; + } + } + } } + _ => {} } } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { From c5fdff26b58704f5b346ab2c96ea784f959eb3dd Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 27 Apr 2019 12:45:17 +0100 Subject: [PATCH 10/16] Add a test for overflowing endpoints --- .../ui/lint/lint-range-endpoint-overflow.rs | 15 +++++++++ .../lint/lint-range-endpoint-overflow.stderr | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/ui/lint/lint-range-endpoint-overflow.rs create mode 100644 src/test/ui/lint/lint-range-endpoint-overflow.stderr diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.rs b/src/test/ui/lint/lint-range-endpoint-overflow.rs new file mode 100644 index 0000000000000..36fcbd36c6a5a --- /dev/null +++ b/src/test/ui/lint/lint-range-endpoint-overflow.rs @@ -0,0 +1,15 @@ +#![deny(overflowing_literals)] + +fn main() { + let range_a = 0..256; //~ ERROR range endpoint is out of range for u8 + let range_b = 0..=255; // ok + let range_c = 0..=256; //~ ERROR literal out of range for u8 + let range_d = 256..5; //~ ERROR literal out of range for u8 + let range_e = 0..257; //~ ERROR literal out of range for u8 + + range_a.collect::>(); + range_b.collect::>(); + range_c.collect::>(); + range_d.collect::>(); + range_e.collect::>(); +} diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.stderr b/src/test/ui/lint/lint-range-endpoint-overflow.stderr new file mode 100644 index 0000000000000..56568765c1c6e --- /dev/null +++ b/src/test/ui/lint/lint-range-endpoint-overflow.stderr @@ -0,0 +1,32 @@ +error: range endpoint is out of range for u8 + --> $DIR/lint-range-endpoint-overflow.rs:4:19 + | +LL | let range_a = 0..256; + | ^^^^^^ help: use an inclusive range instead: `0..=255` + | +note: lint level defined here + --> $DIR/lint-range-endpoint-overflow.rs:1:9 + | +LL | #![deny(overflowing_literals)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: literal out of range for u8 + --> $DIR/lint-range-endpoint-overflow.rs:6:23 + | +LL | let range_c = 0..=256; + | ^^^ + +error: literal out of range for u8 + --> $DIR/lint-range-endpoint-overflow.rs:7:19 + | +LL | let range_d = 256..5; + | ^^^ + +error: literal out of range for u8 + --> $DIR/lint-range-endpoint-overflow.rs:8:22 + | +LL | let range_e = 0..257; + | ^^^ + +error: aborting due to 4 previous errors + From 218982bef07c7d8518348f5d2a2eecf57edc0219 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 27 Apr 2019 12:56:57 +0100 Subject: [PATCH 11/16] Place types inside backticks --- src/librustc_lint/types.rs | 12 +-- src/test/ui/cast_char.rs | 4 +- src/test/ui/cast_char.stderr | 8 +- src/test/ui/enum/enum-discrim-too-small2.rs | 8 +- .../ui/enum/enum-discrim-too-small2.stderr | 8 +- src/test/ui/lint/deny-overflowing-literals.rs | 2 +- .../ui/lint/deny-overflowing-literals.stderr | 2 +- .../ui/lint/lint-range-endpoint-overflow.rs | 8 +- .../lint/lint-range-endpoint-overflow.stderr | 8 +- src/test/ui/lint/lint-type-limits2.rs | 2 +- src/test/ui/lint/lint-type-limits2.stderr | 2 +- src/test/ui/lint/lint-type-limits3.rs | 2 +- src/test/ui/lint/lint-type-limits3.stderr | 2 +- src/test/ui/lint/lint-type-overflow.rs | 38 +++++----- src/test/ui/lint/lint-type-overflow.stderr | 74 +++++++++---------- src/test/ui/lint/lint-type-overflow2.stderr | 10 +-- src/test/ui/lint/type-overflow.stderr | 2 +- 17 files changed, 95 insertions(+), 97 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 989eb350442da..d3b0b1169bf3c 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -106,7 +106,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { cx.span_lint( OVERFLOWING_LITERALS, e.span, - &format!("literal out of range for {:?}", t), + &format!("literal out of range for `{:?}`", t), ); return; } @@ -136,11 +136,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { let mut err = cx.struct_span_lint( OVERFLOWING_LITERALS, parent_expr.span, - "only u8 can be cast into char", + "only `u8` can be cast into `char`", ); err.span_suggestion( parent_expr.span, - &"use a char literal instead", + &"use a `char` literal instead", format!("'\\u{{{:X}}}'", lit_val), Applicability::MachineApplicable, ); @@ -165,7 +165,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { parent_expr.span, &format!( "range endpoint is out of range \ - for {:?}", + for `{:?}`", t, ), ); @@ -206,7 +206,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { cx.span_lint( OVERFLOWING_LITERALS, e.span, - &format!("literal out of range for {:?}", t), + &format!("literal out of range for `{:?}`", t), ); } } @@ -224,7 +224,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { if is_infinite == Ok(true) { cx.span_lint(OVERFLOWING_LITERALS, e.span, - &format!("literal out of range for {:?}", t)); + &format!("literal out of range for `{:?}`", t)); } } _ => (), diff --git a/src/test/ui/cast_char.rs b/src/test/ui/cast_char.rs index 8c319af9c96e7..9634ed56f7b72 100644 --- a/src/test/ui/cast_char.rs +++ b/src/test/ui/cast_char.rs @@ -2,9 +2,9 @@ fn main() { const XYZ: char = 0x1F888 as char; - //~^ ERROR only u8 can be cast into char + //~^ ERROR only `u8` can be cast into `char` const XY: char = 129160 as char; - //~^ ERROR only u8 can be cast into char + //~^ ERROR only `u8` can be cast into `char` const ZYX: char = '\u{01F888}'; println!("{}", XYZ); } diff --git a/src/test/ui/cast_char.stderr b/src/test/ui/cast_char.stderr index f0c9b8988903b..37ef98bcb5067 100644 --- a/src/test/ui/cast_char.stderr +++ b/src/test/ui/cast_char.stderr @@ -1,8 +1,8 @@ -error: only u8 can be cast into char +error: only `u8` can be cast into `char` --> $DIR/cast_char.rs:4:23 | LL | const XYZ: char = 0x1F888 as char; - | ^^^^^^^^^^^^^^^ help: use a char literal instead: `'\u{1F888}'` + | ^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` | note: lint level defined here --> $DIR/cast_char.rs:1:9 @@ -10,11 +10,11 @@ note: lint level defined here LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: only u8 can be cast into char +error: only `u8` can be cast into `char` --> $DIR/cast_char.rs:6:22 | LL | const XY: char = 129160 as char; - | ^^^^^^^^^^^^^^ help: use a char literal instead: `'\u{1F888}'` + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` error: aborting due to 2 previous errors diff --git a/src/test/ui/enum/enum-discrim-too-small2.rs b/src/test/ui/enum/enum-discrim-too-small2.rs index af60564302599..85cd73d6f0855 100644 --- a/src/test/ui/enum/enum-discrim-too-small2.rs +++ b/src/test/ui/enum/enum-discrim-too-small2.rs @@ -5,28 +5,28 @@ enum Ei8 { Ai8 = 23, Bi8 = -23, - Ci8 = 223, //~ ERROR literal out of range for i8 + Ci8 = 223, //~ ERROR literal out of range for `i8` } #[repr(i16)] enum Ei16 { Ai16 = 23, Bi16 = -22333, - Ci16 = 55555, //~ ERROR literal out of range for i16 + Ci16 = 55555, //~ ERROR literal out of range for `i16` } #[repr(i32)] enum Ei32 { Ai32 = 23, Bi32 = -2_000_000_000, - Ci32 = 3_000_000_000, //~ ERROR literal out of range for i32 + Ci32 = 3_000_000_000, //~ ERROR literal out of range for `i32` } #[repr(i64)] enum Ei64 { Ai64 = 23, Bi64 = -9223372036854775808, - Ci64 = 9223372036854775809, //~ ERROR literal out of range for i64 + Ci64 = 9223372036854775809, //~ ERROR literal out of range for `i64` } // u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a diff --git a/src/test/ui/enum/enum-discrim-too-small2.stderr b/src/test/ui/enum/enum-discrim-too-small2.stderr index 6340f5a856ed3..f7220044ba42d 100644 --- a/src/test/ui/enum/enum-discrim-too-small2.stderr +++ b/src/test/ui/enum/enum-discrim-too-small2.stderr @@ -1,4 +1,4 @@ -error: literal out of range for i8 +error: literal out of range for `i8` --> $DIR/enum-discrim-too-small2.rs:8:11 | LL | Ci8 = 223, @@ -10,19 +10,19 @@ note: lint level defined here LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i16 +error: literal out of range for `i16` --> $DIR/enum-discrim-too-small2.rs:15:12 | LL | Ci16 = 55555, | ^^^^^ -error: literal out of range for i32 +error: literal out of range for `i32` --> $DIR/enum-discrim-too-small2.rs:22:12 | LL | Ci32 = 3_000_000_000, | ^^^^^^^^^^^^^ -error: literal out of range for i64 +error: literal out of range for `i64` --> $DIR/enum-discrim-too-small2.rs:29:12 | LL | Ci64 = 9223372036854775809, diff --git a/src/test/ui/lint/deny-overflowing-literals.rs b/src/test/ui/lint/deny-overflowing-literals.rs index ebd6654d39b1f..b887f66e94bd6 100644 --- a/src/test/ui/lint/deny-overflowing-literals.rs +++ b/src/test/ui/lint/deny-overflowing-literals.rs @@ -1,4 +1,4 @@ fn main() { let x: u8 = 256; - //~^ error: literal out of range for u8 + //~^ error: literal out of range for `u8` } diff --git a/src/test/ui/lint/deny-overflowing-literals.stderr b/src/test/ui/lint/deny-overflowing-literals.stderr index 7313dd0bfb5a7..1263a7bb7fd1b 100644 --- a/src/test/ui/lint/deny-overflowing-literals.stderr +++ b/src/test/ui/lint/deny-overflowing-literals.stderr @@ -1,4 +1,4 @@ -error: literal out of range for u8 +error: literal out of range for `u8` --> $DIR/deny-overflowing-literals.rs:2:17 | LL | let x: u8 = 256; diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.rs b/src/test/ui/lint/lint-range-endpoint-overflow.rs index 36fcbd36c6a5a..f2aeb24a79535 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.rs +++ b/src/test/ui/lint/lint-range-endpoint-overflow.rs @@ -1,11 +1,11 @@ #![deny(overflowing_literals)] fn main() { - let range_a = 0..256; //~ ERROR range endpoint is out of range for u8 + let range_a = 0..256; //~ ERROR range endpoint is out of range for `u8` let range_b = 0..=255; // ok - let range_c = 0..=256; //~ ERROR literal out of range for u8 - let range_d = 256..5; //~ ERROR literal out of range for u8 - let range_e = 0..257; //~ ERROR literal out of range for u8 + let range_c = 0..=256; //~ ERROR literal out of range for `u8` + let range_d = 256..5; //~ ERROR literal out of range for `u8` + let range_e = 0..257; //~ ERROR literal out of range for `u8` range_a.collect::>(); range_b.collect::>(); diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.stderr b/src/test/ui/lint/lint-range-endpoint-overflow.stderr index 56568765c1c6e..b2dff2c8d05e4 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.stderr +++ b/src/test/ui/lint/lint-range-endpoint-overflow.stderr @@ -1,4 +1,4 @@ -error: range endpoint is out of range for u8 +error: range endpoint is out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:4:19 | LL | let range_a = 0..256; @@ -10,19 +10,19 @@ note: lint level defined here LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for u8 +error: literal out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:6:23 | LL | let range_c = 0..=256; | ^^^ -error: literal out of range for u8 +error: literal out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:7:19 | LL | let range_d = 256..5; | ^^^ -error: literal out of range for u8 +error: literal out of range for `u8` --> $DIR/lint-range-endpoint-overflow.rs:8:22 | LL | let range_e = 0..257; diff --git a/src/test/ui/lint/lint-type-limits2.rs b/src/test/ui/lint/lint-type-limits2.rs index c4486e0676887..3f90119cd8954 100644 --- a/src/test/ui/lint/lint-type-limits2.rs +++ b/src/test/ui/lint/lint-type-limits2.rs @@ -11,5 +11,5 @@ fn bar() -> i8 { fn baz() -> bool { 128 > bar() //~ ERROR comparison is useless due to type limits - //~| WARN literal out of range for i8 + //~| WARN literal out of range for `i8` } diff --git a/src/test/ui/lint/lint-type-limits2.stderr b/src/test/ui/lint/lint-type-limits2.stderr index e7bc407422a12..0b3d292856707 100644 --- a/src/test/ui/lint/lint-type-limits2.stderr +++ b/src/test/ui/lint/lint-type-limits2.stderr @@ -6,7 +6,7 @@ LL | 128 > bar() | = note: requested on the command line with `-D unused-comparisons` -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/lint-type-limits2.rs:13:5 | LL | 128 > bar() diff --git a/src/test/ui/lint/lint-type-limits3.rs b/src/test/ui/lint/lint-type-limits3.rs index a715c69f7849e..ceecf9ab30bb8 100644 --- a/src/test/ui/lint/lint-type-limits3.rs +++ b/src/test/ui/lint/lint-type-limits3.rs @@ -7,7 +7,7 @@ fn main() { } fn qux() { let mut i = 1i8; while 200 != i { //~ ERROR comparison is useless due to type limits - //~| WARN literal out of range for i8 + //~| WARN literal out of range for `i8` i += 1; } } diff --git a/src/test/ui/lint/lint-type-limits3.stderr b/src/test/ui/lint/lint-type-limits3.stderr index 742b6695c24f2..70cd9c859ecf3 100644 --- a/src/test/ui/lint/lint-type-limits3.stderr +++ b/src/test/ui/lint/lint-type-limits3.stderr @@ -6,7 +6,7 @@ LL | while 200 != i { | = note: requested on the command line with `-D unused-comparisons` -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/lint-type-limits3.rs:9:11 | LL | while 200 != i { diff --git a/src/test/ui/lint/lint-type-overflow.rs b/src/test/ui/lint/lint-type-overflow.rs index 847cdf31dc788..9672da6d3586c 100644 --- a/src/test/ui/lint/lint-type-overflow.rs +++ b/src/test/ui/lint/lint-type-overflow.rs @@ -1,5 +1,3 @@ -// - #![deny(overflowing_literals)] fn test(x: i8) { @@ -9,39 +7,39 @@ fn test(x: i8) { #[allow(unused_variables)] fn main() { let x1: u8 = 255; // should be OK - let x1: u8 = 256; //~ error: literal out of range for u8 + let x1: u8 = 256; //~ error: literal out of range for `u8` let x1 = 255_u8; // should be OK - let x1 = 256_u8; //~ error: literal out of range for u8 + let x1 = 256_u8; //~ error: literal out of range for `u8` let x2: i8 = -128; // should be OK - let x1: i8 = 128; //~ error: literal out of range for i8 + let x1: i8 = 128; //~ error: literal out of range for `i8` - let x3: i8 = -129; //~ error: literal out of range for i8 - let x3: i8 = -(129); //~ error: literal out of range for i8 - let x3: i8 = -{129}; //~ error: literal out of range for i8 + let x3: i8 = -129; //~ error: literal out of range for `i8` + let x3: i8 = -(129); //~ error: literal out of range for `i8` + let x3: i8 = -{129}; //~ error: literal out of range for `i8` - test(1000); //~ error: literal out of range for i8 + test(1000); //~ error: literal out of range for `i8` - let x = 128_i8; //~ error: literal out of range for i8 + let x = 128_i8; //~ error: literal out of range for `i8` let x = 127_i8; let x = -128_i8; let x = -(128_i8); - let x = -129_i8; //~ error: literal out of range for i8 + let x = -129_i8; //~ error: literal out of range for `i8` let x: i32 = 2147483647; // should be OK let x = 2147483647_i32; // should be OK - let x: i32 = 2147483648; //~ error: literal out of range for i32 - let x = 2147483648_i32; //~ error: literal out of range for i32 + let x: i32 = 2147483648; //~ error: literal out of range for `i32` + let x = 2147483648_i32; //~ error: literal out of range for `i32` let x: i32 = -2147483648; // should be OK let x = -2147483648_i32; // should be OK - let x: i32 = -2147483649; //~ error: literal out of range for i32 - let x = -2147483649_i32; //~ error: literal out of range for i32 - let x = 2147483648; //~ error: literal out of range for i32 + let x: i32 = -2147483649; //~ error: literal out of range for `i32` + let x = -2147483649_i32; //~ error: literal out of range for `i32` + let x = 2147483648; //~ error: literal out of range for `i32` - let x = 9223372036854775808_i64; //~ error: literal out of range for i64 + let x = 9223372036854775808_i64; //~ error: literal out of range for `i64` let x = -9223372036854775808_i64; // should be OK - let x = 18446744073709551615_i64; //~ error: literal out of range for i64 - let x: i64 = -9223372036854775809; //~ error: literal out of range for i64 - let x = -9223372036854775809_i64; //~ error: literal out of range for i64 + let x = 18446744073709551615_i64; //~ error: literal out of range for `i64` + let x: i64 = -9223372036854775809; //~ error: literal out of range for `i64` + let x = -9223372036854775809_i64; //~ error: literal out of range for `i64` } diff --git a/src/test/ui/lint/lint-type-overflow.stderr b/src/test/ui/lint/lint-type-overflow.stderr index 9da007457aa83..6fcd9b58b2dc7 100644 --- a/src/test/ui/lint/lint-type-overflow.stderr +++ b/src/test/ui/lint/lint-type-overflow.stderr @@ -1,113 +1,113 @@ -error: literal out of range for u8 - --> $DIR/lint-type-overflow.rs:12:18 +error: literal out of range for `u8` + --> $DIR/lint-type-overflow.rs:10:18 | LL | let x1: u8 = 256; | ^^^ | note: lint level defined here - --> $DIR/lint-type-overflow.rs:3:9 + --> $DIR/lint-type-overflow.rs:1:9 | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for u8 - --> $DIR/lint-type-overflow.rs:15:14 +error: literal out of range for `u8` + --> $DIR/lint-type-overflow.rs:13:14 | LL | let x1 = 256_u8; | ^^^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:18:18 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:16:18 | LL | let x1: i8 = 128; | ^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:20:19 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:18:19 | LL | let x3: i8 = -129; | ^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:21:19 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:19:19 | LL | let x3: i8 = -(129); | ^^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:22:20 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:20:20 | LL | let x3: i8 = -{129}; | ^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:24:10 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:22:10 | LL | test(1000); | ^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:26:13 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:24:13 | LL | let x = 128_i8; | ^^^^^^ -error: literal out of range for i8 - --> $DIR/lint-type-overflow.rs:30:14 +error: literal out of range for `i8` + --> $DIR/lint-type-overflow.rs:28:14 | LL | let x = -129_i8; | ^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:34:18 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:32:18 | LL | let x: i32 = 2147483648; | ^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:35:13 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:33:13 | LL | let x = 2147483648_i32; | ^^^^^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:38:19 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:36:19 | LL | let x: i32 = -2147483649; | ^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:39:14 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:37:14 | LL | let x = -2147483649_i32; | ^^^^^^^^^^^^^^ -error: literal out of range for i32 - --> $DIR/lint-type-overflow.rs:40:13 +error: literal out of range for `i32` + --> $DIR/lint-type-overflow.rs:38:13 | LL | let x = 2147483648; | ^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:42:13 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:40:13 | LL | let x = 9223372036854775808_i64; | ^^^^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:44:13 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:42:13 | LL | let x = 18446744073709551615_i64; | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:45:19 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:43:19 | LL | let x: i64 = -9223372036854775809; | ^^^^^^^^^^^^^^^^^^^ -error: literal out of range for i64 - --> $DIR/lint-type-overflow.rs:46:14 +error: literal out of range for `i64` + --> $DIR/lint-type-overflow.rs:44:14 | LL | let x = -9223372036854775809_i64; | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/lint-type-overflow2.stderr b/src/test/ui/lint/lint-type-overflow2.stderr index 5255f6c75934d..c76e9e25d5a93 100644 --- a/src/test/ui/lint/lint-type-overflow2.stderr +++ b/src/test/ui/lint/lint-type-overflow2.stderr @@ -1,4 +1,4 @@ -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/lint-type-overflow2.rs:9:20 | LL | let x2: i8 = --128; @@ -10,25 +10,25 @@ note: lint level defined here LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f32 +warning: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:11:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f32 +warning: literal out of range for `f32` --> $DIR/lint-type-overflow2.rs:12:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f64 +warning: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:13:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: literal out of range for f64 +warning: literal out of range for `f64` --> $DIR/lint-type-overflow2.rs:14:14 | LL | let x = 1.7976931348623159e+308_f64; diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 5235c9851b53f..dabfb876fbb92 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -1,4 +1,4 @@ -warning: literal out of range for i8 +warning: literal out of range for `i8` --> $DIR/type-overflow.rs:5:17 | LL | let error = 255i8; From 0556e4891ef6e73ee886526cd39d4887850b1a1a Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 27 Apr 2019 13:26:06 +0100 Subject: [PATCH 12/16] Preserve literal suffixes --- src/librustc_lint/types.rs | 18 +++++++++++++++++- .../ui/lint/lint-range-endpoint-overflow.rs | 1 + .../lint/lint-range-endpoint-overflow.stderr | 8 +++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index d3b0b1169bf3c..3b3e2455fa77d 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -172,10 +172,26 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { if let Ok(start) = cx.sess().source_map() .span_to_snippet(eps[0].span) { + use ast::{LitKind::*, LitIntType}; + // We need to preserve the literal's suffix, + // as it may determine typing information. + let suffix = match lit.node { + Int(_, LitIntType::Signed(s)) => { + format!("{}", s) + } + Int(_, LitIntType::Unsigned(s)) => { + format!("{}", s) + } + Int(_, LitIntType::Unsuffixed) => { + "".to_owned() + } + _ => bug!(), + }; let suggestion = format!( - "{}..={}", + "{}..={}{}", start, lit_val - 1, + suffix, ); err.span_suggestion( parent_expr.span, diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.rs b/src/test/ui/lint/lint-range-endpoint-overflow.rs index f2aeb24a79535..5d7430007abb3 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.rs +++ b/src/test/ui/lint/lint-range-endpoint-overflow.rs @@ -6,6 +6,7 @@ fn main() { let range_c = 0..=256; //~ ERROR literal out of range for `u8` let range_d = 256..5; //~ ERROR literal out of range for `u8` let range_e = 0..257; //~ ERROR literal out of range for `u8` + let _range_f = 0..256u8; //~ ERROR range endpoint is out of range for `u8` range_a.collect::>(); range_b.collect::>(); diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.stderr b/src/test/ui/lint/lint-range-endpoint-overflow.stderr index b2dff2c8d05e4..72f548cf4adb1 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.stderr +++ b/src/test/ui/lint/lint-range-endpoint-overflow.stderr @@ -28,5 +28,11 @@ error: literal out of range for `u8` LL | let range_e = 0..257; | ^^^ -error: aborting due to 4 previous errors +error: range endpoint is out of range for `u8` + --> $DIR/lint-range-endpoint-overflow.rs:9:20 + | +LL | let _range_f = 0..256u8; + | ^^^^^^^^ help: use an inclusive range instead: `0..=255u8` + +error: aborting due to 5 previous errors From 13962aff37bbe23e1c9063e2298318e1a9da8af2 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 27 Apr 2019 13:52:42 +0100 Subject: [PATCH 13/16] Include signed integer types in the lint --- src/librustc_lint/types.rs | 142 +++++++++++------- .../ui/lint/lint-range-endpoint-overflow.rs | 1 + .../lint/lint-range-endpoint-overflow.stderr | 8 +- 3 files changed, 96 insertions(+), 55 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 3b3e2455fa77d..379b20ab76034 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -58,6 +58,61 @@ impl TypeLimits { } } +/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint. +/// Returns `true` iff the lint was overridden. +fn lint_overflowing_range_endpoint<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + lit: &ast::Lit, + lit_val: u128, + max: u128, + expr: &'tcx hir::Expr, + parent_expr: &'tcx hir::Expr, + ty: impl std::fmt::Debug, +) -> bool { + // We only want to handle exclusive (`..`) ranges, + // which are represented as `ExprKind::Struct`. + if let ExprKind::Struct(_, eps, _) = &parent_expr.node { + debug_assert_eq!(eps.len(), 2); + // We can suggest using an inclusive range + // (`..=`) instead only if it is the `end` that is + // overflowing and only by 1. + if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + parent_expr.span, + &format!("range endpoint is out of range for `{:?}`", ty), + ); + if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { + use ast::{LitKind, LitIntType}; + // We need to preserve the literal's suffix, + // as it may determine typing information. + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s), + LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s), + LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), + _ => bug!(), + }; + let suggestion = format!( + "{}..={}{}", + start, + lit_val - 1, + suffix, + ); + err.span_suggestion( + parent_expr.span, + &"use an inclusive range instead", + suggestion, + Applicability::MachineApplicable, + ); + err.emit(); + return true; + } + } + } + + false +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) { match e.node { @@ -103,6 +158,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { ); return; } + + let par_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id); + if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(par_id) { + if let hir::ExprKind::Struct(..) = par_e.node { + if is_range_literal(cx.sess(), par_e) + && lint_overflowing_range_endpoint( + cx, + lit, + v, + max, + e, + par_e, + t, + ) + { + // The overflowing literal lint was overridden. + return; + } + } + } + cx.span_lint( OVERFLOWING_LITERALS, e.span, @@ -150,61 +226,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } hir::ExprKind::Struct(..) if is_range_literal(cx.sess(), parent_expr) => { - // We only want to handle exclusive (`..`) ranges, - // which are represented as `ExprKind::Struct`. - if let ExprKind::Struct(_, eps, _) = &parent_expr.node { - debug_assert_eq!(eps.len(), 2); - // We can suggest using an inclusive range - // (`..=`) instead only if it is the `end` that is - // overflowing and only by 1. - if eps[1].expr.hir_id == e.hir_id - && lit_val - 1 == max - { - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - parent_expr.span, - &format!( - "range endpoint is out of range \ - for `{:?}`", - t, - ), - ); - if let Ok(start) = cx.sess().source_map() - .span_to_snippet(eps[0].span) - { - use ast::{LitKind::*, LitIntType}; - // We need to preserve the literal's suffix, - // as it may determine typing information. - let suffix = match lit.node { - Int(_, LitIntType::Signed(s)) => { - format!("{}", s) - } - Int(_, LitIntType::Unsigned(s)) => { - format!("{}", s) - } - Int(_, LitIntType::Unsuffixed) => { - "".to_owned() - } - _ => bug!(), - }; - let suggestion = format!( - "{}..={}{}", - start, - lit_val - 1, - suffix, - ); - err.span_suggestion( - parent_expr.span, - &"use an inclusive range instead", - suggestion, - Applicability::MachineApplicable, - ); - err.emit(); - return; - } - } + if lint_overflowing_range_endpoint( + cx, + lit, + lit_val, + max, + e, + parent_expr, + t, + ) { + // The overflowing literal lint was overridden. + return; } - } + } _ => {} } } diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.rs b/src/test/ui/lint/lint-range-endpoint-overflow.rs index 5d7430007abb3..7034d56aa5d83 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.rs +++ b/src/test/ui/lint/lint-range-endpoint-overflow.rs @@ -7,6 +7,7 @@ fn main() { let range_d = 256..5; //~ ERROR literal out of range for `u8` let range_e = 0..257; //~ ERROR literal out of range for `u8` let _range_f = 0..256u8; //~ ERROR range endpoint is out of range for `u8` + let _range_g = 0..128i8; //~ ERROR range endpoint is out of range for `i8` range_a.collect::>(); range_b.collect::>(); diff --git a/src/test/ui/lint/lint-range-endpoint-overflow.stderr b/src/test/ui/lint/lint-range-endpoint-overflow.stderr index 72f548cf4adb1..939451d6bc022 100644 --- a/src/test/ui/lint/lint-range-endpoint-overflow.stderr +++ b/src/test/ui/lint/lint-range-endpoint-overflow.stderr @@ -34,5 +34,11 @@ error: range endpoint is out of range for `u8` LL | let _range_f = 0..256u8; | ^^^^^^^^ help: use an inclusive range instead: `0..=255u8` -error: aborting due to 5 previous errors +error: range endpoint is out of range for `i8` + --> $DIR/lint-range-endpoint-overflow.rs:10:20 + | +LL | let _range_g = 0..128i8; + | ^^^^^^^^ help: use an inclusive range instead: `0..=127i8` + +error: aborting due to 6 previous errors From 5e709b2910a029ec4f0c640233526e7ed335c26c Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 30 Apr 2019 23:17:58 +0100 Subject: [PATCH 14/16] Refactor is_range_literal --- src/librustc/hir/lowering.rs | 34 +++++++++++++++++----------------- src/librustc_lint/types.rs | 7 +------ 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index b84ee579a10a5..736736fe48b1b 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -5401,29 +5401,29 @@ fn body_ids(bodies: &BTreeMap) -> Vec { body_ids } -/// This function checks if the specified expression is a built-in range literal. +/// 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()); + // Returns whether the given path represents a (desugared) range, + // either in std or core, i.e. has either a `::std::ops::Range` or + // `::core::ops::Range` prefix. + fn is_range_path(path: &Path) -> bool { + let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.as_str().to_string()).collect(); + let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect(); - 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") + // "{{root}}" is the equivalent of `::` prefix in `Path`. + if let ["{{root}}", std_core, "ops", range] = segs.as_slice() { + (*std_core == "std" || *std_core == "core") && 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. + // Check whether a span corresponding to a range expression is a + // range literal, rather than an explicit struct or `new()` call. + fn is_range_literal(sess: &Session, span: &Span) -> bool { let source_map = sess.source_map(); let end_point = source_map.end_point(*span); @@ -5438,21 +5438,21 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool { // 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); + return is_range_path(&path) && is_range_literal(sess, &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); + return is_range_path(&path) && is_range_literal(sess, &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; + let new_call = segment.ident.as_str() == "new"; + return is_range_path(&path) && is_range_literal(sess, &expr.span) && new_call; } } } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 379b20ab76034..5cafd3d676f3f 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -92,12 +92,7 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>( LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), _ => bug!(), }; - let suggestion = format!( - "{}..={}{}", - start, - lit_val - 1, - suffix, - ); + let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); err.span_suggestion( parent_expr.span, &"use an inclusive range instead", From 80f90d866b24e1a8aadda87f58414a5ad80d5c6f Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 30 Apr 2019 23:37:38 +0100 Subject: [PATCH 15/16] Pull out some functors for readability --- src/librustc_lint/types.rs | 582 +++++++++++++++++++------------------ 1 file changed, 293 insertions(+), 289 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 5cafd3d676f3f..f4ebfd79fe1db 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -108,6 +108,297 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>( false } +// For `isize` & `usize`, be conservative with the warnings, so that the +// warnings are consistent between 32- and 64-bit platforms. +fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { + match int_ty { + ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128), + ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128), + ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128), + ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128), + ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128), + ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()), + } +} + +fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { + match uint_ty { + ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128), + ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128), + ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128), + ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128), + ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128), + ast::UintTy::U128 => (u128::min_value(), u128::max_value()), + } +} + +fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &ast::Lit) -> Option { + let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; + let firstch = src.chars().next()?; + + if firstch == '0' { + match src.chars().nth(1) { + Some('x') | Some('b') => return Some(src), + _ => return None, + } + } + + None +} + +fn report_bin_hex_error( + cx: &LateContext<'_, '_>, + expr: &hir::Expr, + ty: attr::IntType, + repr_str: String, + val: u128, + negative: bool, +) { + let size = layout::Integer::from_attr(&cx.tcx, ty).size(); + let (t, actually) = match ty { + attr::IntType::SignedInt(t) => { + let actually = sign_extend(val, size) as i128; + (format!("{:?}", t), actually.to_string()) + } + attr::IntType::UnsignedInt(t) => { + let actually = truncate(val, size); + (format!("{:?}", t), actually.to_string()) + } + }; + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + expr.span, + &format!("literal out of range for {}", t), + ); + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + an `{}` and will become `{}{}`", + repr_str, val, t, actually, t + )); + if let Some(sugg_ty) = + get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) + { + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + let (sans_suffix, _) = repr_str.split_at(pos); + err.span_suggestion( + expr.span, + &format!("consider using `{}` instead", sugg_ty), + format!("{}{}", sans_suffix, sugg_ty), + Applicability::MachineApplicable + ); + } else { + err.help(&format!("consider using `{}` instead", sugg_ty)); + } + } + + err.emit(); +} + +// This function finds the next fitting type and generates a suggestion string. +// It searches for fitting types in the following way (`X < Y`): +// - `iX`: if literal fits in `uX` => `uX`, else => `iY` +// - `-iX` => `iY` +// - `uX` => `uY` +// +// No suggestion for: `isize`, `usize`. +fn get_type_suggestion<'a>( + t: Ty<'_>, + val: u128, + negative: bool, +) -> Option { + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + macro_rules! find_fit { + ($ty:expr, $val:expr, $negative:expr, + $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { + { + let _neg = if negative { 1 } else { 0 }; + match $ty { + $($type => { + $(if !negative && val <= uint_ty_range($utypes).1 { + return Some(format!("{:?}", $utypes)) + })* + $(if val <= int_ty_range($itypes).1 as u128 + _neg { + return Some(format!("{:?}", $itypes)) + })* + None + },)* + _ => None + } + } + } + } + match t.sty { + ty::Int(i) => find_fit!(i, val, negative, + I8 => [U8] => [I16, I32, I64, I128], + I16 => [U16] => [I32, I64, I128], + I32 => [U32] => [I64, I128], + I64 => [U64] => [I128], + I128 => [U128] => []), + ty::Uint(u) => find_fit!(u, val, negative, + U8 => [U8, U16, U32, U64, U128] => [], + U16 => [U16, U32, U64, U128] => [], + U32 => [U32, U64, U128] => [], + U64 => [U64, U128] => [], + U128 => [U128] => []), + _ => None, + } +} + +fn lint_int_literal<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + type_limits: &TypeLimits, + e: &'tcx hir::Expr, + lit: &ast::Lit, + t: ast::IntTy, + v: u128, +) { + let int_type = if let ast::IntTy::Isize = t { + cx.sess().target.isize_ty + } else { + t + }; + + let (_, max) = int_ty_range(int_type); + let max = max as u128; + let negative = type_limits.negated_expr_id == e.hir_id; + + // Detect literal value out of range [min, max] inclusive + // avoiding use of -min to prevent overflow/panic + if (negative && v > max + 1) || (!negative && v > max) { + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + report_bin_hex_error( + cx, + e, + attr::IntType::SignedInt(t), + repr_str, + v, + negative, + ); + return; + } + + let par_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id); + if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(par_id) { + if let hir::ExprKind::Struct(..) = par_e.node { + if is_range_literal(cx.sess(), par_e) + && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t) + { + // The overflowing literal lint was overridden. + return; + } + } + } + + cx.span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for `{:?}`", t), + ); + } +} + +fn lint_uint_literal<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + e: &'tcx hir::Expr, + lit: &ast::Lit, + t: ast::UintTy, +) { + let uint_type = if let ast::UintTy::Usize = t { + cx.sess().target.usize_ty + } else { + t + }; + let (min, max) = uint_ty_range(uint_type); + let lit_val: u128 = match lit.node { + // _v is u8, within range by definition + ast::LitKind::Byte(_v) => return, + ast::LitKind::Int(v, _) => v, + _ => bug!(), + }; + if lit_val < min || lit_val > max { + let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id); + if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(parent_id) { + match par_e.node { + hir::ExprKind::Cast(..) => { + if let ty::Char = cx.tables.expr_ty(par_e).sty { + let mut err = cx.struct_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + "only `u8` can be cast into `char`", + ); + err.span_suggestion( + par_e.span, + &"use a `char` literal instead", + format!("'\\u{{{:X}}}'", lit_val), + Applicability::MachineApplicable, + ); + err.emit(); + return; + } + } + hir::ExprKind::Struct(..) + if is_range_literal(cx.sess(), par_e) => { + if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) { + // The overflowing literal lint was overridden. + return; + } + } + _ => {} + } + } + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false); + return; + } + cx.span_lint( + OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for `{:?}`", t), + ); + } +} + +fn lint_literal<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + type_limits: &TypeLimits, + e: &'tcx hir::Expr, + lit: &ast::Lit, +) { + match cx.tables.node_type(e.hir_id).sty { + ty::Int(t) => { + match lit.node { + ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | + ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => { + lint_int_literal(cx, type_limits, e, lit, t, v) + } + _ => bug!(), + }; + } + ty::Uint(t) => { + lint_uint_literal(cx, e, lit, t) + } + ty::Float(t) => { + let is_infinite = match lit.node { + ast::LitKind::Float(v, _) | + ast::LitKind::FloatUnsuffixed(v) => { + match t { + ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), + ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), + } + } + _ => bug!(), + }; + if is_infinite == Ok(true) { + cx.span_lint(OVERFLOWING_LITERALS, + e.span, + &format!("literal out of range for `{:?}`", t)); + } + } + _ => {} + } +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) { match e.node { @@ -124,158 +415,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { "comparison is useless due to type limits"); } } - hir::ExprKind::Lit(ref lit) => { - match cx.tables.node_type(e.hir_id).sty { - ty::Int(t) => { - match lit.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | - ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => { - let int_type = if let ast::IntTy::Isize = t { - cx.sess().target.isize_ty - } else { - t - }; - let (_, max) = int_ty_range(int_type); - let max = max as u128; - let negative = self.negated_expr_id == e.hir_id; - - // Detect literal value out of range [min, max] inclusive - // avoiding use of -min to prevent overflow/panic - if (negative && v > max + 1) || (!negative && v > max) { - if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error( - cx, - e, - attr::IntType::SignedInt(t), - repr_str, - v, - negative, - ); - return; - } - - let par_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id); - if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(par_id) { - if let hir::ExprKind::Struct(..) = par_e.node { - if is_range_literal(cx.sess(), par_e) - && lint_overflowing_range_endpoint( - cx, - lit, - v, - max, - e, - par_e, - t, - ) - { - // The overflowing literal lint was overridden. - return; - } - } - } - - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for `{:?}`", t), - ); - return; - } - } - _ => bug!(), - }; - } - ty::Uint(t) => { - let uint_type = if let ast::UintTy::Usize = t { - cx.sess().target.usize_ty - } else { - t - }; - let (min, max) = uint_ty_range(uint_type); - let lit_val: u128 = match lit.node { - // _v is u8, within range by definition - ast::LitKind::Byte(_v) => return, - ast::LitKind::Int(v, _) => v, - _ => bug!(), - }; - if lit_val < min || lit_val > max { - let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id); - if let Node::Expr(parent_expr) = cx.tcx.hir().get_by_hir_id(parent_id) { - match parent_expr.node { - hir::ExprKind::Cast(..) => { - if let ty::Char = cx.tables.expr_ty(parent_expr).sty { - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - parent_expr.span, - "only `u8` can be cast into `char`", - ); - err.span_suggestion( - parent_expr.span, - &"use a `char` literal instead", - format!("'\\u{{{:X}}}'", lit_val), - Applicability::MachineApplicable, - ); - err.emit(); - return; - } - } - hir::ExprKind::Struct(..) - if is_range_literal(cx.sess(), parent_expr) => { - if lint_overflowing_range_endpoint( - cx, - lit, - lit_val, - max, - e, - parent_expr, - t, - ) { - // The overflowing literal lint was overridden. - return; - } - } - _ => {} - } - } - if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error( - cx, - e, - attr::IntType::UnsignedInt(t), - repr_str, - lit_val, - false, - ); - return; - } - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for `{:?}`", t), - ); - } - } - ty::Float(t) => { - let is_infinite = match lit.node { - ast::LitKind::Float(v, _) | - ast::LitKind::FloatUnsuffixed(v) => { - match t { - ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), - ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), - } - } - _ => bug!(), - }; - if is_infinite == Ok(true) { - cx.span_lint(OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for `{:?}`", t)); - } - } - _ => (), - }; - } - _ => (), + hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), + _ => {} }; fn is_valid(binop: hir::BinOp, v: T, min: T, max: T) -> bool { @@ -300,30 +441,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { }) } - // for isize & usize, be conservative with the warnings, so that the - // warnings are consistent between 32- and 64-bit platforms - fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { - match int_ty { - ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128), - ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128), - ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128), - ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128), - ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128), - ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()), - } - } - - fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { - match uint_ty { - ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128), - ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128), - ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128), - ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128), - ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128), - ast::UintTy::U128 => (u128::min_value(), u128::max_value()), - } - } - fn check_limits(cx: &LateContext<'_, '_>, binop: hir::BinOp, l: &hir::Expr, @@ -380,119 +497,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { _ => false, } } - - fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &ast::Lit) -> Option { - let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; - let firstch = src.chars().next()?; - - if firstch == '0' { - match src.chars().nth(1) { - Some('x') | Some('b') => return Some(src), - _ => return None, - } - } - - None - } - - // This function finds the next fitting type and generates a suggestion string. - // It searches for fitting types in the following way (`X < Y`): - // - `iX`: if literal fits in `uX` => `uX`, else => `iY` - // - `-iX` => `iY` - // - `uX` => `uY` - // - // No suggestion for: `isize`, `usize`. - fn get_type_suggestion<'a>( - t: Ty<'_>, - val: u128, - negative: bool, - ) -> Option { - use syntax::ast::IntTy::*; - use syntax::ast::UintTy::*; - macro_rules! find_fit { - ($ty:expr, $val:expr, $negative:expr, - $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { - { - let _neg = if negative { 1 } else { 0 }; - match $ty { - $($type => { - $(if !negative && val <= uint_ty_range($utypes).1 { - return Some(format!("{:?}", $utypes)) - })* - $(if val <= int_ty_range($itypes).1 as u128 + _neg { - return Some(format!("{:?}", $itypes)) - })* - None - },)* - _ => None - } - } - } - } - match t.sty { - ty::Int(i) => find_fit!(i, val, negative, - I8 => [U8] => [I16, I32, I64, I128], - I16 => [U16] => [I32, I64, I128], - I32 => [U32] => [I64, I128], - I64 => [U64] => [I128], - I128 => [U128] => []), - ty::Uint(u) => find_fit!(u, val, negative, - U8 => [U8, U16, U32, U64, U128] => [], - U16 => [U16, U32, U64, U128] => [], - U32 => [U32, U64, U128] => [], - U64 => [U64, U128] => [], - U128 => [U128] => []), - _ => None, - } - } - - fn report_bin_hex_error( - cx: &LateContext<'_, '_>, - expr: &hir::Expr, - ty: attr::IntType, - repr_str: String, - val: u128, - negative: bool, - ) { - let size = layout::Integer::from_attr(&cx.tcx, ty).size(); - let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { - let actually = sign_extend(val, size) as i128; - (format!("{:?}", t), actually.to_string()) - } - attr::IntType::UnsignedInt(t) => { - let actually = truncate(val, size); - (format!("{:?}", t), actually.to_string()) - } - }; - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - expr.span, - &format!("literal out of range for {}", t), - ); - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - an `{}` and will become `{}{}`", - repr_str, val, t, actually, t - )); - if let Some(sugg_ty) = - get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) - { - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - let (sans_suffix, _) = repr_str.split_at(pos); - err.span_suggestion( - expr.span, - &format!("consider using `{}` instead", sugg_ty), - format!("{}{}", sans_suffix, sugg_ty), - Applicability::MachineApplicable - ); - } else { - err.help(&format!("consider using `{}` instead", sugg_ty)); - } - } - - err.emit(); - } } } From b7711bff524716c27140256e6f6e78bbb8fd1283 Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 1 May 2019 00:08:01 +0100 Subject: [PATCH 16/16] Rename is_range_literal to is_lit --- src/librustc/hir/lowering.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 736736fe48b1b..0366d1e5c24e9 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -5423,7 +5423,7 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool { // Check whether a span corresponding to a range expression is a // range literal, rather than an explicit struct or `new()` call. - fn is_range_literal(sess: &Session, span: &Span) -> bool { + fn is_lit(sess: &Session, span: &Span) -> bool { let source_map = sess.source_map(); let end_point = source_map.end_point(*span); @@ -5438,13 +5438,13 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool { // 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) && is_range_literal(sess, &expr.span); + return is_range_path(&path) && is_lit(sess, &expr.span); } } // `..` desugars to its struct path. ExprKind::Path(QPath::Resolved(None, ref path)) => { - return is_range_path(&path) && is_range_literal(sess, &expr.span); + return is_range_path(&path) && is_lit(sess, &expr.span); } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. @@ -5452,7 +5452,7 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool { if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node { if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node { let new_call = segment.ident.as_str() == "new"; - return is_range_path(&path) && is_range_literal(sess, &expr.span) && new_call; + return is_range_path(&path) && is_lit(sess, &expr.span) && new_call; } } }