Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock1
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs2
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/regression.rs21
-rw-r--r--crates/ide-completion/src/context/analysis.rs9
-rw-r--r--crates/ide-completion/src/tests/expression.rs252
-rw-r--r--crates/ide-completion/src/tests/special.rs81
-rw-r--r--crates/ide-db/src/active_parameter.rs80
-rw-r--r--crates/ide/src/signature_help.rs124
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/parser/Cargo.toml1
-rw-r--r--crates/parser/src/grammar.rs30
-rw-r--r--crates/parser/src/grammar/attributes.rs2
-rw-r--r--crates/parser/src/grammar/expressions.rs39
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs30
-rw-r--r--crates/parser/src/grammar/generic_args.rs32
-rw-r--r--crates/parser/src/grammar/generic_params.rs29
-rw-r--r--crates/parser/src/grammar/items/adt.rs25
-rw-r--r--crates/parser/src/grammar/params.rs14
-rw-r--r--crates/parser/src/grammar/paths.rs11
-rw-r--r--crates/parser/src/grammar/types.rs3
-rw-r--r--crates/parser/src/tests.rs6
-rw-r--r--crates/parser/src/tests/top_entries.rs2
-rw-r--r--crates/parser/test_data/parser/err/0009_broken_struct_type_parameter.rast3
-rw-r--r--crates/parser/test_data/parser/err/0013_invalid_type.rast38
-rw-r--r--crates/parser/test_data/parser/err/0022_bad_exprs.rast18
-rw-r--r--crates/parser/test_data/parser/err/0024_many_type_parens.rast211
-rw-r--r--crates/parser/test_data/parser/err/0025_nope.rast7
-rw-r--r--crates/parser/test_data/parser/err/0042_weird_blocks.rast2
-rw-r--r--crates/parser/test_data/parser/err/0048_double_fish.rast19
-rw-r--r--crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast2
-rw-r--r--crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast77
-rw-r--r--crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs5
-rw-r--r--crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast2
33 files changed, 815 insertions, 365 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6d8a3eeb73..6ce455424a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1174,6 +1174,7 @@ dependencies = [
"limit",
"rustc-ap-rustc_lexer",
"sourcegen",
+ "stdx",
]
[[package]]
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 49bbc64bff..7a3e8c3b05 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1476,7 +1476,7 @@ macro_rules! m {
/* parse error: expected identifier */
/* parse error: expected SEMICOLON */
/* parse error: expected SEMICOLON */
-/* parse error: expected expression */
+/* parse error: expected expression, item or let statement */
fn f() {
K::(C("0"));
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index d2505e7caf..8358a46f0a 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -830,8 +830,7 @@ macro_rules! rgb_color {
/* parse error: expected COMMA */
/* parse error: expected R_ANGLE */
/* parse error: expected SEMICOLON */
-/* parse error: expected SEMICOLON */
-/* parse error: expected expression */
+/* parse error: expected expression, item or let statement */
pub fn new() {
let _ = 0as u32<<(8+8);
}
@@ -848,21 +847,21 @@ pub fn new() {
@@ -877,9 +876,9 @@ pub fn new() {
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 4c66f95903..b5eb253d03 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -668,8 +668,15 @@ fn classify_name_ref(
};
let after_if_expr = |node: SyntaxNode| {
let prev_expr = (|| {
+ let node = match node.parent().and_then(ast::ExprStmt::cast) {
+ Some(stmt) => stmt.syntax().clone(),
+ None => node,
+ };
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
- ast::ExprStmt::cast(prev_sibling)?.expr()
+
+ ast::ExprStmt::cast(prev_sibling.clone())
+ .and_then(|it| it.expr())
+ .or_else(|| ast::Expr::cast(prev_sibling))
})();
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
};
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 043f552bd8..c1c6a689eb 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -745,3 +745,255 @@ fn return_value_no_block() {
r#"fn f() -> i32 { match () { () => return $0 } }"#,
);
}
+
+#[test]
+fn else_completion_after_if() {
+ check_empty(
+ r#"
+fn foo() { if foo {} $0 }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw const
+ kw crate::
+ kw else
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+ check_empty(
+ r#"
+fn foo() { if foo {} el$0 }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw const
+ kw crate::
+ kw else
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+ check_empty(
+ r#"
+fn foo() { bar(if foo {} $0) }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw crate::
+ kw else
+ kw else if
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check_empty(
+ r#"
+fn foo() { bar(if foo {} el$0) }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw crate::
+ kw else
+ kw else if
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check_empty(
+ r#"
+fn foo() { if foo {} $0 let x = 92; }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw const
+ kw crate::
+ kw else
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+ check_empty(
+ r#"
+fn foo() { if foo {} el$0 let x = 92; }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw const
+ kw crate::
+ kw else
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+ check_empty(
+ r#"
+fn foo() { if foo {} el$0 { let x = 92; } }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32
+ kw const
+ kw crate::
+ kw else
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 6052b06232..cb71c7b2bd 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -2,13 +2,20 @@
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list_no_kw, completion_list_with_trigger_character};
+use crate::tests::{
+ check_edit, completion_list, completion_list_no_kw, completion_list_with_trigger_character,
+};
-fn check(ra_fixture: &str, expect: Expect) {
+fn check_no_kw(ra_fixture: &str, expect: Expect) {
let actual = completion_list_no_kw(ra_fixture);
expect.assert_eq(&actual)
}
+fn check(ra_fixture: &str, expect: Expect) {
+ let actual = completion_list(ra_fixture);
+ expect.assert_eq(&actual)
+}
+
pub(crate) fn check_with_trigger_character(
ra_fixture: &str,
trigger_character: Option<char>,
@@ -59,7 +66,7 @@ fn _alpha() {}
#[test]
fn completes_prelude() {
- check(
+ check_no_kw(
r#"
//- /main.rs edition:2018 crate:main deps:std
fn foo() { let x: $0 }
@@ -81,7 +88,7 @@ pub mod prelude {
#[test]
fn completes_prelude_macros() {
- check(
+ check_no_kw(
r#"
//- /main.rs edition:2018 crate:main deps:std
fn f() {$0}
@@ -110,7 +117,7 @@ mod macros {
#[test]
fn completes_std_prelude_if_core_is_defined() {
- check(
+ check_no_kw(
r#"
//- /main.rs crate:main deps:core,std
fn foo() { let x: $0 }
@@ -140,7 +147,7 @@ pub mod prelude {
#[test]
fn respects_doc_hidden() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib deps:std
fn f() {
@@ -168,7 +175,7 @@ pub mod prelude {
#[test]
fn respects_doc_hidden_in_assoc_item_list() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib deps:std
struct S;
@@ -195,7 +202,7 @@ pub mod prelude {
#[test]
fn associated_item_visibility() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub struct S;
@@ -222,7 +229,7 @@ fn foo() { let _ = lib::S::$0 }
#[test]
fn completes_union_associated_method() {
- check(
+ check_no_kw(
r#"
union U {};
impl U { fn m() { } }
@@ -237,7 +244,7 @@ fn foo() { let _ = U::$0 }
#[test]
fn completes_trait_associated_method_1() {
- check(
+ check_no_kw(
r#"
trait Trait { fn m(); }
@@ -251,7 +258,7 @@ fn foo() { let _ = Trait::$0 }
#[test]
fn completes_trait_associated_method_2() {
- check(
+ check_no_kw(
r#"
trait Trait { fn m(); }
@@ -268,7 +275,7 @@ fn foo() { let _ = S::$0 }
#[test]
fn completes_trait_associated_method_3() {
- check(
+ check_no_kw(
r#"
trait Trait { fn m(); }
@@ -285,7 +292,7 @@ fn foo() { let _ = <S as Trait>::$0 }
#[test]
fn completes_ty_param_assoc_ty() {
- check(
+ check_no_kw(
r#"
trait Super {
type Ty;
@@ -318,7 +325,7 @@ fn foo<T: Sub>() { T::$0 }
#[test]
fn completes_self_param_assoc_ty() {
- check(
+ check_no_kw(
r#"
trait Super {
type Ty;
@@ -358,7 +365,7 @@ impl<T> Sub for Wrap<T> {
#[test]
fn completes_type_alias() {
- check(
+ check_no_kw(
r#"
struct S;
impl S { fn foo() {} }
@@ -376,7 +383,7 @@ fn main() { T::$0; }
#[test]
fn completes_qualified_macros() {
- check(
+ check_no_kw(
r#"
#[macro_export]
macro_rules! foo { () => {} }
@@ -392,7 +399,7 @@ fn main() { let _ = crate::$0 }
#[test]
fn does_not_complete_non_fn_macros() {
- check(
+ check_no_kw(
r#"
mod m {
#[rustc_builtin_macro]
@@ -403,7 +410,7 @@ fn f() {m::$0}
"#,
expect![[r#""#]],
);
- check(
+ check_no_kw(
r#"
mod m {
#[rustc_builtin_macro]
@@ -418,7 +425,7 @@ fn f() {m::$0}
#[test]
fn completes_reexported_items_under_correct_name() {
- check(
+ check_no_kw(
r#"
fn foo() { self::m::$0 }
@@ -475,7 +482,7 @@ mod p {
#[test]
fn completes_in_simple_macro_call() {
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
fn main() { m!(self::f$0); }
@@ -490,7 +497,7 @@ fn foo() {}
#[test]
fn function_mod_share_name() {
- check(
+ check_no_kw(
r#"
fn foo() { self::m::$0 }
@@ -508,7 +515,7 @@ mod m {
#[test]
fn completes_hashmap_new() {
- check(
+ check_no_kw(
r#"
struct RandomState;
struct HashMap<K, V, S = RandomState> {}
@@ -529,7 +536,7 @@ fn foo() {
#[test]
fn completes_variant_through_self() {
cov_mark::check!(completes_variant_through_self);
- check(
+ check_no_kw(
r#"
enum Foo {
Bar,
@@ -552,7 +559,7 @@ impl Foo {
#[test]
fn completes_non_exhaustive_variant_within_the_defining_crate() {
- check(
+ check_no_kw(
r#"
enum Foo {
#[non_exhaustive]
@@ -570,7 +577,7 @@ fn foo(self) {
"#]],
);
- check(
+ check_no_kw(
r#"
//- /main.rs crate:main deps:e
fn foo(self) {
@@ -593,7 +600,7 @@ enum Foo {
#[test]
fn completes_primitive_assoc_const() {
cov_mark::check!(completes_primitive_assoc_const);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib deps:core
fn f() {
@@ -618,7 +625,7 @@ impl u8 {
#[test]
fn completes_variant_through_alias() {
cov_mark::check!(completes_variant_through_alias);
- check(
+ check_no_kw(
r#"
enum Foo {
Bar
@@ -636,7 +643,7 @@ fn main() {
#[test]
fn respects_doc_hidden2() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib deps:dep
fn f() {
@@ -665,7 +672,7 @@ pub mod m {}
#[test]
fn type_anchor_empty() {
- check(
+ check_no_kw(
r#"
trait Foo {
fn foo() -> Self;
@@ -688,7 +695,7 @@ fn bar() -> Bar {
#[test]
fn type_anchor_type() {
- check(
+ check_no_kw(
r#"
trait Foo {
fn foo() -> Self;
@@ -715,7 +722,7 @@ fn bar() -> Bar {
#[test]
fn type_anchor_type_trait() {
- check(
+ check_no_kw(
r#"
trait Foo {
fn foo() -> Self;
@@ -741,7 +748,7 @@ fn bar() -> Bar {
#[test]
fn completes_fn_in_pub_trait_generated_by_macro() {
- check(
+ check_no_kw(
r#"
mod other_mod {
macro_rules! make_method {
@@ -775,7 +782,7 @@ fn main() {
#[test]
fn completes_fn_in_pub_trait_generated_by_recursive_macro() {
- check(
+ check_no_kw(
r#"
mod other_mod {
macro_rules! make_method {
@@ -815,7 +822,7 @@ fn main() {
#[test]
fn completes_const_in_pub_trait_generated_by_macro() {
- check(
+ check_no_kw(
r#"
mod other_mod {
macro_rules! make_const {
@@ -847,7 +854,7 @@ fn main() {
#[test]
fn completes_locals_from_macros() {
- check(
+ check_no_kw(
r#"
macro_rules! x {
@@ -875,7 +882,7 @@ fn main() {
#[test]
fn regression_12644() {
- check(
+ check_no_kw(
r#"
macro_rules! __rust_force_expr {
($e:expr) => {
@@ -974,7 +981,7 @@ fn foo { crate:::$0 }
"#,
expect![""],
);
- check(
+ check_no_kw(
r#"
fn foo { crate::::$0 }
"#,
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs
index 7109c6fd18..2b6b60547b 100644
--- a/crates/ide-db/src/active_parameter.rs
+++ b/crates/ide-db/src/active_parameter.rs
@@ -2,9 +2,10 @@
use either::Either;
use hir::{Semantics, Type};
+use parser::T;
use syntax::{
ast::{self, HasArgList, HasName},
- AstNode, SyntaxToken,
+ match_ast, AstNode, NodeOrToken, SyntaxToken,
};
use crate::RootDatabase;
@@ -58,7 +59,7 @@ pub fn callable_for_node(
calling_node: &ast::CallableExpr,
token: &SyntaxToken,
) -> Option<(hir::Callable, Option<usize>)> {
- let callable = match &calling_node {
+ let callable = match calling_node {
ast::CallableExpr::Call(call) => {
let expr = call.expr()?;
sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
@@ -66,13 +67,78 @@ pub fn callable_for_node(
ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
}?;
let active_param = if let Some(arg_list) = calling_node.arg_list() {
- let param = arg_list
- .args()
- .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
- .count();
- Some(param)
+ Some(
+ arg_list
+ .syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
+ .take_while(|t| t.text_range().start() <= token.text_range().start())
+ .count(),
+ )
} else {
None
};
Some((callable, active_param))
}
+
+pub fn generic_def_for_node(
+ sema: &Semantics<'_, RootDatabase>,
+ generic_arg_list: &ast::GenericArgList,
+ token: &SyntaxToken,
+) -> Option<(hir::GenericDef, usize, bool)> {
+ let parent = generic_arg_list.syntax().parent()?;
+ let def = match_ast! {
+ match parent {
+ ast::PathSegment(ps) => {
+ let res = sema.resolve_path(&ps.parent_path())?;
+ let generic_def: hir::GenericDef = match res {
+ hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
+ hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
+ hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
+ hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
+ hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
+ hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
+ | hir::PathResolution::Def(hir::ModuleDef::Const(_))
+ | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
+ | hir::PathResolution::Def(hir::ModuleDef::Module(_))
+ | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
+ hir::PathResolution::BuiltinAttr(_)
+ | hir::PathResolution::ToolModule(_)
+ | hir::PathResolution::Local(_)
+ | hir::PathResolution::TypeParam(_)
+ | hir::PathResolution::ConstParam(_)
+ | hir::PathResolution::SelfType(_)
+ | hir::PathResolution::DeriveHelper(_) => return None,
+ };
+
+ generic_def
+ },
+ ast::AssocTypeArg(_) => {
+ // FIXME: We don't record the resolutions for this anywhere atm
+ return None;
+ },
+ ast::MethodCallExpr(mcall) => {
+ // recv.method::<$0>()
+ let method = sema.resolve_method_call(&mcall)?;
+ method.into()
+ },
+ _ => return None,
+ }
+ };
+
+ let active_param = generic_arg_list
+ .syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
+ .take_while(|t| t.text_range().start() <= token.text_range().start())
+ .count();
+
+ let first_arg_is_non_lifetime = generic_arg_list
+ .generic_args()
+ .next()
+ .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
+
+ Some((def, active_param, first_arg_is_non_lifetime))
+}
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index a666562f10..f70ca55a50 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -7,12 +7,16 @@ use either::Either;
use hir::{
AssocItem, GenericParam, HasAttrs, HirDisplay, ModuleDef, PathResolution, Semantics, Trait,
};
-use ide_db::{active_parameter::callable_for_node, base_db::FilePosition, FxIndexMap};
+use ide_db::{
+ active_parameter::{callable_for_node, generic_def_for_node},
+ base_db::FilePosition,
+ FxIndexMap,
+};
use stdx::format_to;
use syntax::{
algo,
ast::{self, HasArgList},
- match_ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, TextSize,
+ match_ast, AstNode, Direction, SyntaxToken, TextRange, TextSize,
};
use crate::RootDatabase;
@@ -105,10 +109,10 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
// Stop at multi-line expressions, since the signature of the outer call is not very
// helpful inside them.
if let Some(expr) = ast::Expr::cast(node.clone()) {
- if expr.syntax().text().contains_char('\n')
- && expr.syntax().kind() != SyntaxKind::RECORD_EXPR
+ if !matches!(expr, ast::Expr::RecordExpr(..))
+ && expr.syntax().text().contains_char('\n')
{
- return None;
+ break;
}
}
}
@@ -122,18 +126,16 @@ fn signature_help_for_call(
token: SyntaxToken,
) -> Option<SignatureHelp> {
// Find the calling expression and its NameRef
- let mut node = arg_list.syntax().parent()?;
+ let mut nodes = arg_list.syntax().ancestors().skip(1);
let calling_node = loop {
- if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
- if callable
+ if let Some(callable) = ast::CallableExpr::cast(nodes.next()?) {
+ let inside_callable = callable
.arg_list()
- .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
- {
+ .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()));
+ if inside_callable {
break callable;
}
}
-
- node = node.parent()?;
};
let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?;
@@ -216,59 +218,11 @@ fn signature_help_for_call(
fn signature_help_for_generics(
sema: &Semantics<'_, RootDatabase>,
- garg_list: ast::GenericArgList,
+ arg_list: ast::GenericArgList,
token: SyntaxToken,
) -> Option<SignatureHelp> {
- let arg_list = garg_list
- .syntax()
- .ancestors()
- .filter_map(ast::GenericArgList::cast)
- .find(|list| list.syntax().text_range().contains(token.text_range().start()))?;
-
- let mut active_parameter = arg_list
- .generic_args()
- .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
- .count();
-
- let first_arg_is_non_lifetime = arg_list
- .generic_args()
- .next()
- .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
-
- let mut generics_def = if let Some(path) =
- arg_list.syntax().ancestors().find_map(ast::Path::cast)
- {
- let res = sema.resolve_path(&path)?;
- let generic_def: hir::GenericDef = match res {
- hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
- hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
- hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
- hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
- hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
- hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
- | hir::PathResolution::Def(hir::ModuleDef::Const(_))
- | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
- | hir::PathResolution::Def(hir::ModuleDef::Module(_))
- | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
- hir::PathResolution::BuiltinAttr(_)
- | hir::PathResolution::ToolModule(_)
- | hir::PathResolution::Local(_)
- | hir::PathResolution::TypeParam(_)
- | hir::PathResolution::ConstParam(_)
- | hir::PathResolution::SelfType(_)
- | hir::PathResolution::DeriveHelper(_) => return None,
- };
-
- generic_def
- } else if let Some(method_call) = arg_list.syntax().parent().and_then(ast::MethodCallExpr::cast)
- {
- // recv.method::<$0>()
- let method = sema.resolve_method_call(&method_call)?;
- method.into()
- } else {
- return None;
- };
-
+ let (mut generics_def, mut active_parameter, first_arg_is_non_lifetime) =
+ generic_def_for_node(sema, &arg_list, &token)?;
let mut res = SignatureHelp {
doc: None,
signature: String::new(),
@@ -307,9 +261,9 @@ fn signature_help_for_generics(
// eg. `None::<u8>`
// We'll use the signature of the enum, but include the docs of the variant.
res.doc = it.docs(db).map(|it| it.into());
- let it = it.parent_enum(db);
- format_to!(res.signature, "enum {}", it.name(db));
- generics_def = it.into();
+ let enum_ = it.parent_enum(db);
+ format_to!(res.signature, "enum {}", enum_.name(db));
+ generics_def = enum_.into();
}
// These don't have generic args that can be specified
hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
@@ -388,16 +342,13 @@ fn signature_help_for_record_lit(
record: ast::RecordExpr,
token: SyntaxToken,
) -> Option<SignatureHelp> {
- let arg_list = record
- .syntax()
- .ancestors()
- .filter_map(ast::RecordExpr::cast)
- .find(|list| list.syntax().text_range().contains(token.text_range().start()))?;
-
- let active_parameter = arg_list
+ let active_parameter = record
.record_expr_field_list()?
- .fields()
- .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
+ .syntax()
+ .children_with_tokens()
+ .filter_map(syntax::NodeOrToken::into_token)
+ .filter(|t| t.kind() == syntax::T![,])
+ .take_while(|t| t.text_range().start() <= token.text_range().start())
.count();
let mut res = SignatureHelp {
@@ -1594,4 +1545,27 @@ impl S {
"#]],
);
}
+
+ #[test]
+ fn test_enum_in_nested_method_in_lambda() {
+ check(
+ r#"
+enum A {
+ A,
+ B
+}
+
+fn bar(_: A) { }
+
+fn main() {
+ let foo = Foo;
+ std::thread::spawn(move || { bar(A:$0) } );
+}
+"#,
+ expect![[r#"
+ fn bar(_: A)
+ ^^^^
+ "#]],
+ );
+ }
}
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 2f870d769c..fc9b5d3ba4 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
.count()
};
- assert_eq!(hash, 1609);
+ assert_eq!(hash, 1608);
}
diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml
index 08359133f1..6e962abd75 100644
--- a/crates/parser/Cargo.toml
+++ b/crates/parser/Cargo.toml
@@ -20,4 +20,5 @@ limit.workspace = true
[dev-dependencies]
expect-test = "1.4.0"
+stdx.workspace = true
sourcegen.workspace = true
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 485b612f08..15ec9e167e 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -200,6 +200,8 @@ impl BlockLike {
}
}
+const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]);
+
fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
match p.current() {
T![pub] => {
@@ -340,3 +342,31 @@ fn error_block(p: &mut Parser<'_>, message: &str) {
p.eat(T!['}']);
m.complete(p, ERROR);
}
+
+/// The `parser` passed this is required to at least consume one token if it returns `true`.
+/// If the `parser` returns false, parsing will stop.
+fn delimited(
+ p: &mut Parser<'_>,
+ bra: SyntaxKind,
+ ket: SyntaxKind,
+ delim: SyntaxKind,
+ first_set: TokenSet,
+ mut parser: impl FnMut(&mut Parser<'_>) -> bool,
+) {
+ p.bump(bra);
+ while !p.at(ket) && !p.at(EOF) {
+ if !parser(p) {
+ break;
+ }
+ if !p.at(delim) {
+ if p.at_ts(first_set) {
+ p.error(format!("expected {:?}", delim));
+ } else {
+ break;
+ }
+ } else {
+ p.bump(delim);
+ }
+ }
+ p.expect(ket);
+}
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 0cf6a16f86..4ecaa6e6a8 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -1,5 +1,7 @@
use super::*;
+pub(super) const ATTRIBUTE_FIRST: TokenSet = TokenSet::new(&[T![#]]);
+
pub(super) fn inner_attrs(p: &mut Parser<'_>) {
while p.at(T![#]) && p.nth(1) == T![!] {
attr(p, true);
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 7516ac3c4b..4b080102a2 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -1,5 +1,7 @@
mod atom;
+use crate::grammar::attributes::ATTRIBUTE_FIRST;
+
use super::*;
pub(crate) use self::atom::{block_expr, match_arm_list};
@@ -68,6 +70,12 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
Err(m) => m,
};
+ if !p.at_ts(EXPR_FIRST) {
+ p.err_and_bump("expected expression, item or let statement");
+ m.abandon(p);
+ return;
+ }
+
if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) {
if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) {
// test no_semi_after_block
@@ -227,6 +235,12 @@ fn expr_bp(
attributes::outer_attrs(p);
m
});
+
+ if !p.at_ts(EXPR_FIRST) {
+ p.err_recover("expected expression", atom::EXPR_RECOVERY_SET);
+ m.abandon(p);
+ return None;
+ }
let mut lhs = match lhs(p, r) {
Some((lhs, blocklike)) => {
let lhs = lhs.extend_to(p, m);
@@ -551,23 +565,20 @@ fn cast_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
m.complete(p, CAST_EXPR)
}
+// test_err arg_list_recovery
+// fn main() {
+// foo(bar::);
+// foo(bar:);
+// foo(bar+);
+// }
fn arg_list(p: &mut Parser<'_>) {
assert!(p.at(T!['(']));
let m = p.start();
- p.bump(T!['(']);
- while !p.at(T![')']) && !p.at(EOF) {
- // test arg_with_attr
- // fn main() {
- // foo(#[attr] 92)
- // }
- if !expr(p) {
- break;
- }
- if !p.at(T![')']) && !p.expect(T![,]) {
- break;
- }
- }
- p.eat(T![')']);
+ // test arg_with_attr
+ // fn main() {
+ // foo(#[attr] 92)
+ // }
+ delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr);
m.complete(p, ARG_LIST);
}
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index a23f900b73..efc2603835 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -40,26 +40,28 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
T!['{'],
T!['['],
T![|],
- T![move],
+ T![async],
T![box],
+ T![break],
+ T![const],
+ T![continue],
+ T![do],
+ T![for],
T![if],
- T![while],
+ T![let],
+ T![loop],
T![match],
- T![unsafe],
+ T![move],
T![return],
- T![yield],
- T![do],
- T![break],
- T![continue],
- T![async],
+ T![static],
T![try],
- T![const],
- T![loop],
- T![for],
+ T![unsafe],
+ T![while],
+ T![yield],
LIFETIME_IDENT,
]));
-const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![let]]);
+pub(super) const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![')'], T![']']]);
pub(super) fn atom_expr(
p: &mut Parser<'_>,
@@ -116,7 +118,7 @@ pub(super) fn atom_expr(
// fn main() {
// 'loop: impl
// }
- p.error("expected a loop");
+ p.error("expected a loop or block");
m.complete(p, ERROR);
return None;
}
@@ -157,7 +159,7 @@ pub(super) fn atom_expr(
T![for] => for_expr(p, None),
_ => {
- p.err_recover("expected expression", EXPR_RECOVERY_SET);
+ p.err_and_bump("expected expression");
return None;
}
};
diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs
index c438943a00..919d9b91eb 100644
--- a/crates/parser/src/grammar/generic_args.rs
+++ b/crates/parser/src/grammar/generic_args.rs
@@ -5,27 +5,35 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: boo
if p.at(T![::]) && p.nth(2) == T![<] {
m = p.start();
p.bump(T![::]);
- p.bump(T![<]);
} else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
m = p.start();
- p.bump(T![<]);
} else {
return;
}
- while !p.at(EOF) && !p.at(T![>]) {
- generic_arg(p);
- if !p.at(T![>]) && !p.expect(T![,]) {
- break;
- }
- }
- p.expect(T![>]);
+ delimited(p, T![<], T![>], T![,], GENERIC_ARG_FIRST, generic_arg);
m.complete(p, GENERIC_ARG_LIST);
}
+const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
+ LIFETIME_IDENT,
+ IDENT,
+ T!['{'],
+ T![true],
+ T![false],
+ T![-],
+ INT_NUMBER,
+ FLOAT_NUMBER,
+ CHAR,
+ BYTE,
+ STRING,
+ BYTE_STRING,
+])
+.union(types::TYPE_FIRST);
+
// test generic_arg
// type T = S<i32>;
-fn generic_arg(p: &mut Parser<'_>) {
+fn generic_arg(p: &mut Parser<'_>) -> bool {
match p.current() {
LIFETIME_IDENT => lifetime_arg(p),
T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
@@ -68,8 +76,10 @@ fn generic_arg(p: &mut Parser<'_>) {
}
}
}
- _ => type_arg(p),
+ _ if p.at_ts(types::TYPE_FIRST) => type_arg(p),
+ _ => return false,
}
+ true
}
// test lifetime_arg
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 6db28ef132..7fcf938bab 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -1,3 +1,5 @@
+use crate::grammar::attributes::ATTRIBUTE_FIRST;
+
use super::*;
pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) {
@@ -11,32 +13,31 @@ pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) {
fn generic_param_list(p: &mut Parser<'_>) {
assert!(p.at(T![<]));
let m = p.start();
- p.bump(T![<]);
+ delimited(p, T![<], T![>], T![,], GENERIC_PARAM_FIRST.union(ATTRIBUTE_FIRST), |p| {
+ // test generic_param_attribute
+ // fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
+ let m = p.start();
+ attributes::outer_attrs(p);
+ generic_param(p, m)
+ });
- while !p.at(EOF) && !p.at(T![>]) {
- generic_param(p);
- if !p.at(T![>]) && !p.expect(T![,]) {
- break;
- }
- }
- p.expect(T![>]);
m.complete(p, GENERIC_PARAM_LIST);
}
-fn generic_param(p: &mut Parser<'_>) {
- let m = p.start();
- // test generic_param_attribute
- // fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
- attributes::outer_attrs(p);
+const GENERIC_PARAM_FIRST: TokenSet = TokenSet::new(&[IDENT, LIFETIME_IDENT, T![const]]);
+
+fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
match p.current() {
LIFETIME_IDENT => lifetime_param(p, m),
IDENT => type_param(p, m),
T![const] => const_param(p, m),
_ => {
m.abandon(p);
- p.err_and_bump("expected type parameter");
+ p.err_and_bump("expected generic parameter");
+ return false;
}
}
+ true
}
// test lifetime_param
diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs
index e7d30516b9..17f41b8e13 100644
--- a/crates/parser/src/grammar/items/adt.rs
+++ b/crates/parser/src/grammar/items/adt.rs
@@ -1,3 +1,5 @@
+use crate::grammar::attributes::ATTRIBUTE_FIRST;
+
use super::*;
// test struct_item
@@ -141,28 +143,31 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
}
}
+const TUPLE_FIELD_FIRST: TokenSet =
+ types::TYPE_FIRST.union(ATTRIBUTE_FIRST).union(VISIBILITY_FIRST);
+
fn tuple_field_list(p: &mut Parser<'_>) {
assert!(p.at(T!['(']));
let m = p.start();
- p.bump(T!['(']);
- while !p.at(T![')']) && !p.at(EOF) {
+ delimited(p, T!['('], T![')'], T![,], TUPLE_FIELD_FIRST, |p| {
let m = p.start();
// test tuple_field_attrs
// struct S (#[attr] f32);
attributes::outer_attrs(p);
- opt_visibility(p, true);
+ let has_vis = opt_visibility(p, true);
if !p.at_ts(types::TYPE_FIRST) {
p.error("expected a type");
- m.complete(p, ERROR);
- break;
+ if has_vis {
+ m.complete(p, ERROR);
+ } else {
+ m.abandon(p);
+ }
+ return false;
}
types::type_(p);
m.complete(p, TUPLE_FIELD);
+ true
+ });
- if !p.at(T![')']) {
- p.expect(T![,]);
- }
- }
- p.expect(T![')']);
m.complete(p, TUPLE_FIELD_LIST);
}
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index 20e8e95f06..74eae9151a 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -1,3 +1,5 @@
+use crate::grammar::attributes::ATTRIBUTE_FIRST;
+
use super::*;
// test param_list
@@ -66,14 +68,20 @@ fn list_(p: &mut Parser<'_>, flavor: Flavor) {
}
};
- if !p.at_ts(PARAM_FIRST) {
+ if !p.at_ts(PARAM_FIRST.union(ATTRIBUTE_FIRST)) {
p.error("expected value parameter");
m.abandon(p);
break;
}
param(p, m, flavor);
- if !p.at(ket) {
- p.expect(T![,]);
+ if !p.at(T![,]) {
+ if p.at_ts(PARAM_FIRST.union(ATTRIBUTE_FIRST)) {
+ p.error("expected `,`");
+ } else {
+ break;
+ }
+ } else {
+ p.bump(T![,]);
}
}
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index af3b6f63cf..1064ae9970 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -67,6 +67,10 @@ fn path_for_qualifier(
}
}
+const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
+ items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
+const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
+
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
let m = p.start();
// test qual_paths
@@ -102,7 +106,12 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
m.complete(p, NAME_REF);
}
_ => {
- p.err_recover("expected identifier", items::ITEM_RECOVERY_SET);
+ let recover_set = match mode {
+ Mode::Use => items::ITEM_RECOVERY_SET,
+ Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
+ Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
+ };
+ p.err_recover("expected identifier", recover_set);
if empty {
// test_err empty_segment
// use crate::;
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 5c6e18fee8..7d0b156c5a 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -17,8 +17,9 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
T![Self],
]));
-const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
+pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
T![')'],
+ T![>],
T![,],
// test_err struct_field_recover
// struct S { f pub g: () }
diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs
index c1b4e9a7d8..2fec765bd7 100644
--- a/crates/parser/src/tests.rs
+++ b/crates/parser/src/tests.rs
@@ -15,6 +15,7 @@ use crate::{LexedStr, TopEntryPoint};
#[test]
fn lex_ok() {
for case in TestCase::list("lexer/ok") {
+ let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
let actual = lex(&case.text);
expect_file![case.rast].assert_eq(&actual)
}
@@ -23,6 +24,7 @@ fn lex_ok() {
#[test]
fn lex_err() {
for case in TestCase::list("lexer/err") {
+ let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
let actual = lex(&case.text);
expect_file![case.rast].assert_eq(&actual)
}
@@ -46,6 +48,7 @@ fn lex(text: &str) -> String {
#[test]
fn parse_ok() {
for case in TestCase::list("parser/ok") {
+ let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
expect_file![case.rast].assert_eq(&actual);
@@ -55,6 +58,7 @@ fn parse_ok() {
#[test]
fn parse_inline_ok() {
for case in TestCase::list("parser/inline/ok") {
+ let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
expect_file![case.rast].assert_eq(&actual);
@@ -64,6 +68,7 @@ fn parse_inline_ok() {
#[test]
fn parse_err() {
for case in TestCase::list("parser/err") {
+ let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
expect_file![case.rast].assert_eq(&actual)
@@ -73,6 +78,7 @@ fn parse_err() {
#[test]
fn parse_inline_err() {
for case in TestCase::list("parser/inline/err") {
+ let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
expect_file![case.rast].assert_eq(&actual)
diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs
index eb640dc7fc..49dd9e293b 100644
--- a/crates/parser/src/tests/top_entries.rs
+++ b/crates/parser/src/tests/top_entries.rs
@@ -65,7 +65,7 @@ fn macro_stmt() {
MACRO_STMTS
ERROR
SHEBANG "#!/usr/bin/rust"
- error 0: expected expression
+ error 0: expected expression, item or let statement
"##]],
);
check(
diff --git a/crates/parser/test_data/parser/err/0009_broken_struct_type_parameter.rast b/crates/parser/test_data/parser/err/0009_broken_struct_type_parameter.rast
index a015432171..cdc01863ab 100644
--- a/crates/parser/test_data/parser/err/0009_broken_struct_type_parameter.rast
+++ b/crates/parser/test_data/parser/err/0009_broken_struct_type_parameter.rast
@@ -44,8 +44,7 @@ SOURCE_FILE
IDENT "T"
SEMICOLON ";"
WHITESPACE "\n"
-error 9: expected type parameter
-error 11: expected COMMA
+error 9: expected generic parameter
error 11: expected R_ANGLE
error 11: expected `;`, `{`, or `(`
error 12: expected an item
diff --git a/crates/parser/test_data/parser/err/0013_invalid_type.rast b/crates/parser/test_data/parser/err/0013_invalid_type.rast
index eec84a0c67..b485c71ab3 100644
--- a/crates/parser/test_data/parser/err/0013_invalid_type.rast
+++ b/crates/parser/test_data/parser/err/0013_invalid_type.rast
@@ -43,17 +43,14 @@ SOURCE_FILE
IDENT "Box"
GENERIC_ARG_LIST
L_ANGLE "<"
- TYPE_ARG
- ERROR
- AT "@"
- WHITESPACE " "
- TUPLE_FIELD
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Any"
- ERROR
+ ERROR
+ AT "@"
+ WHITESPACE " "
+ MACRO_CALL
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Any"
ERROR
R_ANGLE ">"
ERROR
@@ -69,17 +66,14 @@ SOURCE_FILE
ERROR
SEMICOLON ";"
WHITESPACE "\n\n"
-error 67: expected type
-error 68: expected COMMA
-error 68: expected R_ANGLE
-error 68: expected COMMA
-error 68: expected R_ANGLE
-error 68: expected COMMA
-error 68: expected R_ANGLE
-error 68: expected COMMA
-error 72: expected COMMA
-error 72: expected a type
-error 72: expected R_PAREN
+error 67: expected R_ANGLE
+error 67: expected R_ANGLE
+error 67: expected R_ANGLE
+error 67: expected R_PAREN
+error 67: expected SEMICOLON
+error 67: expected an item
+error 72: expected BANG
+error 72: expected `{`, `[`, `(`
error 72: expected SEMICOLON
error 72: expected an item
error 73: expected an item
diff --git a/crates/parser/test_data/parser/err/0022_bad_exprs.rast b/crates/parser/test_data/parser/err/0022_bad_exprs.rast
index 900394bd96..d97fc6c720 100644
--- a/crates/parser/test_data/parser/err/0022_bad_exprs.rast
+++ b/crates/parser/test_data/parser/err/0022_bad_exprs.rast
@@ -145,27 +145,29 @@ SOURCE_FILE
error 16: expected expression
error 17: expected R_BRACK
error 17: expected SEMICOLON
-error 17: expected expression
+error 17: expected expression, item or let statement
error 25: expected a name
error 26: expected `;`, `{`, or `(`
error 30: expected pattern
error 31: expected SEMICOLON
error 53: expected expression
+error 54: expected R_PAREN
error 54: expected SEMICOLON
-error 54: expected expression
+error 54: expected expression, item or let statement
error 60: expected type
error 60: expected `{`
-error 60: expected expression
+error 60: expected expression, item or let statement
error 65: expected pattern
error 65: expected SEMICOLON
-error 65: expected expression
+error 65: expected expression, item or let statement
error 92: expected expression
+error 93: expected R_PAREN
error 93: expected SEMICOLON
-error 93: expected expression
-error 95: expected expression
-error 96: expected expression
+error 93: expected expression, item or let statement
+error 95: expected expression, item or let statement
+error 96: expected expression, item or let statement
error 103: expected a name
error 104: expected `{`
error 108: expected pattern
error 108: expected SEMICOLON
-error 108: expected expression
+error 108: expected expression, item or let statement
diff --git a/crates/parser/test_data/parser/err/0024_many_type_parens.rast b/crates/parser/test_data/parser/err/0024_many_type_parens.rast
index d374f86610..f0dbc9b102 100644
--- a/crates/parser/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/parser/test_data/parser/err/0024_many_type_parens.rast
@@ -168,75 +168,21 @@ SOURCE_FILE
L_PAREN "("
ERROR
QUESTION "?"
- EXPR_STMT
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Sized"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Sized"
ERROR
R_PAREN ")"
WHITESPACE " "
ERROR
PLUS "+"
WHITESPACE " "
- TUPLE_EXPR
- L_PAREN "("
- CLOSURE_EXPR
- FOR_KW "for"
- GENERIC_PARAM_LIST
- L_ANGLE "<"
- LIFETIME_PARAM
- LIFETIME
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- WHITESPACE " "
- BIN_EXPR
- BIN_EXPR
- BIN_EXPR
- BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Trait"
- L_ANGLE "<"
- ERROR
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- ERROR
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- PAREN_EXPR
- L_PAREN "("
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Copy"
- R_PAREN ")"
- R_ANGLE ">"
- ERROR
- SEMICOLON ";"
- WHITESPACE "\n "
- LET_EXPR
- LET_KW "let"
- WHITESPACE " "
- WILDCARD_PAT
- UNDERSCORE "_"
- ERROR
- COLON ":"
- WHITESPACE " "
+ EXPR_STMT
BIN_EXPR
BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Box"
- L_ANGLE "<"
TUPLE_EXPR
L_PAREN "("
CLOSURE_EXPR
@@ -250,78 +196,117 @@ SOURCE_FILE
WHITESPACE " "
BIN_EXPR
BIN_EXPR
- BIN_EXPR
- BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Trait"
- L_ANGLE "<"
- ERROR
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- ERROR
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- PAREN_EXPR
- L_PAREN "("
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Copy"
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- PAREN_EXPR
- L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ L_ANGLE "<"
ERROR
- QUESTION "?"
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PAREN_EXPR
+ L_PAREN "("
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
- IDENT "Sized"
+ IDENT "Copy"
R_PAREN ")"
R_ANGLE ">"
ERROR
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ COLON ":"
+ WHITESPACE " "
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Box"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PAREN_TYPE
+ L_PAREN "("
+ FOR_TYPE
+ FOR_KW "for"
+ GENERIC_PARAM_LIST
+ L_ANGLE "<"
+ LIFETIME_PARAM
+ LIFETIME
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ LIFETIME_ARG
+ LIFETIME
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Copy"
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ L_PAREN "("
+ QUESTION "?"
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Sized"
+ R_PAREN ")"
+ ERROR
+ R_ANGLE ">"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
-error 88: expected COMMA
error 88: expected R_ANGLE
error 121: expected SEMICOLON
-error 121: expected expression
+error 121: expected expression, item or let statement
error 140: expected type
error 141: expected R_PAREN
error 141: expected COMMA
-error 141: expected R_ANGLE
-error 141: expected SEMICOLON
+error 146: expected R_ANGLE
error 146: expected SEMICOLON
-error 146: expected expression
-error 148: expected expression
+error 146: expected expression, item or let statement
+error 148: expected expression, item or let statement
error 158: expected `|`
error 158: expected COMMA
error 165: expected expression
error 168: expected expression
error 179: expected expression
-error 180: expected COMMA
-error 190: expected EQ
-error 190: expected expression
-error 191: expected COMMA
-error 204: expected `|`
-error 204: expected COMMA
-error 211: expected expression
-error 214: expected expression
-error 228: expected expression
-error 229: expected R_PAREN
-error 229: expected COMMA
-error 236: expected expression
-error 237: expected COMMA
-error 237: expected expression
-error 237: expected R_PAREN
+error 180: expected SEMICOLON
+error 215: expected R_ANGLE
+error 235: expected SEMICOLON
+error 235: expected expression, item or let statement
diff --git a/crates/parser/test_data/parser/err/0025_nope.rast b/crates/parser/test_data/parser/err/0025_nope.rast
index 6b49724ec9..b6bc008837 100644
--- a/crates/parser/test_data/parser/err/0025_nope.rast
+++ b/crates/parser/test_data/parser/err/0025_nope.rast
@@ -156,8 +156,7 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "i32"
- WHITESPACE " "
- ERROR
+ WHITESPACE " "
ERROR
L_CURLY "{"
R_CURLY "}"
@@ -199,10 +198,8 @@ error 95: expected type
error 95: expected COMMA
error 96: expected field
error 98: expected field declaration
+error 371: expected R_PAREN
error 371: expected COMMA
-error 372: expected a type
-error 372: expected R_PAREN
-error 372: expected COMMA
error 372: expected enum variant
error 374: expected enum variant
error 494: expected pattern
diff --git a/crates/parser/test_data/parser/err/0042_weird_blocks.rast b/crates/parser/test_data/parser/err/0042_weird_blocks.rast
index 9cea337ce9..1cdc6e6e71 100644
--- a/crates/parser/test_data/parser/err/0042_weird_blocks.rast
+++ b/crates/parser/test_data/parser/err/0042_weird_blocks.rast
@@ -72,4 +72,4 @@ SOURCE_FILE
error 24: expected existential, fn, trait or impl
error 41: expected existential, fn, trait or impl
error 56: expected a block
-error 75: expected a loop
+error 75: expected a loop or block
diff --git a/crates/parser/test_data/parser/err/0048_double_fish.rast b/crates/parser/test_data/parser/err/0048_double_fish.rast
index 3a05bfee1e..207a5c24df 100644
--- a/crates/parser/test_data/parser/err/0048_double_fish.rast
+++ b/crates/parser/test_data/parser/err/0048_double_fish.rast
@@ -12,7 +12,7 @@ SOURCE_FILE
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
- EXPR_STMT
+ BIN_EXPR
PATH_EXPR
PATH
PATH_SEGMENT
@@ -41,13 +41,14 @@ SOURCE_FILE
COLON2 "::"
ERROR
L_ANGLE "<"
- BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "nope"
- SHR ">>"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "nope"
+ R_ANGLE ">"
+ R_ANGLE ">"
ERROR
SEMICOLON ";"
WHITESPACE "\n"
@@ -114,8 +115,6 @@ SOURCE_FILE
WHITESPACE "\n"
error 30: expected identifier
error 31: expected COMMA
-error 31: expected R_ANGLE
-error 31: expected SEMICOLON
error 37: expected expression
error 75: expected identifier
error 76: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast b/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast
index 56cea4b156..ea5203fb96 100644
--- a/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast
+++ b/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast
@@ -23,6 +23,6 @@ SOURCE_FILE
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
-error 22: expected a loop
+error 22: expected a loop or block
error 27: expected type
error 27: expected `{`
diff --git a/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast
new file mode 100644
index 0000000000..5d0fe859c2
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast
@@ -0,0 +1,77 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "main"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ ARG_LIST
+ L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ COLON2 "::"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ ARG_LIST
+ L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ ERROR
+ COLON ":"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ ARG_LIST
+ L_PAREN "("
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ PLUS "+"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 25: expected identifier
+error 39: expected COMMA
+error 39: expected expression
+error 55: expected expression
diff --git a/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs
new file mode 100644
index 0000000000..0e7ac9cc30
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs
@@ -0,0 +1,5 @@
+fn main() {
+ foo(bar::);
+ foo(bar:);
+ foo(bar+);
+}
diff --git a/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast b/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast
index e72df374d1..ea50ad35d7 100644
--- a/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast
+++ b/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast
@@ -49,5 +49,5 @@ SOURCE_FILE
R_CURLY "}"
WHITESPACE "\n"
error 6: missing type for function parameter
-error 6: expected COMMA
+error 6: expected `,`
error 16: missing type for function parameter