Skip to content

Commit 74abd44

Browse files
committed
fix: Do completions in path qualifier position
1 parent 7c59d7c commit 74abd44

File tree

2 files changed

+118
-85
lines changed

2 files changed

+118
-85
lines changed

crates/ide-completion/src/completions/expr.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ pub(crate) fn complete_expr_path(
1111
acc: &mut Completions,
1212
ctx: &CompletionContext<'_>,
1313
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
14-
&ExprCtx {
14+
expr_ctx: &ExprCtx,
15+
) {
16+
let _p = profile::span("complete_expr_path");
17+
if !ctx.qualifier_ctx.none() {
18+
return;
19+
}
20+
21+
let &ExprCtx {
1522
in_block_expr,
1623
in_loop_body,
1724
after_if_expr,
@@ -23,12 +30,7 @@ pub(crate) fn complete_expr_path(
2330
ref impl_,
2431
in_match_guard,
2532
..
26-
}: &ExprCtx,
27-
) {
28-
let _p = profile::span("complete_expr_path");
29-
if !ctx.qualifier_ctx.none() {
30-
return;
31-
}
33+
} = expr_ctx;
3234

3335
let wants_mut_token =
3436
ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);

crates/ide-completion/src/context/analysis.rs

+109-78
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ impl<'a> CompletionContext<'a> {
592592
has_call_parens: false,
593593
has_macro_bang: false,
594594
qualified: Qualified::No,
595-
parent: path.parent_path(),
595+
parent: None,
596596
path: path.clone(),
597597
kind: PathKind::Item { kind: ItemListKind::SourceFile },
598598
has_type_args: false,
@@ -827,92 +827,123 @@ impl<'a> CompletionContext<'a> {
827827
PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
828828
};
829829

830+
let mut kind_macro_call = |it: ast::MacroCall| {
831+
path_ctx.has_macro_bang = it.excl_token().is_some();
832+
let parent = it.syntax().parent()?;
833+
// Any path in an item list will be treated as a macro call by the parser
834+
let kind = match_ast! {
835+
match parent {
836+
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
837+
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
838+
ast::MacroType(ty) => make_path_kind_type(ty.into()),
839+
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
840+
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
841+
Some(it) => match_ast! {
842+
match it {
843+
ast::Trait(_) => ItemListKind::Trait,
844+
ast::Impl(it) => if it.trait_().is_some() {
845+
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
846+
} else {
847+
ItemListKind::Impl
848+
},
849+
_ => return None
850+
}
851+
},
852+
None => return None,
853+
} },
854+
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
855+
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
856+
_ => return None,
857+
}
858+
};
859+
Some(kind)
860+
};
861+
let make_path_kind_attr = |meta: ast::Meta| {
862+
let attr = meta.parent_attr()?;
863+
let kind = attr.kind();
864+
let attached = attr.syntax().parent()?;
865+
let is_trailing_outer_attr = kind != AttrKind::Inner
866+
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next)
867+
.is_none();
868+
let annotated_item_kind =
869+
if is_trailing_outer_attr { None } else { Some(attached.kind()) };
870+
Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
871+
};
872+
830873
// Infer the path kind
831874
let parent = path.syntax().parent()?;
832875
let kind = match_ast! {
833-
match parent {
834-
ast::PathType(it) => make_path_kind_type(it.into()),
835-
ast::PathExpr(it) => {
836-
if let Some(p) = it.syntax().parent() {
837-
if ast::ExprStmt::can_cast(p.kind()) {
838-
if let Some(kind) = inbetween_body_and_decl_check(p) {
839-
return Some(make_res(NameRefKind::Keyword(kind)));
840-
}
876+
match parent {
877+
ast::PathType(it) => make_path_kind_type(it.into()),
878+
ast::PathExpr(it) => {
879+
if let Some(p) = it.syntax().parent() {
880+
if ast::ExprStmt::can_cast(p.kind()) {
881+
if let Some(kind) = inbetween_body_and_decl_check(p) {
882+
return Some(make_res(NameRefKind::Keyword(kind)));
841883
}
842884
}
885+
}
843886

844-
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
887+
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
845888

846-
make_path_kind_expr(it.into())
847-
},
848-
ast::TupleStructPat(it) => {
849-
path_ctx.has_call_parens = true;
850-
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
851-
},
852-
ast::RecordPat(it) => {
853-
path_ctx.has_call_parens = true;
854-
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
855-
},
856-
ast::PathPat(it) => {
857-
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
858-
},
859-
ast::MacroCall(it) => {
860-
// A macro call in this position is usually a result of parsing recovery, so check that
861-
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
862-
return Some(make_res(NameRefKind::Keyword(kind)));
863-
}
889+
make_path_kind_expr(it.into())
890+
},
891+
ast::TupleStructPat(it) => {
892+
path_ctx.has_call_parens = true;
893+
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
894+
},
895+
ast::RecordPat(it) => {
896+
path_ctx.has_call_parens = true;
897+
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
898+
},
899+
ast::PathPat(it) => {
900+
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
901+
},
902+
ast::MacroCall(it) => {
903+
// A macro call in this position is usually a result of parsing recovery, so check that
904+
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
905+
return Some(make_res(NameRefKind::Keyword(kind)));
906+
}
864907

865-
path_ctx.has_macro_bang = it.excl_token().is_some();
866-
let parent = it.syntax().parent()?;
867-
// Any path in an item list will be treated as a macro call by the parser
868-
match_ast! {
869-
match parent {
870-
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
871-
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
872-
ast::MacroType(ty) => make_path_kind_type(ty.into()),
873-
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
874-
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
875-
Some(it) => match_ast! {
876-
match it {
877-
ast::Trait(_) => ItemListKind::Trait,
878-
ast::Impl(it) => if it.trait_().is_some() {
879-
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
880-
} else {
881-
ItemListKind::Impl
882-
},
883-
_ => return None
884-
}
885-
},
886-
None => return None,
887-
} },
888-
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
889-
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
890-
_ => return None,
891-
}
892-
}
893-
},
894-
ast::Meta(meta) => {
895-
let attr = meta.parent_attr()?;
896-
let kind = attr.kind();
897-
let attached = attr.syntax().parent()?;
898-
let is_trailing_outer_attr = kind != AttrKind::Inner
899-
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
900-
let annotated_item_kind = if is_trailing_outer_attr {
901-
None
902-
} else {
903-
Some(attached.kind())
904-
};
905-
PathKind::Attr {
906-
attr_ctx: AttrCtx {
907-
kind,
908-
annotated_item_kind,
909-
}
908+
kind_macro_call(it)?
909+
},
910+
ast::Meta(meta) => make_path_kind_attr(meta)?,
911+
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
912+
ast::UseTree(_) => PathKind::Use,
913+
// completing inside a qualifier
914+
ast::Path(parent) => {
915+
path_ctx.parent = Some(parent.clone());
916+
let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
917+
match_ast! {
918+
match parent {
919+
ast::PathType(it) => make_path_kind_type(it.into()),
920+
ast::PathExpr(it) => {
921+
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
922+
923+
make_path_kind_expr(it.into())
924+
},
925+
ast::TupleStructPat(it) => {
926+
path_ctx.has_call_parens = true;
927+
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
928+
},
929+
ast::RecordPat(it) => {
930+
path_ctx.has_call_parens = true;
931+
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
932+
},
933+
ast::PathPat(it) => {
934+
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
935+
},
936+
ast::MacroCall(it) => {
937+
kind_macro_call(it)?
938+
},
939+
ast::Meta(meta) => make_path_kind_attr(meta)?,
940+
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
941+
ast::UseTree(_) => PathKind::Use,
942+
_ => return None,
910943
}
911-
},
912-
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
913-
ast::UseTree(_) => PathKind::Use,
914-
_ => return None,
915-
944+
}
945+
},
946+
_ => return None,
916947
}
917948
};
918949

0 commit comments

Comments
 (0)