From 94a0819fe3a0d679c3042a77bfe6a2afc505daea Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:28 +0200 Subject: Adding upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- src/tools/rust-analyzer/crates/ide/Cargo.toml | 6 +- .../rust-analyzer/crates/ide/src/annotations.rs | 307 +++++++++++++++------ .../crates/ide/src/annotations/fn_references.rs | 103 +++++++ .../rust-analyzer/crates/ide/src/call_hierarchy.rs | 4 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 11 +- .../rust-analyzer/crates/ide/src/expand_macro.rs | 4 +- .../rust-analyzer/crates/ide/src/fn_references.rs | 94 ------- .../crates/ide/src/goto_definition.rs | 221 ++++++++++++++- .../crates/ide/src/goto_implementation.rs | 2 +- .../crates/ide/src/highlight_related.rs | 30 +- src/tools/rust-analyzer/crates/ide/src/hover.rs | 25 +- .../rust-analyzer/crates/ide/src/hover/render.rs | 34 ++- .../rust-analyzer/crates/ide/src/hover/tests.rs | 270 +++++++++++++++++- .../rust-analyzer/crates/ide/src/inlay_hints.rs | 267 +++++++++++++++--- .../rust-analyzer/crates/ide/src/join_lines.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 48 ++-- .../rust-analyzer/crates/ide/src/matching_brace.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/moniker.rs | 143 ++++++++-- .../rust-analyzer/crates/ide/src/move_item.rs | 4 +- .../rust-analyzer/crates/ide/src/parent_module.rs | 20 +- .../rust-analyzer/crates/ide/src/prime_caches.rs | 5 +- .../rust-analyzer/crates/ide/src/references.rs | 25 +- .../rust-analyzer/crates/ide/src/runnables.rs | 267 +++++++++++++++++- .../crates/ide/src/shuffle_crate_graph.rs | 2 +- .../rust-analyzer/crates/ide/src/static_index.rs | 13 +- src/tools/rust-analyzer/crates/ide/src/status.rs | 6 +- .../crates/ide/src/syntax_highlighting.rs | 74 +++-- .../ide/src/syntax_highlighting/highlight.rs | 11 +- .../crates/ide/src/syntax_highlighting/html.rs | 21 +- .../crates/ide/src/syntax_highlighting/inject.rs | 21 +- .../crates/ide/src/syntax_highlighting/tags.rs | 8 +- .../test_data/highlight_assoc_functions.html | 6 +- .../test_data/highlight_doctest.html | 4 +- .../test_data/highlight_general.html | 12 +- .../test_data/highlight_injection.html | 2 +- .../test_data/highlight_lifetimes.html | 4 +- .../test_data/highlight_strings.html | 8 +- .../test_data/highlight_unsafe.html | 8 +- .../crates/ide/src/syntax_highlighting/tests.rs | 30 +- .../rust-analyzer/crates/ide/src/syntax_tree.rs | 2 +- .../crates/ide/src/view_crate_graph.rs | 7 +- src/tools/rust-analyzer/crates/ide/src/view_hir.rs | 16 +- .../rust-analyzer/crates/ide/src/view_item_tree.rs | 2 +- 43 files changed, 1727 insertions(+), 424 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs delete mode 100644 src/tools/rust-analyzer/crates/ide/src/fn_references.rs (limited to 'src/tools/rust-analyzer/crates/ide') diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 0e9771cd2..712459a7e 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -13,12 +13,12 @@ doctest = false cov-mark = "2.0.0-pre.1" crossbeam-channel = "0.5.5" either = "1.7.0" -itertools = "0.10.3" +itertools = "0.10.5" tracing = "0.1.35" oorandom = "11.1.3" -pulldown-cmark-to-cmark = "10.0.1" +pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.1", default-features = false } -url = "2.2.2" +url = "2.3.1" dot = "0.1.4" stdx = { path = "../stdx", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 210c5c7fa..f994c284c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -8,13 +8,15 @@ use ide_db::{ use syntax::{ast::HasName, AstNode, TextRange}; use crate::{ - fn_references::find_all_methods, + annotations::fn_references::find_all_methods, goto_implementation::goto_implementation, references::find_all_refs, runnables::{runnables, Runnable}, NavigationTarget, RunnableKind, }; +mod fn_references; + // Feature: Annotations // // Provides user with annotations above items for looking up references or impl blocks @@ -30,8 +32,8 @@ pub struct Annotation { #[derive(Debug)] pub enum AnnotationKind { Runnable(Runnable), - HasImpls { file_id: FileId, data: Option> }, - HasReferences { file_id: FileId, data: Option> }, + HasImpls { pos: FilePosition, data: Option> }, + HasReferences { pos: FilePosition, data: Option> }, } pub struct AnnotationConfig { @@ -41,6 +43,12 @@ pub struct AnnotationConfig { pub annotate_references: bool, pub annotate_method_references: bool, pub annotate_enum_variant_references: bool, + pub location: AnnotationLocation, +} + +pub enum AnnotationLocation { + AboveName, + AboveWholeItem, } pub(crate) fn annotations( @@ -62,6 +70,16 @@ pub(crate) fn annotations( } } + let mk_ranges = |(range, focus): (_, Option<_>)| { + let cmd_target: TextRange = focus.unwrap_or(range); + let annotation_range = match config.location { + AnnotationLocation::AboveName => cmd_target, + AnnotationLocation::AboveWholeItem => range, + }; + let target_pos = FilePosition { file_id, offset: cmd_target.start() }; + (annotation_range, target_pos) + }; + visit_file_defs(&Semantics::new(db), file_id, &mut |def| { let range = match def { Definition::Const(konst) if config.annotate_references => { @@ -81,9 +99,13 @@ pub(crate) fn annotations( }) .flatten() .for_each(|range| { + let (annotation_range, target_position) = mk_ranges(range); annotations.push(Annotation { - range, - kind: AnnotationKind::HasReferences { file_id, data: None }, + range: annotation_range, + kind: AnnotationKind::HasReferences { + pos: target_position, + data: None, + }, }) }) } @@ -108,15 +130,18 @@ pub(crate) fn annotations( Some(range) => range, None => return, }; - + let (annotation_range, target_pos) = mk_ranges(range); if config.annotate_impls && !matches!(def, Definition::Const(_)) { - annotations - .push(Annotation { range, kind: AnnotationKind::HasImpls { file_id, data: None } }); + annotations.push(Annotation { + range: annotation_range, + kind: AnnotationKind::HasImpls { pos: target_pos, data: None }, + }); } + if config.annotate_references { annotations.push(Annotation { - range, - kind: AnnotationKind::HasReferences { file_id, data: None }, + range: annotation_range, + kind: AnnotationKind::HasReferences { pos: target_pos, data: None }, }); } @@ -124,10 +149,13 @@ pub(crate) fn annotations( db: &RootDatabase, node: InFile, source_file_id: FileId, - ) -> Option { + ) -> Option<(TextRange, Option)> { if let Some(InFile { file_id, value }) = node.original_ast_node(db) { if file_id == source_file_id.into() { - return value.name().map(|it| it.syntax().text_range()); + return Some(( + value.syntax().text_range(), + value.name().map(|name| name.syntax().text_range()), + )); } } None @@ -135,12 +163,13 @@ pub(crate) fn annotations( }); if config.annotate_method_references { - annotations.extend(find_all_methods(db, file_id).into_iter().map( - |FileRange { file_id, range }| Annotation { - range, - kind: AnnotationKind::HasReferences { file_id, data: None }, - }, - )); + annotations.extend(find_all_methods(db, file_id).into_iter().map(|range| { + let (annotation_range, target_range) = mk_ranges(range); + Annotation { + range: annotation_range, + kind: AnnotationKind::HasReferences { pos: target_range, data: None }, + } + })); } annotations @@ -148,18 +177,11 @@ pub(crate) fn annotations( pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation { match annotation.kind { - AnnotationKind::HasImpls { file_id, ref mut data } => { - *data = - goto_implementation(db, FilePosition { file_id, offset: annotation.range.start() }) - .map(|range| range.info); + AnnotationKind::HasImpls { pos, ref mut data } => { + *data = goto_implementation(db, pos).map(|range| range.info); } - AnnotationKind::HasReferences { file_id, ref mut data } => { - *data = find_all_refs( - &Semantics::new(db), - FilePosition { file_id, offset: annotation.range.start() }, - None, - ) - .map(|result| { + AnnotationKind::HasReferences { pos, ref mut data } => { + *data = find_all_refs(&Semantics::new(db), pos, None).map(|result| { result .into_iter() .flat_map(|res| res.references) @@ -188,21 +210,23 @@ mod tests { use crate::{fixture, Annotation, AnnotationConfig}; - fn check(ra_fixture: &str, expect: Expect) { + use super::AnnotationLocation; + + const DEFAULT_CONFIG: AnnotationConfig = AnnotationConfig { + binary_target: true, + annotate_runnables: true, + annotate_impls: true, + annotate_references: true, + annotate_method_references: true, + annotate_enum_variant_references: true, + location: AnnotationLocation::AboveName, + }; + + fn check_with_config(ra_fixture: &str, expect: Expect, config: &AnnotationConfig) { let (analysis, file_id) = fixture::file(ra_fixture); let annotations: Vec = analysis - .annotations( - &AnnotationConfig { - binary_target: true, - annotate_runnables: true, - annotate_impls: true, - annotate_references: true, - annotate_method_references: true, - annotate_enum_variant_references: true, - }, - file_id, - ) + .annotations(config, file_id) .unwrap() .into_iter() .map(|annotation| analysis.resolve_annotation(annotation).unwrap()) @@ -211,6 +235,10 @@ mod tests { expect.assert_debug_eq(&annotations); } + fn check(ra_fixture: &str, expect: Expect) { + check_with_config(ra_fixture, expect, &DEFAULT_CONFIG); + } + #[test] fn const_annotations() { check( @@ -247,9 +275,12 @@ fn main() { Annotation { range: 6..10, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 6, + }, data: Some( [ FileRange { @@ -265,9 +296,12 @@ fn main() { Annotation { range: 30..36, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 30, + }, data: Some( [], ), @@ -276,9 +310,12 @@ fn main() { Annotation { range: 53..57, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 53, + }, data: Some( [], ), @@ -323,9 +360,12 @@ fn main() { Annotation { range: 7..11, kind: HasImpls { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, data: Some( [], ), @@ -334,9 +374,12 @@ fn main() { Annotation { range: 7..11, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, data: Some( [ FileRange { @@ -352,9 +395,12 @@ fn main() { Annotation { range: 17..21, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 17, + }, data: Some( [], ), @@ -403,9 +449,12 @@ fn main() { Annotation { range: 7..11, kind: HasImpls { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, data: Some( [ NavigationTarget { @@ -424,9 +473,12 @@ fn main() { Annotation { range: 7..11, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, data: Some( [ FileRange { @@ -448,9 +500,12 @@ fn main() { Annotation { range: 20..31, kind: HasImpls { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 20, + }, data: Some( [ NavigationTarget { @@ -469,9 +524,12 @@ fn main() { Annotation { range: 20..31, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 20, + }, data: Some( [ FileRange { @@ -487,9 +545,12 @@ fn main() { Annotation { range: 69..73, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 69, + }, data: Some( [], ), @@ -530,9 +591,12 @@ fn main() {} Annotation { range: 3..7, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 3, + }, data: Some( [], ), @@ -581,9 +645,12 @@ fn main() { Annotation { range: 7..11, kind: HasImpls { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, data: Some( [ NavigationTarget { @@ -602,9 +669,12 @@ fn main() { Annotation { range: 7..11, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 7, + }, data: Some( [ FileRange { @@ -626,9 +696,12 @@ fn main() { Annotation { range: 33..44, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 33, + }, data: Some( [ FileRange { @@ -644,9 +717,12 @@ fn main() { Annotation { range: 61..65, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 61, + }, data: Some( [], ), @@ -740,9 +816,12 @@ mod tests { Annotation { range: 3..7, kind: HasReferences { - file_id: FileId( - 0, - ), + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 3, + }, data: Some( [], ), @@ -786,4 +865,48 @@ m!(); "#]], ); } + + #[test] + fn test_annotations_appear_above_whole_item_when_configured_to_do_so() { + check_with_config( + r#" +/// This is a struct named Foo, obviously. +#[derive(Clone)] +struct Foo; +"#, + expect![[r#" + [ + Annotation { + range: 0..71, + kind: HasImpls { + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 67, + }, + data: Some( + [], + ), + }, + }, + Annotation { + range: 0..71, + kind: HasReferences { + pos: FilePosition { + file_id: FileId( + 0, + ), + offset: 67, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], + &AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG }, + ); + } } 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 new file mode 100644 index 000000000..0cadf125f --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs @@ -0,0 +1,103 @@ +//! This module implements a methods and free functions search in the specified file. +//! We have to skip tests, so cannot reuse file_structure module. + +use hir::Semantics; +use ide_assists::utils::test_related_attribute; +use ide_db::RootDatabase; +use syntax::{ast, ast::HasName, AstNode, SyntaxNode, TextRange}; + +use crate::FileId; + +pub(super) fn find_all_methods( + db: &RootDatabase, + file_id: FileId, +) -> Vec<(TextRange, Option)> { + let sema = Semantics::new(db); + let source_file = sema.parse(file_id); + source_file.syntax().descendants().filter_map(|it| method_range(it)).collect() +} + +fn method_range(item: SyntaxNode) -> Option<(TextRange, Option)> { + ast::Fn::cast(item).and_then(|fn_def| { + if test_related_attribute(&fn_def).is_some() { + None + } else { + Some(( + fn_def.syntax().text_range(), + fn_def.name().map(|name| name.syntax().text_range()), + )) + } + }) +} + +#[cfg(test)] +mod tests { + use syntax::TextRange; + + use crate::fixture; + use crate::TextSize; + use std::ops::RangeInclusive; + + #[test] + fn test_find_all_methods() { + let (analysis, pos) = fixture::position( + r#" + fn private_fn() {$0} + + pub fn pub_fn() {} + + pub fn generic_fn(arg: T) {} + "#, + ); + + let refs = super::find_all_methods(&analysis.db, pos.file_id); + check_result(&refs, &[3..=13, 27..=33, 47..=57]); + } + + #[test] + fn test_find_trait_methods() { + let (analysis, pos) = fixture::position( + r#" + trait Foo { + fn bar() {$0} + fn baz() {} + } + "#, + ); + + let refs = super::find_all_methods(&analysis.db, pos.file_id); + check_result(&refs, &[19..=22, 35..=38]); + } + + #[test] + fn test_skip_tests() { + let (analysis, pos) = fixture::position( + r#" + //- /lib.rs + #[test] + fn foo() {$0} + + pub fn pub_fn() {} + + mod tests { + #[test] + fn bar() {} + } + "#, + ); + + let refs = super::find_all_methods(&analysis.db, pos.file_id); + check_result(&refs, &[28..=34]); + } + + fn check_result(refs: &[(TextRange, Option)], expected: &[RangeInclusive]) { + assert_eq!(refs.len(), expected.len()); + + for (i, &(full, focus)) in refs.iter().enumerate() { + let range = &expected[i]; + let item = focus.unwrap_or(full); + assert_eq!(TextSize::from(*range.start()), item.start()); + assert_eq!(TextSize::from(*range.end()), item.end()); + } + } +} 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 a18a6bea9..5a8cda8fb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -7,7 +7,7 @@ use ide_db::{ search::FileReference, FxIndexMap, RootDatabase, }; -use syntax::{ast, AstNode, SyntaxKind::NAME, TextRange}; +use syntax::{ast, AstNode, SyntaxKind::IDENT, TextRange}; use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo, TryToNav}; @@ -79,7 +79,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio let file = sema.parse(file_id); let file = file.syntax(); let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { - NAME => 1, + IDENT => 1, _ => 0, })?; let mut calls = CallLocations::default(); 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 582e9fe7e..d96827326 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -184,10 +184,10 @@ pub(crate) fn resolve_doc_path_for_def( Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns), Definition::Macro(it) => it.resolve_doc_path(db, link, ns), Definition::Field(it) => it.resolve_doc_path(db, link, ns), + Definition::SelfType(it) => it.resolve_doc_path(db, link, ns), Definition::BuiltinAttr(_) | Definition::ToolModule(_) | Definition::BuiltinType(_) - | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) @@ -232,8 +232,13 @@ pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option TextSize::try_from(comment.prefix().len()).ok(), - ast::String(string) => doc_token.parent_ancestors().find_map(ast::Attr::cast) - .filter(|attr| attr.simple_name().as_deref() == Some("doc")).and_then(|_| string.open_quote_text_range().map(|it| it.len())), + ast::String(string) => { + doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?; + if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() { + return None; + } + string.open_quote_text_range().map(|it| it.len()) + }, _ => None, } }).map(|prefix_len| DocCommentToken { prefix_len, doc_token: doc_token.clone() }) 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 efa8551a0..93252339c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -19,7 +19,7 @@ pub struct ExpandedMacro { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Expand macro recursively** +// | VS Code | **rust-analyzer: Expand macro recursively** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] @@ -32,7 +32,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< _ => 0, })?; - // due to how Rust Analyzer works internally, we need to special case derive attributes, + // due to how rust-analyzer works internally, we need to special case derive attributes, // otherwise they might not get found, e.g. here with the cursor at $0 `#[attr]` would expand: // ``` // #[attr] diff --git a/src/tools/rust-analyzer/crates/ide/src/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/fn_references.rs deleted file mode 100644 index 63fb322ce..000000000 --- a/src/tools/rust-analyzer/crates/ide/src/fn_references.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! This module implements a methods and free functions search in the specified file. -//! We have to skip tests, so cannot reuse file_structure module. - -use hir::Semantics; -use ide_assists::utils::test_related_attribute; -use ide_db::RootDatabase; -use syntax::{ast, ast::HasName, AstNode, SyntaxNode}; - -use crate::{FileId, FileRange}; - -pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec { - let sema = Semantics::new(db); - let source_file = sema.parse(file_id); - source_file.syntax().descendants().filter_map(|it| method_range(it, file_id)).collect() -} - -fn method_range(item: SyntaxNode, file_id: FileId) -> Option { - ast::Fn::cast(item).and_then(|fn_def| { - if test_related_attribute(&fn_def).is_some() { - None - } else { - fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) - } - }) -} - -#[cfg(test)] -mod tests { - use crate::fixture; - use crate::{FileRange, TextSize}; - use std::ops::RangeInclusive; - - #[test] - fn test_find_all_methods() { - let (analysis, pos) = fixture::position( - r#" - fn private_fn() {$0} - - pub fn pub_fn() {} - - pub fn generic_fn(arg: T) {} - "#, - ); - - let refs = analysis.find_all_methods(pos.file_id).unwrap(); - check_result(&refs, &[3..=13, 27..=33, 47..=57]); - } - - #[test] - fn test_find_trait_methods() { - let (analysis, pos) = fixture::position( - r#" - trait Foo { - fn bar() {$0} - fn baz() {} - } - "#, - ); - - let refs = analysis.find_all_methods(pos.file_id).unwrap(); - check_result(&refs, &[19..=22, 35..=38]); - } - - #[test] - fn test_skip_tests() { - let (analysis, pos) = fixture::position( - r#" - //- /lib.rs - #[test] - fn foo() {$0} - - pub fn pub_fn() {} - - mod tests { - #[test] - fn bar() {} - } - "#, - ); - - let refs = analysis.find_all_methods(pos.file_id).unwrap(); - check_result(&refs, &[28..=34]); - } - - fn check_result(refs: &[FileRange], expected: &[RangeInclusive]) { - assert_eq!(refs.len(), expected.len()); - - for (i, item) in refs.iter().enumerate() { - let range = &expected[i]; - assert_eq!(TextSize::from(*range.start()), item.range.start()); - assert_eq!(TextSize::from(*range.end()), item.range.end()); - } - } -} 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 d9c97751c..d0be1b3f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, mem::discriminant}; +use std::mem::discriminant; use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav}; use hir::{AsAssocItem, AssocItem, Semantics}; @@ -39,15 +39,23 @@ pub(crate) fn goto_definition( | T![super] | T![crate] | T![Self] - | COMMENT => 2, + | COMMENT => 4, + // index and prefix ops + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + kind if kind.is_keyword() => 2, + T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, })?; if let Some(doc_comment) = token_as_doc_comment(&original_token) { - return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| { - let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(original_token.text_range(), vec![nav])) - }); + return doc_comment.get_definition_with_descend_at( + sema, + position.offset, + |def, _, link_range| { + let nav = def.try_to_nav(db)?; + Some(RangeInfo::new(link_range, vec![nav])) + }, + ); } let navs = sema .descend_into_macros(original_token.clone()) @@ -91,6 +99,14 @@ fn try_lookup_include_path( if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { return None; } + + // Ignore non-built-in macros to account for shadowing + if let Some(it) = sema.resolve_macro_call(¯o_call) { + if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) { + return None; + } + } + 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()?; Some(NavigationTarget { @@ -152,9 +168,6 @@ mod tests { fn check(ra_fixture: &str) { let (analysis, position, expected) = fixture::annotations(ra_fixture); let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; - if navs.is_empty() { - panic!("unresolved reference") - } let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start()); let navs = navs @@ -1344,6 +1357,10 @@ fn f(e: Enum) { check( r#" //- /main.rs + +#[rustc_builtin_macro] +macro_rules! include_str {} + fn main() { let str = include_str!("foo.txt$0"); } @@ -1353,6 +1370,42 @@ fn main() { "#, ); } + + #[test] + fn goto_doc_include_str() { + check( + r#" +//- /main.rs +#[rustc_builtin_macro] +macro_rules! include_str {} + +#[doc = include_str!("docs.md$0")] +struct Item; + +//- /docs.md +// docs +//^file +"#, + ); + } + + #[test] + fn goto_shadow_include() { + check( + r#" +//- /main.rs +macro_rules! include { + ("included.rs") => {} +} + +include!("included.rs$0"); + +//- /included.rs +// empty +"#, + ); + } + #[cfg(test)] mod goto_impl_of_trait_fn { use super::check; @@ -1628,6 +1681,156 @@ macro_rules! foo { } foo!(bar$0); +"#, + ); + } + + #[test] + fn goto_await_poll() { + check( + r#" +//- minicore: future + +struct MyFut; + +impl core::future::Future for MyFut { + type Output = (); + + fn poll( + //^^^^ + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_> + ) -> std::task::Poll + { + () + } +} + +fn f() { + MyFut.await$0; +} +"#, + ); + } + + #[test] + fn goto_await_into_future_poll() { + check( + r#" +//- minicore: future + +struct Futurable; + +impl core::future::IntoFuture for Futurable { + type IntoFuture = MyFut; +} + +struct MyFut; + +impl core::future::Future for MyFut { + type Output = (); + + fn poll( + //^^^^ + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_> + ) -> std::task::Poll + { + () + } +} + +fn f() { + Futurable.await$0; +} +"#, + ); + } + + #[test] + fn goto_try_op() { + check( + r#" +//- minicore: try + +struct Struct; + +impl core::ops::Try for Struct { + fn branch( + //^^^^^^ + self + ) {} +} + +fn f() { + Struct?$0; +} +"#, + ); + } + + #[test] + fn goto_index_op() { + check( + r#" +//- minicore: index + +struct Struct; + +impl core::ops::Index for Struct { + fn index( + //^^^^^ + self + ) {} +} + +fn f() { + Struct[0]$0; +} +"#, + ); + } + + #[test] + fn goto_prefix_op() { + check( + r#" +//- minicore: deref + +struct Struct; + +impl core::ops::Deref for Struct { + fn deref( + //^^^^^ + self + ) {} +} + +fn f() { + $0*Struct; +} +"#, + ); + } + + #[test] + fn goto_bin_op() { + check( + r#" +//- minicore: add + +struct Struct; + +impl core::ops::Add for Struct { + fn add( + //^^^ + self + ) {} +} + +fn f() { + Struct +$0 Struct; +} "#, ); } 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 04b51c839..b3f711b6b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -30,7 +30,7 @@ pub(crate) fn goto_implementation( let original_token = pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind { - IDENT | T![self] => 1, + IDENT | T![self] | INT_NUMBER => 1, _ => 0, })?; let range = original_token.text_range(); 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 f2d7029ea..540a11583 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -333,7 +333,8 @@ fn cover_range(r0: Option, r1: Option) -> Option, token: SyntaxToken) -> FxHashSet { sema.descend_into_macros(token) .into_iter() - .filter_map(|token| IdentClass::classify_token(sema, &token).map(IdentClass::definitions)) + .filter_map(|token| IdentClass::classify_token(sema, &token)) + .map(IdentClass::definitions_no_ops) .flatten() .collect() } @@ -376,6 +377,7 @@ mod tests { match it { ReferenceCategory::Read => "read", ReferenceCategory::Write => "write", + ReferenceCategory::Import => "import", } .to_string() }), @@ -422,12 +424,12 @@ struct Foo; check( r#" use crate$0; - //^^^^^ + //^^^^^ import use self; - //^^^^ + //^^^^ import mod __ { use super; - //^^^^^ + //^^^^^ import } "#, ); @@ -435,7 +437,7 @@ mod __ { r#" //- /main.rs crate:main deps:lib use lib$0; - //^^^ + //^^^ import //- /lib.rs crate:lib "#, ); @@ -449,7 +451,7 @@ use lib$0; mod foo; //- /foo.rs use self$0; - // ^^^^ + // ^^^^ import "#, ); } @@ -1371,6 +1373,22 @@ fn main() { ().func$0(); //^^^^ } +"#, + ); + } + + #[test] + fn test_assoc_type_highlighting() { + check( + r#" +trait Trait { + type Output; + // ^^^^^^ +} +impl Trait for () { + type Output$0 = (); + // ^^^^^^ +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 59c97f2dc..3687b597f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -9,7 +9,7 @@ use either::Either; use hir::{HasSource, Semantics}; use ide_db::{ base_db::FileRange, - defs::{Definition, IdentClass}, + defs::{Definition, IdentClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, FxIndexSet, RootDatabase, @@ -27,6 +27,7 @@ use crate::{ pub struct HoverConfig { pub links_in_hover: bool, pub documentation: Option, + pub keywords: bool, } impl HoverConfig { @@ -101,7 +102,10 @@ pub(crate) fn hover( let offset = range.start(); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { - IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 3, + IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4, + // index and prefix ops + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + kind if kind.is_keyword() => 2, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, @@ -116,6 +120,8 @@ pub(crate) fn hover( } let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + // prefer descending the same token kind in attribute expansions, in normal macros text + // equivalency is more important let descended = if in_attr { [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() } else { @@ -136,6 +142,11 @@ pub(crate) fn hover( .filter_map(|token| { let node = token.parent()?; let class = IdentClass::classify_token(sema, token)?; + if let IdentClass::Operator(OperatorClass::Await(_)) = class { + // It's better for us to fall back to the keyword hover here, + // rendering poll is very confusing + return None; + } Some(class.definitions().into_iter().zip(iter::once(node).cycle())) }) .flatten() @@ -232,10 +243,12 @@ fn hover_type_fallback( token: &SyntaxToken, original_token: &SyntaxToken, ) -> Option> { - let node = token - .parent_ancestors() - .take_while(|it| !ast::Item::can_cast(it.kind())) - .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?; + let node = + token.parent_ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find(|n| { + ast::Expr::can_cast(n.kind()) + || ast::Pat::can_cast(n.kind()) + || ast::Type::can_cast(n.kind()) + })?; let expr_or_pat = match_ast! { match node { 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 6c50a4e6a..d109c0769 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -2,12 +2,13 @@ use std::fmt::Display; use either::Either; -use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HirDisplay, Semantics, TypeInfo}; +use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo}; use ide_db::{ base_db::SourceDatabase, defs::Definition, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, + syntax_helpers::insert_whitespace_into_node, RootDatabase, }; use itertools::Itertools; @@ -230,7 +231,7 @@ pub(super) fn keyword( config: &HoverConfig, token: &SyntaxToken, ) -> Option { - if !token.kind().is_keyword() || !config.documentation.is_some() { + if !token.kind().is_keyword() || !config.documentation.is_some() || !config.keywords { return None; } let parent = token.parent()?; @@ -345,15 +346,38 @@ pub(super) fn definition( Definition::Module(it) => label_and_docs(db, it), Definition::Function(it) => label_and_docs(db, it), Definition::Adt(it) => label_and_docs(db, it), - Definition::Variant(it) => label_and_docs(db, it), + Definition::Variant(it) => label_value_and_docs(db, it, |&it| { + if !it.parent_enum(db).is_data_carrying(db) { + match it.eval(db) { + Ok(x) => Some(format!("{}", x)), + Err(_) => it.value(db).map(|x| format!("{:?}", x)), + } + } else { + None + } + }), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.eval(db); match body { Ok(x) => Some(format!("{}", x)), - Err(_) => it.value(db).map(|x| format!("{}", x)), + Err(_) => { + let source = it.source(db)?; + let mut body = source.value.body()?.syntax().clone(); + if source.file_id.is_macro() { + body = insert_whitespace_into_node::insert_ws_into(body); + } + Some(body.to_string()) + } + } + }), + Definition::Static(it) => label_value_and_docs(db, it, |it| { + let source = it.source(db)?; + let mut body = source.value.body()?.syntax().clone(); + if source.file_id.is_macro() { + body = insert_whitespace_into_node::insert_ws_into(body); } + Some(body.to_string()) }), - Definition::Static(it) => label_value_and_docs(db, it, |it| it.value(db)), Definition::Trait(it) => label_and_docs(db, it), Definition::TypeAlias(it) => label_and_docs(db, it), Definition::BuiltinType(it) => { 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 867d1f54d..eb997e6fe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8,7 +8,11 @@ fn check_hover_no_result(ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap(); @@ -20,7 +24,11 @@ fn check(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -37,7 +45,11 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -54,7 +66,11 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::PlainText) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::PlainText), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -71,7 +87,11 @@ fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() @@ -83,7 +103,11 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, range, ) .unwrap() @@ -95,7 +119,11 @@ fn check_hover_range_no_results(ra_fixture: &str) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, range, ) .unwrap(); @@ -670,6 +698,7 @@ fn hover_enum_variant() { check( r#" enum Option { + Some(T) /// The None variant Non$0e } @@ -3499,6 +3528,112 @@ impl Foo {} ); } +#[test] +fn hover_const_eval_variant() { + // show hex for <10 + check( + r#" +#[repr(u8)] +enum E { + /// This is a doc + A$0 = 1 << 3, +} +"#, + expect![[r#" + *A* + + ```rust + test::E + ``` + + ```rust + A = 8 + ``` + + --- + + This is a doc + "#]], + ); + // show hex for >10 + check( + r#" +#[repr(u8)] +enum E { + /// This is a doc + A$0 = (1 << 3) + (1 << 2), +} +"#, + expect![[r#" + *A* + + ```rust + test::E + ``` + + ```rust + A = 12 (0xC) + ``` + + --- + + This is a doc + "#]], + ); + // enums in const eval + check( + r#" +#[repr(u8)] +enum E { + A = 1, + /// This is a doc + B$0 = E::A as u8 + 1, +} +"#, + expect![[r#" + *B* + + ```rust + test::E + ``` + + ```rust + B = 2 + ``` + + --- + + This is a doc + "#]], + ); + // unspecified variant should increment by one + check( + r#" +#[repr(u8)] +enum E { + A = 4, + /// This is a doc + B$0, +} +"#, + expect![[r#" + *B* + + ```rust + test::E + ``` + + ```rust + B = 5 + ``` + + --- + + This is a doc + "#]], + ); +} + #[test] fn hover_const_eval() { // show hex for <10 @@ -3792,6 +3927,35 @@ fn foo() { --- + This is a doc + "#]], + ); + check( + r#" +enum E { + /// This is a doc + A = 3, +} +fn foo(e: E) { + match e { + E::A$0 => (), + _ => () + } +} +"#, + expect![[r#" + *A* + + ```rust + test::E + ``` + + ```rust + A = 3 + ``` + + --- + This is a doc "#]], ); @@ -5051,3 +5215,95 @@ fn f() { ```"#]], ); } + +#[test] +fn hover_deref() { + check( + r#" +//- minicore: deref + +struct Struct(usize); + +impl core::ops::Deref for Struct { + type Target = usize; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn f() { + $0*Struct(0); +} +"#, + expect![[r#" + *** + + ```rust + test::Struct + ``` + + ```rust + fn deref(&self) -> &Self::Target + ``` + "#]], + ); +} + +#[test] +fn static_const_macro_expanded_body() { + check( + r#" +macro_rules! m { + () => { + pub const V: i8 = { + let e = 123; + f(e) // Prevent const eval from evaluating this constant, we want to print the body's code. + }; + }; +} +m!(); +fn main() { $0V; } +"#, + expect![[r#" + *V* + + ```rust + test + ``` + + ```rust + pub const V: i8 = { + let e = 123; + f(e) + } + ``` + "#]], + ); + check( + r#" +macro_rules! m { + () => { + pub static V: i8 = { + let e = 123; + }; + }; +} +m!(); +fn main() { $0V; } +"#, + expect![[r#" + *V* + + ```rust + test + ``` + + ```rust + pub static V: i8 = { + let e = 123; + } + ``` + "#]], + ); +} 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 5aae669aa..34d8bf67a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -1,3 +1,5 @@ +use std::fmt; + use either::Either; use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo}; use ide_db::{ @@ -69,7 +71,7 @@ pub enum InlayKind { pub struct InlayHint { pub range: TextRange, pub kind: InlayKind, - pub label: String, + pub label: InlayHintLabel, pub tooltip: Option, } @@ -80,6 +82,83 @@ pub enum InlayTooltip { HoverOffset(FileId, TextSize), } +pub struct InlayHintLabel { + pub parts: Vec, +} + +impl InlayHintLabel { + pub fn as_simple_str(&self) -> Option<&str> { + match &*self.parts { + [part] => part.as_simple_str(), + _ => None, + } + } + + pub fn prepend_str(&mut self, s: &str) { + match &mut *self.parts { + [part, ..] if part.as_simple_str().is_some() => part.text = format!("{s}{}", part.text), + _ => self.parts.insert(0, InlayHintLabelPart { text: s.into(), linked_location: None }), + } + } + + pub fn append_str(&mut self, s: &str) { + match &mut *self.parts { + [.., part] if part.as_simple_str().is_some() => part.text.push_str(s), + _ => self.parts.push(InlayHintLabelPart { text: s.into(), linked_location: None }), + } + } +} + +impl From for InlayHintLabel { + fn from(s: String) -> Self { + Self { parts: vec![InlayHintLabelPart { text: s, linked_location: None }] } + } +} + +impl fmt::Display for InlayHintLabel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.parts.iter().map(|part| &part.text).format("")) + } +} + +impl fmt::Debug for InlayHintLabel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(&self.parts).finish() + } +} + +pub struct InlayHintLabelPart { + pub text: String, + /// Source location represented by this label part. The client will use this to fetch the part's + /// hover tooltip, and Ctrl+Clicking the label part will navigate to the definition the location + /// refers to (not necessarily the location itself). + /// When setting this, no tooltip must be set on the containing hint, or VS Code will display + /// them both. + pub linked_location: Option, +} + +impl InlayHintLabelPart { + pub fn as_simple_str(&self) -> Option<&str> { + match self { + Self { text, linked_location: None } => Some(text), + _ => None, + } + } +} + +impl fmt::Debug for InlayHintLabelPart { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.as_simple_str() { + Some(string) => string.fmt(f), + None => f + .debug_struct("InlayHintLabelPart") + .field("text", &self.text) + .field("linked_location", &self.linked_location) + .finish(), + } + } +} + // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -97,12 +176,6 @@ pub enum InlayTooltip { // * elided lifetimes // * compiler inserted reborrows // -// |=== -// | Editor | Action Name -// -// | VS Code | **Rust Analyzer: Toggle inlay hints* -// |=== -// // image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] pub(crate) fn inlay_hints( db: &RootDatabase, @@ -192,10 +265,10 @@ fn closing_brace_hints( ) -> Option<()> { let min_lines = config.closing_brace_hints_min_lines?; - let name = |it: ast::Name| it.syntax().text_range().start(); + let name = |it: ast::Name| it.syntax().text_range(); let mut closing_token; - let (label, name_offset) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) { + let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) { closing_token = item_list.r_curly_token()?; let parent = item_list.syntax().parent()?; @@ -205,11 +278,11 @@ fn closing_brace_hints( let imp = sema.to_def(&imp)?; let ty = imp.self_ty(sema.db); let trait_ = imp.trait_(sema.db); - - (match trait_ { + let hint_text = match trait_ { Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)), None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)), - }, None) + }; + (hint_text, None) }, ast::Trait(tr) => { (format!("trait {}", tr.name()?), tr.name().map(name)) @@ -253,7 +326,7 @@ fn closing_brace_hints( ( format!("{}!", mac.path()?), - mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range().start()), + mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()), ) } else { return None; @@ -278,11 +351,12 @@ fn closing_brace_hints( return None; } + let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBraceHint, - label, - tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)), + label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] }, + tooltip: None, // provided by label part location }); None @@ -311,7 +385,7 @@ fn implicit_static_hints( acc.push(InlayHint { range: t.text_range(), kind: InlayKind::LifetimeHint, - label: "'static".to_owned(), + label: "'static".to_owned().into(), tooltip: Some(InlayTooltip::String("Elided static lifetime".into())), }); } @@ -329,10 +403,10 @@ fn fn_lifetime_fn_hints( return None; } - let mk_lt_hint = |t: SyntaxToken, label| InlayHint { + let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { range: t.text_range(), kind: InlayKind::LifetimeHint, - label, + label: label.into(), tooltip: Some(InlayTooltip::String("Elided lifetime".into())), }; @@ -486,7 +560,8 @@ fn fn_lifetime_fn_hints( "{}{}", allocated_lifetimes.iter().format(", "), if is_empty { "" } else { ", " } - ), + ) + .into(), tooltip: Some(InlayTooltip::String("Elided lifetimes".into())), }); } @@ -535,7 +610,8 @@ fn closure_ret_hints( range: param_list.syntax().text_range(), kind: InlayKind::ClosureReturnTypeHint, label: hint_iterator(sema, &famous_defs, config, &ty) - .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()), + .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()) + .into(), tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())), }); Some(()) @@ -562,7 +638,7 @@ fn reborrow_hints( acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::ImplicitReborrowHint, - label: label.to_string(), + label: label.to_string().into(), tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())), }); Some(()) @@ -620,9 +696,9 @@ fn chaining_hints( acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::ChainingHint, - label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| { - ty.display_truncated(sema.db, config.max_length).to_string() - }), + label: hint_iterator(sema, &famous_defs, config, &ty) + .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()) + .into(), tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())), }); } @@ -674,7 +750,7 @@ fn param_name_hints( InlayHint { range, kind: InlayKind::ParameterHint, - label: param_name, + label: param_name.into(), tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())), } }); @@ -705,7 +781,7 @@ fn binding_mode_hints( acc.push(InlayHint { range, kind: InlayKind::BindingModeHint, - label: r.to_string(), + label: r.to_string().into(), tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), }); }); @@ -720,7 +796,7 @@ fn binding_mode_hints( acc.push(InlayHint { range, kind: InlayKind::BindingModeHint, - label: bm.to_string(), + label: bm.to_string().into(), tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), }); } @@ -772,7 +848,7 @@ fn bind_pat_hints( None => pat.syntax().text_range(), }, kind: InlayKind::TypeHint, - label, + label: label.into(), tooltip: pat .name() .map(|it| it.syntax().text_range()) @@ -1605,6 +1681,74 @@ fn main() { ); } + #[test] + fn iterator_hint_regression_issue_12674() { + // Ensure we don't crash while solving the projection type of iterators. + check_expect( + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: iterators +struct S(T); +impl S { + fn iter(&self) -> Iter<'_, T> { loop {} } +} +struct Iter<'a, T: 'a>(&'a T); +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { loop {} } +} +struct Container<'a> { + elements: S<&'a str>, +} +struct SliceIter<'a, T>(&'a T); +impl<'a, T> Iterator for SliceIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { loop {} } +} + +fn main(a: SliceIter<'_, Container>) { + a + .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v)))) + .map(|e| e); +} + "#, + expect![[r#" + [ + InlayHint { + range: 484..554, + kind: ChainingHint, + label: [ + "impl Iterator>", + ], + tooltip: Some( + HoverRanged( + FileId( + 0, + ), + 484..554, + ), + ), + }, + InlayHint { + range: 484..485, + kind: ChainingHint, + label: [ + "SliceIter", + ], + tooltip: Some( + HoverRanged( + FileId( + 0, + ), + 484..485, + ), + ), + }, + ] + "#]], + ); + } + #[test] fn infer_call_method_return_associated_types_with_generic() { check_types( @@ -1880,7 +2024,14 @@ impl Vec { } impl IntoIterator for Vec { - type Item=T; + type Item = T; + type IntoIter = IntoIter; +} + +struct IntoIter {} + +impl Iterator for IntoIter { + type Item = T; } fn main() { @@ -1910,7 +2061,7 @@ impl Vec { pub struct Box {} trait Display {} -trait Sync {} +auto trait Sync {} fn main() { // The block expression wrapping disables the constructor hint hiding logic @@ -2223,7 +2374,9 @@ fn main() { InlayHint { range: 147..172, kind: ChainingHint, - label: "B", + label: [ + "B", + ], tooltip: Some( HoverRanged( FileId( @@ -2236,7 +2389,9 @@ fn main() { InlayHint { range: 147..154, kind: ChainingHint, - label: "A", + label: [ + "A", + ], tooltip: Some( HoverRanged( FileId( @@ -2294,7 +2449,9 @@ fn main() { InlayHint { range: 143..190, kind: ChainingHint, - label: "C", + label: [ + "C", + ], tooltip: Some( HoverRanged( FileId( @@ -2307,7 +2464,9 @@ fn main() { InlayHint { range: 143..179, kind: ChainingHint, - label: "B", + label: [ + "B", + ], tooltip: Some( HoverRanged( FileId( @@ -2350,7 +2509,9 @@ fn main() { InlayHint { range: 246..283, kind: ChainingHint, - label: "B>", + label: [ + "B>", + ], tooltip: Some( HoverRanged( FileId( @@ -2363,7 +2524,9 @@ fn main() { InlayHint { range: 246..265, kind: ChainingHint, - label: "A>", + label: [ + "A>", + ], tooltip: Some( HoverRanged( FileId( @@ -2408,7 +2571,9 @@ fn main() { InlayHint { range: 174..241, kind: ChainingHint, - label: "impl Iterator", + label: [ + "impl Iterator", + ], tooltip: Some( HoverRanged( FileId( @@ -2421,7 +2586,9 @@ fn main() { InlayHint { range: 174..224, kind: ChainingHint, - label: "impl Iterator", + label: [ + "impl Iterator", + ], tooltip: Some( HoverRanged( FileId( @@ -2434,7 +2601,9 @@ fn main() { InlayHint { range: 174..206, kind: ChainingHint, - label: "impl Iterator", + label: [ + "impl Iterator", + ], tooltip: Some( HoverRanged( FileId( @@ -2447,7 +2616,9 @@ fn main() { InlayHint { range: 174..189, kind: ChainingHint, - label: "&mut MyIter", + label: [ + "&mut MyIter", + ], tooltip: Some( HoverRanged( FileId( @@ -2489,7 +2660,9 @@ fn main() { InlayHint { range: 124..130, kind: TypeHint, - label: "Struct", + label: [ + "Struct", + ], tooltip: Some( HoverRanged( FileId( @@ -2502,7 +2675,9 @@ fn main() { InlayHint { range: 145..185, kind: ChainingHint, - label: "Struct", + label: [ + "Struct", + ], tooltip: Some( HoverRanged( FileId( @@ -2515,7 +2690,9 @@ fn main() { InlayHint { range: 145..168, kind: ChainingHint, - label: "Struct", + label: [ + "Struct", + ], tooltip: Some( HoverRanged( FileId( @@ -2528,7 +2705,9 @@ fn main() { InlayHint { range: 222..228, kind: ParameterHint, - label: "self", + label: [ + "self", + ], tooltip: Some( HoverOffset( FileId( 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 08621adde..edc48e84d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -28,7 +28,7 @@ pub struct JoinLinesConfig { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Join lines** +// | VS Code | **rust-analyzer: Join lines** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[] diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index dd108fa79..416817ca0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -31,7 +31,6 @@ mod highlight_related; mod expand_macro; mod extend_selection; mod file_structure; -mod fn_references; mod folding_ranges; mod goto_declaration; mod goto_definition; @@ -74,7 +73,7 @@ use syntax::SourceFile; use crate::navigation_target::{ToNav, TryToNav}; pub use crate::{ - annotations::{Annotation, AnnotationConfig, AnnotationKind}, + annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, call_hierarchy::CallItem, expand_macro::ExpandedMacro, file_structure::{StructureNode, StructureNodeKind}, @@ -82,12 +81,12 @@ pub use crate::{ highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, inlay_hints::{ - ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, - LifetimeElisionHints, ReborrowHints, + ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, + InlayTooltip, LifetimeElisionHints, ReborrowHints, }, join_lines::JoinLinesConfig, markup::Markup, - moniker::{MonikerKind, MonikerResult, PackageInformation}, + moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation}, move_item::Direction, navigation_target::NavigationTarget, prime_caches::ParallelPrimeCachesProgress, @@ -98,7 +97,7 @@ pub use crate::{ static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData}, syntax_highlighting::{ tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, - HlRange, + HighlightConfig, HlRange, }, }; pub use hir::{Documentation, Semantics}; @@ -236,7 +235,7 @@ impl Analysis { Env::default(), Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None }, + CrateOrigin::CratesIo { repo: None, name: None }, ); change.change_file(file_id, Some(Arc::new(text))); change.set_crate_graph(crate_graph); @@ -429,11 +428,6 @@ impl Analysis { self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) } - /// Finds all methods and free functions for the file. Does not return tests! - pub fn find_all_methods(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| fn_references::find_all_methods(db, file_id)) - } - /// Returns a short text describing element at position. pub fn hover( &self, @@ -488,8 +482,18 @@ impl Analysis { } /// Returns crates this file belongs too. - pub fn crate_for(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| parent_module::crate_for(db, file_id)) + pub fn crates_for(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| parent_module::crates_for(db, file_id)) + } + + /// Returns crates this file belongs too. + pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable> { + self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect()) + } + + /// Returns crates this file *might* belong too. + pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect()) } /// Returns the edition of the given crate. @@ -517,8 +521,12 @@ impl Analysis { } /// Computes syntax highlighting for the given file - pub fn highlight(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) + pub fn highlight( + &self, + highlight_config: HighlightConfig, + file_id: FileId, + ) -> Cancellable> { + self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None)) } /// Computes all ranges to highlight for a given item in a file. @@ -533,9 +541,13 @@ impl Analysis { } /// Computes syntax highlighting for the given file range. - pub fn highlight_range(&self, frange: FileRange) -> Cancellable> { + pub fn highlight_range( + &self, + highlight_config: HighlightConfig, + frange: FileRange, + ) -> Cancellable> { self.with_db(|db| { - syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) + syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range)) }) } 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 da70cecdd..6e8a6d020 100644 --- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs @@ -12,7 +12,7 @@ use syntax::{ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Find matching brace** +// | VS Code | **rust-analyzer: Find matching brace** // |=== // // image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[] diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 6bab9fa1e..852a8fd83 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -13,17 +13,39 @@ use syntax::{AstNode, SyntaxKind::*, T}; use crate::{doc_links::token_as_doc_comment, RangeInfo}; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum MonikerDescriptorKind { + Namespace, + Type, + Term, + Method, + TypeParameter, + Parameter, + Macro, + Meta, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MonikerDescriptor { + pub name: Name, + pub desc: MonikerDescriptorKind, +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MonikerIdentifier { - crate_name: String, - path: Vec, + pub crate_name: String, + pub description: Vec, } impl ToString for MonikerIdentifier { fn to_string(&self) -> String { match self { - MonikerIdentifier { path, crate_name } => { - format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::")) + MonikerIdentifier { description, crate_name } => { + format!( + "{}::{}", + crate_name, + description.iter().map(|x| x.name.to_string()).join("::") + ) } } } @@ -42,6 +64,12 @@ pub struct MonikerResult { pub package_information: PackageInformation, } +impl MonikerResult { + pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option { + def_to_moniker(db, def, from_crate) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PackageInformation { pub name: String, @@ -90,7 +118,7 @@ pub(crate) fn moniker( .descend_into_macros(original_token.clone()) .into_iter() .filter_map(|token| { - IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| { + IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate)) }) }) @@ -105,13 +133,23 @@ pub(crate) fn def_to_moniker( def: Definition, from_crate: Crate, ) -> Option { - if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) { + if matches!( + def, + Definition::GenericParam(_) + | Definition::Label(_) + | Definition::DeriveHelper(_) + | Definition::BuiltinAttr(_) + | Definition::ToolModule(_) + ) { return None; } + let module = def.module(db)?; let krate = module.krate(); - let mut path = vec![]; - path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db))); + let mut description = vec![]; + description.extend(module.path_to_root(db).into_iter().filter_map(|x| { + Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace }) + })); // Handle associated items within a trait if let Some(assoc) = def.as_assoc_item(db) { @@ -120,38 +158,109 @@ pub(crate) fn def_to_moniker( AssocItemContainer::Trait(trait_) => { // Because different traits can have functions with the same name, // we have to include the trait name as part of the moniker for uniqueness. - path.push(trait_.name(db)); + description.push(MonikerDescriptor { + name: trait_.name(db), + desc: MonikerDescriptorKind::Type, + }); } AssocItemContainer::Impl(impl_) => { // Because a struct can implement multiple traits, for implementations // we add both the struct name and the trait name to the path if let Some(adt) = impl_.self_ty(db).as_adt() { - path.push(adt.name(db)); + description.push(MonikerDescriptor { + name: adt.name(db), + desc: MonikerDescriptorKind::Type, + }); } if let Some(trait_) = impl_.trait_(db) { - path.push(trait_.name(db)); + description.push(MonikerDescriptor { + name: trait_.name(db), + desc: MonikerDescriptorKind::Type, + }); } } } } if let Definition::Field(it) = def { - path.push(it.parent_def(db).name(db)); + description.push(MonikerDescriptor { + name: it.parent_def(db).name(db), + desc: MonikerDescriptorKind::Type, + }); } - path.push(def.name(db)?); + let name_desc = match def { + // These are handled by top-level guard (for performance). + Definition::GenericParam(_) + | Definition::Label(_) + | Definition::DeriveHelper(_) + | Definition::BuiltinAttr(_) + | Definition::ToolModule(_) => return None, + + Definition::Local(local) => { + if !local.is_param(db) { + return None; + } + + MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter } + } + Definition::Macro(m) => { + MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro } + } + Definition::Function(f) => { + MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method } + } + Definition::Variant(v) => { + MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type } + } + Definition::Const(c) => { + MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term } + } + Definition::Trait(trait_) => { + MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type } + } + Definition::TypeAlias(ta) => { + MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter } + } + Definition::Module(m) => { + MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace } + } + Definition::BuiltinType(b) => { + MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type } + } + Definition::SelfType(imp) => MonikerDescriptor { + name: imp.self_ty(db).as_adt()?.name(db), + desc: MonikerDescriptorKind::Type, + }, + Definition::Field(it) => { + MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term } + } + Definition::Adt(adt) => { + MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type } + } + Definition::Static(s) => { + MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta } + } + }; + + description.push(name_desc); + Some(MonikerResult { identifier: MonikerIdentifier { crate_name: krate.display_name(db)?.crate_name().to_string(), - path, + description, }, kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import }, package_information: { - let name = krate.display_name(db)?.to_string(); - let (repo, version) = match krate.origin(db) { - CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?), + let (name, repo, version) = match krate.origin(db) { + CrateOrigin::CratesIo { repo, name } => ( + name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()), + repo?, + krate.version(db)?, + ), CrateOrigin::Lang(lang) => ( + krate.display_name(db)?.canonical_name().to_string(), "https://github.com/rust-lang/rust/".to_string(), match lang { LangCrateOrigin::Other => { 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 02e9fb8b5..ffc4bdd7d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -19,8 +19,8 @@ pub enum Direction { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Move item up** -// | VS Code | **Rust Analyzer: Move item down** +// | VS Code | **rust-analyzer: Move item up** +// | VS Code | **rust-analyzer: Move item down** // |=== // // image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[] 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 9b1f48044..506f9452c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -1,6 +1,6 @@ -use hir::Semantics; +use hir::{db::DefDatabase, Semantics}; use ide_db::{ - base_db::{CrateId, FileId, FilePosition}, + base_db::{CrateId, FileId, FileLoader, FilePosition}, RootDatabase, }; use itertools::Itertools; @@ -18,7 +18,7 @@ use crate::NavigationTarget; // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Locate parent module** +// | VS Code | **rust-analyzer: Locate parent module** // |=== // // image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[] @@ -55,9 +55,13 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec Vec { - let sema = Semantics::new(db); - sema.to_module_defs(file_id).map(|module| module.krate().into()).unique().collect() +pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec { + db.relevant_crates(file_id) + .iter() + .copied() + .filter(|&crate_id| db.crate_def_map(crate_id).modules_for_file(file_id).next().is_some()) + .sorted() + .collect() } #[cfg(test)] @@ -147,7 +151,7 @@ $0 mod foo; "#, ); - assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); + assert_eq!(analysis.crates_for(file_id).unwrap().len(), 1); } #[test] @@ -162,6 +166,6 @@ mod baz; mod baz; "#, ); - assert_eq!(analysis.crate_for(file_id).unwrap().len(), 2); + assert_eq!(analysis.crates_for(file_id).unwrap().len(), 2); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs index 296270036..87b3ef380 100644 --- a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs @@ -12,8 +12,9 @@ use ide_db::{ salsa::{Database, ParallelDatabase, Snapshot}, Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt, }, - FxHashSet, FxIndexMap, + FxIndexMap, }; +use stdx::hash::NoHashHashSet; use crate::RootDatabase; @@ -141,7 +142,7 @@ pub(crate) fn parallel_prime_caches( } } -fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet { +fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet { // We're only interested in the workspace crates and the `ImportMap`s of their direct // dependencies, though in practice the latter also compute the `DefMap`s. // We don't prime transitive dependencies because they're generally not visible in diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 1a6beec18..e942413c1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -14,8 +14,9 @@ use ide_db::{ base_db::FileId, defs::{Definition, NameClass, NameRefClass}, search::{ReferenceCategory, SearchScope, UsageSearchResult}, - FxHashMap, RootDatabase, + RootDatabase, }; +use stdx::hash::NoHashHashMap; use syntax::{ algo::find_node_at_offset, ast::{self, HasName}, @@ -29,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { pub declaration: Option, - pub references: FxHashMap)>>, + pub references: NoHashHashMap)>>, } #[derive(Debug, Clone)] @@ -741,7 +742,7 @@ pub struct Foo { expect![[r#" foo Module FileId(0) 0..8 4..7 - FileId(0) 14..17 + FileId(0) 14..17 Import "#]], ); } @@ -759,7 +760,7 @@ use self$0; expect![[r#" foo Module FileId(0) 0..8 4..7 - FileId(1) 4..8 + FileId(1) 4..8 Import "#]], ); } @@ -774,7 +775,7 @@ use self$0; expect![[r#" Module FileId(0) 0..10 - FileId(0) 4..8 + FileId(0) 4..8 Import "#]], ); } @@ -802,7 +803,7 @@ pub(super) struct Foo$0 { expect![[r#" Foo Struct FileId(2) 0..41 18..21 - FileId(1) 20..23 + FileId(1) 20..23 Import FileId(1) 47..50 "#]], ); @@ -965,7 +966,7 @@ fn g() { f(); } expect![[r#" f Function FileId(0) 22..31 25..26 - FileId(1) 11..12 + FileId(1) 11..12 Import FileId(1) 24..25 "#]], ); @@ -1423,9 +1424,9 @@ pub use level1::Foo; expect![[r#" Foo Struct FileId(0) 0..15 11..14 - FileId(1) 16..19 - FileId(2) 16..19 - FileId(3) 16..19 + FileId(1) 16..19 Import + FileId(2) 16..19 Import + FileId(3) 16..19 Import "#]], ); } @@ -1453,7 +1454,7 @@ lib::foo!(); expect![[r#" foo Macro FileId(1) 0..61 29..32 - FileId(0) 46..49 + FileId(0) 46..49 Import FileId(2) 0..3 FileId(3) 5..8 "#]], @@ -1616,7 +1617,7 @@ struct Foo; expect![[r#" derive_identity Derive FileId(2) 1..107 45..60 - FileId(0) 17..31 + FileId(0) 17..31 Import FileId(0) 56..70 "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index bec770ed9..0181c6b8e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -116,7 +116,7 @@ impl Runnable { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Run** +// | VS Code | **rust-analyzer: Run** // |=== // image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[] pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { @@ -202,7 +202,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Peek related tests** +// | VS Code | **rust-analyzer: Peek related tests** // |=== pub(crate) fn related_tests( db: &RootDatabase, @@ -373,11 +373,13 @@ pub(crate) fn runnable_impl( let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.type_arguments().peekable(); let params = if ty_args.peek().is_some() { - format!("<{}>", ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))) + format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db)))) } else { String::new() }; - let test_id = TestId::Path(format!("{}{}", adt_name, params)); + let mut test_id = format!("{}{}", adt_name, params); + test_id.retain(|c| c != ' '); + let test_id = TestId::Path(test_id); Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg }) } @@ -441,10 +443,11 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { format_to!( path, "<{}>", - ty_args.format_with(", ", |ty, cb| cb(&ty.display(db))) + ty_args.format_with(",", |ty, cb| cb(&ty.display(db))) ); } format_to!(path, "::{}", def_name); + path.retain(|c| c != ' '); return Some(path); } } @@ -2067,13 +2070,23 @@ mod tests { $0 struct Foo; +/// ``` +/// ``` impl Foo { /// ```rust /// ```` fn t() {} } + +/// ``` +/// ``` +impl Foo, ()> { + /// ``` + /// ``` + fn t() {} +} "#, - &[DocTest], + &[DocTest, DocTest, DocTest, DocTest], expect![[r#" [ Runnable { @@ -2082,12 +2095,64 @@ impl Foo { file_id: FileId( 0, ), - full_range: 47..85, + full_range: 20..103, + focus_range: 47..56, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "Foo", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 63..101, + name: "t", + }, + kind: DocTest { + test_id: Path( + "Foo::t", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 105..188, + focus_range: 126..146, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "Foo,()>", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 153..186, name: "t", }, kind: DocTest { test_id: Path( - "Foo::t", + "Foo,()>::t", ), }, cfg: None, @@ -2160,4 +2225,190 @@ macro_rules! foo { "#]], ); } + + #[test] + fn test_paths_with_raw_ident() { + check( + r#" +//- /lib.rs +$0 +mod r#mod { + #[test] + fn r#fn() {} + + /// ``` + /// ``` + fn r#for() {} + + /// ``` + /// ``` + struct r#struct(r#type); + + /// ``` + /// ``` + impl r#struct { + /// ``` + /// ``` + fn r#fn() {} + } + + enum r#enum {} + impl r#struct { + /// ``` + /// ``` + fn r#fn() {} + } + + trait r#trait {} + + /// ``` + /// ``` + impl r#trait for r#struct {} +} +"#, + &[TestMod, Test, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..461, + focus_range: 5..10, + name: "r#mod", + kind: Module, + description: "mod r#mod", + }, + kind: TestMod { + path: "r#mod", + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 17..41, + focus_range: 32..36, + name: "r#fn", + kind: Function, + }, + kind: Test { + test_id: Path( + "r#mod::r#fn", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 47..84, + name: "r#for", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#for", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 90..146, + name: "r#struct", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 152..266, + focus_range: 189..205, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "r#struct", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 216..260, + name: "r#fn", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct::r#fn", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 323..367, + name: "r#fn", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct::r#fn", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 401..459, + focus_range: 445..456, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "r#struct", + ), + }, + cfg: None, + }, + ] + "#]], + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs index 15cb89dcc..2d8662764 100644 --- a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs @@ -12,7 +12,7 @@ use ide_db::{ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Shuffle Crate Graph** +// | VS Code | **rust-analyzer: Shuffle Crate Graph** // |=== pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { let crate_graph = db.crate_graph(); 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 d74b64041..27ad1a948 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -130,8 +130,11 @@ impl StaticIndex<'_> { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(x) => Some(x), }); - let hover_config = - HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }; + let hover_config = HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }; let tokens = tokens.filter(|token| { matches!( token.kind(), @@ -204,12 +207,10 @@ impl StaticIndex<'_> { fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { for token in sema.descend_into_macros(token) { - let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions); + let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); - } else { - continue; - }; + } } None } diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index 3191870eb..20810c25b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -1,4 +1,4 @@ -use std::{fmt, iter::FromIterator, sync::Arc}; +use std::{fmt, sync::Arc}; use hir::{ExpandResult, MacroFile}; use ide_db::base_db::{ @@ -29,7 +29,7 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Status** +// | VS Code | **rust-analyzer: Status** // |=== // image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { @@ -45,7 +45,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { if let Some(file_id) = file_id { format_to!(buf, "\nFile info:\n"); - let crates = crate::parent_module::crate_for(db, file_id); + let crates = crate::parent_module::crates_for(db, file_id); if crates.is_empty() { format_to!(buf, "Does not belong to any crate"); } 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 3fb49b45d..50371d620 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -14,7 +14,7 @@ mod html; mod tests; use hir::{Name, Semantics}; -use ide_db::{FxHashMap, RootDatabase}; +use ide_db::{FxHashMap, RootDatabase, SymbolKind}; use syntax::{ ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T, }; @@ -24,7 +24,7 @@ use crate::{ escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter, tags::Highlight, }, - FileId, HlMod, HlTag, + FileId, HlMod, HlOperator, HlPunct, HlTag, }; pub(crate) use html::highlight_as_html; @@ -36,6 +36,26 @@ pub struct HlRange { pub binding_hash: Option, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct HighlightConfig { + /// Whether to highlight strings + pub strings: bool, + /// Whether to highlight punctuation + pub punctuation: bool, + /// Whether to specialize punctuation highlights + pub specialize_punctuation: bool, + /// Whether to highlight operator + pub operator: bool, + /// Whether to specialize operator highlights + pub specialize_operator: bool, + /// Whether to inject highlights into doc comments + pub inject_doc_comment: bool, + /// Whether to highlight the macro call bang + pub macro_bang: bool, + /// Whether to highlight unresolved things be their syntax + pub syntactic_name_ref_highlighting: bool, +} + // Feature: Semantic Syntax Highlighting // // rust-analyzer highlights the code semantically. @@ -155,9 +175,9 @@ pub struct HlRange { // image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[] pub(crate) fn highlight( db: &RootDatabase, + config: HighlightConfig, file_id: FileId, range_to_highlight: Option, - syntactic_name_ref_highlighting: bool, ) -> Vec { let _p = profile::span("highlight"); let sema = Semantics::new(db); @@ -183,26 +203,18 @@ pub(crate) fn highlight( Some(it) => it.krate(), None => return hl.to_vec(), }; - traverse( - &mut hl, - &sema, - file_id, - &root, - krate, - range_to_highlight, - syntactic_name_ref_highlighting, - ); + traverse(&mut hl, &sema, config, file_id, &root, krate, range_to_highlight); hl.to_vec() } fn traverse( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, + config: HighlightConfig, file_id: FileId, root: &SyntaxNode, krate: hir::Crate, range_to_highlight: TextRange, - syntactic_name_ref_highlighting: bool, ) { let is_unlinked = sema.to_module_def(file_id).is_none(); let mut bindings_shadow_count: FxHashMap = FxHashMap::default(); @@ -323,9 +335,11 @@ fn traverse( Enter(it) => it, Leave(NodeOrToken::Token(_)) => continue, Leave(NodeOrToken::Node(node)) => { - // Doc comment highlighting injection, we do this when leaving the node - // so that we overwrite the highlighting of the doc comment itself. - inject::doc_comment(hl, sema, file_id, &node); + if config.inject_doc_comment { + // Doc comment highlighting injection, we do this when leaving the node + // so that we overwrite the highlighting of the doc comment itself. + inject::doc_comment(hl, sema, config, file_id, &node); + } continue; } }; @@ -400,7 +414,8 @@ fn traverse( let string_to_highlight = ast::String::cast(descended_token.clone()); if let Some((string, expanded_string)) = string.zip(string_to_highlight) { if string.is_raw() { - if inject::ra_fixture(hl, sema, &string, &expanded_string).is_some() { + if inject::ra_fixture(hl, sema, config, &string, &expanded_string).is_some() + { continue; } } @@ -421,7 +436,7 @@ fn traverse( sema, krate, &mut bindings_shadow_count, - syntactic_name_ref_highlighting, + config.syntactic_name_ref_highlighting, name_like, ), NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)), @@ -439,6 +454,29 @@ fn traverse( // something unresolvable. FIXME: There should be a way to prevent that continue; } + + // apply config filtering + match &mut highlight.tag { + HlTag::StringLiteral if !config.strings => continue, + // If punctuation is disabled, make the macro bang part of the macro call again. + tag @ HlTag::Punctuation(HlPunct::MacroBang) => { + if !config.macro_bang { + *tag = HlTag::Symbol(SymbolKind::Macro); + } else if !config.specialize_punctuation { + *tag = HlTag::Punctuation(HlPunct::Other); + } + } + HlTag::Punctuation(_) if !config.punctuation => continue, + tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => { + *tag = HlTag::Punctuation(HlPunct::Other); + } + HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue, + tag @ HlTag::Operator(_) if !config.specialize_operator => { + *tag = HlTag::Operator(HlOperator::Other); + } + _ => (), + } + if inside_attribute { highlight |= HlMod::Attribute } 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 9395e914c..e7d0a8be7 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 @@ -87,9 +87,9 @@ fn punctuation( let parent = token.parent(); let parent_kind = parent.as_ref().map_or(EOF, SyntaxNode::kind); match (kind, parent_kind) { - (T![?], _) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow, + (T![?], TRY_EXPR) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow, (T![&], BIN_EXPR) => HlOperator::Bitwise.into(), - (T![&], _) => { + (T![&], REF_EXPR) => { let h = HlTag::Operator(HlOperator::Other).into(); let is_unsafe = parent .and_then(ast::RefExpr::cast) @@ -100,7 +100,9 @@ fn punctuation( h } } - (T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(), + (T![::] | T![->] | T![=>] | T![..] | T![..=] | T![=] | T![@] | T![.], _) => { + HlOperator::Other.into() + } (T![!], MACRO_CALL | MACRO_RULES) => HlPunct::MacroBang.into(), (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(), (T![!], PREFIX_EXPR) => HlOperator::Logical.into(), @@ -129,7 +131,7 @@ fn punctuation( (T![+=] | T![-=] | T![*=] | T![/=] | T![%=], BIN_EXPR) => { Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable } - (T![|] | T![&] | T![!] | T![^] | T![>>] | T![<<], BIN_EXPR) => HlOperator::Bitwise.into(), + (T![|] | T![&] | T![^] | T![>>] | T![<<], BIN_EXPR) => HlOperator::Bitwise.into(), (T![|=] | T![&=] | T![^=] | T![>>=] | T![<<=], BIN_EXPR) => { Highlight::from(HlOperator::Bitwise) | HlMod::Mutable } @@ -137,7 +139,6 @@ fn punctuation( (T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=], BIN_EXPR) => { HlOperator::Comparison.into() } - (_, PREFIX_EXPR | BIN_EXPR | RANGE_EXPR | RANGE_PAT | REST_PAT) => HlOperator::Other.into(), (_, ATTR) => HlTag::AttributeBracket.into(), (kind, _) => match kind { T!['['] | T![']'] => HlPunct::Bracket, 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 9777c014c..e91fd7f12 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 @@ -5,7 +5,10 @@ use oorandom::Rand32; use stdx::format_to; use syntax::AstNode; -use crate::{syntax_highlighting::highlight, FileId, RootDatabase}; +use crate::{ + syntax_highlighting::{highlight, HighlightConfig}, + FileId, RootDatabase, +}; pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { let parse = db.parse(file_id); @@ -20,7 +23,21 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo ) } - let hl_ranges = highlight(db, file_id, None, false); + let hl_ranges = highlight( + db, + HighlightConfig { + strings: true, + punctuation: true, + specialize_punctuation: true, + specialize_operator: true, + operator: true, + inject_doc_comment: true, + macro_bang: true, + syntactic_name_ref_highlighting: false, + }, + file_id, + None, + ); let text = parse.tree().syntax().to_string(); let mut buf = String::new(); buf.push_str(STYLE); 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 f376f9fda..9139528c7 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 @@ -15,13 +15,14 @@ use syntax::{ use crate::{ doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, - syntax_highlighting::{highlights::Highlights, injector::Injector}, + syntax_highlighting::{highlights::Highlights, injector::Injector, HighlightConfig}, Analysis, HlMod, HlRange, HlTag, RootDatabase, }; pub(super) fn ra_fixture( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, + config: HighlightConfig, literal: &ast::String, expanded: &ast::String, ) -> Option<()> { @@ -63,7 +64,13 @@ pub(super) fn ra_fixture( let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text()); - for mut hl_range in analysis.highlight(tmp_file_id).unwrap() { + for mut hl_range in analysis + .highlight( + HighlightConfig { syntactic_name_ref_highlighting: false, ..config }, + tmp_file_id, + ) + .unwrap() + { for range in inj.map_range_up(hl_range.range) { if let Some(range) = literal.map_range_up(range) { hl_range.range = range; @@ -86,6 +93,7 @@ const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; pub(super) fn doc_comment( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, + config: HighlightConfig, src_file_id: FileId, node: &SyntaxNode, ) { @@ -206,7 +214,14 @@ pub(super) fn doc_comment( let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text()); - if let Ok(ranges) = analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)) { + if let Ok(ranges) = analysis.with_db(|db| { + super::highlight( + db, + HighlightConfig { syntactic_name_ref_highlighting: true, ..config }, + tmp_file_id, + None, + ) + }) { for HlRange { range, highlight, binding_hash } in ranges { for range in inj.map_range_up(range) { hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash }); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 5262770f3..3949f1189 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -199,7 +199,7 @@ impl fmt::Display for HlTag { } impl HlMod { - const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[ + const ALL: &'static [HlMod; 19] = &[ HlMod::Associated, HlMod::Async, HlMod::Attribute, @@ -296,7 +296,7 @@ impl Highlight { Highlight { tag, mods: HlMods::default() } } pub fn is_empty(&self) -> bool { - self.tag == HlTag::None && self.mods == HlMods::default() + self.tag == HlTag::None && self.mods.is_empty() } } @@ -330,6 +330,10 @@ impl ops::BitOr for Highlight { } impl HlMods { + pub fn is_empty(&self) -> bool { + self.0 == 0 + } + pub fn contains(self, m: HlMod) -> bool { self.0 & m.mask() == m.mask() } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index e07fd3925..9ed65fbc8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -48,15 +48,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd impl foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } trait t { fn t_is_static() {} - fn t_is_not_static(&self) {} + fn t_is_not_static(&self) {} } impl t for foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } \ No newline at end of file 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 a747b4bc1..18045f1f5 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 @@ -56,7 +56,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd bar: bool, } -/// This is an impl with a code block. +/// This is an impl of [`Foo`] with a code block. /// /// ``` /// fn foo() { @@ -125,7 +125,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index a97802cbb..9f2b1926b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -61,11 +61,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } trait Bar { - fn bar(&self) -> i32; + fn bar(&self) -> i32; } impl Bar for Foo { - fn bar(&self) -> i32 { + fn bar(&self) -> i32 { self.x } } @@ -75,11 +75,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> i32 { + fn quop(&self) -> i32 { self.x } } @@ -96,11 +96,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> u32 { + fn quop(&self) -> u32 { self.x } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index ced7d22f0..abcd80c28 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -42,7 +42,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
fn fixture(ra_fixture: &str) {}
+
fn fixture(ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
index 2d85fc8c9..f98e0b1cd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
@@ -45,8 +45,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 

 #[derive()]
 struct Foo<'a, 'b, 'c> where 'a: 'a, 'static: 'static {
-    field: &'a (),
-    field2: &'static (),
+    field: &'a (),
+    field2: &'static (),
 }
 impl<'a> Foo<'_, 'a, 'static>
 where
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index c627bc9b0..a626cda3f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -62,16 +62,16 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         () => (
             $crate::panicking::panic("explicit panic")
         ),
-        ($msg:literal $(,)?) => (
+        ($msg:literal $(,)?) => (
             $crate::panicking::panic($msg)
         ),
         // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint.
-        ($msg:expr $(,)?) => (
+        ($msg:expr $(,)?) => (
             $crate::panicking::panic_str($msg)
         ),
         // Special-case the single-argument case for const_panic.
-        ("{}", $arg:expr $(,)?) => (
-            $crate::panicking::panic_display(&$arg)
+        ("{}", $arg:expr $(,)?) => (
+            $crate::panicking::panic_display(&$arg)
         ),
         ($fmt:expr, $($arg:tt)+) => (
             $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 0716bae75..1992bdc6a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -49,7 +49,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 macro_rules! unsafe_deref {
     () => {
-        *(&() as *const ())
+        *(&() as *const ())
     };
 }
 static mut MUT_GLOBAL: Struct = Struct { field: 0 };
@@ -63,7 +63,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 struct Struct { field: i32 }
 impl Struct {
-    unsafe fn unsafe_method(&self) {}
+    unsafe fn unsafe_method(&self) {}
 }
 
 #[repr(packed)]
@@ -78,11 +78,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {}
 
 trait DoTheAutoref {
-    fn calls_autoref(&self);
+    fn calls_autoref(&self);
 }
 
 impl DoTheAutoref for u16 {
-    fn calls_autoref(&self) {}
+    fn calls_autoref(&self) {}
 }
 
 fn main() {
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 99be7c664..46cc667fc 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
@@ -4,7 +4,18 @@ use expect_test::{expect_file, ExpectFile};
 use ide_db::SymbolKind;
 use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
 
-use crate::{fixture, FileRange, HlTag, TextRange};
+use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange};
+
+const HL_CONFIG: HighlightConfig = HighlightConfig {
+    strings: true,
+    punctuation: true,
+    specialize_punctuation: true,
+    specialize_operator: true,
+    operator: true,
+    inject_doc_comment: true,
+    macro_bang: true,
+    syntactic_name_ref_highlighting: false,
+};
 
 #[test]
 fn attributes() {
@@ -613,7 +624,7 @@ struct Foo {
     bar: bool,
 }
 
-/// This is an impl with a code block.
+/// This is an impl of [`Foo`] with a code block.
 ///
 /// ```
 /// fn foo() {
@@ -958,7 +969,7 @@ pub struct Struct;
 
 #[test]
 #[cfg_attr(
-    all(unix, not(target_pointer_width = "64")),
+    not(all(unix, target_pointer_width = "64")),
     ignore = "depends on `DefaultHasher` outputs"
 )]
 fn test_rainbow_highlighting() {
@@ -996,7 +1007,10 @@ struct Foo {
 
     // The "x"
     let highlights = &analysis
-        .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
+        .highlight_range(
+            HL_CONFIG,
+            FileRange { file_id, range: TextRange::at(45.into(), 1.into()) },
+        )
         .unwrap();
 
     assert_eq!(&highlights[0].highlight.to_string(), "field.declaration.public");
@@ -1011,7 +1025,7 @@ macro_rules! test {}
 }"#
         .trim(),
     );
-    let _ = analysis.highlight(file_id).unwrap();
+    let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
 }
 
 /// Highlights the code given by the `ra_fixture` argument, renders the
@@ -1035,7 +1049,7 @@ fn benchmark_syntax_highlighting_long_struct() {
     let hash = {
         let _pt = bench("syntax highlighting long struct");
         analysis
-            .highlight(file_id)
+            .highlight(HL_CONFIG, file_id)
             .unwrap()
             .iter()
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@@ -1061,7 +1075,7 @@ fn syntax_highlighting_not_quadratic() {
             let time = Instant::now();
 
             let hash = analysis
-                .highlight(file_id)
+                .highlight(HL_CONFIG, file_id)
                 .unwrap()
                 .iter()
                 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@@ -1086,7 +1100,7 @@ fn benchmark_syntax_highlighting_parser() {
     let hash = {
         let _pt = bench("syntax highlighting parser");
         analysis
-            .highlight(file_id)
+            .highlight(HL_CONFIG, file_id)
             .unwrap()
             .iter()
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
index 9003e7cd3..4256fea0f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
@@ -12,7 +12,7 @@ use syntax::{
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **Rust Analyzer: Show Syntax Tree**
+// | VS Code | **rust-analyzer: Show Syntax Tree**
 // |===
 // image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
 pub(crate) fn syntax_tree(
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 51291a645..17a1e385b 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
@@ -3,8 +3,9 @@ use std::sync::Arc;
 use dot::{Id, LabelText};
 use ide_db::{
     base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
-    FxHashSet, RootDatabase,
+    RootDatabase,
 };
+use stdx::hash::NoHashHashSet;
 
 // Feature: View Crate Graph
 //
@@ -16,7 +17,7 @@ use ide_db::{
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **Rust Analyzer: View Crate Graph**
+// | VS Code | **rust-analyzer: View Crate Graph**
 // |===
 pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result {
     let crate_graph = db.crate_graph();
@@ -41,7 +42,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result,
-    crates_to_render: FxHashSet,
+    crates_to_render: NoHashHashSet,
 }
 
 type Edge<'a> = (CrateId, &'a Dependency);
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 7312afe53..d2bbbf6d2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
@@ -1,4 +1,4 @@
-use hir::{Function, Semantics};
+use hir::{DefWithBody, Semantics};
 use ide_db::base_db::FilePosition;
 use ide_db::RootDatabase;
 use syntax::{algo::find_node_at_offset, ast, AstNode};
@@ -8,7 +8,7 @@ use syntax::{algo::find_node_at_offset, ast, AstNode};
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **Rust Analyzer: View Hir**
+// | VS Code | **rust-analyzer: View Hir**
 // |===
 // image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
 pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
@@ -19,8 +19,12 @@ fn body_hir(db: &RootDatabase, position: FilePosition) -> Option {
     let sema = Semantics::new(db);
     let source_file = sema.parse(position.file_id);
 
-    let function = find_node_at_offset::(source_file.syntax(), position.offset)?;
-
-    let function: Function = sema.to_def(&function)?;
-    Some(function.debug_hir(db))
+    let item = find_node_at_offset::(source_file.syntax(), position.offset)?;
+    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))
 }
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 3dc03085d..9c1f93356 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
@@ -9,7 +9,7 @@ use ide_db::RootDatabase;
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **Rust Analyzer: Debug ItemTree**
+// | VS Code | **rust-analyzer: Debug ItemTree**
 // |===
 pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
     db.file_item_tree(file_id.into()).pretty_print()
-- 
cgit v1.2.3