diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide/src')
72 files changed, 2011 insertions, 1555 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index e47891bbdfe..3d71da985b2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -1,17 +1,17 @@ use hir::{HasSource, InFile, InRealFile, Semantics}; use ide_db::{ - defs::Definition, helpers::visit_file_defs, FileId, FilePosition, FileRange, FxIndexSet, - RootDatabase, + FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition, + helpers::visit_file_defs, }; use itertools::Itertools; -use syntax::{ast::HasName, AstNode, TextRange}; +use syntax::{AstNode, TextRange, ast::HasName}; use crate::{ + NavigationTarget, RunnableKind, annotations::fn_references::find_all_methods, goto_implementation::goto_implementation, references::find_all_refs, - runnables::{runnables, Runnable}, - NavigationTarget, RunnableKind, + runnables::{Runnable, runnables}, }; mod fn_references; @@ -149,7 +149,7 @@ pub(crate) fn annotations( source_file_id: FileId, ) -> Option<(TextRange, Option<TextRange>)> { if let Some(InRealFile { file_id, value }) = node.original_ast_node_rooted(db) { - if file_id == source_file_id { + if file_id.file_id(db) == source_file_id { return Some(( value.syntax().text_range(), value.name().map(|name| name.syntax().text_range()), @@ -209,9 +209,9 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; - use crate::{fixture, Annotation, AnnotationConfig}; + use crate::{Annotation, AnnotationConfig, fixture}; use super::AnnotationLocation; diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs index 08cc10509cb..427a2eff820 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs @@ -4,7 +4,7 @@ use hir::Semantics; use ide_assists::utils::test_related_attribute_syn; use ide_db::RootDatabase; -use syntax::{ast, ast::HasName, AstNode, SyntaxNode, TextRange}; +use syntax::{AstNode, SyntaxNode, TextRange, ast, ast::HasName}; use crate::FileId; @@ -34,8 +34,8 @@ fn method_range(item: SyntaxNode) -> Option<(TextRange, Option<TextRange>)> { mod tests { use syntax::TextRange; - use crate::fixture; use crate::TextSize; + use crate::fixture; use std::ops::RangeInclusive; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index afd6f740c42..4b8d07a2533 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -4,14 +4,14 @@ use std::iter; use hir::Semantics; use ide_db::{ + FileRange, FxIndexMap, RootDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, search::FileReference, - FileRange, FxIndexMap, RootDatabase, }; -use syntax::{ast, AstNode, SyntaxKind::IDENT}; +use syntax::{AstNode, SyntaxKind::IDENT, ast}; -use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo, TryToNav}; +use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav, goto_definition}; #[derive(Debug, Clone)] pub struct CallItem { @@ -76,9 +76,9 @@ pub(crate) fn incoming_calls( } let range = sema.original_range(name.syntax()); - calls.add(nav.call_site, range.into()); + calls.add(nav.call_site, range.into_file_id(db)); if let Some(other) = nav.def_site { - calls.add(other, range.into()); + calls.add(other, range.into_file_id(db)); } } } @@ -143,7 +143,7 @@ pub(crate) fn outgoing_calls( Some(nav_target.into_iter().zip(iter::repeat(range))) }) .flatten() - .for_each(|(nav, range)| calls.add(nav, range.into())); + .for_each(|(nav, range)| calls.add(nav, range.into_file_id(db))); Some(calls.into_items()) } @@ -165,7 +165,7 @@ impl CallLocations { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use ide_db::FilePosition; use itertools::Itertools; diff --git a/src/tools/rust-analyzer/crates/ide/src/child_modules.rs b/src/tools/rust-analyzer/crates/ide/src/child_modules.rs new file mode 100644 index 00000000000..b781596187b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/child_modules.rs @@ -0,0 +1,123 @@ +use hir::Semantics; +use ide_db::{FilePosition, RootDatabase}; +use syntax::{ + algo::find_node_at_offset, + ast::{self, AstNode}, +}; + +use crate::NavigationTarget; + +// Feature: Child Modules +// +// Navigates to the child modules of the current module. +// +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Locate child modules** | + +/// This returns `Vec` because a module may be included from several places. +pub(crate) fn child_modules(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { + let sema = Semantics::new(db); + let source_file = sema.parse_guess_edition(position.file_id); + // First go to the parent module which contains the cursor + let module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset); + + match module { + Some(module) => { + // Return all child modules inside the ItemList of the parent module + sema.to_def(&module) + .into_iter() + .flat_map(|module| module.children(db)) + .map(|module| NavigationTarget::from_module_to_decl(db, module).call_site()) + .collect() + } + None => { + // Return all the child modules inside the source file + sema.file_to_module_defs(position.file_id) + .flat_map(|module| module.children(db)) + .map(|module| NavigationTarget::from_module_to_decl(db, module).call_site()) + .collect() + } + } +} + +#[cfg(test)] +mod tests { + use ide_db::FileRange; + + use crate::fixture; + + fn check_child_module(#[rust_analyzer::rust_fixture] ra_fixture: &str) { + let (analysis, position, expected) = fixture::annotations(ra_fixture); + let navs = analysis.child_modules(position).unwrap(); + let navs = navs + .iter() + .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }) + .collect::<Vec<_>>(); + assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs); + } + + #[test] + fn test_resolve_child_module() { + check_child_module( + r#" +//- /lib.rs +$0 +mod foo; + //^^^ + +//- /foo.rs +// empty +"#, + ); + } + + #[test] + fn test_resolve_child_module_on_module_decl() { + check_child_module( + r#" +//- /lib.rs +mod $0foo; +//- /foo.rs +mod bar; + //^^^ + +//- /foo/bar.rs +// empty +"#, + ); + } + + #[test] + fn test_resolve_child_module_for_inline() { + check_child_module( + r#" +//- /lib.rs +mod foo { + mod $0bar { + mod baz {} + } //^^^ +} +"#, + ); + } + + #[test] + fn test_resolve_multi_child_module() { + check_child_module( + r#" +//- /main.rs +$0 +mod foo; + //^^^ +mod bar; + //^^^ +//- /foo.rs +// empty + +//- /bar.rs +// empty +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 8d2ca33bf25..ebbd68bcdf7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -6,28 +6,29 @@ mod tests; mod intra_doc_links; use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; -use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions}; +use pulldown_cmark_to_cmark::{Options as CMarkOptions, cmark_resume_with_options}; use stdx::format_to; use url::Url; -use hir::{db::HirDatabase, sym, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs}; +use hir::{Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs, db::HirDatabase, sym}; use ide_db::{ - base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, + RootDatabase, + base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb}, defs::{Definition, NameClass, NameRefClass}, - documentation::{docs_with_rangemap, Documentation, HasDocs}, + documentation::{Documentation, HasDocs, docs_with_rangemap}, helpers::pick_best_token, - RootDatabase, }; use syntax::{ - ast::{self, IsString}, - match_ast, AstNode, AstToken, + AstNode, AstToken, SyntaxKind::*, - SyntaxNode, SyntaxToken, TextRange, TextSize, T, + SyntaxNode, SyntaxToken, T, TextRange, TextSize, + ast::{self, IsString}, + match_ast, }; use crate::{ - doc_links::intra_doc_links::{parse_intra_doc_link, strip_prefixes_suffixes}, FilePosition, Semantics, + doc_links::intra_doc_links::{parse_intra_doc_link, strip_prefixes_suffixes}, }; /// Web and local links to an item's documentation. @@ -504,9 +505,7 @@ fn get_doc_base_urls( let Some(krate) = krate else { return Default::default() }; let Some(display_name) = krate.display_name(db) else { return Default::default() }; - let crate_data = &db.crate_graph()[krate.into()]; - - let (web_base, local_base) = match &crate_data.origin { + let (web_base, local_base) = match krate.origin(db) { // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself. // FIXME: Use the toolchains channel instead of nightly CrateOrigin::Lang( @@ -598,7 +597,7 @@ fn filename_and_frag_for_def( Definition::Module(m) => match m.name(db) { // `#[doc(keyword = "...")]` is internal used only by rust compiler Some(name) => { - match m.attrs(db).by_key(&sym::doc).find_string_value_in_tt(&sym::keyword) { + match m.attrs(db).by_key(sym::doc).find_string_value_in_tt(sym::keyword) { Some(kw) => { format!("keyword.{}.html", kw) } @@ -628,7 +627,7 @@ fn filename_and_frag_for_def( return Some((def, file, Some(format!("variant.{}", ev.name(db).as_str())))); } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.as_str()) + format!("constant.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { format!("static.{}.html", s.name(db).as_str()) diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs index 6cc240d6524..c331734c785 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs @@ -53,7 +53,7 @@ pub(super) fn strip_prefixes_suffixes(s: &str) -> &str { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use super::*; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index b09e3a3c804..91785be8d8b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,18 +1,19 @@ use std::iter; -use expect_test::{expect, Expect}; +use expect_test::{Expect, expect}; use hir::Semantics; use ide_db::{ + FilePosition, FileRange, RootDatabase, defs::Definition, documentation::{Documentation, HasDocs}, - FilePosition, FileRange, RootDatabase, }; use itertools::Itertools; -use syntax::{ast, match_ast, AstNode, SyntaxNode}; +use syntax::{AstNode, SyntaxNode, ast, match_ast}; use crate::{ + TryToNav, doc_links::{extract_definitions_from_docs, resolve_doc_path_for_def, rewrite_links}, - fixture, TryToNav, + fixture, }; fn check_external_docs( @@ -43,7 +44,7 @@ fn check_external_docs( fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); - let sema = &Semantics::new(&*analysis.db); + let sema = &Semantics::new(&analysis.db); let (cursor_def, docs) = def_under_cursor(sema, &position); let res = rewrite_links(sema.db, docs.as_str(), cursor_def); expect.assert_eq(&res) @@ -54,7 +55,7 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, mut expected) = fixture::annotations(ra_fixture); expected.sort_by_key(key_fn); - let sema = &Semantics::new(&*analysis.db); + let sema = &Semantics::new(&analysis.db); let (cursor_def, docs) = def_under_cursor(sema, &position); let defs = extract_definitions_from_docs(&docs); let actual: Vec<_> = defs @@ -683,7 +684,9 @@ fn rewrite_intra_doc_link_with_anchor() { //! $0[PartialEq#derivable] fn main() {} "#, - expect"], + expect" + ], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index ad4308e06a1..241a702038d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,12 +1,12 @@ use hir::db::ExpandDatabase; -use hir::{ExpandResult, InFile, MacroFileIdExt, Semantics}; -use ide_db::base_db::CrateId; +use hir::{ExpandResult, InFile, Semantics}; use ide_db::{ - helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId, RootDatabase, + FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, + syntax_helpers::prettify_macro_expansion, }; -use span::{Edition, SpanMap, SyntaxContextId, TextRange, TextSize}; +use span::{Edition, SpanMap, SyntaxContext, TextRange, TextSize}; use stdx::format_to; -use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T}; +use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted}; use crate::FilePosition; @@ -99,7 +99,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .display( db, sema.attach_first_edition(position.file_id) - .map(|it| it.edition()) + .map(|it| it.edition(db)) .unwrap_or(Edition::CURRENT), ) .to_string(), @@ -142,7 +142,7 @@ fn expand_macro_recur( sema: &Semantics<'_, RootDatabase>, macro_call: &ast::Item, error: &mut String, - result_span_map: &mut SpanMap<SyntaxContextId>, + result_span_map: &mut SpanMap<SyntaxContext>, offset_in_original_node: TextSize, ) -> Option<SyntaxNode> { let ExpandResult { value: expanded, err } = match macro_call { @@ -170,7 +170,7 @@ fn expand( sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode, error: &mut String, - result_span_map: &mut SpanMap<SyntaxContextId>, + result_span_map: &mut SpanMap<SyntaxContext>, mut offset_in_original_node: i32, ) -> SyntaxNode { let children = expanded.descendants().filter_map(ast::Item::cast); @@ -207,8 +207,8 @@ fn format( kind: SyntaxKind, file_id: FileId, expanded: SyntaxNode, - span_map: &SpanMap<SyntaxContextId>, - krate: CrateId, + span_map: &SpanMap<SyntaxContext>, + krate: Crate, ) -> String { let expansion = prettify_macro_expansion(db, expanded, span_map, krate).to_string(); @@ -234,7 +234,8 @@ fn _format( file_id: FileId, expansion: &str, ) -> Option<String> { - use ide_db::base_db::{FileLoader, SourceDatabase}; + use ide_db::base_db::RootQueryDb; + // hack until we get hygiene working (same character amount to preserve formatting as much as possible) const DOLLAR_CRATE_REPLACE: &str = "__r_a_"; const BUILTIN_REPLACE: &str = "builtin__POUND"; @@ -249,7 +250,7 @@ fn _format( let expansion = format!("{prefix}{expansion}{suffix}"); let &crate_id = db.relevant_crates(file_id).iter().next()?; - let edition = db.crate_graph()[crate_id].edition; + let edition = crate_id.data(db).edition; #[allow(clippy::disallowed_methods)] let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path()); @@ -289,7 +290,7 @@ fn _format( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use crate::fixture; @@ -550,7 +551,7 @@ macro_rules! foo { } fn main() { - let res = fo$0o!(); + fo$0o!() } "#, expect![[r#" @@ -560,6 +561,24 @@ fn main() { } #[test] + fn macro_expand_item_expansion_in_expression_call() { + check( + r#" +macro_rules! foo { + () => {fn f<T>() {}}; +} + +fn main() { + let res = fo$0o!(); +} +"#, + expect![[r#" + foo! + fn f<T>(){}"#]], + ); + } + + #[test] fn macro_expand_derive() { check( r#" @@ -677,4 +696,26 @@ crate::Foo; crate::Foo;"#]], ); } + + #[test] + fn semi_glueing() { + check( + r#" +macro_rules! __log_value { + ($key:ident :$capture:tt =) => {}; +} + +macro_rules! __log { + ($key:tt $(:$capture:tt)? $(= $value:expr)?; $($arg:tt)+) => { + __log_value!($key $(:$capture)* = $($value)*); + }; +} + +__log!(written:%; "Test"$0); + "#, + expect![[r#" + __log! + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index 76414854e91..a374f9752fc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -3,11 +3,11 @@ use std::iter::successors; use hir::Semantics; use ide_db::RootDatabase; use syntax::{ - algo::{self, skip_trivia_token}, - ast::{self, AstNode, AstToken}, Direction, NodeOrToken, SyntaxKind::{self, *}, - SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, T, + SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, + algo::{self, skip_trivia_token}, + ast::{self, AstNode, AstToken}, }; use crate::FileRange; @@ -178,11 +178,7 @@ fn extend_tokens_from_range( .last()?; let range = first.text_range().cover(last.text_range()); - if range.contains_range(original_range) && original_range != range { - Some(range) - } else { - None - } + if range.contains_range(original_range) && original_range != range { Some(range) } else { None } } /// Find the shallowest node with same range, which allows us to traverse siblings. @@ -216,11 +212,7 @@ fn extend_single_word_in_comment_or_string( let to: TextSize = (cursor_position + end_idx).into(); let range = TextRange::new(from, to); - if range.is_empty() { - None - } else { - Some(range + leaf.text_range().start()) - } + if range.is_empty() { None } else { Some(range + leaf.text_range().start()) } } fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextSize) -> TextRange { diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs index 5ed21444307..956379e722d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs @@ -1,6 +1,6 @@ use ide_db::{ - base_db::{CrateOrigin, SourceDatabase}, FileId, FxIndexSet, RootDatabase, + base_db::{CrateOrigin, RootQueryDb}, }; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -20,21 +20,24 @@ pub struct CrateInfo { // //  pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> { - let crate_graph = db.crate_graph(); - crate_graph + db.all_crates() .iter() - .map(|crate_id| &crate_graph[crate_id]) - .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. })) - .map(crate_info) + .copied() + .map(|crate_id| (crate_id.data(db), crate_id.extra_data(db))) + .filter(|(data, _)| !matches!(data.origin, CrateOrigin::Local { .. })) + .map(|(data, extra_data)| crate_info(data, extra_data)) .collect() } -fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo { - let crate_name = crate_name(data); - let version = data.version.clone(); +fn crate_info( + data: &ide_db::base_db::BuiltCrateData, + extra_data: &ide_db::base_db::ExtraCrateData, +) -> CrateInfo { + let crate_name = crate_name(extra_data); + let version = extra_data.version.clone(); CrateInfo { name: crate_name, version, root_file_id: data.root_file_id } } -fn crate_name(data: &ide_db::base_db::CrateData) -> Option<String> { +fn crate_name(data: &ide_db::base_db::ExtraCrateData) -> Option<String> { data.display_name.as_ref().map(|it| it.canonical_name().as_str().to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 52fbab6fa12..347da4e85b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -1,8 +1,8 @@ use ide_db::SymbolKind; use syntax::{ + AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, TextRange, WalkEvent, ast::{self, HasAttrs, HasGenericParams, HasName}, - match_ast, AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, TextRange, - WalkEvent, + match_ast, }; #[derive(Debug, Clone)] @@ -250,7 +250,7 @@ fn structure_token(token: SyntaxToken) -> Option<StructureNode> { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use super::*; diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs index a0612f48d37..fbf89042fae 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs @@ -1,16 +1,16 @@ //! Utilities for creating `Analysis` instances for tests. use test_fixture::ChangeFixture; -use test_utils::{extract_annotations, RangeOrOffset}; +use test_utils::{RangeOrOffset, extract_annotations}; use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; /// Creates analysis for a single file. pub(crate) fn file(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(ra_fixture); + let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); - (host.analysis(), change_fixture.files[0].into()) + (host.analysis(), change_fixture.files[0].file_id(&host.db)) } /// Creates analysis from a multi-file fixture, returns positions marked with $0. @@ -18,23 +18,23 @@ pub(crate) fn position( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, FilePosition) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(ra_fixture); + let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - (host.analysis(), FilePosition { file_id: file_id.into(), offset }) + (host.analysis(), FilePosition { file_id: file_id.file_id(&host.db), offset }) } /// Creates analysis for a single file, returns range marked with a pair of $0. pub(crate) fn range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileRange) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(ra_fixture); + let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let range = range_or_offset.expect_range(); - (host.analysis(), FileRange { file_id: file_id.into(), range }) + (host.analysis(), FileRange { file_id: file_id.file_id(&host.db), range }) } /// Creates analysis for a single file, returns range marked with a pair of $0 or a position marked with $0. @@ -42,11 +42,11 @@ pub(crate) fn range_or_position( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, FileId, RangeOrOffset) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(ra_fixture); + let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); - (host.analysis(), file_id.into(), range_or_offset) + (host.analysis(), file_id.file_id(&host.db), range_or_offset) } /// Creates analysis from a multi-file fixture, returns positions marked with $0. @@ -54,24 +54,25 @@ pub(crate) fn annotations( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(ra_fixture); + let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); + let db = &host.db; let annotations = change_fixture .files .iter() .flat_map(|&file_id| { - let file_text = host.analysis().file_text(file_id.into()).unwrap(); + let file_text = host.analysis().file_text(file_id.file_id(&host.db)).unwrap(); let annotations = extract_annotations(&file_text); annotations .into_iter() - .map(move |(range, data)| (FileRange { file_id: file_id.into(), range }, data)) + .map(move |(range, data)| (FileRange { file_id: file_id.file_id(db), range }, data)) }) .collect(); - (host.analysis(), FilePosition { file_id: file_id.into(), offset }, annotations) + (host.analysis(), FilePosition { file_id: file_id.file_id(&host.db), offset }, annotations) } /// Creates analysis from a multi-file fixture with annotations without $0 @@ -79,19 +80,20 @@ pub(crate) fn annotations_without_marker( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, Vec<(FileRange, String)>) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(ra_fixture); + let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); + let db = &host.db; let annotations = change_fixture .files .iter() .flat_map(|&file_id| { - let file_text = host.analysis().file_text(file_id.into()).unwrap(); + let file_text = host.analysis().file_text(file_id.file_id(db)).unwrap(); let annotations = extract_annotations(&file_text); annotations .into_iter() - .map(move |(range, data)| (FileRange { file_id: file_id.into(), range }, data)) + .map(move |(range, data)| (FileRange { file_id: file_id.file_id(db), range }, data)) }) .collect(); (host.analysis(), annotations) diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index e5a94ff9fe9..194e8c968f7 100755 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -1,9 +1,10 @@ -use ide_db::{syntax_helpers::node_ext::vis_eq, FxHashSet}; +use ide_db::{FxHashSet, syntax_helpers::node_ext::vis_eq}; use syntax::{ - ast::{self, AstNode, AstToken}, - match_ast, Direction, NodeOrToken, SourceFile, + Direction, NodeOrToken, SourceFile, SyntaxKind::{self, *}, TextRange, TextSize, + ast::{self, AstNode, AstToken}, + match_ast, }; use std::hash::Hash; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index 3742edc8db8..38c032d382e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -1,13 +1,13 @@ use hir::{AsAssocItem, Semantics}; use ide_db::{ - defs::{Definition, NameClass, NameRefClass}, RootDatabase, + defs::{Definition, NameClass, NameRefClass}, }; -use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T}; +use syntax::{AstNode, SyntaxKind::*, T, ast, match_ast}; use crate::{ - goto_definition::goto_definition, navigation_target::TryToNav, FilePosition, NavigationTarget, - RangeInfo, + FilePosition, NavigationTarget, RangeInfo, goto_definition::goto_definition, + navigation_target::TryToNav, }; // Feature: Go to Declaration @@ -32,7 +32,7 @@ pub(crate) fn goto_declaration( .descend_into_macros_no_opaque(original_token) .iter() .filter_map(|token| { - let parent = token.parent()?; + let parent = token.value.parent()?; let def = match_ast! { match parent { ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? { @@ -52,7 +52,7 @@ pub(crate) fn goto_declaration( }; let assoc = match def? { Definition::Module(module) => { - return Some(NavigationTarget::from_module_to_decl(db, module)) + return Some(NavigationTarget::from_module_to_decl(db, module)); } Definition::Const(c) => c.as_assoc_item(db), Definition::TypeAlias(ta) => ta.as_assoc_item(db), @@ -69,11 +69,7 @@ pub(crate) fn goto_declaration( .flatten() .collect(); - if info.is_empty() { - goto_definition(db, position) - } else { - Some(RangeInfo::new(range, info)) - } + if info.is_empty() { goto_definition(db, position) } else { Some(RangeInfo::new(range, info)) } } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 60a904233a9..b894e857522 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1,28 +1,28 @@ use std::{iter, mem::discriminant}; use crate::{ + FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, doc_links::token_as_doc_comment, navigation_target::{self, ToNav}, - FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; use hir::{ - sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt, - ModuleDef, Semantics, + AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, }; use ide_db::{ - base_db::{AnchoredPath, FileLoader, SourceDatabase}, + RootDatabase, SymbolKind, + base_db::{AnchoredPath, SourceDatabase}, defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, - RootDatabase, SymbolKind, }; use itertools::Itertools; use span::{Edition, FileId}; use syntax::{ - ast::{self, HasLoopBody}, - match_ast, AstNode, AstToken, + AstNode, AstToken, SyntaxKind::*, - SyntaxNode, SyntaxToken, TextRange, T, + SyntaxNode, SyntaxToken, T, TextRange, + ast::{self, HasLoopBody}, + match_ast, }; // Feature: Go to Definition @@ -43,7 +43,7 @@ pub(crate) fn goto_definition( let sema = &Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); let edition = - sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); + sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER @@ -91,16 +91,19 @@ pub(crate) fn goto_definition( .descend_into_macros_no_opaque(original_token.clone()) .into_iter() .filter_map(|token| { - let parent = token.parent()?; + let parent = token.value.parent()?; - if let Some(token) = ast::String::cast(token.clone()) { - if let Some(x) = try_lookup_include_path(sema, token, file_id) { + let token_file_id = token.file_id; + if let Some(token) = ast::String::cast(token.value.clone()) { + if let Some(x) = + try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id) + { return Some(vec![x]); } } if ast::TokenTree::can_cast(parent.kind()) { - if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) { + if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value) { return Some(vec![x]); } } @@ -204,20 +207,22 @@ fn find_definition_for_known_blanket_dual_impls( fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, - token: ast::String, + token: InFile<ast::String>, file_id: FileId, ) -> Option<NavigationTarget> { - let file = sema.hir_file_for(&token.syntax().parent()?).macro_file()?; + let file = token.file_id.macro_file()?; + + // Check that we are in the eager argument expansion of an include macro + // that is we are the string input of it if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file()) - // Check that we are in the eager argument expansion of an include macro .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none()) { return None; } - let path = token.value().ok()?; + let path = token.value.value().ok()?; let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; - let size = sema.db.file_text(file_id).len().try_into().ok()?; + let size = sema.db.file_text(file_id).text(sema.db).len().try_into().ok()?; Some(NavigationTarget { file_id, full_range: TextRange::new(0.into(), size), @@ -358,7 +363,7 @@ fn nav_for_exit_points( if let Some(FileRange { file_id, range }) = focus_frange { let contains_frange = |nav: &NavigationTarget| { - nav.file_id == file_id && nav.full_range.contains_range(range) + nav.file_id == file_id.file_id(db) && nav.full_range.contains_range(range) }; if let Some(def_site) = nav.def_site.as_mut() { @@ -2047,7 +2052,10 @@ fn main() { ); } + // macros in this position are not yet supported #[test] + // FIXME + #[should_panic] fn goto_doc_include_str() { check( r#" @@ -2190,8 +2198,8 @@ where T : Bound struct A; impl Bound for A{} fn f() { - let gen = Gen::<A>(A); - gen.g$0(); + let g = Gen::<A>(A); + g.g$0(); } "#, ); @@ -2216,8 +2224,8 @@ where T : Bound struct A; impl Bound for A{} fn f() { - let gen = Gen::<A>(A); - gen.g$0(); + let g = Gen::<A>(A); + g.g$0(); } "#, ); @@ -3324,4 +3332,218 @@ fn main() { "#, ); } + + #[test] + fn struct_shadow_by_module() { + check( + r#" +mod foo { + pub mod bar { + // ^^^ + pub type baz = usize; + } +} +struct bar; +fn main() { + use foo::bar; + let x: ba$0r::baz = 5; + +} +"#, + ); + } + + #[test] + fn type_alias_shadow_by_module() { + check( + r#" +mod foo { + pub mod bar { + // ^^^ + pub fn baz() {} + } +} + +trait Qux {} + +fn item<bar: Qux>() { + use foo::bar; + ba$0r::baz(); +} +} +"#, + ); + + check( + r#" +mod foo { + pub mod bar { + // ^^^ + pub fn baz() {} + } +} + +fn item<bar>(x: bar) { + use foo::bar; + let x: bar$0 = x; +} +"#, + ); + } + + #[test] + fn trait_shadow_by_module() { + check( + r#" +pub mod foo { + pub mod Bar {} + // ^^^ +} + +trait Bar {} + +fn main() { + use foo::Bar; + fn f<Qux: B$0ar>() {} +} + "#, + ); + } + + #[test] + fn const_shadow_by_module() { + check( + r#" +pub mod foo { + pub struct u8 {} + pub mod bar { + pub mod u8 {} + } +} + +fn main() { + use foo::u8; + { + use foo::bar::u8; + + fn f1<const N: u$08>() {} + } + fn f2<const N: u8>() {} +} +"#, + ); + + check( + r#" +pub mod foo { + pub struct u8 {} + // ^^ + pub mod bar { + pub mod u8 {} + } +} + +fn main() { + use foo::u8; + { + use foo::bar::u8; + + fn f1<const N: u8>() {} + } + fn f2<const N: u$08>() {} +} +"#, + ); + + check( + r#" +pub mod foo { + pub struct buz {} + pub mod bar { + pub mod buz {} + // ^^^ + } +} + +fn main() { + use foo::buz; + { + use foo::bar::buz; + + fn f1<const N: buz$0>() {} + } +} +"#, + ); + } + + #[test] + fn offset_of() { + check( + r#" +//- minicore: offset_of +struct Foo { + field: i32, + // ^^^^^ +} + +fn foo() { + let _ = core::mem::offset_of!(Foo, fiel$0d); +} + "#, + ); + + check( + r#" +//- minicore: offset_of +struct Bar(Foo); +struct Foo { + field: i32, + // ^^^^^ +} + +fn foo() { + let _ = core::mem::offset_of!(Bar, 0.fiel$0d); +} + "#, + ); + + check( + r#" +//- minicore: offset_of +struct Bar(Baz); +enum Baz { + Abc(Foo), + None, +} +struct Foo { + field: i32, + // ^^^^^ +} + +fn foo() { + let _ = core::mem::offset_of!(Bar, 0.Abc.0.fiel$0d); +} + "#, + ); + + check( + r#" +//- minicore: offset_of +struct Bar(Baz); +enum Baz { + Abc(Foo), + // ^^^ + None, +} +struct Foo { + field: i32, +} + +fn foo() { + let _ = core::mem::offset_of!(Bar, 0.Ab$0c.0.field); +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index e1d834b5d1c..1bc28f28b6f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -1,10 +1,10 @@ use hir::{AsAssocItem, Impl, Semantics}; use ide_db::{ + RootDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, - RootDatabase, }; -use syntax::{ast, AstNode, SyntaxKind::*, T}; +use syntax::{AstNode, SyntaxKind::*, T, ast}; use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index ddc274a8303..a78f5cdc9d0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -1,6 +1,6 @@ use hir::GenericParam; -use ide_db::{base_db::Upcast, defs::Definition, helpers::pick_best_token, RootDatabase}; -use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T}; +use ide_db::{RootDatabase, defs::Definition, helpers::pick_best_token}; +use syntax::{AstNode, SyntaxKind::*, SyntaxToken, T, ast, match_ast}; use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; @@ -71,8 +71,8 @@ pub(crate) fn goto_type_definition( sema.descend_into_macros_no_opaque(token) .into_iter() .filter_map(|token| { - let ty = sema - .token_ancestors_with_macros(token) + sema + .token_ancestors_with_macros(token.value) // When `token` is within a macro call, we can't determine its type. Don't continue // this traversal because otherwise we'll end up returning the type of *that* macro // call, which is not what we want in general. @@ -87,7 +87,7 @@ pub(crate) fn goto_type_definition( ast::Pat(it) => sema.type_of_pat(&it)?.original, ast::SelfParam(it) => sema.type_of_self(&it)?, ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()), + ast::RecordField(it) => sema.to_def(&it)?.ty(db), // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise ast::NameRef(it) => { if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { @@ -103,8 +103,7 @@ pub(crate) fn goto_type_definition( }; Some(ty) - }); - ty + }) }) .for_each(process_ty); Some(RangeInfo::new(range, res)) diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 6463206596a..80624eeae80 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -1,7 +1,8 @@ use std::iter; -use hir::{db, FilePosition, FileRange, HirFileId, InFile, Semantics}; +use hir::{EditionedFileId, FilePosition, FileRange, HirFileId, InFile, Semantics, db}; use ide_db::{ + FxHashMap, FxHashSet, RootDatabase, defs::{Definition, IdentClass}, helpers::pick_best_token, search::{FileReference, ReferenceCategory, SearchScope}, @@ -9,17 +10,17 @@ use ide_db::{ eq_label_lt, for_each_tail_expr, full_path_of_name_ref, is_closure_or_blk_with_modif, preorder_expr_with_ctx_checker, }, - FxHashMap, FxHashSet, RootDatabase, }; -use span::EditionedFileId; +use span::FileId; use syntax::{ - ast::{self, HasLoopBody}, - match_ast, AstNode, + AstNode, SyntaxKind::{self, IDENT, INT_NUMBER}, - SyntaxToken, TextRange, WalkEvent, T, + SyntaxToken, T, TextRange, WalkEvent, + ast::{self, HasLoopBody}, + match_ast, }; -use crate::{goto_definition, navigation_target::ToNav, NavigationTarget, TryToNav}; +use crate::{NavigationTarget, TryToNav, goto_definition, navigation_target::ToNav}; #[derive(PartialEq, Eq, Hash)] pub struct HighlightedRange { @@ -59,13 +60,14 @@ pub(crate) fn highlight_related( let _p = tracing::info_span!("highlight_related").entered(); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition(sema.db, file_id)); + let span_file_id = file_id.editioned_file_id(sema.db); let syntax = sema.parse(file_id).syntax().clone(); let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?` T![->] => 4, - kind if kind.is_keyword(file_id.edition()) => 3, + kind if kind.is_keyword(span_file_id.edition()) => 3, IDENT | INT_NUMBER => 2, T![|] => 1, _ => 0, @@ -87,11 +89,18 @@ pub(crate) fn highlight_related( T![break] | T![loop] | T![while] | T![continue] if config.break_points => { highlight_break_points(sema, token).remove(&file_id) } - T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), - T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), - _ if config.references => { - highlight_references(sema, token, FilePosition { file_id, offset }) + T![|] if config.closure_captures => { + highlight_closure_captures(sema, token, file_id, span_file_id.file_id()) + } + T![move] if config.closure_captures => { + highlight_closure_captures(sema, token, file_id, span_file_id.file_id()) } + _ if config.references => highlight_references( + sema, + token, + FilePosition { file_id, offset }, + span_file_id.file_id(), + ), _ => None, } } @@ -100,6 +109,7 @@ fn highlight_closure_captures( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, file_id: EditionedFileId, + vfs_file_id: FileId, ) -> Option<Vec<HighlightedRange>> { let closure = token.parent_ancestors().take(2).find_map(ast::ClosureExpr::cast)?; let search_range = closure.body()?.syntax().text_range(); @@ -132,7 +142,7 @@ fn highlight_closure_captures( .sources(sema.db) .into_iter() .flat_map(|x| x.to_nav(sema.db)) - .filter(|decl| decl.file_id == file_id) + .filter(|decl| decl.file_id == vfs_file_id) .filter_map(|decl| decl.focus_range) .map(move |range| HighlightedRange { range, category }) .chain(usages) @@ -145,6 +155,7 @@ fn highlight_references( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, FilePosition { file_id, offset }: FilePosition, + vfs_file_id: FileId, ) -> Option<Vec<HighlightedRange>> { let defs = if let Some((range, resolution)) = sema.check_for_format_args_template(token.clone(), offset) @@ -152,7 +163,10 @@ fn highlight_references( match resolution.map(Definition::from) { Some(def) => iter::once(def).collect(), None => { - return Some(vec![HighlightedRange { range, category: ReferenceCategory::empty() }]) + return Some(vec![HighlightedRange { + range, + category: ReferenceCategory::empty(), + }]); } } } else { @@ -224,6 +238,23 @@ fn highlight_references( } } + // highlight the tail expr of the labelled block + if matches!(def, Definition::Label(_)) { + let label = token.parent_ancestors().nth(1).and_then(ast::Label::cast); + if let Some(block) = + label.and_then(|label| label.syntax().parent()).and_then(ast::BlockExpr::cast) + { + for_each_tail_expr(&block.into(), &mut |tail| { + if !matches!(tail, ast::Expr::BreakExpr(_)) { + res.insert(HighlightedRange { + range: tail.syntax().text_range(), + category: ReferenceCategory::empty(), + }); + } + }); + } + } + // highlight the defs themselves match def { Definition::Local(local) => { @@ -236,7 +267,7 @@ fn highlight_references( .sources(sema.db) .into_iter() .flat_map(|x| x.to_nav(sema.db)) - .filter(|decl| decl.file_id == file_id) + .filter(|decl| decl.file_id == vfs_file_id) .filter_map(|decl| decl.focus_range) .map(|range| HighlightedRange { range, category }) .for_each(|x| { @@ -254,7 +285,7 @@ fn highlight_references( }, }; for nav in navs { - if nav.file_id != file_id { + if nav.file_id != vfs_file_id { continue; } let hl_range = nav.focus_range.map(|range| { @@ -274,11 +305,7 @@ fn highlight_references( } res.extend(usages); - if res.is_empty() { - None - } else { - Some(res.into_iter().collect()) - } + if res.is_empty() { None } else { Some(res.into_iter().collect()) } } fn hl_exit_points( @@ -442,6 +469,18 @@ pub(crate) fn highlight_break_points( push_to_highlights(file_id, text_range); }); + if matches!(expr, ast::Expr::BlockExpr(_)) { + for_each_tail_expr(&expr, &mut |tail| { + if matches!(tail, ast::Expr::BreakExpr(_)) { + return; + } + + let file_id = sema.hir_file_for(tail.syntax()); + let range = tail.syntax().text_range(); + push_to_highlights(file_id, Some(range)); + }); + } + Some(highlights) } @@ -2068,4 +2107,41 @@ pub unsafe fn bootstrap() -> ! { "#, ) } + + #[test] + fn labeled_block_tail_expr() { + check( + r#" +fn foo() { + 'a: { + // ^^^ + if true { break$0 'a 0; } + // ^^^^^^^^ + 5 + // ^ + } +} +"#, + ); + } + + #[test] + fn labeled_block_tail_expr_2() { + check( + r#" +fn foo() { + let _ = 'b$0lk: { + // ^^^^ + let x = 1; + if true { break 'blk 42; } + // ^^^^ + if false { break 'blk 24; } + // ^^^^ + 100 + // ^^^ + }; +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index b00de6ba408..2f2d2252f84 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -7,26 +7,30 @@ use std::{iter, ops::Not}; use either::Either; use hir::{ - db::DefDatabase, DisplayTarget, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem, - Semantics, + DisplayTarget, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics, + db::DefDatabase, }; use ide_db::{ + FileRange, FxIndexSet, Ranker, RootDatabase, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, - FileRange, FxIndexSet, Ranker, RootDatabase, }; -use itertools::{multizip, Itertools}; +use itertools::{Itertools, multizip}; use span::Edition; -use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T}; +use syntax::{ + AstNode, + SyntaxKind::{self, *}, + SyntaxNode, T, ast, +}; use crate::{ + FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav, doc_links::token_as_doc_comment, markdown_remove::remove_markdown, markup::Markup, navigation_target::UpmappingResult, runnables::{runnable_fn, runnable_mod}, - FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav, }; #[derive(Clone, Debug, PartialEq, Eq)] pub struct HoverConfig { @@ -129,8 +133,8 @@ pub(crate) fn hover( let sema = &hir::Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); let edition = - sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); - let display_target = sema.first_crate_or_default(file_id).to_display_target(db); + sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); + let display_target = sema.first_crate(file_id)?.to_display_target(db); let mut res = if range.is_empty() { hover_offset( sema, @@ -274,11 +278,13 @@ fn hover_offset( } class => { - let is_def = matches!(class, IdentClass::NameClass(_)); + let render_extras = matches!(class, IdentClass::NameClass(_)) + // Render extra information for `Self` keyword as well + || ast::NameRef::cast(node.clone()).is_some_and(|name_ref| name_ref.token_kind() == SyntaxKind::SELF_TYPE_KW); multizip(( class.definitions(), iter::repeat(None), - iter::repeat(is_def), + iter::repeat(render_extras), iter::repeat(node), )) .collect::<Vec<_>>() @@ -422,7 +428,7 @@ pub(crate) fn hover_for_definition( subst: Option<GenericSubstitution>, scope_node: &SyntaxNode, macro_arm: Option<u32>, - hovered_definition: bool, + render_extras: bool, config: &HoverConfig, edition: Edition, display_target: DisplayTarget, @@ -456,7 +462,7 @@ pub(crate) fn hover_for_definition( famous_defs.as_ref(), ¬able_traits, macro_arm, - hovered_definition, + render_extras, subst_types.as_ref(), config, edition, @@ -499,6 +505,7 @@ fn notable_traits( ) }) }) + .sorted_by_cached_key(|(trait_, _)| trait_.name(db)) .collect::<Vec<_>>() } @@ -512,7 +519,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov let adt = match def { Definition::Trait(it) => { - return it.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) + return it.try_to_nav(db).map(UpmappingResult::call_site).map(to_action); } Definition::Adt(it) => Some(it), Definition::SelfType(it) => it.self_ty(db).as_adt(), @@ -544,7 +551,7 @@ fn runnable_action( Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), Definition::Function(func) => { let src = func.source(sema.db)?; - if src.file_id != file_id { + if src.file_id.file_id().is_none_or(|f| f.file_id(sema.db) != file_id) { cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr); return None; diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 31ef89a07cd..69b83f3b12d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,34 +3,34 @@ use std::{env, mem, ops::Not}; use either::Either; use hir::{ - db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DisplayTarget, DropGlue, + Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DisplayTarget, DropGlue, DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError, MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef, + db::ExpandDatabase, }; use ide_db::{ - base_db::SourceDatabase, + RootDatabase, defs::Definition, documentation::HasDocs, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, syntax_helpers::prettify_macro_expansion, - RootDatabase, }; use itertools::Itertools; use rustc_apfloat::{ - ieee::{Half as f16, Quad as f128}, Float, + ieee::{Half as f16, Quad as f128}, }; use span::Edition; use stdx::format_to; -use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T}; +use syntax::{AstNode, AstToken, Direction, SyntaxToken, T, algo, ast, match_ast}; use crate::{ - doc_links::{remove_links, rewrite_links}, - hover::{notable_traits, walk_and_push_ty, SubstTyLen}, - interpret::render_const_eval_error, HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + doc_links::{remove_links, rewrite_links}, + hover::{SubstTyLen, notable_traits, walk_and_push_ty}, + interpret::render_const_eval_error, }; pub(super) fn type_info_of( @@ -346,11 +346,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<Hove .is_some_and(|t| { t.kind() == T![ident] && t.into_token().is_some_and(|t| t.text() == "clippy") }); - if is_clippy { - (true, CLIPPY_LINTS) - } else { - (false, DEFAULT_LINTS) - } + if is_clippy { (true, CLIPPY_LINTS) } else { (false, DEFAULT_LINTS) } } _ => return None, }; @@ -418,7 +414,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) - "{}::{}", name.display(db, edition), it.name(db).display(db, edition) - )) + )); } None => Some(it.name(db)), } @@ -436,7 +432,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) - "{}::{}", name.display(db, edition), it.name(db)?.display(db, edition) - )) + )); } None => it.name(db), } @@ -466,8 +462,7 @@ pub(super) fn path( item_name: Option<String>, edition: Edition, ) -> String { - let crate_name = - db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string()); + let crate_name = module.krate().display_name(db).as_ref().map(|it| it.to_string()); let module_path = module .path_to_root(db) .into_iter() @@ -482,7 +477,7 @@ pub(super) fn definition( famous_defs: Option<&FamousDefs<'_, '_>>, notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)], macro_arm: Option<u32>, - hovered_definition: bool, + render_extras: bool, subst_types: Option<&Vec<(Symbol, Type)>>, config: &HoverConfig, edition: Edition, @@ -645,6 +640,12 @@ pub(super) fn definition( Definition::Local(it) => { render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None) } + Definition::SelfType(it) => render_memory_layout( + config.memory_layout, + || it.self_ty(db).layout(db), + |_| None, + |_| None, + ), _ => None, }; @@ -717,18 +718,17 @@ pub(super) fn definition( } _ => return None, }; - let rendered_drop_glue = match drop_info.drop_glue { - DropGlue::None => "does not contain types with destructors (drop glue)", - DropGlue::DependOnParams => { - "may contain types with destructors (drop glue) depending on type parameters" + let rendered_drop_glue = if drop_info.has_dtor == Some(true) { + "impl Drop" + } else { + match drop_info.drop_glue { + DropGlue::HasDropGlue => "needs Drop", + DropGlue::None => "no Drop", + DropGlue::DependOnParams => "type param may need Drop", } - DropGlue::HasDropGlue => "contain types with destructors (drop glue)", }; - Some(match drop_info.has_dtor { - Some(true) => format!("{}; has a destructor", rendered_drop_glue), - Some(false) => format!("{}; doesn't have a destructor", rendered_drop_glue), - None => rendered_drop_glue.to_owned(), - }) + + Some(rendered_drop_glue.to_owned()) }; let dyn_compatibility_info = || match def { @@ -746,7 +746,7 @@ pub(super) fn definition( }; let mut extra = String::new(); - if hovered_definition { + if render_extras { if let Some(notable_traits) = render_notable_trait(db, notable_traits, edition, display_target) { @@ -760,15 +760,18 @@ pub(super) fn definition( if let Some(layout_info) = layout_info() { extra.push_str("\n___\n"); extra.push_str(&layout_info); + if let Some(drop_info) = drop_info() { + extra.push_str(", "); + extra.push_str(&drop_info) + } + } else if let Some(drop_info) = drop_info() { + extra.push_str("\n___\n"); + extra.push_str(&drop_info); } if let Some(dyn_compatibility_info) = dyn_compatibility_info() { extra.push_str("\n___\n"); extra.push_str(&dyn_compatibility_info); } - if let Some(drop_info) = drop_info() { - extra.push_str("\n___\n"); - extra.push_str(&drop_info); - } } let mut desc = String::new(); desc.push_str(&label); @@ -906,9 +909,9 @@ fn render_notable_trait( let mut needs_impl_header = true; for (trait_, assoc_types) in notable_traits { desc.push_str(if mem::take(&mut needs_impl_header) { - "Implements notable traits: " + "Implements notable traits: `" } else { - ", " + "`, `" }); format_to!(desc, "{}", trait_.name(db).display(db, edition)); if !assoc_types.is_empty() { @@ -928,7 +931,12 @@ fn render_notable_trait( desc.push('>'); } } - desc.is_empty().not().then_some(desc) + if desc.is_empty() { + None + } else { + desc.push('`'); + Some(desc) + } } fn type_info( @@ -955,37 +963,12 @@ fn type_info( res.markup = if let Some(adjusted_ty) = adjusted { walk_and_push_ty(db, &adjusted_ty, &mut push_new_def); - let notable = { - let mut desc = String::new(); - let mut needs_impl_header = true; - for (trait_, assoc_types) in notable_traits(db, &original) { - desc.push_str(if mem::take(&mut needs_impl_header) { - "Implements Notable Traits: " - } else { - ", " - }); - format_to!(desc, "{}", trait_.name(db).display(db, edition)); - if !assoc_types.is_empty() { - desc.push('<'); - format_to!( - desc, - "{}", - assoc_types.into_iter().format_with(", ", |(ty, name), f| { - f(&name.display(db, edition))?; - f(&" = ")?; - match ty { - Some(ty) => f(&ty.display(db, display_target)), - None => f(&"?"), - } - }) - ); - desc.push('>'); - } - } - if !desc.is_empty() { - desc.push('\n'); - } - desc + let notable = if let Some(notable) = + render_notable_trait(db, ¬able_traits(db, &original), edition, display_target) + { + format!("{notable}\n") + } else { + String::new() }; let original = original.display(db, display_target).to_string(); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 80a2d4690d4..d469cd7c0cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1,9 +1,9 @@ -use expect_test::{expect, Expect}; -use ide_db::{base_db::SourceDatabase, FileRange}; +use expect_test::{Expect, expect}; +use ide_db::{FileRange, base_db::SourceDatabase}; use syntax::TextRange; use crate::{ - fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, fixture, }; const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { @@ -47,7 +47,7 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { .unwrap(); let content = analysis.db.file_text(position.file_id); - let hovered_element = &content[hover.range]; + let hovered_element = &content.text(&analysis.db)[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); expect.assert_eq(&actual) @@ -72,7 +72,7 @@ fn check_hover_fields_limit( .unwrap() .unwrap(); - let content = analysis.db.file_text(position.file_id); + let content = analysis.db.file_text(position.file_id).text(&analysis.db); let hovered_element = &content[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); @@ -98,7 +98,7 @@ fn check_hover_enum_variants_limit( .unwrap() .unwrap(); - let content = analysis.db.file_text(position.file_id); + let content = analysis.db.file_text(position.file_id).text(&analysis.db); let hovered_element = &content[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); @@ -124,7 +124,7 @@ fn check_assoc_count( .unwrap() .unwrap(); - let content = analysis.db.file_text(position.file_id); + let content = analysis.db.file_text(position.file_id).text(&analysis.db); let hovered_element = &content[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); @@ -141,7 +141,7 @@ fn check_hover_no_links(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: .unwrap() .unwrap(); - let content = analysis.db.file_text(position.file_id); + let content = analysis.db.file_text(position.file_id).text(&analysis.db); let hovered_element = &content[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); @@ -158,7 +158,7 @@ fn check_hover_no_memory_layout(#[rust_analyzer::rust_fixture] ra_fixture: &str, .unwrap() .unwrap(); - let content = analysis.db.file_text(position.file_id); + let content = analysis.db.file_text(position.file_id).text(&analysis.db); let hovered_element = &content[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); @@ -179,7 +179,7 @@ fn check_hover_no_markdown(#[rust_analyzer::rust_fixture] ra_fixture: &str, expe .unwrap() .unwrap(); - let content = analysis.db.file_text(position.file_id); + let content = analysis.db.file_text(position.file_id).text(&analysis.db); let hovered_element = &content[hover.range]; let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); @@ -567,11 +567,7 @@ fn main() { --- - size = 8, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 8, align = 4, no Drop "#]], ); } @@ -816,11 +812,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } --- - size = 1, align = 1, offset = 6 - - --- - - does not contain types with destructors (drop glue) + size = 1, align = 1, offset = 6, no Drop "#]], ); } @@ -871,11 +863,7 @@ fn main() { --- - size = 4, align = 4, offset = 0 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, offset = 0, no Drop "#]], ); } @@ -945,11 +933,7 @@ struct Foo$0(pub u32) where u32: Copy; --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); } @@ -975,11 +959,7 @@ struct Foo$0 { field: u32 } --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); check( @@ -1004,11 +984,7 @@ struct Foo$0 where u32: Copy { field: u32 } --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); } @@ -1037,11 +1013,7 @@ fn hover_record_struct_limit() { --- - size = 12 (0xC), align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 12 (0xC), align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1064,11 +1036,7 @@ fn hover_record_struct_limit() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1094,11 +1062,7 @@ fn hover_record_struct_limit() { --- - size = 16 (0x10), align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 16 (0x10), align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1119,11 +1083,7 @@ fn hover_record_struct_limit() { --- - size = 12 (0xC), align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 12 (0xC), align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1144,11 +1104,7 @@ fn hover_record_struct_limit() { --- - size = 12 (0xC), align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 12 (0xC), align = 4, no Drop "#]], ); @@ -1171,11 +1127,7 @@ fn hover_record_struct_limit() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 0, align = 1, no Drop "#]], ); } @@ -1200,11 +1152,7 @@ fn hover_record_variant_limit() { --- - size = 12 (0xC), align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 12 (0xC), align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1225,11 +1173,7 @@ fn hover_record_variant_limit() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1250,11 +1194,7 @@ fn hover_record_variant_limit() { --- - size = 16 (0x10), align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 16 (0x10), align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1275,11 +1215,7 @@ fn hover_record_variant_limit() { --- - size = 12 (0xC), align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 12 (0xC), align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1300,11 +1236,7 @@ fn hover_record_variant_limit() { --- - size = 12 (0xC), align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 12 (0xC), align = 4, no Drop "#]], ); } @@ -1330,11 +1262,7 @@ fn hover_enum_limit() { --- - size = 1, align = 1, niches = 254 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 1, align = 1, niches = 254, no Drop "#]], ); check_hover_enum_variants_limit( @@ -1356,11 +1284,7 @@ fn hover_enum_limit() { --- - size = 1, align = 1, niches = 254 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 1, align = 1, niches = 254, no Drop "#]], ); check_hover_enum_variants_limit( @@ -1379,11 +1303,7 @@ fn hover_enum_limit() { --- - size = 1, align = 1, niches = 254 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 1, align = 1, niches = 254, no Drop "#]], ); check_hover_enum_variants_limit( @@ -1402,11 +1322,7 @@ fn hover_enum_limit() { --- - size = 1, align = 1, niches = 254 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 1, align = 1, niches = 254, no Drop "#]], ); check_hover_enum_variants_limit( @@ -1443,11 +1359,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = a lot - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 12 (0xC), align = 4, niches = a lot, no Drop "#]], ); } @@ -1473,11 +1385,7 @@ fn hover_union_limit() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1499,11 +1407,7 @@ fn hover_union_limit() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1522,11 +1426,7 @@ fn hover_union_limit() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); check_hover_fields_limit( @@ -1545,11 +1445,7 @@ fn hover_union_limit() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, no Drop "#]], ); } @@ -1575,11 +1471,7 @@ struct Foo$0 where u32: Copy; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 0, align = 1, no Drop "#]], ); } @@ -1605,7 +1497,7 @@ type Fo$0o: Trait = S where T: Trait; --- - does not contain types with destructors (drop glue) + no Drop "#]], ); } @@ -1754,11 +1646,7 @@ fn main() { --- - size = 8, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 8, align = 4, no Drop "#]], ); check_hover_range( @@ -1813,11 +1701,7 @@ fn main() { let b$0ar = Some(12); } --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ); } @@ -1845,7 +1729,7 @@ enum Option<T> { --- - does not contain types with destructors (drop glue) + no Drop --- @@ -1908,11 +1792,7 @@ fn hover_for_local_variable_pat() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ) } @@ -1944,11 +1824,7 @@ fn hover_for_param_edge() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ) } @@ -1974,7 +1850,7 @@ fn hover_for_param_with_multiple_traits() { --- - may contain types with destructors (drop glue) depending on type parameters + type param may need Drop "#]], ) } @@ -2000,11 +1876,7 @@ fn main() { let foo_$0test = Thing::new(); } --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ) } @@ -2089,6 +1961,10 @@ impl Thing { x: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); check_hover_fields_limit( @@ -2109,6 +1985,10 @@ impl Thing { ```rust struct Thing ``` + + --- + + size = 4, align = 4 "#]], ); check( @@ -2130,6 +2010,10 @@ impl Thing { x: u32, } ``` + + --- + + size = 4, align = 4 "#]], ); check( @@ -2151,6 +2035,10 @@ impl Thing { A, } ``` + + --- + + size = 0, align = 1 "#]], ); check( @@ -2172,6 +2060,10 @@ impl Thing { A, } ``` + + --- + + size = 0, align = 1 "#]], ); check( @@ -2190,6 +2082,10 @@ impl usize { ```rust usize ``` + + --- + + size = 8, align = 8 "#]], ); check( @@ -2208,6 +2104,32 @@ impl fn() -> usize { ```rust fn() -> usize ``` + + --- + + size = 8, align = 8, niches = 1 + "#]], + ); + check( + r#" +pub struct Foo +where + Self$0:; +"#, + expect![[r#" + *Self* + + ```rust + ra_test_fixture + ``` + + ```rust + pub struct Foo + ``` + + --- + + size = 0, align = 1, no Drop "#]], ); } @@ -2753,11 +2675,7 @@ fn test_hover_function_pointer_show_identifiers() { --- - size = 8, align = 8, niches = 1 - - --- - - does not contain types with destructors (drop glue) + size = 8, align = 8, niches = 1, no Drop "#]], ); } @@ -2779,11 +2697,7 @@ fn test_hover_function_pointer_no_identifier() { --- - size = 8, align = 8, niches = 1 - - --- - - does not contain types with destructors (drop glue) + size = 8, align = 8, niches = 1, no Drop "#]], ); } @@ -3026,11 +2940,7 @@ pub struct B$0ar --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 0, align = 1, no Drop --- @@ -3061,11 +2971,7 @@ pub struct B$0ar --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 0, align = 1, no Drop --- @@ -3158,11 +3064,7 @@ fn test_hover_layout_of_variant() { --- - size = 4, align = 2 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 2, no Drop "#]], ); } @@ -3187,7 +3089,7 @@ fn test_hover_layout_of_variant_generic() { --- - does not contain types with destructors (drop glue) + no Drop "#]], ); } @@ -3212,11 +3114,7 @@ struct S$0<T>(core::marker::PhantomData<T>); --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 0, align = 1, no Drop "#]], ); } @@ -3244,11 +3142,7 @@ fn test_hover_layout_of_enum() { --- - size = 16 (0x10), align = 8, niches = 254 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 16 (0x10), align = 8, niches = 254, no Drop "#]], ); } @@ -3270,7 +3164,7 @@ fn test_hover_no_memory_layout() { --- - does not contain types with destructors (drop glue) + no Drop "#]], ); @@ -4578,11 +4472,7 @@ fn main() { --- - size = 8, align = 8, niches = 1 - - --- - - does not contain types with destructors (drop glue) + size = 8, align = 8, niches = 1, no Drop --- @@ -4596,11 +4486,7 @@ fn main() { --- - size = 4, align = 4, offset = 0 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, offset = 0, no Drop "#]], ); } @@ -4620,16 +4506,12 @@ struct S$0T<const C: usize = 1, T = Foo>(T); ``` ```rust - struct ST<const C: usize = 1, T = Foo>(T) + struct ST<const C: usize = {const}, T = Foo>(T) ``` --- - size = 0, align = 1 - - --- - - may contain types with destructors (drop glue) depending on type parameters; doesn't have a destructor + size = 0, align = 1, type param may need Drop "#]], ); } @@ -4654,11 +4536,7 @@ struct S$0T<const C: usize = {40 + 2}, T = Foo>(T); --- - size = 0, align = 1 - - --- - - may contain types with destructors (drop glue) depending on type parameters; doesn't have a destructor + size = 0, align = 1, type param may need Drop "#]], ); } @@ -4679,16 +4557,12 @@ struct S$0T<const C: usize = VAL, T = Foo>(T); ``` ```rust - struct ST<const C: usize = VAL, T = Foo>(T) + struct ST<const C: usize = {const}, T = Foo>(T) ``` --- - size = 0, align = 1 - - --- - - may contain types with destructors (drop glue) depending on type parameters; doesn't have a destructor + size = 0, align = 1, type param may need Drop "#]], ); } @@ -4712,11 +4586,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -4740,11 +4610,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -4763,16 +4629,12 @@ fn main() { *value* ```rust - let value: Const<-1> + let value: Const<_> ``` --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -4796,11 +4658,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -4824,11 +4682,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -4851,11 +4705,7 @@ impl Foo { --- - size = 8, align = 8, niches = 1 - - --- - - does not contain types with destructors (drop glue) + size = 8, align = 8, niches = 1, no Drop "#]], ); } @@ -4879,11 +4729,7 @@ impl Foo { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -5368,16 +5214,12 @@ type Fo$0o2 = Foo<2>; ``` ```rust - type Foo2 = Foo<2> + type Foo2 = Foo<<expr>> ``` --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -5427,11 +5269,7 @@ enum E { --- - size = 1, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 1, align = 1, no Drop --- @@ -5460,11 +5298,7 @@ enum E { --- - size = 1, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 1, align = 1, no Drop --- @@ -5494,11 +5328,7 @@ enum E { --- - size = 1, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 1, align = 1, no Drop --- @@ -5528,11 +5358,7 @@ enum E { --- - size = 1, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 1, align = 1, no Drop --- @@ -6197,7 +6023,7 @@ const FOO$0: &[i32; 5] = &[12; 5]; ``` ```rust - const FOO: &[i32; 5] = &[12, 12, 12, 12, 12] + const FOO: &[i32; {const}] = &[12, 12, 12, 12, 12] ``` "#]], ); @@ -6463,11 +6289,7 @@ fn main() { --- - size = 32 (0x20), align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 32 (0x20), align = 4, no Drop "#]], ); } @@ -7671,11 +7493,7 @@ enum Enum { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ); } @@ -7701,11 +7519,7 @@ enum Enum { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ); } @@ -8375,11 +8189,7 @@ fn test() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -9024,15 +8834,11 @@ fn main(notable$0: u32) {} --- - Implements notable traits: Notable\<Assoc = &str, Assoc2 = char> + Implements notable traits: `Notable<Assoc = &str, Assoc2 = char>` --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop "#]], ); } @@ -9124,11 +8930,7 @@ extern "C" { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -9157,7 +8959,7 @@ fn main() { S ``` ___ - Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>"#]], + Implements notable traits: `Future<Output = u32>`, `Iterator<Item = S>`, `Notable`"#]], ); } @@ -9274,11 +9076,7 @@ struct Pedro$0<'a> { --- - size = 16 (0x10), align = 8, niches = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 16 (0x10), align = 8, niches = 1, no Drop "#]], ) } @@ -9299,7 +9097,7 @@ fn main(a$0: impl T) {} --- - may contain types with destructors (drop glue) depending on type parameters + type param may need Drop "#]], ); } @@ -9320,11 +9118,7 @@ fn main(a$0: T) {} --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -9377,11 +9171,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -9715,11 +9505,7 @@ type A$0 = B; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop --- @@ -9752,11 +9538,7 @@ type A$0 = B; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop --- @@ -9790,11 +9572,7 @@ type A$0 = B; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop --- @@ -9826,11 +9604,7 @@ type A$0 = B; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); @@ -9954,11 +9728,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); @@ -9986,11 +9756,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); @@ -10025,11 +9791,7 @@ fn main() { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); } @@ -10348,11 +10110,7 @@ fn bar() { --- - size = 4, align = 4 - - --- - - does not contain types with destructors (drop glue) + size = 4, align = 4, no Drop --- @@ -10366,7 +10124,7 @@ fn bar() { --- - may contain types with destructors (drop glue) depending on type parameters + type param may need Drop --- @@ -10599,11 +10357,7 @@ struct NoDrop$0; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); doesn't have a destructor + size = 0, align = 1, no Drop "#]], ); check( @@ -10627,11 +10381,7 @@ impl Drop for NeedsDrop { --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue); has a destructor + size = 0, align = 1, impl Drop "#]], ); check( @@ -10656,11 +10406,7 @@ type NoDrop$0 = core::mem::ManuallyDrop<NeedsDrop>; --- - size = 0, align = 1 - - --- - - does not contain types with destructors (drop glue) + size = 0, align = 1, no Drop "#]], ); check( @@ -10691,11 +10437,7 @@ struct DropField$0 { --- - size = 4, align = 4 - - --- - - contain types with destructors (drop glue); doesn't have a destructor + size = 4, align = 4, needs Drop "#]], ); check( @@ -10716,7 +10458,7 @@ type Foo$0 = impl Sized; --- - contain types with destructors (drop glue) + needs Drop "#]], ); check( @@ -10744,11 +10486,7 @@ enum Enum { --- - size = 16 (0x10), align = 8, niches = 1 - - --- - - does not contain types with destructors (drop glue) + size = 16 (0x10), align = 8, niches = 1, no Drop "#]], ); check( @@ -10768,7 +10506,7 @@ struct Foo$0<T>(T); --- - may contain types with destructors (drop glue) depending on type parameters; doesn't have a destructor + type param may need Drop "#]], ); check( @@ -10791,7 +10529,7 @@ struct Foo$0<T: Copy>(T); --- - does not contain types with destructors (drop glue); doesn't have a destructor + no Drop "#]], ); check( @@ -10817,7 +10555,7 @@ struct Foo$0<T: Trait>(T::Assoc); --- - does not contain types with destructors (drop glue); doesn't have a destructor + no Drop "#]], ); check( @@ -10848,7 +10586,7 @@ pub struct ManuallyDrop$0<T: ?Sized> { --- - does not contain types with destructors (drop glue); doesn't have a destructor + no Drop "#]], ); } @@ -10891,3 +10629,73 @@ impl PublicFlags for NoteDialects { "#]], ); } + +#[test] +fn bounds_from_container_do_not_panic() { + check( + r#" +//- minicore: copy +struct Foo<T>(T); + +impl<T: Copy> Foo<T> { + fn foo<U: Copy>(&self, _u: U) {} +} + +fn bar(v: &Foo<i32>) { + v.$0foo(1u32); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl<T> Foo<T> + fn foo<U>(&self, _u: U) + where + U: Copy, + // Bounds from impl: + T: Copy, + ``` + + --- + + `T` = `i32`, `U` = `u32` + "#]], + ); +} + +#[test] +fn extra_lifetime_param_on_trait_method_subst() { + check( + r#" +struct AudioFormat; + +trait ValueEnum { + fn to_possible_value(&self); +} + +impl ValueEnum for AudioFormat { + fn to_possible_value<'a>(&'a self) {} +} + +fn main() { + ValueEnum::to_possible_value$0(&AudioFormat); +} + "#, + expect![[r#" + *to_possible_value* + + ```rust + ra_test_fixture::AudioFormat + ``` + + ```rust + fn to_possible_value<'a>(&'a self) + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6babdff52a2..82704af647d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -5,21 +5,21 @@ use std::{ use either::Either; use hir::{ - sym, ClosureStyle, DisplayTarget, HasVisibility, HirDisplay, HirDisplayError, HirWrite, - ModuleDef, ModuleDefId, Semantics, + ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, + HirWrite, ModuleDef, ModuleDefId, Semantics, sym, }; -use ide_db::{famous_defs::FamousDefs, FileRange, RootDatabase}; -use ide_db::{text_edit::TextEdit, FxHashSet}; +use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder}; +use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; -use smallvec::{smallvec, SmallVec}; -use span::EditionedFileId; +use smallvec::{SmallVec, smallvec}; use stdx::never; use syntax::{ + SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, ast::{self, AstNode, HasGenericParams}, - format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, + format_smolstr, match_ast, }; -use crate::{navigation_target::TryToNav, FileId}; +use crate::{FileId, navigation_target::TryToNav}; mod adjustment; mod bind_pat; @@ -85,7 +85,7 @@ pub(crate) fn inlay_hints( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); let file = sema.parse(file_id); let file = file.syntax(); @@ -136,7 +136,7 @@ pub(crate) fn inlay_hints_resolve( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); let file = sema.parse(file_id); let file = file.syntax(); @@ -207,7 +207,11 @@ fn hints( file_id: EditionedFileId, node: SyntaxNode, ) { - let display_target = sema.first_crate_or_default(file_id.file_id()).to_display_target(sema.db); + let file_id = file_id.editioned_file_id(sema.db); + let Some(krate) = sema.first_crate(file_id.file_id()) else { + return; + }; + let display_target = krate.to_display_target(sema.db); closing_brace::hints(hints, sema, config, file_id, display_target, node.clone()); if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { generic_param::hints(hints, famous_defs, config, any_has_generic_args); @@ -219,12 +223,12 @@ fn hints( chaining::hints(hints, famous_defs, config, display_target, &expr); adjustment::hints(hints, famous_defs, config, display_target, &expr); match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) + param_name::hints(hints, famous_defs, config, ast::Expr::from(it)) } ast::Expr::ClosureExpr(it) => { - closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); + closure_captures::hints(hints, famous_defs, config, it.clone()); closure_ret::hints(hints, famous_defs, config, display_target, it) }, ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it), @@ -793,7 +797,7 @@ fn hint_iterator( if ty.impls_trait(db, iter_trait, &[]) { let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { - hir::AssocItem::TypeAlias(alias) if alias.name(db) == sym::Item.clone() => Some(alias), + hir::AssocItem::TypeAlias(alias) if alias.name(db) == sym::Item => Some(alias), _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) { @@ -809,7 +813,8 @@ fn ty_to_text_edit( config: &InlayHintsConfig, node_for_hint: &SyntaxNode, ty: &hir::Type, - offset_to_insert: TextSize, + offset_to_insert_ty: TextSize, + additional_edits: &dyn Fn(&mut TextEditBuilder), prefix: impl Into<String>, ) -> Option<LazyProperty<TextEdit>> { // FIXME: Limit the length and bail out on excess somehow? @@ -818,8 +823,11 @@ fn ty_to_text_edit( .and_then(|scope| ty.display_source_code(scope.db, scope.module().into(), false).ok())?; Some(config.lazy_text_edit(|| { let mut builder = TextEdit::builder(); - builder.insert(offset_to_insert, prefix.into()); - builder.insert(offset_to_insert, rendered); + builder.insert(offset_to_insert_ty, prefix.into()); + builder.insert(offset_to_insert_ty, rendered); + + additional_edits(&mut builder); + builder.finish() })) } @@ -836,9 +844,9 @@ mod tests { use itertools::Itertools; use test_utils::extract_annotations; - use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; use crate::DiscriminantHints; - use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; + use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; + use crate::{LifetimeElisionHints, fixture, inlay_hints::InlayHintsConfig}; use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve}; @@ -996,4 +1004,51 @@ fn foo() { "#, ); } + + #[test] + fn closure_dependency_cycle_no_panic() { + check( + r#" +fn foo() { + let closure; + // ^^^^^^^ impl Fn() + closure = || { + closure(); + }; +} + +fn bar() { + let closure1; + // ^^^^^^^^ impl Fn() + let closure2; + // ^^^^^^^^ impl Fn() + closure1 = || { + closure2(); + }; + closure2 = || { + closure1(); + }; +} + "#, + ); + } + + #[test] + fn regression_19610() { + check( + r#" +trait Trait { + type Assoc; +} +struct Foo<A>(A); +impl<A: Trait<Assoc = impl Trait>> Foo<A> { + fn foo<'a, 'b>(_: &'a [i32], _: &'b [i32]) {} +} + +fn bar() { + Foo::foo(&[1], &[2]); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 91b81872952..f2844a2eaa6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -13,7 +13,7 @@ use hir::{ use ide_db::famous_defs::FamousDefs; use ide_db::text_edit::TextEditBuilder; -use syntax::ast::{self, prec::ExprPrecedence, AstNode}; +use syntax::ast::{self, AstNode, prec::ExprPrecedence}; use crate::{ AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart, @@ -224,7 +224,7 @@ fn mode_and_needs_parens_for_adjustment_hints( expr: &ast::Expr, mode: AdjustmentHintsMode, ) -> (bool, bool, bool) { - use {std::cmp::Ordering::*, AdjustmentHintsMode::*}; + use {AdjustmentHintsMode::*, std::cmp::Ordering::*}; match mode { Prefix | Postfix => { @@ -284,8 +284,8 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, AdjustmentHints, AdjustmentHintsMode, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 4379153acaa..52ea2e5ec58 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -4,7 +4,7 @@ //! let _x /* i32 */= f(4, 4); //! ``` use hir::{DisplayTarget, Semantics}; -use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use ide_db::{RootDatabase, famous_defs::FamousDefs}; use itertools::Itertools; use syntax::{ @@ -13,8 +13,8 @@ use syntax::{ }; use crate::{ - inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, }; pub(super) fn hints( @@ -87,6 +87,7 @@ pub(super) fn hints( .as_ref() .map_or_else(|| pat.syntax().text_range(), |t| t.text_range()) .end(), + &|_| (), if colon_token.is_some() { "" } else { ": " }, ) } else { @@ -181,10 +182,10 @@ mod tests { use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints}; + use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig}; use crate::inlay_hints::tests::{ - check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, + DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config, }; #[track_caller] @@ -861,28 +862,6 @@ fn main() { check_with_config( InlayHintsConfig { type_hints: true, - closure_style: ClosureStyle::ClosureWithId, - ..DISABLED_CONFIG - }, - r#" -//- minicore: fn -fn main() { - let x = || 2; - //^ {closure#0} - let y = |t: i32| x() + t; - //^ {closure#1} - let mut t = 5; - //^ i32 - let z = |k: i32| { t += k; }; - //^ {closure#2} - let p = (y, z); - //^ ({closure#1}, {closure#2}) -} - "#, - ); - check_with_config( - InlayHintsConfig { - type_hints: true, closure_style: ClosureStyle::Hide, ..DISABLED_CONFIG }, @@ -1140,12 +1119,11 @@ fn test() { #[test] fn no_edit_for_closure_return_without_body_block() { - // We can lift this limitation; see FIXME in closure_ret module. let config = InlayHintsConfig { closure_return_type_hints: ClosureReturnTypeHints::Always, ..TEST_CONFIG }; - check_no_edit( + check_edit( config, r#" struct S<T>(T); @@ -1154,6 +1132,13 @@ fn test() { let f = |a: S<usize>| S(a); } "#, + expect![[r#" + struct S<T>(T); + fn test() { + let f = || -> i32 { 3 }; + let f = |a: S<usize>| -> S<S<usize>> { S(a) }; + } + "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index 5bbb4fe4e66..d2917320688 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -128,8 +128,8 @@ mod tests { use expect_test::expect; use crate::{ - inlay_hints::tests::{check_edit, check_with_config, DISABLED_CONFIG}, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_edit, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index e9b728bcaa7..8ddbfaeffe8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -1,7 +1,7 @@ //! Implementation of trait bound hints. //! //! Currently this renders the implied `Sized` bound. -use ide_db::{famous_defs::FamousDefs, FileRange}; +use ide_db::{FileRange, famous_defs::FamousDefs}; use span::EditionedFileId; use syntax::ast::{self, AstNode, HasTypeBounds}; @@ -86,7 +86,7 @@ mod tests { use crate::inlay_hints::InlayHintsConfig; - use crate::inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG}; + use crate::inlay_hints::tests::{DISABLED_CONFIG, check_expect, check_with_config}; #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 604719bc366..ff157fa171b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -2,8 +2,8 @@ use hir::DisplayTarget; use ide_db::famous_defs::FamousDefs; use syntax::{ - ast::{self, AstNode}, Direction, NodeOrToken, SyntaxKind, T, + ast::{self, AstNode}, }; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; @@ -76,16 +76,15 @@ pub(super) fn hints( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use ide_db::text_edit::{TextRange, TextSize}; use crate::{ - fixture, + InlayHintsConfig, fixture, inlay_hints::{ - tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, LazyProperty, + tests::{DISABLED_CONFIG, TEST_CONFIG, check_expect, check_with_config}, }, - InlayHintsConfig, }; #[track_caller] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index bec6d38ee9c..de9ca8c000f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -7,13 +7,14 @@ use hir::{DisplayTarget, HirDisplay, Semantics}; use ide_db::{FileRange, RootDatabase}; use span::EditionedFileId; use syntax::{ + SyntaxKind, SyntaxNode, T, ast::{self, AstNode, HasLoopBody, HasName}, - match_ast, SyntaxKind, SyntaxNode, T, + match_ast, }; use crate::{ - inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, - InlayKind, + InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::LazyProperty, }; pub(super) fn hints( @@ -159,8 +160,8 @@ pub(super) fn hints( #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 9b981c0a3ac..3186a566d2b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -3,8 +3,7 @@ //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; use ide_db::text_edit::{TextRange, TextSize}; -use span::EditionedFileId; -use stdx::{never, TupleExt}; +use stdx::{TupleExt, never}; use syntax::ast::{self, AstNode}; use crate::{ @@ -15,7 +14,6 @@ pub(super) fn hints( acc: &mut Vec<InlayHint>, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _file_id: EditionedFileId, closure: ast::ClosureExpr, ) -> Option<()> { if !config.closure_capture_hints { @@ -75,10 +73,12 @@ pub(super) fn hints( // force cache the source file, otherwise sema lookup will potentially panic _ = sema.parse_or_expand(source.file()); source.name().and_then(|name| { - name.syntax() - .original_file_range_opt(sema.db) - .map(TupleExt::head) - .map(Into::into) + name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map( + |frange| ide_db::FileRange { + file_id: frange.file_id.file_id(sema.db), + range: frange.range, + }, + ) }) }), tooltip: None, @@ -96,8 +96,8 @@ pub(super) fn hints( #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 61c9c25fe73..9e600b5455b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -2,12 +2,12 @@ //! //! Tests live in [`bind_pat`][super::bind_pat] module. use hir::DisplayTarget; -use ide_db::famous_defs::FamousDefs; +use ide_db::{famous_defs::FamousDefs, text_edit::TextEditBuilder}; use syntax::ast::{self, AstNode}; use crate::{ - inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, }; pub(super) fn hints( @@ -35,8 +35,9 @@ pub(super) fn hints( let param_list = closure.param_list()?; - let closure = sema.descend_node_into_attributes(closure).pop()?; - let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted(); + let resolve_parent = Some(closure.syntax().text_range()); + let descended_closure = sema.descend_node_into_attributes(closure.clone()).pop()?; + let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(descended_closure.clone()))?.adjusted(); let callable = ty.as_callable(sema.db)?; let ty = callable.return_type(); if arrow.is_none() && ty.is_unit() { @@ -48,23 +49,30 @@ pub(super) fn hints( if arrow.is_none() { label.prepend_str(" -> "); } - // FIXME?: We could provide text edit to insert braces for closures with non-block body. - let text_edit = if has_block_body { - ty_to_text_edit( - sema, - config, - closure.syntax(), - &ty, - arrow - .as_ref() - .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range()) - .end(), - if arrow.is_none() { " -> " } else { "" }, - ) - } else { - None + + let offset_to_insert_ty = + arrow.as_ref().map_or_else(|| param_list.syntax().text_range(), |t| t.text_range()).end(); + + // Insert braces if necessary + let insert_braces = |builder: &mut TextEditBuilder| { + if !has_block_body { + if let Some(range) = closure.body().map(|b| b.syntax().text_range()) { + builder.insert(range.start(), "{ ".to_owned()); + builder.insert(range.end(), " }".to_owned()); + } + } }; + let text_edit = ty_to_text_edit( + sema, + config, + descended_closure.syntax(), + &ty, + offset_to_insert_ty, + &insert_braces, + if arrow.is_none() { " -> " } else { "" }, + ); + acc.push(InlayHint { range: param_list.syntax().text_range(), kind: InlayKind::Type, @@ -73,14 +81,14 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, - resolve_parent: Some(closure.syntax().text_range()), + resolve_parent, }); Some(()) } #[cfg(test)] mod tests { - use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG}; + use crate::inlay_hints::tests::{DISABLED_CONFIG, check_with_config}; use super::*; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index f1e1955d14c..827a0438dd0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -6,7 +6,7 @@ //! ``` use hir::Semantics; use ide_db::text_edit::TextEdit; -use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use ide_db::{RootDatabase, famous_defs::FamousDefs}; use span::EditionedFileId; use syntax::ast::{self, AstNode, HasName}; @@ -107,8 +107,8 @@ mod tests { use expect_test::expect; use crate::inlay_hints::{ - tests::{check_edit, check_with_config, DISABLED_CONFIG}, DiscriminantHints, InlayHintsConfig, + tests::{DISABLED_CONFIG, check_edit, check_with_config}, }; #[track_caller] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs index 652dff0bc56..20f54b2cd19 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs @@ -1,7 +1,7 @@ //! Extern block hints use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit}; use span::EditionedFileId; -use syntax::{ast, AstNode, SyntaxToken}; +use syntax::{AstNode, SyntaxToken, ast}; use crate::{InlayHint, InlayHintsConfig}; @@ -98,7 +98,7 @@ fn item_hint( #[cfg(test)] mod tests { - use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG}; + use crate::inlay_hints::tests::{DISABLED_CONFIG, check_with_config}; #[test] fn unadorned() { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 762a4c26551..6e1b3bdbdf0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -1,12 +1,14 @@ //! Implementation of inlay hints for generic parameters. +use either::Either; use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs}; use syntax::{ - ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, AstNode, + ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, }; use crate::{ - inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, + InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, + inlay_hints::{GenericParameterHints, param_name}, }; use super::param_name::is_argument_similar_to_param_name; @@ -62,8 +64,17 @@ pub(crate) fn hints( let param_name = param.name(sema.db); let should_hide = { - let argument = get_string_representation(&arg)?; - is_argument_similar_to_param_name(&argument, param_name.as_str()) + let param_name = param_name.as_str(); + get_segment_representation(&arg).map_or(false, |seg| match seg { + Either::Left(Either::Left(argument)) => { + is_argument_similar_to_param_name(&argument, param_name) + } + Either::Left(Either::Right(argument)) => argument + .segment() + .and_then(|it| it.name_ref()) + .is_some_and(|it| it.text().eq_ignore_ascii_case(param_name)), + Either::Right(lifetime) => lifetime.text().eq_ignore_ascii_case(param_name), + }) }; if should_hide { @@ -91,7 +102,10 @@ pub(crate) fn hints( } }; let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); - linked_location.map(Into::into) + linked_location.map(|frange| ide_db::FileRange { + file_id: frange.file_id.file_id(sema.db), + range: frange.range, + }) }), ); @@ -111,32 +125,34 @@ pub(crate) fn hints( Some(()) } -fn get_string_representation(arg: &ast::GenericArg) -> Option<String> { +fn get_segment_representation( + arg: &ast::GenericArg, +) -> Option<Either<Either<Vec<ast::NameRef>, ast::Path>, ast::Lifetime>> { return match arg { ast::GenericArg::AssocTypeArg(_) => None, - ast::GenericArg::ConstArg(const_arg) => Some(const_arg.to_string()), + ast::GenericArg::ConstArg(const_arg) => { + param_name::get_segment_representation(&const_arg.expr()?).map(Either::Left) + } ast::GenericArg::LifetimeArg(lifetime_arg) => { let lifetime = lifetime_arg.lifetime()?; - Some(lifetime.to_string()) + Some(Either::Right(lifetime)) } ast::GenericArg::TypeArg(type_arg) => { let ty = type_arg.ty()?; - Some( - type_path_segment(&ty) - .map_or_else(|| type_arg.to_string(), |segment| segment.to_string()), - ) + type_path(&ty).map(Either::Right).map(Either::Left) } }; - fn type_path_segment(ty: &ast::Type) -> Option<ast::PathSegment> { + fn type_path(ty: &ast::Type) -> Option<ast::Path> { match ty { - ast::Type::ArrayType(it) => type_path_segment(&it.ty()?), - ast::Type::ForType(it) => type_path_segment(&it.ty()?), - ast::Type::ParenType(it) => type_path_segment(&it.ty()?), - ast::Type::PathType(path_type) => path_type.path()?.segment(), - ast::Type::PtrType(it) => type_path_segment(&it.ty()?), - ast::Type::RefType(it) => type_path_segment(&it.ty()?), - ast::Type::SliceType(it) => type_path_segment(&it.ty()?), + ast::Type::ArrayType(it) => type_path(&it.ty()?), + ast::Type::ForType(it) => type_path(&it.ty()?), + ast::Type::ParenType(it) => type_path(&it.ty()?), + ast::Type::PathType(path_type) => path_type.path(), + ast::Type::PtrType(it) => type_path(&it.ty()?), + ast::Type::RefType(it) => type_path(&it.ty()?), + ast::Type::SliceType(it) => type_path(&it.ty()?), + ast::Type::MacroType(macro_type) => macro_type.macro_call()?.path(), _ => None, } } @@ -145,11 +161,11 @@ fn get_string_representation(arg: &ast::GenericArg) -> Option<String> { #[cfg(test)] mod tests { use crate::{ + InlayHintsConfig, inlay_hints::{ - tests::{check_with_config, DISABLED_CONFIG}, GenericParameterHints, + tests::{DISABLED_CONFIG, check_with_config}, }, - InlayHintsConfig, }; #[track_caller] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 390139d214e..f52e27946ff 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -6,16 +6,17 @@ //! } //! ``` use hir::{ + ChalkTyInterner, DefWithBody, db::{DefDatabase as _, HirDatabase as _}, mir::{MirSpan, TerminatorKind}, - ChalkTyInterner, DefWithBody, }; -use ide_db::{famous_defs::FamousDefs, FileRange}; +use ide_db::{FileRange, famous_defs::FamousDefs}; use span::EditionedFileId; use syntax::{ + ToSmolStr, ast::{self, AstNode}, - match_ast, ToSmolStr, + match_ast, }; use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; @@ -107,7 +108,7 @@ pub(super) fn hints( .and_then(|d| source_map.pat_syntax(*d).ok()) .and_then(|d| { Some(FileRange { - file_id: d.file_id.file_id()?.into(), + file_id: d.file_id.file_id()?.file_id(sema.db), range: d.value.text_range(), }) }) @@ -143,8 +144,8 @@ fn nearest_token_after_node( #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; const ONLY_DROP_CONFIG: InlayHintsConfig = diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index ae5b519b43d..f3be09f30a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -7,8 +7,8 @@ use ide_db::famous_defs::FamousDefs; use ide_db::text_edit::TextEdit; use span::EditionedFileId; use syntax::{ - ast::{self, AstNode}, SyntaxKind, + ast::{self, AstNode}, }; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; @@ -56,8 +56,8 @@ pub(super) fn hints( #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, TEST_CONFIG}, InlayHintsConfig, LifetimeElisionHints, + inlay_hints::tests::{TEST_CONFIG, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index 1fdd6989917..baba49a427d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -4,18 +4,18 @@ //! ``` use std::iter; -use ide_db::{famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap}; +use ide_db::{FxHashMap, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty}; use itertools::Itertools; use span::EditionedFileId; +use syntax::{SmolStr, format_smolstr}; use syntax::{ - ast::{self, AstNode, HasGenericParams, HasName}, SyntaxKind, SyntaxToken, + ast::{self, AstNode, HasGenericParams, HasName}, }; -use syntax::{format_smolstr, SmolStr}; use crate::{ - inlay_hints::InlayHintCtx, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, - LifetimeElisionHints, + InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints, + inlay_hints::InlayHintCtx, }; pub(super) fn fn_hints( @@ -268,13 +268,14 @@ fn hints_( ctx.lifetime_stacks.iter().flat_map(|it| it.iter()).cloned().zip(iter::repeat(0)).collect(); // allocate names let mut gen_idx_name = { - let mut gen = (0u8..).map(|idx| match idx { + let mut generic = (0u8..).map(|idx| match idx { idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]), idx => format_smolstr!("'{idx}"), }); let ctx = &*ctx; move || { - gen.by_ref() + generic + .by_ref() .find(|s| ctx.lifetime_stacks.iter().flat_map(|it| it.iter()).all(|n| n != s)) .unwrap_or_default() } @@ -406,8 +407,8 @@ fn hints_( #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check, check_with_config, TEST_CONFIG}, InlayHintsConfig, LifetimeElisionHints, + inlay_hints::tests::{TEST_CONFIG, check, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 8f01b1bd38b..5ff9fee60ab 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -4,16 +4,14 @@ //! _ = max(/*x*/4, /*y*/4); //! ``` +use std::iter::zip; + use either::Either; -use hir::{Callable, Semantics}; -use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use hir::Semantics; +use ide_db::{RootDatabase, famous_defs::FamousDefs}; -use span::EditionedFileId; use stdx::to_lower_snake_case; -use syntax::{ - ast::{self, AstNode, HasArgList, HasName, UnaryOp}, - ToSmolStr, -}; +use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp}; use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; @@ -21,7 +19,6 @@ pub(super) fn hints( acc: &mut Vec<InlayHint>, FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -29,6 +26,12 @@ pub(super) fn hints( } let (callable, arg_list) = get_callable(sema, &expr)?; + let unary_function = callable.n_params() == 1; + let function_name = match callable.kind() { + hir::CallableKind::Function(function) => Some(function.name(sema.db)), + _ => None, + }; + let function_name = function_name.as_ref().map(|it| it.as_str()); let hints = callable .params() .into_iter() @@ -40,7 +43,13 @@ pub(super) fn hints( Some((p, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg) + !should_hide_param_name_hint( + sema, + unary_function, + function_name, + param_name.as_str(), + arg, + ) }) .map(|(param, param_name, _, hir::FileRange { range, .. })| { let colon = if config.render_colons { ":" } else { "" }; @@ -56,7 +65,10 @@ pub(super) fn hints( _ => None, }, }?; - sema.original_range_opt(name_syntax.syntax()).map(Into::into) + sema.original_range_opt(name_syntax.syntax()).map(|frange| ide_db::FileRange { + file_id: frange.file_id.file_id(sema.db), + range: frange.range, + }) }), ); InlayHint { @@ -94,9 +106,13 @@ fn get_callable( } } +const INSIGNIFICANT_METHOD_NAMES: &[&str] = &["clone", "as_ref", "into"]; +const INSIGNIFICANT_PARAMETER_NAMES: &[&str] = &["predicate", "value", "pat", "rhs", "other"]; + fn should_hide_param_name_hint( sema: &Semantics<'_, RootDatabase>, - callable: &hir::Callable, + unary_function: bool, + function_name: Option<&str>, param_name: &str, argument: &ast::Expr, ) -> bool { @@ -114,95 +130,128 @@ fn should_hide_param_name_hint( return true; } - if matches!(argument, ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(UnaryOp::Not)) { - return false; + if param_name.starts_with("ra_fixture") { + return true; } - let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()), - _ => None, - }; - let fn_name = fn_name.as_deref(); - is_param_name_suffix_of_fn_name(param_name, callable, fn_name) - || is_argument_expr_similar_to_param_name(argument, param_name) - || param_name.starts_with("ra_fixture") - || (callable.n_params() == 1 && is_obvious_param(param_name)) - || is_adt_constructor_similar_to_param_name(sema, argument, param_name) + if unary_function { + if let Some(function_name) = function_name { + if is_param_name_suffix_of_fn_name(param_name, function_name) { + return true; + } + } + if is_obvious_param(param_name) { + return true; + } + } + + is_argument_expr_similar_to_param_name(sema, argument, param_name) } /// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal. /// /// `fn strip_suffix(suffix)` will be hidden. /// `fn stripsuffix(suffix)` will not be hidden. -fn is_param_name_suffix_of_fn_name( +fn is_param_name_suffix_of_fn_name(param_name: &str, fn_name: &str) -> bool { + fn_name == param_name + || fn_name + .len() + .checked_sub(param_name.len()) + .and_then(|at| fn_name.is_char_boundary(at).then(|| fn_name.split_at(at))) + .is_some_and(|(prefix, suffix)| { + suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_') + }) +} + +fn is_argument_expr_similar_to_param_name( + sema: &Semantics<'_, RootDatabase>, + argument: &ast::Expr, param_name: &str, - callable: &Callable, - fn_name: Option<&str>, ) -> bool { - match (callable.n_params(), fn_name) { - (1, Some(function)) => { - function == param_name - || function - .len() - .checked_sub(param_name.len()) - .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at))) - .is_some_and(|(prefix, suffix)| { - suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_') - }) + match get_segment_representation(argument) { + Some(Either::Left(argument)) => is_argument_similar_to_param_name(&argument, param_name), + Some(Either::Right(path)) => { + path.segment() + .and_then(|it| it.name_ref()) + .is_some_and(|name_ref| name_ref.text().eq_ignore_ascii_case(param_name)) + || is_adt_constructor_similar_to_param_name(sema, &path, param_name) } - _ => false, + None => false, } } -fn is_argument_expr_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool { - let argument = match get_string_representation(argument) { - Some(argument) => argument, - None => return false, - }; - is_argument_similar_to_param_name(&argument, param_name) -} - /// Check whether param_name and argument are the same or /// whether param_name is a prefix/suffix of argument(split at `_`). -pub(super) fn is_argument_similar_to_param_name(argument: &str, param_name: &str) -> bool { - // std is honestly too panic happy... - let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at)); - - let param_name = param_name.trim_start_matches('_'); - let argument = argument.trim_start_matches('_'); - - match str_split_at(argument, param_name.len()) { - Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => { - return rest.is_empty() || rest.starts_with('_'); - } - _ => (), - } - match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) { - Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => { - return rest.is_empty() || rest.ends_with('_'); - } - _ => (), - } - false +pub(super) fn is_argument_similar_to_param_name( + argument: &[ast::NameRef], + param_name: &str, +) -> bool { + debug_assert!(!argument.is_empty()); + debug_assert!(!param_name.is_empty()); + let param_name = param_name.split('_'); + let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_')); + + let prefix_match = zip(argument.clone(), param_name.clone()) + .all(|(arg, param)| arg.eq_ignore_ascii_case(param)); + let postfix_match = || { + zip(argument.rev(), param_name.rev()).all(|(arg, param)| arg.eq_ignore_ascii_case(param)) + }; + prefix_match || postfix_match() } -fn get_string_representation(expr: &ast::Expr) -> Option<String> { +pub(super) fn get_segment_representation( + expr: &ast::Expr, +) -> Option<Either<Vec<ast::NameRef>, ast::Path>> { match expr { ast::Expr::MethodCallExpr(method_call_expr) => { + let receiver = + method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr)); let name_ref = method_call_expr.name_ref()?; - match name_ref.text().as_str() { - "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()), - name_ref => Some(name_ref.to_owned()), + if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text().as_str()) { + return receiver; } + Some(Either::Left(match receiver { + Some(Either::Left(mut left)) => { + left.push(name_ref); + left + } + Some(Either::Right(_)) | None => vec![name_ref], + })) + } + ast::Expr::FieldExpr(field_expr) => { + let expr = field_expr.expr().and_then(|expr| get_segment_representation(&expr)); + let name_ref = field_expr.name_ref()?; + let res = match expr { + Some(Either::Left(mut left)) => { + left.push(name_ref); + left + } + Some(Either::Right(_)) | None => vec![name_ref], + }; + Some(Either::Left(res)) } - ast::Expr::MacroExpr(macro_expr) => { - Some(macro_expr.macro_call()?.path()?.segment()?.to_string()) + // paths + ast::Expr::MacroExpr(macro_expr) => macro_expr.macro_call()?.path().map(Either::Right), + ast::Expr::RecordExpr(record_expr) => record_expr.path().map(Either::Right), + ast::Expr::PathExpr(path_expr) => { + let path = path_expr.path()?; + // single segment paths are likely locals + Some(match path.as_single_name_ref() { + None => Either::Right(path), + Some(name_ref) => Either::Left(vec![name_ref]), + }) } - ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()), - ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()), - ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?), - ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), - ast::Expr::CastExpr(cast_expr) => get_string_representation(&cast_expr.expr()?), + ast::Expr::PrefixExpr(prefix_expr) if prefix_expr.op_kind() == Some(UnaryOp::Not) => None, + // recurse + ast::Expr::PrefixExpr(prefix_expr) => get_segment_representation(&prefix_expr.expr()?), + ast::Expr::RefExpr(ref_expr) => get_segment_representation(&ref_expr.expr()?), + ast::Expr::CastExpr(cast_expr) => get_segment_representation(&cast_expr.expr()?), + ast::Expr::CallExpr(call_expr) => get_segment_representation(&call_expr.expr()?), + ast::Expr::AwaitExpr(await_expr) => get_segment_representation(&await_expr.expr()?), + ast::Expr::IndexExpr(index_expr) => get_segment_representation(&index_expr.base()?), + ast::Expr::ParenExpr(paren_expr) => get_segment_representation(&paren_expr.expr()?), + ast::Expr::TryExpr(try_expr) => get_segment_representation(&try_expr.expr()?), + // ast::Expr::ClosureExpr(closure_expr) => todo!(), _ => None, } } @@ -210,30 +259,15 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> { fn is_obvious_param(param_name: &str) -> bool { // avoid displaying hints for common functions like map, filter, etc. // or other obvious words used in std - let is_obvious_param_name = - matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other"); - param_name.len() == 1 || is_obvious_param_name + param_name.len() == 1 || INSIGNIFICANT_PARAMETER_NAMES.contains(¶m_name) } fn is_adt_constructor_similar_to_param_name( sema: &Semantics<'_, RootDatabase>, - argument: &ast::Expr, + path: &ast::Path, param_name: &str, ) -> bool { - let path = match argument { - ast::Expr::CallExpr(c) => c.expr().and_then(|e| match e { - ast::Expr::PathExpr(p) => p.path(), - _ => None, - }), - ast::Expr::PathExpr(p) => p.path(), - ast::Expr::RecordExpr(r) => r.path(), - _ => return false, - }; - let path = match path { - Some(it) => it, - None => return false, - }; - (|| match sema.resolve_path(&path)? { + (|| match sema.resolve_path(path)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name) } @@ -257,8 +291,8 @@ fn is_adt_constructor_similar_to_param_name( #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; #[track_caller] @@ -501,6 +535,7 @@ fn enum_matches_param_name(completion_kind: CompletionKind) {} fn foo(param: u32) {} fn bar(param_eter: u32) {} +fn baz(a_d_e: u32) {} enum CompletionKind { Keyword, @@ -553,6 +588,14 @@ fn main() { //^^^^^^^^^^^ param_eter non_ident_pat((0, 0)); + + baz(a.d.e); + baz(a.dc.e); + // ^^^^^^ a_d_e + baz(ac.d.e); + // ^^^^^^ a_d_e + baz(a.d.ec); + // ^^^^^^ a_d_e }"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index de9b0e98a4b..d67d8458840 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -5,7 +5,7 @@ //! ``` use ide_db::famous_defs::FamousDefs; use span::EditionedFileId; -use syntax::{ast, SyntaxToken, T}; +use syntax::{SyntaxToken, T, ast}; use crate::{InlayHint, InlayHintsConfig}; @@ -41,8 +41,8 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { #[cfg(test)] mod tests { use crate::{ - inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, InlayHintsConfig, + inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs index 74dad488b4d..8f9d2d6bf11 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -1,8 +1,8 @@ use hir::{ConstEvalError, DefWithBody, DisplayTarget, Semantics}; -use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase}; +use ide_db::{FilePosition, LineIndexDatabase, RootDatabase, base_db::SourceDatabase}; use std::time::{Duration, Instant}; use stdx::format_to; -use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; +use syntax::{AstNode, TextRange, algo::ancestors_at_offset, ast}; // Feature: Interpret A Function, Static Or Const. // @@ -35,10 +35,10 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Dura _ => return None, }; let span_formatter = |file_id, text_range: TextRange| { - let path = &db - .source_root(db.file_source_root(file_id)) - .path_for_file(&file_id) - .map(|x| x.to_string()); + let source_root = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root).source_root(db); + + let path = source_root.path_for_file(&file_id).map(|x| x.to_string()); let path = path.as_deref().unwrap_or("<unknown file>"); match db.line_index(file_id).try_line_col(text_range.start()) { Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col), @@ -64,10 +64,9 @@ pub(crate) fn render_const_eval_error( display_target: DisplayTarget, ) -> String { let span_formatter = |file_id, text_range: TextRange| { - let path = &db - .source_root(db.file_source_root(file_id)) - .path_for_file(&file_id) - .map(|x| x.to_string()); + let source_root = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root).source_root(db); + let path = source_root.path_for_file(&file_id).map(|x| x.to_string()); let path = path.as_deref().unwrap_or("<unknown file>"); match db.line_index(file_id).try_line_col(text_range.start()) { Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col), diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs index ea18a97070c..0188c105faa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -2,10 +2,10 @@ use ide_assists::utils::extract_trivial_expression; use ide_db::syntax_helpers::node_ext::expr_as_name_ref; use itertools::Itertools; use syntax::{ - ast::{self, AstNode, AstToken, IsString}, NodeOrToken, SourceFile, SyntaxElement, SyntaxKind::{self, USE_TREE, WHITESPACE}, - SyntaxToken, TextRange, TextSize, T, + SyntaxToken, T, TextRange, TextSize, + ast::{self, AstNode, AstToken, IsString}, }; use ide_db::text_edit::{TextEdit, TextEditBuilder}; diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 8ac1a96cc65..a13be6c4927 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -20,6 +20,7 @@ mod navigation_target; mod annotations; mod call_hierarchy; +mod child_modules; mod doc_links; mod expand_macro; mod extend_selection; @@ -57,23 +58,22 @@ mod view_memory_layout; mod view_mir; mod view_syntax_tree; -use std::{iter, panic::UnwindSafe}; +use std::panic::{AssertUnwindSafe, UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::{sym, ChangeWithProcMacros}; +use hir::{ChangeWithProcMacros, EditionedFileId, sym}; use ide_db::{ + FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ - ra_salsa::{self, ParallelDatabase}, - CrateOrigin, CrateWorkspaceData, Env, FileLoader, FileSet, SourceDatabase, - SourceRootDatabase, VfsPath, + CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, + salsa::Cancelled, }, - prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, + prime_caches, symbol_index, }; -use span::EditionedFileId; use syntax::SourceFile; use triomphe::Arc; -use view_memory_layout::{view_memory_layout, RecursiveMemoryLayout}; +use view_memory_layout::{RecursiveMemoryLayout, view_memory_layout}; use crate::navigation_target::ToNav; @@ -110,8 +110,8 @@ pub use crate::{ StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig, }, syntax_highlighting::{ - tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, HighlightConfig, HlRange, + tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, }, test_explorer::{TestItem, TestItemKind}, }; @@ -125,7 +125,8 @@ pub use ide_completion::{ }; pub use ide_db::text_edit::{Indel, TextEdit}; pub use ide_db::{ - base_db::{Cancelled, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId}, + FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind, + base_db::{Crate, CrateGraphBuilder, FileChange, SourceRoot, SourceRootId}, documentation::Documentation, label::Label, line_index::{LineCol, LineIndex}, @@ -133,7 +134,6 @@ pub use ide_db::{ search::{ReferenceCategory, SearchScope}, source_change::{FileSystemEdit, SnippetEdit, SourceChange}, symbol_index::Query, - FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind, }; pub use ide_diagnostics::{Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode}; pub use ide_ssr::SsrError; @@ -217,7 +217,7 @@ impl Default for AnalysisHost { /// `Analysis` are canceled (most method return `Err(Canceled)`). #[derive(Debug)] pub struct Analysis { - db: ra_salsa::Snapshot<RootDatabase>, + db: RootDatabase, } // As a general design guideline, `Analysis` API are intended to be independent @@ -237,34 +237,37 @@ impl Analysis { file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_owned())); let source_root = SourceRoot::new_local(file_set); - let mut change = ChangeWithProcMacros::new(); + let mut change = ChangeWithProcMacros::default(); change.set_roots(vec![source_root]); - let mut crate_graph = CrateGraph::default(); + let mut crate_graph = CrateGraphBuilder::default(); // FIXME: cfg options // Default to enable test for single file. let mut cfg_options = CfgOptions::default(); - cfg_options.insert_atom(sym::test.clone()); + + // FIXME: This is less than ideal + let proc_macro_cwd = Arc::new( + TryFrom::try_from(&*std::env::current_dir().unwrap().as_path().to_string_lossy()) + .unwrap(), + ); + cfg_options.insert_atom(sym::test); crate_graph.add_crate_root( file_id, Edition::CURRENT, None, None, - Arc::new(cfg_options), + cfg_options, None, Env::default(), CrateOrigin::Local { repo: None, name: None }, false, - None, - ); - change.change_file(file_id, Some(text)); - let ws_data = crate_graph - .iter() - .zip(iter::repeat(Arc::new(CrateWorkspaceData { + proc_macro_cwd, + Arc::new(CrateWorkspaceData { data_layout: Err("fixture has no layout".into()), toolchain: None, - }))) - .collect(); - change.set_crate_graph(crate_graph, ws_data); + }), + ); + change.change_file(file_id, Some(text)); + change.set_crate_graph(crate_graph); host.apply_change(change); (host.analysis(), file_id) @@ -276,12 +279,12 @@ impl Analysis { } pub fn source_root_id(&self, file_id: FileId) -> Cancellable<SourceRootId> { - self.with_db(|db| db.file_source_root(file_id)) + self.with_db(|db| db.file_source_root(file_id).source_root_id(db)) } pub fn is_local_source_root(&self, source_root_id: SourceRootId) -> Cancellable<bool> { self.with_db(|db| { - let sr = db.source_root(source_root_id); + let sr = db.source_root(source_root_id).source_root(db); !sr.is_library }) } @@ -295,18 +298,25 @@ impl Analysis { /// Gets the text of the source file. pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> { - self.with_db(|db| SourceDatabase::file_text(db, file_id)) + self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db)) } /// Gets the syntax tree of the file. pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> { // FIXME edition - self.with_db(|db| db.parse(EditionedFileId::current_edition(file_id)).tree()) + self.with_db(|db| { + let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); + + db.parse(editioned_file_id_wrapper).tree() + }) } /// Returns true if this file belongs to an immutable library. pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> { - self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) + self.with_db(|db| { + let source_root = db.file_source_root(file_id).source_root_id(db); + db.source_root(source_root).source_root(db).is_library + }) } /// Gets the file's `LineIndex`: data structure to convert between absolute @@ -324,7 +334,8 @@ impl Analysis { /// supported). pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> { self.with_db(|db| { - let parse = db.parse(EditionedFileId::current_edition(position.file_id)); + let file_id = EditionedFileId::current_edition(&self.db, position.file_id); + let parse = db.parse(file_id); let file = parse.tree(); matching_brace::matching_brace(&file, position.offset) }) @@ -358,7 +369,7 @@ impl Analysis { self.with_db(|db| test_explorer::discover_tests_in_crate_by_test_id(db, crate_id)) } - pub fn discover_tests_in_crate(&self, crate_id: CrateId) -> Cancellable<Vec<TestItem>> { + pub fn discover_tests_in_crate(&self, crate_id: Crate) -> Cancellable<Vec<TestItem>> { self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id)) } @@ -383,7 +394,9 @@ impl Analysis { /// stuff like trailing commas. pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable<TextEdit> { self.with_db(|db| { - let parse = db.parse(EditionedFileId::current_edition(frange.file_id)); + let editioned_file_id_wrapper = + EditionedFileId::current_edition(&self.db, frange.file_id); + let parse = db.parse(editioned_file_id_wrapper); join_lines::join_lines(config, &parse.tree(), frange.range) }) } @@ -419,9 +432,9 @@ impl Analysis { pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> { // FIXME: Edition self.with_db(|db| { - file_structure::file_structure( - &db.parse(EditionedFileId::current_edition(file_id)).tree(), - ) + let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); + + file_structure::file_structure(&db.parse(editioned_file_id_wrapper).tree()) }) } @@ -450,9 +463,9 @@ impl Analysis { /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> { self.with_db(|db| { - folding_ranges::folding_ranges( - &db.parse(EditionedFileId::current_edition(file_id)).tree(), - ) + let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); + + folding_ranges::folding_ranges(&db.parse(editioned_file_id_wrapper).tree()) }) } @@ -506,7 +519,11 @@ impl Analysis { position: FilePosition, search_scope: Option<SearchScope>, ) -> Cancellable<Option<Vec<ReferenceSearchResult>>> { - self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) + let search_scope = AssertUnwindSafe(search_scope); + self.with_db(|db| { + let _ = &search_scope; + references::find_all_refs(&Semantics::new(db), position, search_scope.0) + }) } /// Returns a short text describing element at position. @@ -577,34 +594,44 @@ impl Analysis { self.with_db(|db| parent_module::parent_module(db, position)) } + /// Returns vec of `mod name;` declaration which are created by the current module. + pub fn child_modules(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> { + self.with_db(|db| child_modules::child_modules(db, position)) + } + /// Returns crates that this file belongs to. - pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> { + pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> { self.with_db(|db| parent_module::crates_for(db, file_id)) } /// Returns crates that this file belongs to. - pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> { - self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect()) + pub fn transitive_rev_deps(&self, crate_id: Crate) -> Cancellable<Vec<Crate>> { + self.with_db(|db| Vec::from_iter(db.transitive_rev_deps(crate_id))) } /// Returns crates that this file *might* belong to. - pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> { + pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> { self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect()) } /// Returns the edition of the given crate. - pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> { - self.with_db(|db| db.crate_graph()[crate_id].edition) + pub fn crate_edition(&self, crate_id: Crate) -> Cancellable<Edition> { + self.with_db(|db| crate_id.data(db).edition) + } + + /// Returns whether the given crate is a proc macro. + pub fn is_proc_macro_crate(&self, crate_id: Crate) -> Cancellable<bool> { + self.with_db(|db| crate_id.data(db).is_proc_macro) } /// Returns true if this crate has `no_std` or `no_core` specified. - pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable<bool> { + pub fn is_crate_no_std(&self, crate_id: Crate) -> Cancellable<bool> { self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std()) } /// Returns the root file of the given crate. - pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> { - self.with_db(|db| db.crate_graph()[crate_id].root_file_id) + pub fn crate_root(&self, crate_id: Crate) -> Cancellable<FileId> { + self.with_db(|db| crate_id.data(db).root_file_id) } /// Returns the set of possible targets to run for the current file. @@ -618,7 +645,11 @@ impl Analysis { position: FilePosition, search_scope: Option<SearchScope>, ) -> Cancellable<Vec<Runnable>> { - self.with_db(|db| runnables::related_tests(db, position, search_scope)) + let search_scope = AssertUnwindSafe(search_scope); + self.with_db(|db| { + let _ = &search_scope; + runnables::related_tests(db, position, search_scope.0) + }) } /// Computes syntax highlighting for the given file @@ -717,7 +748,7 @@ impl Analysis { frange: FileRange, ) -> Cancellable<Vec<Assist>> { let include_fixes = match &assist_config.allowed { - Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix), + Some(it) => it.contains(&AssistKind::QuickFix), None => true, }; @@ -811,6 +842,10 @@ impl Analysis { self.with_db(|db| view_memory_layout(db, position)) } + pub fn editioned_file_id_to_vfs(&self, file_id: hir::EditionedFileId) -> FileId { + file_id.file_id(&self.db) + } + /// Performs an operation on the database that may be canceled. /// /// rust-analyzer needs to be able to answer semantic questions about the @@ -828,7 +863,8 @@ impl Analysis { where F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, { - Cancelled::catch(|| f(&self.db)) + let snap = self.db.snapshot(); + Cancelled::catch(|| f(&snap)) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs index 67346ea9cf9..b2b91d6e3cf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs @@ -1,6 +1,6 @@ use syntax::{ + SourceFile, SyntaxKind, T, TextSize, ast::{self, AstNode}, - SourceFile, SyntaxKind, TextSize, T, }; // Feature: Matching Brace diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 5754b4fa82f..4a06cd919fc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -5,15 +5,15 @@ use core::fmt; use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics}; use ide_db::{ + FilePosition, RootDatabase, base_db::{CrateOrigin, LangCrateOrigin}, defs::{Definition, IdentClass}, helpers::pick_best_token, - FilePosition, RootDatabase, }; use itertools::Itertools; use syntax::{AstNode, SyntaxKind::*, T}; -use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo}; +use crate::{RangeInfo, doc_links::token_as_doc_comment, parent_module::crates_for}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum MonikerDescriptorKind { @@ -194,11 +194,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Definition::Function(it) => { if it.as_assoc_item(db).is_some() { if it.has_self_param(db) { - if it.has_body(db) { - Method - } else { - TraitMethod - } + if it.has_body(db) { Method } else { TraitMethod } } else { StaticMethod } @@ -405,7 +401,7 @@ fn display<T: HirDisplay>(db: &RootDatabase, module: hir::Module, it: T) -> Stri #[cfg(test)] mod tests { - use crate::{fixture, MonikerResult}; + use crate::{MonikerResult, fixture}; use super::MonikerKind; diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index 3fb3a788b91..f3bb3df1cd8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -3,9 +3,9 @@ use std::{iter::once, mem}; use hir::Semantics; use ide_db::syntax_helpers::tree_diff::diff; use ide_db::text_edit::{TextEdit, TextEditBuilder}; -use ide_db::{helpers::pick_best_token, FileRange, RootDatabase}; +use ide_db::{FileRange, RootDatabase, helpers::pick_best_token}; use itertools::Itertools; -use syntax::{ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange}; +use syntax::{AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, ast, match_ast}; #[derive(Copy, Clone, Debug)] pub enum Direction { @@ -174,7 +174,7 @@ fn replace_nodes<'a>( #[cfg(test)] mod tests { use crate::fixture; - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use crate::Direction; diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index d67aaac06fb..9334b73fc7b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -5,19 +5,20 @@ use std::fmt; use arrayvec::ArrayVec; use either::Either; use hir::{ - db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasCrate, - HasSource, HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, + AssocItem, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId, InFile, + LocalSource, ModuleSource, db::ExpandDatabase, symbols::FileSymbol, }; use ide_db::{ + FileId, FileRange, RootDatabase, SymbolKind, defs::Definition, documentation::{Documentation, HasDocs}, - FileId, FileRange, RootDatabase, SymbolKind, }; use span::Edition; use stdx::never; use syntax::{ + AstNode, SmolStr, SyntaxNode, TextRange, ToSmolStr, ast::{self, HasName}, - format_smolstr, AstNode, SmolStr, SyntaxNode, TextRange, ToSmolStr, + format_smolstr, }; /// `NavigationTarget` represents an element in the editor's UI which you can @@ -816,14 +817,10 @@ pub(crate) fn orig_range_with_focus_r( ) -> UpmappingResult<(FileRange, Option<TextRange>)> { let Some(name) = focus_range else { return orig_range_r(db, hir_file, value) }; - let call_kind = - || db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id).kind; + let call_kind = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).kind; - let def_range = || { - db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) - .def - .definition_range(db) - }; + let def_range = + || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).def.definition_range(db); // FIXME: Also make use of the syntax context to determine which site we are at? let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db); @@ -900,7 +897,7 @@ pub(crate) fn orig_range_with_focus_r( UpmappingResult { call_site: ( - call_site_range.into(), + call_site_range.into_file_id(db), call_site_focus.and_then(|hir::FileRange { file_id, range }| { if call_site_range.file_id == file_id && call_site_range.range.contains_range(range) { @@ -912,7 +909,7 @@ pub(crate) fn orig_range_with_focus_r( ), def_site: def_site.map(|(def_site_range, def_site_focus)| { ( - def_site_range.into(), + def_site_range.into_file_id(db), def_site_focus.and_then(|hir::FileRange { file_id, range }| { if def_site_range.file_id == file_id && def_site_range.range.contains_range(range) @@ -933,7 +930,10 @@ fn orig_range( value: &SyntaxNode, ) -> UpmappingResult<(FileRange, Option<TextRange>)> { UpmappingResult { - call_site: (InFile::new(hir_file, value).original_file_range_rooted(db).into(), None), + call_site: ( + InFile::new(hir_file, value).original_file_range_rooted(db).into_file_id(db), + None, + ), def_site: None, } } @@ -944,7 +944,10 @@ fn orig_range_r( value: TextRange, ) -> UpmappingResult<(FileRange, Option<TextRange>)> { UpmappingResult { - call_site: (InFile::new(hir_file, value).original_node_file_range(db).0.into(), None), + call_site: ( + InFile::new(hir_file, value).original_node_file_range(db).0.into_file_id(db), + None, + ), def_site: None, } } @@ -953,7 +956,7 @@ fn orig_range_r( mod tests { use expect_test::expect; - use crate::{fixture, Query}; + use crate::{Query, fixture}; #[test] fn test_nav_for_symbol() { diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 6d82f9b0634..6dc01c45063 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -1,7 +1,7 @@ -use hir::{db::DefDatabase, Semantics}; +use hir::{Semantics, db::DefDatabase}; use ide_db::{ - base_db::{CrateId, FileLoader}, FileId, FilePosition, RootDatabase, + base_db::{Crate, RootQueryDb}, }; use itertools::Itertools; use syntax::{ @@ -53,11 +53,13 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na } /// This returns `Vec` because a module may be included from several places. -pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { +pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<Crate> { db.relevant_crates(file_id) .iter() .copied() - .filter(|&crate_id| db.crate_def_map(crate_id).modules_for_file(file_id).next().is_some()) + .filter(|&crate_id| { + db.crate_def_map(crate_id).modules_for_file(db, file_id).next().is_some() + }) .sorted() .collect() } diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 069818d50e7..4fa116444b7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -11,21 +11,22 @@ use hir::{PathResolution, Semantics}; use ide_db::{ + FileId, RootDatabase, defs::{Definition, NameClass, NameRefClass}, search::{ReferenceCategory, SearchScope, UsageSearchResult}, - FileId, RootDatabase, }; use itertools::Itertools; use nohash_hasher::IntMap; use span::Edition; use syntax::{ - ast::{self, HasName}, - match_ast, AstNode, + AstNode, SyntaxKind::*, - SyntaxNode, TextRange, TextSize, T, + SyntaxNode, T, TextRange, TextSize, + ast::{self, HasName}, + match_ast, }; -use crate::{highlight_related, FilePosition, HighlightedRange, NavigationTarget, TryToNav}; +use crate::{FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { @@ -67,7 +68,7 @@ pub(crate) fn find_all_refs( .into_iter() .map(|(file_id, refs)| { ( - file_id.into(), + file_id.file_id(sema.db), refs.into_iter() .map(|file_ref| (file_ref.range, file_ref.category)) .unique() @@ -123,11 +124,11 @@ pub(crate) fn find_all_refs( } } -pub(crate) fn find_defs<'a>( - sema: &'a Semantics<'_, RootDatabase>, +pub(crate) fn find_defs( + sema: &Semantics<'_, RootDatabase>, syntax: &SyntaxNode, offset: TextSize, -) -> Option<impl IntoIterator<Item = Definition> + 'a> { +) -> Option<Vec<Definition>> { let token = syntax.token_at_offset(offset).find(|t| { matches!( t.kind(), @@ -306,8 +307,10 @@ fn handle_control_flow_keywords( FilePosition { file_id, offset }: FilePosition, ) -> Option<ReferenceSearchResult> { let file = sema.parse_guess_edition(file_id); - let edition = - sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); + let edition = sema + .attach_first_edition(file_id) + .map(|it| it.edition(sema.db)) + .unwrap_or(Edition::CURRENT); let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword(edition))?; let references = match token.kind() { @@ -327,7 +330,7 @@ fn handle_control_flow_keywords( .into_iter() .map(|HighlightedRange { range, category }| (range, category)) .collect(); - (file_id.into(), ranges) + (file_id.file_id(sema.db), ranges) }) .collect(); @@ -336,12 +339,12 @@ fn handle_control_flow_keywords( #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; - use ide_db::FileId; - use span::EditionedFileId; + use expect_test::{Expect, expect}; + use hir::EditionedFileId; + use ide_db::{FileId, RootDatabase}; use stdx::format_to; - use crate::{fixture, SearchScope}; + use crate::{SearchScope, fixture}; #[test] fn exclude_tests() { @@ -1003,7 +1006,9 @@ pub(super) struct Foo$0 { check_with_scope( code, - Some(SearchScope::single_file(EditionedFileId::current_edition(FileId::from_raw(2)))), + Some(&mut |db| { + SearchScope::single_file(EditionedFileId::current_edition(db, FileId::from_raw(2))) + }), expect![[r#" quux Function FileId(0) 19..35 26..30 @@ -1259,11 +1264,12 @@ impl Foo { fn check_with_scope( #[rust_analyzer::rust_fixture] ra_fixture: &str, - search_scope: Option<SearchScope>, + search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>, expect: Expect, ) { let (analysis, pos) = fixture::position(ra_fixture); - let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap(); + let refs = + analysis.find_all_refs(pos, search_scope.map(|it| it(&analysis.db))).unwrap().unwrap(); let mut actual = String::new(); for mut refs in refs { diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index d0e1c2097a7..e6cda60cd95 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -4,16 +4,16 @@ //! tests. This module also implements a couple of magic tricks, like renaming //! `self` and to `self` (to switch between associated function and method). -use hir::{AsAssocItem, HirFileIdExt, InFile, Semantics}; +use hir::{AsAssocItem, InFile, Semantics}; use ide_db::{ + FileId, FileRange, RootDatabase, defs::{Definition, NameClass, NameRefClass}, - rename::{bail, format_err, source_edit_from_references, IdentifierKind}, + rename::{IdentifierKind, bail, format_err, source_edit_from_references}, source_change::SourceChangeBuilder, - FileId, FileRange, RootDatabase, }; use itertools::Itertools; use stdx::{always, never}; -use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize}; +use syntax::{AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, ast}; use ide_db::text_edit::TextEdit; @@ -120,7 +120,7 @@ pub(crate) fn rename( source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { ( position.file_id, - source_edit_from_references(refs, def, new_name, file_id.edition()), + source_edit_from_references(refs, def, new_name, file_id.edition(db)), ) })); @@ -297,7 +297,7 @@ fn find_definitions( // remove duplicates, comparing `Definition`s Ok(v.into_iter() .unique_by(|&(.., def)| def) - .map(|(a, b, c)| (a.into(), b, c)) + .map(|(a, b, c)| (a.into_file_id(sema.db), b, c)) .collect::<Vec<_>>() .into_iter()) } @@ -368,10 +368,13 @@ fn rename_to_self( let usages = def.usages(sema).all(); let mut source_change = SourceChange::default(); source_change.extend(usages.iter().map(|(file_id, references)| { - (file_id.into(), source_edit_from_references(references, def, "self", file_id.edition())) + ( + file_id.file_id(sema.db), + source_edit_from_references(references, def, "self", file_id.edition(sema.db)), + ) })); source_change.insert_source_edit( - file_id.original_file(sema.db), + file_id.original_file(sema.db).file_id(sema.db), TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)), ); Ok(source_change) @@ -402,9 +405,12 @@ fn rename_self_to_param( bail!("Cannot rename reference to `_` as it is being referenced multiple times"); } let mut source_change = SourceChange::default(); - source_change.insert_source_edit(file_id.original_file(sema.db), edit); + source_change.insert_source_edit(file_id.original_file(sema.db).file_id(sema.db), edit); source_change.extend(usages.iter().map(|(file_id, references)| { - (file_id.into(), source_edit_from_references(references, def, new_name, file_id.edition())) + ( + file_id.file_id(sema.db), + source_edit_from_references(references, def, new_name, file_id.edition(sema.db)), + ) })); Ok(source_change) } @@ -443,7 +449,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; use itertools::Itertools; @@ -509,10 +515,9 @@ mod tests { let found_conflicts = source_change .source_file_edits .iter() + .filter(|(_, (edit, _))| edit.change_annotation().is_some()) .flat_map(|(file_id, (edit, _))| { - edit.into_iter() - .filter(|edit| edit.annotation.is_some()) - .map(move |edit| (*file_id, edit.delete)) + edit.into_iter().map(move |edit| (*file_id, edit.delete)) }) .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start())) .collect_vec(); @@ -1081,7 +1086,6 @@ mod foo$0; Indel { insert: "foo2", delete: 4..7, - annotation: None, }, ], ), @@ -1129,7 +1133,6 @@ use crate::foo$0::FooContent; Indel { insert: "quux", delete: 8..11, - annotation: None, }, ], ), @@ -1141,7 +1144,6 @@ use crate::foo$0::FooContent; Indel { insert: "quux", delete: 11..14, - annotation: None, }, ], ), @@ -1183,7 +1185,6 @@ mod fo$0o; Indel { insert: "foo2", delete: 4..7, - annotation: None, }, ], ), @@ -1232,7 +1233,6 @@ mod outer { mod fo$0o; } Indel { insert: "bar", delete: 16..19, - annotation: None, }, ], ), @@ -1304,7 +1304,6 @@ pub mod foo$0; Indel { insert: "foo2", delete: 27..30, - annotation: None, }, ], ), @@ -1316,7 +1315,6 @@ pub mod foo$0; Indel { insert: "foo2", delete: 8..11, - annotation: None, }, ], ), @@ -1372,7 +1370,6 @@ mod quux; Indel { insert: "foo2", delete: 4..7, - annotation: None, }, ], ), @@ -1506,12 +1503,10 @@ pub fn baz() {} Indel { insert: "r#fn", delete: 4..7, - annotation: None, }, Indel { insert: "r#fn", delete: 22..25, - annotation: None, }, ], ), @@ -1576,12 +1571,10 @@ pub fn baz() {} Indel { insert: "foo", delete: 4..8, - annotation: None, }, Indel { insert: "foo", delete: 23..27, - annotation: None, }, ], ), @@ -1643,7 +1636,6 @@ fn bar() { Indel { insert: "dyn", delete: 7..10, - annotation: None, }, ], ), @@ -1655,7 +1647,6 @@ fn bar() { Indel { insert: "r#dyn", delete: 18..21, - annotation: None, }, ], ), @@ -1685,7 +1676,6 @@ fn bar() { Indel { insert: "r#dyn", delete: 7..10, - annotation: None, }, ], ), @@ -1697,7 +1687,6 @@ fn bar() { Indel { insert: "dyn", delete: 18..21, - annotation: None, }, ], ), @@ -1727,7 +1716,6 @@ fn bar() { Indel { insert: "r#dyn", delete: 7..10, - annotation: None, }, ], ), @@ -1739,7 +1727,6 @@ fn bar() { Indel { insert: "dyn", delete: 18..21, - annotation: None, }, ], ), @@ -1776,12 +1763,10 @@ fn bar() { Indel { insert: "abc", delete: 7..10, - annotation: None, }, Indel { insert: "abc", delete: 32..35, - annotation: None, }, ], ), @@ -1793,7 +1778,6 @@ fn bar() { Indel { insert: "abc", delete: 18..23, - annotation: None, }, ], ), @@ -1827,12 +1811,10 @@ fn bar() { Indel { insert: "abc", delete: 7..12, - annotation: None, }, Indel { insert: "abc", delete: 34..39, - annotation: None, }, ], ), @@ -1844,7 +1826,6 @@ fn bar() { Indel { insert: "abc", delete: 18..21, - annotation: None, }, ], ), diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index b8deed01fb7..ab139602404 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -4,28 +4,29 @@ use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ - db::HirDatabase, sym, symbols::FxIndexSet, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, - HasSource, HirFileIdExt, ModPath, Name, PathKind, Semantics, Symbol, + AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, ModPath, Name, PathKind, Semantics, + Symbol, db::HirDatabase, sym, symbols::FxIndexSet, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ - base_db::SourceDatabase, + FilePosition, FxHashMap, FxIndexMap, RootDatabase, SymbolKind, + base_db::RootQueryDb, defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, search::{FileReferenceNode, SearchScope}, - FilePosition, FxHashMap, FxIndexMap, RootDatabase, SymbolKind, }; use itertools::Itertools; use smallvec::SmallVec; use span::{Edition, TextSize}; use stdx::format_to; use syntax::{ + SmolStr, SyntaxNode, ToSmolStr, ast::{self, AstNode}, - format_smolstr, SmolStr, SyntaxNode, ToSmolStr, + format_smolstr, }; -use crate::{references, FileId, NavigationTarget, ToNav, TryToNav}; +use crate::{FileId, NavigationTarget, ToNav, TryToNav, references}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Runnable { @@ -284,8 +285,10 @@ fn find_related_tests_in_module( let file_id = mod_source.file_id.original_file(sema.db); let mod_scope = SearchScope::file_range(hir::FileRange { file_id, range: mod_source.value }); - let fn_pos = - FilePosition { file_id: file_id.into(), offset: fn_name.syntax().text_range().start() }; + let fn_pos = FilePosition { + file_id: file_id.file_id(sema.db), + offset: fn_name.syntax().text_range().start(), + }; find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests) } @@ -499,7 +502,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> { let krate = def.krate(db); let edition = krate.map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let display_target = krate - .unwrap_or_else(|| (*db.crate_graph().crates_in_topological_order().last().unwrap()).into()) + .unwrap_or_else(|| (*db.all_crates().last().expect("no crate graph present")).into()) .to_display_target(db); if !has_runnable_doc_test(&attrs) { return None; @@ -752,7 +755,7 @@ impl UpdateTest { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use crate::fixture; @@ -1209,13 +1212,13 @@ impl Foo { r#" //- /lib.rs $0 -macro_rules! gen { +macro_rules! generate { () => { #[test] fn foo_test() {} } } -macro_rules! gen2 { +macro_rules! generate2 { () => { mod tests2 { #[test] @@ -1223,25 +1226,25 @@ macro_rules! gen2 { } } } -macro_rules! gen_main { +macro_rules! generate_main { () => { fn main() {} } } mod tests { - gen!(); + generate!(); } -gen2!(); -gen_main!(); +generate2!(); +generate_main!(); "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..315, name: \"\", kind: Module })", - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 267..292, focus_range: 271..276, name: \"tests\", kind: Module, description: \"mod tests\" })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 283..290, name: \"foo_test\", kind: Function })", - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 293..301, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 293..301, name: \"foo_test2\", kind: Function }, true)", - "(Bin, NavigationTarget { file_id: FileId(0), full_range: 302..314, name: \"main\", kind: Function })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..310, name: \"foo_test\", kind: Function })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..326, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 313..326, name: \"foo_test2\", kind: Function }, true)", + "(Bin, NavigationTarget { file_id: FileId(0), full_range: 327..344, name: \"main\", kind: Function })", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index b5468a5aee9..0e17b355907 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -5,20 +5,22 @@ use std::collections::BTreeSet; use either::Either; use hir::{ - AssocItem, DisplayTarget, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait, + AssocItem, DisplayTarget, GenericDef, GenericParam, HirDisplay, ModuleDef, PathResolution, + Semantics, Trait, }; use ide_db::{ - active_parameter::{callable_for_node, generic_def_for_node}, - documentation::{Documentation, HasDocs}, FilePosition, FxIndexMap, + active_parameter::{callable_for_arg_list, generic_def_for_node}, + documentation::{Documentation, HasDocs}, }; +use itertools::Itertools; use span::Edition; use stdx::format_to; use syntax::{ - algo, - ast::{self, AstChildren, HasArgList}, - match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken, - TextRange, TextSize, ToSmolStr, T, + AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken, T, TextRange, + TextSize, ToSmolStr, algo, + ast::{self, AstChildren}, + match_ast, }; use crate::RootDatabase; @@ -83,8 +85,8 @@ pub(crate) fn signature_help( .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; let token = sema.descend_into_macros_single_exact(token); let edition = - sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); - let display_target = sema.first_crate_or_default(file_id).to_display_target(db); + sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); + let display_target = sema.first_crate(file_id)?.to_display_target(db); for node in token.parent_ancestors() { match_ast! { @@ -163,20 +165,8 @@ fn signature_help_for_call( edition: Edition, display_target: DisplayTarget, ) -> Option<SignatureHelp> { - // Find the calling expression and its NameRef - let mut nodes = arg_list.syntax().ancestors().skip(1); - let calling_node = loop { - if let Some(callable) = ast::CallableExpr::cast(nodes.next()?) { - let inside_callable = callable - .arg_list() - .is_some_and(|it| it.syntax().text_range().contains(token.text_range().start())); - if inside_callable { - break callable; - } - } - }; - - let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?; + let (callable, active_parameter) = + callable_for_arg_list(sema, arg_list, token.text_range().start())?; let mut res = SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter }; @@ -187,6 +177,20 @@ fn signature_help_for_call( hir::CallableKind::Function(func) => { res.doc = func.docs(db); format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); + + let generic_params = GenericDef::Function(func) + .params(db) + .iter() + .filter(|param| match param { + GenericParam::TypeParam(type_param) => !type_param.is_implicit(db), + GenericParam::ConstParam(_) | GenericParam::LifetimeParam(_) => true, + }) + .map(|param| param.display(db, display_target)) + .join(", "); + if !generic_params.is_empty() { + format_to!(res.signature, "<{}>", generic_params); + } + fn_params = Some(match callable.receiver_param(db) { Some(_self) => func.params_without_self(db), None => func.assoc_fn_params(db), @@ -195,15 +199,34 @@ fn signature_help_for_call( hir::CallableKind::TupleStruct(strukt) => { res.doc = strukt.docs(db); format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition)); + + let generic_params = GenericDef::Adt(strukt.into()) + .params(db) + .iter() + .map(|param| param.display(db, display_target)) + .join(", "); + if !generic_params.is_empty() { + format_to!(res.signature, "<{}>", generic_params); + } } hir::CallableKind::TupleEnumVariant(variant) => { res.doc = variant.docs(db); format_to!( res.signature, - "enum {}::{}", + "enum {}", variant.parent_enum(db).name(db).display(db, edition), - variant.name(db).display(db, edition) ); + + let generic_params = GenericDef::Adt(variant.parent_enum(db).into()) + .params(db) + .iter() + .map(|param| param.display(db, display_target)) + .join(", "); + if !generic_params.is_empty() { + format_to!(res.signature, "<{}>", generic_params); + } + + format_to!(res.signature, "::{}", variant.name(db).display(db, edition)) } hir::CallableKind::Closure(closure) => { let fn_trait = closure.fn_trait(db); @@ -327,7 +350,7 @@ fn signature_help_for_generics( } // These don't have generic args that can be specified hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) | hir::GenericDef::Static(_) => { - return None + return None; } } @@ -351,6 +374,20 @@ fn signature_help_for_generics( buf.clear(); format_to!(buf, "{}", param.display(db, display_target)); + match param { + GenericParam::TypeParam(param) => { + if let Some(ty) = param.default(db) { + format_to!(buf, " = {}", ty.display(db, display_target)); + } + } + GenericParam::ConstParam(param) => { + if let Some(expr) = param.default(db, display_target).and_then(|konst| konst.expr()) + { + format_to!(buf, " = {}", expr); + } + } + _ => {} + } res.push_generic_param(&buf); } if let hir::GenericDef::Trait(tr) = generics_def { @@ -695,9 +732,8 @@ fn signature_help_for_tuple_pat_ish( } #[cfg(test)] mod tests { - use std::iter; - use expect_test::{expect, Expect}; + use expect_test::{Expect, expect}; use ide_db::FilePosition; use stdx::format_to; use test_fixture::ChangeFixture; @@ -708,13 +744,14 @@ mod tests { pub(crate) fn position( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (RootDatabase, FilePosition) { - let change_fixture = ChangeFixture::parse(ra_fixture); let mut database = RootDatabase::default(); + let change_fixture = ChangeFixture::parse(&database, ra_fixture); database.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - (database, FilePosition { file_id: file_id.into(), offset }) + let position = FilePosition { file_id: file_id.file_id(&database), offset }; + (database, position) } #[track_caller] @@ -742,11 +779,11 @@ mod tests { let gap = start.checked_sub(offset).unwrap_or_else(|| { panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges()) }); - rendered.extend(iter::repeat(' ').take(gap as usize)); + rendered.extend(std::iter::repeat_n(' ', gap as usize)); let param_text = &sig_help.signature[*range]; let width = param_text.chars().count(); // … let marker = if is_active { '^' } else { '-' }; - rendered.extend(iter::repeat(marker).take(width)); + rendered.extend(std::iter::repeat_n(marker, width)); offset += gap + u32::from(range.len()); } if !sig_help.parameter_ranges().is_empty() { @@ -828,8 +865,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 fn bar() { foo($03, ); } "#, expect![[r#" - fn foo(x: i32, y: U) -> u32 - ^^^^^^ ---- + fn foo<T, U>(x: i32, y: U) -> u32 + ^^^^^^ ---- "#]], ); } @@ -842,7 +879,7 @@ fn foo<T>() -> T where T: Copy + Display {} fn bar() { foo($0); } "#, expect![[r#" - fn foo() -> T + fn foo<T>() -> T "#]], ); } @@ -1292,8 +1329,8 @@ fn main() { } "#, expect![[r#" - struct S({unknown}) - ^^^^^^^^^ + struct S<T>({unknown}) + ^^^^^^^^^ "#]], ); } @@ -1388,7 +1425,7 @@ id! { fn test() { S.foo($0); } "#, expect![[r#" - fn foo(&'a mut self) + fn foo<'a>(&'a mut self) "#]], ); } @@ -1737,8 +1774,8 @@ fn sup() { } "#, expect![[r#" - fn test(&mut self, val: V) - ^^^^^^ + fn test<V>(&mut self, val: V) + ^^^^^^ "#]], ); } @@ -1914,8 +1951,8 @@ fn f() { } "#, expect![[r#" - fn foo(x: Wrap<impl Trait<U>>) - ^^^^^^^^^^^^^^^^^^^^^^ + fn foo<U>(x: Wrap<impl Trait<U>>) + ^^^^^^^^^^^^^^^^^^^^^^ "#]], ); } @@ -2407,4 +2444,96 @@ fn main() { "#]], ); } + + #[test] + fn test_tuple_generic_param() { + check( + r#" +struct S<T>(T); + +fn main() { + let s: S<$0 +} + "#, + expect![[r#" + struct S<T> + ^ + "#]], + ); + } + + #[test] + fn test_enum_generic_param() { + check( + r#" +enum Option<T> { + Some(T), + None, +} + +fn main() { + let opt: Option<$0 +} + "#, + expect![[r#" + enum Option<T> + ^ + "#]], + ); + } + + #[test] + fn test_enum_variant_generic_param() { + check( + r#" +enum Option<T> { + Some(T), + None, +} + +fn main() { + let opt = Option::Some($0); +} + "#, + expect![[r#" + enum Option<T>::Some({unknown}) + ^^^^^^^^^ + "#]], + ); + } + + #[test] + fn test_generic_arg_with_default() { + check( + r#" +struct S<T = u8> { + field: T, +} + +fn main() { + let s: S<$0 +} + "#, + expect![[r#" + struct S<T = u8> + ^^^^^^ + "#]], + ); + + check( + r#" +struct S<const C: u8 = 5> { + field: C, +} + +fn main() { + let s: S<$0 +} + "#, + expect![[r#" + struct S<const C: u8 = 5> + ^^^^^^^^^^^^^^^ + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs index 90e350949b8..7df4499a0c2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs +++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs @@ -2,8 +2,8 @@ //! assist in ide_assists because that would require the ide_assists crate //! depend on the ide_ssr crate. -use ide_assists::{Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel}; -use ide_db::{label::Label, source_change::SourceChange, FileRange, RootDatabase}; +use ide_assists::{Assist, AssistId, AssistResolveStrategy, GroupLabel}; +use ide_db::{FileRange, RootDatabase, label::Label, source_change::SourceChange}; pub(crate) fn ssr_assists( db: &RootDatabase, @@ -16,7 +16,7 @@ pub(crate) fn ssr_assists( Some(ssr_data) => ssr_data, None => return ssr_assists, }; - let id = AssistId("ssr", AssistKind::RefactorRewrite); + let id = AssistId::refactor_rewrite("ssr"); let (source_change_for_file, source_change_for_workspace) = if resolve.should_resolve(&id) { let edits = match_finder.edits(); @@ -59,8 +59,8 @@ mod tests { use expect_test::expect; use ide_assists::{Assist, AssistResolveStrategy}; use ide_db::{ - base_db::ra_salsa::Durability, symbol_index::SymbolsDatabase, FileRange, FxHashSet, - RootDatabase, + FileRange, FxHashSet, RootDatabase, base_db::salsa::Durability, + symbol_index::SymbolsDatabase, }; use test_fixture::WithFixture; use triomphe::Arc; @@ -78,7 +78,7 @@ mod tests { ssr_assists( &db, &resolve, - FileRange { file_id: file_id.into(), range: range_or_offset.into() }, + FileRange { file_id: file_id.file_id(&db), range: range_or_offset.into() }, ) } @@ -120,6 +120,7 @@ mod tests { id: AssistId( "ssr", RefactorRewrite, + None, ), label: "Apply SSR in file", group: Some( @@ -139,9 +140,9 @@ mod tests { Indel { insert: "3", delete: 33..34, - annotation: None, }, ], + annotation: None, }, None, ), @@ -163,6 +164,7 @@ mod tests { id: AssistId( "ssr", RefactorRewrite, + None, ), label: "Apply SSR in workspace", group: Some( @@ -182,9 +184,9 @@ mod tests { Indel { insert: "3", delete: 33..34, - annotation: None, }, ], + annotation: None, }, None, ), @@ -196,9 +198,9 @@ mod tests { Indel { insert: "3", delete: 11..12, - annotation: None, }, ], + annotation: None, }, None, ), @@ -240,6 +242,7 @@ mod tests { id: AssistId( "ssr", RefactorRewrite, + None, ), label: "Apply SSR in file", group: Some( @@ -260,6 +263,7 @@ mod tests { id: AssistId( "ssr", RefactorRewrite, + None, ), label: "Apply SSR in workspace", group: Some( diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 332aecf1e3c..efee39c13db 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -1,25 +1,25 @@ //! This module provides `StaticIndex` which is used for powering //! read-only code browsers and emitting LSIF -use hir::{db::HirDatabase, Crate, HirFileIdExt, Module, Semantics}; +use arrayvec::ArrayVec; +use hir::{Crate, Module, Semantics, db::HirDatabase}; use ide_db::{ - base_db::{SourceDatabase, SourceRootDatabase, VfsPath}, - defs::Definition, + FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, + base_db::{RootQueryDb, SourceDatabase, VfsPath}, + defs::{Definition, IdentClass}, documentation::Documentation, famous_defs::FamousDefs, - helpers::get_definition, - FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, }; use span::Edition; -use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T}; +use syntax::{AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T, TextRange}; use crate::navigation_target::UpmappingResult; use crate::{ - hover::{hover_for_definition, SubstTyLen}, + Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav, + hover::{SubstTyLen, hover_for_definition}, inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve}, - moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind}, + moniker::{MonikerResult, SymbolInformationKind, def_to_kind, def_to_moniker}, parent_module::crates_for, - Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav, }; /// A static representation of fully analyzed source code. @@ -120,12 +120,28 @@ fn documentation_for_definition( famous_defs.as_ref(), def.krate(sema.db) .unwrap_or_else(|| { - (*sema.db.crate_graph().crates_in_topological_order().last().unwrap()).into() + (*sema.db.all_crates().last().expect("no crate graph present")).into() }) .to_display_target(sema.db), ) } +// FIXME: This is a weird function +fn get_definitions( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> Option<ArrayVec<Definition, 2>> { + for token in sema.descend_into_macros_exact(token) { + let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); + if let Some(defs) = def { + if !defs.is_empty() { + return Some(defs); + } + } + } + None +} + pub enum VendoredLibrariesConfig<'a> { Included { workspace_root: &'a VfsPath }, Excluded, @@ -175,9 +191,14 @@ impl StaticIndex<'_> { // hovers let sema = hir::Semantics::new(self.db); let root = sema.parse_guess_edition(file_id).syntax().clone(); - let edition = - sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); - let display_target = sema.first_crate_or_default(file_id).to_display_target(self.db); + let edition = sema + .attach_first_edition(file_id) + .map(|it| it.edition(self.db)) + .unwrap_or(Edition::CURRENT); + let display_target = match sema.first_crate(file_id) { + Some(krate) => krate.to_display_target(sema.db), + None => return, + }; let tokens = root.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), @@ -254,11 +275,14 @@ impl StaticIndex<'_> { for token in tokens { let range = token.text_range(); let node = token.parent().unwrap(); - let def = match get_definition(&sema, token.clone()) { - Some(it) => it, + match get_definitions(&sema, token.clone()) { + Some(it) => { + for i in it { + add_token(i, range, &node); + } + } None => continue, }; - add_token(def, range, &node); } self.files.push(result); } @@ -267,14 +291,14 @@ impl StaticIndex<'_> { analysis: &'a Analysis, vendored_libs_config: VendoredLibrariesConfig<'_>, ) -> StaticIndex<'a> { - let db = &*analysis.db; + let db = &analysis.db; let work = all_modules(db).into_iter().filter(|module| { let file_id = module.definition_source_file_id(db).original_file(db); - let source_root = db.file_source_root(file_id.into()); - let source_root = db.source_root(source_root); + let source_root = db.file_source_root(file_id.file_id(&analysis.db)).source_root_id(db); + let source_root = db.source_root(source_root).source_root(db); let is_vendored = match vendored_libs_config { VendoredLibrariesConfig::Included { workspace_root } => source_root - .path_for_file(&file_id.into()) + .path_for_file(&file_id.file_id(&analysis.db)) .is_some_and(|module_path| module_path.starts_with(workspace_root)), VendoredLibrariesConfig::Excluded => false, }; @@ -294,7 +318,7 @@ impl StaticIndex<'_> { if visited_files.contains(&file_id) { continue; } - this.add_file(file_id.into()); + this.add_file(file_id.file_id(&analysis.db)); // mark the file visited_files.insert(file_id); } @@ -304,8 +328,8 @@ impl StaticIndex<'_> { #[cfg(test)] mod tests { - use crate::{fixture, StaticIndex}; - use ide_db::{base_db::VfsPath, FileRange, FxHashSet}; + use crate::{StaticIndex, fixture}; + use ide_db::{FileRange, FxHashMap, FxHashSet, base_db::VfsPath}; use syntax::TextSize; use super::VendoredLibrariesConfig; @@ -360,6 +384,71 @@ mod tests { } } + #[track_caller] + fn check_references( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + vendored_libs_config: VendoredLibrariesConfig<'_>, + ) { + let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); + let s = StaticIndex::compute(&analysis, vendored_libs_config); + let mut range_set: FxHashMap<_, i32> = ranges.iter().map(|it| (it.0, 0)).collect(); + + // Make sure that all references have at least one range. We use a HashMap instead of a + // a HashSet so that we can have more than one reference at the same range. + for (_, t) in s.tokens.iter() { + for r in &t.references { + if r.is_definition { + continue; + } + if r.range.range.start() == TextSize::from(0) { + // ignore whole file range corresponding to module definition + continue; + } + match range_set.entry(r.range) { + std::collections::hash_map::Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count += 1; + } + std::collections::hash_map::Entry::Vacant(_) => { + panic!("additional reference {r:?}"); + } + } + } + } + for (range, count) in range_set.iter() { + if *count == 0 { + panic!("unfound reference {range:?}"); + } + } + } + + #[test] + fn field_initialization() { + check_references( + r#" +struct Point { + x: f64, + //^^^ + y: f64, + //^^^ +} + fn foo() { + let x = 5.; + let y = 10.; + let mut p = Point { x, y }; + //^^^^^ ^ ^ + p.x = 9.; + //^ ^ + p.y = 10.; + //^ ^ + } +"#, + VendoredLibrariesConfig::Included { + workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), + }, + ); + } + #[test] fn struct_and_enum() { check_all_ranges( @@ -384,6 +473,17 @@ enum E { X(Foo) } workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, ); + + check_references( + r#" +struct Foo; +enum E { X(Foo) } + // ^^^ +"#, + VendoredLibrariesConfig::Included { + workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), + }, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index a44be67668c..55a0db2d820 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -1,29 +1,8 @@ -use std::{fmt, marker::PhantomData}; - -use hir::{ - db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery}, - Attr, Attrs, ExpandResult, MacroFileId, Module, -}; -use ide_db::{ - base_db::{ - ra_salsa::{ - debug::{DebugQueryTable, TableEntry}, - Query, QueryTable, - }, - CompressedFileTextQuery, CrateData, ParseQuery, SourceDatabase, SourceRootId, - }, - symbol_index::ModuleSymbolsQuery, -}; -use ide_db::{ - symbol_index::{LibrarySymbolsQuery, SymbolIndex}, - RootDatabase, -}; +use ide_db::RootDatabase; +use ide_db::base_db::{BuiltCrateData, ExtraCrateData}; use itertools::Itertools; -use profile::{memory_usage, Bytes}; -use span::{EditionedFileId, FileId}; +use span::FileId; use stdx::format_to; -use syntax::{ast, Parse, SyntaxNode}; -use triomphe::Arc; // Feature: Status // @@ -37,17 +16,17 @@ use triomphe::Arc; pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { let mut buf = String::new(); - format_to!(buf, "{}\n", collect_query(CompressedFileTextQuery.in_db(db))); - format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db))); - format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db))); - format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db))); - format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db))); - format_to!(buf, "{} in total\n", memory_usage()); + // format_to!(buf, "{}\n", collect_query(CompressedFileTextQuery.in_db(db))); + // format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db))); + // format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db))); + // format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db))); + // format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db))); + // format_to!(buf, "{} in total\n", memory_usage()); - format_to!(buf, "\nDebug info:\n"); - format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db))); - format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db))); - format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db))); + // format_to!(buf, "\nDebug info:\n"); + // format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db))); + // format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db))); + // format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db))); if let Some(file_id) = file_id { format_to!(buf, "\nCrates for file {}:\n", file_id.index()); @@ -55,27 +34,25 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { if crates.is_empty() { format_to!(buf, "Does not belong to any crate"); } - let crate_graph = db.crate_graph(); for crate_id in crates { - let CrateData { + let BuiltCrateData { root_file_id, edition, - version, - display_name, - cfg_options, - potential_cfg_options, - env, dependencies, origin, is_proc_macro, proc_macro_cwd, - } = &crate_graph[crate_id]; + } = crate_id.data(db); + let ExtraCrateData { version, display_name, potential_cfg_options } = + crate_id.extra_data(db); + let cfg_options = crate_id.cfg_options(db); + let env = crate_id.env(db); format_to!( buf, "Crate: {}\n", match display_name { - Some(it) => format!("{it}({})", crate_id.into_raw()), - None => format!("{}", crate_id.into_raw()), + Some(it) => format!("{it}({:?})", crate_id), + None => format!("{:?}", crate_id), } ); format_to!(buf, " Root module file id: {}\n", root_file_id.index()); @@ -89,7 +66,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { format_to!(buf, " Proc macro cwd: {:?}\n", proc_macro_cwd); let deps = dependencies .iter() - .map(|dep| format!("{}={}", dep.name, dep.crate_id.into_raw())) + .map(|dep| format!("{}={:?}", dep.name, dep.crate_id)) .format(", "); format_to!(buf, " Dependencies: {}\n", deps); } @@ -97,190 +74,3 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { buf.trim().to_owned() } - -fn collect_query<'q, Q>(table: QueryTable<'q, Q>) -> <Q as QueryCollect>::Collector -where - QueryTable<'q, Q>: DebugQueryTable, - Q: QueryCollect, - <Q as Query>::Storage: 'q, - <Q as QueryCollect>::Collector: StatCollect< - <QueryTable<'q, Q> as DebugQueryTable>::Key, - <QueryTable<'q, Q> as DebugQueryTable>::Value, - >, -{ - struct StatCollectorWrapper<C>(C); - impl<C: StatCollect<K, V>, K, V> FromIterator<TableEntry<K, V>> for StatCollectorWrapper<C> { - fn from_iter<T>(iter: T) -> StatCollectorWrapper<C> - where - T: IntoIterator<Item = TableEntry<K, V>>, - { - let mut res = C::default(); - for entry in iter { - res.collect_entry(entry.key, entry.value); - } - StatCollectorWrapper(res) - } - } - table.entries::<StatCollectorWrapper<<Q as QueryCollect>::Collector>>().0 -} - -fn collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize -where - QueryTable<'q, Q>: DebugQueryTable, - Q: Query, - <Q as Query>::Storage: 'q, -{ - struct EntryCounter(usize); - impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter { - fn from_iter<T>(iter: T) -> EntryCounter - where - T: IntoIterator<Item = TableEntry<K, V>>, - { - EntryCounter(iter.into_iter().count()) - } - } - table.entries::<EntryCounter>().0 -} - -trait QueryCollect: Query { - type Collector; -} - -impl QueryCollect for LibrarySymbolsQuery { - type Collector = SymbolsStats<SourceRootId>; -} - -impl QueryCollect for ParseQuery { - type Collector = SyntaxTreeStats<false>; -} - -impl QueryCollect for ParseMacroExpansionQuery { - type Collector = SyntaxTreeStats<true>; -} - -impl QueryCollect for CompressedFileTextQuery { - type Collector = FilesStats; -} - -impl QueryCollect for ModuleSymbolsQuery { - type Collector = SymbolsStats<Module>; -} - -impl QueryCollect for AttrsQuery { - type Collector = AttrsStats; -} - -trait StatCollect<K, V>: Default { - fn collect_entry(&mut self, key: K, value: Option<V>); -} - -#[derive(Default)] -struct FilesStats { - total: usize, - size: Bytes, -} - -impl fmt::Display for FilesStats { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{} of files", self.size) - } -} - -impl StatCollect<FileId, Arc<[u8]>> for FilesStats { - fn collect_entry(&mut self, _: FileId, value: Option<Arc<[u8]>>) { - self.total += 1; - self.size += value.unwrap().len(); - } -} - -#[derive(Default)] -pub(crate) struct SyntaxTreeStats<const MACROS: bool> { - total: usize, - pub(crate) retained: usize, -} - -impl<const MACROS: bool> fmt::Display for SyntaxTreeStats<MACROS> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - fmt, - "{} trees, {} preserved{}", - self.total, - self.retained, - if MACROS { " (macros)" } else { "" } - ) - } -} - -impl StatCollect<EditionedFileId, Parse<ast::SourceFile>> for SyntaxTreeStats<false> { - fn collect_entry(&mut self, _: EditionedFileId, value: Option<Parse<ast::SourceFile>>) { - self.total += 1; - self.retained += value.is_some() as usize; - } -} - -impl<M> StatCollect<MacroFileId, ExpandResult<(Parse<SyntaxNode>, M)>> for SyntaxTreeStats<true> { - fn collect_entry( - &mut self, - _: MacroFileId, - value: Option<ExpandResult<(Parse<SyntaxNode>, M)>>, - ) { - self.total += 1; - self.retained += value.is_some() as usize; - } -} - -struct SymbolsStats<Key> { - total: usize, - size: Bytes, - phantom: PhantomData<Key>, -} - -impl<Key> Default for SymbolsStats<Key> { - fn default() -> Self { - Self { total: Default::default(), size: Default::default(), phantom: PhantomData } - } -} - -impl fmt::Display for SymbolsStats<Module> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{} of module index symbols ({})", self.size, self.total) - } -} -impl fmt::Display for SymbolsStats<SourceRootId> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{} of library index symbols ({})", self.size, self.total) - } -} -impl<Key> StatCollect<Key, Arc<SymbolIndex>> for SymbolsStats<Key> { - fn collect_entry(&mut self, _: Key, value: Option<Arc<SymbolIndex>>) { - if let Some(symbols) = value { - self.total += symbols.len(); - self.size += symbols.memory_size(); - } - } -} - -#[derive(Default)] -struct AttrsStats { - entries: usize, - total: usize, -} - -impl fmt::Display for AttrsStats { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let size = self.entries * size_of::<Attrs>() + self.total * size_of::<Attr>(); - let size = Bytes::new(size as _); - write!( - fmt, - "{} attribute query entries, {} total attributes ({} for storing entries)", - self.entries, self.total, size - ) - } -} - -impl<Key> StatCollect<Key, Attrs> for AttrsStats { - fn collect_entry(&mut self, _: Key, value: Option<Attrs>) { - self.entries += 1; - self.total += value.map_or(0, |it| it.len()); - } -} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 83082496d5b..e1bc76318f8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -15,26 +15,23 @@ mod tests; use std::ops::ControlFlow; use either::Either; -use hir::{ - DefWithBody, HirFileIdExt, InFile, InRealFile, MacroFileIdExt, MacroKind, Name, Semantics, -}; +use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; -use span::EditionedFileId; use syntax::{ - ast::{self, IsString}, AstNode, AstToken, NodeOrToken, SyntaxKind::*, - SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, + SyntaxNode, SyntaxToken, T, TextRange, WalkEvent, + ast::{self, IsString}, }; use crate::{ + FileId, HlMod, HlOperator, HlPunct, HlTag, syntax_highlighting::{ escape::{highlight_escape_byte, highlight_escape_char, highlight_escape_string}, format::highlight_format_string, highlights::Highlights, tags::Highlight, }, - FileId, HlMod, HlOperator, HlPunct, HlTag, }; pub(crate) use html::highlight_as_html; @@ -199,7 +196,7 @@ pub(crate) fn highlight( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); // Determine the root based on the given range. let (root, range_to_highlight) = { @@ -218,10 +215,7 @@ pub(crate) fn highlight( }; let mut hl = highlights::Highlights::new(root.text_range()); - let krate = match sema.scope(&root) { - Some(it) => it.krate(), - None => return hl.to_vec(), - }; + let krate = sema.scope(&root).map(|it| it.krate()); traverse(&mut hl, &sema, config, InRealFile::new(file_id, &root), krate, range_to_highlight); hl.to_vec() } @@ -231,10 +225,10 @@ fn traverse( sema: &Semantics<'_, RootDatabase>, config: HighlightConfig, InRealFile { file_id, value: root }: InRealFile<&SyntaxNode>, - krate: hir::Crate, + krate: Option<hir::Crate>, range_to_highlight: TextRange, ) { - let is_unlinked = sema.file_to_module_def(file_id).is_none(); + let is_unlinked = sema.file_to_module_def(file_id.file_id(sema.db)).is_none(); enum AttrOrDerive { Attr(ast::Item), @@ -494,7 +488,7 @@ fn string_injections( sema: &Semantics<'_, RootDatabase>, config: HighlightConfig, file_id: EditionedFileId, - krate: hir::Crate, + krate: Option<hir::Crate>, token: SyntaxToken, descended_token: &SyntaxToken, ) -> ControlFlow<()> { @@ -508,7 +502,14 @@ fn string_injections( { return ControlFlow::Break(()); } - highlight_format_string(hl, sema, krate, &string, &descended_string, file_id.edition()); + highlight_format_string( + hl, + sema, + krate, + &string, + &descended_string, + file_id.edition(sema.db), + ); if !string.is_raw() { highlight_escape_string(hl, &string); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index cc02aff2acf..3716dcfed00 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -1,21 +1,21 @@ //! Syntax highlighting for format macro strings. use ide_db::{ - defs::Definition, - syntax_helpers::format_string::{is_format_string, lex_format_specifiers, FormatSpecifier}, SymbolKind, + defs::Definition, + syntax_helpers::format_string::{FormatSpecifier, is_format_string, lex_format_specifiers}, }; use span::Edition; -use syntax::{ast, AstToken}; +use syntax::{AstToken, ast}; use crate::{ - syntax_highlighting::{highlight::highlight_def, highlights::Highlights}, HlRange, HlTag, + syntax_highlighting::{highlight::highlight_def, highlights::Highlights}, }; pub(super) fn highlight_format_string( stack: &mut Highlights, sema: &hir::Semantics<'_, ide_db::RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, string: &ast::String, expanded_string: &ast::String, edition: Edition, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 282fbb4433b..87db0cd7dc5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -3,23 +3,23 @@ use std::ops::ControlFlow; use either::Either; -use hir::{AsAssocItem, HasVisibility, MacroFileIdExt, Semantics}; +use hir::{AsAssocItem, HasVisibility, Semantics}; use ide_db::{ + FxHashMap, RootDatabase, SymbolKind, defs::{Definition, IdentClass, NameClass, NameRefClass}, syntax_helpers::node_ext::walk_pat, - FxHashMap, RootDatabase, SymbolKind, }; use span::Edition; use stdx::hash_once; use syntax::{ - ast, match_ast, AstNode, AstPtr, AstToken, NodeOrToken, + AstNode, AstPtr, AstToken, NodeOrToken, SyntaxKind::{self, *}, - SyntaxNode, SyntaxNodePtr, SyntaxToken, T, + SyntaxNode, SyntaxNodePtr, SyntaxToken, T, ast, match_ast, }; use crate::{ - syntax_highlighting::tags::{HlOperator, HlPunct}, Highlight, HlMod, HlTag, + syntax_highlighting::tags::{HlOperator, HlPunct}, }; pub(super) fn token( @@ -63,7 +63,7 @@ pub(super) fn token( pub(super) fn name_like( sema: &Semantics<'_, RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>, is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, syntactic_name_ref_highlighting: bool, @@ -113,7 +113,8 @@ fn punctuation( ) -> Highlight { let operator_parent = token.parent(); let parent_kind = operator_parent.as_ref().map_or(EOF, SyntaxNode::kind); - let h = match (kind, parent_kind) { + + match (kind, parent_kind) { (T![?], TRY_EXPR) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow, (T![&], BIN_EXPR) => HlOperator::Bitwise.into(), (T![&], REF_EXPR | REF_PAT) => HlTag::Operator(HlOperator::Other).into(), @@ -143,11 +144,7 @@ fn punctuation( let ptr = operator_parent .as_ref() .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it))); - if ptr.is_some_and(is_unsafe_node) { - h | HlMod::Unsafe - } else { - h - } + if ptr.is_some_and(is_unsafe_node) { h | HlMod::Unsafe } else { h } } (T![-], PREFIX_EXPR) => { let prefix_expr = @@ -223,11 +220,7 @@ fn punctuation( let is_unsafe = is_unsafe_macro || operator_parent .and_then(|it| { - if ast::ArgList::can_cast(it.kind()) { - it.parent() - } else { - Some(it) - } + if ast::ArgList::can_cast(it.kind()) { it.parent() } else { Some(it) } }) .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(&it))) .is_some_and(is_unsafe_node); @@ -248,8 +241,7 @@ fn punctuation( _ => HlPunct::Other, } .into(), - }; - h + } } fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight { @@ -280,7 +272,7 @@ fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight { fn highlight_name_ref( sema: &Semantics<'_, RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>, binding_hash: &mut Option<u64>, is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, @@ -296,7 +288,7 @@ fn highlight_name_ref( let name_class = match NameRefClass::classify(sema, &name_ref) { Some(name_kind) => name_kind, None if syntactic_name_ref_highlighting => { - return highlight_name_ref_by_syntax(name_ref, sema, krate, is_unsafe_node) + return highlight_name_ref_by_syntax(name_ref, sema, krate, is_unsafe_node); } // FIXME: This is required for helper attributes used by proc-macros, as those do not map down // to anything when used. @@ -409,9 +401,10 @@ fn highlight_name_ref( NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { let mut h = HlTag::Symbol(SymbolKind::Module).into(); - if resolved_krate != krate { - h |= HlMod::Library + if krate.as_ref().is_some_and(|krate| resolved_krate != *krate) { + h |= HlMod::Library; } + let is_public = decl.visibility(db) == hir::Visibility::Public; if is_public { h |= HlMod::Public @@ -439,7 +432,7 @@ fn highlight_name( bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>, binding_hash: &mut Option<u64>, is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, - krate: hir::Crate, + krate: Option<hir::Crate>, name: ast::Name, edition: Edition, ) -> Highlight { @@ -484,7 +477,7 @@ fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { pub(super) fn highlight_def( sema: &Semantics<'_, RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, def: Definition, edition: Edition, is_ref: bool, @@ -668,7 +661,7 @@ pub(super) fn highlight_def( }; let def_crate = def.krate(db); - let is_from_other_crate = def_crate != Some(krate); + let is_from_other_crate = def_crate != krate; let is_from_builtin_crate = def_crate.is_some_and(|def_crate| def_crate.is_builtin(db)); let is_builtin = matches!( def, @@ -689,7 +682,7 @@ pub(super) fn highlight_def( fn highlight_method_call_by_name_ref( sema: &Semantics<'_, RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, name_ref: &ast::NameRef, is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Option<Highlight> { @@ -699,7 +692,7 @@ fn highlight_method_call_by_name_ref( fn highlight_method_call( sema: &Semantics<'_, RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, method_call: &ast::MethodCallExpr, is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Option<Highlight> { @@ -726,7 +719,7 @@ fn highlight_method_call( } let def_crate = func.module(sema.db).krate(); - let is_from_other_crate = def_crate != krate; + let is_from_other_crate = krate.as_ref().map_or(false, |krate| def_crate != *krate); let is_from_builtin_crate = def_crate.is_builtin(sema.db); let is_public = func.visibility(sema.db) == hir::Visibility::Public; @@ -799,7 +792,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { fn highlight_name_ref_by_syntax( name: ast::NameRef, sema: &Semantics<'_, RootDatabase>, - krate: hir::Crate, + krate: Option<hir::Crate>, is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Highlight { let default = HlTag::UnresolvedReference; @@ -818,12 +811,9 @@ fn highlight_name_ref_by_syntax( let h = HlTag::Symbol(SymbolKind::Field); let is_unsafe = ast::Expr::cast(parent) .is_some_and(|it| is_unsafe_node(AstPtr::new(&it).wrap_left())); - if is_unsafe { - h | HlMod::Unsafe - } else { - h.into() - } + if is_unsafe { h | HlMod::Unsafe } else { h.into() } } + RECORD_EXPR_FIELD | RECORD_PAT_FIELD => HlTag::Symbol(SymbolKind::Field).into(), PATH_SEGMENT => { let name_based_fallback = || { if name.text().chars().next().unwrap_or_default().is_uppercase() { @@ -862,6 +852,8 @@ fn highlight_name_ref_by_syntax( .into(), } } + ASSOC_TYPE_ARG => SymbolKind::TypeAlias.into(), + USE_BOUND_GENERIC_ARGS => SymbolKind::TypeParam.into(), _ => default.into(), } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index 07d40bafeba..9fd807f031f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -1,21 +1,20 @@ //! Renders a bit of code as HTML. -use hir::Semantics; +use hir::{EditionedFileId, Semantics}; use oorandom::Rand32; -use span::EditionedFileId; use stdx::format_to; use syntax::AstNode; use crate::{ - syntax_highlighting::{highlight, HighlightConfig}, FileId, RootDatabase, + syntax_highlighting::{HighlightConfig, highlight}, }; pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); let file = sema.parse(file_id); let file = file.syntax(); fn rainbowify(seed: u64) -> String { @@ -40,7 +39,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo macro_bang: true, syntactic_name_ref_highlighting: false, }, - file_id.into(), + file_id.file_id(db), None, ); let text = file.to_string(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 1be90ad6a1e..0998e14c87b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -3,21 +3,20 @@ use std::mem; use either::Either; -use hir::{sym, HirFileId, InFile, Semantics}; +use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym}; use ide_db::{ - active_parameter::ActiveParameter, defs::Definition, documentation::docs_with_rangemap, - rust_doc::is_rust_fence, SymbolKind, + SymbolKind, active_parameter::ActiveParameter, defs::Definition, + documentation::docs_with_rangemap, rust_doc::is_rust_fence, }; -use span::EditionedFileId; use syntax::{ - ast::{self, AstNode, IsString, QuoteOffsets}, AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize, + ast::{self, AstNode, IsString, QuoteOffsets}, }; use crate::{ - doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, - syntax_highlighting::{highlights::Highlights, injector::Injector, HighlightConfig}, Analysis, HlMod, HlRange, HlTag, RootDatabase, + doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, + syntax_highlighting::{HighlightConfig, highlights::Highlights, injector::Injector}, }; pub(super) fn ra_fixture( @@ -161,7 +160,7 @@ pub(super) fn doc_comment( let mut new_comments = Vec::new(); let mut string; - for attr in attributes.by_key(&sym::doc).attrs() { + for attr in attributes.by_key(sym::doc).attrs() { let InFile { file_id, value: src } = attrs_source_map.source_of(attr); if file_id != src_file_id { continue; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs index a902fd717f0..c30f7973249 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs @@ -53,11 +53,7 @@ impl<T> Delta<T> { where T: Ord + Sub<Output = T>, { - if to >= from { - Delta::Add(to - from) - } else { - Delta::Sub(from - to) - } + if to >= from { Delta::Add(to - from) } else { Delta::Sub(from - to) } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index c8c8c5dba4c..d00f279c829 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -45,14 +45,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> <span class="comment documentation">//! ```</span> +<span class="comment documentation">//! Syntactic name ref highlighting testing</span> <span class="comment documentation">//! ```rust</span> <span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="self_keyword crate_root injected">self</span><span class="semicolon injected">;</span> -<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">std</span><span class="semicolon injected">;</span> +<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">other</span><span class="none injected"> </span><span class="keyword injected">as</span><span class="none injected"> </span><span class="module crate_root declaration injected">otter</span><span class="semicolon injected">;</span> <span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">core</span><span class="semicolon injected">;</span> -<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">alloc</span><span class="semicolon injected">;</span> -<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">proc_macro</span><span class="semicolon injected">;</span> -<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">test</span><span class="semicolon injected">;</span> -<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">extern</span><span class="none injected"> </span><span class="keyword injected">crate</span><span class="none injected"> </span><span class="module crate_root injected">Krate</span><span class="semicolon injected">;</span> +<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">T</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="keyword injected">type</span><span class="none injected"> </span><span class="type_alias associated declaration injected static trait">Assoc</span><span class="semicolon injected">;</span><span class="none injected"> </span><span class="brace injected">}</span> +<span class="comment documentation">//!</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">f</span><span class="angle injected"><</span><span class="type_param declaration injected">Arg</span><span class="angle injected">></span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">-></span><span class="none injected"> </span><span class="keyword injected">use</span><span class="angle injected"><</span><span class="struct injected">Arg</span><span class="angle injected">></span><span class="none injected"> </span><span class="keyword injected">where</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="colon injected">:</span><span class="none injected"> </span><span class="trait injected">T</span><span class="comparison injected"><</span><span class="struct injected">Assoc</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="comparison injected">></span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> <span class="comment documentation">//! ```</span> <span class="keyword">mod</span> <span class="module declaration">outline_module</span><span class="semicolon">;</span> diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_19357.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_19357.html new file mode 100644 index 00000000000..36ed8c594f7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_19357.html @@ -0,0 +1,46 @@ + +<style> +body { margin: 0; } +pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } + +.lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } +.comment { color: #7F9F7F; } +.documentation { color: #629755; } +.intra_doc_link { font-style: italic; } +.injected { opacity: 0.65 ; } +.struct, .enum { color: #7CB8BB; } +.enum_variant { color: #BDE0F3; } +.string_literal { color: #CC9393; } +.field { color: #94BFF3; } +.function { color: #93E0E3; } +.parameter { color: #94BFF3; } +.text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.builtin_type { color: #8CD0D3; } +.type_param { color: #DFAF8F; } +.attribute { color: #94BFF3; } +.numeric_literal { color: #BFEBBF; } +.bool_literal { color: #BFE6EB; } +.macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } +.derive { color: #94BFF3; font-style: italic; } +.module { color: #AFD8AF; } +.value_param { color: #DCDCCC; } +.variable { color: #DCDCCC; } +.format_specifier { color: #CC696B; } +.mutable { text-decoration: underline; } +.escape_sequence { color: #94BFF3; } +.keyword { color: #F0DFAF; font-weight: bold; } +.control { font-style: italic; } +.reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } +.unsafe { color: #BC8383; } + +.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +</style> +<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> + <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword">mut</span> <span class="numeric_literal">5</span><span class="semicolon">;</span> +<span class="brace">}</span> +</code></pre> \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index 7f6b4c2c880..e1a8d876c41 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html @@ -41,14 +41,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> - <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="10753541418856619067" style="color: hsl(51,52%,47%);">x</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="9865812862466303869" style="color: hsl(329,86%,55%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="5697120079570210533" style="color: hsl(268,86%,80%);">x</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="4222724691718692706" style="color: hsl(156,71%,51%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="4890670724659097491" style="color: hsl(330,46%,45%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="4002942168268782293" style="color: hsl(114,87%,67%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="4890670724659097491" style="color: hsl(330,46%,45%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="17855021198829413584" style="color: hsl(230,76%,79%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="16380625810977895757" style="color: hsl(262,75%,75%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="17855021198829413584" style="color: hsl(230,76%,79%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> - <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre> \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 8f69bb82300..dd359326c61 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1,11 +1,11 @@ use std::time::Instant; -use expect_test::{expect_file, ExpectFile}; +use expect_test::{ExpectFile, expect_file}; use ide_db::SymbolKind; use span::Edition; -use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear}; +use test_utils::{AssertLinear, bench, bench_fixture, skip_slow_tests}; -use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange}; +use crate::{FileRange, HighlightConfig, HlTag, TextRange, fixture}; const HL_CONFIG: HighlightConfig = HighlightConfig { strings: true, @@ -739,14 +739,13 @@ fn test_highlight_doc_comment() { //! fn test() {} //! ``` +//! Syntactic name ref highlighting testing //! ```rust //! extern crate self; -//! extern crate std; +//! extern crate other as otter; //! extern crate core; -//! extern crate alloc; -//! extern crate proc_macro; -//! extern crate test; -//! extern crate Krate; +//! trait T { type Assoc; } +//! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {} //! ``` mod outline_module; @@ -1302,7 +1301,7 @@ fn benchmark_syntax_highlighting_parser() { }) .count() }; - assert_eq!(hash, 1167); + assert_eq!(hash, 1606); } #[test] @@ -1421,3 +1420,18 @@ fn template() {} false, ); } + +#[test] +fn issue_19357() { + check_highlighting( + r#" +//- /foo.rs +fn main() { + let x = &raw mut 5; +} +//- /main.rs +"#, + expect_file!["./test_data/highlight_issue_19357.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs index 30b1d4c39b3..06cbd50e946 100644 --- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs +++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs @@ -1,17 +1,15 @@ //! Discovers tests use hir::{Crate, Module, ModuleDef, Semantics}; -use ide_db::{ - base_db::{CrateGraph, CrateId, SourceDatabase}, - FileId, RootDatabase, -}; +use ide_db::base_db; +use ide_db::{FileId, RootDatabase, base_db::RootQueryDb}; use syntax::TextRange; -use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav}; +use crate::{NavigationTarget, Runnable, TryToNav, runnables::runnable_fn}; #[derive(Debug)] pub enum TestItemKind { - Crate(CrateId), + Crate(base_db::Crate), Module, Function, } @@ -28,12 +26,12 @@ pub struct TestItem { } pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> { - let crate_graph = db.crate_graph(); - crate_graph + db.all_crates() .iter() - .filter(|&id| crate_graph[id].origin.is_local()) + .copied() + .filter(|&id| id.data(db).origin.is_local()) .filter_map(|id| { - let test_id = crate_graph[id].display_name.as_ref()?.to_string(); + let test_id = id.extra_data(db).display_name.as_ref()?.to_string(); Some(TestItem { kind: TestItemKind::Crate(id), label: test_id.clone(), @@ -47,12 +45,12 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> { .collect() } -fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option<CrateId> { +fn find_crate_by_id(db: &RootDatabase, crate_id: &str) -> Option<base_db::Crate> { // here, we use display_name as the crate id. This is not super ideal, but it works since we // only show tests for the local crates. - crate_graph.iter().find(|&id| { - crate_graph[id].origin.is_local() - && crate_graph[id].display_name.as_ref().is_some_and(|x| x.to_string() == crate_id) + db.all_crates().iter().copied().find(|&id| { + id.data(db).origin.is_local() + && id.extra_data(db).display_name.as_ref().is_some_and(|x| x.to_string() == crate_id) }) } @@ -115,8 +113,7 @@ pub(crate) fn discover_tests_in_crate_by_test_id( db: &RootDatabase, crate_test_id: &str, ) -> Vec<TestItem> { - let crate_graph = db.crate_graph(); - let Some(crate_id) = find_crate_by_id(&crate_graph, crate_test_id) else { + let Some(crate_id) = find_crate_by_id(db, crate_test_id) else { return vec![]; }; discover_tests_in_crate(db, crate_id) @@ -171,12 +168,14 @@ fn find_module_id_and_test_parents( Some((r, id)) } -pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec<TestItem> { - let crate_graph = db.crate_graph(); - if !crate_graph[crate_id].origin.is_local() { +pub(crate) fn discover_tests_in_crate( + db: &RootDatabase, + crate_id: base_db::Crate, +) -> Vec<TestItem> { + if !crate_id.data(db).origin.is_local() { return vec![]; } - let Some(crate_test_id) = &crate_graph[crate_id].display_name else { + let Some(crate_test_id) = &crate_id.extra_data(db).display_name else { return vec![]; }; let kind = TestItemKind::Crate(crate_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index 8c9dd051452..4df7e25223d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -15,14 +15,15 @@ mod on_enter; +use hir::EditionedFileId; +use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb}; +use span::Edition; use std::iter; -use ide_db::{base_db::SourceDatabase, FilePosition, RootDatabase}; -use span::{Edition, EditionedFileId}; use syntax::{ - algo::{ancestors_at_offset, find_node_at_offset}, - ast::{self, edit::IndentLevel, AstToken}, AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, + algo::{ancestors_at_offset, find_node_at_offset}, + ast::{self, AstToken, edit::IndentLevel}, }; use ide_db::text_edit::TextEdit; @@ -73,7 +74,8 @@ pub(crate) fn on_char_typed( // FIXME: We are hitting the database here, if we are unlucky this call might block momentarily // causing the editor to feel sluggish! let edition = Edition::CURRENT_FIXME; - let file = &db.parse(EditionedFileId::new(position.file_id, edition)); + let editioned_file_id_wrapper = EditionedFileId::new(db, position.file_id, edition); + let file = &db.parse(editioned_file_id_wrapper); let char_matches_position = file.tree().syntax().text().char_at(position.offset) == Some(char_typed); if !stdx::always!(char_matches_position) { diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index c6d1c283f4e..fdc583a15cc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -1,15 +1,14 @@ //! Handles the `Enter` key press. At the momently, this only continues //! comments, but should handle indent some time in the future as well. -use ide_db::RootDatabase; -use ide_db::{base_db::SourceDatabase, FilePosition}; -use span::EditionedFileId; +use ide_db::base_db::RootQueryDb; +use ide_db::{FilePosition, RootDatabase}; use syntax::{ - algo::find_node_at_offset, - ast::{self, edit::IndentLevel, AstToken}, AstNode, SmolStr, SourceFile, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, + algo::find_node_at_offset, + ast::{self, AstToken, edit::IndentLevel}, }; use ide_db::text_edit::TextEdit; @@ -51,7 +50,9 @@ use ide_db::text_edit::TextEdit; // //  pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { - let parse = db.parse(EditionedFileId::current_edition(position.file_id)); + let editioned_file_id_wrapper = + ide_db::base_db::EditionedFileId::current_edition(db, position.file_id); + let parse = db.parse(editioned_file_id_wrapper); let file = parse.tree(); let token = file.syntax().token_at_offset(position.offset).left_biased()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index eb6eb7da1e9..4696fef3209 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -1,9 +1,11 @@ use dot::{Id, LabelText}; +use ide_db::base_db::salsa::plumbing::AsId; use ide_db::{ - base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceRootDatabase}, - FxHashSet, RootDatabase, + FxHashMap, RootDatabase, + base_db::{ + BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, RootQueryDb, SourceDatabase, + }, }; -use triomphe::Arc; // Feature: View Crate Graph // @@ -16,76 +18,81 @@ use triomphe::Arc; // |---------|-------------| // | VS Code | **rust-analyzer: View Crate Graph** | pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> { - let crate_graph = db.crate_graph(); - let crates_to_render = crate_graph + let all_crates = db.all_crates(); + let crates_to_render = all_crates .iter() - .filter(|krate| { + .copied() + .map(|krate| (krate, (krate.data(db), krate.extra_data(db)))) + .filter(|(_, (crate_data, _))| { if full { true } else { // Only render workspace crates - let root_id = db.file_source_root(crate_graph[*krate].root_file_id); - !db.source_root(root_id).is_library + let root_id = db.file_source_root(crate_data.root_file_id).source_root_id(db); + !db.source_root(root_id).source_root(db).is_library } }) .collect(); - let graph = DotCrateGraph { graph: crate_graph, crates_to_render }; + let graph = DotCrateGraph { crates_to_render }; let mut dot = Vec::new(); dot::render(&graph, &mut dot).unwrap(); Ok(String::from_utf8(dot).unwrap()) } -struct DotCrateGraph { - graph: Arc<CrateGraph>, - crates_to_render: FxHashSet<CrateId>, +struct DotCrateGraph<'db> { + crates_to_render: FxHashMap<Crate, (&'db BuiltCrateData, &'db ExtraCrateData)>, } -type Edge<'a> = (CrateId, &'a Dependency); +type Edge<'a> = (Crate, &'a BuiltDependency); -impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph { - fn nodes(&'a self) -> dot::Nodes<'a, CrateId> { - self.crates_to_render.iter().copied().collect() +impl<'a> dot::GraphWalk<'a, Crate, Edge<'a>> for DotCrateGraph<'_> { + fn nodes(&'a self) -> dot::Nodes<'a, Crate> { + self.crates_to_render.keys().copied().collect() } fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.crates_to_render .iter() - .flat_map(|krate| { - self.graph[*krate] + .flat_map(|(krate, (crate_data, _))| { + crate_data .dependencies .iter() - .filter(|dep| self.crates_to_render.contains(&dep.crate_id)) + .filter(|dep| self.crates_to_render.contains_key(&dep.crate_id)) .map(move |dep| (*krate, dep)) }) .collect() } - fn source(&'a self, edge: &Edge<'a>) -> CrateId { + fn source(&'a self, edge: &Edge<'a>) -> Crate { edge.0 } - fn target(&'a self, edge: &Edge<'a>) -> CrateId { + fn target(&'a self, edge: &Edge<'a>) -> Crate { edge.1.crate_id } } -impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph { +impl<'a> dot::Labeller<'a, Crate, Edge<'a>> for DotCrateGraph<'_> { fn graph_id(&'a self) -> Id<'a> { Id::new("rust_analyzer_crate_graph").unwrap() } - fn node_id(&'a self, n: &CrateId) -> Id<'a> { - Id::new(format!("_{}", u32::from(n.into_raw()))).unwrap() + fn node_id(&'a self, n: &Crate) -> Id<'a> { + let id = n.as_id().as_u32(); + Id::new(format!("_{:?}", id)).unwrap() } - fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> { + fn node_shape(&'a self, _node: &Crate) -> Option<LabelText<'a>> { Some(LabelText::LabelStr("box".into())) } - fn node_label(&'a self, n: &CrateId) -> LabelText<'a> { - let name = - self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name.as_str()); + fn node_label(&'a self, n: &Crate) -> LabelText<'a> { + let name = self.crates_to_render[n] + .1 + .display_name + .as_ref() + .map_or("(unnamed crate)", |name| name.as_str()); LabelText::LabelStr(name.into()) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs index bfdf9d0f337..ec5e993f5a6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs @@ -1,6 +1,6 @@ -use hir::{DefWithBody, Semantics}; +use hir::Semantics; use ide_db::{FilePosition, RootDatabase}; -use syntax::{algo::ancestors_at_offset, ast, AstNode}; +use syntax::AstNode; // Feature: View Hir // @@ -10,21 +10,10 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // //  pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { - body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) -} - -fn body_hir(db: &RootDatabase, position: FilePosition) -> Option<String> { - let sema = Semantics::new(db); - let source_file = sema.parse_guess_edition(position.file_id); - - let item = ancestors_at_offset(source_file.syntax(), position.offset) - .filter(|it| !ast::MacroCall::can_cast(it.kind())) - .find_map(ast::Item::cast)?; - let def: DefWithBody = match item { - ast::Item::Fn(it) => sema.to_def(&it)?.into(), - ast::Item::Const(it) => sema.to_def(&it)?.into(), - ast::Item::Static(it) => sema.to_def(&it)?.into(), - _ => return None, - }; - Some(def.debug_hir(db)) + (|| { + let sema = Semantics::new(db); + let source_file = sema.parse_guess_edition(position.file_id); + sema.debug_hir_at(source_file.syntax().token_at_offset(position.offset).next()?) + })() + .unwrap_or_else(|| "Not inside a lowerable item".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs index 67c241cbb91..2cd751463bd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs @@ -1,6 +1,5 @@ -use hir::{db::DefDatabase, Semantics}; +use hir::{EditionedFileId, Semantics, db::DefDatabase}; use ide_db::{FileId, RootDatabase}; -use span::EditionedFileId; // Feature: Debug ItemTree // @@ -13,6 +12,6 @@ pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); - db.file_item_tree(file_id.into()).pretty_print(db, file_id.edition()) + .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + db.file_item_tree(file_id.into()).pretty_print(db, file_id.edition(db)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 34bca7bce12..140ae4265be 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -2,9 +2,9 @@ use std::fmt; use hir::{DisplayTarget, Field, HirDisplay, Layout, Semantics, Type}; use ide_db::{ + RootDatabase, defs::Definition, helpers::{get_definition, pick_best_token}, - RootDatabase, }; use syntax::{AstNode, SyntaxKind}; @@ -83,7 +83,7 @@ pub(crate) fn view_memory_layout( ) -> Option<RecursiveMemoryLayout> { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); - let display_target = sema.first_crate_or_default(position.file_id).to_display_target(db); + let display_target = sema.first_crate(position.file_id)?.to_display_target(db); let token = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 3, diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs index aa4ff64a819..6ca231c7a81 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs @@ -1,6 +1,6 @@ use hir::{DefWithBody, Semantics}; use ide_db::{FilePosition, RootDatabase}; -use syntax::{algo::ancestors_at_offset, ast, AstNode}; +use syntax::{AstNode, algo::ancestors_at_offset, ast}; // Feature: View Mir // diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs index 407720864bf..ecd93e8b281 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs @@ -1,13 +1,13 @@ use hir::Semantics; use ide_db::{ - line_index::{LineCol, LineIndex}, FileId, LineIndexDatabase, RootDatabase, + line_index::{LineCol, LineIndex}, }; use span::{TextRange, TextSize}; use stdx::format_to; use syntax::{ - ast::{self, IsString}, AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent, + ast::{self, IsString}, }; use triomphe::Arc; |
