|
1 | 1 | //! Module responsible for analyzing the code surrounding the cursor for completion.
|
2 | 2 | use std::iter;
|
3 | 3 |
|
4 |
| -use hir::{Semantics, Type, TypeInfo, Variant}; |
| 4 | +use hir::{HasSource, Semantics, Type, TypeInfo, Variant}; |
5 | 5 | use ide_db::{active_parameter::ActiveParameter, RootDatabase};
|
6 | 6 | use syntax::{
|
7 | 7 | algo::{find_node_at_offset, non_trivia_sibling},
|
8 |
| - ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef}, |
| 8 | + ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef}, |
9 | 9 | match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
|
10 | 10 | SyntaxToken, TextRange, TextSize, T,
|
11 | 11 | };
|
@@ -719,6 +719,136 @@ fn classify_name_ref(
|
719 | 719 | None
|
720 | 720 | };
|
721 | 721 |
|
| 722 | + let generic_arg_location = |arg: ast::GenericArg| { |
| 723 | + let mut override_location = None; |
| 724 | + let location = find_opt_node_in_file_compensated( |
| 725 | + sema, |
| 726 | + original_file, |
| 727 | + arg.syntax().parent().and_then(ast::GenericArgList::cast), |
| 728 | + ) |
| 729 | + .map(|args| { |
| 730 | + let mut in_trait = None; |
| 731 | + let param = (|| { |
| 732 | + let parent = args.syntax().parent()?; |
| 733 | + let params = match_ast! { |
| 734 | + match parent { |
| 735 | + ast::PathSegment(segment) => { |
| 736 | + match sema.resolve_path(&segment.parent_path().top_path())? { |
| 737 | + hir::PathResolution::Def(def) => match def { |
| 738 | + hir::ModuleDef::Function(func) => { |
| 739 | + func.source(sema.db)?.value.generic_param_list() |
| 740 | + } |
| 741 | + hir::ModuleDef::Adt(adt) => { |
| 742 | + adt.source(sema.db)?.value.generic_param_list() |
| 743 | + } |
| 744 | + hir::ModuleDef::Variant(variant) => { |
| 745 | + variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() |
| 746 | + } |
| 747 | + hir::ModuleDef::Trait(trait_) => { |
| 748 | + if let ast::GenericArg::AssocTypeArg(arg) = &arg { |
| 749 | + let arg_name = arg.name_ref()?; |
| 750 | + let arg_name = arg_name.text(); |
| 751 | + for item in trait_.items_with_supertraits(sema.db) { |
| 752 | + match item { |
| 753 | + hir::AssocItem::TypeAlias(assoc_ty) => { |
| 754 | + if assoc_ty.name(sema.db).as_str()? == arg_name { |
| 755 | + override_location = Some(TypeLocation::AssocTypeEq); |
| 756 | + return None; |
| 757 | + } |
| 758 | + }, |
| 759 | + hir::AssocItem::Const(const_) => { |
| 760 | + if const_.name(sema.db)?.as_str()? == arg_name { |
| 761 | + override_location = Some(TypeLocation::AssocConstEq); |
| 762 | + return None; |
| 763 | + } |
| 764 | + }, |
| 765 | + _ => (), |
| 766 | + } |
| 767 | + } |
| 768 | + return None; |
| 769 | + } else { |
| 770 | + in_trait = Some(trait_); |
| 771 | + trait_.source(sema.db)?.value.generic_param_list() |
| 772 | + } |
| 773 | + } |
| 774 | + hir::ModuleDef::TraitAlias(trait_) => { |
| 775 | + trait_.source(sema.db)?.value.generic_param_list() |
| 776 | + } |
| 777 | + hir::ModuleDef::TypeAlias(ty_) => { |
| 778 | + ty_.source(sema.db)?.value.generic_param_list() |
| 779 | + } |
| 780 | + _ => None, |
| 781 | + }, |
| 782 | + _ => None, |
| 783 | + } |
| 784 | + }, |
| 785 | + ast::MethodCallExpr(call) => { |
| 786 | + let func = sema.resolve_method_call(&call)?; |
| 787 | + func.source(sema.db)?.value.generic_param_list() |
| 788 | + }, |
| 789 | + ast::AssocTypeArg(arg) => { |
| 790 | + let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; |
| 791 | + match sema.resolve_path(&trait_.parent_path().top_path())? { |
| 792 | + hir::PathResolution::Def(def) => match def { |
| 793 | + hir::ModuleDef::Trait(trait_) => { |
| 794 | + let arg_name = arg.name_ref()?; |
| 795 | + let arg_name = arg_name.text(); |
| 796 | + let trait_items = trait_.items_with_supertraits(sema.db); |
| 797 | + let assoc_ty = trait_items.iter().find_map(|item| match item { |
| 798 | + hir::AssocItem::TypeAlias(assoc_ty) => { |
| 799 | + (assoc_ty.name(sema.db).as_str()? == arg_name) |
| 800 | + .then_some(assoc_ty) |
| 801 | + }, |
| 802 | + _ => None, |
| 803 | + })?; |
| 804 | + assoc_ty.source(sema.db)?.value.generic_param_list() |
| 805 | + } |
| 806 | + _ => None, |
| 807 | + }, |
| 808 | + _ => None, |
| 809 | + } |
| 810 | + }, |
| 811 | + _ => None, |
| 812 | + } |
| 813 | + }?; |
| 814 | + // Determine the index of the argument in the `GenericArgList` and match it with |
| 815 | + // the corresponding parameter in the `GenericParamList`. Since lifetime parameters |
| 816 | + // are often omitted, ignore them for the purposes of matching the argument with |
| 817 | + // its parameter unless a lifetime argument is provided explicitly. That is, for |
| 818 | + // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`. |
| 819 | + // FIXME: This operates on the syntax tree and will produce incorrect results when |
| 820 | + // generic parameters are disabled by `#[cfg]` directives. It should operate on the |
| 821 | + // HIR, but the functionality necessary to do so is not exposed at the moment. |
| 822 | + let mut explicit_lifetime_arg = false; |
| 823 | + let arg_idx = arg |
| 824 | + .syntax() |
| 825 | + .siblings(Direction::Prev) |
| 826 | + // Skip the node itself |
| 827 | + .skip(1) |
| 828 | + .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true }) |
| 829 | + .count(); |
| 830 | + let param_idx = if explicit_lifetime_arg { |
| 831 | + arg_idx |
| 832 | + } else { |
| 833 | + // Lifetimes parameters always precede type and generic parameters, |
| 834 | + // so offset the argument index by the total number of lifetime params |
| 835 | + arg_idx + params.lifetime_params().count() |
| 836 | + }; |
| 837 | + params.generic_params().nth(param_idx) |
| 838 | + })(); |
| 839 | + (args, in_trait, param) |
| 840 | + }); |
| 841 | + let (arg_list, of_trait, corresponding_param) = match location { |
| 842 | + Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param), |
| 843 | + _ => (None, None, None), |
| 844 | + }; |
| 845 | + override_location.unwrap_or(TypeLocation::GenericArg { |
| 846 | + args: arg_list, |
| 847 | + of_trait, |
| 848 | + corresponding_param, |
| 849 | + }) |
| 850 | + }; |
| 851 | + |
722 | 852 | let type_location = |node: &SyntaxNode| {
|
723 | 853 | let parent = node.parent()?;
|
724 | 854 | let res = match_ast! {
|
@@ -774,9 +904,12 @@ fn classify_name_ref(
|
774 | 904 | ast::TypeBound(_) => TypeLocation::TypeBound,
|
775 | 905 | // is this case needed?
|
776 | 906 | ast::TypeBoundList(_) => TypeLocation::TypeBound,
|
777 |
| - ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), |
| 907 | + ast::GenericArg(it) => generic_arg_location(it), |
778 | 908 | // is this case needed?
|
779 |
| - ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), |
| 909 | + ast::GenericArgList(it) => { |
| 910 | + let args = find_opt_node_in_file_compensated(sema, original_file, Some(it)); |
| 911 | + TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None } |
| 912 | + }, |
780 | 913 | ast::TupleField(_) => TypeLocation::TupleField,
|
781 | 914 | _ => return None,
|
782 | 915 | }
|
|
0 commit comments