From fdc527f0965a4bf150932976b51f7adc61afe079 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 11 Mar 2024 15:35:06 +0100 Subject: [PATCH] fix: Fix method resolution snapshotting receiver_ty too early --- crates/hir-ty/src/infer/unify.rs | 1 - crates/hir-ty/src/method_resolution.rs | 39 +++++++-------- .../src/handlers/unresolved_field.rs | 50 +++++++++++-------- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 18029adbaf..be7547f9ba 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -438,7 +438,6 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - // TODO check this vec here self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) } diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 3a97c55a67..a679a114b4 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -1060,27 +1060,26 @@ fn iterate_method_candidates_by_receiver( name: Option<&Name>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { + let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); + // We're looking for methods with *receiver* type receiver_ty. These could + // be found in any of the derefs of receiver_ty, so we have to go through + // that, including raw derefs. + table.run_in_snapshot(|table| { + let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); + while let Some((self_ty, _)) = autoderef.next() { + iterate_inherent_methods( + &self_ty, + autoderef.table, + name, + Some(&receiver_ty), + Some(receiver_adjustments.clone()), + visible_from_module, + &mut callback, + )? + } + ControlFlow::Continue(()) + })?; table.run_in_snapshot(|table| { - let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); - // We're looking for methods with *receiver* type receiver_ty. These could - // be found in any of the derefs of receiver_ty, so we have to go through - // that, including raw derefs. - table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); - while let Some((self_ty, _)) = autoderef.next() { - iterate_inherent_methods( - &self_ty, - autoderef.table, - name, - Some(&receiver_ty), - Some(receiver_adjustments.clone()), - visible_from_module, - &mut callback, - )? - } - ControlFlow::Continue(()) - })?; - let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); while let Some((self_ty, _)) = autoderef.next() { iterate_trait_method_candidates( diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 06399e5f00..55e91ceb94 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -57,27 +57,34 @@ pub(crate) fn unresolved_field( } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option> { - let mut fixes = if d.method_with_same_name_exists { method_fix(ctx, &d.expr) } else { None }; - if let Some(fix) = add_field_fix(ctx, d) { - fixes.get_or_insert_with(Vec::new).extend(fix); + let mut fixes = Vec::new(); + if d.method_with_same_name_exists { + fixes.extend(method_fix(ctx, &d.expr)); + } + fixes.extend(field_fix(ctx, d)); + if fixes.is_empty() { + None + } else { + Some(fixes) } - fixes } -fn add_field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option> { +// FIXME: Add Snippet Support +fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option { // Get the FileRange of the invalid field access let root = ctx.sema.db.parse_or_expand(d.expr.file_id); let expr = d.expr.value.to_node(&root); let error_range = ctx.sema.original_range_opt(expr.syntax())?; + let field_name = d.name.as_str()?; // Convert the receiver to an ADT - let adt = d.receiver.as_adt()?; + let adt = d.receiver.strip_references().as_adt()?; let target_module = adt.module(ctx.sema.db); let suggested_type = if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) { let display = - new_field_type.display_source_code(ctx.sema.db, target_module.into(), true).ok(); + new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok(); make::ty(display.as_deref().unwrap_or("()")) } else { make::ty("()") @@ -87,9 +94,6 @@ fn add_field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Opti return None; } - // FIXME: Add Snippet Support - let field_name = d.name.as_str()?; - match adt { Adt::Struct(adt_struct) => { add_field_to_struct_fix(ctx, adt_struct, field_name, suggested_type, error_range) @@ -100,13 +104,14 @@ fn add_field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Opti _ => None, } } + fn add_variant_to_union( ctx: &DiagnosticsContext<'_>, adt_union: Union, field_name: &str, suggested_type: Type, error_range: FileRange, -) -> Option> { +) -> Option { let adt_source = adt_union.source(ctx.sema.db)?; let adt_syntax = adt_source.syntax(); let Some(field_list) = adt_source.value.record_field_list() else { @@ -120,22 +125,23 @@ fn add_variant_to_union( let mut src_change_builder = SourceChangeBuilder::new(range.file_id); src_change_builder.insert(offset, record_field); - Some(vec![Assist { + Some(Assist { id: AssistId("add-variant-to-union", AssistKind::QuickFix), label: Label::new("Add field to union".to_owned()), group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), trigger_signature_help: false, - }]) + }) } + fn add_field_to_struct_fix( ctx: &DiagnosticsContext<'_>, adt_struct: Struct, field_name: &str, suggested_type: Type, error_range: FileRange, -) -> Option> { +) -> Option { let struct_source = adt_struct.source(ctx.sema.db)?; let struct_syntax = struct_source.syntax(); let struct_range = struct_syntax.original_file_range(ctx.sema.db); @@ -162,14 +168,14 @@ fn add_field_to_struct_fix( // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 src_change_builder.insert(offset, record_field); - Some(vec![Assist { + Some(Assist { id: AssistId("add-field-to-record-struct", AssistKind::QuickFix), label: Label::new("Add field to Record Struct".to_owned()), group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), trigger_signature_help: false, - }]) + }) } None => { // Add a field list to the Unit Struct @@ -193,14 +199,14 @@ fn add_field_to_struct_fix( } src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string()); - Some(vec![Assist { + Some(Assist { id: AssistId("convert-unit-struct-to-record-struct", AssistKind::QuickFix), label: Label::new("Convert Unit Struct to Record Struct and add field".to_owned()), group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), trigger_signature_help: false, - }]) + }) } Some(FieldList::TupleFieldList(_tuple)) => { // FIXME: Add support for Tuple Structs. Tuple Structs are not sent to this diagnostic @@ -208,6 +214,7 @@ fn add_field_to_struct_fix( } } } + /// Used to determine the layout of the record field in the struct. fn record_field_layout( visibility: Option, @@ -242,15 +249,16 @@ fn record_field_layout( Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}"))) } + // FIXME: We should fill out the call here, move the cursor and trigger signature help fn method_fix( ctx: &DiagnosticsContext<'_>, expr_ptr: &InFile>, -) -> Option> { +) -> Option { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; - Some(vec![Assist { + Some(Assist { id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix), label: Label::new("Use parentheses to call the method".to_owned()), group: None, @@ -260,7 +268,7 @@ fn method_fix( TextEdit::insert(range.end(), "()".to_owned()), )), trigger_signature_help: false, - }]) + }) } #[cfg(test)] mod tests {