From 082c03b0780b2de24b68be04e96d1596a7c5b3cf Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Wed, 6 Jan 2016 23:58:45 +0100 Subject: [PATCH 1/8] libsyntax: note that `let a = (let b = something)` is invalid in parse_bottom_expr (parser.rs) --- src/libsyntax/parse/parser.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index bfa42e761294b..edb1f7eb926c1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2199,6 +2199,12 @@ impl<'a> Parser<'a> { UnsafeBlock(ast::UserProvided), attrs); } + if self.eat_keyword(keywords::Let) { + return Err(self.span_fatal(self.span, + "`let` is not an expression, so it cannot \ + be used in this way")) + + } if self.eat_keyword(keywords::Return) { if self.token.can_begin_expr() { let e = try!(self.parse_expr()); From 79f2cff44ec62cf42f6c97d28dc4286de683b1e5 Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Fri, 8 Jan 2016 00:01:59 +0100 Subject: [PATCH 2/8] libsyntax: move check for keyword Let to a more logical spot --- src/libsyntax/parse/parser.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index edb1f7eb926c1..f3809455fe0f6 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2134,6 +2134,12 @@ impl<'a> Parser<'a> { } hi = self.last_span.hi; } + _ if self.token.is_keyword(keywords::Let) => { + // Catch this syntax error here, instead of in `check_strict_keywords`, so that + // we can explicitly mention that let is not to be used as an expression + let msg = "`let` is not an expression, so it cannot be used in this way"; + return Err(self.diagnostic().struct_span_err(self.span, &msg)); + }, _ => { if self.eat_lt() { let (qself, path) = @@ -2199,12 +2205,6 @@ impl<'a> Parser<'a> { UnsafeBlock(ast::UserProvided), attrs); } - if self.eat_keyword(keywords::Let) { - return Err(self.span_fatal(self.span, - "`let` is not an expression, so it cannot \ - be used in this way")) - - } if self.eat_keyword(keywords::Return) { if self.token.can_begin_expr() { let e = try!(self.parse_expr()); From 2b1e2732930830fc295d26bfb4bb29931e7e84ac Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Thu, 14 Jan 2016 16:04:35 +0100 Subject: [PATCH 3/8] Update qquote.rs test case and make unexpected `let` error fatal --- src/libsyntax/parse/parser.rs | 12 ++++++------ src/test/run-fail-fulldeps/qquote.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f3809455fe0f6..c071670ea6e24 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2134,12 +2134,6 @@ impl<'a> Parser<'a> { } hi = self.last_span.hi; } - _ if self.token.is_keyword(keywords::Let) => { - // Catch this syntax error here, instead of in `check_strict_keywords`, so that - // we can explicitly mention that let is not to be used as an expression - let msg = "`let` is not an expression, so it cannot be used in this way"; - return Err(self.diagnostic().struct_span_err(self.span, &msg)); - }, _ => { if self.eat_lt() { let (qself, path) = @@ -2162,6 +2156,12 @@ impl<'a> Parser<'a> { let lo = self.last_span.lo; return self.parse_while_expr(None, lo, attrs); } + if self.token.is_keyword(keywords::Let) { + // Catch this syntax error here, instead of in `check_strict_keywords`, so + // that we can explicitly mention that let is not to be used as an expression + let msg = "`let` is not an expression, so it cannot be used in this way"; + self.span_err(self.span, msg); + } if self.token.is_lifetime() { let lifetime = self.get_lifetime(); let lo = self.span.lo; diff --git a/src/test/run-fail-fulldeps/qquote.rs b/src/test/run-fail-fulldeps/qquote.rs index d42a777a019a3..297a1da25b5d7 100644 --- a/src/test/run-fail-fulldeps/qquote.rs +++ b/src/test/run-fail-fulldeps/qquote.rs @@ -10,7 +10,7 @@ // ignore-cross-compile -// error-pattern:expected identifier, found keyword `let` +// error-pattern:`let` is not an expression, so it cannot be used in this way #![feature(quote, rustc_private)] From 1745153eaeeb0793876b36422bda6764483ceefb Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Thu, 14 Jan 2016 16:52:24 +0100 Subject: [PATCH 4/8] do not additionally note about unexpected identifier after unexpected let error, by moving unexpected let check into the proper if-else clause --- src/libsyntax/parse/parser.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c071670ea6e24..daa13885e0d1e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2156,12 +2156,6 @@ impl<'a> Parser<'a> { let lo = self.last_span.lo; return self.parse_while_expr(None, lo, attrs); } - if self.token.is_keyword(keywords::Let) { - // Catch this syntax error here, instead of in `check_strict_keywords`, so - // that we can explicitly mention that let is not to be used as an expression - let msg = "`let` is not an expression, so it cannot be used in this way"; - self.span_err(self.span, msg); - } if self.token.is_lifetime() { let lifetime = self.get_lifetime(); let lo = self.span.lo; @@ -2224,6 +2218,11 @@ impl<'a> Parser<'a> { ex = ExprBreak(None); } hi = self.last_span.hi; + } else if self.token.is_keyword(keywords::Let) { + // Catch this syntax error here, instead of in `check_strict_keywords`, so + // that we can explicitly mention that let is not to be used as an expression + let msg = "`let` is not an expression, so it cannot be used in this way"; + return Err(self.fatal(&msg)); } else if self.check(&token::ModSep) || self.token.is_ident() && !self.check_keyword(keywords::True) && From fee457d3af355f24ef321ea7250e968638f403c8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 24 Jan 2016 21:14:56 -0800 Subject: [PATCH 5/8] std: Fix some behavior without stdio handles On all platforms, reading from stdin where the actual stdin isn't present should return 0 bytes as having been read rather than the entire buffer. On Windows, handle the case where we're inheriting stdio handles but one of them isn't present. Currently the behavior is to fail returning an I/O error but instead this commit corrects it to detecting this situation and propagating the non-set handle. Closes #31167 --- src/libstd/io/stdio.rs | 2 +- src/libstd/sys/windows/process.rs | 11 ++- src/test/run-pass/no-stdio.rs | 124 ++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 src/test/run-pass/no-stdio.rs diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 79091fd3d6b9d..cd2d5e52462bb 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -112,7 +112,7 @@ impl io::Write for Maybe { impl io::Read for Maybe { fn read(&mut self, buf: &mut [u8]) -> io::Result { match *self { - Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), buf.len()), + Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), 0), Maybe::Fake => Ok(0) } } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index e0f8d6f9df963..61f73b00265b8 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -351,10 +351,15 @@ fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec) { impl Stdio { fn to_handle(&self, stdio_id: c::DWORD) -> io::Result { match *self { + // If no stdio handle is available, then inherit means that it + // should still be unavailable so propagate the + // INVALID_HANDLE_VALUE. Stdio::Inherit => { - stdio::get(stdio_id).and_then(|io| { - io.handle().duplicate(0, true, c::DUPLICATE_SAME_ACCESS) - }) + match stdio::get(stdio_id) { + Ok(io) => io.handle().duplicate(0, true, + c::DUPLICATE_SAME_ACCESS), + Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), + } } Stdio::Raw(handle) => { RawHandle::new(handle).duplicate(0, true, c::DUPLICATE_SAME_ACCESS) diff --git a/src/test/run-pass/no-stdio.rs b/src/test/run-pass/no-stdio.rs new file mode 100644 index 0000000000000..3658b6a508ab2 --- /dev/null +++ b/src/test/run-pass/no-stdio.rs @@ -0,0 +1,124 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(libc)] + +extern crate libc; + +use std::process::{Command, Stdio}; +use std::env; +use std::io::{self, Read, Write}; + +#[cfg(unix)] +unsafe fn without_stdio R>(f: F) -> R { + let doit = |a| { + let r = libc::dup(a); + assert!(r >= 0); + return r + }; + let a = doit(0); + let b = doit(1); + let c = doit(2); + + assert!(libc::close(0) >= 0); + assert!(libc::close(1) >= 0); + assert!(libc::close(2) >= 0); + + let r = f(); + + assert!(libc::dup2(a, 0) >= 0); + assert!(libc::dup2(b, 1) >= 0); + assert!(libc::dup2(c, 2) >= 0); + + return r +} + +#[cfg(windows)] +unsafe fn without_stdio R>(f: F) -> R { + type DWORD = u32; + type HANDLE = *mut u8; + type BOOL = i32; + + const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD; + const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; + const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; + const INVALID_HANDLE_VALUE: HANDLE = !0 as HANDLE; + + extern "system" { + fn GetStdHandle(which: DWORD) -> HANDLE; + fn SetStdHandle(which: DWORD, handle: HANDLE) -> BOOL; + } + + let doit = |id| { + let handle = GetStdHandle(id); + assert!(handle != INVALID_HANDLE_VALUE); + assert!(SetStdHandle(id, INVALID_HANDLE_VALUE) != 0); + return handle + }; + + let a = doit(STD_INPUT_HANDLE); + let b = doit(STD_OUTPUT_HANDLE); + let c = doit(STD_ERROR_HANDLE); + + let r = f(); + + let doit = |id, handle| { + assert!(SetStdHandle(id, handle) != 0); + }; + doit(STD_INPUT_HANDLE, a); + doit(STD_OUTPUT_HANDLE, b); + doit(STD_ERROR_HANDLE, c); + + return r +} + +fn main() { + if env::args().len() > 1 { + println!("test"); + assert!(io::stdout().write(b"test\n").is_ok()); + assert!(io::stderr().write(b"test\n").is_ok()); + assert_eq!(io::stdin().read(&mut [0; 10]).unwrap(), 0); + return + } + + // First, make sure reads/writes without stdio work if stdio itself is + // missing. + let (a, b, c) = unsafe { + without_stdio(|| { + let a = io::stdout().write(b"test\n"); + let b = io::stderr().write(b"test\n"); + let c = io::stdin().read(&mut [0; 10]); + + (a, b, c) + }) + }; + + assert_eq!(a.unwrap(), 5); + assert_eq!(b.unwrap(), 5); + assert_eq!(c.unwrap(), 0); + + // Second, spawn a child and do some work with "null" descriptors to make + // sure it's ok + let me = env::current_exe().unwrap(); + let status = Command::new(&me) + .arg("next") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status().unwrap(); + assert!(status.success(), "{:?} isn't a success", status); + + // Finally, close everything then spawn a child to make sure everything is + // *still* ok. + let status = unsafe { + without_stdio(|| Command::new(&me).arg("next").status()) + }.unwrap(); + assert!(status.success(), "{:?} isn't a success", status); +} From faf0852fc1d01aef18fe8098a0f2f601dbfebd9b Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 22 Jan 2016 09:55:29 +0000 Subject: [PATCH 6/8] Resolve: fix #23880, a scoping bug This fixes a bug in which items in a block are shadowed by local variables and type parameters that are in scope. It is a [breaking-change]. For example, the following code breaks: ```rust fn foo() { let mut f = 1; { fn f() {} f += 1; // This will now resolve to the function instead of the local variable } } ``` Any breakage can be fixed by renaming the item that is no longer shadowed. --- src/librustc_resolve/lib.rs | 80 +++++++++++++++++----------- src/test/run-pass/lexical-scoping.rs | 28 ++++++++++ 2 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 src/test/run-pass/lexical-scoping.rs diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 444c43163e3c3..ee1150933e123 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -723,7 +723,7 @@ enum FallbackSuggestion { } #[derive(Copy, Clone)] -enum TypeParameters<'a> { +enum TypeParameters<'tcx, 'a> { NoTypeParameters, HasTypeParameters(// Type parameters. &'a Generics, @@ -733,13 +733,13 @@ enum TypeParameters<'a> { ParamSpace, // The kind of the rib used for type parameters. - RibKind), + RibKind<'tcx>), } // The rib kind controls the translation of local // definitions (`Def::Local`) to upvars (`Def::Upvar`). #[derive(Copy, Clone, Debug)] -enum RibKind { +enum RibKind<'a> { // No translation needs to be applied. NormalRibKind, @@ -758,6 +758,9 @@ enum RibKind { // We're in a constant item. Can't refer to dynamic stuff. ConstantItemRibKind, + + // We passed through an anonymous module. + AnonymousModuleRibKind(Module<'a>), } #[derive(Copy, Clone)] @@ -799,13 +802,13 @@ enum BareIdentifierPatternResolution { /// One local scope. #[derive(Debug)] -struct Rib { +struct Rib<'a> { bindings: HashMap, - kind: RibKind, + kind: RibKind<'a>, } -impl Rib { - fn new(kind: RibKind) -> Rib { +impl<'a> Rib<'a> { + fn new(kind: RibKind<'a>) -> Rib<'a> { Rib { bindings: HashMap::new(), kind: kind, @@ -1180,13 +1183,13 @@ pub struct Resolver<'a, 'tcx: 'a> { // The current set of local scopes, for values. // FIXME #4948: Reuse ribs to avoid allocation. - value_ribs: Vec, + value_ribs: Vec>, // The current set of local scopes, for types. - type_ribs: Vec, + type_ribs: Vec>, // The current set of local scopes, for labels. - label_ribs: Vec, + label_ribs: Vec>, // The trait that the current context can refer to. current_trait_ref: Option<(DefId, TraitRef)>, @@ -1304,6 +1307,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.arenas.modules.alloc(ModuleS::new(parent_link, def, external, is_public)) } + fn get_ribs<'b>(&'b mut self, ns: Namespace) -> &'b mut Vec> { + match ns { ValueNS => &mut self.value_ribs, TypeNS => &mut self.type_ribs } + } + #[inline] fn record_import_use(&mut self, import_id: NodeId, name: Name) { if !self.make_glob_map { @@ -2122,7 +2129,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn with_type_parameter_rib(&mut self, type_parameters: TypeParameters, f: F) + fn with_type_parameter_rib<'b, F>(&'b mut self, type_parameters: TypeParameters<'a, 'b>, f: F) where F: FnOnce(&mut Resolver) { match type_parameters { @@ -2191,7 +2198,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn resolve_function(&mut self, rib_kind: RibKind, declaration: &FnDecl, block: &Block) { + fn resolve_function(&mut self, rib_kind: RibKind<'a>, declaration: &FnDecl, block: &Block) { // Create a value rib for the function. self.value_ribs.push(Rib::new(rib_kind)); @@ -2494,18 +2501,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { fn resolve_block(&mut self, block: &Block) { debug!("(resolving block) entering block"); - self.value_ribs.push(Rib::new(NormalRibKind)); - // Move down in the graph, if there's an anonymous module rooted here. let orig_module = self.current_module; - match orig_module.anonymous_children.borrow().get(&block.id) { - None => { - // Nothing to do. - } - Some(anonymous_module) => { - debug!("(resolving block) found anonymous module, moving down"); - self.current_module = anonymous_module; - } + let anonymous_module = + orig_module.anonymous_children.borrow().get(&block.id).map(|module| *module); + + if let Some(anonymous_module) = anonymous_module { + debug!("(resolving block) found anonymous module, moving down"); + self.value_ribs.push(Rib::new(AnonymousModuleRibKind(anonymous_module))); + self.type_ribs.push(Rib::new(AnonymousModuleRibKind(anonymous_module))); + self.current_module = anonymous_module; + } else { + self.value_ribs.push(Rib::new(NormalRibKind)); } // Check for imports appearing after non-item statements. @@ -2538,6 +2545,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if !self.resolved { self.current_module = orig_module; self.value_ribs.pop(); + if let Some(_) = anonymous_module { + self.type_ribs.pop(); + } } debug!("(resolving block) leaving block"); } @@ -3072,7 +3082,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Def::Local(_, node_id) => { for rib in ribs { match rib.kind { - NormalRibKind => { + NormalRibKind | AnonymousModuleRibKind(..) => { // Nothing to do. Continue. } ClosureRibKind(function_id) => { @@ -3120,7 +3130,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Def::TyParam(..) | Def::SelfTy(..) => { for rib in ribs { match rib.kind { - NormalRibKind | MethodRibKind | ClosureRibKind(..) => { + NormalRibKind | MethodRibKind | ClosureRibKind(..) | + AnonymousModuleRibKind(..) => { // Nothing to do. Continue. } ItemRibKind => { @@ -3271,13 +3282,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { namespace: Namespace) -> Option { // Check the local set of ribs. - let (name, ribs) = match namespace { - ValueNS => (ident.name, &self.value_ribs), - TypeNS => (ident.unhygienic_name, &self.type_ribs), - }; + let name = match namespace { ValueNS => ident.name, TypeNS => ident.unhygienic_name }; - for (i, rib) in ribs.iter().enumerate().rev() { - if let Some(def_like) = rib.bindings.get(&name).cloned() { + for i in (0 .. self.get_ribs(namespace).len()).rev() { + if let Some(def_like) = self.get_ribs(namespace)[i].bindings.get(&name).cloned() { match def_like { DlDef(def) => { debug!("(resolving path in local ribs) resolved `{}` to {:?} at {}", @@ -3297,6 +3305,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } } + + if let AnonymousModuleRibKind(module) = self.get_ribs(namespace)[i].kind { + if let Success((target, _)) = self.resolve_name_in_module(module, + ident.unhygienic_name, + namespace, + PathSearch, + true) { + if let Some(def) = target.binding.def() { + return Some(LocalDef::from_def(def)); + } + } + } } None diff --git a/src/test/run-pass/lexical-scoping.rs b/src/test/run-pass/lexical-scoping.rs new file mode 100644 index 0000000000000..36604042d0f25 --- /dev/null +++ b/src/test/run-pass/lexical-scoping.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that items in subscopes can shadow type parameters and local variables (see issue #23880). + +#![allow(unused)] +struct Foo { x: Box } +impl Foo { + fn foo(&self) { + type Bar = i32; + let _: Bar = 42; + } +} + +fn main() { + let f = 1; + { + fn f() {} + f(); + } +} From d829019ff4874e2a4f1b6780c442dfa524e75d38 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 26 Jan 2016 13:49:21 +0530 Subject: [PATCH 7/8] Make emitter handle DUMMY_SP correctly --- src/libsyntax/errors/emitter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index c21bf1e6a1fa0..51013d68930ea 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -10,7 +10,7 @@ use self::Destination::*; -use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span}; +use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, DUMMY_SP, Pos, Span}; use diagnostics; use errors::{Level, RenderSpan, DiagnosticBuilder}; @@ -109,8 +109,8 @@ impl Emitter for EmitterWriter { lvl: Level) { let error = match sp { Some(COMMAND_LINE_SP) => self.emit_(FileLine(COMMAND_LINE_SP), msg, code, lvl), + Some(DUMMY_SP) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code), Some(sp) => self.emit_(FullSpan(sp), msg, code, lvl), - None => print_diagnostic(&mut self.dst, "", lvl, msg, code), }; if let Err(e) = error { From 065e47eb3b23a9bd3074d3406157f0aafc575150 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 26 Jan 2016 13:53:28 +0530 Subject: [PATCH 8/8] Improve error message for let-in-expr-position --- src/libsyntax/parse/parser.rs | 5 +++-- src/test/run-fail-fulldeps/qquote.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index daa13885e0d1e..4ee3ce63a8d0b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2221,8 +2221,9 @@ impl<'a> Parser<'a> { } else if self.token.is_keyword(keywords::Let) { // Catch this syntax error here, instead of in `check_strict_keywords`, so // that we can explicitly mention that let is not to be used as an expression - let msg = "`let` is not an expression, so it cannot be used in this way"; - return Err(self.fatal(&msg)); + let mut db = self.fatal("expected expression, found statement (`let`)"); + db.note("variable declaration using `let` is a statement"); + return Err(db); } else if self.check(&token::ModSep) || self.token.is_ident() && !self.check_keyword(keywords::True) && diff --git a/src/test/run-fail-fulldeps/qquote.rs b/src/test/run-fail-fulldeps/qquote.rs index 297a1da25b5d7..41a6fd05c3741 100644 --- a/src/test/run-fail-fulldeps/qquote.rs +++ b/src/test/run-fail-fulldeps/qquote.rs @@ -10,7 +10,7 @@ // ignore-cross-compile -// error-pattern:`let` is not an expression, so it cannot be used in this way +// error-pattern:expected expression, found statement (`let`) #![feature(quote, rustc_private)]