Provide a setting to disable showing rename conflicts

This commit is contained in:
Chayim Refael Friedman 2025-07-07 22:05:33 +03:00
parent ec84b26c1c
commit 562b485585
14 changed files with 108 additions and 28 deletions

View file

@ -9,6 +9,7 @@ use ide_db::{
SnippetCap,
assists::ExprFillDefaultMode,
imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig},
rename::RenameConfig,
};
use crate::AssistKind;
@ -27,6 +28,7 @@ pub struct AssistConfig {
pub code_action_grouping: bool,
pub expr_fill_default: ExprFillDefaultMode,
pub prefer_self_ty: bool,
pub show_rename_conflicts: bool,
}
impl AssistConfig {
@ -46,4 +48,8 @@ impl AssistConfig {
allow_unstable,
}
}
pub fn rename_config(&self) -> RenameConfig {
RenameConfig { show_conflicts: self.show_rename_conflicts }
}
}

View file

@ -62,7 +62,9 @@ pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
"Remove underscore from a used variable",
text_range,
|builder| {
let changes = def.rename(&ctx.sema, new_name, RenameDefinition::Yes).unwrap();
let changes = def
.rename(&ctx.sema, new_name, RenameDefinition::Yes, &ctx.config.rename_config())
.unwrap();
builder.source_change = changes;
},
)

View file

@ -38,6 +38,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
code_action_grouping: true,
expr_fill_default: ExprFillDefaultMode::Todo,
prefer_self_ty: false,
show_rename_conflicts: true,
};
pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
@ -59,6 +60,7 @@ pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
code_action_grouping: false,
expr_fill_default: ExprFillDefaultMode::Todo,
prefer_self_ty: false,
show_rename_conflicts: true,
};
pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
@ -80,6 +82,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
code_action_grouping: true,
expr_fill_default: ExprFillDefaultMode::Todo,
prefer_self_ty: false,
show_rename_conflicts: true,
};
pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
@ -101,6 +104,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
code_action_grouping: true,
expr_fill_default: ExprFillDefaultMode::Todo,
prefer_self_ty: false,
show_rename_conflicts: true,
};
fn assists(

View file

@ -45,6 +45,11 @@ use crate::{
traits::convert_to_def_in_trait,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RenameConfig {
pub show_conflicts: bool,
}
pub type Result<T, E = RenameError> = std::result::Result<T, E>;
#[derive(Debug)]
@ -81,6 +86,7 @@ impl Definition {
sema: &Semantics<'_, RootDatabase>,
new_name: &str,
rename_definition: RenameDefinition,
config: &RenameConfig,
) -> Result<SourceChange> {
// self.krate() returns None if
// self is a built-in attr, built-in type or tool module.
@ -109,10 +115,15 @@ impl Definition {
bail!("Cannot rename a builtin attr.")
}
Definition::SelfType(_) => bail!("Cannot rename `Self`"),
Definition::Macro(mac) => {
rename_reference(sema, Definition::Macro(mac), new_name, rename_definition, edition)
}
def => rename_reference(sema, def, new_name, rename_definition, edition),
Definition::Macro(mac) => rename_reference(
sema,
Definition::Macro(mac),
new_name,
rename_definition,
edition,
config,
),
def => rename_reference(sema, def, new_name, rename_definition, edition, config),
}
}
@ -338,6 +349,7 @@ fn rename_reference(
new_name: &str,
rename_definition: RenameDefinition,
edition: Edition,
config: &RenameConfig,
) -> Result<SourceChange> {
let (mut new_name, ident_kind) = IdentifierKind::classify(edition, new_name)?;
@ -396,7 +408,8 @@ fn rename_reference(
if rename_definition == RenameDefinition::Yes {
// This needs to come after the references edits, because we change the annotation of existing edits
// if a conflict is detected.
let (file_id, edit) = source_edit_from_def(sema, def, &new_name, &mut source_change)?;
let (file_id, edit) =
source_edit_from_def(sema, config, def, &new_name, &mut source_change)?;
source_change.insert_source_edit(file_id, edit);
}
Ok(source_change)
@ -554,6 +567,7 @@ fn source_edit_from_name_ref(
fn source_edit_from_def(
sema: &Semantics<'_, RootDatabase>,
config: &RenameConfig,
def: Definition,
new_name: &Name,
source_change: &mut SourceChange,
@ -562,21 +576,22 @@ fn source_edit_from_def(
if let Definition::Local(local) = def {
let mut file_id = None;
let conflict_annotation = if !sema.rename_conflicts(&local, new_name).is_empty() {
Some(
source_change.insert_annotation(ChangeAnnotation {
label: "This rename will change the program's meaning".to_owned(),
needs_confirmation: true,
description: Some(
"Some variable(s) will shadow the renamed variable \
let conflict_annotation =
if config.show_conflicts && !sema.rename_conflicts(&local, new_name).is_empty() {
Some(
source_change.insert_annotation(ChangeAnnotation {
label: "This rename will change the program's meaning".to_owned(),
needs_confirmation: true,
description: Some(
"Some variable(s) will shadow the renamed variable \
or be shadowed by it if the rename is performed"
.to_owned(),
),
}),
)
} else {
None
};
.to_owned(),
),
}),
)
} else {
None
};
for source in local.sources(sema.db) {
let source = match source.source.clone().original_ast_node_rooted(sema.db) {

View file

@ -44,7 +44,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Ass
let label = format!("Rename to {}", d.suggested_text);
let mut res = unresolved_fix("change_case", &label, frange.range);
if ctx.resolve.should_resolve(&res.id) {
let source_change = def.rename(&ctx.sema, &d.suggested_text, RenameDefinition::Yes);
let source_change = def.rename(
&ctx.sema,
&d.suggested_text,
RenameDefinition::Yes,
&ctx.config.rename_config(),
);
res.source_change = Some(source_change.ok().unwrap_or_default());
}

View file

@ -96,6 +96,7 @@ use ide_db::{
generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup},
imports::insert_use::InsertUseConfig,
label::Label,
rename::RenameConfig,
source_change::SourceChange,
syntax_helpers::node_ext::parse_tt_as_comma_sep_paths,
};
@ -236,6 +237,7 @@ pub struct DiagnosticsConfig {
pub prefer_absolute: bool,
pub term_search_fuel: u64,
pub term_search_borrowck: bool,
pub show_rename_conflicts: bool,
}
impl DiagnosticsConfig {
@ -264,8 +266,13 @@ impl DiagnosticsConfig {
prefer_absolute: false,
term_search_fuel: 400,
term_search_borrowck: true,
show_rename_conflicts: true,
}
}
pub fn rename_config(&self) -> RenameConfig {
RenameConfig { show_conflicts: self.show_rename_conflicts }
}
}
struct DiagnosticsContext<'a> {

View file

@ -846,8 +846,9 @@ impl Analysis {
&self,
file_id: FileId,
new_name_stem: &str,
config: &RenameConfig,
) -> Cancellable<Option<SourceChange>> {
self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem))
self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem, config))
}
pub fn structural_search_replace(

View file

@ -31,6 +31,7 @@ pub struct RenameConfig {
pub prefer_no_std: bool,
pub prefer_prelude: bool,
pub prefer_absolute: bool,
pub show_conflicts: bool,
}
impl RenameConfig {
@ -42,6 +43,10 @@ impl RenameConfig {
allow_unstable: true,
}
}
fn ide_db_config(&self) -> ide_db::rename::RenameConfig {
ide_db::rename::RenameConfig { show_conflicts: self.show_conflicts }
}
}
/// This is similar to `collect::<Result<Vec<_>, _>>`, but unlike it, it succeeds if there is *any* `Ok` item.
@ -190,7 +195,7 @@ pub(crate) fn rename(
return rename_to_self(&sema, local);
}
}
def.rename(&sema, new_name.as_str(), rename_def)
def.rename(&sema, new_name.as_str(), rename_def, &config.ide_db_config())
})),
};
@ -205,11 +210,13 @@ pub(crate) fn will_rename_file(
db: &RootDatabase,
file_id: FileId,
new_name_stem: &str,
config: &RenameConfig,
) -> Option<SourceChange> {
let sema = Semantics::new(db);
let module = sema.file_to_module_def(file_id)?;
let def = Definition::Module(module);
let mut change = def.rename(&sema, new_name_stem, RenameDefinition::Yes).ok()?;
let mut change =
def.rename(&sema, new_name_stem, RenameDefinition::Yes, &config.ide_db_config()).ok()?;
change.file_system_edits.clear();
Some(change)
}
@ -803,8 +810,12 @@ mod tests {
use super::{RangeInfo, RenameConfig, RenameError};
const TEST_CONFIG: RenameConfig =
RenameConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false };
const TEST_CONFIG: RenameConfig = RenameConfig {
prefer_no_std: false,
prefer_prelude: true,
prefer_absolute: false,
show_conflicts: true,
};
#[track_caller]
fn check(
@ -893,7 +904,7 @@ mod tests {
) {
let (analysis, position) = fixture::position(ra_fixture);
let source_change = analysis
.will_rename_file(position.file_id, new_name)
.will_rename_file(position.file_id, new_name, &TEST_CONFIG)
.unwrap()
.expect("Expect returned a RenameError");
expect.assert_eq(&filter_expect(source_change))

View file

@ -1153,6 +1153,7 @@ impl flags::AnalysisStats {
style_lints: false,
term_search_fuel: 400,
term_search_borrowck: true,
show_rename_conflicts: true,
},
ide::AssistResolveStrategy::All,
analysis.editioned_file_id_to_vfs(file_id),

View file

@ -707,6 +707,9 @@ config_data! {
///
/// E.g. `use ::std::io::Read;`.
imports_prefixExternPrelude: bool = false,
/// Whether to warn when a rename will cause conflicts (change the meaning of the code).
rename_showConflicts: bool = true,
}
}
@ -1702,6 +1705,7 @@ impl Config {
ExprFillDefaultDef::Underscore => ExprFillDefaultMode::Underscore,
},
prefer_self_ty: *self.assist_preferSelf(source_root),
show_rename_conflicts: *self.rename_showConflicts(source_root),
}
}
@ -1710,6 +1714,7 @@ impl Config {
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
show_conflicts: *self.rename_showConflicts(source_root),
}
}
@ -1809,6 +1814,7 @@ impl Config {
style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(),
term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
show_rename_conflicts: *self.rename_showConflicts(source_root),
}
}

View file

@ -787,7 +787,11 @@ pub(crate) fn handle_will_rename_files(
}
})
.filter_map(|(file_id, new_name)| {
snap.analysis.will_rename_file(file_id?, &new_name).ok()?
let file_id = file_id?;
let source_root = snap.analysis.source_root_id(file_id).ok();
snap.analysis
.will_rename_file(file_id, &new_name, &snap.config.rename(source_root))
.ok()?
})
.collect();

View file

@ -363,6 +363,7 @@ fn integrated_diagnostics_benchmark() {
prefer_absolute: false,
term_search_fuel: 400,
term_search_borrowck: true,
show_rename_conflicts: true,
};
host.analysis()
.full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)

View file

@ -1303,6 +1303,13 @@ Default: `false`
Exclude tests from find-all-references and call-hierarchy.
## rust-analyzer.rename.showConflicts {#rename.showConflicts}
Default: `true`
Whether to warn when a rename will cause conflicts (change the meaning of the code).
## rust-analyzer.runnables.command {#runnables.command}
Default: `null`

View file

@ -2769,6 +2769,16 @@
}
}
},
{
"title": "Rename",
"properties": {
"rust-analyzer.rename.showConflicts": {
"markdownDescription": "Whether to warn when a rename will cause conflicts (change the meaning of the code).",
"default": true,
"type": "boolean"
}
}
},
{
"title": "Runnables",
"properties": {