diff options
Diffstat (limited to 'crates/ra_assists/src/ast_transform.rs')
-rw-r--r-- | crates/ra_assists/src/ast_transform.rs | 84 |
1 files changed, 40 insertions, 44 deletions
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 56b7588ef..7846e9798 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -1,14 +1,12 @@ | |||
1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. | 1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{db::HirDatabase, InFile, PathResolution}; | 4 | use hir::{PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::ast::{self, AstNode}; | 6 | use ra_syntax::ast::{self, AstNode}; |
6 | 7 | ||
7 | pub trait AstTransform<'a> { | 8 | pub trait AstTransform<'a> { |
8 | fn get_substitution( | 9 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>; |
9 | &self, | ||
10 | node: InFile<&ra_syntax::SyntaxNode>, | ||
11 | ) -> Option<ra_syntax::SyntaxNode>; | ||
12 | 10 | ||
13 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; | 11 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; |
14 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> | 12 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> |
@@ -22,10 +20,7 @@ pub trait AstTransform<'a> { | |||
22 | struct NullTransformer; | 20 | struct NullTransformer; |
23 | 21 | ||
24 | impl<'a> AstTransform<'a> for NullTransformer { | 22 | impl<'a> AstTransform<'a> for NullTransformer { |
25 | fn get_substitution( | 23 | fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
26 | &self, | ||
27 | _node: InFile<&ra_syntax::SyntaxNode>, | ||
28 | ) -> Option<ra_syntax::SyntaxNode> { | ||
29 | None | 24 | None |
30 | } | 25 | } |
31 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 26 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
@@ -33,18 +28,20 @@ impl<'a> AstTransform<'a> for NullTransformer { | |||
33 | } | 28 | } |
34 | } | 29 | } |
35 | 30 | ||
36 | pub struct SubstituteTypeParams<'a, DB: HirDatabase> { | 31 | pub struct SubstituteTypeParams<'a> { |
37 | db: &'a DB, | 32 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
38 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, | 33 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, |
39 | previous: Box<dyn AstTransform<'a> + 'a>, | 34 | previous: Box<dyn AstTransform<'a> + 'a>, |
40 | } | 35 | } |
41 | 36 | ||
42 | impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | 37 | impl<'a> SubstituteTypeParams<'a> { |
43 | pub fn for_trait_impl( | 38 | pub fn for_trait_impl( |
44 | db: &'a DB, | 39 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
40 | db: &'a RootDatabase, | ||
41 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... | ||
45 | trait_: hir::Trait, | 42 | trait_: hir::Trait, |
46 | impl_block: ast::ImplBlock, | 43 | impl_block: ast::ImplBlock, |
47 | ) -> SubstituteTypeParams<'a, DB> { | 44 | ) -> SubstituteTypeParams<'a> { |
48 | let substs = get_syntactic_substs(impl_block).unwrap_or_default(); | 45 | let substs = get_syntactic_substs(impl_block).unwrap_or_default(); |
49 | let generic_def: hir::GenericDef = trait_.into(); | 46 | let generic_def: hir::GenericDef = trait_.into(); |
50 | let substs_by_param: FxHashMap<_, _> = generic_def | 47 | let substs_by_param: FxHashMap<_, _> = generic_def |
@@ -55,7 +52,7 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | |||
55 | .zip(substs.into_iter()) | 52 | .zip(substs.into_iter()) |
56 | .collect(); | 53 | .collect(); |
57 | return SubstituteTypeParams { | 54 | return SubstituteTypeParams { |
58 | db, | 55 | source_scope, |
59 | substs: substs_by_param, | 56 | substs: substs_by_param, |
60 | previous: Box::new(NullTransformer), | 57 | previous: Box::new(NullTransformer), |
61 | }; | 58 | }; |
@@ -79,15 +76,15 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | |||
79 | } | 76 | } |
80 | fn get_substitution_inner( | 77 | fn get_substitution_inner( |
81 | &self, | 78 | &self, |
82 | node: InFile<&ra_syntax::SyntaxNode>, | 79 | node: &ra_syntax::SyntaxNode, |
83 | ) -> Option<ra_syntax::SyntaxNode> { | 80 | ) -> Option<ra_syntax::SyntaxNode> { |
84 | let type_ref = ast::TypeRef::cast(node.value.clone())?; | 81 | let type_ref = ast::TypeRef::cast(node.clone())?; |
85 | let path = match &type_ref { | 82 | let path = match &type_ref { |
86 | ast::TypeRef::PathType(path_type) => path_type.path()?, | 83 | ast::TypeRef::PathType(path_type) => path_type.path()?, |
87 | _ => return None, | 84 | _ => return None, |
88 | }; | 85 | }; |
89 | let analyzer = hir::SourceAnalyzer::new(self.db, node, None); | 86 | let path = hir::Path::from_ast(path)?; |
90 | let resolution = analyzer.resolve_path(self.db, &path)?; | 87 | let resolution = self.source_scope.resolve_hir_path(&path)?; |
91 | match resolution { | 88 | match resolution { |
92 | hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), | 89 | hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), |
93 | _ => None, | 90 | _ => None, |
@@ -95,11 +92,8 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | |||
95 | } | 92 | } |
96 | } | 93 | } |
97 | 94 | ||
98 | impl<'a, DB: HirDatabase> AstTransform<'a> for SubstituteTypeParams<'a, DB> { | 95 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { |
99 | fn get_substitution( | 96 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
100 | &self, | ||
101 | node: InFile<&ra_syntax::SyntaxNode>, | ||
102 | ) -> Option<ra_syntax::SyntaxNode> { | ||
103 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | 97 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) |
104 | } | 98 | } |
105 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 99 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
@@ -107,30 +101,35 @@ impl<'a, DB: HirDatabase> AstTransform<'a> for SubstituteTypeParams<'a, DB> { | |||
107 | } | 101 | } |
108 | } | 102 | } |
109 | 103 | ||
110 | pub struct QualifyPaths<'a, DB: HirDatabase> { | 104 | pub struct QualifyPaths<'a> { |
111 | db: &'a DB, | 105 | target_scope: &'a SemanticsScope<'a, RootDatabase>, |
112 | from: Option<hir::Module>, | 106 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
107 | db: &'a RootDatabase, | ||
113 | previous: Box<dyn AstTransform<'a> + 'a>, | 108 | previous: Box<dyn AstTransform<'a> + 'a>, |
114 | } | 109 | } |
115 | 110 | ||
116 | impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | 111 | impl<'a> QualifyPaths<'a> { |
117 | pub fn new(db: &'a DB, from: Option<hir::Module>) -> Self { | 112 | pub fn new( |
118 | Self { db, from, previous: Box::new(NullTransformer) } | 113 | target_scope: &'a SemanticsScope<'a, RootDatabase>, |
114 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
115 | db: &'a RootDatabase, | ||
116 | ) -> Self { | ||
117 | Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) } | ||
119 | } | 118 | } |
120 | 119 | ||
121 | fn get_substitution_inner( | 120 | fn get_substitution_inner( |
122 | &self, | 121 | &self, |
123 | node: InFile<&ra_syntax::SyntaxNode>, | 122 | node: &ra_syntax::SyntaxNode, |
124 | ) -> Option<ra_syntax::SyntaxNode> { | 123 | ) -> Option<ra_syntax::SyntaxNode> { |
125 | // FIXME handle value ns? | 124 | // FIXME handle value ns? |
126 | let from = self.from?; | 125 | let from = self.target_scope.module()?; |
127 | let p = ast::Path::cast(node.value.clone())?; | 126 | let p = ast::Path::cast(node.clone())?; |
128 | if p.segment().and_then(|s| s.param_list()).is_some() { | 127 | if p.segment().and_then(|s| s.param_list()).is_some() { |
129 | // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway | 128 | // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway |
130 | return None; | 129 | return None; |
131 | } | 130 | } |
132 | let analyzer = hir::SourceAnalyzer::new(self.db, node, None); | 131 | let hir_path = hir::Path::from_ast(p.clone()); |
133 | let resolution = analyzer.resolve_path(self.db, &p)?; | 132 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; |
134 | match resolution { | 133 | match resolution { |
135 | PathResolution::Def(def) => { | 134 | PathResolution::Def(def) => { |
136 | let found_path = from.find_use_path(self.db, def)?; | 135 | let found_path = from.find_use_path(self.db, def)?; |
@@ -139,7 +138,7 @@ impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | |||
139 | let type_args = p | 138 | let type_args = p |
140 | .segment() | 139 | .segment() |
141 | .and_then(|s| s.type_arg_list()) | 140 | .and_then(|s| s.type_arg_list()) |
142 | .map(|arg_list| apply(self, node.with_value(arg_list))); | 141 | .map(|arg_list| apply(self, arg_list)); |
143 | if let Some(type_args) = type_args { | 142 | if let Some(type_args) = type_args { |
144 | let last_segment = path.segment().unwrap(); | 143 | let last_segment = path.segment().unwrap(); |
145 | path = path.with_segment(last_segment.with_type_args(type_args)) | 144 | path = path.with_segment(last_segment.with_type_args(type_args)) |
@@ -156,11 +155,11 @@ impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | |||
156 | } | 155 | } |
157 | } | 156 | } |
158 | 157 | ||
159 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N { | 158 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
160 | let syntax = node.value.syntax(); | 159 | let syntax = node.syntax(); |
161 | let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { | 160 | let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { |
162 | ra_syntax::SyntaxElement::Node(n) => { | 161 | ra_syntax::SyntaxElement::Node(n) => { |
163 | let replacement = transformer.get_substitution(node.with_value(&n))?; | 162 | let replacement = transformer.get_substitution(&n)?; |
164 | Some(replacement.into()) | 163 | Some(replacement.into()) |
165 | } | 164 | } |
166 | _ => None, | 165 | _ => None, |
@@ -168,11 +167,8 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N> | |||
168 | N::cast(result).unwrap() | 167 | N::cast(result).unwrap() |
169 | } | 168 | } |
170 | 169 | ||
171 | impl<'a, DB: HirDatabase> AstTransform<'a> for QualifyPaths<'a, DB> { | 170 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { |
172 | fn get_substitution( | 171 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
173 | &self, | ||
174 | node: InFile<&ra_syntax::SyntaxNode>, | ||
175 | ) -> Option<ra_syntax::SyntaxNode> { | ||
176 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | 172 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) |
177 | } | 173 | } |
178 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 174 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |