Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/context/analysis.rs')
| -rw-r--r-- | crates/ide-completion/src/context/analysis.rs | 110 |
1 files changed, 92 insertions, 18 deletions
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 2eabf99fc6..b33a547dee 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -559,6 +559,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option<Type<'db>>, Option<NameOrNameRef>) { + let token = prev_special_biased_token_at_trivia(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -629,6 +630,17 @@ fn expected_type_and_name<'db>( .map(TypeInfo::original); (ty, None) }, + ast::BinExpr(it) => { + if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() { + let ty = it.lhs() + .and_then(|lhs| sema.type_of_expr(&lhs)) + .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs))) + .map(TypeInfo::original); + (ty, None) + } else { + (None, None) + } + }, ast::ArgList(_) => { cov_mark::hit!(expected_type_fn_param); ActiveParameter::at_token( @@ -712,6 +724,18 @@ fn expected_type_and_name<'db>( let def = sema.to_def(&it); (def.map(|def| def.ret_type(sema.db)), None) }, + ast::ReturnExpr(it) => { + let fn_ = sema.ancestors_with_macros(it.syntax().clone()) + .find_map(Either::<ast::Fn, ast::ClosureExpr>::cast); + let ty = fn_.and_then(|f| match f { + Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Right(f) => { + let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; + Some(ty.return_type()) + }, + }); + (ty, None) + }, ast::ClosureExpr(it) => { let ty = sema.type_of_expr(&it.into()); ty.and_then(|ty| ty.original.as_callable(sema.db)) @@ -923,20 +947,39 @@ fn classify_name_ref<'db>( None } }; - let after_if_expr = |node: SyntaxNode| { - let prev_expr = (|| { - let node = match node.parent().and_then(ast::ExprStmt::cast) { - Some(stmt) => stmt.syntax().clone(), - None => node, - }; - let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; + let prev_expr = |node: SyntaxNode| { + let node = match node.parent().and_then(ast::ExprStmt::cast) { + Some(stmt) => stmt.syntax().clone(), + None => node, + }; + let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - ast::ExprStmt::cast(prev_sibling.clone()) - .and_then(|it| it.expr()) - .or_else(|| ast::Expr::cast(prev_sibling)) - })(); + match_ast! { + match prev_sibling { + ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), + ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), + ast::Expr(expr) => Some(expr), + _ => None, + } + } + }; + let after_if_expr = |node: SyntaxNode| { + let prev_expr = prev_expr(node); matches!(prev_expr, Some(ast::Expr::IfExpr(_))) }; + let after_incomplete_let = |node: SyntaxNode| { + prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast) + }; + let before_else_kw = |node: &SyntaxNode| { + node.parent() + .and_then(ast::ExprStmt::cast) + .filter(|stmt| stmt.semicolon_token().is_none()) + .and_then(|stmt| non_trivia_sibling(stmt.syntax().clone().into(), Direction::Next)) + .and_then(NodeOrToken::into_node) + .filter(|next| next.kind() == SyntaxKind::ERROR) + .and_then(|next| next.first_token()) + .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW) + }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1025,9 +1068,6 @@ fn classify_name_ref<'db>( sema.source(trait_)?.value.generic_param_list() } } - hir::ModuleDef::TraitAlias(trait_) => { - sema.source(trait_)?.value.generic_param_list() - } hir::ModuleDef::TypeAlias(ty_) => { sema.source(ty_)?.value.generic_param_list() } @@ -1162,19 +1202,23 @@ fn classify_name_ref<'db>( Some(res) }; - let is_in_condition = |it: &ast::Expr| { + fn is_in_condition(it: &ast::Expr) -> bool { (|| { let parent = it.syntax().parent()?; if let Some(expr) = ast::WhileExpr::cast(parent.clone()) { Some(expr.condition()? == *it) - } else if let Some(expr) = ast::IfExpr::cast(parent) { + } else if let Some(expr) = ast::IfExpr::cast(parent.clone()) { Some(expr.condition()? == *it) + } else if let Some(expr) = ast::BinExpr::cast(parent) + && expr.op_token()?.kind() == T![&&] + { + Some(is_in_condition(&expr.into())) } else { None } })() .unwrap_or(false) - }; + } let make_path_kind_expr = |expr: ast::Expr| { let it = expr.syntax(); @@ -1235,10 +1279,15 @@ fn classify_name_ref<'db>( }; let is_func_update = func_update_record(it); let in_condition = is_in_condition(&expr); + let after_incomplete_let = after_incomplete_let(it.clone()).is_some(); + let incomplete_expr_stmt = + it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none()); let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) - .is_some_and(|it| it.semicolon_token().is_none()); + .is_some_and(|it| it.semicolon_token().is_none()) + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it); + let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { @@ -1259,7 +1308,9 @@ fn classify_name_ref<'db>( is_func_update, innermost_ret_ty, self_param, + in_value, incomplete_let, + after_incomplete_let, impl_, in_match_guard, }, @@ -1856,3 +1907,26 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> { } None } + +fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { + while token.kind().is_trivia() + && let Some(prev) = token.prev_token() + && let T![=] + | T![+=] + | T![/=] + | T![*=] + | T![%=] + | T![>>=] + | T![<<=] + | T![-=] + | T![|=] + | T![&=] + | T![^=] + | T![return] + | T![break] + | T![continue] = prev.kind() + { + token = prev + } + token +} |