diff options
-rw-r--r-- | crates/assists/src/ast_transform.rs | 74 |
1 files changed, 34 insertions, 40 deletions
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs index 835da3bb2..4307e0191 100644 --- a/crates/assists/src/ast_transform.rs +++ b/crates/assists/src/ast_transform.rs | |||
@@ -5,12 +5,13 @@ use hir::{HirDisplay, PathResolution, SemanticsScope}; | |||
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
7 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
8 | SyntaxNode, | ||
8 | }; | 9 | }; |
9 | 10 | ||
10 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 11 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
11 | SyntaxRewriter::from_fn(|element| match element { | 12 | SyntaxRewriter::from_fn(|element| match element { |
12 | syntax::SyntaxElement::Node(n) => { | 13 | syntax::SyntaxElement::Node(n) => { |
13 | let replacement = transformer.get_substitution(&n)?; | 14 | let replacement = transformer.get_substitution(&n, transformer)?; |
14 | Some(replacement.into()) | 15 | Some(replacement.into()) |
15 | } | 16 | } |
16 | _ => None, | 17 | _ => None, |
@@ -47,32 +48,35 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | |||
47 | /// We'd want to somehow express this concept simpler, but so far nobody got to | 48 | /// We'd want to somehow express this concept simpler, but so far nobody got to |
48 | /// simplifying this! | 49 | /// simplifying this! |
49 | pub trait AstTransform<'a> { | 50 | pub trait AstTransform<'a> { |
50 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; | 51 | fn get_substitution( |
52 | &self, | ||
53 | node: &SyntaxNode, | ||
54 | recur: &dyn AstTransform<'a>, | ||
55 | ) -> Option<SyntaxNode>; | ||
51 | 56 | ||
52 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; | ||
53 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> | 57 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> |
54 | where | 58 | where |
55 | Self: Sized + 'a, | 59 | Self: Sized + 'a, |
56 | { | 60 | { |
57 | self.chain_before(Box::new(other)) | 61 | Box::new(Or(Box::new(self), Box::new(other))) |
58 | } | 62 | } |
59 | } | 63 | } |
60 | 64 | ||
61 | struct NullTransformer; | 65 | struct Or<'a>(Box<dyn AstTransform<'a> + 'a>, Box<dyn AstTransform<'a> + 'a>); |
62 | 66 | ||
63 | impl<'a> AstTransform<'a> for NullTransformer { | 67 | impl<'a> AstTransform<'a> for Or<'a> { |
64 | fn get_substitution(&self, _node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | 68 | fn get_substitution( |
65 | None | 69 | &self, |
66 | } | 70 | node: &SyntaxNode, |
67 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 71 | recur: &dyn AstTransform<'a>, |
68 | other | 72 | ) -> Option<SyntaxNode> { |
73 | self.0.get_substitution(node, recur).or_else(|| self.1.get_substitution(node, recur)) | ||
69 | } | 74 | } |
70 | } | 75 | } |
71 | 76 | ||
72 | pub struct SubstituteTypeParams<'a> { | 77 | pub struct SubstituteTypeParams<'a> { |
73 | source_scope: &'a SemanticsScope<'a>, | 78 | source_scope: &'a SemanticsScope<'a>, |
74 | substs: FxHashMap<hir::TypeParam, ast::Type>, | 79 | substs: FxHashMap<hir::TypeParam, ast::Type>, |
75 | previous: Box<dyn AstTransform<'a> + 'a>, | ||
76 | } | 80 | } |
77 | 81 | ||
78 | impl<'a> SubstituteTypeParams<'a> { | 82 | impl<'a> SubstituteTypeParams<'a> { |
@@ -111,11 +115,7 @@ impl<'a> SubstituteTypeParams<'a> { | |||
111 | } | 115 | } |
112 | }) | 116 | }) |
113 | .collect(); | 117 | .collect(); |
114 | return SubstituteTypeParams { | 118 | return SubstituteTypeParams { source_scope, substs: substs_by_param }; |
115 | source_scope, | ||
116 | substs: substs_by_param, | ||
117 | previous: Box::new(NullTransformer), | ||
118 | }; | ||
119 | 119 | ||
120 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the | 120 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the |
121 | // trait ref, and then go from the types in the substs back to the syntax). | 121 | // trait ref, and then go from the types in the substs back to the syntax). |
@@ -140,7 +140,14 @@ impl<'a> SubstituteTypeParams<'a> { | |||
140 | Some(result) | 140 | Some(result) |
141 | } | 141 | } |
142 | } | 142 | } |
143 | fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | 143 | } |
144 | |||
145 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | ||
146 | fn get_substitution( | ||
147 | &self, | ||
148 | node: &SyntaxNode, | ||
149 | _recur: &dyn AstTransform<'a>, | ||
150 | ) -> Option<SyntaxNode> { | ||
144 | let type_ref = ast::Type::cast(node.clone())?; | 151 | let type_ref = ast::Type::cast(node.clone())?; |
145 | let path = match &type_ref { | 152 | let path = match &type_ref { |
146 | ast::Type::PathType(path_type) => path_type.path()?, | 153 | ast::Type::PathType(path_type) => path_type.path()?, |
@@ -154,27 +161,23 @@ impl<'a> SubstituteTypeParams<'a> { | |||
154 | } | 161 | } |
155 | } | 162 | } |
156 | 163 | ||
157 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | ||
158 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | ||
159 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | ||
160 | } | ||
161 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | ||
162 | Box::new(SubstituteTypeParams { previous: other, ..self }) | ||
163 | } | ||
164 | } | ||
165 | |||
166 | pub struct QualifyPaths<'a> { | 164 | pub struct QualifyPaths<'a> { |
167 | target_scope: &'a SemanticsScope<'a>, | 165 | target_scope: &'a SemanticsScope<'a>, |
168 | source_scope: &'a SemanticsScope<'a>, | 166 | source_scope: &'a SemanticsScope<'a>, |
169 | previous: Box<dyn AstTransform<'a> + 'a>, | ||
170 | } | 167 | } |
171 | 168 | ||
172 | impl<'a> QualifyPaths<'a> { | 169 | impl<'a> QualifyPaths<'a> { |
173 | pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { | 170 | pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { |
174 | Self { target_scope, source_scope, previous: Box::new(NullTransformer) } | 171 | Self { target_scope, source_scope } |
175 | } | 172 | } |
173 | } | ||
176 | 174 | ||
177 | fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | 175 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { |
176 | fn get_substitution( | ||
177 | &self, | ||
178 | node: &SyntaxNode, | ||
179 | recur: &dyn AstTransform<'a>, | ||
180 | ) -> Option<SyntaxNode> { | ||
178 | // FIXME handle value ns? | 181 | // FIXME handle value ns? |
179 | let from = self.target_scope.module()?; | 182 | let from = self.target_scope.module()?; |
180 | let p = ast::Path::cast(node.clone())?; | 183 | let p = ast::Path::cast(node.clone())?; |
@@ -191,7 +194,7 @@ impl<'a> QualifyPaths<'a> { | |||
191 | let type_args = p | 194 | let type_args = p |
192 | .segment() | 195 | .segment() |
193 | .and_then(|s| s.generic_arg_list()) | 196 | .and_then(|s| s.generic_arg_list()) |
194 | .map(|arg_list| apply(self, arg_list)); | 197 | .map(|arg_list| apply(recur, arg_list)); |
195 | if let Some(type_args) = type_args { | 198 | if let Some(type_args) = type_args { |
196 | let last_segment = path.segment().unwrap(); | 199 | let last_segment = path.segment().unwrap(); |
197 | path = path.with_segment(last_segment.with_generic_args(type_args)) | 200 | path = path.with_segment(last_segment.with_generic_args(type_args)) |
@@ -208,15 +211,6 @@ impl<'a> QualifyPaths<'a> { | |||
208 | } | 211 | } |
209 | } | 212 | } |
210 | 213 | ||
211 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { | ||
212 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | ||
213 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | ||
214 | } | ||
215 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | ||
216 | Box::new(QualifyPaths { previous: other, ..self }) | ||
217 | } | ||
218 | } | ||
219 | |||
220 | pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { | 214 | pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { |
221 | let parse = ast::SourceFile::parse(&path.to_string()); | 215 | let parse = ast::SourceFile::parse(&path.to_string()); |
222 | parse | 216 | parse |