aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs57
-rw-r--r--crates/ra_ide/src/references/rename.rs211
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
28scoped-tls = "1" 28scoped-tls = "1"
29 29
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } 30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" }
31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } 31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" }
32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } 32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35insta = "0.16.0" 35insta = "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 &parameter_kinds 267 &parameter_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
897pub(crate) fn program_clauses_for_chalk_env_query( 916pub(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};
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; 4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use 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};
9use ra_text_edit::TextEdit; 10use ra_text_edit::TextEdit;
11use std::convert::TryInto;
10use test_utils::tested_by; 12use test_utils::tested_by;
11 13
12use crate::{ 14use 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
17pub(crate) fn rename( 19pub(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
134fn 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
177fn 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
201fn 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
128fn rename_reference( 240fn 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();