1 //! See `CompletionContext` structure.
5 use base_db::SourceDatabaseExt;
7 HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
10 active_parameter::ActiveParameter,
11 base_db::{FilePosition, SourceDatabase},
12 famous_defs::FamousDefs,
13 FxHashMap, FxHashSet, RootDatabase,
16 algo::{find_node_at_offset, non_trivia_sibling},
17 ast::{self, AttrKind, HasArgList, HasName, NameOrNameRef},
18 match_ast, AstNode, AstToken, Direction, NodeOrToken,
19 SyntaxKind::{self, *},
20 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
26 determine_location, is_in_loop_body, is_in_token_of_for_loop, previous_token,
32 const COMPLETION_MARKER: &str = "intellijRulezz";
34 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
35 pub(crate) enum PatternRefutability {
40 pub(crate) enum Visible {
46 #[derive(Clone, Debug, PartialEq, Eq)]
47 pub(super) enum PathKind {
52 ref_expr_parent: Option<ast::RefExpr>,
55 in_tuple_struct: bool,
59 annotated_item_kind: Option<SyntaxKind>,
62 /// Path in item position, that is inside an (Assoc)ItemList
73 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
74 pub(super) enum ItemListKind {
83 #[derive(Debug, Default)]
84 pub(super) struct QualifierCtx {
85 pub(super) unsafe_tok: Option<SyntaxToken>,
86 pub(super) vis_node: Option<ast::Visibility>,
90 pub(super) fn none(&self) -> bool {
91 self.unsafe_tok.is_none() && self.vis_node.is_none()
96 pub(crate) struct PathCompletionCtx {
97 /// If this is a call with () already there (or {} in case of record patterns)
98 pub(super) has_call_parens: bool,
99 /// If this has a macro call bang !
100 pub(super) has_macro_bang: bool,
101 /// Whether this path stars with a `::`.
102 pub(super) is_absolute_path: bool,
103 /// The qualifier of the current path if it exists.
104 pub(super) qualifier: Option<PathQualifierCtx>,
105 /// The parent of the path we are completing.
106 pub(super) parent: Option<ast::Path>,
107 pub(super) kind: PathKind,
108 /// Whether the path segment has type args or not.
109 pub(super) has_type_args: bool,
112 impl PathCompletionCtx {
113 pub(super) fn is_trivial_path(&self) -> bool {
117 has_call_parens: false,
118 has_macro_bang: false,
119 is_absolute_path: false,
122 has_type_args: false,
130 pub(crate) struct PathQualifierCtx {
131 pub(crate) path: ast::Path,
132 pub(crate) resolution: Option<PathResolution>,
133 /// Whether this path consists solely of `super` segments
134 pub(crate) is_super_chain: bool,
135 /// Whether the qualifier comes from a use tree parent or not
136 pub(crate) use_tree_parent: bool,
138 pub(crate) is_infer_qualifier: bool,
142 pub(super) struct PatternContext {
143 pub(super) refutability: PatternRefutability,
144 pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>,
145 pub(super) has_type_ascription: bool,
146 pub(super) parent_pat: Option<ast::Pat>,
147 pub(super) ref_token: Option<SyntaxToken>,
148 pub(super) mut_token: Option<SyntaxToken>,
149 /// The record pattern this name or ref is a field of
150 pub(super) record_pat: Option<ast::RecordPat>,
154 pub(super) struct LifetimeContext {
155 pub(super) lifetime: Option<ast::Lifetime>,
156 pub(super) kind: LifetimeKind,
160 pub(super) enum LifetimeKind {
161 LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
168 pub(super) struct NameContext {
170 pub(super) name: Option<ast::Name>,
171 pub(super) kind: NameKind,
176 pub(super) enum NameKind {
199 pub(super) struct NameRefContext {
200 /// NameRef syntax in the original file
201 pub(super) nameref: Option<ast::NameRef>,
202 // FIXME: these fields are actually disjoint -> enum
203 pub(super) dot_access: Option<DotAccess>,
204 pub(super) path_ctx: Option<PathCompletionCtx>,
205 /// Position where we are only interested in keyword completions
206 pub(super) keyword: Option<ast::Item>,
207 /// The record expression this nameref is a field of
208 pub(super) record_expr: Option<(ast::RecordExpr, bool)>,
212 pub(super) enum IdentContext {
214 NameRef(NameRefContext),
215 Lifetime(LifetimeContext),
216 /// Original token, fake token
218 original: ast::String,
219 expanded: Option<ast::String>,
222 fake_attribute_under_caret: Option<ast::Attr>,
227 pub(super) struct DotAccess {
228 pub(super) receiver: Option<ast::Expr>,
229 pub(super) receiver_ty: Option<TypeInfo>,
230 pub(super) kind: DotAccessKind,
234 pub(super) enum DotAccessKind {
236 /// True if the receiver is an integer and there is no ident in the original file after it yet
238 receiver_is_ambiguous_float_literal: bool,
245 #[derive(Clone, Debug, PartialEq, Eq)]
246 pub(crate) enum ParamKind {
248 Closure(ast::ClosureExpr),
251 /// `CompletionContext` is created early during completion to figure out, where
252 /// exactly is the cursor, syntax-wise.
254 pub(crate) struct CompletionContext<'a> {
255 pub(super) sema: Semantics<'a, RootDatabase>,
256 pub(super) scope: SemanticsScope<'a>,
257 pub(super) db: &'a RootDatabase,
258 pub(super) config: &'a CompletionConfig,
259 pub(super) position: FilePosition,
261 /// The token before the cursor, in the original file.
262 pub(super) original_token: SyntaxToken,
263 /// The token before the cursor, in the macro-expanded file.
264 pub(super) token: SyntaxToken,
265 /// The crate of the current file.
266 pub(super) krate: hir::Crate,
267 /// The module of the `scope`.
268 pub(super) module: hir::Module,
270 /// The expected name of what we are completing.
271 /// This is usually the parameter name of the function argument we are completing.
272 pub(super) expected_name: Option<NameOrNameRef>,
273 /// The expected type of what we are completing.
274 pub(super) expected_type: Option<Type>,
276 /// The parent function of the cursor position if it exists.
277 pub(super) function_def: Option<ast::Fn>,
278 /// The parent impl of the cursor position if it exists.
279 pub(super) impl_def: Option<ast::Impl>,
280 /// Are we completing inside a let statement with a missing semicolon?
281 pub(super) incomplete_let: bool,
283 pub(super) completion_location: Option<ImmediateLocation>,
284 pub(super) previous_token: Option<SyntaxToken>,
286 pub(super) ident_ctx: IdentContext,
288 pub(super) pattern_ctx: Option<PatternContext>,
289 pub(super) qualifier_ctx: QualifierCtx,
291 pub(super) existing_derives: FxHashSet<hir::Macro>,
293 pub(super) locals: FxHashMap<Name, Local>,
296 impl<'a> CompletionContext<'a> {
297 /// The range of the identifier that is being completed.
298 pub(crate) fn source_range(&self) -> TextRange {
299 // check kind of macro-expanded token, but use range of original token
300 let kind = self.token.kind();
303 // assume we are completing a lifetime but the user has only typed the '
304 cov_mark::hit!(completes_if_lifetime_without_idents);
305 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
307 IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
308 _ if kind.is_keyword() => self.original_token.text_range(),
309 _ => TextRange::empty(self.position.offset),
313 pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
314 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
317 pub(crate) fn famous_defs(&self) -> FamousDefs {
318 FamousDefs(&self.sema, self.krate)
321 pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> {
322 match &self.ident_ctx {
323 IdentContext::NameRef(it) => Some(it),
328 pub(super) fn name_ctx(&self) -> Option<&NameContext> {
329 match &self.ident_ctx {
330 IdentContext::Name(it) => Some(it),
335 pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> {
336 match &self.ident_ctx {
337 IdentContext::Lifetime(it) => Some(it),
342 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
343 match self.nameref_ctx() {
344 Some(NameRefContext { dot_access: Some(DotAccess { receiver, .. }), .. }) => {
351 pub(crate) fn has_dot_receiver(&self) -> bool {
352 self.dot_receiver().is_some()
355 // FIXME: This shouldn't exist
356 pub(crate) fn expects_generic_arg(&self) -> bool {
357 matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
360 pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
361 self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref())
364 pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
365 self.path_context().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
368 /// Checks if an item is visible and not `doc(hidden)` at the completion site.
369 pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
371 I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
373 self.is_visible_impl(&item.visibility(self.db), &item.attrs(self.db), item.krate(self.db))
376 pub(crate) fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
377 if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
378 return self.is_doc_hidden(&attrs, krate);
384 /// Check if an item is `#[doc(hidden)]`.
385 pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
386 let attrs = item.attrs(self.db);
387 let krate = item.krate(self.db);
388 match (attrs, krate) {
389 (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
393 /// Whether the given trait is an operator trait or not.
394 pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
395 match trait_.attrs(self.db).lang() {
396 Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
401 /// Returns the traits in scope, with the [`Drop`] trait removed.
402 pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
403 let mut traits_in_scope = self.scope.visible_traits();
404 if let Some(drop) = self.famous_defs().core_ops_Drop() {
405 traits_in_scope.0.remove(&drop.into());
410 /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
411 pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
412 let _p = profile::span("CompletionContext::process_all_names");
413 self.scope.process_all_names(&mut |name, def| {
414 if self.is_scope_def_hidden(def) {
422 pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
423 let _p = profile::span("CompletionContext::process_all_names_raw");
424 self.scope.process_all_names(&mut |name, def| f(name, def));
429 vis: &hir::Visibility,
431 defining_crate: hir::Crate,
433 if !vis.is_visible_from(self.db, self.module.into()) {
434 if !self.config.enable_private_editable {
437 // If the definition location is editable, also show private items
438 let root_file = defining_crate.root_file(self.db);
439 let source_root_id = self.db.file_source_root(root_file);
440 let is_editable = !self.db.source_root(source_root_id).is_library;
441 return if is_editable { Visible::Editable } else { Visible::No };
444 if self.is_doc_hidden(attrs, defining_crate) {
451 fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
452 // `doc(hidden)` items are only completed within the defining crate.
453 self.krate != defining_crate && attrs.has_doc_hidden()
457 // CompletionContext construction
458 impl<'a> CompletionContext<'a> {
460 db: &'a RootDatabase,
461 position @ FilePosition { file_id, offset }: FilePosition,
462 config: &'a CompletionConfig,
463 ) -> Option<CompletionContext<'a>> {
464 let _p = profile::span("CompletionContext::new");
465 let sema = Semantics::new(db);
467 let original_file = sema.parse(file_id);
469 // Insert a fake ident to get a valid parse tree. We will use this file
470 // to determine context, though the original_file will be used for
471 // actual completion.
472 let file_with_fake_ident = {
473 let parse = db.parse(file_id);
474 let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
475 parse.reparse(&edit).tree()
477 let fake_ident_token =
478 file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
480 let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
481 let token = sema.descend_into_macros_single(original_token.clone());
482 let scope = sema.scope_at_offset(&token.parent()?, offset)?;
483 let krate = scope.krate();
484 let module = scope.module();
486 let mut locals = FxHashMap::default();
487 scope.process_all_names(&mut |name, scope| {
488 if let ScopeDef::Local(local) = scope {
489 locals.insert(name, local);
493 let mut ctx = CompletionContext {
507 incomplete_let: false,
508 completion_location: None,
509 previous_token: None,
510 // dummy value, will be overwritten
511 ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
513 qualifier_ctx: Default::default(),
514 existing_derives: Default::default(),
518 original_file.syntax().clone(),
519 file_with_fake_ident.syntax().clone(),
526 /// Expand attributes and macro calls at the current cursor position for both the original file
527 /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
528 /// and speculative states stay in sync.
531 mut original_file: SyntaxNode,
532 mut speculative_file: SyntaxNode,
533 mut offset: TextSize,
534 mut fake_ident_token: SyntaxToken,
536 let _p = profile::span("CompletionContext::expand_and_fill");
537 let mut derive_ctx = None;
541 |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
542 let ancestor_items = iter::successors(
544 find_node_at_offset::<ast::Item>(&original_file, offset),
545 find_node_at_offset::<ast::Item>(&speculative_file, offset),
547 |(a, b)| parent_item(a).zip(parent_item(b)),
550 // first try to expand attributes as these are always the outermost macro calls
551 'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
553 self.sema.expand_attr_macro(&actual_item),
554 self.sema.speculative_expand_attr_macro(
556 &item_with_fake_ident,
557 fake_ident_token.clone(),
560 // maybe parent items have attributes, so continue walking the ancestors
561 (None, None) => continue 'ancestors,
562 // successful expansions
563 (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
564 let new_offset = fake_mapped_token.text_range().start();
565 if new_offset > actual_expansion.text_range().end() {
566 // offset outside of bounds from the original expansion,
567 // stop here to prevent problems from happening
570 original_file = actual_expansion;
571 speculative_file = fake_expansion;
572 fake_ident_token = fake_mapped_token;
576 // exactly one expansion failed, inconsistent state so stop expanding completely
577 _ => break 'expansion,
581 // No attributes have been expanded, so look for macro_call! token trees or derive token trees
582 let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
584 None => break 'expansion,
586 let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
588 None => break 'expansion,
591 // Expand pseudo-derive expansion
592 if let (Some(orig_attr), Some(spec_attr)) = (
593 orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
594 spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
596 if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
597 self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
598 self.sema.speculative_expand_derive_as_pseudo_attr_macro(
601 fake_ident_token.clone(),
607 fake_mapped_token.text_range().start(),
611 // at this point we won't have any more successful expansions, so stop
615 // Expand fn-like macro calls
616 if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
617 orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
618 spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
620 let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
622 macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
624 // inconsistent state, stop expanding
625 if mac_call_path0 != mac_call_path1 {
628 let speculative_args = match macro_call_with_fake_ident.token_tree() {
630 None => break 'expansion,
634 self.sema.expand(&actual_macro_call),
635 self.sema.speculative_expand(
638 fake_ident_token.clone(),
641 // successful expansions
642 (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
643 let new_offset = fake_mapped_token.text_range().start();
644 if new_offset > actual_expansion.text_range().end() {
645 // offset outside of bounds from the original expansion,
646 // stop here to prevent problems from happening
649 original_file = actual_expansion;
650 speculative_file = fake_expansion;
651 fake_ident_token = fake_mapped_token;
655 // at least on expansion failed, we won't have anything to expand from this point
656 // onwards so break out
657 _ => break 'expansion,
661 // none of our states have changed so stop the loop
665 self.fill(&original_file, speculative_file, offset, derive_ctx)
668 /// Calculate the expected type and name of the cursor position.
669 fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
670 let mut node = match self.token.parent() {
672 None => return (None, None),
677 ast::LetStmt(it) => {
678 cov_mark::hit!(expected_type_let_with_leading_char);
679 cov_mark::hit!(expected_type_let_without_leading_char);
681 .and_then(|pat| self.sema.type_of_pat(&pat))
682 .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)))
683 .map(TypeInfo::original);
684 let name = match it.pat() {
685 Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
686 Some(_) | None => None,
691 ast::LetExpr(it) => {
692 cov_mark::hit!(expected_type_if_let_without_leading_char);
694 .and_then(|pat| self.sema.type_of_pat(&pat))
695 .or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
696 .map(TypeInfo::original);
700 cov_mark::hit!(expected_type_fn_param);
701 ActiveParameter::at_token(
705 let name = ap.ident().map(NameOrNameRef::Name);
706 let ty = if has_ref(&self.token) {
707 cov_mark::hit!(expected_type_fn_param_ref);
714 .unwrap_or((None, None))
716 ast::RecordExprFieldList(it) => {
717 // wouldn't try {} be nice...
719 if self.token.kind() == T![..]
720 || self.token.prev_token().map(|t| t.kind()) == Some(T![..])
722 cov_mark::hit!(expected_type_struct_func_update);
723 let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
724 let ty = self.sema.type_of_expr(&record_expr.into())?;
730 cov_mark::hit!(expected_type_struct_field_without_leading_char);
731 let expr_field = self.token.prev_sibling_or_token()?
733 .and_then(ast::RecordExprField::cast)?;
734 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
737 expr_field.field_name().map(NameOrNameRef::NameRef),
740 })().unwrap_or((None, None))
742 ast::RecordExprField(it) => {
743 if let Some(expr) = it.expr() {
744 cov_mark::hit!(expected_type_struct_field_with_leading_char);
746 self.sema.type_of_expr(&expr).map(TypeInfo::original),
747 it.field_name().map(NameOrNameRef::NameRef),
750 cov_mark::hit!(expected_type_struct_field_followed_by_comma);
751 let ty = self.sema.resolve_record_field(&it)
752 .map(|(_, _, ty)| ty);
755 it.field_name().map(NameOrNameRef::NameRef),
760 // match foo { ..., pat => $0 }
761 ast::MatchExpr(it) => {
762 let ty = if self.previous_token_is(T![=>]) {
763 // match foo { ..., pat => $0 }
764 cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
765 cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
766 self.sema.type_of_expr(&it.into())
769 cov_mark::hit!(expected_type_match_arm_without_leading_char);
770 it.expr().and_then(|e| self.sema.type_of_expr(&e))
771 }.map(TypeInfo::original);
775 let ty = it.condition()
776 .and_then(|e| self.sema.type_of_expr(&e))
777 .map(TypeInfo::original);
780 ast::IdentPat(it) => {
781 cov_mark::hit!(expected_type_if_let_with_leading_char);
782 cov_mark::hit!(expected_type_match_arm_with_leading_char);
783 let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
787 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
788 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
789 let def = self.sema.to_def(&it);
790 (def.map(|def| def.ret_type(self.db)), None)
792 ast::ClosureExpr(it) => {
793 let ty = self.sema.type_of_expr(&it.into());
794 ty.and_then(|ty| ty.original.as_callable(self.db))
795 .map(|c| (Some(c.return_type()), None))
796 .unwrap_or((None, None))
798 ast::ParamList(_) => (None, None),
799 ast::Stmt(_) => (None, None),
800 ast::Item(_) => (None, None),
802 match node.parent() {
807 None => (None, None),
815 /// Fill the completion context, this is what does semantic reasoning about the surrounding context
816 /// of the completion location.
819 original_file: &SyntaxNode,
820 file_with_fake_ident: SyntaxNode,
822 derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
824 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?;
825 let syntax_element = NodeOrToken::Token(fake_ident_token);
826 if is_in_token_of_for_loop(syntax_element.clone()) {
828 // there is nothing to complete here except `in` keyword
829 // don't bother populating the context
830 // FIXME: the completion calculations should end up good enough
831 // such that this special case becomes unnecessary
835 self.previous_token = previous_token(syntax_element.clone());
837 self.incomplete_let =
838 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
839 it.syntax().text_range().end() == syntax_element.text_range().end()
842 (self.expected_type, self.expected_name) = self.expected_type_and_name();
844 // Overwrite the path kind for derives
845 if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
846 self.existing_derives = self
848 .resolve_derive_macro(&origin_attr)
854 if let Some(ast::NameLike::NameRef(name_ref)) =
855 find_node_at_offset(&file_with_fake_ident, offset)
857 let parent = name_ref.syntax().parent()?;
858 let (mut nameref_ctx, _, _) =
859 Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
860 if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
861 path_ctx.kind = PathKind::Derive;
863 self.ident_ctx = IdentContext::NameRef(nameref_ctx);
869 let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
872 if let Some(original) = ast::String::cast(self.original_token.clone()) {
873 self.ident_ctx = IdentContext::String {
875 expanded: ast::String::cast(self.token.clone()),
878 // Fix up trailing whitespace problem
880 let token = if self.token.kind() == SyntaxKind::WHITESPACE {
881 self.previous_token.as_ref()?
885 let p = token.parent()?;
886 if p.kind() == SyntaxKind::TOKEN_TREE
887 && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
889 self.ident_ctx = IdentContext::UnexpandedAttrTT {
890 fake_attribute_under_caret: syntax_element
892 .find_map(ast::Attr::cast),
901 self.completion_location =
902 determine_location(&self.sema, original_file, offset, &name_like);
905 .token_ancestors_with_macros(self.token.clone())
906 .take_while(|it| it.kind() != SOURCE_FILE)
907 .filter_map(ast::Item::cast)
909 .find_map(|it| match it {
910 ast::Item::Impl(impl_) => Some(impl_),
913 self.function_def = self
915 .token_ancestors_with_macros(self.token.clone())
916 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
917 .filter_map(ast::Item::cast)
919 .find_map(|it| match it {
920 ast::Item::Fn(fn_) => Some(fn_),
925 ast::NameLike::Lifetime(lifetime) => {
926 self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime(
932 ast::NameLike::NameRef(name_ref) => {
933 let parent = name_ref.syntax().parent()?;
934 let (nameref_ctx, pat_ctx, qualifier_ctx) =
935 Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone());
937 self.qualifier_ctx = qualifier_ctx;
938 self.ident_ctx = IdentContext::NameRef(nameref_ctx);
939 self.pattern_ctx = pat_ctx;
941 ast::NameLike::Name(name) => {
942 let (name_ctx, pat_ctx) = Self::classify_name(&self.sema, original_file, name)?;
943 self.pattern_ctx = pat_ctx;
944 self.ident_ctx = IdentContext::Name(name_ctx);
950 fn classify_lifetime(
951 _sema: &Semantics<RootDatabase>,
952 original_file: &SyntaxNode,
953 lifetime: ast::Lifetime,
954 ) -> Option<LifetimeContext> {
955 let parent = lifetime.syntax().parent()?;
956 if parent.kind() == ERROR {
960 let kind = match_ast! {
962 ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
963 is_decl: param.lifetime().as_ref() == Some(&lifetime),
966 ast::BreakExpr(_) => LifetimeKind::LabelRef,
967 ast::ContinueExpr(_) => LifetimeKind::LabelRef,
968 ast::Label(_) => LifetimeKind::LabelDef,
969 _ => LifetimeKind::Lifetime,
972 let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start());
974 Some(LifetimeContext { lifetime, kind })
978 _sema: &Semantics<RootDatabase>,
979 original_file: &SyntaxNode,
981 ) -> Option<(NameContext, Option<PatternContext>)> {
982 let parent = name.syntax().parent()?;
983 let mut pat_ctx = None;
984 let kind = match_ast! {
986 ast::Const(_) => NameKind::Const,
987 ast::ConstParam(_) => NameKind::ConstParam,
988 ast::Enum(_) => NameKind::Enum,
989 ast::Fn(_) => NameKind::Function,
990 ast::IdentPat(bind_pat) => {
992 let mut pat_ctx = pattern_context_for(original_file, bind_pat.into());
993 if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
994 pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
1001 ast::MacroDef(_) => NameKind::MacroDef,
1002 ast::MacroRules(_) => NameKind::MacroRules,
1003 ast::Module(module) => NameKind::Module(module),
1004 ast::RecordField(_) => NameKind::RecordField,
1005 ast::Rename(_) => NameKind::Rename,
1006 ast::SelfParam(_) => NameKind::SelfParam,
1007 ast::Static(_) => NameKind::Static,
1008 ast::Struct(_) => NameKind::Struct,
1009 ast::Trait(_) => NameKind::Trait,
1010 ast::TypeAlias(_) => NameKind::TypeAlias,
1011 ast::TypeParam(_) => NameKind::TypeParam,
1012 ast::Union(_) => NameKind::Union,
1013 ast::Variant(_) => NameKind::Variant,
1017 let name = find_node_at_offset(&original_file, name.syntax().text_range().start());
1018 Some((NameContext { name, kind }, pat_ctx))
1021 fn classify_name_ref(
1022 sema: &Semantics<RootDatabase>,
1023 original_file: &SyntaxNode,
1024 name_ref: ast::NameRef,
1026 ) -> (NameRefContext, Option<PatternContext>, QualifierCtx) {
1027 let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
1038 QualifierCtx::default(),
1040 let (nameref_ctx, pattern_ctx, qualifier_ctx) = &mut res;
1042 if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
1043 nameref_ctx.record_expr =
1044 find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
1048 if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
1049 *pattern_ctx = Some(PatternContext {
1051 has_type_ascription: false,
1054 record_pat: find_node_in_file_compensated(
1056 &record_field.parent_record_pat(),
1058 ..pattern_context_for(
1060 record_field.parent_record_pat().clone().into(),
1066 let segment = match_ast! {
1068 ast::PathSegment(segment) => segment,
1069 ast::FieldExpr(field) => {
1070 let receiver = find_in_original_file(field.expr(), original_file);
1071 let receiver_is_ambiguous_float_literal = match &receiver {
1072 Some(ast::Expr::Literal(l)) => matches! {
1074 ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.'))
1078 nameref_ctx.dot_access = Some(DotAccess {
1079 receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
1080 kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
1085 ast::MethodCallExpr(method) => {
1086 let receiver = find_in_original_file(method.receiver(), original_file);
1087 nameref_ctx.dot_access = Some(DotAccess {
1088 receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
1089 kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
1098 let path = segment.parent_path();
1099 let mut path_ctx = PathCompletionCtx {
1100 has_call_parens: false,
1101 has_macro_bang: false,
1102 is_absolute_path: false,
1104 parent: path.parent_path(),
1105 kind: PathKind::Item { kind: ItemListKind::SourceFile },
1106 has_type_args: false,
1109 let is_in_block = |it: &SyntaxNode| {
1112 ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
1116 let mut fill_record_expr = |syn: &SyntaxNode| {
1117 if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
1118 nameref_ctx.record_expr =
1119 find_node_in_file_compensated(original_file, &record_expr).zip(Some(true));
1122 let after_if_expr = |node: SyntaxNode| {
1123 let prev_expr = (|| {
1124 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
1125 ast::ExprStmt::cast(prev_sibling)?.expr()
1127 matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
1130 // We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
1131 // ex. trait Foo $0 {}
1132 // in these cases parser recovery usually kicks in for our inserted identifier, causing it
1133 // to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block
1134 // expression or an item list.
1135 // The following code checks if the body is missing, if it is we either cut off the body
1136 // from the item or it was missing in the first place
1137 let inbetween_body_and_decl_check = |node: SyntaxNode| {
1138 if let Some(NodeOrToken::Node(n)) =
1139 syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev)
1141 if let Some(item) = ast::Item::cast(n) {
1142 let is_inbetween = match &item {
1143 ast::Item::Const(it) => it.body().is_none(),
1144 ast::Item::Enum(it) => it.variant_list().is_none(),
1145 ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
1146 ast::Item::Fn(it) => it.body().is_none(),
1147 ast::Item::Impl(it) => it.assoc_item_list().is_none(),
1148 ast::Item::Module(it) => it.item_list().is_none(),
1149 ast::Item::Static(it) => it.body().is_none(),
1150 ast::Item::Struct(it) => it.field_list().is_none(),
1151 ast::Item::Trait(it) => it.assoc_item_list().is_none(),
1152 ast::Item::TypeAlias(it) => it.ty().is_none(),
1153 ast::Item::Union(it) => it.record_field_list().is_none(),
1164 // Infer the path kind
1165 let kind = path.syntax().parent().and_then(|it| {
1168 ast::PathType(it) => Some(PathKind::Type {
1169 in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind()))
1171 ast::PathExpr(it) => {
1172 if let Some(p) = it.syntax().parent() {
1173 if ast::ExprStmt::can_cast(p.kind()) {
1174 if let Some(kind) = inbetween_body_and_decl_check(p) {
1175 nameref_ctx.keyword = Some(kind);
1181 fill_record_expr(it.syntax());
1183 path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
1184 let in_block_expr = is_in_block(it.syntax());
1185 let in_loop_body = is_in_loop_body(it.syntax());
1186 let after_if_expr = after_if_expr(it.syntax().clone());
1187 let ref_expr_parent = path.as_single_name_ref()
1188 .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
1190 Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent })
1192 ast::TupleStructPat(it) => {
1193 path_ctx.has_call_parens = true;
1194 *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
1197 ast::RecordPat(it) => {
1198 path_ctx.has_call_parens = true;
1199 *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
1202 ast::PathPat(it) => {
1203 *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
1206 ast::MacroCall(it) => {
1207 if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
1208 nameref_ctx.keyword = Some(kind);
1212 path_ctx.has_macro_bang = it.excl_token().is_some();
1213 let parent = it.syntax().parent();
1214 match parent.as_ref().map(|it| it.kind()) {
1215 Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
1216 Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type { in_tuple_struct: false }),
1217 Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
1218 Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
1219 Some(it) => match_ast! {
1221 ast::Trait(_) => ItemListKind::Trait,
1222 ast::Impl(it) => if it.trait_().is_some() {
1223 ItemListKind::TraitImpl
1230 None => return None,
1232 Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
1233 Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
1235 return parent.and_then(ast::MacroExpr::cast).map(|it| {
1236 let in_loop_body = is_in_loop_body(it.syntax());
1237 let in_block_expr = is_in_block(it.syntax());
1238 let after_if_expr = after_if_expr(it.syntax().clone());
1239 fill_record_expr(it.syntax());
1240 let ref_expr_parent = path.as_single_name_ref()
1241 .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
1242 PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }
1247 ast::Meta(meta) => (|| {
1248 let attr = meta.parent_attr()?;
1249 let kind = attr.kind();
1250 let attached = attr.syntax().parent()?;
1251 let is_trailing_outer_attr = kind != AttrKind::Inner
1252 && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
1253 let annotated_item_kind = if is_trailing_outer_attr {
1256 Some(attached.kind())
1258 Some(PathKind::Attr {
1260 annotated_item_kind,
1263 ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
1264 ast::UseTree(_) => Some(PathKind::Use),
1271 Some(kind) => path_ctx.kind = kind,
1274 path_ctx.has_type_args = segment.generic_arg_list().is_some();
1276 if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
1277 if !use_tree_parent {
1278 path_ctx.is_absolute_path =
1279 path.top_path().segment().map_or(false, |it| it.coloncolon_token().is_some());
1284 .and_then(|it| find_node_in_file(original_file, &it))
1285 .map(|it| it.parent_path());
1286 path_ctx.qualifier = path.map(|path| {
1287 let res = sema.resolve_path(&path);
1288 let is_super_chain = iter::successors(Some(path.clone()), |p| p.qualifier())
1289 .all(|p| p.segment().and_then(|s| s.super_token()).is_some());
1292 let is_infer_qualifier = path.qualifier().is_none()
1294 path.segment().and_then(|it| it.kind()),
1295 Some(ast::PathSegmentKind::Type {
1296 type_ref: Some(ast::Type::InferType(_)),
1309 } else if let Some(segment) = path.segment() {
1310 if segment.coloncolon_token().is_some() {
1311 path_ctx.is_absolute_path = true;
1315 if path_ctx.is_trivial_path() {
1316 // fetch the full expression that may have qualifiers attached to it
1317 let top_node = match path_ctx.kind {
1318 PathKind::Expr { in_block_expr: true, .. } => {
1319 parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
1320 let parent = p.parent()?;
1321 if ast::StmtList::can_cast(parent.kind()) {
1323 } else if ast::ExprStmt::can_cast(parent.kind()) {
1330 PathKind::Item { .. } => {
1331 parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
1335 if let Some(top) = top_node {
1336 if let Some(NodeOrToken::Node(error_node)) =
1337 syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev)
1339 if error_node.kind() == SyntaxKind::ERROR {
1340 qualifier_ctx.unsafe_tok = error_node
1341 .children_with_tokens()
1342 .filter_map(NodeOrToken::into_token)
1343 .find(|it| it.kind() == T![unsafe]);
1344 qualifier_ctx.vis_node =
1345 error_node.children().find_map(ast::Visibility::cast);
1349 if let PathKind::Item { .. } = path_ctx.kind {
1350 if qualifier_ctx.none() {
1351 if let Some(t) = top.first_token() {
1352 if let Some(prev) = t
1354 .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev))
1356 if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
1357 // This was inferred to be an item position path, but it seems
1358 // to be part of some other broken node which leaked into an item
1359 // list, so return without setting the path context
1368 nameref_ctx.path_ctx = Some(path_ctx);
1373 fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternContext {
1374 let mut is_param = None;
1375 let (refutability, has_type_ascription) =
1379 .skip_while(|it| ast::Pat::can_cast(it.kind()))
1381 .map_or((PatternRefutability::Irrefutable, false), |node| {
1382 let refutability = match_ast! {
1384 ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
1385 ast::Param(param) => {
1386 let has_type_ascription = param.ty().is_some();
1388 let fake_param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
1389 let param_list = find_node_in_file_compensated(original_file, &fake_param_list)?;
1390 let param_list_owner = param_list.syntax().parent()?;
1391 let kind = match_ast! {
1392 match param_list_owner {
1393 ast::ClosureExpr(closure) => ParamKind::Closure(closure),
1394 ast::Fn(fn_) => ParamKind::Function(fn_),
1398 Some((param_list, param, kind))
1400 return (PatternRefutability::Irrefutable, has_type_ascription)
1402 ast::MatchArm(_) => PatternRefutability::Refutable,
1403 ast::LetExpr(_) => PatternRefutability::Refutable,
1404 ast::ForExpr(_) => PatternRefutability::Irrefutable,
1405 _ => PatternRefutability::Irrefutable,
1408 (refutability, false)
1410 let (ref_token, mut_token) = match &pat {
1411 ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()),
1416 param_ctx: is_param,
1417 has_type_ascription,
1418 parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
1425 fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> {
1426 fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
1427 let range = syntax.text_range().intersect(range)?;
1428 syntax.covering_element(range).ancestors().find_map(N::cast)
1430 x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r))
1433 /// Attempts to find `node` inside `syntax` via `node`'s text range.
1434 fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
1435 let syntax_range = syntax.text_range();
1436 let range = node.syntax().text_range();
1437 let intersection = range.intersect(syntax_range)?;
1438 syntax.covering_element(intersection).ancestors().find_map(N::cast)
1441 /// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
1442 /// for the offset introduced by the fake ident.
1443 /// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
1444 fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
1445 let syntax_range = syntax.text_range();
1446 let range = node.syntax().text_range();
1447 let end = range.end().checked_sub(TextSize::try_from(COMPLETION_MARKER.len()).ok()?)?;
1448 if end < range.start() {
1451 let range = TextRange::new(range.start(), end);
1452 // our inserted ident could cause `range` to be go outside of the original syntax, so cap it
1453 let intersection = range.intersect(syntax_range)?;
1454 syntax.covering_element(intersection).ancestors().find_map(N::cast)
1457 fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
1458 if let Some(qual) = path.qualifier() {
1459 return Some((qual, false));
1461 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
1462 let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
1463 Some((use_tree.path()?, true))
1466 fn has_ref(token: &SyntaxToken) -> bool {
1467 let mut token = token.clone();
1468 for skip in [IDENT, WHITESPACE, T![mut]] {
1469 if token.kind() == skip {
1470 token = match token.prev_token() {
1472 None => return false,
1476 token.kind() == T![&]
1479 const OP_TRAIT_LANG_NAMES: &[&str] = &[
1514 use expect_test::{expect, Expect};
1515 use hir::HirDisplay;
1517 use crate::tests::{position, TEST_CONFIG};
1519 use super::CompletionContext;
1521 fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
1522 let (db, pos) = position(ra_fixture);
1523 let config = TEST_CONFIG;
1524 let completion_context = CompletionContext::new(&db, pos, &config).unwrap();
1526 let ty = completion_context
1528 .map(|t| t.display_test(&db).to_string())
1529 .unwrap_or("?".to_owned());
1531 let name = completion_context
1533 .map_or_else(|| "?".to_owned(), |name| name.to_string());
1535 expect.assert_eq(&format!("ty: {}, name: {}", ty, name));
1539 fn expected_type_let_without_leading_char() {
1540 cov_mark::check!(expected_type_let_without_leading_char);
1541 check_expected_type_and_name(
1547 expect![[r#"ty: u32, name: x"#]],
1552 fn expected_type_let_with_leading_char() {
1553 cov_mark::check!(expected_type_let_with_leading_char);
1554 check_expected_type_and_name(
1560 expect![[r#"ty: u32, name: x"#]],
1565 fn expected_type_let_pat() {
1566 check_expected_type_and_name(
1572 expect![[r#"ty: u32, name: ?"#]],
1574 check_expected_type_and_name(
1580 expect![[r#"ty: u32, name: ?"#]],
1585 fn expected_type_fn_param() {
1586 cov_mark::check!(expected_type_fn_param);
1587 check_expected_type_and_name(
1589 fn foo() { bar($0); }
1592 expect![[r#"ty: u32, name: x"#]],
1594 check_expected_type_and_name(
1596 fn foo() { bar(c$0); }
1599 expect![[r#"ty: u32, name: x"#]],
1604 fn expected_type_fn_param_ref() {
1605 cov_mark::check!(expected_type_fn_param_ref);
1606 check_expected_type_and_name(
1608 fn foo() { bar(&$0); }
1611 expect![[r#"ty: u32, name: x"#]],
1613 check_expected_type_and_name(
1615 fn foo() { bar(&mut $0); }
1616 fn bar(x: &mut u32) {}
1618 expect![[r#"ty: u32, name: x"#]],
1620 check_expected_type_and_name(
1622 fn foo() { bar(& c$0); }
1625 expect![[r#"ty: u32, name: x"#]],
1627 check_expected_type_and_name(
1629 fn foo() { bar(&mut c$0); }
1630 fn bar(x: &mut u32) {}
1632 expect![[r#"ty: u32, name: x"#]],
1634 check_expected_type_and_name(
1636 fn foo() { bar(&c$0); }
1639 expect![[r#"ty: u32, name: x"#]],
1644 fn expected_type_struct_field_without_leading_char() {
1645 cov_mark::check!(expected_type_struct_field_without_leading_char);
1646 check_expected_type_and_name(
1648 struct Foo { a: u32 }
1653 expect![[r#"ty: u32, name: a"#]],
1658 fn expected_type_struct_field_followed_by_comma() {
1659 cov_mark::check!(expected_type_struct_field_followed_by_comma);
1660 check_expected_type_and_name(
1662 struct Foo { a: u32 }
1667 expect![[r#"ty: u32, name: a"#]],
1672 fn expected_type_generic_struct_field() {
1673 check_expected_type_and_name(
1675 struct Foo<T> { a: T }
1676 fn foo() -> Foo<u32> {
1680 expect![[r#"ty: u32, name: a"#]],
1685 fn expected_type_struct_field_with_leading_char() {
1686 cov_mark::check!(expected_type_struct_field_with_leading_char);
1687 check_expected_type_and_name(
1689 struct Foo { a: u32 }
1694 expect![[r#"ty: u32, name: a"#]],
1699 fn expected_type_match_arm_without_leading_char() {
1700 cov_mark::check!(expected_type_match_arm_without_leading_char);
1701 check_expected_type_and_name(
1708 expect![[r#"ty: E, name: ?"#]],
1713 fn expected_type_match_arm_with_leading_char() {
1714 cov_mark::check!(expected_type_match_arm_with_leading_char);
1715 check_expected_type_and_name(
1722 expect![[r#"ty: E, name: ?"#]],
1727 fn expected_type_match_arm_body_without_leading_char() {
1728 cov_mark::check!(expected_type_match_arm_body_without_leading_char);
1729 check_expected_type_and_name(
1734 match E::X { E::X => $0 }
1737 expect![[r#"ty: Foo, name: ?"#]],
1742 fn expected_type_match_body_arm_with_leading_char() {
1743 cov_mark::check!(expected_type_match_arm_body_with_leading_char);
1744 check_expected_type_and_name(
1749 match E::X { E::X => c$0 }
1752 expect![[r#"ty: Foo, name: ?"#]],
1757 fn expected_type_if_let_without_leading_char() {
1758 cov_mark::check!(expected_type_if_let_without_leading_char);
1759 check_expected_type_and_name(
1761 enum Foo { Bar, Baz, Quux }
1768 expect![[r#"ty: Foo, name: ?"#]],
1773 fn expected_type_if_let_with_leading_char() {
1774 cov_mark::check!(expected_type_if_let_with_leading_char);
1775 check_expected_type_and_name(
1777 enum Foo { Bar, Baz, Quux }
1784 expect![[r#"ty: Foo, name: ?"#]],
1789 fn expected_type_fn_ret_without_leading_char() {
1790 cov_mark::check!(expected_type_fn_ret_without_leading_char);
1791 check_expected_type_and_name(
1797 expect![[r#"ty: u32, name: ?"#]],
1802 fn expected_type_fn_ret_with_leading_char() {
1803 cov_mark::check!(expected_type_fn_ret_with_leading_char);
1804 check_expected_type_and_name(
1810 expect![[r#"ty: u32, name: ?"#]],
1815 fn expected_type_fn_ret_fn_ref_fully_typed() {
1816 check_expected_type_and_name(
1822 expect![[r#"ty: u32, name: ?"#]],
1827 fn expected_type_closure_param_return() {
1828 // FIXME: make this work with `|| $0`
1829 check_expected_type_and_name(
1836 fn bar(f: impl FnOnce() -> u32) {}
1838 expect![[r#"ty: u32, name: ?"#]],
1843 fn expected_type_generic_function() {
1844 check_expected_type_and_name(
1852 expect![[r#"ty: u32, name: t"#]],
1857 fn expected_type_generic_method() {
1858 check_expected_type_and_name(
1866 fn bar(self, t: T) {}
1869 expect![[r#"ty: u32, name: t"#]],
1874 fn expected_type_functional_update() {
1875 cov_mark::check!(expected_type_struct_func_update);
1876 check_expected_type_and_name(
1878 struct Foo { field: u32 }
1885 expect![[r#"ty: Foo, name: ?"#]],
1890 fn expected_type_param_pat() {
1891 check_expected_type_and_name(
1893 struct Foo { field: u32 }
1896 expect![[r#"ty: Foo, name: ?"#]],
1898 check_expected_type_and_name(
1900 struct Foo { field: u32 }
1903 // FIXME make this work, currently fails due to pattern recovery eating the `:`
1904 expect![[r#"ty: ?, name: ?"#]],