Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 3 additions & 39 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,9 @@ impl<'a> Parser<'a> {
if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? {
return Ok(expr);
}
if let Some(arr) = this.recover_from_c_array(lo) {
return Ok(arr);
}
this.parse_expr_block(None, lo, BlockCheckMode::Default)
} else if this.check(exp!(Or)) || this.check(exp!(OrOr)) {
this.parse_expr_closure().map_err(|mut err| {
Expand Down Expand Up @@ -2227,39 +2230,6 @@ impl<'a> Parser<'a> {
}
}

fn is_array_like_block(&mut self) -> bool {
self.token.kind == TokenKind::OpenBrace
&& self
.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
&& self.look_ahead(2, |t| t == &token::Comma)
&& self.look_ahead(3, |t| t.can_begin_expr())
}

/// Emits a suggestion if it looks like the user meant an array but
/// accidentally used braces, causing the code to be interpreted as a block
/// expression.
fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option<Box<Expr>> {
let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => {
let guar = self.dcx().emit_err(crate::diagnostics::ArrayBracketsInsteadOfBraces {
span: arr.span,
sub: crate::diagnostics::ArrayBracketsInsteadOfBracesSugg {
left: lo,
right: snapshot.prev_token.span,
},
});

self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span, guar))
}
Err(e) => {
e.cancel();
None
}
}
}

fn suggest_missing_semicolon_before_array(
&self,
prev_span: Span,
Expand Down Expand Up @@ -2309,12 +2279,6 @@ impl<'a> Parser<'a> {
lo: Span,
blk_mode: BlockCheckMode,
) -> PResult<'a, Box<Expr>> {
if self.may_recover() && self.is_array_like_block() {
if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) {
return Ok(arr);
}
}

if self.token.is_metavar_block() {
self.dcx().emit_err(crate::diagnostics::InvalidBlockMacroSegment {
span: self.token.span,
Expand Down
34 changes: 33 additions & 1 deletion compiler/rustc_parse/src/parser/expr/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use rustc_ast::{BinOpKind, Expr, ExprKind, token};
use rustc_errors::{Applicability, Diag, PResult};
use rustc_span::{Span, Spanned, respan, sym};

use crate::diagnostics;
use crate::parser::Parser;
use crate::{diagnostics, exp};

impl<'a> Parser<'a> {
/// Recover from alphabetic logic operators `and` and `or` as found in e.g., Python and PHP.
Expand Down Expand Up @@ -216,6 +216,38 @@ impl<'a> Parser<'a> {
}
err
}

/// Recover from array expressions as found in C like `{0, 1, 2, 3}`.
pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option<Box<Expr>> {
if !self.may_recover()
|| self.token.kind != token::OpenBrace
|| self.look_ahead(1, |t| !matches!(t.kind, token::Literal(_)))
|| self.look_ahead(2, |t| t != &token::Comma)
|| self.look_ahead(3, |t| !t.can_begin_expr())
{
return None;
}

let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => {
let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces {
span: arr.span,
sub: diagnostics::ArrayBracketsInsteadOfBracesSugg {
left: lo,
right: snapshot.prev_token.span,
},
});

self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span, guar))
}
Err(e) => {
e.cancel();
None
}
}
}
}

#[derive(Copy, Clone)]
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_ast::{
LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
};
use rustc_errors::{Applicability, Diag, PResult};
use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
use thin_vec::{ThinVec, thin_vec};

use super::attr::InnerAttrForbiddenReason;
Expand Down Expand Up @@ -467,7 +467,7 @@ impl<'a> Parser<'a> {
),
};
self.dcx().emit_err(diagnostics::InvalidCurlyInLetElse {
span: span.with_lo(span.hi() - BytePos(1)),
span: self.psess.source_map().end_point(span),
sugg,
});
}
Expand Down
22 changes: 19 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use rustc_ast::util::case::Case;
use rustc_ast::{
self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy,
GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability,
Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty,
TyKind, UnsafeBinderTy,
Path, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax,
Ty, TyKind, UnsafeBinderTy,
};
use rustc_errors::{Applicability, Diag, E0516, PResult};
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
Expand Down Expand Up @@ -406,7 +406,23 @@ impl<'a> Parser<'a> {
let msg = format!("expected type, found {}", super::token_descr(&self.token));
let mut err = self.dcx().struct_span_err(lo, msg);
err.span_label(lo, "expected type");
return Err(err);
if self.may_recover()
&& (self.eat_keyword_noexpect(kw::True) || self.eat_keyword_noexpect(kw::False))
{
err.span_suggestion(
self.prev_token.span,
"the type is called",
"bool",
Applicability::MachineApplicable,
);
err.emit();
TyKind::Path(
None,
Path::from_ident(Ident { span: self.prev_token.span, name: sym::bool }),
)
} else {
return Err(err);
}
};

let span = lo.to(self.prev_token.span);
Expand Down
22 changes: 22 additions & 0 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,28 @@ impl Dir {
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
self.inner.remove_dir(path.as_ref())
}

/// Creates a new `Dir` instance that shares the same underlying directory handle
/// as the existing `Dir` instance.
///
/// # Examples
///
/// Creates two handles for a directory named `foo`:
///
/// ```no_run
/// #![feature(dirfd)]
/// use std::fs::Dir;
///
/// fn main() -> std::io::Result<()> {
/// let dir = Dir::open("foo")?;
/// let dir_copy = dir.try_clone()?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "dirfd", issue = "120426")]
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Dir { inner: self.inner.duplicate()? })
}
}

impl AsInner<fs_imp::Dir> for Dir {
Expand Down
13 changes: 13 additions & 0 deletions library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2726,6 +2726,19 @@ fn test_dir_read_file() {
assert_eq!("bar", &buf);
}

#[test]
fn test_dir_clone() {
let tmpdir = tmpdir();
let mut f = check!(File::create(tmpdir.join("foo.txt")));
check!(f.write_all(b"bar"));
drop(f);

let dir = check!(Dir::open(tmpdir.path()));
let dir2 = check!(dir.try_clone());
let f = check!(dir2.open_file("foo.txt"));
drop(f);
}

#[test]
fn test_dir_metadata() {
let tmpdir = tmpdir();
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/fs/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ impl Dir {
Self::open(path, &opts)
}

pub fn duplicate(&self) -> io::Result<Self> {
Ok(Self { path: self.path.clone() })
}

pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
File::open(&self.path.join(path), opts)
}
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/fs/unix/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ impl Dir {
run_path_with_cstr(path, &|path| Self::open_traversal_c(path))
}

pub fn duplicate(&self) -> io::Result<Self> {
Ok(Self(self.0.try_clone()?))
}

pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts, 0))
.map(FileDesc::from_inner)
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/fs/windows/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ impl Dir {
with_native_path(path, &|path| Self::open_with_native(path, &opts))
}

pub fn duplicate(&self) -> io::Result<Self> {
Ok(Self { handle: self.handle.try_clone()? })
}

pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
// NtCreateFile will fail if given an absolute path and a non-null RootDirectory
if path.is_absolute() {
Expand Down
1 change: 0 additions & 1 deletion tests/assembly-llvm/pauth-basic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//@ add-minicore
//@ assembly-output: emit-asm
//@ only-pauthtest
//@ revisions: aarch64_unknown_linux_pauthtest
//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target=aarch64-unknown-linux-pauthtest
//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64
Expand Down
1 change: 0 additions & 1 deletion tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// ignore-tidy-file-linelength
//@ only-pauthtest
//@ revisions: DEFAULT ALL DISABLE_JUMP DISABLE_AUTH_TRAPS DISABLE_CALLS DISABLE_INDIRCT_GOTOS DISABLE_RETURNS DISABLE_INTRINSICS DISABLE_TYPEINFO DISABLE_VT_PTR_ADDR DISABLE_VT_PTR_TYPE NONE

//@ add-minicore
Expand Down
2 changes: 2 additions & 0 deletions tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth
// attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the
// test.
// Cannot be converted to minicore: catch_unwind is a std API and is what causes rustc to generate
// the __rust_try wrapper this test is checking.

//@ needs-llvm-components: aarch64

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//@ add-minicore
// ignore-tidy-linelength
//@ only-pauthtest
//@ revisions: O0_PAUTH O3_PAUTH

//@ [O0_PAUTH] needs-llvm-components: aarch64
//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0
// O3_PAUTH relies on an InstCombine fold that removes the redundant `ptrauth` bundle from calls.
// That fold first shipped in LLVM 22, gate this revision accordingly.
//@ [O3_PAUTH] min-llvm-version: 22
//@ [O3_PAUTH] needs-llvm-components: aarch64
//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3

Expand Down
1 change: 0 additions & 1 deletion tests/codegen-llvm/pauth/pauth-extern-c.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// ignore-tidy-file-linelength
//@ only-pauthtest
//@ add-minicore

//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH
Expand Down
1 change: 0 additions & 1 deletion tests/codegen-llvm/pauth/pauth-extern-weak-global.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// ignore-tidy-linelength
//@ only-pauthtest
//@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH
//@ add-minicore

Expand Down
1 change: 0 additions & 1 deletion tests/codegen-llvm/pauth/pauth-init-fini.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// ignore-tidy-file-linelength
//@ add-minicore
//@ only-pauthtest
//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC O0_PAUTH-NO-INIT-FINI O3_PAUTH-NO-INIT-FINI

//@ [O0_PAUTH] needs-llvm-components: aarch64
Expand Down
32 changes: 28 additions & 4 deletions tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
// Test that we can recover from very basic C arrays in the parser & provide a good diagnostic.

fn main() {}

const FOO: [u8; 3] = {
const INTS: [u8; 3] = {
//~^ ERROR this is a block expression, not an array
1, 2, 3
};

const BAR: [&str; 3] = {"one", "two", "three"};
const STRS: [&str; 3] = {"one", "two", "three"};
//~^ ERROR this is a block expression, not an array

fn foo() {
fn expr_stmt() {
{1, 2, 3};
//~^ ERROR this is a block expression, not an array
}

fn bar() {
// Don't trigger here.
fn unsafe_block() {
unsafe { 1, 2, 3 } //~ ERROR expected one of
}

// Don't trigger here.
fn labeled_block() {
'label: { 1, 2, 3 } //~ ERROR expected one of
}

// Don't trigger here, this is not a block expression, only a block.
fn fn_body_block() {
1, 2, 3 //~ ERROR expected one of
}

// Don't trigger here, this is not a block expression, only a block.
fn closure_body_block() {
|| -> i32 { 1, 2, 3 }; //~ ERROR expected one of
}

// Don't trigger here.
fn const_arg() {
struct Casket<const N: usize>;
Casket::<{ 1, 2, 3 }>; //~ ERROR expected one of
}
Loading
Loading