| mod builtin_derives; | |
| mod closure_captures; | |
| mod coercion; | |
| mod consteval; | |
| mod diagnostics; | |
| mod display_source_code; | |
| mod dyn_compatibility; | |
| mod incremental; | |
| mod layout; | |
| mod macros; | |
| mod method_resolution; | |
| mod mir; | |
| mod never_type; | |
| mod opaque_types; | |
| mod patterns; | |
| mod regression; | |
| mod simple; | |
| mod trait_aliases; | |
| mod traits; | |
| mod variance; | |
| use base_db::{Crate, EditionedFileId, SourceDatabase}; | |
| use expect_test::Expect; | |
| use hir_def::{ | |
| AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, ModuleDefId, ModuleId, | |
| SyntheticSyntax, VariantId, | |
| expr_store::{Body, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr}, | |
| hir::{BindingId, ClosureKind, Expr, ExprId, Pat, PatId}, | |
| item_scope::ItemScope, | |
| nameres::DefMap, | |
| type_ref::TypeRefId, | |
| }; | |
| use hir_expand::{FileRange, InFile}; | |
| use itertools::Itertools; | |
| use rustc_hash::FxHashMap; | |
| use stdx::format_to; | |
| use syntax::{ | |
| SyntaxNode, | |
| ast::{self, AstNode, HasName}, | |
| }; | |
| use test_fixture::WithFixture; | |
| use crate::{ | |
| Adjustment, InferBodyId, InferenceDiagnostic, InferenceResult, | |
| db::{AnonConstId, HirDatabase}, | |
| display::{DisplayTarget, HirDisplay}, | |
| next_solver::Ty, | |
| setup_tracing, | |
| test_db::TestDB, | |
| }; | |
| // These tests compare the inference results for all expressions in a file | |
| // against snapshots of the expected results using expect. Use | |
| // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. | |
| #[track_caller] | |
| fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) { | |
| check_impl(ra_fixture, false, true, false) | |
| } | |
| #[track_caller] | |
| fn check_types_source_code(#[rust_analyzer::rust_fixture] ra_fixture: &str) { | |
| check_impl(ra_fixture, false, true, true) | |
| } | |
| #[track_caller] | |
| fn check_no_mismatches(#[rust_analyzer::rust_fixture] ra_fixture: &str) { | |
| check_impl(ra_fixture, true, false, false) | |
| } | |
| #[track_caller] | |
| fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { | |
| check_impl(ra_fixture, false, false, false) | |
| } | |
| fn infer_defs<'db>( | |
| db: &'db TestDB, | |
| files: &[EditionedFileId], | |
| ) -> Vec<( | |
| InferBodyId<'db>, | |
| &'db ExpressionStore, | |
| &'db ExpressionStoreSourceMap, | |
| ExprId, | |
| Option<(BindingId, Option<InFile<SelfParamPtr>>)>, | |
| Crate, | |
| )> { | |
| let mut defs = Vec::new(); | |
| let anon_const_data = |anon_const: AnonConstId<'db>, krate| { | |
| let loc = anon_const.loc(db); | |
| let (store, source_map) = ExpressionStore::with_source_map(db, loc.owner); | |
| (InferBodyId::AnonConstId(anon_const), store, source_map, loc.expr, None, krate) | |
| }; | |
| let push_generic_def = |defs: &mut Vec<_>, generic_def: Option<GenericDefId>, krate| { | |
| let Some(generic_def) = generic_def else { return }; | |
| defs.extend( | |
| AnonConstId::all_from_signature(db, generic_def).map(|it| anon_const_data(it, krate)), | |
| ); | |
| }; | |
| let push_def_with_body = |defs: &mut Vec<_>, def_with_body: Option<DefWithBodyId>, krate| { | |
| let Some(def_with_body) = def_with_body else { return }; | |
| let (body, source_map) = Body::with_source_map(db, def_with_body); | |
| let mut root_expr = body.root_expr(); | |
| if source_map.expr_syntax(root_expr).is_err() | |
| && let Expr::Closure { | |
| closure_kind: ClosureKind::Coroutine { .. }, | |
| body: closure_body, | |
| .. | |
| } = body[root_expr] | |
| && let Expr::Block { tail: Some(tail), .. } = body[closure_body] | |
| { | |
| // async fns etc. lower to coroutines without source as the root expr. | |
| root_expr = tail; | |
| } | |
| defs.push(( | |
| InferBodyId::DefWithBodyId(def_with_body), | |
| &***body, | |
| &**source_map, | |
| root_expr, | |
| body.self_param.map(|param| (param.formal, source_map.self_param_syntax())), | |
| krate, | |
| )); | |
| }; | |
| for file_id in files { | |
| let module = db.module_for_file_opt(file_id.file_id(db)); | |
| let module = match module { | |
| Some(m) => m, | |
| None => continue, | |
| }; | |
| let def_map = module.def_map(db); | |
| let krate = module.krate(db); | |
| visit_module(db, def_map, module, &mut |it| { | |
| let generic_def = match it { | |
| ModuleDefId::FunctionId(it) => Some(it.into()), | |
| ModuleDefId::AdtId(it) => Some(it.into()), | |
| ModuleDefId::ConstId(it) => Some(it.into()), | |
| ModuleDefId::StaticId(it) => Some(it.into()), | |
| ModuleDefId::TraitId(it) => Some(it.into()), | |
| ModuleDefId::TypeAliasId(it) => Some(it.into()), | |
| ModuleDefId::ModuleId(_) => None, | |
| ModuleDefId::EnumVariantId(_) => None, | |
| ModuleDefId::BuiltinType(_) => None, | |
| ModuleDefId::MacroId(_) => None, | |
| }; | |
| push_generic_def(&mut defs, generic_def, krate); | |
| let variant_def: Option<VariantId> = match it { | |
| ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it.into()), | |
| ModuleDefId::AdtId(AdtId::UnionId(it)) => Some(it.into()), | |
| ModuleDefId::EnumVariantId(it) => Some(it.into()), | |
| _ => None, | |
| }; | |
| if let Some(variant_def) = variant_def { | |
| defs.extend( | |
| db.field_types_with_diagnostics(variant_def) | |
| .defined_anon_consts() | |
| .iter() | |
| .map(|&it| anon_const_data(it, krate)), | |
| ); | |
| } | |
| let def_with_body = match it { | |
| ModuleDefId::FunctionId(it) => Some(it.into()), | |
| ModuleDefId::EnumVariantId(it) => Some(it.into()), | |
| ModuleDefId::ConstId(it) => Some(it.into()), | |
| ModuleDefId::StaticId(it) => Some(it.into()), | |
| _ => None, | |
| }; | |
| push_def_with_body(&mut defs, def_with_body, krate); | |
| }); | |
| } | |
| defs.retain(|(_, _, source_map, root_expr, _, _)| source_map.expr_syntax(*root_expr).is_ok()); | |
| defs.sort_by_key(|(_, _, source_map, root_expr, _, _)| { | |
| source_map.expr_syntax(*root_expr).unwrap().value.text_range().start() | |
| }); | |
| defs | |
| } | |
| #[track_caller] | |
| fn check_impl( | |
| #[rust_analyzer::rust_fixture] ra_fixture: &str, | |
| allow_none: bool, | |
| only_types: bool, | |
| display_source: bool, | |
| ) { | |
| let _tracing = setup_tracing(); | |
| let (db, files) = TestDB::with_many_files(ra_fixture); | |
| crate::attach_db(&db, || { | |
| let mut had_annotations = false; | |
| let mut mismatches = FxHashMap::default(); | |
| let mut types = FxHashMap::default(); | |
| let mut adjustments = FxHashMap::default(); | |
| for (file_id, annotations) in db.extract_annotations() { | |
| for (range, expected) in annotations { | |
| let file_range = FileRange { file_id, range }; | |
| if only_types { | |
| types.insert(file_range, expected); | |
| } else if let Some(ty) = expected.strip_prefix("type: ") { | |
| types.insert(file_range, ty.to_owned()); | |
| } else if expected.starts_with("expected") { | |
| mismatches.insert(file_range, expected); | |
| } else if let Some(adjs) = expected.strip_prefix("adjustments:") { | |
| adjustments.insert(file_range, adjs.trim().to_owned()); | |
| } else { | |
| panic!("unexpected annotation: {expected} @ {range:?}"); | |
| } | |
| had_annotations = true; | |
| } | |
| } | |
| assert!(had_annotations || allow_none, "no `//^` annotations found"); | |
| let defs = infer_defs(&db, &files); | |
| let mut unexpected_type_mismatches = String::new(); | |
| for (def, store, source_map, _root_expr, _self_param, krate) in defs { | |
| let display_target = DisplayTarget::from_crate(&db, krate); | |
| let inference_result = InferenceResult::of(&db, def); | |
| for (pat, ty) in inference_result.type_of_pat.iter() { | |
| let mut ty = ty.as_ref(); | |
| if let Pat::Bind { id, .. } = store[pat] { | |
| ty = inference_result.type_of_binding[id].as_ref(); | |
| } | |
| let node = match pat_node(source_map, pat, &db) { | |
| Some(value) => value, | |
| None => continue, | |
| }; | |
| let range = node.as_ref().original_file_range_rooted(&db); | |
| if let Some(expected) = types.remove(&range) { | |
| let actual = if display_source { | |
| ty.display_source_code(&db, def.module(&db), true).unwrap() | |
| } else { | |
| ty.display_test(&db, display_target).to_string() | |
| }; | |
| assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); | |
| } | |
| } | |
| for (expr, ty) in inference_result.type_of_expr.iter() { | |
| let ty = ty.as_ref(); | |
| let node = match expr_node(source_map, expr, &db) { | |
| Some(value) => value, | |
| None => continue, | |
| }; | |
| let range = node.as_ref().original_file_range_rooted(&db); | |
| if let Some(expected) = types.remove(&range) { | |
| let actual = if display_source { | |
| ty.display_source_code(&db, def.module(&db), true).unwrap() | |
| } else { | |
| ty.display_test(&db, display_target).to_string() | |
| }; | |
| assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); | |
| } | |
| if let Some(expected) = adjustments.remove(&range) { | |
| let adjustments = inference_result | |
| .expr_adjustments | |
| .get(&expr) | |
| .map_or_else(Default::default, |it| &**it); | |
| assert_eq!( | |
| expected, | |
| adjustments | |
| .iter() | |
| .map(|Adjustment { kind, .. }| format!("{kind:?}")) | |
| .join(", ") | |
| ); | |
| } | |
| } | |
| let type_mismatches = | |
| inference_result.diagnostics().iter().filter_map(|diag| match diag { | |
| InferenceDiagnostic::TypeMismatch { node, expected, found } => { | |
| Some((*node, expected.as_ref(), found.as_ref())) | |
| } | |
| _ => None, | |
| }); | |
| for (expr_or_pat, expected, actual) in type_mismatches { | |
| let Some(node) = (match expr_or_pat.unpack() { | |
| hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(source_map, expr, &db), | |
| hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(source_map, pat, &db), | |
| }) else { | |
| continue; | |
| }; | |
| let range = node.as_ref().original_file_range_rooted(&db); | |
| let actual = format!( | |
| "expected {}, got {}", | |
| expected.display_test(&db, display_target), | |
| actual.display_test(&db, display_target) | |
| ); | |
| match mismatches.remove(&range) { | |
| Some(annotation) => assert_eq!(actual, annotation), | |
| None => { | |
| format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual) | |
| } | |
| } | |
| } | |
| for (type_ref, ty) in inference_result.placeholder_types() { | |
| let node = match type_node(source_map, type_ref, &db) { | |
| Some(value) => value, | |
| None => continue, | |
| }; | |
| let range = node.as_ref().original_file_range_rooted(&db); | |
| if let Some(expected) = types.remove(&range) { | |
| let actual = salsa::attach(&db, || { | |
| if display_source { | |
| ty.display_source_code(&db, def.module(&db), true).unwrap() | |
| } else { | |
| ty.display_test(&db, display_target).to_string() | |
| } | |
| }); | |
| assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); | |
| } | |
| } | |
| } | |
| let mut buf = String::new(); | |
| if !unexpected_type_mismatches.is_empty() { | |
| format_to!(buf, "Unexpected type mismatches:\n{}", unexpected_type_mismatches); | |
| } | |
| if !mismatches.is_empty() { | |
| format_to!(buf, "Unchecked mismatch annotations:\n"); | |
| for m in mismatches { | |
| format_to!(buf, "{:?}: {}\n", m.0.range, m.1); | |
| } | |
| } | |
| if !types.is_empty() { | |
| format_to!(buf, "Unchecked type annotations:\n"); | |
| for t in types { | |
| format_to!(buf, "{:?}: type {}\n", t.0.range, t.1); | |
| } | |
| } | |
| if !adjustments.is_empty() { | |
| format_to!(buf, "Unchecked adjustments annotations:\n"); | |
| for t in adjustments { | |
| format_to!(buf, "{:?}: type {:?}\n", t.0.range, t.1); | |
| } | |
| } | |
| assert!(buf.is_empty(), "{}", buf); | |
| }); | |
| } | |
| fn expr_node( | |
| source_map: &ExpressionStoreSourceMap, | |
| expr: ExprId, | |
| db: &TestDB, | |
| ) -> Option<InFile<SyntaxNode>> { | |
| Some(match source_map.expr_syntax(expr) { | |
| Ok(sp) => { | |
| let root = sp.file_id.parse_or_expand(db); | |
| sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | |
| } | |
| Err(SyntheticSyntax) => return None, | |
| }) | |
| } | |
| fn pat_node( | |
| source_map: &ExpressionStoreSourceMap, | |
| pat: PatId, | |
| db: &TestDB, | |
| ) -> Option<InFile<SyntaxNode>> { | |
| Some(match source_map.pat_syntax(pat) { | |
| Ok(sp) => { | |
| let root = sp.file_id.parse_or_expand(db); | |
| sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | |
| } | |
| Err(SyntheticSyntax) => return None, | |
| }) | |
| } | |
| fn type_node( | |
| source_map: &ExpressionStoreSourceMap, | |
| type_ref: TypeRefId, | |
| db: &TestDB, | |
| ) -> Option<InFile<SyntaxNode>> { | |
| Some(match source_map.type_syntax(type_ref) { | |
| Ok(sp) => { | |
| let root = sp.file_id.parse_or_expand(db); | |
| sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | |
| } | |
| Err(SyntheticSyntax) => return None, | |
| }) | |
| } | |
| fn infer(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { | |
| infer_with_mismatches(ra_fixture, false) | |
| } | |
| fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | |
| let _tracing = setup_tracing(); | |
| let (db, file_id) = TestDB::with_single_file(content); | |
| crate::attach_db(&db, || { | |
| let mut buf = String::new(); | |
| let mut infer_def = |inference_result: &InferenceResult<'_>, | |
| store: &ExpressionStore, | |
| source_map: &ExpressionStoreSourceMap, | |
| self_param: Option<(BindingId, Option<InFile<SelfParamPtr>>)>, | |
| krate: Crate| { | |
| let display_target = DisplayTarget::from_crate(&db, krate); | |
| let mut types: Vec<(InFile<SyntaxNode>, Ty<'_>)> = Vec::new(); | |
| let type_mismatch_for_node = inference_result | |
| .diagnostics() | |
| .iter() | |
| .filter_map(|diag| match diag { | |
| InferenceDiagnostic::TypeMismatch { node, expected, found } => { | |
| Some((*node, (expected.as_ref(), found.as_ref()))) | |
| } | |
| _ => None, | |
| }) | |
| .collect::<FxHashMap<_, _>>(); | |
| let mut mismatches: Vec<(InFile<SyntaxNode>, (Ty<'_>, Ty<'_>))> = Vec::new(); | |
| if let Some((binding_id, syntax_ptr)) = self_param { | |
| let ty = &inference_result.type_of_binding[binding_id]; | |
| if let Some(syntax_ptr) = syntax_ptr { | |
| let root = syntax_ptr.file_id.parse_or_expand(&db); | |
| let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); | |
| types.push((node, ty.as_ref())); | |
| } | |
| } | |
| for (pat, mut ty) in inference_result.type_of_pat.iter() { | |
| if let Pat::Bind { id, .. } = store[pat] { | |
| ty = &inference_result.type_of_binding[id]; | |
| } | |
| let node = match source_map.pat_syntax(pat) { | |
| Ok(sp) => { | |
| let root = sp.file_id.parse_or_expand(&db); | |
| sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | |
| } | |
| Err(SyntheticSyntax) => continue, | |
| }; | |
| types.push((node.clone(), ty.as_ref())); | |
| if let Some(mismatch) = type_mismatch_for_node.get(&pat.into()) { | |
| mismatches.push((node, *mismatch)); | |
| } | |
| } | |
| for (expr, ty) in inference_result.type_of_expr.iter() { | |
| let node = match source_map.expr_syntax(expr) { | |
| Ok(sp) => { | |
| let root = sp.file_id.parse_or_expand(&db); | |
| sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | |
| } | |
| Err(SyntheticSyntax) => continue, | |
| }; | |
| types.push((node.clone(), ty.as_ref())); | |
| if let Some(mismatch) = type_mismatch_for_node.get(&expr.into()) { | |
| mismatches.push((node, *mismatch)); | |
| } | |
| } | |
| // sort ranges for consistency | |
| types.sort_by_key(|(node, _)| { | |
| let range = node.value.text_range(); | |
| (range.start(), range.end()) | |
| }); | |
| for (node, ty) in &types { | |
| let (range, text) = | |
| if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) { | |
| (self_param.name().unwrap().syntax().text_range(), "self".to_owned()) | |
| } else { | |
| (node.value.text_range(), node.value.text().to_string().replace('\n', " ")) | |
| }; | |
| let macro_prefix = if node.file_id != file_id { "!" } else { "" }; | |
| format_to!( | |
| buf, | |
| "{}{:?} '{}': {}\n", | |
| macro_prefix, | |
| range, | |
| ellipsize(text, 15), | |
| ty.display_test(&db, display_target) | |
| ); | |
| } | |
| if include_mismatches { | |
| mismatches.sort_by_key(|(node, _)| { | |
| let range = node.value.text_range(); | |
| (range.start(), range.end()) | |
| }); | |
| for (src_ptr, (expected, actual)) in &mismatches { | |
| let range = src_ptr.value.text_range(); | |
| let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" }; | |
| format_to!( | |
| buf, | |
| "{}{:?}: expected {}, got {}\n", | |
| macro_prefix, | |
| range, | |
| expected.display_test(&db, display_target), | |
| actual.display_test(&db, display_target), | |
| ); | |
| } | |
| } | |
| }; | |
| let defs = infer_defs(&db, &[file_id]); | |
| for (def, store, source_map, _root_expr, self_param, krate) in defs { | |
| let infer = InferenceResult::of(&db, def); | |
| infer_def(infer, store, source_map, self_param, krate); | |
| } | |
| buf.truncate(buf.trim_end().len()); | |
| buf | |
| }) | |
| } | |
| pub(crate) fn visit_module( | |
| db: &TestDB, | |
| crate_def_map: &DefMap, | |
| module_id: ModuleId, | |
| cb: &mut dyn FnMut(ModuleDefId), | |
| ) { | |
| visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb); | |
| for impl_id in crate_def_map[module_id].scope.impls() { | |
| let impl_data = impl_id.impl_items(db); | |
| for &(_, item) in impl_data.items.iter() { | |
| match item { | |
| AssocItemId::FunctionId(it) => { | |
| let body = Body::of(db, it.into()); | |
| cb(it.into()); | |
| visit_body(db, body, cb); | |
| } | |
| AssocItemId::ConstId(it) => { | |
| let body = Body::of(db, it.into()); | |
| cb(it.into()); | |
| visit_body(db, body, cb); | |
| } | |
| AssocItemId::TypeAliasId(it) => { | |
| cb(it.into()); | |
| } | |
| } | |
| } | |
| } | |
| fn visit_scope( | |
| db: &TestDB, | |
| crate_def_map: &DefMap, | |
| scope: &ItemScope, | |
| cb: &mut dyn FnMut(ModuleDefId), | |
| ) { | |
| for decl in scope.declarations() { | |
| cb(decl); | |
| match decl { | |
| ModuleDefId::FunctionId(it) => { | |
| let body = Body::of(db, it.into()); | |
| visit_body(db, body, cb); | |
| } | |
| ModuleDefId::ConstId(it) => { | |
| let body = Body::of(db, it.into()); | |
| visit_body(db, body, cb); | |
| } | |
| ModuleDefId::StaticId(it) => { | |
| let body = Body::of(db, it.into()); | |
| visit_body(db, body, cb); | |
| } | |
| ModuleDefId::AdtId(AdtId::EnumId(it)) => { | |
| it.enum_variants(db).variants.values().for_each(|&(it, _)| { | |
| let body = Body::of(db, it.into()); | |
| cb(it.into()); | |
| visit_body(db, body, cb); | |
| }); | |
| } | |
| ModuleDefId::TraitId(it) => { | |
| let trait_data = it.trait_items(db); | |
| for &(_, item) in trait_data.items.iter() { | |
| match item { | |
| AssocItemId::FunctionId(it) => cb(it.into()), | |
| AssocItemId::ConstId(it) => cb(it.into()), | |
| AssocItemId::TypeAliasId(it) => cb(it.into()), | |
| } | |
| } | |
| } | |
| ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it, cb), | |
| _ => (), | |
| } | |
| } | |
| } | |
| fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(ModuleDefId)) { | |
| for (_, def_map) in body.blocks(db) { | |
| for (mod_id, _) in def_map.modules() { | |
| visit_module(db, def_map, mod_id, cb); | |
| } | |
| } | |
| } | |
| } | |
| fn ellipsize(mut text: String, max_len: usize) -> String { | |
| if text.len() <= max_len { | |
| return text; | |
| } | |
| let ellipsis = "..."; | |
| let e_len = ellipsis.len(); | |
| let mut prefix_len = (max_len - e_len) / 2; | |
| while !text.is_char_boundary(prefix_len) { | |
| prefix_len += 1; | |
| } | |
| let mut suffix_len = max_len - e_len - prefix_len; | |
| while !text.is_char_boundary(text.len() - suffix_len) { | |
| suffix_len += 1; | |
| } | |
| text.replace_range(prefix_len..text.len() - suffix_len, ellipsis); | |
| text | |
| } | |
| fn check_infer(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { | |
| let mut actual = infer(ra_fixture); | |
| actual.push('\n'); | |
| expect.assert_eq(&actual); | |
| } | |
| fn check_infer_with_mismatches(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { | |
| let mut actual = infer_with_mismatches(ra_fixture, true); | |
| actual.push('\n'); | |
| expect.assert_eq(&actual); | |
| } | |
| #[test] | |
| fn salsa_bug() { | |
| let (mut db, pos) = TestDB::with_position( | |
| " | |
| //- /lib.rs | |
| trait Index { | |
| type Output; | |
| } | |
| type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key; | |
| pub trait UnificationStoreBase: Index<Output = Key<Self>> { | |
| type Key; | |
| fn len(&self) -> usize; | |
| } | |
| pub trait UnificationStoreMut: UnificationStoreBase { | |
| fn push(&mut self, value: Self::Key); | |
| } | |
| fn main() { | |
| let x = 1; | |
| x.push(1);$0 | |
| } | |
| ", | |
| ); | |
| crate::attach_db(&db, || { | |
| let module = db.module_for_file(pos.file_id.file_id(&db)); | |
| let crate_def_map = module.def_map(&db); | |
| visit_module(&db, crate_def_map, module, &mut |def| { | |
| let body_def: DefWithBodyId = match def { | |
| ModuleDefId::FunctionId(it) => it.into(), | |
| ModuleDefId::EnumVariantId(it) => it.into(), | |
| ModuleDefId::ConstId(it) => it.into(), | |
| ModuleDefId::StaticId(it) => it.into(), | |
| _ => return, | |
| }; | |
| InferenceResult::of(&db, body_def); | |
| }); | |
| }); | |
| let new_text = " | |
| //- /lib.rs | |
| trait Index { | |
| type Output; | |
| } | |
| type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key; | |
| pub trait UnificationStoreBase: Index<Output = Key<Self>> { | |
| type Key; | |
| fn len(&self) -> usize; | |
| } | |
| pub trait UnificationStoreMut: UnificationStoreBase { | |
| fn push(&mut self, value: Self::Key); | |
| } | |
| fn main() { | |
| let x = 1; | |
| x.push(1); | |
| } | |
| "; | |
| db.set_file_text(pos.file_id.file_id(&db), new_text); | |
| crate::attach_db(&db, || { | |
| let module = db.module_for_file(pos.file_id.file_id(&db)); | |
| let crate_def_map = module.def_map(&db); | |
| visit_module(&db, crate_def_map, module, &mut |def| { | |
| let body_def: DefWithBodyId = match def { | |
| ModuleDefId::FunctionId(it) => it.into(), | |
| ModuleDefId::EnumVariantId(it) => it.into(), | |
| ModuleDefId::ConstId(it) => it.into(), | |
| ModuleDefId::StaticId(it) => it.into(), | |
| _ => return, | |
| }; | |
| InferenceResult::of(&db, body_def); | |
| }); | |
| }) | |
| } |