From 4de9c6d4913a02b5ce19a14e9e2ab0c46ceea771 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Feb 2023 15:32:38 -0700 Subject: [PATCH 01/16] rustdoc: search by macro when query ends with `!` Related to #96399 --- src/librustdoc/html/static/js/search.js | 28 ++++++++++++--- tests/rustdoc-js-std/parser-errors.js | 20 +++++++++++ tests/rustdoc-js-std/parser-filter.js | 47 ++++++++++++++++++++++++- tests/rustdoc-js-std/parser-ident.js | 40 +++++++++++---------- tests/rustdoc-js/macro-search.js | 10 ++++++ tests/rustdoc-js/macro-search.rs | 10 ++++++ 6 files changed, 132 insertions(+), 23 deletions(-) create mode 100644 tests/rustdoc-js/macro-search.js create mode 100644 tests/rustdoc-js/macro-search.rs diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1e6c94d29ba47..6a8e93a243681 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -300,20 +300,21 @@ function initSearch(rawSearchIndex) { * @return {integer} */ function getIdentEndPosition(parserState) { + const start = parserState.pos; let end = parserState.pos; - let foundExclamation = false; + let foundExclamation = -1; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; if (!isIdentCharacter(c)) { if (c === "!") { - if (foundExclamation) { + if (foundExclamation !== -1) { throw new Error("Cannot have more than one `!` in an ident"); } else if (parserState.pos + 1 < parserState.length && isIdentCharacter(parserState.userQuery[parserState.pos + 1]) ) { throw new Error("`!` can only be at the end of an ident"); } - foundExclamation = true; + foundExclamation = parserState.pos; } else if (isErrorCharacter(c)) { throw new Error(`Unexpected \`${c}\``); } else if ( @@ -326,9 +327,18 @@ function initSearch(rawSearchIndex) { if (!isPathStart(parserState)) { break; } + if (foundExclamation !== -1) { + if (start <= (end - 2)) { + throw new Error("Cannot have associated items in macros"); + } else { + // if start == end - 1, we got the never type + // while the never type has no associated macros, we still + // can parse a path like that + foundExclamation = -1; + } + } // Skip current ":". parserState.pos += 1; - foundExclamation = false; } else { throw new Error(`Unexpected \`${c}\``); } @@ -336,6 +346,16 @@ function initSearch(rawSearchIndex) { parserState.pos += 1; end = parserState.pos; } + // if start == end - 1, we got the never type + if (foundExclamation !== -1 && start <= (end - 2)) { + if (parserState.typeFilter === null) { + parserState.typeFilter = "macro"; + } else if (parserState.typeFilter !== "macro") { + throw new Error(`Invalid search type: macro \`!\` and ` + + `\`${parserState.typeFilter}\` both specified`); + } + end = foundExclamation; + } return end; } diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index dc42031e05f2f..f82a2472063ce 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -37,6 +37,8 @@ const QUERY = [ "mod : :", "a!a", "a!!", + "mod:a!", + "a!::a", ]; const PARSED = [ @@ -382,4 +384,22 @@ const PARSED = [ userQuery: "a!!", error: 'Cannot have more than one `!` in an ident', }, + { + elems: [], + foundElems: 0, + original: "mod:a!", + returned: [], + typeFilter: -1, + userQuery: "mod:a!", + error: 'Invalid search type: macro `!` and `mod` both specified', + }, + { + elems: [], + foundElems: 0, + original: "a!::a", + returned: [], + typeFilter: -1, + userQuery: "a!::a", + error: 'Cannot have associated items in macros', + }, ]; diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js index e5a87a415ac47..01f65b478f8e9 100644 --- a/tests/rustdoc-js-std/parser-filter.js +++ b/tests/rustdoc-js-std/parser-filter.js @@ -1,4 +1,4 @@ -const QUERY = ['fn:foo', 'enum : foo', 'macro:foo']; +const QUERY = ['fn:foo', 'enum : foo', 'macro:foo', 'macro!', 'macro:mac!', 'a::mac!']; const PARSED = [ { @@ -40,4 +40,49 @@ const PARSED = [ userQuery: "macro:foo", error: "Unexpected `:`", }, + { + elems: [{ + name: "macro", + fullPath: ["macro"], + pathWithoutLast: [], + pathLast: "macro", + generics: [], + }], + foundElems: 1, + original: "macro!", + returned: [], + typeFilter: 14, + userQuery: "macro!", + error: null, + }, + { + elems: [{ + name: "mac", + fullPath: ["mac"], + pathWithoutLast: [], + pathLast: "mac", + generics: [], + }], + foundElems: 1, + original: "macro:mac!", + returned: [], + typeFilter: 14, + userQuery: "macro:mac!", + error: null, + }, + { + elems: [{ + name: "a::mac", + fullPath: ["a", "mac"], + pathWithoutLast: ["a"], + pathLast: "mac", + generics: [], + }], + foundElems: 1, + original: "a::mac!", + returned: [], + typeFilter: 14, + userQuery: "a::mac!", + error: null, + }, ]; diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js index 4b5ab01ac761b..6c17d00f16edc 100644 --- a/tests/rustdoc-js-std/parser-ident.js +++ b/tests/rustdoc-js-std/parser-ident.js @@ -3,6 +3,7 @@ const QUERY = [ "!", "a!", "a!::b", + "!::b", "a!::b!", ]; @@ -47,47 +48,50 @@ const PARSED = [ }, { elems: [{ - name: "a!", - fullPath: ["a!"], + name: "a", + fullPath: ["a"], pathWithoutLast: [], - pathLast: "a!", + pathLast: "a", generics: [], }], foundElems: 1, original: "a!", returned: [], - typeFilter: -1, + typeFilter: 14, userQuery: "a!", error: null, }, { - elems: [{ - name: "a!::b", - fullPath: ["a!", "b"], - pathWithoutLast: ["a!"], - pathLast: "b", - generics: [], - }], - foundElems: 1, + elems: [], + foundElems: 0, original: "a!::b", returned: [], typeFilter: -1, userQuery: "a!::b", - error: null, + error: "Cannot have associated items in macros", }, { elems: [{ - name: "a!::b!", - fullPath: ["a!", "b!"], - pathWithoutLast: ["a!"], - pathLast: "b!", + name: "!::b", + fullPath: ["!", "b"], + pathWithoutLast: ["!"], + pathLast: "b", generics: [], }], foundElems: 1, + original: "!::b", + returned: [], + typeFilter: -1, + userQuery: "!::b", + error: null, + }, + { + elems: [], + foundElems: 0, original: "a!::b!", returned: [], typeFilter: -1, userQuery: "a!::b!", - error: null, + error: "Cannot have associated items in macros", }, ]; diff --git a/tests/rustdoc-js/macro-search.js b/tests/rustdoc-js/macro-search.js new file mode 100644 index 0000000000000..2b179ce146bf0 --- /dev/null +++ b/tests/rustdoc-js/macro-search.js @@ -0,0 +1,10 @@ +// exact-check + +const QUERY = 'abracadabra!'; + +const EXPECTED = { + 'others': [ + { 'path': 'macro_search', 'name': 'abracadabra' }, + { 'path': 'macro_search', 'name': 'abracadabra_b' }, + ], +}; diff --git a/tests/rustdoc-js/macro-search.rs b/tests/rustdoc-js/macro-search.rs new file mode 100644 index 0000000000000..dc397490cf583 --- /dev/null +++ b/tests/rustdoc-js/macro-search.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! abracadabra { + () => {} +} +#[macro_export] +macro_rules! abracadabra_b { + () => {} +} +pub fn abracadabra() {} +pub fn abracadabra_c() {} From d963318c1d04b2250f78f7fa43a8ee1173110d9f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Feb 2023 19:22:03 -0700 Subject: [PATCH 02/16] Correct eslint warning --- src/librustdoc/html/static/js/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 6a8e93a243681..ae15155341d25 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -351,7 +351,7 @@ function initSearch(rawSearchIndex) { if (parserState.typeFilter === null) { parserState.typeFilter = "macro"; } else if (parserState.typeFilter !== "macro") { - throw new Error(`Invalid search type: macro \`!\` and ` + + throw new Error("Invalid search type: macro `!` and " + `\`${parserState.typeFilter}\` both specified`); } end = foundExclamation; From 07cf219722420deb87b0186a64d26eed25449e66 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 21 Feb 2023 21:52:44 -0700 Subject: [PATCH 03/16] Update how-to-read-rustdoc.md --- src/doc/rustdoc/src/how-to-read-rustdoc.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md index d666d54b31579..28a004a92531a 100644 --- a/src/doc/rustdoc/src/how-to-read-rustdoc.md +++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md @@ -84,6 +84,9 @@ When typing in the search bar, you can prefix your search term with a type followed by a colon (such as `mod:`) to restrict the results to just that kind of item. (The available items are listed in the help popup.) +Searching for `println!` will search for a macro named `println`, just like +searching for `macro:println` does. + ### Changing displayed theme You can change the displayed theme by opening the settings menu (the gear From 611ab684e109cf4d10e9989460cd50aaf6d1ef9a Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sun, 19 Feb 2023 21:15:49 +0100 Subject: [PATCH 04/16] use span of semicolon for eager recovery in expression instead of the span of the token after the semicolon --- compiler/rustc_parse/src/parser/expr.rs | 2 +- .../issue-108242-semicolon-recovery.rs | 6 ++ .../issue-108242-semicolon-recovery.stderr | 62 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/ui/argument-suggestions/issue-108242-semicolon-recovery.rs create mode 100644 tests/ui/argument-suggestions/issue-108242-semicolon-recovery.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 12f65a436e3be..6e4612eee28a7 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1389,7 +1389,7 @@ impl<'a> Parser<'a> { // 2 | foo(bar(; // | ^ expected expression self.bump(); - Ok(self.mk_expr_err(self.token.span)) + Ok(self.mk_expr_err(self.prev_token.span)) } else if self.token.uninterpolated_span().rust_2018() { // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. if self.check_keyword(kw::Async) { diff --git a/tests/ui/argument-suggestions/issue-108242-semicolon-recovery.rs b/tests/ui/argument-suggestions/issue-108242-semicolon-recovery.rs new file mode 100644 index 0000000000000..7cab377ea251a --- /dev/null +++ b/tests/ui/argument-suggestions/issue-108242-semicolon-recovery.rs @@ -0,0 +1,6 @@ +fn foo() {} +fn main() { + foo(; //~ ERROR this function takes 0 arguments but 2 arguments were supplied + foo(; //~ ERROR this function takes 0 arguments but 1 argument was supplied + //~^ ERROR expected one of +} //~ ERROR mismatched closing delimiter diff --git a/tests/ui/argument-suggestions/issue-108242-semicolon-recovery.stderr b/tests/ui/argument-suggestions/issue-108242-semicolon-recovery.stderr new file mode 100644 index 0000000000000..2ed8fd3ea8bbc --- /dev/null +++ b/tests/ui/argument-suggestions/issue-108242-semicolon-recovery.stderr @@ -0,0 +1,62 @@ +error: expected one of `)`, `,`, `.`, `?`, or an operator, found `foo` + --> $DIR/issue-108242-semicolon-recovery.rs:4:5 + | +LL | foo(; + | - + | | + | expected one of `)`, `,`, `.`, `?`, or an operator + | help: missing `,` +LL | foo(; + | ^^^ unexpected token + +error: mismatched closing delimiter: `}` + --> $DIR/issue-108242-semicolon-recovery.rs:4:8 + | +LL | fn main() { + | - closing delimiter possibly meant for this +LL | foo(; +LL | foo(; + | ^ unclosed delimiter +LL | +LL | } + | ^ mismatched closing delimiter + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/issue-108242-semicolon-recovery.rs:4:5 + | +LL | foo(; + | ^^^ - + | | + | unexpected argument + | help: remove the extra argument + | +note: function defined here + --> $DIR/issue-108242-semicolon-recovery.rs:1:4 + | +LL | fn foo() {} + | ^^^ + +error[E0061]: this function takes 0 arguments but 2 arguments were supplied + --> $DIR/issue-108242-semicolon-recovery.rs:3:5 + | +LL | foo(; + | ^^^ - unexpected argument +LL | / foo(; +LL | | +LL | | } + | |_- unexpected argument of type `()` + | +note: function defined here + --> $DIR/issue-108242-semicolon-recovery.rs:1:4 + | +LL | fn foo() {} + | ^^^ +help: remove the extra arguments + | +LL - foo(; +LL + foo( + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0061`. From 7ee01b4b800233db0d494d433b318ce06ec25115 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 23 Feb 2023 18:51:31 +0000 Subject: [PATCH 05/16] Lazily compute crate name for consider_optimizing The extra query is unnecessary in the common case of not having fuel. --- compiler/rustc_middle/src/ty/context.rs | 3 +-- compiler/rustc_session/src/session.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7e6a6e71670f3..9027422167d04 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -793,8 +793,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn consider_optimizing String>(self, msg: T) -> bool { - let cname = self.crate_name(LOCAL_CRATE); - self.sess.consider_optimizing(cname.as_str(), msg) + self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg) } /// Obtain all lang items of this crate and all dependencies (recursively) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 446ba63ed1c86..12634f67185fd 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -882,10 +882,14 @@ impl Session { /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { + pub fn consider_optimizing( + &self, + get_crate_name: impl Fn() -> Symbol, + msg: impl Fn() -> String, + ) -> bool { let mut ret = true; if let Some((ref c, _)) = self.opts.unstable_opts.fuel { - if c == crate_name { + if c == get_crate_name().as_str() { assert_eq!(self.threads(), 1); let mut fuel = self.optimization_fuel.lock(); ret = fuel.remaining != 0; @@ -903,7 +907,7 @@ impl Session { } } if let Some(ref c) = self.opts.unstable_opts.print_fuel { - if c == crate_name { + if c == get_crate_name().as_str() { assert_eq!(self.threads(), 1); self.print_fuel.fetch_add(1, SeqCst); } From 8574085abb91d2218af4045e25c5a5e3870dc879 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Thu, 23 Feb 2023 20:12:01 +0100 Subject: [PATCH 06/16] recover from for-else and while-else --- compiler/rustc_parse/locales/en-US.ftl | 3 +++ compiler/rustc_parse/src/errors.rs | 9 +++++++++ compiler/rustc_parse/src/parser/expr.rs | 20 +++++++++++++++++++ tests/ui/for/for-else-err.rs | 8 ++++++++ tests/ui/for/for-else-err.stderr | 14 +++++++++++++ tests/ui/for/for-else-let-else-err.rs | 8 ++++++++ tests/ui/for/for-else-let-else-err.stderr | 14 +++++++++++++ tests/ui/while/while-else-err.rs | 8 ++++++++ tests/ui/while/while-else-err.stderr | 14 +++++++++++++ tests/ui/while/while-else-let-else-err.rs | 8 ++++++++ tests/ui/while/while-else-let-else-err.stderr | 14 +++++++++++++ 11 files changed, 120 insertions(+) create mode 100644 tests/ui/for/for-else-err.rs create mode 100644 tests/ui/for/for-else-err.stderr create mode 100644 tests/ui/for/for-else-let-else-err.rs create mode 100644 tests/ui/for/for-else-let-else-err.stderr create mode 100644 tests/ui/while/while-else-err.rs create mode 100644 tests/ui/while/while-else-err.stderr create mode 100644 tests/ui/while/while-else-let-else-err.rs create mode 100644 tests/ui/while/while-else-let-else-err.stderr diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index a31b1f6ac1a0a..0c63026ed35fb 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -151,6 +151,9 @@ parse_missing_in_in_for_loop = missing `in` in `for` loop parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop .suggestion = try adding an expression to the `for` loop +parse_loop_else = `{$loop_kind}...else` loops are not supported + .note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + parse_missing_comma_after_match_arm = expected `,` following `match` arm .suggestion = missing a comma here to end this `match` arm diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index c746a87096476..1a6884e7fb54c 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -451,6 +451,15 @@ pub(crate) struct MissingExpressionInForLoop { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_loop_else)] +#[note] +pub(crate) struct LoopElseNotSupported { + #[primary_span] + pub span: Span, + pub loop_kind: &'static str, +} + #[derive(Diagnostic)] #[diag(parse_missing_comma_after_match_arm)] pub(crate) struct MissingCommaAfterMatchArm { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 12f65a436e3be..50e3a049c9072 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2491,9 +2491,26 @@ impl<'a> Parser<'a> { let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label); + + self.recover_loop_else("for")?; + Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } + /// Recovers from an `else` clause after a loop (`for...else`, `while...else`) + fn recover_loop_else(&mut self, loop_kind: &'static str) -> PResult<'a, ()> { + if self.token.is_keyword(kw::Else) && self.may_recover() { + let else_span = self.token.span; + self.bump(); + let else_clause = self.parse_else_expr()?; + self.sess.emit_err(errors::LoopElseNotSupported { + span: else_span.to(else_clause.span), + loop_kind, + }); + } + Ok(()) + } + fn error_missing_in_for_loop(&mut self) { let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) { // Possibly using JS syntax (#75311). @@ -2518,6 +2535,9 @@ impl<'a> Parser<'a> { err.span_label(cond.span, "this `while` condition successfully parsed"); err })?; + + self.recover_loop_else("while")?; + Ok(self.mk_expr_with_attrs( lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), diff --git a/tests/ui/for/for-else-err.rs b/tests/ui/for/for-else-err.rs new file mode 100644 index 0000000000000..c221084ada0dc --- /dev/null +++ b/tests/ui/for/for-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + for _ in 0..1 { + + } else { + //~^ ERROR `for...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + } +} diff --git a/tests/ui/for/for-else-err.stderr b/tests/ui/for/for-else-err.stderr new file mode 100644 index 0000000000000..a5af8bbab30c0 --- /dev/null +++ b/tests/ui/for/for-else-err.stderr @@ -0,0 +1,14 @@ +error: `for...else` loops are not supported + --> $DIR/for-else-err.rs:4:7 + | +LL | } else { + | _______^ +LL | | +LL | | +LL | | } + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/for/for-else-let-else-err.rs b/tests/ui/for/for-else-let-else-err.rs new file mode 100644 index 0000000000000..d4cb1425fc3d4 --- /dev/null +++ b/tests/ui/for/for-else-let-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + let _ = for _ in 0..1 { + + } else { + //~^ ERROR `for...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/for/for-else-let-else-err.stderr b/tests/ui/for/for-else-let-else-err.stderr new file mode 100644 index 0000000000000..33b3879cd90b5 --- /dev/null +++ b/tests/ui/for/for-else-let-else-err.stderr @@ -0,0 +1,14 @@ +error: `for...else` loops are not supported + --> $DIR/for-else-let-else-err.rs:4:7 + | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/while/while-else-err.rs b/tests/ui/while/while-else-err.rs new file mode 100644 index 0000000000000..5b38d0daa2e6c --- /dev/null +++ b/tests/ui/while/while-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + while false { + + } else { + //~^ ERROR `while...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/while/while-else-err.stderr b/tests/ui/while/while-else-err.stderr new file mode 100644 index 0000000000000..7ade56ea4f844 --- /dev/null +++ b/tests/ui/while/while-else-err.stderr @@ -0,0 +1,14 @@ +error: `while...else` loops are not supported + --> $DIR/while-else-err.rs:4:7 + | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/while/while-else-let-else-err.rs b/tests/ui/while/while-else-let-else-err.rs new file mode 100644 index 0000000000000..d9ac6df4de307 --- /dev/null +++ b/tests/ui/while/while-else-let-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + let _ = while false { + + } else { + //~^ ERROR `while...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/while/while-else-let-else-err.stderr b/tests/ui/while/while-else-let-else-err.stderr new file mode 100644 index 0000000000000..87e9f2c244ddc --- /dev/null +++ b/tests/ui/while/while-else-let-else-err.stderr @@ -0,0 +1,14 @@ +error: `while...else` loops are not supported + --> $DIR/while-else-let-else-err.rs:4:7 + | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + From 8820631a53c3754bf68742364372db91426d356e Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:28:39 +0100 Subject: [PATCH 07/16] add loop keyword to error diag --- compiler/rustc_parse/locales/en-US.ftl | 1 + compiler/rustc_parse/src/errors.rs | 2 ++ compiler/rustc_parse/src/parser/expr.rs | 7 ++++--- tests/ui/for/for-else-err.rs | 2 +- tests/ui/for/for-else-err.stderr | 3 +++ tests/ui/for/for-else-let-else-err.rs | 2 +- tests/ui/for/for-else-let-else-err.stderr | 3 +++ tests/ui/while/while-else-err.rs | 2 +- tests/ui/while/while-else-err.stderr | 3 +++ tests/ui/while/while-else-let-else-err.rs | 2 +- tests/ui/while/while-else-let-else-err.stderr | 3 +++ 11 files changed, 23 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index 0c63026ed35fb..5dd3e9ac33df6 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -153,6 +153,7 @@ parse_missing_expression_in_for_loop = missing expression to iterate on in `for` parse_loop_else = `{$loop_kind}...else` loops are not supported .note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + .loop_keyword = `else` is attached to this loop parse_missing_comma_after_match_arm = expected `,` following `match` arm .suggestion = missing a comma here to end this `match` arm diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 1a6884e7fb54c..1662db36d10f9 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -458,6 +458,8 @@ pub(crate) struct LoopElseNotSupported { #[primary_span] pub span: Span, pub loop_kind: &'static str, + #[label(parse_loop_keyword)] + pub loop_kw: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 50e3a049c9072..abfc1b55fda86 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2492,13 +2492,13 @@ impl<'a> Parser<'a> { let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label); - self.recover_loop_else("for")?; + self.recover_loop_else("for", lo)?; Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } /// Recovers from an `else` clause after a loop (`for...else`, `while...else`) - fn recover_loop_else(&mut self, loop_kind: &'static str) -> PResult<'a, ()> { + fn recover_loop_else(&mut self, loop_kind: &'static str, loop_kw: Span) -> PResult<'a, ()> { if self.token.is_keyword(kw::Else) && self.may_recover() { let else_span = self.token.span; self.bump(); @@ -2506,6 +2506,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::LoopElseNotSupported { span: else_span.to(else_clause.span), loop_kind, + loop_kw, }); } Ok(()) @@ -2536,7 +2537,7 @@ impl<'a> Parser<'a> { err })?; - self.recover_loop_else("while")?; + self.recover_loop_else("while", lo)?; Ok(self.mk_expr_with_attrs( lo.to(self.prev_token.span), diff --git a/tests/ui/for/for-else-err.rs b/tests/ui/for/for-else-err.rs index c221084ada0dc..16252e980e4ff 100644 --- a/tests/ui/for/for-else-err.rs +++ b/tests/ui/for/for-else-err.rs @@ -1,6 +1,6 @@ fn main() { for _ in 0..1 { - + //~^ NOTE `else` is attached to this loop } else { //~^ ERROR `for...else` loops are not supported //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run diff --git a/tests/ui/for/for-else-err.stderr b/tests/ui/for/for-else-err.stderr index a5af8bbab30c0..b330d10764769 100644 --- a/tests/ui/for/for-else-err.stderr +++ b/tests/ui/for/for-else-err.stderr @@ -1,6 +1,9 @@ error: `for...else` loops are not supported --> $DIR/for-else-err.rs:4:7 | +LL | for _ in 0..1 { + | --- `else` is attached to this loop +LL | LL | } else { | _______^ LL | | diff --git a/tests/ui/for/for-else-let-else-err.rs b/tests/ui/for/for-else-let-else-err.rs index d4cb1425fc3d4..c0b96f9729456 100644 --- a/tests/ui/for/for-else-let-else-err.rs +++ b/tests/ui/for/for-else-let-else-err.rs @@ -1,6 +1,6 @@ fn main() { let _ = for _ in 0..1 { - + //~^ NOTE `else` is attached to this loop } else { //~^ ERROR `for...else` loops are not supported //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run diff --git a/tests/ui/for/for-else-let-else-err.stderr b/tests/ui/for/for-else-let-else-err.stderr index 33b3879cd90b5..a2396a8fbb161 100644 --- a/tests/ui/for/for-else-let-else-err.stderr +++ b/tests/ui/for/for-else-let-else-err.stderr @@ -1,6 +1,9 @@ error: `for...else` loops are not supported --> $DIR/for-else-let-else-err.rs:4:7 | +LL | let _ = for _ in 0..1 { + | --- `else` is attached to this loop +LL | LL | } else { | _______^ LL | | diff --git a/tests/ui/while/while-else-err.rs b/tests/ui/while/while-else-err.rs index 5b38d0daa2e6c..36b60fbd4be24 100644 --- a/tests/ui/while/while-else-err.rs +++ b/tests/ui/while/while-else-err.rs @@ -1,6 +1,6 @@ fn main() { while false { - + //~^ NOTE `else` is attached to this loop } else { //~^ ERROR `while...else` loops are not supported //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run diff --git a/tests/ui/while/while-else-err.stderr b/tests/ui/while/while-else-err.stderr index 7ade56ea4f844..88f715d566609 100644 --- a/tests/ui/while/while-else-err.stderr +++ b/tests/ui/while/while-else-err.stderr @@ -1,6 +1,9 @@ error: `while...else` loops are not supported --> $DIR/while-else-err.rs:4:7 | +LL | while false { + | ----- `else` is attached to this loop +LL | LL | } else { | _______^ LL | | diff --git a/tests/ui/while/while-else-let-else-err.rs b/tests/ui/while/while-else-let-else-err.rs index d9ac6df4de307..6d9909347c3b1 100644 --- a/tests/ui/while/while-else-let-else-err.rs +++ b/tests/ui/while/while-else-let-else-err.rs @@ -1,6 +1,6 @@ fn main() { let _ = while false { - + //~^ NOTE `else` is attached to this loop } else { //~^ ERROR `while...else` loops are not supported //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run diff --git a/tests/ui/while/while-else-let-else-err.stderr b/tests/ui/while/while-else-let-else-err.stderr index 87e9f2c244ddc..431d37c007c6d 100644 --- a/tests/ui/while/while-else-let-else-err.stderr +++ b/tests/ui/while/while-else-let-else-err.stderr @@ -1,6 +1,9 @@ error: `while...else` loops are not supported --> $DIR/while-else-let-else-err.rs:4:7 | +LL | let _ = while false { + | ----- `else` is attached to this loop +LL | LL | } else { | _______^ LL | | From 152a0297fc080bc0fc20252a765c34ca787e86c3 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 24 Feb 2023 19:06:11 +0100 Subject: [PATCH 08/16] recover from `loop...else` --- compiler/rustc_parse/src/parser/expr.rs | 1 + tests/ui/loops/loop-else-err.rs | 8 ++++++++ tests/ui/loops/loop-else-err.stderr | 17 +++++++++++++++++ tests/ui/loops/loop-else-let-else-err.rs | 8 ++++++++ tests/ui/loops/loop-else-let-else-err.stderr | 17 +++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 tests/ui/loops/loop-else-err.rs create mode 100644 tests/ui/loops/loop-else-err.stderr create mode 100644 tests/ui/loops/loop-else-let-else-err.rs create mode 100644 tests/ui/loops/loop-else-let-else-err.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index abfc1b55fda86..9be437d69d235 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2550,6 +2550,7 @@ impl<'a> Parser<'a> { fn parse_loop_expr(&mut self, opt_label: Option