@@ -6,11 +6,16 @@ use ide_db::{
66 defs:: { classify_name, classify_name_ref, Definition , NameClass , NameRefClass } ,
77 RootDatabase ,
88} ;
9- use std:: convert:: TryInto ;
9+
10+ use std:: {
11+ convert:: TryInto ,
12+ error:: Error ,
13+ fmt:: { self , Display } ,
14+ } ;
1015use syntax:: {
1116 algo:: find_node_at_offset,
1217 ast:: { self , NameOwner } ,
13- lex_single_valid_syntax_kind , match_ast, AstNode , SyntaxKind , SyntaxNode , SyntaxToken ,
18+ lex_single_syntax_kind , match_ast, AstNode , SyntaxKind , SyntaxNode , SyntaxToken ,
1419} ;
1520use test_utils:: mark;
1621use text_edit:: TextEdit ;
@@ -20,17 +25,37 @@ use crate::{
2025 SourceChange , SourceFileEdit , TextRange , TextSize ,
2126} ;
2227
28+ #[ derive( Debug ) ]
29+ pub struct RenameError ( pub ( crate ) String ) ;
30+
31+ impl fmt:: Display for RenameError {
32+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
33+ Display :: fmt ( & self . 0 , f)
34+ }
35+ }
36+
37+ impl Error for RenameError { }
38+
2339pub ( crate ) fn rename (
2440 db : & RootDatabase ,
2541 position : FilePosition ,
2642 new_name : & str ,
27- ) -> Option < RangeInfo < SourceChange > > {
43+ ) -> Result < RangeInfo < SourceChange > , RenameError > {
2844 let sema = Semantics :: new ( db) ;
2945
30- match lex_single_valid_syntax_kind ( new_name) ? {
31- SyntaxKind :: IDENT | SyntaxKind :: UNDERSCORE => ( ) ,
32- SyntaxKind :: SELF_KW => return rename_to_self ( & sema, position) ,
33- _ => return None ,
46+ match lex_single_syntax_kind ( new_name) {
47+ Some ( res) => match res {
48+ ( SyntaxKind :: IDENT , _) => ( ) ,
49+ ( SyntaxKind :: UNDERSCORE , _) => ( ) ,
50+ ( SyntaxKind :: SELF_KW , _) => return rename_to_self ( & sema, position) ,
51+ ( _, Some ( syntax_error) ) => {
52+ return Err ( RenameError ( format ! ( "Invalid name `{}`: {}" , new_name, syntax_error) ) )
53+ }
54+ ( _, None ) => {
55+ return Err ( RenameError ( format ! ( "Invalid name `{}`: not an identifier" , new_name) ) )
56+ }
57+ } ,
58+ None => return Err ( RenameError ( format ! ( "Invalid name `{}`: not an identifier" , new_name) ) ) ,
3459 }
3560
3661 let source_file = sema. parse ( position. file_id ) ;
@@ -103,7 +128,7 @@ fn rename_mod(
103128 position : FilePosition ,
104129 module : Module ,
105130 new_name : & str ,
106- ) -> Option < RangeInfo < SourceChange > > {
131+ ) -> Result < RangeInfo < SourceChange > , RenameError > {
107132 let mut source_file_edits = Vec :: new ( ) ;
108133 let mut file_system_edits = Vec :: new ( ) ;
109134
@@ -125,51 +150,56 @@ fn rename_mod(
125150
126151 if let Some ( src) = module. declaration_source ( sema. db ) {
127152 let file_id = src. file_id . original_file ( sema. db ) ;
128- let name = src. value . name ( ) ? ;
153+ let name = src. value . name ( ) . unwrap ( ) ;
129154 let edit = SourceFileEdit {
130155 file_id,
131156 edit : TextEdit :: replace ( name. syntax ( ) . text_range ( ) , new_name. into ( ) ) ,
132157 } ;
133158 source_file_edits. push ( edit) ;
134159 }
135160
136- let RangeInfo { range, info : refs } = find_all_refs ( sema, position, None ) ?;
161+ let RangeInfo { range, info : refs } = find_all_refs ( sema, position, None )
162+ . ok_or_else ( || RenameError ( "No references found at position" . to_string ( ) ) ) ?;
137163 let ref_edits = refs
138164 . references
139165 . into_iter ( )
140166 . map ( |reference| source_edit_from_reference ( reference, new_name) ) ;
141167 source_file_edits. extend ( ref_edits) ;
142168
143- Some ( RangeInfo :: new ( range, SourceChange :: from_edits ( source_file_edits, file_system_edits) ) )
169+ Ok ( RangeInfo :: new ( range, SourceChange :: from_edits ( source_file_edits, file_system_edits) ) )
144170}
145171
146172fn rename_to_self (
147173 sema : & Semantics < RootDatabase > ,
148174 position : FilePosition ,
149- ) -> Option < RangeInfo < SourceChange > > {
175+ ) -> Result < RangeInfo < SourceChange > , RenameError > {
150176 let source_file = sema. parse ( position. file_id ) ;
151177 let syn = source_file. syntax ( ) ;
152178
153- let fn_def = find_node_at_offset :: < ast:: Fn > ( syn, position. offset ) ?;
154- let params = fn_def. param_list ( ) ?;
179+ let fn_def = find_node_at_offset :: < ast:: Fn > ( syn, position. offset )
180+ . ok_or_else ( || RenameError ( "No surrounding method declaration found" . to_string ( ) ) ) ?;
181+ let params =
182+ fn_def. param_list ( ) . ok_or_else ( || RenameError ( "Method has no parameters" . to_string ( ) ) ) ?;
155183 if params. self_param ( ) . is_some ( ) {
156- return None ; // method already has self param
184+ return Err ( RenameError ( "Method already has a self parameter" . to_string ( ) ) ) ;
157185 }
158- let first_param = params. params ( ) . next ( ) ?;
186+ let first_param =
187+ params. params ( ) . next ( ) . ok_or_else ( || RenameError ( "Method has no parameters" . into ( ) ) ) ?;
159188 let mutable = match first_param. ty ( ) {
160189 Some ( ast:: Type :: RefType ( rt) ) => rt. mut_token ( ) . is_some ( ) ,
161- _ => return None , // not renaming other types
190+ _ => return Err ( RenameError ( "Not renaming other types" . to_string ( ) ) ) ,
162191 } ;
163192
164- let RangeInfo { range, info : refs } = find_all_refs ( sema, position, None ) ?;
193+ let RangeInfo { range, info : refs } = find_all_refs ( sema, position, None )
194+ . ok_or_else ( || RenameError ( "No reference found at position" . to_string ( ) ) ) ?;
165195
166196 let param_range = first_param. syntax ( ) . text_range ( ) ;
167197 let ( param_ref, usages) : ( Vec < Reference > , Vec < Reference > ) = refs
168198 . into_iter ( )
169199 . partition ( |reference| param_range. intersect ( reference. file_range . range ) . is_some ( ) ) ;
170200
171201 if param_ref. is_empty ( ) {
172- return None ;
202+ return Err ( RenameError ( "Parameter to rename not found" . to_string ( ) ) ) ;
173203 }
174204
175205 let mut edits = usages
@@ -185,7 +215,7 @@ fn rename_to_self(
185215 ) ,
186216 } ) ;
187217
188- Some ( RangeInfo :: new ( range, SourceChange :: from ( edits) ) )
218+ Ok ( RangeInfo :: new ( range, SourceChange :: from ( edits) ) )
189219}
190220
191221fn text_edit_from_self_param (
@@ -216,12 +246,13 @@ fn rename_self_to_param(
216246 position : FilePosition ,
217247 self_token : SyntaxToken ,
218248 new_name : & str ,
219- ) -> Option < RangeInfo < SourceChange > > {
249+ ) -> Result < RangeInfo < SourceChange > , RenameError > {
220250 let source_file = sema. parse ( position. file_id ) ;
221251 let syn = source_file. syntax ( ) ;
222252
223253 let text = sema. db . file_text ( position. file_id ) ;
224- let fn_def = find_node_at_offset :: < ast:: Fn > ( syn, position. offset ) ?;
254+ let fn_def = find_node_at_offset :: < ast:: Fn > ( syn, position. offset )
255+ . ok_or_else ( || RenameError ( "No surrounding method declaration found" . to_string ( ) ) ) ?;
225256 let search_range = fn_def. syntax ( ) . text_range ( ) ;
226257
227258 let mut edits: Vec < SourceFileEdit > = vec ! [ ] ;
@@ -235,7 +266,8 @@ fn rename_self_to_param(
235266 syn. token_at_offset ( offset) . find ( |t| t. kind ( ) == SyntaxKind :: SELF_KW )
236267 {
237268 let edit = if let Some ( ref self_param) = ast:: SelfParam :: cast ( usage. parent ( ) ) {
238- text_edit_from_self_param ( syn, self_param, new_name) ?
269+ text_edit_from_self_param ( syn, self_param, new_name)
270+ . ok_or_else ( || RenameError ( "No target type found" . to_string ( ) ) ) ?
239271 } else {
240272 TextEdit :: replace ( usage. text_range ( ) , String :: from ( new_name) )
241273 } ;
@@ -246,26 +278,29 @@ fn rename_self_to_param(
246278 let range = ast:: SelfParam :: cast ( self_token. parent ( ) )
247279 . map_or ( self_token. text_range ( ) , |p| p. syntax ( ) . text_range ( ) ) ;
248280
249- Some ( RangeInfo :: new ( range, SourceChange :: from ( edits) ) )
281+ Ok ( RangeInfo :: new ( range, SourceChange :: from ( edits) ) )
250282}
251283
252284fn rename_reference (
253285 sema : & Semantics < RootDatabase > ,
254286 position : FilePosition ,
255287 new_name : & str ,
256- ) -> Option < RangeInfo < SourceChange > > {
257- let RangeInfo { range, info : refs } = find_all_refs ( sema, position, None ) ?;
288+ ) -> Result < RangeInfo < SourceChange > , RenameError > {
289+ let RangeInfo { range, info : refs } = match find_all_refs ( sema, position, None ) {
290+ Some ( range_info) => range_info,
291+ None => return Err ( RenameError ( "No references found at position" . to_string ( ) ) ) ,
292+ } ;
258293
259294 let edit = refs
260295 . into_iter ( )
261296 . map ( |reference| source_edit_from_reference ( reference, new_name) )
262297 . collect :: < Vec < _ > > ( ) ;
263298
264299 if edit. is_empty ( ) {
265- return None ;
300+ return Err ( RenameError ( "No references found at position" . to_string ( ) ) ) ;
266301 }
267302
268- Some ( RangeInfo :: new ( range, SourceChange :: from ( edit) ) )
303+ Ok ( RangeInfo :: new ( range, SourceChange :: from ( edit) ) )
269304}
270305
271306#[ cfg( test) ]
@@ -280,25 +315,45 @@ mod tests {
280315 fn check ( new_name : & str , ra_fixture_before : & str , ra_fixture_after : & str ) {
281316 let ra_fixture_after = & trim_indent ( ra_fixture_after) ;
282317 let ( analysis, position) = fixture:: position ( ra_fixture_before) ;
283- let source_change = analysis. rename ( position, new_name) . unwrap ( ) ;
284- let mut text_edit_builder = TextEdit :: builder ( ) ;
285- let mut file_id: Option < FileId > = None ;
286- if let Some ( change) = source_change {
287- for edit in change. info . source_file_edits {
288- file_id = Some ( edit. file_id ) ;
289- for indel in edit. edit . into_iter ( ) {
290- text_edit_builder. replace ( indel. delete , indel. insert ) ;
318+ let rename_result = analysis
319+ . rename ( position, new_name)
320+ . unwrap_or_else ( |err| panic ! ( "Rename to '{}' was cancelled: {}" , new_name, err) ) ;
321+ match rename_result {
322+ Ok ( source_change) => {
323+ let mut text_edit_builder = TextEdit :: builder ( ) ;
324+ let mut file_id: Option < FileId > = None ;
325+ for edit in source_change. info . source_file_edits {
326+ file_id = Some ( edit. file_id ) ;
327+ for indel in edit. edit . into_iter ( ) {
328+ text_edit_builder. replace ( indel. delete , indel. insert ) ;
329+ }
291330 }
331+ let mut result = analysis. file_text ( file_id. unwrap ( ) ) . unwrap ( ) . to_string ( ) ;
332+ text_edit_builder. finish ( ) . apply ( & mut result) ;
333+ assert_eq_text ! ( ra_fixture_after, & * result) ;
292334 }
293- }
294- let mut result = analysis. file_text ( file_id. unwrap ( ) ) . unwrap ( ) . to_string ( ) ;
295- text_edit_builder. finish ( ) . apply ( & mut result) ;
296- assert_eq_text ! ( ra_fixture_after, & * result) ;
335+ Err ( err) => {
336+ if ra_fixture_after. starts_with ( "error:" ) {
337+ let error_message = ra_fixture_after
338+ . chars ( )
339+ . into_iter ( )
340+ . skip ( "error:" . len ( ) )
341+ . collect :: < String > ( ) ;
342+ assert_eq ! ( error_message. trim( ) , err. to_string( ) ) ;
343+ return ;
344+ } else {
345+ panic ! ( "Rename to '{}' failed unexpectedly: {}" , new_name, err)
346+ }
347+ }
348+ } ;
297349 }
298350
299351 fn check_expect ( new_name : & str , ra_fixture : & str , expect : Expect ) {
300352 let ( analysis, position) = fixture:: position ( ra_fixture) ;
301- let source_change = analysis. rename ( position, new_name) . unwrap ( ) . unwrap ( ) ;
353+ let source_change = analysis
354+ . rename ( position, new_name)
355+ . unwrap ( )
356+ . expect ( "Expect returned RangeInfo to be Some, but was None" ) ;
302357 expect. assert_debug_eq ( & source_change)
303358 }
304359
@@ -313,11 +368,30 @@ mod tests {
313368 }
314369
315370 #[ test]
316- fn test_rename_to_invalid_identifier ( ) {
317- let ( analysis, position) = fixture:: position ( r#"fn main() { let i<|> = 1; }"# ) ;
318- let new_name = "invalid!" ;
319- let source_change = analysis. rename ( position, new_name) . unwrap ( ) ;
320- assert ! ( source_change. is_none( ) ) ;
371+ fn test_rename_to_invalid_identifier1 ( ) {
372+ check (
373+ "invalid!" ,
374+ r#"fn main() { let i<|> = 1; }"# ,
375+ "error: Invalid name `invalid!`: not an identifier" ,
376+ ) ;
377+ }
378+
379+ #[ test]
380+ fn test_rename_to_invalid_identifier2 ( ) {
381+ check (
382+ "multiple tokens" ,
383+ r#"fn main() { let i<|> = 1; }"# ,
384+ "error: Invalid name `multiple tokens`: not an identifier" ,
385+ ) ;
386+ }
387+
388+ #[ test]
389+ fn test_rename_to_invalid_identifier3 ( ) {
390+ check (
391+ "let" ,
392+ r#"fn main() { let i<|> = 1; }"# ,
393+ "error: Invalid name `let`: not an identifier" ,
394+ ) ;
321395 }
322396
323397 #[ test]
@@ -349,6 +423,15 @@ fn main() {
349423 ) ;
350424 }
351425
426+ #[ test]
427+ fn test_rename_unresolved_reference ( ) {
428+ check (
429+ "new_name" ,
430+ r#"fn main() { let _ = unresolved_ref<|>; }"# ,
431+ "error: No references found at position" ,
432+ ) ;
433+ }
434+
352435 #[ test]
353436 fn test_rename_for_macro_args ( ) {
354437 check (
0 commit comments