diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide')
42 files changed, 782 insertions, 252 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index f994c284c..fb79b5dc2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -94,10 +94,9 @@ pub(crate) fn annotations( enum_ .variants(db) .into_iter() - .map(|variant| { + .filter_map(|variant| { variant.source(db).and_then(|node| name_range(db, node, file_id)) }) - .flatten() .for_each(|range| { let (annotation_range, target_position) = mk_ranges(range); annotations.push(Annotation { 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 dd1d0d75c..f834f2ce5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -74,18 +74,20 @@ pub(crate) fn incoming_calls( Some(calls.into_items()) } -pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { +pub(crate) fn outgoing_calls( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option<Vec<CallItem>> { let sema = Semantics::new(db); - let file_id = position.file_id; let file = sema.parse(file_id); let file = file.syntax(); - let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { + let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT => 1, _ => 0, })?; let mut calls = CallLocations::default(); - sema.descend_into_macros(token) + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast)) .filter_map(|item| match item { 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 d240127f3..37a177622 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -16,6 +16,7 @@ use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasA use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, defs::{Definition, NameClass, NameRefClass}, + documentation::{docs_with_rangemap, Documentation, HasDocs}, helpers::pick_best_token, RootDatabase, }; @@ -131,19 +132,19 @@ pub(crate) fn remove_links(markdown: &str) -> String { // |=== pub(crate) fn external_docs( db: &RootDatabase, - position: &FilePosition, + FilePosition { file_id, offset }: FilePosition, target_dir: Option<&OsStr>, sysroot: Option<&OsStr>, ) -> Option<DocumentationLinks> { let sema = &Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { + let file = sema.parse(file_id).syntax().clone(); + let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 3, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, })?; - let token = sema.descend_into_macros_single(token); + let token = sema.descend_into_macros_single(token, offset); let node = token.parent()?; let definition = match_ast! { @@ -171,7 +172,7 @@ pub(crate) fn external_docs( /// Extracts all links from a given markdown text returning the definition text range, link-text /// and the namespace if known. pub(crate) fn extract_definitions_from_docs( - docs: &hir::Documentation, + docs: &Documentation, ) -> Vec<(TextRange, String, Option<hir::Namespace>)> { Parser::new_with_broken_link_callback( docs.as_str(), @@ -285,7 +286,7 @@ impl DocCommentToken { let original_start = doc_token.text_range().start(); let relative_comment_offset = offset - original_start - prefix_len; - sema.descend_into_macros(doc_token).into_iter().find_map(|t| { + sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| { let (node, descended_prefix_len) = match_ast! { match t { ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?), @@ -297,7 +298,7 @@ impl DocCommentToken { let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len; let (attributes, def) = doc_attributes(sema, &node)?; - let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?; + let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?; let (in_expansion_range, link, ns) = extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| { let mapped = doc_mapping.map(range)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 05a64b33b..9ae70ae66 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,10 +1,11 @@ use std::ffi::OsStr; use expect_test::{expect, Expect}; -use hir::{HasAttrs, Semantics}; +use hir::Semantics; use ide_db::{ base_db::{FilePosition, FileRange}, defs::Definition, + documentation::{Documentation, HasDocs}, RootDatabase, }; use itertools::Itertools; @@ -78,7 +79,7 @@ fn check_doc_links(ra_fixture: &str) { fn def_under_cursor( sema: &Semantics<'_, RootDatabase>, position: &FilePosition, -) -> (Definition, hir::Documentation) { +) -> (Definition, Documentation) { let (docs, def) = sema .parse(position.file_id) .syntax() @@ -96,7 +97,7 @@ fn def_under_cursor( fn node_to_def( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, -) -> Option<Option<(Option<hir::Documentation>, Definition)>> { +) -> Option<Option<(Option<Documentation>, Definition)>> { Some(match_ast! { match node { ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))), @@ -518,6 +519,62 @@ fn function(); } #[test] +fn doc_links_field() { + check_doc_links( + r#" +/// [`S::f`] +/// [`S2::f`] +/// [`T::0`] +/// [`U::a`] +/// [`E::A::f`] +/// [`E::B::0`] +struct S$0 { + f: i32, + //^ S::f + //^ S2::f +} +type S2 = S; +struct T(i32); + //^^^ T::0 +union U { + a: i32, + //^ U::a +} +enum E { + A { f: i32 }, + //^ E::A::f + B(i32), + //^^^ E::B::0 +} +"#, + ); +} + +#[test] +fn doc_links_field_via_self() { + check_doc_links( + r#" +/// [`Self::f`] +struct S$0 { + f: i32, + //^ Self::f +} +"#, + ); +} + +#[test] +fn doc_links_tuple_field_via_self() { + check_doc_links( + r#" +/// [`Self::0`] +struct S$0(i32); + //^^^ Self::0 +"#, + ); +} + +#[test] fn rewrite_html_root_url() { check_rewrite( r#" 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 d6bbf2bf7..119a9c7c3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -40,28 +40,33 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< // struct Bar; // ``` - let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| { - let hir_file = sema.hir_file_for(&descended.parent()?); - if !hir_file.is_derive_attr_pseudo_expansion(db) { - return None; - } + let derive = + sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| { + let hir_file = sema.hir_file_for(&descended.parent()?); + if !hir_file.is_derive_attr_pseudo_expansion(db) { + return None; + } - let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); - // up map out of the #[derive] expansion - let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; - let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; - let expansions = sema.expand_derive_macro(&attr)?; - let idx = attr - .token_tree()? - .token_trees_and_tokens() - .filter_map(NodeOrToken::into_token) - .take_while(|it| it != &token) - .filter(|it| it.kind() == T![,]) - .count(); - let expansion = - format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?); - Some(ExpandedMacro { name, expansion }) - }); + let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); + // up map out of the #[derive] expansion + let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; + let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; + let expansions = sema.expand_derive_macro(&attr)?; + let idx = attr + .token_tree()? + .token_trees_and_tokens() + .filter_map(NodeOrToken::into_token) + .take_while(|it| it != &token) + .filter(|it| it.kind() == T![,]) + .count(); + let expansion = format( + db, + SyntaxKind::MACRO_ITEMS, + position.file_id, + expansions.get(idx).cloned()?, + ); + Some(ExpandedMacro { name, expansion }) + }); if derive.is_some() { return derive; diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index f90618222..3d89599c5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -17,8 +17,6 @@ use crate::FileRange; // Extends or shrinks the current selection to the encompassing syntactic construct // (expression, statement, item, module, etc). It works with multiple cursors. // -// This is a standard LSP feature and not a protocol extension. -// // |=== // | Editor | Shortcut // @@ -142,8 +140,10 @@ fn extend_tokens_from_range( // compute original mapped token range let extended = { - let fst_expanded = sema.descend_into_macros_single(first_token.clone()); - let lst_expanded = sema.descend_into_macros_single(last_token.clone()); + let fst_expanded = + sema.descend_into_macros_single(first_token.clone(), original_range.start()); + let lst_expanded = + sema.descend_into_macros_single(last_token.clone(), original_range.end()); let mut lca = algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?; lca = shallowest_node(&lca); @@ -154,13 +154,16 @@ fn extend_tokens_from_range( }; // Compute parent node range - let validate = |token: &SyntaxToken| -> bool { - let expanded = sema.descend_into_macros_single(token.clone()); - let parent = match expanded.parent() { - Some(it) => it, - None => return false, - }; - algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended) + let validate = |offset: TextSize| { + let extended = &extended; + move |token: &SyntaxToken| -> bool { + let expanded = sema.descend_into_macros_single(token.clone(), offset); + let parent = match expanded.parent() { + Some(it) => it, + None => return false, + }; + algo::least_common_ancestor(extended, &parent).as_ref() == Some(extended) + } }; // Find the first and last text range under expanded parent @@ -168,14 +171,14 @@ fn extend_tokens_from_range( let token = token.prev_token()?; skip_trivia_token(token, Direction::Prev) }) - .take_while(validate) + .take_while(validate(original_range.start())) .last()?; let last = successors(Some(last_token), |token| { let token = token.next_token()?; skip_trivia_token(token, Direction::Next) }) - .take_while(validate) + .take_while(validate(original_range.end())) .last()?; let range = first.text_range().cover(last.text_range()); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index c39c696cf..7e0fab426 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -20,16 +20,16 @@ use crate::{ // - fields in patterns will navigate to the field declaration of the struct, union or variant pub(crate) fn goto_declaration( db: &RootDatabase, - position: FilePosition, + position @ FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); + let file = sema.parse(file_id).syntax().clone(); let original_token = file - .token_at_offset(position.offset) + .token_at_offset(offset) .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?; let range = original_token.text_range(); let info: Vec<NavigationTarget> = sema - .descend_into_macros(original_token) + .descend_into_macros(original_token, offset) .iter() .filter_map(|token| { let parent = token.parent()?; 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 21471ab2a..e09b9f391 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -29,45 +29,39 @@ use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; // image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[] pub(crate) fn goto_definition( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = &Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let original_token = - pick_best_token(file.token_at_offset(position.offset), |kind| match kind { - IDENT - | INT_NUMBER - | LIFETIME_IDENT - | T![self] - | T![super] - | T![crate] - | T![Self] - | 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, - })?; + let file = sema.parse(file_id).syntax().clone(); + 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] + | 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, _, link_range| { - let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(link_range, vec![nav])) - }, - ); + return doc_comment.get_definition_with_descend_at(sema, 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()) + .descend_into_macros(original_token.clone(), offset) .into_iter() .filter_map(|token| { let parent = token.parent()?; if let Some(tt) = ast::TokenTree::cast(parent) { - if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id) - { + if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) { return Some(vec![x]); } } 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 37166bdbd..544c6b423 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -22,20 +22,19 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[] pub(crate) fn goto_implementation( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let source_file = sema.parse(file_id); let syntax = source_file.syntax().clone(); - let original_token = - pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind { - IDENT | T![self] | INT_NUMBER => 1, - _ => 0, - })?; + let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { + IDENT | T![self] | INT_NUMBER => 1, + _ => 0, + })?; let range = original_token.text_range(); let navs = - sema.descend_into_macros(original_token) + sema.descend_into_macros(original_token, offset) .into_iter() .filter_map(|token| token.parent().and_then(ast::NameLike::cast)) .filter_map(|node| match &node { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 6048990f7..955923d76 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -16,13 +16,13 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] pub(crate) fn goto_type_definition( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option<RangeInfo<Vec<NavigationTarget>>> { let sema = hir::Semantics::new(db); - let file: ast::SourceFile = sema.parse(position.file_id); + let file: ast::SourceFile = sema.parse(file_id); let token: SyntaxToken = - pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { + pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 2, kind if kind.is_trivia() => 0, _ => 1, @@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition( } }; let range = token.text_range(); - sema.descend_into_macros(token) + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|token| { let ty = sema 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 43e89a334..46a0464e9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -15,6 +15,7 @@ use syntax::{ SyntaxKind::{self, IDENT, INT_NUMBER}, SyntaxNode, SyntaxToken, TextRange, T, }; +use text_edit::TextSize; use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav}; @@ -51,7 +52,7 @@ pub struct HighlightRelatedConfig { pub(crate) fn highlight_related( sema: &Semantics<'_, RootDatabase>, config: HighlightRelatedConfig, - FilePosition { offset, file_id }: FilePosition, + pos @ FilePosition { offset, file_id }: FilePosition, ) -> Option<Vec<HighlightedRange>> { let _p = profile::span("highlight_related"); let syntax = sema.parse(file_id).syntax().clone(); @@ -79,7 +80,7 @@ pub(crate) fn highlight_related( } T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), - _ if config.references => highlight_references(sema, &syntax, token, file_id), + _ if config.references => highlight_references(sema, &syntax, token, pos), _ => None, } } @@ -129,9 +130,9 @@ fn highlight_references( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, token: SyntaxToken, - file_id: FileId, + FilePosition { file_id, offset }: FilePosition, ) -> Option<Vec<HighlightedRange>> { - let defs = find_defs(sema, token.clone()); + let defs = find_defs(sema, token.clone(), offset); let usages = defs .iter() .filter_map(|&d| { @@ -455,8 +456,12 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange } } -fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> { - sema.descend_into_macros(token) +fn find_defs( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, + offset: TextSize, +) -> FxHashSet<Definition> { + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|token| IdentClass::classify_token(sema, &token)) .map(IdentClass::definitions_no_ops) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 40659e6c2..21934b948 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -162,9 +162,9 @@ fn hover_simple( // 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() + [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into() } else { - sema.descend_into_macros_with_same_text(original_token.clone()) + sema.descend_into_macros_with_same_text(original_token.clone(), offset) }; let descended = || descended.iter(); 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 a33a6ee18..f72ce37d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,12 +3,13 @@ use std::fmt::Display; use either::Either; use hir::{ - Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout, - LayoutError, Semantics, TypeInfo, + Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, + Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, defs::Definition, + documentation::{Documentation, HasDocs}, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, syntax_helpers::insert_whitespace_into_node, @@ -470,7 +471,7 @@ pub(super) fn definition( Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? } - Definition::GenericParam(it) => label_and_docs(db, it), + Definition::GenericParam(it) => (it.display(db).to_string(), None), Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))), Definition::ExternCrateDecl(it) => label_and_docs(db, it), // FIXME: We should be able to show more info about these @@ -616,9 +617,9 @@ fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Mark markup(Some(docs.replace('*', "\\*")), desc, None) } -fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>) +fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, { let label = def.display(db).to_string(); let docs = def.docs(db); @@ -631,9 +632,9 @@ fn label_and_layout_info_and_docs<D, E, E2>( config: &HoverConfig, layout_extractor: E, layout_offset_extractor: E2, -) -> (String, Option<hir::Documentation>) +) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, E: Fn(&D) -> Result<Layout, LayoutError>, E2: Fn(&Layout) -> Option<u64>, { @@ -657,9 +658,9 @@ fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>( value_extractor: E, layout_extractor: E2, layout_tag_extractor: E3, -) -> (String, Option<hir::Documentation>) +) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, E: Fn(&D) -> Option<V>, E2: Fn(&D) -> Result<Layout, LayoutError>, E3: Fn(&Layout) -> Option<usize>, @@ -686,9 +687,9 @@ fn label_value_and_docs<D, E, V>( db: &RootDatabase, def: D, value_extractor: E, -) -> (String, Option<hir::Documentation>) +) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, E: Fn(&D) -> Option<V>, V: Display, { 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 ddc71dffa..81d6db564 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1557,6 +1557,49 @@ fn test_hover_function_show_types() { } #[test] +fn test_hover_function_associated_type_params() { + check( + r#" +trait Foo { type Bar; } +impl Foo for i32 { type Bar = i64; } +fn foo(arg: <i32 as Foo>::Bar) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: <i32 as Foo>::Bar) + ``` + "#]], + ); + + check( + r#" +trait Foo<T> { type Bar<U>; } +impl Foo<i64> for i32 { type Bar<U> = i32; } +fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) + ``` + "#]], + ); +} + +#[test] fn test_hover_function_pointer_show_identifiers() { check( r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#, @@ -3292,7 +3335,50 @@ struct S$0T<const C: usize = 1, T = Foo>(T); ``` ```rust - struct ST<const C: usize, T = Foo> + struct ST<const C: usize = 1, T = Foo> + ``` + "#]], + ); +} + +#[test] +fn const_generic_default_value() { + check( + r#" +struct Foo; +struct S$0T<const C: usize = {40 + 2}, T = Foo>(T); +"#, + expect![[r#" + *ST* + + ```rust + test + ``` + + ```rust + struct ST<const C: usize = {const}, T = Foo> + ``` + "#]], + ); +} + +#[test] +fn const_generic_default_value_2() { + check( + r#" +struct Foo; +const VAL = 1; +struct S$0T<const C: usize = VAL, T = Foo>(T); +"#, + expect![[r#" + *ST* + + ```rust + test + ``` + + ```rust + struct ST<const C: usize = VAL, T = Foo> ``` "#]], ); @@ -6469,3 +6555,42 @@ fn test() { "#]], ); } + +#[test] +fn generic_params_disabled_by_cfg() { + check( + r#" +struct S<#[cfg(never)] T>; +fn test() { + let s$0: S = S; +} +"#, + expect![[r#" + *s* + + ```rust + let s: S // size = 0, align = 1 + ``` + "#]], + ); +} + +#[test] +fn format_args_arg() { + check( + r#" +//- minicore: fmt +fn test() { + let foo = 0; + format_args!("{}", foo$0); +} +"#, + expect![[r#" + *foo* + + ```rust + let foo: i32 // size = 4, align = 4 + ``` + "#]], + ); +} 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 292591674..a5d070fe7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -52,6 +52,28 @@ pub struct InlayHintsConfig { pub closure_style: ClosureStyle, pub max_length: Option<usize>, pub closing_brace_hints_min_lines: Option<usize>, + pub fields_to_resolve: InlayFieldsToResolve, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct InlayFieldsToResolve { + pub resolve_text_edits: bool, + pub resolve_hint_tooltip: bool, + pub resolve_label_tooltip: bool, + pub resolve_label_location: bool, + pub resolve_label_command: bool, +} + +impl InlayFieldsToResolve { + pub const fn empty() -> Self { + Self { + resolve_text_edits: false, + resolve_hint_tooltip: false, + resolve_label_tooltip: false, + resolve_label_location: false, + resolve_label_command: false, + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -123,11 +145,13 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option<TextEdit>, + pub needs_resolve: bool, } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { + needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -139,6 +163,7 @@ impl InlayHint { } fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { + needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -196,6 +221,10 @@ impl InlayHintLabel { }), } } + + pub fn needs_resolve(&self) -> bool { + self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + } } impl From<String> for InlayHintLabel { @@ -529,6 +558,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { #[cfg(test)] mod tests { + use expect_test::Expect; use hir::ClosureStyle; use itertools::Itertools; @@ -538,7 +568,7 @@ mod tests { use crate::DiscriminantHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; - use super::ClosureReturnTypeHints; + use super::{ClosureReturnTypeHints, InlayFieldsToResolve}; pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { discriminant_hints: DiscriminantHints::Never, @@ -559,6 +589,7 @@ mod tests { param_names_for_lifetime_elision_hints: false, max_length: None, closing_brace_hints_min_lines: None, + fields_to_resolve: InlayFieldsToResolve::empty(), }; pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { type_hints: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 6d6bd315e..631807d99 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -137,21 +137,23 @@ pub(super) fn hints( } _ => continue, }; + let label = InlayHintLabel::simple( + if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, + Some(InlayTooltip::Markdown(format!( + "`{}` → `{}` ({coercion} coercion)", + source.display(sema.db), + target.display(sema.db), + ))), + None, + ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before }, kind: InlayKind::Adjustment, - label: InlayHintLabel::simple( - if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, - Some(InlayTooltip::Markdown(format!( - "`{}` → `{}` ({coercion} coercion)", - source.display(sema.db), - target.display(sema.db), - ))), - None, - ), + label, text_edit: None, }); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 07b9f9cc1..680035c72 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,6 +99,7 @@ pub(super) fn hints( None => pat.syntax().text_range(), }; acc.push(InlayHint { + needs_resolve: label.needs_resolve() || text_edit.is_some(), range: match type_ascriptable { Some(Some(t)) => text_range.cover(t.text_range()), _ => text_range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index 343cf17e5..35504ffa7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -50,9 +50,10 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { + needs_resolve: false, range, kind: InlayKind::BindingMode, - label: r.to_string().into(), + label: r.into(), text_edit: None, position: InlayHintPosition::Before, pad_left: false, @@ -68,9 +69,10 @@ pub(super) fn hints( hir::BindingMode::Ref(Mutability::Shared) => "ref", }; acc.push(InlayHint { + needs_resolve: false, range: pat.syntax().text_range(), kind: InlayKind::BindingMode, - label: bm.to_string().into(), + label: bm.into(), text_edit: None, position: InlayHintPosition::Before, pad_left: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index b621a8dda..12e46c0f8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -57,10 +57,12 @@ pub(super) fn hints( } } } + let label = label_of_ty(famous_defs, config, &ty)?; acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), kind: InlayKind::Chaining, - label: label_of_ty(famous_defs, config, &ty)?, + label, text_edit: None, position: InlayHintPosition::After, pad_left: true, @@ -128,6 +130,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 147..154, @@ -152,6 +155,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -221,6 +225,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 143..179, @@ -245,6 +250,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -298,6 +304,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 143..179, @@ -322,6 +329,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -389,6 +397,7 @@ fn main() { "<i32, bool>>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 246..265, @@ -426,6 +435,7 @@ fn main() { "<i32, bool>>", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -474,7 +484,7 @@ fn main() { file_id: FileId( 1, ), - range: 9289..9297, + range: 10739..10747, }, ), tooltip: "", @@ -487,7 +497,7 @@ fn main() { file_id: FileId( 1, ), - range: 9321..9325, + range: 10771..10775, }, ), tooltip: "", @@ -495,6 +505,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..224, @@ -511,7 +522,7 @@ fn main() { file_id: FileId( 1, ), - range: 9289..9297, + range: 10739..10747, }, ), tooltip: "", @@ -524,7 +535,7 @@ fn main() { file_id: FileId( 1, ), - range: 9321..9325, + range: 10771..10775, }, ), tooltip: "", @@ -532,6 +543,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..206, @@ -548,7 +560,7 @@ fn main() { file_id: FileId( 1, ), - range: 9289..9297, + range: 10739..10747, }, ), tooltip: "", @@ -561,7 +573,7 @@ fn main() { file_id: FileId( 1, ), - range: 9321..9325, + range: 10771..10775, }, ), tooltip: "", @@ -569,6 +581,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..189, @@ -593,6 +606,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -655,6 +669,7 @@ fn main() { ], }, ), + needs_resolve: true, }, InlayHint { range: 145..185, @@ -679,6 +694,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 145..168, @@ -703,6 +719,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 222..228, @@ -725,6 +742,7 @@ fn main() { }, ], text_edit: None, + needs_resolve: true, }, ] "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 2cefd5acd..2b68538c1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,6 +109,7 @@ pub(super) fn hints( let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { + needs_resolve: linked_location.is_some(), range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 9d5defcbb..d691303c1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -31,9 +31,10 @@ pub(super) fn hints( let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = TextRange::new(range.end() - TextSize::from(1), range.end()); acc.push(InlayHint { + needs_resolve: false, range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple("move", None, None), + label: InlayHintLabel::from("move"), text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -43,6 +44,7 @@ pub(super) fn hints( } }; acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -59,23 +61,25 @@ pub(super) fn hints( // force cache the source file, otherwise sema lookup will potentially panic _ = sema.parse_or_expand(source.file()); + let label = InlayHintLabel::simple( + format!( + "{}{}", + match capture.kind() { + hir::CaptureKind::SharedRef => "&", + hir::CaptureKind::UniqueSharedRef => "&unique ", + hir::CaptureKind::MutableRef => "&mut ", + hir::CaptureKind::Move => "", + }, + capture.display_place(sema.db) + ), + None, + source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), + ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple( - format!( - "{}{}", - match capture.kind() { - hir::CaptureKind::SharedRef => "&", - hir::CaptureKind::UniqueSharedRef => "&unique ", - hir::CaptureKind::MutableRef => "&mut ", - hir::CaptureKind::Move => "", - }, - capture.display_place(sema.db) - ), - None, - source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), - ), + label, text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -84,9 +88,10 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple(", ", None, None), + label: InlayHintLabel::from(", "), text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -95,6 +100,7 @@ pub(super) fn hints( } } acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 3b41db0f1..204967cd7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,6 +64,7 @@ pub(super) fn hints( }; acc.push(InlayHint { + needs_resolve: label.needs_resolve() || text_edit.is_some(), range: param_list.syntax().text_range(), kind: InlayKind::Type, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index c4d2ac75c..26dc6fa8b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -79,6 +79,7 @@ fn variant_hints( None, ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: match eq_token { Some(t) => range.cover(t.text_range()), _ => range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 5fce11b78..7b05e32ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,6 +22,7 @@ pub(super) fn hints( } let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { + needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), @@ -185,6 +186,7 @@ pub(super) fn hints( let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); acc.push(InlayHint { + needs_resolve: false, range: angle_tok.text_range(), kind: InlayKind::Lifetime, label: format!( @@ -200,6 +202,7 @@ pub(super) fn hints( }); } (None, allocated_lifetimes) => acc.push(InlayHint { + needs_resolve: false, range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index fc297a8d8..f18e6421c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,9 +31,10 @@ pub(super) fn hints( if ty.lifetime().is_none() { let t = ty.amp_token()?; acc.push(InlayHint { + needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, - label: "'static".to_owned().into(), + label: "'static".into(), text_edit: None, position: InlayHintPosition::After, pad_left: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index c4f43f411..b4260d825 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -57,6 +57,7 @@ pub(super) fn hints( let label = InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { + needs_resolve: label.needs_resolve(), range, kind: InlayKind::Parameter, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index bf77d55d5..aee03d218 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,9 +91,9 @@ pub use crate::{ MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, }, inlay_hints::{ - AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, - InlayTooltip, LifetimeElisionHints, + AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -111,7 +111,7 @@ pub use crate::{ HighlightConfig, HlRange, }, }; -pub use hir::{Documentation, Semantics}; +pub use hir::Semantics; pub use ide_assists::{ Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve, }; @@ -124,6 +124,7 @@ pub use ide_db::{ Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, SourceRootId, }, + documentation::Documentation, label::Label, line_index::{LineCol, LineIndex}, search::{ReferenceCategory, SearchScope}, @@ -484,7 +485,7 @@ impl Analysis { sysroot: Option<&OsStr>, ) -> Cancellable<doc_links::DocumentationLinks> { self.with_db(|db| { - doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default() + doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default() }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 17f3771b1..2ca2b5b1d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -99,7 +99,7 @@ pub(crate) fn moniker( }); } let navs = sema - .descend_into_macros(original_token.clone()) + .descend_into_macros(original_token.clone(), offset) .into_iter() .filter_map(|token| { IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index d1479dd1e..32f211c6b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -4,14 +4,15 @@ use std::fmt; use either::Either; use hir::{ - symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource, - HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, + symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, HirDisplay, HirFileId, + InFile, LocalSource, ModuleSource, }; use ide_db::{ base_db::{FileId, FileRange}, - SymbolKind, + defs::Definition, + documentation::{Documentation, HasDocs}, + RootDatabase, SymbolKind, }; -use ide_db::{defs::Definition, RootDatabase}; use stdx::never; use syntax::{ ast::{self, HasName}, @@ -175,8 +176,12 @@ impl TryToNav for FileSymbol { Some(NavigationTarget { file_id: full_range.file_id, - name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() }, - alias: if self.is_alias { Some(self.name.clone()) } else { None }, + name: self + .is_alias + .then(|| self.def.name(db)) + .flatten() + .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), + alias: self.is_alias.then(|| self.name.clone()), kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, focus_range, @@ -323,7 +328,7 @@ impl ToNavFromAst for hir::TraitAlias { impl<D> TryToNav for D where - D: HasSource + ToNavFromAst + Copy + HasAttrs + HirDisplay, + D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay, D::Ast: ast::HasName, { fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 813f9ed94..2d0295692 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>( ) }); token.map(|token| { - sema.descend_into_macros_with_same_text(token) + sema.descend_into_macros_with_same_text(token, offset) .into_iter() .filter_map(|it| ast::NameLike::cast(it.parent()?)) .filter_map(move |name_like| { diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index dae8e71e8..ac9df5ed6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2634,4 +2634,33 @@ use qux as frob; // ", // ); } + + #[test] + fn disallow_renaming_for_non_local_definition() { + check( + "Baz", + r#" +//- /lib.rs crate:lib new_source_root:library +pub struct S; +//- /main.rs crate:main deps:lib new_source_root:local +use lib::S$0; +"#, + "error: Cannot rename a non-local definition.", + ); + } + + #[test] + fn disallow_renaming_for_builtin_macros() { + check( + "Baz", + r#" +//- minicore: derive, hash +//- /main.rs crate:main +use core::hash::Hash; +#[derive(H$0ash)] +struct A; + "#, + "error: Cannot rename a non-local definition.", + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 5f87a7855..2d528c642 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -7,6 +7,7 @@ use ide_assists::utils::test_related_attribute; use ide_db::{ base_db::{FilePosition, FileRange}, defs::Definition, + documentation::docs_from_attrs, helpers::visit_file_defs, search::SearchScope, FxHashMap, FxHashSet, RootDatabase, SymbolKind, @@ -496,7 +497,7 @@ const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"]; fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { - attrs.docs().map_or(false, |doc| { + docs_from_attrs(attrs).map_or(false, |doc| { let mut in_code_block = false; for line in String::from(doc).lines() { diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 7795be54e..e020b52e1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -4,12 +4,11 @@ use std::collections::BTreeSet; use either::Either; -use hir::{ - AssocItem, GenericParam, HasAttrs, HirDisplay, ModuleDef, PathResolution, Semantics, Trait, -}; +use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait}; use ide_db::{ active_parameter::{callable_for_node, generic_def_for_node}, base_db::FilePosition, + documentation::{Documentation, HasDocs}, FxIndexMap, }; use stdx::format_to; @@ -28,7 +27,7 @@ use crate::RootDatabase; /// edited. #[derive(Debug)] pub struct SignatureHelp { - pub doc: Option<String>, + pub doc: Option<Documentation>, pub signature: String, pub active_parameter: Option<usize>, parameters: Vec<TextRange>, @@ -67,17 +66,20 @@ impl SignatureHelp { } /// Computes parameter information for the given position. -pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> { +pub(crate) fn signature_help( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option<SignatureHelp> { let sema = Semantics::new(db); - let file = sema.parse(position.file_id); + let file = sema.parse(file_id); let file = file.syntax(); let token = file - .token_at_offset(position.offset) + .token_at_offset(offset) .left_biased() // if the cursor is sandwiched between two space tokens and the call is unclosed // this prevents us from leaving the CallExpression .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; - let token = sema.descend_into_macros_single(token); + let token = sema.descend_into_macros_single(token, offset); for node in token.parent_ancestors() { match_ast! { @@ -176,7 +178,7 @@ fn signature_help_for_call( let mut fn_params = None; match callable.kind() { hir::CallableKind::Function(func) => { - res.doc = func.docs(db).map(|it| it.into()); + res.doc = func.docs(db); format_to!(res.signature, "fn {}", func.name(db).display(db)); fn_params = Some(match callable.receiver_param(db) { Some(_self) => func.params_without_self(db), @@ -184,11 +186,11 @@ fn signature_help_for_call( }); } hir::CallableKind::TupleStruct(strukt) => { - res.doc = strukt.docs(db).map(|it| it.into()); + res.doc = strukt.docs(db); format_to!(res.signature, "struct {}", strukt.name(db).display(db)); } hir::CallableKind::TupleEnumVariant(variant) => { - res.doc = variant.docs(db).map(|it| it.into()); + res.doc = variant.docs(db); format_to!( res.signature, "enum {}::{}", @@ -202,7 +204,7 @@ fn signature_help_for_call( res.signature.push('('); { if let Some((self_param, _)) = callable.receiver_param(db) { - format_to!(res.signature, "{}", self_param) + format_to!(res.signature, "{}", self_param.display(db)) } let mut buf = String::new(); for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() { @@ -262,38 +264,38 @@ fn signature_help_for_generics( let db = sema.db; match generics_def { hir::GenericDef::Function(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "fn {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Enum(it)) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "enum {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Struct(it)) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "struct {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Union(it)) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "union {}", it.name(db).display(db)); } hir::GenericDef::Trait(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TraitAlias(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TypeAlias(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "type {}", it.name(db).display(db)); } hir::GenericDef::Variant(it) => { // In paths, generics of an enum can be specified *after* one of its variants. // eg. `None::<u8>` // We'll use the signature of the enum, but include the docs of the variant. - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); let enum_ = it.parent_enum(db); format_to!(res.signature, "enum {}", enum_.name(db).display(db)); generics_def = enum_.into(); @@ -1315,6 +1317,25 @@ id! { } #[test] + fn fn_signature_for_method_call_defined_in_macro() { + check( + r#" +macro_rules! id { ($($tt:tt)*) => { $($tt)* } } +struct S; +id! { + impl S { + fn foo<'a>(&'a mut self) {} + } +} +fn test() { S.foo($0); } +"#, + expect![[r#" + fn foo(&'a mut self) + "#]], + ); + } + + #[test] fn call_info_for_lambdas() { check( r#" 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 d8696198d..aabd26da2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -12,6 +12,7 @@ use ide_db::{ }; use syntax::{AstNode, SyntaxKind::*, TextRange, T}; +use crate::inlay_hints::InlayFieldsToResolve; use crate::{ hover::hover_for_definition, inlay_hints::AdjustmentHintsMode, @@ -125,6 +126,7 @@ impl StaticIndex<'_> { max_length: Some(25), closure_capture_hints: false, closing_brace_hints_min_lines: Some(25), + fields_to_resolve: InlayFieldsToResolve::empty(), }, file_id, None, diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index d2c77e2dc..c9ee460a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -66,6 +66,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { None => format!("{}", krate.into_raw()), }; format_to!(buf, "Crate: {}\n", display_crate(krate)); + format_to!(buf, "Enabled cfgs: {:?}\n", crate_graph[krate].cfg_options); let deps = crate_graph[krate] .dependencies .iter() 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 ae9723640..bb01c81d6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -395,10 +395,10 @@ fn traverse( NodeOrToken::Token(token) if token.kind() != COMMENT => { let token = match attr_or_derive_item { Some(AttrOrDerive::Attr(_)) => { - sema.descend_into_macros_with_kind_preference(token) + sema.descend_into_macros_with_kind_preference(token, 0.into()) } Some(AttrOrDerive::Derive(_)) | None => { - sema.descend_into_macros_single(token) + sema.descend_into_macros_single(token, 0.into()) } }; match token.parent().and_then(ast::NameLike::cast) { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 2ed57e201..2ef131594 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -17,6 +17,7 @@ pub(super) fn highlight_format_string( return; } + // FIXME: Replace this with the HIR info we have now. lex_format_specifiers(string, &mut |piece_range, kind| { if let Some(highlight) = highlight_format_specifier(kind) { stack.add(HlRange { 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 8e96bfa01..7d00282fc 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 @@ -617,6 +617,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { CONST => SymbolKind::Const, STATIC => SymbolKind::Static, IDENT_PAT => SymbolKind::Local, + FORMAT_ARGS_ARG => SymbolKind::Local, _ => return default.into(), }; 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 2657a6414..71f4d0724 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 @@ -5,8 +5,8 @@ use std::mem; use either::Either; use hir::{InFile, Semantics}; use ide_db::{ - active_parameter::ActiveParameter, base_db::FileId, defs::Definition, rust_doc::is_rust_fence, - SymbolKind, + active_parameter::ActiveParameter, base_db::FileId, defs::Definition, + documentation::docs_with_rangemap, rust_doc::is_rust_fence, SymbolKind, }; use syntax::{ ast::{self, AstNode, IsString, QuoteOffsets}, @@ -118,7 +118,7 @@ pub(super) fn doc_comment( let src_file_id = src_file_id.into(); // Extract intra-doc links and emit highlights for them. - if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) { + if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { extract_definitions_from_docs(&docs) .into_iter() .filter_map(|(range, link, ns)| { 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 3ac8aa9cc..64e614cec 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 @@ -45,17 +45,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd </style> <pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="brace">{</span> - <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span><span class="parenthesis">)</span> <span class="brace">}</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args</span> <span class="brace">{</span><span class="brace">}</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">const_format_args</span> <span class="brace">{</span><span class="brace">}</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span> <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span> @@ -75,7 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="punctuation">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span> <span class="parenthesis">)</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span> - <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span> + <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="parenthesis">)</span><span class="comma">,</span> <span class="brace">}</span> <span class="brace">}</span> @@ -92,7 +86,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> @@ -114,18 +108,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello, world!"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "The number is 1"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "(3, 4)"</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "1 2"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "0042" with leading zerosV</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1 1 2"</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "{2}"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> @@ -140,10 +134,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span> @@ -167,16 +161,24 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span> <span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="none macro">toho</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more {}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{} asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span> + <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span> + <span class="string_literal macro">"mov {0}, {1}"</span><span class="comma macro">,</span> + <span class="string_literal macro">"add {0}, 5"</span><span class="comma macro">,</span> + <span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span> + <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span> + <span class="parenthesis macro">)</span><span class="semicolon">;</span> + + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre>
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 8749d355c..542d89925 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 @@ -401,19 +401,14 @@ fn test_string_highlighting() { // thus, we have to copy the macro definition from `std` check_highlighting( r#" +//- minicore: fmt macro_rules! println { ($($arg:tt)*) => ({ - $crate::io::_print($crate::format_args_nl!($($arg)*)); + $crate::io::_print(format_args_nl!($($arg)*)); }) } #[rustc_builtin_macro] #[macro_export] -macro_rules! format_args {} -#[rustc_builtin_macro] -#[macro_export] -macro_rules! const_format_args {} -#[rustc_builtin_macro] -#[macro_export] macro_rules! format_args_nl {} mod panic { @@ -433,7 +428,7 @@ mod panic { $crate::panicking::panic_display(&$arg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+)) + $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+)) ), } } @@ -450,7 +445,7 @@ macro_rules! concat {} macro_rules! toho { () => ($crate::panic!("not yet implemented")); - ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+))); + ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); } fn main() { @@ -534,7 +529,15 @@ fn main() { assert!(true, "{}", 1); assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); - asm!("mov eax, {0}"); + let i: u64 = 3; + let o: u64; + asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, + ); + format_args!(concat!("{}"), "{}"); format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); }"#, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index c7e403f6b..b40509715 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -32,7 +32,7 @@ use crate::SourceChange; pub(crate) use on_enter::on_enter; // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. -pub(crate) const TRIGGER_CHARS: &str = ".=<>{"; +pub(crate) const TRIGGER_CHARS: &str = ".=<>{("; struct ExtendedTextEdit { edit: TextEdit, @@ -86,45 +86,57 @@ fn on_char_typed_inner( if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { return None; } - return match char_typed { + let conv = |text_edit: Option<TextEdit>| { + Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) + }; + match char_typed { '.' => conv(on_dot_typed(&file.tree(), offset)), '=' => conv(on_eq_typed(&file.tree(), offset)), '<' => on_left_angle_typed(&file.tree(), offset), '>' => conv(on_right_angle_typed(&file.tree(), offset)), - '{' => conv(on_opening_brace_typed(file, offset)), - _ => return None, - }; - - fn conv(text_edit: Option<TextEdit>) -> Option<ExtendedTextEdit> { - Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) + '{' => conv(on_opening_bracket_typed(file, offset, '{')), + '(' => conv(on_opening_bracket_typed(file, offset, '(')), + _ => None, } } -/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a -/// block, or a part of a `use` item. -fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { - if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { +/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a +/// block, or a part of a `use` item (for `{`). +fn on_opening_bracket_typed( + file: &Parse<SourceFile>, + offset: TextSize, + opening_bracket: char, +) -> Option<TextEdit> { + let (closing_bracket, expected_ast_bracket) = match opening_bracket { + '{' => ('}', SyntaxKind::L_CURLY), + '(' => (')', SyntaxKind::L_PAREN), + _ => return None, + }; + + if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) { return None; } let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; - if brace_token.kind() != SyntaxKind::L_CURLY { + if brace_token.kind() != expected_ast_bracket { return None; } - // Remove the `{` to get a better parse tree, and reparse. + // Remove the opening bracket to get a better parse tree, and reparse. let range = brace_token.text_range(); - if !stdx::always!(range.len() == TextSize::of('{')) { + if !stdx::always!(range.len() == TextSize::of(opening_bracket)) { return None; } let file = file.reparse(&Indel::delete(range)); - if let Some(edit) = brace_expr(&file.tree(), offset) { + if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) { return Some(edit); } - if let Some(edit) = brace_use_path(&file.tree(), offset) { - return Some(edit); + if closing_bracket == '}' { + if let Some(edit) = brace_use_path(&file.tree(), offset) { + return Some(edit); + } } return None; @@ -143,7 +155,12 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< )) } - fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { + fn bracket_expr( + file: &SourceFile, + offset: TextSize, + opening_bracket: char, + closing_bracket: char, + ) -> Option<TextEdit> { let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?; if expr.syntax().text_range().start() != offset { return None; @@ -166,10 +183,10 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< return None; } - // Insert `}` right after the expression. + // Insert the closing bracket right after the expression. Some(TextEdit::insert( - expr.syntax().text_range().end() + TextSize::of("{"), - "}".to_string(), + expr.syntax().text_range().end() + TextSize::of(opening_bracket), + closing_bracket.to_string(), )) } } @@ -938,6 +955,193 @@ use some::pa$0th::to::Item; } #[test] + fn adds_closing_parenthesis_for_expr() { + type_char( + '(', + r#" +fn f() { match () { _ => $0() } } + "#, + r#" +fn f() { match () { _ => (()) } } + "#, + ); + type_char( + '(', + r#" +fn f() { $0() } + "#, + r#" +fn f() { (()) } + "#, + ); + type_char( + '(', + r#" +fn f() { let x = $0(); } + "#, + r#" +fn f() { let x = (()); } + "#, + ); + type_char( + '(', + r#" +fn f() { let x = $0a.b(); } + "#, + r#" +fn f() { let x = (a.b()); } + "#, + ); + type_char( + '(', + r#" +const S: () = $0(); +fn f() {} + "#, + r#" +const S: () = (()); +fn f() {} + "#, + ); + type_char( + '(', + r#" +const S: () = $0a.b(); +fn f() {} + "#, + r#" +const S: () = (a.b()); +fn f() {} + "#, + ); + type_char( + '(', + r#" +fn f() { + match x { + 0 => $0(), + 1 => (), + } +} + "#, + r#" +fn f() { + match x { + 0 => (()), + 1 => (), + } +} + "#, + ); + type_char( + '(', + r#" + fn f() { + let z = Some($03); + } + "#, + r#" + fn f() { + let z = Some((3)); + } + "#, + ); + } + + #[test] + fn parenthesis_noop_in_string_literal() { + // Regression test for #9351 + type_char_noop( + '(', + r##" +fn check_with(ra_fixture: &str, expect: Expect) { + let base = r#" +enum E { T(), R$0, C } +use self::E::X; +const Z: E = E::C; +mod m {} +asdasdasdasdasdasda +sdasdasdasdasdasda +sdasdasdasdasd +"#; + let actual = completion_list(&format!("{}\n{}", base, ra_fixture)); + expect.assert_eq(&actual) +} + "##, + ); + } + + #[test] + fn parenthesis_noop_in_item_position_with_macro() { + type_char_noop('(', r#"$0println!();"#); + type_char_noop( + '(', + r#" +fn main() $0println!("hello"); +}"#, + ); + } + + #[test] + fn parenthesis_noop_in_use_tree() { + type_char_noop( + '(', + r#" +use some::$0Path; + "#, + ); + type_char_noop( + '(', + r#" +use some::{Path, $0Other}; + "#, + ); + type_char_noop( + '(', + r#" +use some::{$0Path, Other}; + "#, + ); + type_char_noop( + '(', + r#" +use some::path::$0to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use some::$0path::to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use $0some::path::to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use some::path::$0to::{Item}; + "#, + ); + type_char_noop( + '(', + r#" +use $0Thing as _; + "#, + ); + + type_char_noop( + '(', + r#" +use some::pa$0th::to::Item; + "#, + ); + } + + #[test] fn adds_closing_angle_bracket_for_generic_args() { type_char( '<', |