Skip to content

Commit 75bf832

Browse files
bors[bot]Veykril
andauthored
Merge #8540
8540: Prevent being able to rename items that are not part of the workspace r=Veykril a=Veykril This change causes renames that happen on items coming from crates outside the workspace to fail. I believe this should be the right approach, but usage of cargo's workspace might not be entirely correct for preventing these kinds of refactoring from touching things they shouldn't. I'm not entirely sure? cc #6623, this is one of the bigger footguns when it comes to refactoring, especially in combination with import aliases people tend to rename items coming from a crates dependency which this prevents. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
2 parents 0308fd6 + 493aaa1 commit 75bf832

File tree

11 files changed

+103
-30
lines changed

11 files changed

+103
-30
lines changed

crates/ide/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ impl Analysis {
244244
self.with_db(|db| db.parse(file_id).tree())
245245
}
246246

247+
/// Returns true if this file belongs to an immutable library.
248+
pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> {
249+
use ide_db::base_db::SourceDatabaseExt;
250+
self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
251+
}
252+
247253
/// Gets the file's `LineIndex`: data structure to convert between absolute
248254
/// offsets and line/column representation.
249255
pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> {

crates/rust-analyzer/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,17 @@ impl Config {
400400
pub fn will_rename(&self) -> bool {
401401
try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false)
402402
}
403+
pub fn change_annotation_support(&self) -> bool {
404+
try_!(self
405+
.caps
406+
.workspace
407+
.as_ref()?
408+
.workspace_edit
409+
.as_ref()?
410+
.change_annotation_support
411+
.as_ref()?)
412+
.is_some()
413+
}
403414
pub fn code_action_resolve(&self) -> bool {
404415
try_or!(
405416
self.caps

crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@
326326
},
327327
),
328328
document_changes: None,
329+
change_annotations: None,
329330
},
330331
),
331332
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
},
180180
),
181181
document_changes: None,
182+
change_annotations: None,
182183
},
183184
),
184185
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
},
180180
),
181181
document_changes: None,
182+
change_annotations: None,
182183
},
183184
),
184185
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
},
180180
),
181181
document_changes: None,
182+
change_annotations: None,
182183
},
183184
),
184185
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@
339339
},
340340
),
341341
document_changes: None,
342+
change_annotations: None,
342343
},
343344
),
344345
is_preferred: Some(

crates/rust-analyzer/src/diagnostics/to_proto.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ fn map_rust_child_diagnostic(
136136
// FIXME: there's no good reason to use edit_map here....
137137
changes: Some(edit_map),
138138
document_changes: None,
139+
change_annotations: None,
139140
}),
140141
is_preferred: Some(true),
141142
data: None,

crates/rust-analyzer/src/lsp_ext.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit {
312312
pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
313313
#[serde(skip_serializing_if = "Option::is_none")]
314314
pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
315+
#[serde(skip_serializing_if = "Option::is_none")]
316+
pub change_annotations:
317+
Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>,
315318
}
316319

317320
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit {
335338
pub new_text: String,
336339
#[serde(skip_serializing_if = "Option::is_none")]
337340
pub insert_text_format: Option<lsp_types::InsertTextFormat>,
341+
/// The annotation id if this is an annotated
342+
#[serde(skip_serializing_if = "Option::is_none")]
343+
pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>,
338344
}
339345

340346
pub enum HoverRequest {}

crates/rust-analyzer/src/to_proto.rs

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
22
use std::{
3+
iter::once,
34
path::{self, Path},
45
sync::atomic::{AtomicU32, Ordering},
56
};
67

78
use ide::{
8-
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
9-
CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
10-
Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind,
11-
InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity,
12-
SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
9+
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem,
10+
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
11+
Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
12+
InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable,
13+
Severity, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
1314
};
1415
use itertools::Itertools;
1516
use serde_json::to_value;
@@ -174,6 +175,7 @@ pub(crate) fn snippet_text_edit(
174175
range: text_edit.range,
175176
new_text: text_edit.new_text,
176177
insert_text_format,
178+
annotation_id: None,
177179
}
178180
}
179181

@@ -688,6 +690,10 @@ pub(crate) fn goto_definition_response(
688690
}
689691
}
690692

693+
fn outside_workspace_annotation_id() -> String {
694+
String::from("OutsideWorkspace")
695+
}
696+
691697
pub(crate) fn snippet_text_document_edit(
692698
snap: &GlobalStateSnapshot,
693699
is_snippet: bool,
@@ -696,14 +702,21 @@ pub(crate) fn snippet_text_document_edit(
696702
) -> Result<lsp_ext::SnippetTextDocumentEdit> {
697703
let text_document = optional_versioned_text_document_identifier(snap, file_id);
698704
let line_index = snap.file_line_index(file_id)?;
699-
let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
705+
let mut edits: Vec<_> =
706+
edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
707+
708+
if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
709+
for edit in &mut edits {
710+
edit.annotation_id = Some(outside_workspace_annotation_id())
711+
}
712+
}
700713
Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
701714
}
702715

703716
pub(crate) fn snippet_text_document_ops(
704717
snap: &GlobalStateSnapshot,
705718
file_system_edit: FileSystemEdit,
706-
) -> Vec<lsp_ext::SnippetDocumentChangeOperation> {
719+
) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
707720
let mut ops = Vec::new();
708721
match file_system_edit {
709722
FileSystemEdit::CreateFile { dst, initial_contents } => {
@@ -721,6 +734,7 @@ pub(crate) fn snippet_text_document_ops(
721734
range: lsp_types::Range::default(),
722735
new_text: initial_contents,
723736
insert_text_format: Some(lsp_types::InsertTextFormat::PlainText),
737+
annotation_id: None,
724738
};
725739
let edit_file =
726740
lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
@@ -730,33 +744,55 @@ pub(crate) fn snippet_text_document_ops(
730744
FileSystemEdit::MoveFile { src, dst } => {
731745
let old_uri = snap.file_id_to_url(src);
732746
let new_uri = snap.anchored_path(&dst);
733-
let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
734-
old_uri,
735-
new_uri,
736-
options: None,
737-
annotation_id: None,
738-
});
739-
ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file))
747+
let mut rename_file =
748+
lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
749+
if snap.analysis.is_library_file(src) == Ok(true)
750+
&& snap.config.change_annotation_support()
751+
{
752+
rename_file.annotation_id = Some(outside_workspace_annotation_id())
753+
}
754+
ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
755+
rename_file,
756+
)))
740757
}
741758
}
742-
ops
759+
Ok(ops)
743760
}
744761

745762
pub(crate) fn snippet_workspace_edit(
746763
snap: &GlobalStateSnapshot,
747764
source_change: SourceChange,
748765
) -> Result<lsp_ext::SnippetWorkspaceEdit> {
749766
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
767+
750768
for op in source_change.file_system_edits {
751-
let ops = snippet_text_document_ops(snap, op);
769+
let ops = snippet_text_document_ops(snap, op)?;
752770
document_changes.extend_from_slice(&ops);
753771
}
754772
for (file_id, edit) in source_change.source_file_edits {
755773
let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?;
756774
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
757775
}
758-
let workspace_edit =
759-
lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) };
776+
let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
777+
changes: None,
778+
document_changes: Some(document_changes),
779+
change_annotations: None,
780+
};
781+
if snap.config.change_annotation_support() {
782+
workspace_edit.change_annotations = Some(
783+
once((
784+
outside_workspace_annotation_id(),
785+
lsp_types::ChangeAnnotation {
786+
label: String::from("Edit outside of the workspace"),
787+
needs_confirmation: Some(true),
788+
description: Some(String::from(
789+
"This edit lies outside of the workspace and may affect dependencies",
790+
)),
791+
},
792+
))
793+
.collect(),
794+
)
795+
}
760796
Ok(workspace_edit)
761797
}
762798

@@ -784,24 +820,31 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
784820
lsp_types::DocumentChangeOperation::Edit(
785821
lsp_types::TextDocumentEdit {
786822
text_document: edit.text_document,
787-
edits: edit
788-
.edits
789-
.into_iter()
790-
.map(|edit| {
791-
lsp_types::OneOf::Left(lsp_types::TextEdit {
792-
range: edit.range,
793-
new_text: edit.new_text,
794-
})
795-
})
796-
.collect(),
823+
edits: edit.edits.into_iter().map(From::from).collect(),
797824
},
798825
)
799826
}
800827
})
801828
.collect(),
802829
)
803830
}),
804-
change_annotations: None,
831+
change_annotations: snippet_workspace_edit.change_annotations,
832+
}
833+
}
834+
}
835+
836+
impl From<lsp_ext::SnippetTextEdit>
837+
for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
838+
{
839+
fn from(
840+
lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
841+
) -> Self {
842+
match annotation_id {
843+
Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
844+
text_edit: lsp_types::TextEdit { range, new_text },
845+
annotation_id,
846+
}),
847+
None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
805848
}
806849
}
807850
}

0 commit comments

Comments
 (0)