From 91781c7ce8201b28afd56b4e35eba47e076a8498 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 31 Jul 2020 18:29:29 +0200 Subject: Rename TypeArgList -> GenericArgList --- crates/ra_ssr/src/resolving.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index 78d456546..c2fd3b905 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -217,7 +217,7 @@ fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode { fn path_contains_type_arguments(path: Option) -> bool { if let Some(path) = path { if let Some(segment) = path.segment() { - if segment.type_arg_list().is_some() { + if segment.generic_arg_list().is_some() { mark::hit!(type_arguments_within_path); return true; } -- cgit v1.2.3 From 98181087984157e27faba0b969e384f3c62c39d5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 31 Jul 2020 20:09:09 +0200 Subject: Rename BindPat -> IdentPat --- crates/ra_ssr/src/resolving.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index c2fd3b905..6f62000f4 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -198,7 +198,7 @@ fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode { return n; } } - SyntaxKind::LET_STMT | SyntaxKind::BIND_PAT => { + SyntaxKind::LET_STMT | SyntaxKind::IDENT_PAT => { if let Some(next) = node.next_sibling() { return pick_node_for_resolution(next); } -- cgit v1.2.3 From 21d2cebcf1a417bce72da98aa638a20235c050db Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Sat, 1 Aug 2020 12:43:10 +1000 Subject: SSR: Matching trait associated constants, types and functions This fixes matching of things like `HashMap::default()` by resolving `HashMap` instead of `default` (which resolves to `Default::default`). Same for associated constants and types that are part of a trait implementation. However, we still don't support matching calls to trait methods. --- crates/ra_ssr/src/resolving.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index 6f62000f4..d5b65eaac 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -5,7 +5,7 @@ use crate::{parsing, SsrError}; use parsing::Placeholder; use ra_db::FilePosition; use ra_syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use test_utils::mark; pub(crate) struct ResolutionScope<'db> { @@ -111,8 +111,10 @@ impl Resolver<'_, '_> { .resolution_scope .resolve_path(&path) .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?; - resolved_paths.insert(node, ResolvedPath { resolution, depth }); - return Ok(()); + if self.ok_to_use_path_resolution(&resolution) { + resolved_paths.insert(node, ResolvedPath { resolution, depth }); + return Ok(()); + } } } for node in node.children() { @@ -136,6 +138,27 @@ impl Resolver<'_, '_> { } false } + + fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool { + match resolution { + hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => { + if function.has_self_param(self.resolution_scope.scope.db) { + // If we don't use this path resolution, then we won't be able to match method + // calls. e.g. `Foo::bar($s)` should match `x.bar()`. + true + } else { + mark::hit!(replace_associated_trait_default_function_call); + false + } + } + hir::PathResolution::AssocItem(_) => { + // Not a function. Could be a constant or an associated type. + mark::hit!(replace_associated_trait_constant); + false + } + _ => true, + } + } } impl<'db> ResolutionScope<'db> { @@ -176,7 +199,7 @@ impl<'db> ResolutionScope<'db> { adt.ty(self.scope.db).iterate_path_candidates( self.scope.db, self.scope.module()?.krate(), - &FxHashSet::default(), + &self.scope.traits_in_scope(), Some(hir_path.segments().last()?.name), |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)), ) -- cgit v1.2.3 From 6bbeffc8c56893548f5667844f59ce5a76f9fd98 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Sat, 1 Aug 2020 17:41:42 +1000 Subject: SSR: Allow `self` in patterns. It's now consistent with other variables in that if the pattern references self, only the `self` in scope where the rule is invoked will be accepted. Since `self` doesn't work the same as other paths, this is implemented by restricting the search to just the current function. Prior to this change (since path resolution was implemented), having self in a pattern would just result in no matches. --- crates/ra_ssr/src/resolving.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index 6f62000f4..0ac929bd5 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -11,6 +11,7 @@ use test_utils::mark; pub(crate) struct ResolutionScope<'db> { scope: hir::SemanticsScope<'db>, hygiene: hir::Hygiene, + node: SyntaxNode, } pub(crate) struct ResolvedRule { @@ -25,6 +26,7 @@ pub(crate) struct ResolvedPattern { // Paths in `node` that we've resolved. pub(crate) resolved_paths: FxHashMap, pub(crate) ufcs_function_calls: FxHashMap, + pub(crate) contains_self: bool, } pub(crate) struct ResolvedPath { @@ -68,6 +70,7 @@ struct Resolver<'a, 'db> { impl Resolver<'_, '_> { fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result { + use ra_syntax::{SyntaxElement, T}; let mut resolved_paths = FxHashMap::default(); self.resolve(pattern.clone(), 0, &mut resolved_paths)?; let ufcs_function_calls = resolved_paths @@ -85,11 +88,17 @@ impl Resolver<'_, '_> { None }) .collect(); + let contains_self = + pattern.descendants_with_tokens().any(|node_or_token| match node_or_token { + SyntaxElement::Token(t) => t.kind() == T![self], + _ => false, + }); Ok(ResolvedPattern { node: pattern, resolved_paths, placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), ufcs_function_calls, + contains_self, }) } @@ -101,6 +110,10 @@ impl Resolver<'_, '_> { ) -> Result<(), SsrError> { use ra_syntax::ast::AstNode; if let Some(path) = ast::Path::cast(node.clone()) { + if is_self(&path) { + // Self cannot be resolved like other paths. + return Ok(()); + } // Check if this is an appropriate place in the path to resolve. If the path is // something like `a::B::::c` then we want to resolve `a::B`. If the path contains // a placeholder. e.g. `a::$b::c` then we want to resolve `a`. @@ -157,6 +170,18 @@ impl<'db> ResolutionScope<'db> { ResolutionScope { scope, hygiene: hir::Hygiene::new(sema.db, resolve_context.file_id.into()), + node, + } + } + + /// Returns the function in which SSR was invoked, if any. + pub(crate) fn current_function(&self) -> Option { + let mut node = self.node.clone(); + loop { + if node.kind() == SyntaxKind::FN { + return Some(node); + } + node = node.parent()?; } } @@ -186,6 +211,10 @@ impl<'db> ResolutionScope<'db> { } } +fn is_self(path: &ast::Path) -> bool { + path.segment().map(|segment| segment.self_token().is_some()).unwrap_or(false) +} + /// Returns a suitable node for resolving paths in the current scope. If we create a scope based on /// a statement node, then we can't resolve local variables that were defined in the current scope /// (only in parent scopes). So we find another node, ideally a child of the statement where local -- cgit v1.2.3 From 3eea41a68ca2de28dca35b6e713cbb36aa09f0c8 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Thu, 6 Aug 2020 07:36:03 +1000 Subject: Use SyntaxNode.ancestors instead of a loop --- crates/ra_ssr/src/resolving.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index 0ac929bd5..df60048eb 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -176,13 +176,7 @@ impl<'db> ResolutionScope<'db> { /// Returns the function in which SSR was invoked, if any. pub(crate) fn current_function(&self) -> Option { - let mut node = self.node.clone(); - loop { - if node.kind() == SyntaxKind::FN { - return Some(node); - } - node = node.parent()?; - } + self.node.ancestors().find(|node| node.kind() == SyntaxKind::FN).map(|node| node.clone()) } fn resolve_path(&self, path: &ast::Path) -> Option { -- cgit v1.2.3 From a1c187eef3ba08076aedb5154929f7eda8d1b424 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 12 Aug 2020 18:26:51 +0200 Subject: Rename ra_syntax -> syntax --- crates/ra_ssr/src/resolving.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index d53bd46c7..7e7585c8b 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -4,8 +4,8 @@ use crate::errors::error; use crate::{parsing, SsrError}; use parsing::Placeholder; use ra_db::FilePosition; -use ra_syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; use rustc_hash::FxHashMap; +use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; use test_utils::mark; pub(crate) struct ResolutionScope<'db> { @@ -70,7 +70,7 @@ struct Resolver<'a, 'db> { impl Resolver<'_, '_> { fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result { - use ra_syntax::{SyntaxElement, T}; + use syntax::{SyntaxElement, T}; let mut resolved_paths = FxHashMap::default(); self.resolve(pattern.clone(), 0, &mut resolved_paths)?; let ufcs_function_calls = resolved_paths @@ -108,7 +108,7 @@ impl Resolver<'_, '_> { depth: u32, resolved_paths: &mut FxHashMap, ) -> Result<(), SsrError> { - use ra_syntax::ast::AstNode; + use syntax::ast::AstNode; if let Some(path) = ast::Path::cast(node.clone()) { if is_self(&path) { // Self cannot be resolved like other paths. @@ -179,7 +179,7 @@ impl<'db> ResolutionScope<'db> { sema: &hir::Semantics<'db, ra_ide_db::RootDatabase>, resolve_context: FilePosition, ) -> ResolutionScope<'db> { - use ra_syntax::ast::AstNode; + use syntax::ast::AstNode; let file = sema.parse(resolve_context.file_id); // Find a node at the requested position, falling back to the whole file. let node = file -- cgit v1.2.3 From 3100de842b3cc33c9ad364f10c7f740ac760f564 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Wed, 5 Aug 2020 19:48:52 +1000 Subject: Structured search replace now handles UFCS calls to trait methods --- crates/ra_ssr/src/resolving.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index 7e7585c8b..bfc20705b 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -25,7 +25,7 @@ pub(crate) struct ResolvedPattern { pub(crate) node: SyntaxNode, // Paths in `node` that we've resolved. pub(crate) resolved_paths: FxHashMap, - pub(crate) ufcs_function_calls: FxHashMap, + pub(crate) ufcs_function_calls: FxHashMap, pub(crate) contains_self: bool, } @@ -35,6 +35,12 @@ pub(crate) struct ResolvedPath { pub(crate) depth: u32, } +pub(crate) struct UfcsCallInfo { + pub(crate) call_expr: ast::CallExpr, + pub(crate) function: hir::Function, + pub(crate) qualifier_type: Option, +} + impl ResolvedRule { pub(crate) fn new( rule: parsing::ParsedRule, @@ -70,6 +76,7 @@ struct Resolver<'a, 'db> { impl Resolver<'_, '_> { fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result { + use syntax::ast::AstNode; use syntax::{SyntaxElement, T}; let mut resolved_paths = FxHashMap::default(); self.resolve(pattern.clone(), 0, &mut resolved_paths)?; @@ -77,11 +84,15 @@ impl Resolver<'_, '_> { .iter() .filter_map(|(path_node, resolved)| { if let Some(grandparent) = path_node.parent().and_then(|parent| parent.parent()) { - if grandparent.kind() == SyntaxKind::CALL_EXPR { + if let Some(call_expr) = ast::CallExpr::cast(grandparent.clone()) { if let hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) = - &resolved.resolution + resolved.resolution { - return Some((grandparent, *function)); + let qualifier_type = self.resolution_scope.qualifier_type(path_node); + return Some(( + grandparent, + UfcsCallInfo { call_expr, function, qualifier_type }, + )); } } } @@ -226,6 +237,20 @@ impl<'db> ResolutionScope<'db> { None } } + + fn qualifier_type(&self, path: &SyntaxNode) -> Option { + use syntax::ast::AstNode; + if let Some(path) = ast::Path::cast(path.clone()) { + if let Some(qualifier) = path.qualifier() { + if let Some(resolved_qualifier) = self.resolve_path(&qualifier) { + if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier { + return Some(adt.ty(self.scope.db)); + } + } + } + } + None + } } fn is_self(path: &ast::Path) -> bool { -- cgit v1.2.3 From ed20a857f485a471369cd99b843af19a4d875ad0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Aug 2020 16:25:38 +0200 Subject: Rename ra_db -> base_db --- crates/ra_ssr/src/resolving.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index bfc20705b..dac09bae8 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -2,8 +2,8 @@ use crate::errors::error; use crate::{parsing, SsrError}; +use base_db::FilePosition; use parsing::Placeholder; -use ra_db::FilePosition; use rustc_hash::FxHashMap; use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; use test_utils::mark; -- cgit v1.2.3 From bb5c189b7dae1ea63ccd5d7a0c2e097d7c676f77 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Aug 2020 16:39:16 +0200 Subject: Rename ra_ide_db -> ide_db --- crates/ra_ssr/src/resolving.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index dac09bae8..020fd7994 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -187,7 +187,7 @@ impl Resolver<'_, '_> { impl<'db> ResolutionScope<'db> { pub(crate) fn new( - sema: &hir::Semantics<'db, ra_ide_db::RootDatabase>, + sema: &hir::Semantics<'db, ide_db::RootDatabase>, resolve_context: FilePosition, ) -> ResolutionScope<'db> { use syntax::ast::AstNode; -- cgit v1.2.3 From ae3abd6e575940eb1221acf26c09e96352f052fa Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Aug 2020 16:45:10 +0200 Subject: Rename ra_ssr -> ssr --- crates/ra_ssr/src/resolving.rs | 299 ----------------------------------------- 1 file changed, 299 deletions(-) delete mode 100644 crates/ra_ssr/src/resolving.rs (limited to 'crates/ra_ssr/src/resolving.rs') diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs deleted file mode 100644 index 020fd7994..000000000 --- a/crates/ra_ssr/src/resolving.rs +++ /dev/null @@ -1,299 +0,0 @@ -//! This module is responsible for resolving paths within rules. - -use crate::errors::error; -use crate::{parsing, SsrError}; -use base_db::FilePosition; -use parsing::Placeholder; -use rustc_hash::FxHashMap; -use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; -use test_utils::mark; - -pub(crate) struct ResolutionScope<'db> { - scope: hir::SemanticsScope<'db>, - hygiene: hir::Hygiene, - node: SyntaxNode, -} - -pub(crate) struct ResolvedRule { - pub(crate) pattern: ResolvedPattern, - pub(crate) template: Option, - pub(crate) index: usize, -} - -pub(crate) struct ResolvedPattern { - pub(crate) placeholders_by_stand_in: FxHashMap, - pub(crate) node: SyntaxNode, - // Paths in `node` that we've resolved. - pub(crate) resolved_paths: FxHashMap, - pub(crate) ufcs_function_calls: FxHashMap, - pub(crate) contains_self: bool, -} - -pub(crate) struct ResolvedPath { - pub(crate) resolution: hir::PathResolution, - /// The depth of the ast::Path that was resolved within the pattern. - pub(crate) depth: u32, -} - -pub(crate) struct UfcsCallInfo { - pub(crate) call_expr: ast::CallExpr, - pub(crate) function: hir::Function, - pub(crate) qualifier_type: Option, -} - -impl ResolvedRule { - pub(crate) fn new( - rule: parsing::ParsedRule, - resolution_scope: &ResolutionScope, - index: usize, - ) -> Result { - let resolver = - Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in }; - let resolved_template = if let Some(template) = rule.template { - Some(resolver.resolve_pattern_tree(template)?) - } else { - None - }; - Ok(ResolvedRule { - pattern: resolver.resolve_pattern_tree(rule.pattern)?, - template: resolved_template, - index, - }) - } - - pub(crate) fn get_placeholder(&self, token: &SyntaxToken) -> Option<&Placeholder> { - if token.kind() != SyntaxKind::IDENT { - return None; - } - self.pattern.placeholders_by_stand_in.get(token.text()) - } -} - -struct Resolver<'a, 'db> { - resolution_scope: &'a ResolutionScope<'db>, - placeholders_by_stand_in: FxHashMap, -} - -impl Resolver<'_, '_> { - fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result { - use syntax::ast::AstNode; - use syntax::{SyntaxElement, T}; - let mut resolved_paths = FxHashMap::default(); - self.resolve(pattern.clone(), 0, &mut resolved_paths)?; - let ufcs_function_calls = resolved_paths - .iter() - .filter_map(|(path_node, resolved)| { - if let Some(grandparent) = path_node.parent().and_then(|parent| parent.parent()) { - if let Some(call_expr) = ast::CallExpr::cast(grandparent.clone()) { - if let hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) = - resolved.resolution - { - let qualifier_type = self.resolution_scope.qualifier_type(path_node); - return Some(( - grandparent, - UfcsCallInfo { call_expr, function, qualifier_type }, - )); - } - } - } - None - }) - .collect(); - let contains_self = - pattern.descendants_with_tokens().any(|node_or_token| match node_or_token { - SyntaxElement::Token(t) => t.kind() == T![self], - _ => false, - }); - Ok(ResolvedPattern { - node: pattern, - resolved_paths, - placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), - ufcs_function_calls, - contains_self, - }) - } - - fn resolve( - &self, - node: SyntaxNode, - depth: u32, - resolved_paths: &mut FxHashMap, - ) -> Result<(), SsrError> { - use syntax::ast::AstNode; - if let Some(path) = ast::Path::cast(node.clone()) { - if is_self(&path) { - // Self cannot be resolved like other paths. - return Ok(()); - } - // Check if this is an appropriate place in the path to resolve. If the path is - // something like `a::B::::c` then we want to resolve `a::B`. If the path contains - // a placeholder. e.g. `a::$b::c` then we want to resolve `a`. - if !path_contains_type_arguments(path.qualifier()) - && !self.path_contains_placeholder(&path) - { - let resolution = self - .resolution_scope - .resolve_path(&path) - .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?; - if self.ok_to_use_path_resolution(&resolution) { - resolved_paths.insert(node, ResolvedPath { resolution, depth }); - return Ok(()); - } - } - } - for node in node.children() { - self.resolve(node, depth + 1, resolved_paths)?; - } - Ok(()) - } - - /// Returns whether `path` contains a placeholder, but ignores any placeholders within type - /// arguments. - fn path_contains_placeholder(&self, path: &ast::Path) -> bool { - if let Some(segment) = path.segment() { - if let Some(name_ref) = segment.name_ref() { - if self.placeholders_by_stand_in.contains_key(name_ref.text()) { - return true; - } - } - } - if let Some(qualifier) = path.qualifier() { - return self.path_contains_placeholder(&qualifier); - } - false - } - - fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool { - match resolution { - hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => { - if function.has_self_param(self.resolution_scope.scope.db) { - // If we don't use this path resolution, then we won't be able to match method - // calls. e.g. `Foo::bar($s)` should match `x.bar()`. - true - } else { - mark::hit!(replace_associated_trait_default_function_call); - false - } - } - hir::PathResolution::AssocItem(_) => { - // Not a function. Could be a constant or an associated type. - mark::hit!(replace_associated_trait_constant); - false - } - _ => true, - } - } -} - -impl<'db> ResolutionScope<'db> { - pub(crate) fn new( - sema: &hir::Semantics<'db, ide_db::RootDatabase>, - resolve_context: FilePosition, - ) -> ResolutionScope<'db> { - use syntax::ast::AstNode; - let file = sema.parse(resolve_context.file_id); - // Find a node at the requested position, falling back to the whole file. - let node = file - .syntax() - .token_at_offset(resolve_context.offset) - .left_biased() - .map(|token| token.parent()) - .unwrap_or_else(|| file.syntax().clone()); - let node = pick_node_for_resolution(node); - let scope = sema.scope(&node); - ResolutionScope { - scope, - hygiene: hir::Hygiene::new(sema.db, resolve_context.file_id.into()), - node, - } - } - - /// Returns the function in which SSR was invoked, if any. - pub(crate) fn current_function(&self) -> Option { - self.node.ancestors().find(|node| node.kind() == SyntaxKind::FN).map(|node| node.clone()) - } - - fn resolve_path(&self, path: &ast::Path) -> Option { - let hir_path = hir::Path::from_src(path.clone(), &self.hygiene)?; - // First try resolving the whole path. This will work for things like - // `std::collections::HashMap`, but will fail for things like - // `std::collections::HashMap::new`. - if let Some(resolution) = self.scope.resolve_hir_path(&hir_path) { - return Some(resolution); - } - // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if - // that succeeds, then iterate through the candidates on the resolved type with the provided - // name. - let resolved_qualifier = self.scope.resolve_hir_path_qualifier(&hir_path.qualifier()?)?; - if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier { - adt.ty(self.scope.db).iterate_path_candidates( - self.scope.db, - self.scope.module()?.krate(), - &self.scope.traits_in_scope(), - Some(hir_path.segments().last()?.name), - |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)), - ) - } else { - None - } - } - - fn qualifier_type(&self, path: &SyntaxNode) -> Option { - use syntax::ast::AstNode; - if let Some(path) = ast::Path::cast(path.clone()) { - if let Some(qualifier) = path.qualifier() { - if let Some(resolved_qualifier) = self.resolve_path(&qualifier) { - if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier { - return Some(adt.ty(self.scope.db)); - } - } - } - } - None - } -} - -fn is_self(path: &ast::Path) -> bool { - path.segment().map(|segment| segment.self_token().is_some()).unwrap_or(false) -} - -/// Returns a suitable node for resolving paths in the current scope. If we create a scope based on -/// a statement node, then we can't resolve local variables that were defined in the current scope -/// (only in parent scopes). So we find another node, ideally a child of the statement where local -/// variable resolution is permitted. -fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode { - match node.kind() { - SyntaxKind::EXPR_STMT => { - if let Some(n) = node.first_child() { - mark::hit!(cursor_after_semicolon); - return n; - } - } - SyntaxKind::LET_STMT | SyntaxKind::IDENT_PAT => { - if let Some(next) = node.next_sibling() { - return pick_node_for_resolution(next); - } - } - SyntaxKind::NAME => { - if let Some(parent) = node.parent() { - return pick_node_for_resolution(parent); - } - } - _ => {} - } - node -} - -/// Returns whether `path` or any of its qualifiers contains type arguments. -fn path_contains_type_arguments(path: Option) -> bool { - if let Some(path) = path { - if let Some(segment) = path.segment() { - if segment.generic_arg_list().is_some() { - mark::hit!(type_arguments_within_path); - return true; - } - } - return path_contains_type_arguments(path.qualifier()); - } - false -} -- cgit v1.2.3