mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Auto merge of #14128 - Veykril:parser, r=Veykril
internal: Improve parser recovery for delimited lists Closes https://github.com/rust-lang/rust-analyzer/issues/11188, https://github.com/rust-lang/rust-analyzer/issues/10410, https://github.com/rust-lang/rust-analyzer/issues/10173 Should probably be merged after the stable release as this might get the parser stuck if I missed something
This commit is contained in:
commit
44568007d1
33 changed files with 817 additions and 367 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1174,6 +1174,7 @@ dependencies = [
|
||||||
"limit",
|
"limit",
|
||||||
"rustc-ap-rustc_lexer",
|
"rustc-ap-rustc_lexer",
|
||||||
"sourcegen",
|
"sourcegen",
|
||||||
|
"stdx",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -1476,7 +1476,7 @@ macro_rules! m {
|
||||||
/* parse error: expected identifier */
|
/* parse error: expected identifier */
|
||||||
/* parse error: expected SEMICOLON */
|
/* parse error: expected SEMICOLON */
|
||||||
/* parse error: expected SEMICOLON */
|
/* parse error: expected SEMICOLON */
|
||||||
/* parse error: expected expression */
|
/* parse error: expected expression, item or let statement */
|
||||||
fn f() {
|
fn f() {
|
||||||
K::(C("0"));
|
K::(C("0"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -830,8 +830,7 @@ macro_rules! rgb_color {
|
||||||
/* parse error: expected COMMA */
|
/* parse error: expected COMMA */
|
||||||
/* parse error: expected R_ANGLE */
|
/* parse error: expected R_ANGLE */
|
||||||
/* parse error: expected SEMICOLON */
|
/* parse error: expected SEMICOLON */
|
||||||
/* parse error: expected SEMICOLON */
|
/* parse error: expected expression, item or let statement */
|
||||||
/* parse error: expected expression */
|
|
||||||
pub fn new() {
|
pub fn new() {
|
||||||
let _ = 0as u32<<(8+8);
|
let _ = 0as u32<<(8+8);
|
||||||
}
|
}
|
||||||
|
@ -848,21 +847,21 @@ pub fn new() {
|
||||||
// BLOCK_EXPR@10..31
|
// BLOCK_EXPR@10..31
|
||||||
// STMT_LIST@10..31
|
// STMT_LIST@10..31
|
||||||
// L_CURLY@10..11 "{"
|
// L_CURLY@10..11 "{"
|
||||||
// LET_STMT@11..27
|
// LET_STMT@11..28
|
||||||
// LET_KW@11..14 "let"
|
// LET_KW@11..14 "let"
|
||||||
// WILDCARD_PAT@14..15
|
// WILDCARD_PAT@14..15
|
||||||
// UNDERSCORE@14..15 "_"
|
// UNDERSCORE@14..15 "_"
|
||||||
// EQ@15..16 "="
|
// EQ@15..16 "="
|
||||||
// CAST_EXPR@16..27
|
// CAST_EXPR@16..28
|
||||||
// LITERAL@16..17
|
// LITERAL@16..17
|
||||||
// INT_NUMBER@16..17 "0"
|
// INT_NUMBER@16..17 "0"
|
||||||
// AS_KW@17..19 "as"
|
// AS_KW@17..19 "as"
|
||||||
// PATH_TYPE@19..27
|
// PATH_TYPE@19..28
|
||||||
// PATH@19..27
|
// PATH@19..28
|
||||||
// PATH_SEGMENT@19..27
|
// PATH_SEGMENT@19..28
|
||||||
// NAME_REF@19..22
|
// NAME_REF@19..22
|
||||||
// IDENT@19..22 "u32"
|
// IDENT@19..22 "u32"
|
||||||
// GENERIC_ARG_LIST@22..27
|
// GENERIC_ARG_LIST@22..28
|
||||||
// L_ANGLE@22..23 "<"
|
// L_ANGLE@22..23 "<"
|
||||||
// TYPE_ARG@23..27
|
// TYPE_ARG@23..27
|
||||||
// DYN_TRAIT_TYPE@23..27
|
// DYN_TRAIT_TYPE@23..27
|
||||||
|
@ -877,9 +876,9 @@ pub fn new() {
|
||||||
// ERROR@25..26
|
// ERROR@25..26
|
||||||
// INT_NUMBER@25..26 "8"
|
// INT_NUMBER@25..26 "8"
|
||||||
// PLUS@26..27 "+"
|
// PLUS@26..27 "+"
|
||||||
// EXPR_STMT@27..28
|
// CONST_ARG@27..28
|
||||||
// LITERAL@27..28
|
// LITERAL@27..28
|
||||||
// INT_NUMBER@27..28 "8"
|
// INT_NUMBER@27..28 "8"
|
||||||
// ERROR@28..29
|
// ERROR@28..29
|
||||||
// R_PAREN@28..29 ")"
|
// R_PAREN@28..29 ")"
|
||||||
// SEMICOLON@29..30 ";"
|
// SEMICOLON@29..30 ";"
|
||||||
|
|
|
@ -668,8 +668,15 @@ fn classify_name_ref(
|
||||||
};
|
};
|
||||||
let after_if_expr = |node: SyntaxNode| {
|
let after_if_expr = |node: SyntaxNode| {
|
||||||
let prev_expr = (|| {
|
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()?;
|
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(_)))
|
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
|
||||||
};
|
};
|
||||||
|
|
|
@ -745,3 +745,255 @@ fn return_value_no_block() {
|
||||||
r#"fn f() -> i32 { match () { () => return $0 } }"#,
|
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
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -2,10 +2,17 @@
|
||||||
|
|
||||||
use expect_test::{expect, Expect};
|
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_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) {
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
let actual = completion_list_no_kw(ra_fixture);
|
let actual = completion_list(ra_fixture);
|
||||||
expect.assert_eq(&actual)
|
expect.assert_eq(&actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +66,7 @@ fn _alpha() {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_prelude() {
|
fn completes_prelude() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs edition:2018 crate:main deps:std
|
//- /main.rs edition:2018 crate:main deps:std
|
||||||
fn foo() { let x: $0 }
|
fn foo() { let x: $0 }
|
||||||
|
@ -81,7 +88,7 @@ pub mod prelude {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_prelude_macros() {
|
fn completes_prelude_macros() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs edition:2018 crate:main deps:std
|
//- /main.rs edition:2018 crate:main deps:std
|
||||||
fn f() {$0}
|
fn f() {$0}
|
||||||
|
@ -110,7 +117,7 @@ mod macros {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_std_prelude_if_core_is_defined() {
|
fn completes_std_prelude_if_core_is_defined() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs crate:main deps:core,std
|
//- /main.rs crate:main deps:core,std
|
||||||
fn foo() { let x: $0 }
|
fn foo() { let x: $0 }
|
||||||
|
@ -140,7 +147,7 @@ pub mod prelude {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn respects_doc_hidden() {
|
fn respects_doc_hidden() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:lib deps:std
|
//- /lib.rs crate:lib deps:std
|
||||||
fn f() {
|
fn f() {
|
||||||
|
@ -168,7 +175,7 @@ pub mod prelude {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn respects_doc_hidden_in_assoc_item_list() {
|
fn respects_doc_hidden_in_assoc_item_list() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:lib deps:std
|
//- /lib.rs crate:lib deps:std
|
||||||
struct S;
|
struct S;
|
||||||
|
@ -195,7 +202,7 @@ pub mod prelude {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn associated_item_visibility() {
|
fn associated_item_visibility() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:lib new_source_root:library
|
//- /lib.rs crate:lib new_source_root:library
|
||||||
pub struct S;
|
pub struct S;
|
||||||
|
@ -222,7 +229,7 @@ fn foo() { let _ = lib::S::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_union_associated_method() {
|
fn completes_union_associated_method() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
union U {};
|
union U {};
|
||||||
impl U { fn m() { } }
|
impl U { fn m() { } }
|
||||||
|
@ -237,7 +244,7 @@ fn foo() { let _ = U::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_trait_associated_method_1() {
|
fn completes_trait_associated_method_1() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Trait { fn m(); }
|
trait Trait { fn m(); }
|
||||||
|
|
||||||
|
@ -251,7 +258,7 @@ fn foo() { let _ = Trait::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_trait_associated_method_2() {
|
fn completes_trait_associated_method_2() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Trait { fn m(); }
|
trait Trait { fn m(); }
|
||||||
|
|
||||||
|
@ -268,7 +275,7 @@ fn foo() { let _ = S::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_trait_associated_method_3() {
|
fn completes_trait_associated_method_3() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Trait { fn m(); }
|
trait Trait { fn m(); }
|
||||||
|
|
||||||
|
@ -285,7 +292,7 @@ fn foo() { let _ = <S as Trait>::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_ty_param_assoc_ty() {
|
fn completes_ty_param_assoc_ty() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Super {
|
trait Super {
|
||||||
type Ty;
|
type Ty;
|
||||||
|
@ -318,7 +325,7 @@ fn foo<T: Sub>() { T::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_self_param_assoc_ty() {
|
fn completes_self_param_assoc_ty() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Super {
|
trait Super {
|
||||||
type Ty;
|
type Ty;
|
||||||
|
@ -358,7 +365,7 @@ impl<T> Sub for Wrap<T> {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_type_alias() {
|
fn completes_type_alias() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
struct S;
|
struct S;
|
||||||
impl S { fn foo() {} }
|
impl S { fn foo() {} }
|
||||||
|
@ -376,7 +383,7 @@ fn main() { T::$0; }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_qualified_macros() {
|
fn completes_qualified_macros() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! foo { () => {} }
|
macro_rules! foo { () => {} }
|
||||||
|
@ -392,7 +399,7 @@ fn main() { let _ = crate::$0 }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn does_not_complete_non_fn_macros() {
|
fn does_not_complete_non_fn_macros() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
mod m {
|
mod m {
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
|
@ -403,7 +410,7 @@ fn f() {m::$0}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#""#]],
|
expect![[r#""#]],
|
||||||
);
|
);
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
mod m {
|
mod m {
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
|
@ -418,7 +425,7 @@ fn f() {m::$0}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_reexported_items_under_correct_name() {
|
fn completes_reexported_items_under_correct_name() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
fn foo() { self::m::$0 }
|
fn foo() { self::m::$0 }
|
||||||
|
|
||||||
|
@ -475,7 +482,7 @@ mod p {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_in_simple_macro_call() {
|
fn completes_in_simple_macro_call() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! m { ($e:expr) => { $e } }
|
macro_rules! m { ($e:expr) => { $e } }
|
||||||
fn main() { m!(self::f$0); }
|
fn main() { m!(self::f$0); }
|
||||||
|
@ -490,7 +497,7 @@ fn foo() {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_mod_share_name() {
|
fn function_mod_share_name() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
fn foo() { self::m::$0 }
|
fn foo() { self::m::$0 }
|
||||||
|
|
||||||
|
@ -508,7 +515,7 @@ mod m {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_hashmap_new() {
|
fn completes_hashmap_new() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
struct RandomState;
|
struct RandomState;
|
||||||
struct HashMap<K, V, S = RandomState> {}
|
struct HashMap<K, V, S = RandomState> {}
|
||||||
|
@ -529,7 +536,7 @@ fn foo() {
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_variant_through_self() {
|
fn completes_variant_through_self() {
|
||||||
cov_mark::check!(completes_variant_through_self);
|
cov_mark::check!(completes_variant_through_self);
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
enum Foo {
|
enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
|
@ -552,7 +559,7 @@ impl Foo {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_non_exhaustive_variant_within_the_defining_crate() {
|
fn completes_non_exhaustive_variant_within_the_defining_crate() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
enum Foo {
|
enum Foo {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@ -570,7 +577,7 @@ fn foo(self) {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs crate:main deps:e
|
//- /main.rs crate:main deps:e
|
||||||
fn foo(self) {
|
fn foo(self) {
|
||||||
|
@ -593,7 +600,7 @@ enum Foo {
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_primitive_assoc_const() {
|
fn completes_primitive_assoc_const() {
|
||||||
cov_mark::check!(completes_primitive_assoc_const);
|
cov_mark::check!(completes_primitive_assoc_const);
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:lib deps:core
|
//- /lib.rs crate:lib deps:core
|
||||||
fn f() {
|
fn f() {
|
||||||
|
@ -618,7 +625,7 @@ impl u8 {
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_variant_through_alias() {
|
fn completes_variant_through_alias() {
|
||||||
cov_mark::check!(completes_variant_through_alias);
|
cov_mark::check!(completes_variant_through_alias);
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
enum Foo {
|
enum Foo {
|
||||||
Bar
|
Bar
|
||||||
|
@ -636,7 +643,7 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn respects_doc_hidden2() {
|
fn respects_doc_hidden2() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:lib deps:dep
|
//- /lib.rs crate:lib deps:dep
|
||||||
fn f() {
|
fn f() {
|
||||||
|
@ -665,7 +672,7 @@ pub mod m {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_anchor_empty() {
|
fn type_anchor_empty() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Foo {
|
trait Foo {
|
||||||
fn foo() -> Self;
|
fn foo() -> Self;
|
||||||
|
@ -688,7 +695,7 @@ fn bar() -> Bar {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_anchor_type() {
|
fn type_anchor_type() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Foo {
|
trait Foo {
|
||||||
fn foo() -> Self;
|
fn foo() -> Self;
|
||||||
|
@ -715,7 +722,7 @@ fn bar() -> Bar {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_anchor_type_trait() {
|
fn type_anchor_type_trait() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
trait Foo {
|
trait Foo {
|
||||||
fn foo() -> Self;
|
fn foo() -> Self;
|
||||||
|
@ -741,7 +748,7 @@ fn bar() -> Bar {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_fn_in_pub_trait_generated_by_macro() {
|
fn completes_fn_in_pub_trait_generated_by_macro() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
mod other_mod {
|
mod other_mod {
|
||||||
macro_rules! make_method {
|
macro_rules! make_method {
|
||||||
|
@ -775,7 +782,7 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_fn_in_pub_trait_generated_by_recursive_macro() {
|
fn completes_fn_in_pub_trait_generated_by_recursive_macro() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
mod other_mod {
|
mod other_mod {
|
||||||
macro_rules! make_method {
|
macro_rules! make_method {
|
||||||
|
@ -815,7 +822,7 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_const_in_pub_trait_generated_by_macro() {
|
fn completes_const_in_pub_trait_generated_by_macro() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
mod other_mod {
|
mod other_mod {
|
||||||
macro_rules! make_const {
|
macro_rules! make_const {
|
||||||
|
@ -847,7 +854,7 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_locals_from_macros() {
|
fn completes_locals_from_macros() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
|
|
||||||
macro_rules! x {
|
macro_rules! x {
|
||||||
|
@ -875,7 +882,7 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn regression_12644() {
|
fn regression_12644() {
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! __rust_force_expr {
|
macro_rules! __rust_force_expr {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
|
@ -974,7 +981,7 @@ fn foo { crate:::$0 }
|
||||||
"#,
|
"#,
|
||||||
expect![""],
|
expect![""],
|
||||||
);
|
);
|
||||||
check(
|
check_no_kw(
|
||||||
r#"
|
r#"
|
||||||
fn foo { crate::::$0 }
|
fn foo { crate::::$0 }
|
||||||
"#,
|
"#,
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{Semantics, Type};
|
use hir::{Semantics, Type};
|
||||||
|
use parser::T;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasArgList, HasName},
|
ast::{self, HasArgList, HasName},
|
||||||
AstNode, SyntaxToken,
|
match_ast, AstNode, NodeOrToken, SyntaxToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::RootDatabase;
|
use crate::RootDatabase;
|
||||||
|
@ -58,7 +59,7 @@ pub fn callable_for_node(
|
||||||
calling_node: &ast::CallableExpr,
|
calling_node: &ast::CallableExpr,
|
||||||
token: &SyntaxToken,
|
token: &SyntaxToken,
|
||||||
) -> Option<(hir::Callable, Option<usize>)> {
|
) -> Option<(hir::Callable, Option<usize>)> {
|
||||||
let callable = match &calling_node {
|
let callable = match calling_node {
|
||||||
ast::CallableExpr::Call(call) => {
|
ast::CallableExpr::Call(call) => {
|
||||||
let expr = call.expr()?;
|
let expr = call.expr()?;
|
||||||
sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
|
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),
|
ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
|
||||||
}?;
|
}?;
|
||||||
let active_param = if let Some(arg_list) = calling_node.arg_list() {
|
let active_param = if let Some(arg_list) = calling_node.arg_list() {
|
||||||
let param = arg_list
|
Some(
|
||||||
.args()
|
arg_list
|
||||||
.take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
|
.syntax()
|
||||||
.count();
|
.children_with_tokens()
|
||||||
Some(param)
|
.filter_map(NodeOrToken::into_token)
|
||||||
|
.filter(|t| t.kind() == T![,])
|
||||||
|
.take_while(|t| t.text_range().start() <= token.text_range().start())
|
||||||
|
.count(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Some((callable, active_param))
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -7,12 +7,16 @@ use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
AssocItem, GenericParam, HasAttrs, HirDisplay, ModuleDef, PathResolution, Semantics, Trait,
|
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 stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo,
|
algo,
|
||||||
ast::{self, HasArgList},
|
ast::{self, HasArgList},
|
||||||
match_ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, TextSize,
|
match_ast, AstNode, Direction, SyntaxToken, TextRange, TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::RootDatabase;
|
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
|
// Stop at multi-line expressions, since the signature of the outer call is not very
|
||||||
// helpful inside them.
|
// helpful inside them.
|
||||||
if let Some(expr) = ast::Expr::cast(node.clone()) {
|
if let Some(expr) = ast::Expr::cast(node.clone()) {
|
||||||
if expr.syntax().text().contains_char('\n')
|
if !matches!(expr, ast::Expr::RecordExpr(..))
|
||||||
&& expr.syntax().kind() != SyntaxKind::RECORD_EXPR
|
&& expr.syntax().text().contains_char('\n')
|
||||||
{
|
{
|
||||||
return None;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,18 +126,16 @@ fn signature_help_for_call(
|
||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
) -> Option<SignatureHelp> {
|
) -> Option<SignatureHelp> {
|
||||||
// Find the calling expression and its NameRef
|
// 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 {
|
let calling_node = loop {
|
||||||
if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
|
if let Some(callable) = ast::CallableExpr::cast(nodes.next()?) {
|
||||||
if callable
|
let inside_callable = callable
|
||||||
.arg_list()
|
.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;
|
break callable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.parent()?;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?;
|
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(
|
fn signature_help_for_generics(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
garg_list: ast::GenericArgList,
|
arg_list: ast::GenericArgList,
|
||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
) -> Option<SignatureHelp> {
|
) -> Option<SignatureHelp> {
|
||||||
let arg_list = garg_list
|
let (mut generics_def, mut active_parameter, first_arg_is_non_lifetime) =
|
||||||
.syntax()
|
generic_def_for_node(sema, &arg_list, &token)?;
|
||||||
.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 res = SignatureHelp {
|
let mut res = SignatureHelp {
|
||||||
doc: None,
|
doc: None,
|
||||||
signature: String::new(),
|
signature: String::new(),
|
||||||
|
@ -307,9 +261,9 @@ fn signature_help_for_generics(
|
||||||
// eg. `None::<u8>`
|
// eg. `None::<u8>`
|
||||||
// We'll use the signature of the enum, but include the docs of the variant.
|
// We'll use the signature of the enum, but include the docs of the variant.
|
||||||
res.doc = it.docs(db).map(|it| it.into());
|
res.doc = it.docs(db).map(|it| it.into());
|
||||||
let it = it.parent_enum(db);
|
let enum_ = it.parent_enum(db);
|
||||||
format_to!(res.signature, "enum {}", it.name(db));
|
format_to!(res.signature, "enum {}", enum_.name(db));
|
||||||
generics_def = it.into();
|
generics_def = enum_.into();
|
||||||
}
|
}
|
||||||
// These don't have generic args that can be specified
|
// These don't have generic args that can be specified
|
||||||
hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
|
hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
|
||||||
|
@ -388,16 +342,13 @@ fn signature_help_for_record_lit(
|
||||||
record: ast::RecordExpr,
|
record: ast::RecordExpr,
|
||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
) -> Option<SignatureHelp> {
|
) -> Option<SignatureHelp> {
|
||||||
let arg_list = record
|
let active_parameter = record
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.filter_map(ast::RecordExpr::cast)
|
|
||||||
.find(|list| list.syntax().text_range().contains(token.text_range().start()))?;
|
|
||||||
|
|
||||||
let active_parameter = arg_list
|
|
||||||
.record_expr_field_list()?
|
.record_expr_field_list()?
|
||||||
.fields()
|
.syntax()
|
||||||
.take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
|
.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();
|
.count();
|
||||||
|
|
||||||
let mut res = SignatureHelp {
|
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)
|
||||||
|
^^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
|
||||||
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
|
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
|
||||||
.count()
|
.count()
|
||||||
};
|
};
|
||||||
assert_eq!(hash, 1609);
|
assert_eq!(hash, 1608);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,4 +20,5 @@ limit.workspace = true
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
expect-test = "1.4.0"
|
expect-test = "1.4.0"
|
||||||
|
|
||||||
|
stdx.workspace = true
|
||||||
sourcegen.workspace = true
|
sourcegen.workspace = true
|
||||||
|
|
|
@ -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 {
|
fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
|
||||||
match p.current() {
|
match p.current() {
|
||||||
T![pub] => {
|
T![pub] => {
|
||||||
|
@ -340,3 +342,31 @@ fn error_block(p: &mut Parser<'_>, message: &str) {
|
||||||
p.eat(T!['}']);
|
p.eat(T!['}']);
|
||||||
m.complete(p, ERROR);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
pub(super) const ATTRIBUTE_FIRST: TokenSet = TokenSet::new(&[T![#]]);
|
||||||
|
|
||||||
pub(super) fn inner_attrs(p: &mut Parser<'_>) {
|
pub(super) fn inner_attrs(p: &mut Parser<'_>) {
|
||||||
while p.at(T![#]) && p.nth(1) == T![!] {
|
while p.at(T![#]) && p.nth(1) == T![!] {
|
||||||
attr(p, true);
|
attr(p, true);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod atom;
|
mod atom;
|
||||||
|
|
||||||
|
use crate::grammar::attributes::ATTRIBUTE_FIRST;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(crate) use self::atom::{block_expr, match_arm_list};
|
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,
|
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 let Some((cm, blocklike)) = expr_stmt(p, Some(m)) {
|
||||||
if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) {
|
if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) {
|
||||||
// test no_semi_after_block
|
// test no_semi_after_block
|
||||||
|
@ -227,6 +235,12 @@ fn expr_bp(
|
||||||
attributes::outer_attrs(p);
|
attributes::outer_attrs(p);
|
||||||
m
|
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) {
|
let mut lhs = match lhs(p, r) {
|
||||||
Some((lhs, blocklike)) => {
|
Some((lhs, blocklike)) => {
|
||||||
let lhs = lhs.extend_to(p, m);
|
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)
|
m.complete(p, CAST_EXPR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test_err arg_list_recovery
|
||||||
|
// fn main() {
|
||||||
|
// foo(bar::);
|
||||||
|
// foo(bar:);
|
||||||
|
// foo(bar+);
|
||||||
|
// }
|
||||||
fn arg_list(p: &mut Parser<'_>) {
|
fn arg_list(p: &mut Parser<'_>) {
|
||||||
assert!(p.at(T!['(']));
|
assert!(p.at(T!['(']));
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
p.bump(T!['(']);
|
// test arg_with_attr
|
||||||
while !p.at(T![')']) && !p.at(EOF) {
|
// fn main() {
|
||||||
// test arg_with_attr
|
// foo(#[attr] 92)
|
||||||
// fn main() {
|
// }
|
||||||
// foo(#[attr] 92)
|
delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr);
|
||||||
// }
|
|
||||||
if !expr(p) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if !p.at(T![')']) && !p.expect(T![,]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.eat(T![')']);
|
|
||||||
m.complete(p, ARG_LIST);
|
m.complete(p, ARG_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,26 +40,28 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
|
||||||
T!['{'],
|
T!['{'],
|
||||||
T!['['],
|
T!['['],
|
||||||
T![|],
|
T![|],
|
||||||
T![move],
|
|
||||||
T![box],
|
|
||||||
T![if],
|
|
||||||
T![while],
|
|
||||||
T![match],
|
|
||||||
T![unsafe],
|
|
||||||
T![return],
|
|
||||||
T![yield],
|
|
||||||
T![do],
|
|
||||||
T![break],
|
|
||||||
T![continue],
|
|
||||||
T![async],
|
T![async],
|
||||||
T![try],
|
T![box],
|
||||||
|
T![break],
|
||||||
T![const],
|
T![const],
|
||||||
T![loop],
|
T![continue],
|
||||||
|
T![do],
|
||||||
T![for],
|
T![for],
|
||||||
|
T![if],
|
||||||
|
T![let],
|
||||||
|
T![loop],
|
||||||
|
T![match],
|
||||||
|
T![move],
|
||||||
|
T![return],
|
||||||
|
T![static],
|
||||||
|
T![try],
|
||||||
|
T![unsafe],
|
||||||
|
T![while],
|
||||||
|
T![yield],
|
||||||
LIFETIME_IDENT,
|
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(
|
pub(super) fn atom_expr(
|
||||||
p: &mut Parser<'_>,
|
p: &mut Parser<'_>,
|
||||||
|
@ -116,7 +118,7 @@ pub(super) fn atom_expr(
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// 'loop: impl
|
// 'loop: impl
|
||||||
// }
|
// }
|
||||||
p.error("expected a loop");
|
p.error("expected a loop or block");
|
||||||
m.complete(p, ERROR);
|
m.complete(p, ERROR);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +159,7 @@ pub(super) fn atom_expr(
|
||||||
T![for] => for_expr(p, None),
|
T![for] => for_expr(p, None),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
p.err_recover("expected expression", EXPR_RECOVERY_SET);
|
p.err_and_bump("expected expression");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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![<] {
|
if p.at(T![::]) && p.nth(2) == T![<] {
|
||||||
m = p.start();
|
m = p.start();
|
||||||
p.bump(T![::]);
|
p.bump(T![::]);
|
||||||
p.bump(T![<]);
|
|
||||||
} else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
|
} else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
|
||||||
m = p.start();
|
m = p.start();
|
||||||
p.bump(T![<]);
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while !p.at(EOF) && !p.at(T![>]) {
|
delimited(p, T![<], T![>], T![,], GENERIC_ARG_FIRST, generic_arg);
|
||||||
generic_arg(p);
|
|
||||||
if !p.at(T![>]) && !p.expect(T![,]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.expect(T![>]);
|
|
||||||
m.complete(p, GENERIC_ARG_LIST);
|
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
|
// test generic_arg
|
||||||
// type T = S<i32>;
|
// type T = S<i32>;
|
||||||
fn generic_arg(p: &mut Parser<'_>) {
|
fn generic_arg(p: &mut Parser<'_>) -> bool {
|
||||||
match p.current() {
|
match p.current() {
|
||||||
LIFETIME_IDENT => lifetime_arg(p),
|
LIFETIME_IDENT => lifetime_arg(p),
|
||||||
T!['{'] | T![true] | T![false] | T![-] => const_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
|
// test lifetime_arg
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::grammar::attributes::ATTRIBUTE_FIRST;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) {
|
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<'_>) {
|
fn generic_param_list(p: &mut Parser<'_>) {
|
||||||
assert!(p.at(T![<]));
|
assert!(p.at(T![<]));
|
||||||
let m = p.start();
|
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);
|
m.complete(p, GENERIC_PARAM_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_param(p: &mut Parser<'_>) {
|
const GENERIC_PARAM_FIRST: TokenSet = TokenSet::new(&[IDENT, LIFETIME_IDENT, T![const]]);
|
||||||
let m = p.start();
|
|
||||||
// test generic_param_attribute
|
fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
|
||||||
// fn foo<#[lt_attr] 'a, #[t_attr] T>() {}
|
|
||||||
attributes::outer_attrs(p);
|
|
||||||
match p.current() {
|
match p.current() {
|
||||||
LIFETIME_IDENT => lifetime_param(p, m),
|
LIFETIME_IDENT => lifetime_param(p, m),
|
||||||
IDENT => type_param(p, m),
|
IDENT => type_param(p, m),
|
||||||
T![const] => const_param(p, m),
|
T![const] => const_param(p, m),
|
||||||
_ => {
|
_ => {
|
||||||
m.abandon(p);
|
m.abandon(p);
|
||||||
p.err_and_bump("expected type parameter");
|
p.err_and_bump("expected generic parameter");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// test lifetime_param
|
// test lifetime_param
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::grammar::attributes::ATTRIBUTE_FIRST;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// test struct_item
|
// 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<'_>) {
|
fn tuple_field_list(p: &mut Parser<'_>) {
|
||||||
assert!(p.at(T!['(']));
|
assert!(p.at(T!['(']));
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
p.bump(T!['(']);
|
delimited(p, T!['('], T![')'], T![,], TUPLE_FIELD_FIRST, |p| {
|
||||||
while !p.at(T![')']) && !p.at(EOF) {
|
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
// test tuple_field_attrs
|
// test tuple_field_attrs
|
||||||
// struct S (#[attr] f32);
|
// struct S (#[attr] f32);
|
||||||
attributes::outer_attrs(p);
|
attributes::outer_attrs(p);
|
||||||
opt_visibility(p, true);
|
let has_vis = opt_visibility(p, true);
|
||||||
if !p.at_ts(types::TYPE_FIRST) {
|
if !p.at_ts(types::TYPE_FIRST) {
|
||||||
p.error("expected a type");
|
p.error("expected a type");
|
||||||
m.complete(p, ERROR);
|
if has_vis {
|
||||||
break;
|
m.complete(p, ERROR);
|
||||||
|
} else {
|
||||||
|
m.abandon(p);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
types::type_(p);
|
types::type_(p);
|
||||||
m.complete(p, TUPLE_FIELD);
|
m.complete(p, TUPLE_FIELD);
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
if !p.at(T![')']) {
|
|
||||||
p.expect(T![,]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.expect(T![')']);
|
|
||||||
m.complete(p, TUPLE_FIELD_LIST);
|
m.complete(p, TUPLE_FIELD_LIST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::grammar::attributes::ATTRIBUTE_FIRST;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// test param_list
|
// 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");
|
p.error("expected value parameter");
|
||||||
m.abandon(p);
|
m.abandon(p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
param(p, m, flavor);
|
param(p, m, flavor);
|
||||||
if !p.at(ket) {
|
if !p.at(T![,]) {
|
||||||
p.expect(T![,]);
|
if p.at_ts(PARAM_FIRST.union(ATTRIBUTE_FIRST)) {
|
||||||
|
p.error("expected `,`");
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.bump(T![,]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
// test qual_paths
|
// test qual_paths
|
||||||
|
@ -102,7 +106,12 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
|
||||||
m.complete(p, NAME_REF);
|
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 {
|
if empty {
|
||||||
// test_err empty_segment
|
// test_err empty_segment
|
||||||
// use crate::;
|
// use crate::;
|
||||||
|
|
|
@ -17,8 +17,9 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
|
||||||
T![Self],
|
T![Self],
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
|
pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
|
||||||
T![')'],
|
T![')'],
|
||||||
|
T![>],
|
||||||
T![,],
|
T![,],
|
||||||
// test_err struct_field_recover
|
// test_err struct_field_recover
|
||||||
// struct S { f pub g: () }
|
// struct S { f pub g: () }
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::{LexedStr, TopEntryPoint};
|
||||||
#[test]
|
#[test]
|
||||||
fn lex_ok() {
|
fn lex_ok() {
|
||||||
for case in TestCase::list("lexer/ok") {
|
for case in TestCase::list("lexer/ok") {
|
||||||
|
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||||
let actual = lex(&case.text);
|
let actual = lex(&case.text);
|
||||||
expect_file![case.rast].assert_eq(&actual)
|
expect_file![case.rast].assert_eq(&actual)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +24,7 @@ fn lex_ok() {
|
||||||
#[test]
|
#[test]
|
||||||
fn lex_err() {
|
fn lex_err() {
|
||||||
for case in TestCase::list("lexer/err") {
|
for case in TestCase::list("lexer/err") {
|
||||||
|
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
|
||||||
let actual = lex(&case.text);
|
let actual = lex(&case.text);
|
||||||
expect_file![case.rast].assert_eq(&actual)
|
expect_file![case.rast].assert_eq(&actual)
|
||||||
}
|
}
|
||||||
|
@ -46,6 +48,7 @@ fn lex(text: &str) -> String {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_ok() {
|
fn parse_ok() {
|
||||||
for case in TestCase::list("parser/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);
|
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||||
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
|
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
|
||||||
expect_file![case.rast].assert_eq(&actual);
|
expect_file![case.rast].assert_eq(&actual);
|
||||||
|
@ -55,6 +58,7 @@ fn parse_ok() {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_inline_ok() {
|
fn parse_inline_ok() {
|
||||||
for case in TestCase::list("parser/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);
|
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||||
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
|
assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
|
||||||
expect_file![case.rast].assert_eq(&actual);
|
expect_file![case.rast].assert_eq(&actual);
|
||||||
|
@ -64,6 +68,7 @@ fn parse_inline_ok() {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_err() {
|
fn parse_err() {
|
||||||
for case in TestCase::list("parser/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);
|
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||||
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
|
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
|
||||||
expect_file![case.rast].assert_eq(&actual)
|
expect_file![case.rast].assert_eq(&actual)
|
||||||
|
@ -73,6 +78,7 @@ fn parse_err() {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_inline_err() {
|
fn parse_inline_err() {
|
||||||
for case in TestCase::list("parser/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);
|
let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
|
||||||
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
|
assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
|
||||||
expect_file![case.rast].assert_eq(&actual)
|
expect_file![case.rast].assert_eq(&actual)
|
||||||
|
|
|
@ -65,7 +65,7 @@ fn macro_stmt() {
|
||||||
MACRO_STMTS
|
MACRO_STMTS
|
||||||
ERROR
|
ERROR
|
||||||
SHEBANG "#!/usr/bin/rust"
|
SHEBANG "#!/usr/bin/rust"
|
||||||
error 0: expected expression
|
error 0: expected expression, item or let statement
|
||||||
"##]],
|
"##]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
|
|
|
@ -44,8 +44,7 @@ SOURCE_FILE
|
||||||
IDENT "T"
|
IDENT "T"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 9: expected type parameter
|
error 9: expected generic parameter
|
||||||
error 11: expected COMMA
|
|
||||||
error 11: expected R_ANGLE
|
error 11: expected R_ANGLE
|
||||||
error 11: expected `;`, `{`, or `(`
|
error 11: expected `;`, `{`, or `(`
|
||||||
error 12: expected an item
|
error 12: expected an item
|
||||||
|
|
|
@ -43,17 +43,14 @@ SOURCE_FILE
|
||||||
IDENT "Box"
|
IDENT "Box"
|
||||||
GENERIC_ARG_LIST
|
GENERIC_ARG_LIST
|
||||||
L_ANGLE "<"
|
L_ANGLE "<"
|
||||||
TYPE_ARG
|
ERROR
|
||||||
ERROR
|
AT "@"
|
||||||
AT "@"
|
WHITESPACE " "
|
||||||
WHITESPACE " "
|
MACRO_CALL
|
||||||
TUPLE_FIELD
|
PATH
|
||||||
PATH_TYPE
|
PATH_SEGMENT
|
||||||
PATH
|
NAME_REF
|
||||||
PATH_SEGMENT
|
IDENT "Any"
|
||||||
NAME_REF
|
|
||||||
IDENT "Any"
|
|
||||||
ERROR
|
|
||||||
ERROR
|
ERROR
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
ERROR
|
ERROR
|
||||||
|
@ -69,17 +66,14 @@ SOURCE_FILE
|
||||||
ERROR
|
ERROR
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n\n"
|
WHITESPACE "\n\n"
|
||||||
error 67: expected type
|
error 67: expected R_ANGLE
|
||||||
error 68: expected COMMA
|
error 67: expected R_ANGLE
|
||||||
error 68: expected R_ANGLE
|
error 67: expected R_ANGLE
|
||||||
error 68: expected COMMA
|
error 67: expected R_PAREN
|
||||||
error 68: expected R_ANGLE
|
error 67: expected SEMICOLON
|
||||||
error 68: expected COMMA
|
error 67: expected an item
|
||||||
error 68: expected R_ANGLE
|
error 72: expected BANG
|
||||||
error 68: expected COMMA
|
error 72: expected `{`, `[`, `(`
|
||||||
error 72: expected COMMA
|
|
||||||
error 72: expected a type
|
|
||||||
error 72: expected R_PAREN
|
|
||||||
error 72: expected SEMICOLON
|
error 72: expected SEMICOLON
|
||||||
error 72: expected an item
|
error 72: expected an item
|
||||||
error 73: expected an item
|
error 73: expected an item
|
||||||
|
|
|
@ -145,27 +145,29 @@ SOURCE_FILE
|
||||||
error 16: expected expression
|
error 16: expected expression
|
||||||
error 17: expected R_BRACK
|
error 17: expected R_BRACK
|
||||||
error 17: expected SEMICOLON
|
error 17: expected SEMICOLON
|
||||||
error 17: expected expression
|
error 17: expected expression, item or let statement
|
||||||
error 25: expected a name
|
error 25: expected a name
|
||||||
error 26: expected `;`, `{`, or `(`
|
error 26: expected `;`, `{`, or `(`
|
||||||
error 30: expected pattern
|
error 30: expected pattern
|
||||||
error 31: expected SEMICOLON
|
error 31: expected SEMICOLON
|
||||||
error 53: expected expression
|
error 53: expected expression
|
||||||
|
error 54: expected R_PAREN
|
||||||
error 54: expected SEMICOLON
|
error 54: expected SEMICOLON
|
||||||
error 54: expected expression
|
error 54: expected expression, item or let statement
|
||||||
error 60: expected type
|
error 60: expected type
|
||||||
error 60: expected `{`
|
error 60: expected `{`
|
||||||
error 60: expected expression
|
error 60: expected expression, item or let statement
|
||||||
error 65: expected pattern
|
error 65: expected pattern
|
||||||
error 65: expected SEMICOLON
|
error 65: expected SEMICOLON
|
||||||
error 65: expected expression
|
error 65: expected expression, item or let statement
|
||||||
error 92: expected expression
|
error 92: expected expression
|
||||||
|
error 93: expected R_PAREN
|
||||||
error 93: expected SEMICOLON
|
error 93: expected SEMICOLON
|
||||||
error 93: expected expression
|
error 93: expected expression, item or let statement
|
||||||
error 95: expected expression
|
error 95: expected expression, item or let statement
|
||||||
error 96: expected expression
|
error 96: expected expression, item or let statement
|
||||||
error 103: expected a name
|
error 103: expected a name
|
||||||
error 104: expected `{`
|
error 104: expected `{`
|
||||||
error 108: expected pattern
|
error 108: expected pattern
|
||||||
error 108: expected SEMICOLON
|
error 108: expected SEMICOLON
|
||||||
error 108: expected expression
|
error 108: expected expression, item or let statement
|
||||||
|
|
|
@ -168,44 +168,44 @@ SOURCE_FILE
|
||||||
L_PAREN "("
|
L_PAREN "("
|
||||||
ERROR
|
ERROR
|
||||||
QUESTION "?"
|
QUESTION "?"
|
||||||
EXPR_STMT
|
TYPE_ARG
|
||||||
PATH_EXPR
|
PATH_TYPE
|
||||||
PATH
|
PATH
|
||||||
PATH_SEGMENT
|
PATH_SEGMENT
|
||||||
NAME_REF
|
NAME_REF
|
||||||
IDENT "Sized"
|
IDENT "Sized"
|
||||||
ERROR
|
ERROR
|
||||||
R_PAREN ")"
|
R_PAREN ")"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
ERROR
|
ERROR
|
||||||
PLUS "+"
|
PLUS "+"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
TUPLE_EXPR
|
EXPR_STMT
|
||||||
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
|
||||||
BIN_EXPR
|
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
|
||||||
PATH_EXPR
|
BIN_EXPR
|
||||||
PATH
|
PATH_EXPR
|
||||||
PATH_SEGMENT
|
PATH
|
||||||
NAME_REF
|
PATH_SEGMENT
|
||||||
IDENT "Trait"
|
NAME_REF
|
||||||
L_ANGLE "<"
|
IDENT "Trait"
|
||||||
ERROR
|
L_ANGLE "<"
|
||||||
LIFETIME_IDENT "'a"
|
ERROR
|
||||||
R_ANGLE ">"
|
LIFETIME_IDENT "'a"
|
||||||
ERROR
|
R_ANGLE ">"
|
||||||
R_PAREN ")"
|
R_PAREN ")"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
PLUS "+"
|
PLUS "+"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
|
@ -220,108 +220,93 @@ SOURCE_FILE
|
||||||
R_ANGLE ">"
|
R_ANGLE ">"
|
||||||
ERROR
|
ERROR
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n "
|
WHITESPACE "\n "
|
||||||
LET_EXPR
|
LET_STMT
|
||||||
LET_KW "let"
|
LET_KW "let"
|
||||||
WHITESPACE " "
|
|
||||||
WILDCARD_PAT
|
|
||||||
UNDERSCORE "_"
|
|
||||||
ERROR
|
|
||||||
COLON ":"
|
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
BIN_EXPR
|
WILDCARD_PAT
|
||||||
BIN_EXPR
|
UNDERSCORE "_"
|
||||||
PATH_EXPR
|
COLON ":"
|
||||||
PATH
|
WHITESPACE " "
|
||||||
PATH_SEGMENT
|
DYN_TRAIT_TYPE
|
||||||
NAME_REF
|
TYPE_BOUND_LIST
|
||||||
IDENT "Box"
|
TYPE_BOUND
|
||||||
L_ANGLE "<"
|
PATH_TYPE
|
||||||
TUPLE_EXPR
|
PATH
|
||||||
L_PAREN "("
|
PATH_SEGMENT
|
||||||
CLOSURE_EXPR
|
NAME_REF
|
||||||
FOR_KW "for"
|
IDENT "Box"
|
||||||
GENERIC_PARAM_LIST
|
GENERIC_ARG_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 "<"
|
L_ANGLE "<"
|
||||||
ERROR
|
TYPE_ARG
|
||||||
LIFETIME_IDENT "'a"
|
PAREN_TYPE
|
||||||
R_ANGLE ">"
|
L_PAREN "("
|
||||||
ERROR
|
FOR_TYPE
|
||||||
R_PAREN ")"
|
FOR_KW "for"
|
||||||
WHITESPACE " "
|
GENERIC_PARAM_LIST
|
||||||
PLUS "+"
|
L_ANGLE "<"
|
||||||
WHITESPACE " "
|
LIFETIME_PARAM
|
||||||
PAREN_EXPR
|
LIFETIME
|
||||||
L_PAREN "("
|
LIFETIME_IDENT "'a"
|
||||||
PATH_EXPR
|
R_ANGLE ">"
|
||||||
PATH
|
WHITESPACE " "
|
||||||
PATH_SEGMENT
|
PATH_TYPE
|
||||||
NAME_REF
|
PATH
|
||||||
IDENT "Copy"
|
PATH_SEGMENT
|
||||||
R_PAREN ")"
|
NAME_REF
|
||||||
WHITESPACE " "
|
IDENT "Trait"
|
||||||
PLUS "+"
|
GENERIC_ARG_LIST
|
||||||
WHITESPACE " "
|
L_ANGLE "<"
|
||||||
PAREN_EXPR
|
LIFETIME_ARG
|
||||||
L_PAREN "("
|
LIFETIME
|
||||||
ERROR
|
LIFETIME_IDENT "'a"
|
||||||
QUESTION "?"
|
R_ANGLE ">"
|
||||||
PATH_EXPR
|
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
|
||||||
PATH_SEGMENT
|
PATH_SEGMENT
|
||||||
NAME_REF
|
NAME_REF
|
||||||
IDENT "Sized"
|
IDENT "Sized"
|
||||||
R_PAREN ")"
|
R_PAREN ")"
|
||||||
R_ANGLE ">"
|
ERROR
|
||||||
ERROR
|
R_ANGLE ">"
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 88: expected COMMA
|
|
||||||
error 88: expected R_ANGLE
|
error 88: expected R_ANGLE
|
||||||
error 121: expected SEMICOLON
|
error 121: expected SEMICOLON
|
||||||
error 121: expected expression
|
error 121: expected expression, item or let statement
|
||||||
error 140: expected type
|
error 140: expected type
|
||||||
error 141: expected R_PAREN
|
error 141: expected R_PAREN
|
||||||
error 141: expected COMMA
|
error 141: expected COMMA
|
||||||
error 141: expected R_ANGLE
|
error 146: expected R_ANGLE
|
||||||
error 141: expected SEMICOLON
|
|
||||||
error 146: expected SEMICOLON
|
error 146: expected SEMICOLON
|
||||||
error 146: expected expression
|
error 146: expected expression, item or let statement
|
||||||
error 148: expected expression
|
error 148: expected expression, item or let statement
|
||||||
error 158: expected `|`
|
error 158: expected `|`
|
||||||
error 158: expected COMMA
|
error 158: expected COMMA
|
||||||
error 165: expected expression
|
error 165: expected expression
|
||||||
error 168: expected expression
|
error 168: expected expression
|
||||||
error 179: expected expression
|
error 179: expected expression
|
||||||
error 180: expected COMMA
|
error 180: expected SEMICOLON
|
||||||
error 190: expected EQ
|
error 215: expected R_ANGLE
|
||||||
error 190: expected expression
|
error 235: expected SEMICOLON
|
||||||
error 191: expected COMMA
|
error 235: expected expression, item or let statement
|
||||||
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
|
|
||||||
|
|
|
@ -156,8 +156,7 @@ SOURCE_FILE
|
||||||
PATH_SEGMENT
|
PATH_SEGMENT
|
||||||
NAME_REF
|
NAME_REF
|
||||||
IDENT "i32"
|
IDENT "i32"
|
||||||
WHITESPACE " "
|
WHITESPACE " "
|
||||||
ERROR
|
|
||||||
ERROR
|
ERROR
|
||||||
L_CURLY "{"
|
L_CURLY "{"
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
|
@ -199,10 +198,8 @@ error 95: expected type
|
||||||
error 95: expected COMMA
|
error 95: expected COMMA
|
||||||
error 96: expected field
|
error 96: expected field
|
||||||
error 98: expected field declaration
|
error 98: expected field declaration
|
||||||
|
error 371: expected R_PAREN
|
||||||
error 371: expected COMMA
|
error 371: expected COMMA
|
||||||
error 372: expected a type
|
|
||||||
error 372: expected R_PAREN
|
|
||||||
error 372: expected COMMA
|
|
||||||
error 372: expected enum variant
|
error 372: expected enum variant
|
||||||
error 374: expected enum variant
|
error 374: expected enum variant
|
||||||
error 494: expected pattern
|
error 494: expected pattern
|
||||||
|
|
|
@ -72,4 +72,4 @@ SOURCE_FILE
|
||||||
error 24: expected existential, fn, trait or impl
|
error 24: expected existential, fn, trait or impl
|
||||||
error 41: expected existential, fn, trait or impl
|
error 41: expected existential, fn, trait or impl
|
||||||
error 56: expected a block
|
error 56: expected a block
|
||||||
error 75: expected a loop
|
error 75: expected a loop or block
|
||||||
|
|
|
@ -12,7 +12,7 @@ SOURCE_FILE
|
||||||
STMT_LIST
|
STMT_LIST
|
||||||
L_CURLY "{"
|
L_CURLY "{"
|
||||||
WHITESPACE "\n "
|
WHITESPACE "\n "
|
||||||
EXPR_STMT
|
BIN_EXPR
|
||||||
PATH_EXPR
|
PATH_EXPR
|
||||||
PATH
|
PATH
|
||||||
PATH_SEGMENT
|
PATH_SEGMENT
|
||||||
|
@ -41,13 +41,14 @@ SOURCE_FILE
|
||||||
COLON2 "::"
|
COLON2 "::"
|
||||||
ERROR
|
ERROR
|
||||||
L_ANGLE "<"
|
L_ANGLE "<"
|
||||||
BIN_EXPR
|
TYPE_ARG
|
||||||
PATH_EXPR
|
PATH_TYPE
|
||||||
PATH
|
PATH
|
||||||
PATH_SEGMENT
|
PATH_SEGMENT
|
||||||
NAME_REF
|
NAME_REF
|
||||||
IDENT "nope"
|
IDENT "nope"
|
||||||
SHR ">>"
|
R_ANGLE ">"
|
||||||
|
R_ANGLE ">"
|
||||||
ERROR
|
ERROR
|
||||||
SEMICOLON ";"
|
SEMICOLON ";"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
|
@ -114,8 +115,6 @@ SOURCE_FILE
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 30: expected identifier
|
error 30: expected identifier
|
||||||
error 31: expected COMMA
|
error 31: expected COMMA
|
||||||
error 31: expected R_ANGLE
|
|
||||||
error 31: expected SEMICOLON
|
|
||||||
error 37: expected expression
|
error 37: expected expression
|
||||||
error 75: expected identifier
|
error 75: expected identifier
|
||||||
error 76: expected SEMICOLON
|
error 76: expected SEMICOLON
|
||||||
|
|
|
@ -23,6 +23,6 @@ SOURCE_FILE
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 22: expected a loop
|
error 22: expected a loop or block
|
||||||
error 27: expected type
|
error 27: expected type
|
||||||
error 27: expected `{`
|
error 27: expected `{`
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn main() {
|
||||||
|
foo(bar::);
|
||||||
|
foo(bar:);
|
||||||
|
foo(bar+);
|
||||||
|
}
|
|
@ -49,5 +49,5 @@ SOURCE_FILE
|
||||||
R_CURLY "}"
|
R_CURLY "}"
|
||||||
WHITESPACE "\n"
|
WHITESPACE "\n"
|
||||||
error 6: missing type for function parameter
|
error 6: missing type for function parameter
|
||||||
error 6: expected COMMA
|
error 6: expected `,`
|
||||||
error 16: missing type for function parameter
|
error 16: missing type for function parameter
|
||||||
|
|
Loading…
Reference in a new issue