fix: Fix method resolution snapshotting receiver_ty too early

This commit is contained in:
Lukas Wirth 2024-03-11 15:35:06 +01:00
parent 8f8bcfc131
commit fdc527f096
3 changed files with 48 additions and 42 deletions

View File

@ -438,7 +438,6 @@ pub(crate) fn resolve_with_fallback<T>(
where where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>, T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{ {
// TODO check this vec here
self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
} }

View File

@ -1060,27 +1060,26 @@ fn iterate_method_candidates_by_receiver(
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> 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| { 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); let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
while let Some((self_ty, _)) = autoderef.next() { while let Some((self_ty, _)) = autoderef.next() {
iterate_trait_method_candidates( iterate_trait_method_candidates(

View File

@ -57,27 +57,34 @@ pub(crate) fn unresolved_field(
} }
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> { fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> {
let mut fixes = if d.method_with_same_name_exists { method_fix(ctx, &d.expr) } else { None }; let mut fixes = Vec::new();
if let Some(fix) = add_field_fix(ctx, d) { if d.method_with_same_name_exists {
fixes.get_or_insert_with(Vec::new).extend(fix); 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<Vec<Assist>> { // FIXME: Add Snippet Support
fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Assist> {
// Get the FileRange of the invalid field access // Get the FileRange of the invalid field access
let root = ctx.sema.db.parse_or_expand(d.expr.file_id); let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
let expr = d.expr.value.to_node(&root); let expr = d.expr.value.to_node(&root);
let error_range = ctx.sema.original_range_opt(expr.syntax())?; let error_range = ctx.sema.original_range_opt(expr.syntax())?;
let field_name = d.name.as_str()?;
// Convert the receiver to an ADT // 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 target_module = adt.module(ctx.sema.db);
let suggested_type = let suggested_type =
if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) { if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) {
let display = 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("()")) make::ty(display.as_deref().unwrap_or("()"))
} else { } else {
make::ty("()") make::ty("()")
@ -87,9 +94,6 @@ fn add_field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Opti
return None; return None;
} }
// FIXME: Add Snippet Support
let field_name = d.name.as_str()?;
match adt { match adt {
Adt::Struct(adt_struct) => { Adt::Struct(adt_struct) => {
add_field_to_struct_fix(ctx, adt_struct, field_name, suggested_type, error_range) 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, _ => None,
} }
} }
fn add_variant_to_union( fn add_variant_to_union(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
adt_union: Union, adt_union: Union,
field_name: &str, field_name: &str,
suggested_type: Type, suggested_type: Type,
error_range: FileRange, error_range: FileRange,
) -> Option<Vec<Assist>> { ) -> Option<Assist> {
let adt_source = adt_union.source(ctx.sema.db)?; let adt_source = adt_union.source(ctx.sema.db)?;
let adt_syntax = adt_source.syntax(); let adt_syntax = adt_source.syntax();
let Some(field_list) = adt_source.value.record_field_list() else { 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); let mut src_change_builder = SourceChangeBuilder::new(range.file_id);
src_change_builder.insert(offset, record_field); src_change_builder.insert(offset, record_field);
Some(vec![Assist { Some(Assist {
id: AssistId("add-variant-to-union", AssistKind::QuickFix), id: AssistId("add-variant-to-union", AssistKind::QuickFix),
label: Label::new("Add field to union".to_owned()), label: Label::new("Add field to union".to_owned()),
group: None, group: None,
target: error_range.range, target: error_range.range,
source_change: Some(src_change_builder.finish()), source_change: Some(src_change_builder.finish()),
trigger_signature_help: false, trigger_signature_help: false,
}]) })
} }
fn add_field_to_struct_fix( fn add_field_to_struct_fix(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
adt_struct: Struct, adt_struct: Struct,
field_name: &str, field_name: &str,
suggested_type: Type, suggested_type: Type,
error_range: FileRange, error_range: FileRange,
) -> Option<Vec<Assist>> { ) -> Option<Assist> {
let struct_source = adt_struct.source(ctx.sema.db)?; let struct_source = adt_struct.source(ctx.sema.db)?;
let struct_syntax = struct_source.syntax(); let struct_syntax = struct_source.syntax();
let struct_range = struct_syntax.original_file_range(ctx.sema.db); 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 // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563
src_change_builder.insert(offset, record_field); src_change_builder.insert(offset, record_field);
Some(vec![Assist { Some(Assist {
id: AssistId("add-field-to-record-struct", AssistKind::QuickFix), id: AssistId("add-field-to-record-struct", AssistKind::QuickFix),
label: Label::new("Add field to Record Struct".to_owned()), label: Label::new("Add field to Record Struct".to_owned()),
group: None, group: None,
target: error_range.range, target: error_range.range,
source_change: Some(src_change_builder.finish()), source_change: Some(src_change_builder.finish()),
trigger_signature_help: false, trigger_signature_help: false,
}]) })
} }
None => { None => {
// Add a field list to the Unit Struct // 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()); 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), id: AssistId("convert-unit-struct-to-record-struct", AssistKind::QuickFix),
label: Label::new("Convert Unit Struct to Record Struct and add field".to_owned()), label: Label::new("Convert Unit Struct to Record Struct and add field".to_owned()),
group: None, group: None,
target: error_range.range, target: error_range.range,
source_change: Some(src_change_builder.finish()), source_change: Some(src_change_builder.finish()),
trigger_signature_help: false, trigger_signature_help: false,
}]) })
} }
Some(FieldList::TupleFieldList(_tuple)) => { Some(FieldList::TupleFieldList(_tuple)) => {
// FIXME: Add support for Tuple Structs. Tuple Structs are not sent to this diagnostic // 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. /// Used to determine the layout of the record field in the struct.
fn record_field_layout( fn record_field_layout(
visibility: Option<Visibility>, visibility: Option<Visibility>,
@ -242,15 +249,16 @@ fn record_field_layout(
Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}"))) 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 // FIXME: We should fill out the call here, move the cursor and trigger signature help
fn method_fix( fn method_fix(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>, expr_ptr: &InFile<AstPtr<ast::Expr>>,
) -> Option<Vec<Assist>> { ) -> Option<Assist> {
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root); let expr = expr_ptr.value.to_node(&root);
let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; 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), id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix),
label: Label::new("Use parentheses to call the method".to_owned()), label: Label::new("Use parentheses to call the method".to_owned()),
group: None, group: None,
@ -260,7 +268,7 @@ fn method_fix(
TextEdit::insert(range.end(), "()".to_owned()), TextEdit::insert(range.end(), "()".to_owned()),
)), )),
trigger_signature_help: false, trigger_signature_help: false,
}]) })
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {