diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index b3d9ff004610..351cd145e090 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)?; hir::attach_db(sema.db, || analyze(sema, expansion, original_token, &token)).map( |(analysis, expected, qualifier_ctx)| AnalysisResult { @@ -734,6 +735,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); @@ -1993,3 +1999,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 51d28bd4ff98..ce90601ebb6b 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -605,3 +605,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: ?"#]], + ); +}