diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_ty/Cargo.toml | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/chalk.rs | 57 | ||||
-rw-r--r-- | crates/ra_ide/src/references/rename.rs | 211 |
3 files changed, 247 insertions, 27 deletions
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 65db6d1b0..5fc0ec5e3 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" } | |||
27 | 27 | ||
28 | scoped-tls = "1" | 28 | scoped-tls = "1" |
29 | 29 | ||
30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } | 30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } |
31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } | 31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } |
32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } | 32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } |
33 | 33 | ||
34 | [dev-dependencies] | 34 | [dev-dependencies] |
35 | insta = "0.16.0" | 35 | insta = "0.16.0" |
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 1ccb7c3b4..5870618a0 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -182,7 +182,10 @@ impl chalk_ir::interner::Interner for Interner { | |||
182 | Arc::new(goal) | 182 | Arc::new(goal) |
183 | } | 183 | } |
184 | 184 | ||
185 | fn intern_goals(&self, data: impl IntoIterator<Item = Goal<Self>>) -> Self::InternedGoals { | 185 | fn intern_goals<E>( |
186 | &self, | ||
187 | data: impl IntoIterator<Item = Result<Goal<Self>, E>>, | ||
188 | ) -> Result<Self::InternedGoals, E> { | ||
186 | data.into_iter().collect() | 189 | data.into_iter().collect() |
187 | } | 190 | } |
188 | 191 | ||
@@ -222,10 +225,10 @@ impl chalk_ir::interner::Interner for Interner { | |||
222 | clause | 225 | clause |
223 | } | 226 | } |
224 | 227 | ||
225 | fn intern_program_clauses( | 228 | fn intern_program_clauses<E>( |
226 | &self, | 229 | &self, |
227 | data: impl IntoIterator<Item = chalk_ir::ProgramClause<Self>>, | 230 | data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>, |
228 | ) -> Arc<[chalk_ir::ProgramClause<Self>]> { | 231 | ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> { |
229 | data.into_iter().collect() | 232 | data.into_iter().collect() |
230 | } | 233 | } |
231 | 234 | ||
@@ -236,10 +239,10 @@ impl chalk_ir::interner::Interner for Interner { | |||
236 | &clauses | 239 | &clauses |
237 | } | 240 | } |
238 | 241 | ||
239 | fn intern_quantified_where_clauses( | 242 | fn intern_quantified_where_clauses<E>( |
240 | &self, | 243 | &self, |
241 | data: impl IntoIterator<Item = chalk_ir::QuantifiedWhereClause<Self>>, | 244 | data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>, |
242 | ) -> Self::InternedQuantifiedWhereClauses { | 245 | ) -> Result<Self::InternedQuantifiedWhereClauses, E> { |
243 | data.into_iter().collect() | 246 | data.into_iter().collect() |
244 | } | 247 | } |
245 | 248 | ||
@@ -250,10 +253,10 @@ impl chalk_ir::interner::Interner for Interner { | |||
250 | clauses | 253 | clauses |
251 | } | 254 | } |
252 | 255 | ||
253 | fn intern_parameter_kinds( | 256 | fn intern_parameter_kinds<E>( |
254 | &self, | 257 | &self, |
255 | data: impl IntoIterator<Item = chalk_ir::ParameterKind<()>>, | 258 | data: impl IntoIterator<Item = Result<chalk_ir::ParameterKind<()>, E>>, |
256 | ) -> Self::InternedParameterKinds { | 259 | ) -> Result<Self::InternedParameterKinds, E> { |
257 | data.into_iter().collect() | 260 | data.into_iter().collect() |
258 | } | 261 | } |
259 | 262 | ||
@@ -264,10 +267,10 @@ impl chalk_ir::interner::Interner for Interner { | |||
264 | ¶meter_kinds | 267 | ¶meter_kinds |
265 | } | 268 | } |
266 | 269 | ||
267 | fn intern_canonical_var_kinds( | 270 | fn intern_canonical_var_kinds<E>( |
268 | &self, | 271 | &self, |
269 | data: impl IntoIterator<Item = chalk_ir::ParameterKind<UniverseIndex>>, | 272 | data: impl IntoIterator<Item = Result<chalk_ir::ParameterKind<UniverseIndex>, E>>, |
270 | ) -> Self::InternedCanonicalVarKinds { | 273 | ) -> Result<Self::InternedCanonicalVarKinds, E> { |
271 | data.into_iter().collect() | 274 | data.into_iter().collect() |
272 | } | 275 | } |
273 | 276 | ||
@@ -460,6 +463,14 @@ impl ToChalk for TypeCtor { | |||
460 | TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), | 463 | TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), |
461 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), | 464 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), |
462 | TypeName::OpaqueType(_) => unreachable!(), | 465 | TypeName::OpaqueType(_) => unreachable!(), |
466 | |||
467 | TypeName::Scalar(_) => unreachable!(), | ||
468 | TypeName::Tuple(_) => unreachable!(), | ||
469 | TypeName::Raw(_) => unreachable!(), | ||
470 | TypeName::Slice => unreachable!(), | ||
471 | TypeName::Ref(_) => unreachable!(), | ||
472 | TypeName::Str => unreachable!(), | ||
473 | |||
463 | TypeName::Error => { | 474 | TypeName::Error => { |
464 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor | 475 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor |
465 | unreachable!() | 476 | unreachable!() |
@@ -862,12 +873,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
862 | // We don't do coherence checking (yet) | 873 | // We don't do coherence checking (yet) |
863 | unimplemented!() | 874 | unimplemented!() |
864 | } | 875 | } |
865 | fn as_struct_id(&self, id: &TypeName<Interner>) -> Option<StructId> { | ||
866 | match id { | ||
867 | TypeName::Struct(struct_id) => Some(*struct_id), | ||
868 | _ => None, | ||
869 | } | ||
870 | } | ||
871 | fn interner(&self) -> &Interner { | 876 | fn interner(&self) -> &Interner { |
872 | &Interner | 877 | &Interner |
873 | } | 878 | } |
@@ -892,6 +897,20 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
892 | ) -> Arc<chalk_rust_ir::OpaqueTyDatum<Interner>> { | 897 | ) -> Arc<chalk_rust_ir::OpaqueTyDatum<Interner>> { |
893 | unimplemented!() | 898 | unimplemented!() |
894 | } | 899 | } |
900 | |||
901 | fn force_impl_for( | ||
902 | &self, | ||
903 | _well_known: chalk_rust_ir::WellKnownTrait, | ||
904 | _ty: &chalk_ir::TyData<Interner>, | ||
905 | ) -> Option<bool> { | ||
906 | // this method is mostly for rustc | ||
907 | None | ||
908 | } | ||
909 | |||
910 | fn is_object_safe(&self, _trait_id: chalk_ir::TraitId<Interner>) -> bool { | ||
911 | // FIXME: implement actual object safety | ||
912 | true | ||
913 | } | ||
895 | } | 914 | } |
896 | 915 | ||
897 | pub(crate) fn program_clauses_for_chalk_env_query( | 916 | pub(crate) fn program_clauses_for_chalk_env_query( |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 2cbb82c1a..410dae75c 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -4,14 +4,16 @@ use hir::{ModuleSource, Semantics}; | |||
4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; | 4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, | 7 | algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, |
8 | AstNode, SyntaxKind, SyntaxNode, SyntaxToken, | ||
8 | }; | 9 | }; |
9 | use ra_text_edit::TextEdit; | 10 | use ra_text_edit::TextEdit; |
11 | use std::convert::TryInto; | ||
10 | use test_utils::tested_by; | 12 | use test_utils::tested_by; |
11 | 13 | ||
12 | use crate::{ | 14 | use crate::{ |
13 | references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, | 15 | references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, |
14 | SourceChange, SourceFileEdit, TextRange, | 16 | SourceChange, SourceFileEdit, TextRange, TextSize, |
15 | }; | 17 | }; |
16 | 18 | ||
17 | pub(crate) fn rename( | 19 | pub(crate) fn rename( |
@@ -21,17 +23,21 @@ pub(crate) fn rename( | |||
21 | ) -> Option<RangeInfo<SourceChange>> { | 23 | ) -> Option<RangeInfo<SourceChange>> { |
22 | match lex_single_valid_syntax_kind(new_name)? { | 24 | match lex_single_valid_syntax_kind(new_name)? { |
23 | SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), | 25 | SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), |
26 | SyntaxKind::SELF_KW => return rename_to_self(db, position), | ||
24 | _ => return None, | 27 | _ => return None, |
25 | } | 28 | } |
26 | 29 | ||
27 | let sema = Semantics::new(db); | 30 | let sema = Semantics::new(db); |
28 | let source_file = sema.parse(position.file_id); | 31 | let source_file = sema.parse(position.file_id); |
29 | if let Some((ast_name, ast_module)) = | 32 | let syntax = source_file.syntax(); |
30 | find_name_and_module_at_offset(source_file.syntax(), position) | 33 | if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { |
31 | { | ||
32 | let range = ast_name.syntax().text_range(); | 34 | let range = ast_name.syntax().text_range(); |
33 | rename_mod(&sema, &ast_name, &ast_module, position, new_name) | 35 | rename_mod(&sema, &ast_name, &ast_module, position, new_name) |
34 | .map(|info| RangeInfo::new(range, info)) | 36 | .map(|info| RangeInfo::new(range, info)) |
37 | } else if let Some(self_token) = | ||
38 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | ||
39 | { | ||
40 | rename_self_to_param(db, position, self_token, new_name) | ||
35 | } else { | 41 | } else { |
36 | rename_reference(sema.db, position, new_name) | 42 | rename_reference(sema.db, position, new_name) |
37 | } | 43 | } |
@@ -125,6 +131,112 @@ fn rename_mod( | |||
125 | Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) | 131 | Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) |
126 | } | 132 | } |
127 | 133 | ||
134 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { | ||
135 | let sema = Semantics::new(db); | ||
136 | let source_file = sema.parse(position.file_id); | ||
137 | let syn = source_file.syntax(); | ||
138 | |||
139 | let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; | ||
140 | let params = fn_def.param_list()?; | ||
141 | if params.self_param().is_some() { | ||
142 | return None; // method already has self param | ||
143 | } | ||
144 | let first_param = params.params().next()?; | ||
145 | let mutable = match first_param.ascribed_type() { | ||
146 | Some(ast::TypeRef::ReferenceType(rt)) => rt.mut_token().is_some(), | ||
147 | _ => return None, // not renaming other types | ||
148 | }; | ||
149 | |||
150 | let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; | ||
151 | |||
152 | let param_range = first_param.syntax().text_range(); | ||
153 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs | ||
154 | .into_iter() | ||
155 | .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); | ||
156 | |||
157 | if param_ref.is_empty() { | ||
158 | return None; | ||
159 | } | ||
160 | |||
161 | let mut edits = usages | ||
162 | .into_iter() | ||
163 | .map(|reference| source_edit_from_reference(reference, "self")) | ||
164 | .collect::<Vec<_>>(); | ||
165 | |||
166 | edits.push(SourceFileEdit { | ||
167 | file_id: position.file_id, | ||
168 | edit: TextEdit::replace( | ||
169 | param_range, | ||
170 | String::from(if mutable { "&mut self" } else { "&self" }), | ||
171 | ), | ||
172 | }); | ||
173 | |||
174 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) | ||
175 | } | ||
176 | |||
177 | fn text_edit_from_self_param( | ||
178 | syn: &SyntaxNode, | ||
179 | self_param: &ast::SelfParam, | ||
180 | new_name: &str, | ||
181 | ) -> Option<TextEdit> { | ||
182 | fn target_type_name(impl_def: &ast::ImplDef) -> Option<String> { | ||
183 | if let Some(ast::TypeRef::PathType(p)) = impl_def.target_type() { | ||
184 | return Some(p.path()?.segment()?.name_ref()?.text().to_string()); | ||
185 | } | ||
186 | None | ||
187 | } | ||
188 | |||
189 | let impl_def = | ||
190 | find_node_at_offset::<ast::ImplDef>(syn, self_param.syntax().text_range().start())?; | ||
191 | let type_name = target_type_name(&impl_def)?; | ||
192 | |||
193 | let mut replacement_text = String::from(new_name); | ||
194 | replacement_text.push_str(": "); | ||
195 | replacement_text.push_str(self_param.mut_token().map_or("&", |_| "&mut ")); | ||
196 | replacement_text.push_str(type_name.as_str()); | ||
197 | |||
198 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) | ||
199 | } | ||
200 | |||
201 | fn rename_self_to_param( | ||
202 | db: &RootDatabase, | ||
203 | position: FilePosition, | ||
204 | self_token: SyntaxToken, | ||
205 | new_name: &str, | ||
206 | ) -> Option<RangeInfo<SourceChange>> { | ||
207 | let sema = Semantics::new(db); | ||
208 | let source_file = sema.parse(position.file_id); | ||
209 | let syn = source_file.syntax(); | ||
210 | |||
211 | let text = db.file_text(position.file_id); | ||
212 | let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; | ||
213 | let search_range = fn_def.syntax().text_range(); | ||
214 | |||
215 | let mut edits: Vec<SourceFileEdit> = vec![]; | ||
216 | |||
217 | for (idx, _) in text.match_indices("self") { | ||
218 | let offset: TextSize = idx.try_into().unwrap(); | ||
219 | if !search_range.contains_inclusive(offset) { | ||
220 | continue; | ||
221 | } | ||
222 | if let Some(ref usage) = | ||
223 | syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | ||
224 | { | ||
225 | let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) { | ||
226 | text_edit_from_self_param(syn, self_param, new_name)? | ||
227 | } else { | ||
228 | TextEdit::replace(usage.text_range(), String::from(new_name)) | ||
229 | }; | ||
230 | edits.push(SourceFileEdit { file_id: position.file_id, edit }); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | let range = ast::SelfParam::cast(self_token.parent()) | ||
235 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); | ||
236 | |||
237 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) | ||
238 | } | ||
239 | |||
128 | fn rename_reference( | 240 | fn rename_reference( |
129 | db: &RootDatabase, | 241 | db: &RootDatabase, |
130 | position: FilePosition, | 242 | position: FilePosition, |
@@ -774,6 +886,95 @@ mod tests { | |||
774 | ); | 886 | ); |
775 | } | 887 | } |
776 | 888 | ||
889 | #[test] | ||
890 | fn test_parameter_to_self() { | ||
891 | test_rename( | ||
892 | r#" | ||
893 | struct Foo { | ||
894 | i: i32, | ||
895 | } | ||
896 | |||
897 | impl Foo { | ||
898 | fn f(foo<|>: &mut Foo) -> i32 { | ||
899 | foo.i | ||
900 | } | ||
901 | } | ||
902 | "#, | ||
903 | "self", | ||
904 | r#" | ||
905 | struct Foo { | ||
906 | i: i32, | ||
907 | } | ||
908 | |||
909 | impl Foo { | ||
910 | fn f(&mut self) -> i32 { | ||
911 | self.i | ||
912 | } | ||
913 | } | ||
914 | "#, | ||
915 | ); | ||
916 | } | ||
917 | |||
918 | #[test] | ||
919 | fn test_self_to_parameter() { | ||
920 | test_rename( | ||
921 | r#" | ||
922 | struct Foo { | ||
923 | i: i32, | ||
924 | } | ||
925 | |||
926 | impl Foo { | ||
927 | fn f(&mut <|>self) -> i32 { | ||
928 | self.i | ||
929 | } | ||
930 | } | ||
931 | "#, | ||
932 | "foo", | ||
933 | r#" | ||
934 | struct Foo { | ||
935 | i: i32, | ||
936 | } | ||
937 | |||
938 | impl Foo { | ||
939 | fn f(foo: &mut Foo) -> i32 { | ||
940 | foo.i | ||
941 | } | ||
942 | } | ||
943 | "#, | ||
944 | ); | ||
945 | } | ||
946 | |||
947 | #[test] | ||
948 | fn test_self_in_path_to_parameter() { | ||
949 | test_rename( | ||
950 | r#" | ||
951 | struct Foo { | ||
952 | i: i32, | ||
953 | } | ||
954 | |||
955 | impl Foo { | ||
956 | fn f(&self) -> i32 { | ||
957 | let self_var = 1; | ||
958 | self<|>.i | ||
959 | } | ||
960 | } | ||
961 | "#, | ||
962 | "foo", | ||
963 | r#" | ||
964 | struct Foo { | ||
965 | i: i32, | ||
966 | } | ||
967 | |||
968 | impl Foo { | ||
969 | fn f(foo: &Foo) -> i32 { | ||
970 | let self_var = 1; | ||
971 | foo.i | ||
972 | } | ||
973 | } | ||
974 | "#, | ||
975 | ); | ||
976 | } | ||
977 | |||
777 | fn test_rename(text: &str, new_name: &str, expected: &str) { | 978 | fn test_rename(text: &str, new_name: &str, expected: &str) { |
778 | let (analysis, position) = single_file_with_position(text); | 979 | let (analysis, position) = single_file_with_position(text); |
779 | let source_change = analysis.rename(position, new_name).unwrap(); | 980 | let source_change = analysis.rename(position, new_name).unwrap(); |