Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/ide-completion/src/completions.rs | 43 | ||||
| -rw-r--r-- | crates/ide-completion/src/completions/type.rs | 8 | ||||
| -rw-r--r-- | crates/ide-completion/src/context.rs | 24 | ||||
| -rw-r--r-- | crates/ide-completion/src/context/analysis.rs | 9 | ||||
| -rw-r--r-- | crates/ide-completion/src/item.rs | 36 | ||||
| -rw-r--r-- | crates/ide-completion/src/render.rs | 54 | ||||
| -rw-r--r-- | crates/ide-completion/src/tests/item.rs | 32 | ||||
| -rw-r--r-- | crates/ide-completion/src/tests/type_pos.rs | 226 | ||||
| -rw-r--r-- | crates/ide/src/typing.rs | 6 | ||||
| -rw-r--r-- | crates/parser/src/grammar.rs | 12 | ||||
| -rw-r--r-- | crates/parser/src/grammar/items.rs | 7 | ||||
| -rw-r--r-- | crates/parser/test_data/generated/runner.rs | 4 | ||||
| -rw-r--r-- | crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast | 50 | ||||
| -rw-r--r-- | crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs | 2 |
14 files changed, 467 insertions, 46 deletions
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 1fb1fd4e57..4a94383ff4 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -34,7 +34,7 @@ use crate::{ CompletionContext, CompletionItem, CompletionItemKind, context::{ DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, PatternContext, TypeLocation, Visible, + PathCompletionCtx, PathKind, PatternContext, TypeAscriptionTarget, TypeLocation, Visible, }, item::Builder, render::{ @@ -45,7 +45,7 @@ use crate::{ macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, render_expr, render_field, render_path_resolution, render_pattern_resolution, - render_tuple_field, + render_tuple_field, render_type_keyword_snippet, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, }, @@ -104,6 +104,21 @@ impl Completions { } } + pub(crate) fn add_nameref_keywords_with_type_like( + &mut self, + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + ) { + let mut add_keyword = |kw| { + render_type_keyword_snippet(ctx, path_ctx, kw, kw).add_to(self, ctx.db); + }; + ["self::", "crate::"].into_iter().for_each(&mut add_keyword); + + if ctx.depth_from_crate_root > 0 { + add_keyword("super::"); + } + } + pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_>) { ["self", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); @@ -112,11 +127,19 @@ impl Completions { } } - pub(crate) fn add_type_keywords(&mut self, ctx: &CompletionContext<'_>) { - self.add_keyword_snippet(ctx, "fn", "fn($1)"); - self.add_keyword_snippet(ctx, "dyn", "dyn $0"); - self.add_keyword_snippet(ctx, "impl", "impl $0"); - self.add_keyword_snippet(ctx, "for", "for<$1>"); + pub(crate) fn add_type_keywords( + &mut self, + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + ) { + let mut add_keyword = |kw, snippet| { + render_type_keyword_snippet(ctx, path_ctx, kw, snippet).add_to(self, ctx.db); + }; + + add_keyword("fn", "fn($1)"); + add_keyword("dyn", "dyn $0"); + add_keyword("impl", "impl $0"); + add_keyword("for", "for<$1>"); } pub(crate) fn add_super_keyword( @@ -747,6 +770,12 @@ pub(super) fn complete_name_ref( field::complete_field_list_tuple_variant(acc, ctx, path_ctx); } TypeLocation::TypeAscription(ascription) => { + if let TypeAscriptionTarget::RetType { item: Some(item), .. } = + ascription + && path_ctx.required_thin_arrow().is_some() + { + keyword::complete_for_and_where(acc, ctx, &item.clone().into()); + } r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } TypeLocation::GenericArg { .. } diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 8ff9c3258e..e2125a9678 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -206,8 +206,8 @@ pub(crate) fn complete_type_path( _ => {} }; - acc.add_nameref_keywords_with_colon(ctx); - acc.add_type_keywords(ctx); + acc.add_nameref_keywords_with_type_like(ctx, path_ctx); + acc.add_type_keywords(ctx, path_ctx); ctx.process_all_names(&mut |name, def, doc_aliases| { if scope_def_applicable(def) { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases); @@ -230,14 +230,14 @@ pub(crate) fn complete_ascribed_type( TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => { ctx.sema.type_of_pat(pat.as_ref()?) } - TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => { + TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType { body: exp, .. } => { ctx.sema.type_of_expr(exp.as_ref()?) } }? .adjusted(); if !ty.is_unknown() { let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?; - acc.add(render_type_inference(ty_string, ctx)); + acc.add(render_type_inference(ty_string, ctx, path_ctx)); } None } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index a91f123176..ae3f717607 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -102,6 +102,28 @@ impl PathCompletionCtx<'_> { } ) } + + pub(crate) fn required_thin_arrow(&self) -> Option<(&'static str, TextSize)> { + let PathKind::Type { + location: + TypeLocation::TypeAscription(TypeAscriptionTarget::RetType { + item: Some(ref fn_item), + .. + }), + } = self.kind + else { + return None; + }; + if fn_item.ret_type().is_some_and(|it| it.thin_arrow_token().is_some()) { + return None; + } + let ret_type = fn_item.ret_type().and_then(|it| it.ty()); + match (ret_type, fn_item.param_list()) { + (Some(ty), _) => Some(("-> ", ty.syntax().text_range().start())), + (None, Some(param)) => Some((" ->", param.syntax().text_range().end())), + (None, None) => None, + } + } } /// The kind of path we are completing right now. @@ -231,7 +253,7 @@ impl TypeLocation { pub(crate) enum TypeAscriptionTarget { Let(Option<ast::Pat>), FnParam(Option<ast::Pat>), - RetType(Option<ast::Expr>), + RetType { body: Option<ast::Expr>, item: Option<ast::Fn> }, Const(Option<ast::Expr>), } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 294e70dd56..d8f160c100 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1278,15 +1278,14 @@ fn classify_name_ref<'db>( let original = ast::Static::cast(name.syntax().parent()?)?; TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body())) }, - ast::RetType(it) => { - it.thin_arrow_token()?; + ast::RetType(_) => { let parent = match ast::Fn::cast(parent.parent()?) { Some(it) => it.param_list(), None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), }; let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?; - TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! { + let body = match_ast! { match parent { ast::ClosureExpr(it) => { it.body() @@ -1296,7 +1295,9 @@ fn classify_name_ref<'db>( }, _ => return None, } - })) + }; + let item = ast::Fn::cast(parent); + TypeLocation::TypeAscription(TypeAscriptionTarget::RetType { body, item }) }, ast::Param(it) => { it.colon_token()?; diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 1a9139d855..e6dd1d37d9 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -450,6 +450,7 @@ impl CompletionItem { ref_match: None, imports_to_add: Default::default(), doc_aliases: vec![], + adds_text: None, edition, } } @@ -480,12 +481,13 @@ impl CompletionItem { /// A helper to make `CompletionItem`s. #[must_use] -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) struct Builder { source_range: TextRange, imports_to_add: SmallVec<[LocatedImport; 1]>, trait_name: Option<SmolStr>, doc_aliases: Vec<SmolStr>, + adds_text: Option<SmolStr>, label: SmolStr, insert_text: Option<String>, is_snippet: bool, @@ -526,9 +528,16 @@ impl Builder { let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); let mut detail_left = None; + let mut to_detail_left = |args: fmt::Arguments<'_>| { + let detail_left = detail_left.get_or_insert_with(String::new); + if !detail_left.is_empty() { + detail_left.push(' '); + } + format_to!(detail_left, "{args}") + }; if !self.doc_aliases.is_empty() { let doc_aliases = self.doc_aliases.iter().join(", "); - detail_left = Some(format!("(alias {doc_aliases})")); + to_detail_left(format_args!("(alias {doc_aliases})")); let lookup_doc_aliases = self .doc_aliases .iter() @@ -548,22 +557,17 @@ impl Builder { lookup = format_smolstr!("{lookup}{lookup_doc_aliases}"); } } + if let Some(adds_text) = self.adds_text { + to_detail_left(format_args!("(adds {})", adds_text.trim())); + } if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one - let detail_left = detail_left.get_or_insert_with(String::new); - format_to!( - detail_left, - "{}(use {})", - if detail_left.is_empty() { "" } else { " " }, + to_detail_left(format_args!( + "(use {})", import_edit.import_path.display(db, self.edition) - ); + )); } else if let Some(trait_name) = self.trait_name { - let detail_left = detail_left.get_or_insert_with(String::new); - format_to!( - detail_left, - "{}(as {trait_name})", - if detail_left.is_empty() { "" } else { " " }, - ); + to_detail_left(format_args!("(as {trait_name})")); } let text_edit = match self.text_edit { @@ -613,6 +617,10 @@ impl Builder { self.doc_aliases = doc_aliases; self } + pub(crate) fn adds_text(&mut self, adds_text: SmolStr) -> &mut Builder { + self.adds_text = Some(adds_text); + self + } pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder { self.insert_text = Some(insert_text.into()); self diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 4751ee36ec..f37b73a28a 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -220,13 +220,15 @@ pub(crate) fn render_tuple_field( pub(crate) fn render_type_inference( ty_string: String, ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, ) -> CompletionItem { let mut builder = CompletionItem::new( CompletionItemKind::InferredType, ctx.source_range(), - ty_string, + &ty_string, ctx.edition, ); + adds_ret_type_arrow(ctx, path_ctx, &mut builder, ty_string); builder.set_relevance(CompletionRelevance { type_match: Some(CompletionRelevanceTypeMatch::Exact), exact_name_match: true, @@ -425,11 +427,10 @@ fn render_resolution_path( let config = completion.config; let requires_import = import_to_add.is_some(); - let name = local_name.display_no_db(ctx.completion.edition).to_smolstr(); + let name = local_name.display(db, completion.edition).to_smolstr(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.needs_escape(completion.edition) { - item.insert_text(local_name.display_no_db(completion.edition).to_smolstr()); - } + let mut insert_text = name.clone(); + // Add `<>` for generic types let type_path_no_ty_args = matches!( path_ctx, @@ -446,12 +447,14 @@ fn render_resolution_path( if has_non_default_type_params { cov_mark::hit!(inserts_angle_brackets_for_generics); + insert_text = format_smolstr!("{insert_text}<$0>"); item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{}<$0>", local_name.display(db, completion.edition))); + .insert_snippet(cap, ""); // set is snippet } } + adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); let mut set_item_relevance = |ty: Type<'_>| { if !ty.is_unknown() { @@ -577,6 +580,45 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo } } +pub(crate) fn render_type_keyword_snippet( + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + label: &str, + snippet: &str, +) -> Builder { + let source_range = ctx.source_range(); + let mut item = + CompletionItem::new(CompletionItemKind::Keyword, source_range, label, ctx.edition); + + let cap = ctx.config.snippet_cap; + if let Some(cap) = cap { + item.insert_snippet(cap, snippet); + } + + let insert_text = if cap.is_some() { snippet } else { label }.to_owned(); + adds_ret_type_arrow(ctx, path_ctx, &mut item, insert_text); + + item +} + +fn adds_ret_type_arrow( + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx<'_>, + item: &mut Builder, + insert_text: String, +) { + if let Some((arrow, at)) = path_ctx.required_thin_arrow() { + let mut edit = TextEdit::builder(); + + edit.insert(at, arrow.to_owned()); + edit.replace(ctx.source_range(), insert_text); + + item.text_edit(edit.finish()).adds_text(SmolStr::new_static(arrow)); + } else { + item.insert_text(insert_text); + } +} + // FIXME: This checks types without possible coercions which some completions might want to do fn match_types( ctx: &CompletionContext<'_>, diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index 61a9da8c27..2f032c3f4c 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -116,8 +116,23 @@ fn completes_where() { check_with_base_items( r"fn func() $0", expect![[r#" - kw where - "#]], + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], ); check_with_base_items( r"enum Enum $0", @@ -244,6 +259,19 @@ impl Copy for S where $0 } #[test] +fn fn_item_where_kw() { + check_edit( + "where", + r#" +fn foo() $0 +"#, + r#" +fn foo() where $0 +"#, + ); +} + +#[test] fn test_is_not_considered_macro() { check_with_base_items( r#" diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 7c6b7370aa..7d4a7fe6b8 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -1,7 +1,7 @@ //! Completion tests for type position. use expect_test::expect; -use crate::tests::{check, check_with_base_items}; +use crate::tests::{check, check_edit, check_with_base_items}; #[test] fn record_field_ty() { @@ -94,6 +94,230 @@ fn x<'lt, T, const C: usize>() -> $0 } #[test] +fn fn_return_type_missing_thin_arrow() { + check_with_base_items( + r#" +fn x() u$0 +"#, + expect![[r#" + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); + + check_with_base_items( + r#" +fn x() $0 +"#, + expect![[r#" + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); +} + +#[test] +fn fn_return_type_missing_thin_arrow_path_completion() { + check_edit( + "u32", + r#" +fn foo() u$0 +"#, + r#" +fn foo() -> u32 +"#, + ); + + check_edit( + "u32", + r#" +fn foo() $0 +"#, + r#" +fn foo() -> u32 +"#, + ); + + check_edit( + "Num", + r#" +type Num = u32; +fn foo() $0 +"#, + r#" +type Num = u32; +fn foo() -> Num +"#, + ); + + check_edit( + "impl", + r#" +fn foo() $0 +"#, + r#" +fn foo() -> impl $0 +"#, + ); + + check_edit( + "foo", + r#" +mod foo { pub type Num = u32; } +fn foo() $0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() -> foo +"#, + ); + + check_edit( + "crate::", + r#" +mod foo { pub type Num = u32; } +fn foo() $0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() -> crate:: +"#, + ); + + check_edit( + "Num", + r#" +mod foo { pub type Num = u32; } +fn foo() foo::$0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() -> foo::Num +"#, + ); + + // no spaces, test edit order + check_edit( + "foo", + r#" +mod foo { pub type Num = u32; } +fn foo()$0 +"#, + r#" +mod foo { pub type Num = u32; } +fn foo() ->foo +"#, + ); +} + +#[test] +fn fn_return_type_missing_thin_arrow_path_completion_with_generic_args() { + check_edit( + "Foo", + r#" +struct Foo<T>(T); +fn foo() F$0 +"#, + r#" +struct Foo<T>(T); +fn foo() -> Foo<$0> +"#, + ); + + check_edit( + "Foo", + r#" +struct Foo<T>(T); +fn foo() $0 +"#, + r#" +struct Foo<T>(T); +fn foo() -> Foo<$0> +"#, + ); + + check_edit( + "Foo", + r#" +type Foo<T> = T; +fn foo() $0 +"#, + r#" +type Foo<T> = T; +fn foo() -> Foo<$0> +"#, + ); +} + +#[test] +fn fn_return_type_missing_thin_arrow_infer_ref_type() { + check_with_base_items( + r#" +fn x() u$0 {&2u32} +"#, + expect![[r#" + en Enum (adds ->) Enum + ma makro!(…) macro_rules! makro + md module (adds ->) + st Record (adds ->) Record + st Tuple (adds ->) Tuple + st Unit (adds ->) Unit + tt Trait (adds ->) + un Union (adds ->) Union + bt u32 (adds ->) u32 + it &u32 (adds ->) + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); + + check_edit( + "&u32", + r#" +struct Foo<T>(T); +fn x() u$0 {&2u32} +"#, + r#" +struct Foo<T>(T); +fn x() -> &u32 {&2u32} +"#, + ); +} + +#[test] fn fn_return_type_after_reference() { check_with_base_items( r#" diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index ec620982ff..a49a85fe78 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -1239,12 +1239,6 @@ sdasdasdasdasd #[test] fn parenthesis_noop_in_item_position_with_macro() { type_char_noop('(', r#"$0println!();"#); - type_char_noop( - '(', - r#" -fn main() $0println!("hello"); -}"#, - ); } #[test] diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index e481bbe9bc..1ff8a56b58 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -303,6 +303,18 @@ fn opt_ret_type(p: &mut Parser<'_>) -> bool { } } +fn opt_no_arrow_ret_type(p: &mut Parser<'_>) -> bool { + if p.at_ts(PATH_NAME_REF_KINDS) { + let m = p.start(); + p.error("missing thin-arrow `->`"); + types::type_no_bounds(p); + m.complete(p, RET_TYPE); + true + } else { + false + } +} + fn name_r(p: &mut Parser<'_>, recovery: TokenSet) { if p.at(IDENT) { let m = p.start(); diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index c609f9383e..c0acdde2a7 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs @@ -422,7 +422,12 @@ fn fn_(p: &mut Parser<'_>, m: Marker) { // test function_ret_type // fn foo() {} // fn bar() -> () {} - opt_ret_type(p); + if !opt_ret_type(p) { + // test_err function_ret_type_missing_arrow + // fn foo() usize {} + // fn bar() super::Foo {} + opt_no_arrow_ret_type(p); + } // test_err fn_ret_recovery // fn foo() -> A>]) { let x = 1; } diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 4c001104fe..01fc172ed9 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -793,6 +793,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/fn_ret_recovery.rs"); } #[test] + fn function_ret_type_missing_arrow() { + run_and_expect_errors("test_data/parser/inline/err/function_ret_type_missing_arrow.rs"); + } + #[test] fn gen_fn() { run_and_expect_errors_with_edition( "test_data/parser/inline/err/gen_fn.rs", diff --git a/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast new file mode 100644 index 0000000000..c0bca6ed1c --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast @@ -0,0 +1,50 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "usize" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "bar" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + PATH_TYPE + PATH + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Foo" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 9: missing thin-arrow `->` +error 27: missing thin-arrow `->` diff --git a/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs new file mode 100644 index 0000000000..f48e539df5 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs @@ -0,0 +1,2 @@ +fn foo() usize {} +fn bar() super::Foo {} |