From e79036d17fa8037f9f3f7e98a9b4ef80fb943ca7 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 26 Sep 2019 11:00:53 +0100 Subject: hir: Disallow `target_feature` on constants This commit fixes an ICE when `target_feature` is applied to constants. Signed-off-by: David Wood --- src/test/ui/attributes/multiple-invalid.rs | 10 +++ src/test/ui/attributes/multiple-invalid.stderr | 21 +++++ src/test/ui/target-feature-gate.rs | 35 -------- src/test/ui/target-feature-gate.stderr | 12 --- src/test/ui/target-feature-wrong.rs | 48 ----------- src/test/ui/target-feature-wrong.stderr | 50 ------------ src/test/ui/target-feature/gate.rs | 35 ++++++++ src/test/ui/target-feature/gate.stderr | 12 +++ src/test/ui/target-feature/invalid-attribute.rs | 73 +++++++++++++++++ .../ui/target-feature/invalid-attribute.stderr | 95 ++++++++++++++++++++++ 10 files changed, 246 insertions(+), 145 deletions(-) create mode 100644 src/test/ui/attributes/multiple-invalid.rs create mode 100644 src/test/ui/attributes/multiple-invalid.stderr delete mode 100644 src/test/ui/target-feature-gate.rs delete mode 100644 src/test/ui/target-feature-gate.stderr delete mode 100644 src/test/ui/target-feature-wrong.rs delete mode 100644 src/test/ui/target-feature-wrong.stderr create mode 100644 src/test/ui/target-feature/gate.rs create mode 100644 src/test/ui/target-feature/gate.stderr create mode 100644 src/test/ui/target-feature/invalid-attribute.rs create mode 100644 src/test/ui/target-feature/invalid-attribute.stderr (limited to 'src/test') diff --git a/src/test/ui/attributes/multiple-invalid.rs b/src/test/ui/attributes/multiple-invalid.rs new file mode 100644 index 00000000000..ae044eb843b --- /dev/null +++ b/src/test/ui/attributes/multiple-invalid.rs @@ -0,0 +1,10 @@ +// This test checks that all expected errors occur when there are multiple invalid attributes +// on an item. + +#[inline] +//~^ ERROR attribute should be applied to function or closure [E0518] +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +const FOO: u8 = 0; + +fn main() { } diff --git a/src/test/ui/attributes/multiple-invalid.stderr b/src/test/ui/attributes/multiple-invalid.stderr new file mode 100644 index 00000000000..9bd29f15dbc --- /dev/null +++ b/src/test/ui/attributes/multiple-invalid.stderr @@ -0,0 +1,21 @@ +error[E0518]: attribute should be applied to function or closure + --> $DIR/multiple-invalid.rs:4:1 + | +LL | #[inline] + | ^^^^^^^^^ +... +LL | const FOO: u8 = 0; + | ------------------ not a function or closure + +error: attribute should be applied to a function + --> $DIR/multiple-invalid.rs:6:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const FOO: u8 = 0; + | ------------------ not a function + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/target-feature-gate.rs b/src/test/ui/target-feature-gate.rs deleted file mode 100644 index bc7f7caa107..00000000000 --- a/src/test/ui/target-feature-gate.rs +++ /dev/null @@ -1,35 +0,0 @@ -// ignore-arm -// ignore-aarch64 -// ignore-wasm -// ignore-emscripten -// ignore-mips -// ignore-mips64 -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-sparc -// ignore-sparc64 -// ignore-s390x -// gate-test-sse4a_target_feature -// gate-test-powerpc_target_feature -// gate-test-avx512_target_feature -// gate-test-tbm_target_feature -// gate-test-arm_target_feature -// gate-test-aarch64_target_feature -// gate-test-hexagon_target_feature -// gate-test-mips_target_feature -// gate-test-mmx_target_feature -// gate-test-wasm_target_feature -// gate-test-adx_target_feature -// gate-test-cmpxchg16b_target_feature -// gate-test-movbe_target_feature -// gate-test-rtm_target_feature -// gate-test-f16c_target_feature -// min-llvm-version 6.0 - -#[target_feature(enable = "avx512bw")] -//~^ ERROR: currently unstable -unsafe fn foo() { -} - -fn main() {} diff --git a/src/test/ui/target-feature-gate.stderr b/src/test/ui/target-feature-gate.stderr deleted file mode 100644 index 9f17110b6d8..00000000000 --- a/src/test/ui/target-feature-gate.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/target-feature-gate.rs:30:18 - | -LL | #[target_feature(enable = "avx512bw")] - | ^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see https://github.com/rust-lang/rust/issues/44839 - = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/target-feature-wrong.rs b/src/test/ui/target-feature-wrong.rs deleted file mode 100644 index 646a98763e1..00000000000 --- a/src/test/ui/target-feature-wrong.rs +++ /dev/null @@ -1,48 +0,0 @@ -// ignore-arm -// ignore-aarch64 -// ignore-wasm -// ignore-emscripten -// ignore-mips -// ignore-mips64 -// ignore-powerpc -// ignore-powerpc64 -// ignore-powerpc64le -// ignore-s390x -// ignore-sparc -// ignore-sparc64 - -#![feature(target_feature)] - -#[target_feature = "+sse2"] -//~^ ERROR malformed `target_feature` attribute -#[target_feature(enable = "foo")] -//~^ ERROR not valid for this target -//~| NOTE `foo` is not valid for this target -#[target_feature(bar)] -//~^ ERROR malformed `target_feature` attribute -#[target_feature(disable = "baz")] -//~^ ERROR malformed `target_feature` attribute -unsafe fn foo() {} - -#[target_feature(enable = "sse2")] -//~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions -//~| NOTE can only be applied to `unsafe` functions -fn bar() {} -//~^ NOTE not an `unsafe` function - -#[target_feature(enable = "sse2")] -//~^ ERROR attribute should be applied to a function -mod another {} -//~^ NOTE not a function - -#[inline(always)] -//~^ ERROR: cannot use `#[inline(always)]` -#[target_feature(enable = "sse2")] -unsafe fn test() {} - -fn main() { - unsafe { - foo(); - bar(); - } -} diff --git a/src/test/ui/target-feature-wrong.stderr b/src/test/ui/target-feature-wrong.stderr deleted file mode 100644 index 47ca5a5ca47..00000000000 --- a/src/test/ui/target-feature-wrong.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: malformed `target_feature` attribute input - --> $DIR/target-feature-wrong.rs:16:1 - | -LL | #[target_feature = "+sse2"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[target_feature(enable = "name")]` - -error: the feature named `foo` is not valid for this target - --> $DIR/target-feature-wrong.rs:18:18 - | -LL | #[target_feature(enable = "foo")] - | ^^^^^^^^^^^^^^ `foo` is not valid for this target - -error: malformed `target_feature` attribute input - --> $DIR/target-feature-wrong.rs:21:18 - | -LL | #[target_feature(bar)] - | ^^^ help: must be of the form: `enable = ".."` - -error: malformed `target_feature` attribute input - --> $DIR/target-feature-wrong.rs:23:18 - | -LL | #[target_feature(disable = "baz")] - | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` - -error: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/target-feature-wrong.rs:27:1 - | -LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions -... -LL | fn bar() {} - | ----------- not an `unsafe` function - -error: attribute should be applied to a function - --> $DIR/target-feature-wrong.rs:33:1 - | -LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | mod another {} - | -------------- not a function - -error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/target-feature-wrong.rs:38:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 7 previous errors - diff --git a/src/test/ui/target-feature/gate.rs b/src/test/ui/target-feature/gate.rs new file mode 100644 index 00000000000..bc7f7caa107 --- /dev/null +++ b/src/test/ui/target-feature/gate.rs @@ -0,0 +1,35 @@ +// ignore-arm +// ignore-aarch64 +// ignore-wasm +// ignore-emscripten +// ignore-mips +// ignore-mips64 +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-sparc +// ignore-sparc64 +// ignore-s390x +// gate-test-sse4a_target_feature +// gate-test-powerpc_target_feature +// gate-test-avx512_target_feature +// gate-test-tbm_target_feature +// gate-test-arm_target_feature +// gate-test-aarch64_target_feature +// gate-test-hexagon_target_feature +// gate-test-mips_target_feature +// gate-test-mmx_target_feature +// gate-test-wasm_target_feature +// gate-test-adx_target_feature +// gate-test-cmpxchg16b_target_feature +// gate-test-movbe_target_feature +// gate-test-rtm_target_feature +// gate-test-f16c_target_feature +// min-llvm-version 6.0 + +#[target_feature(enable = "avx512bw")] +//~^ ERROR: currently unstable +unsafe fn foo() { +} + +fn main() {} diff --git a/src/test/ui/target-feature/gate.stderr b/src/test/ui/target-feature/gate.stderr new file mode 100644 index 00000000000..05dbc6e90ad --- /dev/null +++ b/src/test/ui/target-feature/gate.stderr @@ -0,0 +1,12 @@ +error[E0658]: the target feature `avx512bw` is currently unstable + --> $DIR/gate.rs:30:18 + | +LL | #[target_feature(enable = "avx512bw")] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/44839 + = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/target-feature/invalid-attribute.rs b/src/test/ui/target-feature/invalid-attribute.rs new file mode 100644 index 00000000000..46680336632 --- /dev/null +++ b/src/test/ui/target-feature/invalid-attribute.rs @@ -0,0 +1,73 @@ +// ignore-arm +// ignore-aarch64 +// ignore-wasm +// ignore-emscripten +// ignore-mips +// ignore-mips64 +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-s390x +// ignore-sparc +// ignore-sparc64 + +#![feature(target_feature)] + +#[target_feature = "+sse2"] +//~^ ERROR malformed `target_feature` attribute +#[target_feature(enable = "foo")] +//~^ ERROR not valid for this target +//~| NOTE `foo` is not valid for this target +#[target_feature(bar)] +//~^ ERROR malformed `target_feature` attribute +#[target_feature(disable = "baz")] +//~^ ERROR malformed `target_feature` attribute +unsafe fn foo() {} + +#[target_feature(enable = "sse2")] +//~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions +//~| NOTE can only be applied to `unsafe` functions +fn bar() {} +//~^ NOTE not an `unsafe` function + +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +mod another {} +//~^ NOTE not a function + +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +const FOO: usize = 7; +//~^ NOTE not a function + +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +struct Foo; +//~^ NOTE not a function + +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +enum Bar { } +//~^ NOTE not a function + +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +union Qux { f1: u16, f2: u16 } +//~^ NOTE not a function + +#[target_feature(enable = "sse2")] +//~^ ERROR attribute should be applied to a function +trait Baz { } +//~^ NOTE not a function + +#[inline(always)] +//~^ ERROR: cannot use `#[inline(always)]` +#[target_feature(enable = "sse2")] +unsafe fn test() {} + +fn main() { + unsafe { + foo(); + bar(); + } +} diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr new file mode 100644 index 00000000000..abfe5dd2197 --- /dev/null +++ b/src/test/ui/target-feature/invalid-attribute.stderr @@ -0,0 +1,95 @@ +error: malformed `target_feature` attribute input + --> $DIR/invalid-attribute.rs:16:1 + | +LL | #[target_feature = "+sse2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[target_feature(enable = "name")]` + +error: the feature named `foo` is not valid for this target + --> $DIR/invalid-attribute.rs:18:18 + | +LL | #[target_feature(enable = "foo")] + | ^^^^^^^^^^^^^^ `foo` is not valid for this target + +error: malformed `target_feature` attribute input + --> $DIR/invalid-attribute.rs:21:18 + | +LL | #[target_feature(bar)] + | ^^^ help: must be of the form: `enable = ".."` + +error: malformed `target_feature` attribute input + --> $DIR/invalid-attribute.rs:23:18 + | +LL | #[target_feature(disable = "baz")] + | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` + +error: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/invalid-attribute.rs:27:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions +... +LL | fn bar() {} + | ----------- not an `unsafe` function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:33:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | mod another {} + | -------------- not a function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:38:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const FOO: usize = 7; + | --------------------- not a function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:43:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | struct Foo; + | ----------- not a function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:48:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | enum Bar { } + | ------------ not a function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:53:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | union Qux { f1: u16, f2: u16 } + | ------------------------------ not a function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:58:1 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | trait Baz { } + | ------------- not a function + +error: cannot use `#[inline(always)]` with `#[target_feature]` + --> $DIR/invalid-attribute.rs:63:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + -- cgit 1.4.1-3-g733a5 From 9ef6edb04ad059242551f134077dab88a36bdd61 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 27 Sep 2019 06:09:32 +0200 Subject: lowering: don't .abort_if_errors() --- src/librustc/hir/lowering/expr.rs | 3 +-- src/test/ui/generator/no-parameters-on-generators.rs | 1 + src/test/ui/generator/no-parameters-on-generators.stderr | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'src/test') diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 90b6c9474ac..56a7b88ab06 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -705,7 +705,6 @@ impl LoweringContext<'_> { E0628, "generators cannot have explicit parameters" ); - self.sess.abort_if_errors(); } Some(match movability { Movability::Movable => hir::GeneratorMovability::Movable, @@ -998,7 +997,7 @@ impl LoweringContext<'_> { E0727, "`async` generators are not yet supported", ); - self.sess.abort_if_errors(); + return hir::ExprKind::Err; }, None => self.generator_kind = Some(hir::GeneratorKind::Gen), } diff --git a/src/test/ui/generator/no-parameters-on-generators.rs b/src/test/ui/generator/no-parameters-on-generators.rs index a2632a4bd7d..6b5a5579339 100644 --- a/src/test/ui/generator/no-parameters-on-generators.rs +++ b/src/test/ui/generator/no-parameters-on-generators.rs @@ -2,6 +2,7 @@ fn main() { let gen = |start| { //~ ERROR generators cannot have explicit parameters + //~^ ERROR type inside generator must be known in this context yield; }; } diff --git a/src/test/ui/generator/no-parameters-on-generators.stderr b/src/test/ui/generator/no-parameters-on-generators.stderr index 41862f2b070..5e8e043a391 100644 --- a/src/test/ui/generator/no-parameters-on-generators.stderr +++ b/src/test/ui/generator/no-parameters-on-generators.stderr @@ -4,5 +4,18 @@ error[E0628]: generators cannot have explicit parameters LL | let gen = |start| { | ^^^^^^^ -error: aborting due to previous error +error[E0698]: type inside generator must be known in this context + --> $DIR/no-parameters-on-generators.rs:4:16 + | +LL | let gen = |start| { + | ^^^^^ cannot infer type + | +note: the type is part of the generator because of this `yield` + --> $DIR/no-parameters-on-generators.rs:6:9 + | +LL | yield; + | ^^^^^ + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0698`. -- cgit 1.4.1-3-g733a5 From 46a38dc183d2f79b764b2cca0011d0843f0fffcb Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 25 Sep 2019 23:01:01 -0700 Subject: Account for tail expressions when pointing at return type When there's a type mismatch we make an effort to check if it was caused by a function's return type. This logic now makes sure to only point at the return type if the error happens in a tail expression. --- src/librustc/hir/map/mod.rs | 25 ++++++++++++++++++++++++- src/librustc/hir/mod.rs | 2 +- src/librustc_typeck/check/expr.rs | 8 ++++++-- src/test/ui/struct-literal-variant-in-if.stderr | 3 --- 4 files changed, 31 insertions(+), 7 deletions(-) (limited to 'src/test') diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index ca854395d7b..9854d8b04fd 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -741,7 +741,28 @@ impl<'hir> Map<'hir> { /// } /// ``` pub fn get_return_block(&self, id: HirId) -> Option { - for (hir_id, node) in ParentHirIterator::new(id, &self) { + let mut iter = ParentHirIterator::new(id, &self).peekable(); + let mut ignore_tail = false; + if let Some(entry) = self.find_entry(id) { + if let Node::Expr(Expr { node: ExprKind::Ret(_), .. }) = entry.node { + // When dealing with `return` statements, we don't care about climbing only tail + // expressions. + ignore_tail = true; + } + } + while let Some((hir_id, node)) = iter.next() { + if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { + match next_node { + Node::Block(Block { expr: None, .. }) => return None, + Node::Block(Block { expr: Some(expr), .. }) => { + if hir_id != expr.hir_id { + // The current node is not the tail expression of its parent. + return None; + } + } + _ => {} + } + } match node { Node::Item(_) | Node::ForeignItem(_) | @@ -750,10 +771,12 @@ impl<'hir> Map<'hir> { Node::ImplItem(_) => return Some(hir_id), Node::Expr(ref expr) => { match expr.kind { + // Ignore `return`s on the first iteration ExprKind::Loop(..) | ExprKind::Ret(..) => return None, _ => {} } } + Node::Local(_) => return None, _ => {} } } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 01cb5cc9bc1..0b1fcd0da3a 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1563,7 +1563,7 @@ pub enum ExprKind { /// Thus, `x.foo::(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. MethodCall(P, Span, HirVec), - /// A tuple (e.g., `(a, b, c ,d)`). + /// A tuple (e.g., `(a, b, c, d)`). Tup(HirVec), /// A binary operation (e.g., `a + b`, `a * b`). Binary(BinOp, P, P), diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 58f41ca4f88..317d829d135 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -620,8 +620,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr ) -> Ty<'tcx> { if self.ret_coercion.is_none() { - struct_span_err!(self.tcx.sess, expr.span, E0572, - "return statement outside of function body").emit(); + struct_span_err!( + self.tcx.sess, + expr.span, + E0572, + "return statement outside of function body", + ).emit(); } else if let Some(ref e) = expr_opt { if self.ret_coercion_span.borrow().is_none() { *self.ret_coercion_span.borrow_mut() = Some(e.span); diff --git a/src/test/ui/struct-literal-variant-in-if.stderr b/src/test/ui/struct-literal-variant-in-if.stderr index f91b9d7dce6..85cbc787bc2 100644 --- a/src/test/ui/struct-literal-variant-in-if.stderr +++ b/src/test/ui/struct-literal-variant-in-if.stderr @@ -49,9 +49,6 @@ LL | if x == E::V { field } {} error[E0308]: mismatched types --> $DIR/struct-literal-variant-in-if.rs:10:20 | -LL | fn test_E(x: E) { - | - help: try adding a return type: `-> bool` -LL | let field = true; LL | if x == E::V { field } {} | ^^^^^ expected (), found bool | -- cgit 1.4.1-3-g733a5 From aa03f1f5e3791f1ff07d414ba003f395ad6538d8 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 28 Sep 2019 02:30:48 +0200 Subject: Improve diagnostic for `let A = 0;` where `A` is a constant, not a new variable. --- src/librustc/hir/map/mod.rs | 8 ++++ src/librustc_mir/hair/pattern/check_match.rs | 47 ++++++++++++++++++---- src/librustc_typeck/astconv.rs | 7 +--- src/librustc_typeck/check/callee.rs | 11 +---- .../ui/consts/const-pattern-irrefutable.stderr | 24 +++++++++-- .../const-pat-non-exaustive-let-new-var.rs | 10 +++++ .../const-pat-non-exaustive-let-new-var.stderr | 15 +++++++ 7 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs create mode 100644 src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr (limited to 'src/test') diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index d4efe0297b6..e7f4c5982f6 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -1064,6 +1064,14 @@ impl<'hir> Map<'hir> { self.as_local_hir_id(id).map(|id| self.span(id)) } + pub fn res_span(&self, res: Res) -> Option { + match res { + Res::Err => None, + Res::Local(id) => Some(self.span(id)), + res => self.span_if_local(res.opt_def_id()?), + } + } + pub fn node_to_string(&self, id: HirId) -> String { hir_id_to_string(self, id, true) } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 4572519683d..3a8e5f0930c 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -270,20 +270,51 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { "refutable pattern in {}: {} not covered", origin, joined_patterns ); - err.span_label(pat.span, match &pat.kind { + match &pat.kind { hir::PatKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].args.is_none() => { - format!("interpreted as {} {} pattern, not new variable", - path.res.article(), path.res.descr()) + if path.segments.len() == 1 && path.segments[0].args.is_none() => + { + const_not_var(&mut err, cx.tcx, pat, path); } - _ => pattern_not_convered_label(&witnesses, &joined_patterns), - }); + _ => { + err.span_label( + pat.span, + pattern_not_covered_label(&witnesses, &joined_patterns), + ); + } + } + adt_defined_here(cx, &mut err, pattern_ty, &witnesses); err.emit(); }); } } +/// A path pattern was interpreted as a constant, not a new variable. +/// This caused an irrefutable match failure in e.g. `let`. +fn const_not_var(err: &mut DiagnosticBuilder<'_>, tcx: TyCtxt<'_>, pat: &Pat, path: &hir::Path) { + let descr = path.res.descr(); + err.span_label(pat.span, format!( + "interpreted as {} {} pattern, not a new variable", + path.res.article(), + descr, + )); + + err.span_suggestion( + pat.span, + "introduce a variable instead", + format!("{}_var", path.segments[0].ident).to_lowercase(), + // Cannot use `MachineApplicable` as it's not really *always* correct + // because there may be such an identifier in scope or the user maybe + // really wanted to match against the constant. This is quite unlikely however. + Applicability::MaybeIncorrect, + ); + + if let Some(span) = tcx.hir().res_span(path.res) { + err.span_label(span, format!("{} defined here", descr)); + } +} + fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) { pat.walk(|p| { if let hir::PatKind::Binding(_, _, ident, None) = p.kind { @@ -449,7 +480,7 @@ fn check_exhaustive<'tcx>( cx.tcx.sess, sp, format!("non-exhaustive patterns: {} not covered", joined_patterns), ); - err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns)); + err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); adt_defined_here(cx, &mut err, scrut_ty, &witnesses); err.help( "ensure that all possible cases are being handled, \ @@ -475,7 +506,7 @@ fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String { } } -fn pattern_not_convered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String { +fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String { format!("pattern{} {} not covered", rustc_errors::pluralise!(witnesses.len()), joined_patterns) } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 6f1d854481a..5606f36632e 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1368,11 +1368,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, format!("associated type `{}` must be specified", assoc_item.ident), ); - if item_def_id.is_local() { - err.span_label( - tcx.def_span(*item_def_id), - format!("`{}` defined here", assoc_item.ident), - ); + if let Some(sp) = tcx.hir().span_if_local(*item_def_id) { + err.span_label(sp, format!("`{}` defined here", assoc_item.ident)); } if suggest { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet( diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 4d8ec6fb0b8..7e0a3e78188 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -351,16 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(call_expr.span, "call expression requires function"); - let def_span = match def { - Res::Err => None, - Res::Local(id) => { - Some(self.tcx.hir().span(id)) - }, - _ => def - .opt_def_id() - .and_then(|did| self.tcx.hir().span_if_local(did)), - }; - if let Some(span) = def_span { + if let Some(span) = self.tcx.hir().res_span(def) { let label = match (unit_variant, inner_callee_path) { (Some(path), _) => format!("`{}` defined here", path), (_, Some(hir::QPath::Resolved(_, path))) => format!( diff --git a/src/test/ui/consts/const-pattern-irrefutable.stderr b/src/test/ui/consts/const-pattern-irrefutable.stderr index 06f5e90d2f1..4814aa9a5b2 100644 --- a/src/test/ui/consts/const-pattern-irrefutable.stderr +++ b/src/test/ui/consts/const-pattern-irrefutable.stderr @@ -1,20 +1,38 @@ error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:12:9 | +LL | const a: u8 = 2; + | ---------------- constant defined here +... LL | let a = 4; - | ^ interpreted as a constant pattern, not new variable + | ^ + | | + | interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `a_var` error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:13:9 | +LL | pub const b: u8 = 2; + | -------------------- constant defined here +... LL | let c = 4; - | ^ interpreted as a constant pattern, not new variable + | ^ + | | + | interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `c_var` error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:14:9 | +LL | pub const d: u8 = 2; + | -------------------- constant defined here +... LL | let d = 4; - | ^ interpreted as a constant pattern, not new variable + | ^ + | | + | interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `d_var` error: aborting due to 3 previous errors diff --git a/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs new file mode 100644 index 00000000000..2a11871db8e --- /dev/null +++ b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs @@ -0,0 +1,10 @@ +fn main() { + let A = 3; + //~^ ERROR refutable pattern in local binding: `std::i32::MIN..=1i32` and + //~| interpreted as a constant pattern, not a new variable + //~| HELP introduce a variable instead + //~| SUGGESTION a_var + + const A: i32 = 2; + //~^ constant defined here +} diff --git a/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr new file mode 100644 index 00000000000..fc17199bf91 --- /dev/null +++ b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr @@ -0,0 +1,15 @@ +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=1i32` and `3i32..=std::i32::MAX` not covered + --> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9 + | +LL | let A = 3; + | ^ + | | + | interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `a_var` +... +LL | const A: i32 = 2; + | ----------------- constant defined here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0005`. -- cgit 1.4.1-3-g733a5