diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index ce26d3806c..654c1b6134 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -14,7 +14,7 @@ use syntax::{ }, ast::{ self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, - NameOrNameRef, + NameOrNameRef, RangeItem, }, match_ast, }; @@ -86,7 +86,8 @@ pub(super) fn expand_and_analyze<'db>( // add the relative offset back, so that left_biased finds the proper token let original_offset = expansion.original_offset + relative_offset; - let token = expansion.original_file.token_at_offset(original_offset).left_biased()?; + let token = expansion.original_file.token_at_offset(original_offset); + let token = left_biased_or_after_l_paren(token)?; analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| { AnalysisResult { analysis, expected, qualifier_ctx, token, original_offset } @@ -761,6 +762,11 @@ fn expected_type_and_name<'db>( let ty = sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original); (ty, None) }, + ast::RangeExpr(it) => { + let bound = it.start().or_else(|| it.end()); + let ty = bound.and_then(|it| sema.type_of_expr(&it)).map(TypeInfo::original); + (ty, None) + }, ast::Fn(it) => { cov_mark::hit!(expected_type_fn_ret_with_leading_char); cov_mark::hit!(expected_type_fn_ret_without_leading_char); @@ -2065,3 +2071,12 @@ fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { } token } + +fn left_biased_or_after_l_paren(token: syntax::TokenAtOffset) -> Option { + match token { + syntax::TokenAtOffset::None => None, + syntax::TokenAtOffset::Single(it) => Some(it), + syntax::TokenAtOffset::Between(left, right) if left.kind() == T!['('] => Some(right), + syntax::TokenAtOffset::Between(left, _) => Some(left), + } +} diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index 09a9b6f112..7aa1f43e88 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -647,3 +647,55 @@ fn foo() { expect![[r#"ty: bool, name: ?"#]], ); } + +#[test] +fn expected_type_range_expr() { + check_expected_type_and_name( + r#" +//- minicore: range +enum State { Stop } +fn bar(x: core::ops::Range) {} +fn foo() { + bar(State::Stop..$0) +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +//- minicore: range +enum State { Stop } +fn bar(x: core::ops::Range) {} +fn foo() { + bar($0..State::Stop) +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); + + // FIXME: Analyze the type of `..` and use the generic parameters of Range* + check_expected_type_and_name( + r#" +//- minicore: range +enum State { Stop } +fn bar(x: core::ops::Range) {} +fn foo() { + bar(..$0) +} +"#, + expect![[r#"ty: ?, name: ?"#]], + ); + + check_expected_type_and_name( + r#" +//- minicore: range +enum State { Stop } +fn bar(x: core::ops::Range) {} +fn foo() { + bar($0..) +} +"#, + expect![[r#"ty: ?, name: ?"#]], + ); +}