aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock67
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs59
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs99
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs12
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs41
-rw-r--r--crates/assists/src/handlers/reorder_impl.rs201
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs35
-rw-r--r--crates/completion/src/completions/attribute.rs21
-rw-r--r--crates/completion/src/completions/dot.rs8
-rw-r--r--crates/completion/src/completions/postfix.rs26
-rw-r--r--crates/completion/src/completions/trait_impl.rs6
-rw-r--r--crates/completion/src/patterns.rs10
-rw-r--r--crates/flycheck/Cargo.toml2
-rw-r--r--crates/hir/src/code_model.rs18
-rw-r--r--crates/hir/src/from_id.rs2
-rw-r--r--crates/hir_def/src/body/lower.rs4
-rw-r--r--crates/hir_def/src/expr.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs8
-rw-r--r--crates/hir_def/src/per_ns.rs2
-rw-r--r--crates/hir_def/src/resolver.rs12
-rw-r--r--crates/hir_def/src/visibility.rs2
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs4
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs4
-rw-r--r--crates/hir_ty/src/display.rs100
-rw-r--r--crates/hir_ty/src/lower.rs6
-rw-r--r--crates/hir_ty/src/tests/display_source_code.rs15
-rw-r--r--crates/hir_ty/src/tests/traits.rs8
-rw-r--r--crates/ide/src/call_hierarchy.rs51
-rw-r--r--crates/ide/src/display/navigation_target.rs14
-rw-r--r--crates/ide/src/doc_links.rs34
-rw-r--r--crates/ide/src/goto_definition.rs93
-rw-r--r--crates/ide/src/hover.rs47
-rw-r--r--crates/ide/src/inlay_hints.rs62
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/references.rs93
-rw-r--r--crates/ide/src/references/rename.rs341
-rw-r--r--crates/ide/src/runnables.rs110
-rw-r--r--crates/ide/src/syntax_highlighting.rs821
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs94
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs530
-rw-r--r--crates/ide/src/syntax_highlighting/highlights.rs92
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs28
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs158
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs240
-rw-r--r--crates/ide/src/syntax_highlighting/injector.rs78
-rw-r--r--crates/ide/src/syntax_highlighting/macro_rules.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs173
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html28
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html71
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html20
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html106
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html88
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html260
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html16
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html20
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs5
-rw-r--r--crates/ide_db/src/defs.rs36
-rw-r--r--crates/ide_db/src/helpers.rs89
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs120
-rw-r--r--crates/ide_db/src/imports_locator.rs2
-rw-r--r--crates/ide_db/src/search.rs112
-rw-r--r--crates/mbe/src/lib.rs4
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs2
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs8
-rw-r--r--crates/mbe/src/syntax_bridge.rs12
-rw-r--r--crates/mbe/src/tests.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs12
-rw-r--r--crates/parser/src/grammar/items.rs26
-rw-r--r--crates/parser/src/grammar/items/traits.rs2
-rw-r--r--crates/parser/src/grammar/items/use_item.rs2
-rw-r--r--crates/parser/src/grammar/patterns.rs2
-rw-r--r--crates/parser/src/grammar/type_args.rs6
-rw-r--r--crates/parser/src/grammar/type_params.rs4
-rw-r--r--crates/proc_macro_api/src/msg.rs2
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs2
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/caps.rs24
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs39
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/global_state.rs3
-rw-r--r--crates/rust-analyzer/src/handlers.rs69
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs10
-rw-r--r--crates/rust-analyzer/src/main_loop.rs82
-rw-r--r--crates/rust-analyzer/src/markdown.rs2
-rw-r--r--crates/rust-analyzer/src/op_queue.rs25
-rw-r--r--crates/rust-analyzer/src/reload.rs69
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs8
-rw-r--r--crates/rust-analyzer/src/to_proto.rs85
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs144
-rw-r--r--crates/ssr/src/search.rs20
-rw-r--r--crates/stdx/src/lib.rs18
-rw-r--r--crates/syntax/src/algo.rs4
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs2
-rw-r--r--crates/syntax/src/parsing/lexer.rs8
-rw-r--r--crates/syntax/src/validation.rs6
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast98
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs2
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast28
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs2
-rw-r--r--crates/test_utils/src/lib.rs2
-rw-r--r--crates/vfs/src/anchored_path.rs10
-rw-r--r--crates/vfs/src/file_set.rs57
-rw-r--r--crates/vfs/src/lib.rs70
-rw-r--r--crates/vfs/src/loader.rs80
-rw-r--r--crates/vfs/src/path_interner.rs14
-rw-r--r--crates/vfs/src/vfs_path.rs94
-rw-r--r--docs/dev/style.md23
-rw-r--r--xtask/src/main.rs12
-rw-r--r--xtask/tests/tidy.rs38
116 files changed, 3695 insertions, 2394 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 57383491a..7b175ec67 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,9 +26,9 @@ dependencies = [
26 26
27[[package]] 27[[package]]
28name = "anyhow" 28name = "anyhow"
29version = "1.0.37" 29version = "1.0.38"
30source = "registry+https://github.com/rust-lang/crates.io-index" 30source = "registry+https://github.com/rust-lang/crates.io-index"
31checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" 31checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
32 32
33[[package]] 33[[package]]
34name = "anymap" 34name = "anymap"
@@ -122,17 +122,28 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
122 122
123[[package]] 123[[package]]
124name = "byteorder" 124name = "byteorder"
125version = "1.3.4" 125version = "1.4.2"
126source = "registry+https://github.com/rust-lang/crates.io-index"
127checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
128
129[[package]]
130name = "cargo-platform"
131version = "0.1.1"
126source = "registry+https://github.com/rust-lang/crates.io-index" 132source = "registry+https://github.com/rust-lang/crates.io-index"
127checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 133checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7"
134dependencies = [
135 "serde",
136]
128 137
129[[package]] 138[[package]]
130name = "cargo_metadata" 139name = "cargo_metadata"
131version = "0.12.0" 140version = "0.12.2"
132source = "registry+https://github.com/rust-lang/crates.io-index" 141source = "registry+https://github.com/rust-lang/crates.io-index"
133checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" 142checksum = "11a47b6286279a9998588ef7050d1ebc2500c69892a557c90fe5d071c64415dc"
134dependencies = [ 143dependencies = [
144 "cargo-platform",
135 "semver", 145 "semver",
146 "semver-parser",
136 "serde", 147 "serde",
137 "serde_json", 148 "serde_json",
138] 149]
@@ -262,9 +273,9 @@ dependencies = [
262 273
263[[package]] 274[[package]]
264name = "const_fn" 275name = "const_fn"
265version = "0.4.4" 276version = "0.4.5"
266source = "registry+https://github.com/rust-lang/crates.io-index" 277source = "registry+https://github.com/rust-lang/crates.io-index"
267checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" 278checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
268 279
269[[package]] 280[[package]]
270name = "crc32fast" 281name = "crc32fast"
@@ -774,9 +785,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
774 785
775[[package]] 786[[package]]
776name = "libc" 787name = "libc"
777version = "0.2.81" 788version = "0.2.82"
778source = "registry+https://github.com/rust-lang/crates.io-index" 789source = "registry+https://github.com/rust-lang/crates.io-index"
779checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 790checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
780 791
781[[package]] 792[[package]]
782name = "libloading" 793name = "libloading"
@@ -808,9 +819,9 @@ dependencies = [
808 819
809[[package]] 820[[package]]
810name = "log" 821name = "log"
811version = "0.4.11" 822version = "0.4.13"
812source = "registry+https://github.com/rust-lang/crates.io-index" 823source = "registry+https://github.com/rust-lang/crates.io-index"
813checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 824checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
814dependencies = [ 825dependencies = [
815 "cfg-if 0.1.10", 826 "cfg-if 0.1.10",
816] 827]
@@ -1134,9 +1145,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
1134 1145
1135[[package]] 1146[[package]]
1136name = "pin-project-lite" 1147name = "pin-project-lite"
1137version = "0.2.0" 1148version = "0.2.4"
1138source = "registry+https://github.com/rust-lang/crates.io-index" 1149source = "registry+https://github.com/rust-lang/crates.io-index"
1139checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" 1150checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
1140 1151
1141[[package]] 1152[[package]]
1142name = "proc-macro2" 1153name = "proc-macro2"
@@ -1275,9 +1286,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1275 1286
1276[[package]] 1287[[package]]
1277name = "regex" 1288name = "regex"
1278version = "1.4.2" 1289version = "1.4.3"
1279source = "registry+https://github.com/rust-lang/crates.io-index" 1290source = "registry+https://github.com/rust-lang/crates.io-index"
1280checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 1291checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
1281dependencies = [ 1292dependencies = [
1282 "regex-syntax", 1293 "regex-syntax",
1283] 1294]
@@ -1294,9 +1305,9 @@ dependencies = [
1294 1305
1295[[package]] 1306[[package]]
1296name = "regex-syntax" 1307name = "regex-syntax"
1297version = "0.6.21" 1308version = "0.6.22"
1298source = "registry+https://github.com/rust-lang/crates.io-index" 1309source = "registry+https://github.com/rust-lang/crates.io-index"
1299checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 1310checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
1300 1311
1301[[package]] 1312[[package]]
1302name = "rowan" 1313name = "rowan"
@@ -1449,9 +1460,9 @@ dependencies = [
1449 1460
1450[[package]] 1461[[package]]
1451name = "semver-parser" 1462name = "semver-parser"
1452version = "0.10.1" 1463version = "0.10.2"
1453source = "registry+https://github.com/rust-lang/crates.io-index" 1464source = "registry+https://github.com/rust-lang/crates.io-index"
1454checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" 1465checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
1455dependencies = [ 1466dependencies = [
1456 "pest", 1467 "pest",
1457] 1468]
@@ -1525,9 +1536,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1525 1536
1526[[package]] 1537[[package]]
1527name = "smallvec" 1538name = "smallvec"
1528version = "1.6.0" 1539version = "1.6.1"
1529source = "registry+https://github.com/rust-lang/crates.io-index" 1540source = "registry+https://github.com/rust-lang/crates.io-index"
1530checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" 1541checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
1531 1542
1532[[package]] 1543[[package]]
1533name = "smol_str" 1544name = "smol_str"
@@ -1558,9 +1569,9 @@ version = "0.0.0"
1558 1569
1559[[package]] 1570[[package]]
1560name = "syn" 1571name = "syn"
1561version = "1.0.57" 1572version = "1.0.58"
1562source = "registry+https://github.com/rust-lang/crates.io-index" 1573source = "registry+https://github.com/rust-lang/crates.io-index"
1563checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 1574checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
1564dependencies = [ 1575dependencies = [
1565 "proc-macro2", 1576 "proc-macro2",
1566 "quote", 1577 "quote",
@@ -1624,9 +1635,9 @@ dependencies = [
1624 1635
1625[[package]] 1636[[package]]
1626name = "text-size" 1637name = "text-size"
1627version = "1.0.0" 1638version = "1.1.0"
1628source = "registry+https://github.com/rust-lang/crates.io-index" 1639source = "registry+https://github.com/rust-lang/crates.io-index"
1629checksum = "f03e7efdedc3bc78cb2337f1e2785c39e45f5ef762d9e4ebb137fff7380a6d8a" 1640checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
1630 1641
1631[[package]] 1642[[package]]
1632name = "text_edit" 1643name = "text_edit"
@@ -1643,9 +1654,9 @@ checksum = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e"
1643 1654
1644[[package]] 1655[[package]]
1645name = "thread_local" 1656name = "thread_local"
1646version = "1.0.1" 1657version = "1.1.0"
1647source = "registry+https://github.com/rust-lang/crates.io-index" 1658source = "registry+https://github.com/rust-lang/crates.io-index"
1648checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1659checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
1649dependencies = [ 1660dependencies = [
1650 "lazy_static", 1661 "lazy_static",
1651] 1662]
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 40028fc01..e3ef04932 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -2,12 +2,16 @@ use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{AsName, Module, ModuleDef, Name, Variant};
5use ide_db::helpers::{ 5use ide_db::{
6 insert_use::{insert_use, ImportScope}, 6 defs::Definition,
7 mod_path_to_ast, 7 helpers::{
8 insert_use::{insert_use, ImportScope},
9 mod_path_to_ast,
10 },
11 search::FileReference,
12 RootDatabase,
8}; 13};
9use ide_db::{defs::Definition, search::Reference, RootDatabase}; 14use rustc_hash::FxHashSet;
10use rustc_hash::{FxHashMap, FxHashSet};
11use syntax::{ 15use syntax::{
12 algo::{find_node_at_offset, SyntaxRewriter}, 16 algo::{find_node_at_offset, SyntaxRewriter},
13 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, 17 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
@@ -58,29 +62,29 @@ pub(crate) fn extract_struct_from_enum_variant(
58 let mut visited_modules_set = FxHashSet::default(); 62 let mut visited_modules_set = FxHashSet::default();
59 let current_module = enum_hir.module(ctx.db()); 63 let current_module = enum_hir.module(ctx.db());
60 visited_modules_set.insert(current_module); 64 visited_modules_set.insert(current_module);
61 let mut rewriters = FxHashMap::default(); 65 let mut def_rewriter = None;
62 for reference in usages { 66 for (file_id, references) in usages {
63 let rewriter = rewriters 67 let mut rewriter = SyntaxRewriter::default();
64 .entry(reference.file_range.file_id) 68 let source_file = ctx.sema.parse(file_id);
65 .or_insert_with(SyntaxRewriter::default); 69 for reference in references {
66 let source_file = ctx.sema.parse(reference.file_range.file_id); 70 update_reference(
67 update_reference( 71 ctx,
68 ctx, 72 &mut rewriter,
69 rewriter, 73 reference,
70 reference, 74 &source_file,
71 &source_file, 75 &enum_module_def,
72 &enum_module_def, 76 &variant_hir_name,
73 &variant_hir_name, 77 &mut visited_modules_set,
74 &mut visited_modules_set, 78 );
75 ); 79 }
76 } 80 if file_id == ctx.frange.file_id {
77 let mut rewriter = 81 def_rewriter = Some(rewriter);
78 rewriters.remove(&ctx.frange.file_id).unwrap_or_else(SyntaxRewriter::default); 82 continue;
79 for (file_id, rewriter) in rewriters { 83 }
80 builder.edit_file(file_id); 84 builder.edit_file(file_id);
81 builder.rewrite(rewriter); 85 builder.rewrite(rewriter);
82 } 86 }
83 builder.edit_file(ctx.frange.file_id); 87 let mut rewriter = def_rewriter.unwrap_or_default();
84 update_variant(&mut rewriter, &variant); 88 update_variant(&mut rewriter, &variant);
85 extract_struct_def( 89 extract_struct_def(
86 &mut rewriter, 90 &mut rewriter,
@@ -90,6 +94,7 @@ pub(crate) fn extract_struct_from_enum_variant(
90 &variant.parent_enum().syntax().clone().into(), 94 &variant.parent_enum().syntax().clone().into(),
91 enum_ast.visibility(), 95 enum_ast.visibility(),
92 ); 96 );
97 builder.edit_file(ctx.frange.file_id);
93 builder.rewrite(rewriter); 98 builder.rewrite(rewriter);
94 }, 99 },
95 ) 100 )
@@ -205,13 +210,13 @@ fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Opti
205fn update_reference( 210fn update_reference(
206 ctx: &AssistContext, 211 ctx: &AssistContext,
207 rewriter: &mut SyntaxRewriter, 212 rewriter: &mut SyntaxRewriter,
208 reference: Reference, 213 reference: FileReference,
209 source_file: &SourceFile, 214 source_file: &SourceFile,
210 enum_module_def: &ModuleDef, 215 enum_module_def: &ModuleDef,
211 variant_hir_name: &Name, 216 variant_hir_name: &Name,
212 visited_modules_set: &mut FxHashSet<Module>, 217 visited_modules_set: &mut FxHashSet<Module>,
213) -> Option<()> { 218) -> Option<()> {
214 let offset = reference.file_range.range.start(); 219 let offset = reference.range.start();
215 let (segment, expr) = if let Some(path_expr) = 220 let (segment, expr) = if let Some(path_expr) =
216 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) 221 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset)
217 { 222 {
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index d559be9cb..dc798daaa 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,7 @@
1use ide_db::{defs::Definition, search::ReferenceKind}; 1use ide_db::{
2 defs::Definition,
3 search::{FileReference, ReferenceKind},
4};
2use syntax::{ 5use syntax::{
3 ast::{self, AstNode, AstToken}, 6 ast::{self, AstNode, AstToken},
4 TextRange, 7 TextRange,
@@ -44,8 +47,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
44 47
45 let def = ctx.sema.to_def(&bind_pat)?; 48 let def = ctx.sema.to_def(&bind_pat)?;
46 let def = Definition::Local(def); 49 let def = Definition::Local(def);
47 let refs = def.usages(&ctx.sema).all(); 50 let usages = def.usages(&ctx.sema).all();
48 if refs.is_empty() { 51 if usages.is_empty() {
49 mark::hit!(test_not_applicable_if_variable_unused); 52 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 53 return None;
51 }; 54 };
@@ -63,48 +66,45 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
63 let_stmt.syntax().text_range() 66 let_stmt.syntax().text_range()
64 }; 67 };
65 68
66 let mut wrap_in_parens = vec![true; refs.len()]; 69 let wrap_in_parens = usages
67 70 .references
68 for (i, desc) in refs.iter().enumerate() { 71 .values()
69 let usage_node = ctx 72 .flatten()
70 .covering_node_for_range(desc.file_range.range) 73 .map(|&FileReference { range, .. }| {
71 .ancestors() 74 let usage_node =
72 .find_map(ast::PathExpr::cast)?; 75 ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?;
73 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); 76 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
74 let usage_parent = match usage_parent_option { 77 let usage_parent = match usage_parent_option {
75 Some(u) => u, 78 Some(u) => u,
76 None => { 79 None => return Ok(false),
77 wrap_in_parens[i] = false; 80 };
78 continue; 81
79 } 82 Ok(!matches!((&initializer_expr, usage_parent),
80 }; 83 (ast::Expr::CallExpr(_), _)
81 84 | (ast::Expr::IndexExpr(_), _)
82 wrap_in_parens[i] = match (&initializer_expr, usage_parent) { 85 | (ast::Expr::MethodCallExpr(_), _)
83 (ast::Expr::CallExpr(_), _) 86 | (ast::Expr::FieldExpr(_), _)
84 | (ast::Expr::IndexExpr(_), _) 87 | (ast::Expr::TryExpr(_), _)
85 | (ast::Expr::MethodCallExpr(_), _) 88 | (ast::Expr::RefExpr(_), _)
86 | (ast::Expr::FieldExpr(_), _) 89 | (ast::Expr::Literal(_), _)
87 | (ast::Expr::TryExpr(_), _) 90 | (ast::Expr::TupleExpr(_), _)
88 | (ast::Expr::RefExpr(_), _) 91 | (ast::Expr::ArrayExpr(_), _)
89 | (ast::Expr::Literal(_), _) 92 | (ast::Expr::ParenExpr(_), _)
90 | (ast::Expr::TupleExpr(_), _) 93 | (ast::Expr::PathExpr(_), _)
91 | (ast::Expr::ArrayExpr(_), _) 94 | (ast::Expr::BlockExpr(_), _)
92 | (ast::Expr::ParenExpr(_), _) 95 | (ast::Expr::EffectExpr(_), _)
93 | (ast::Expr::PathExpr(_), _) 96 | (_, ast::Expr::CallExpr(_))
94 | (ast::Expr::BlockExpr(_), _) 97 | (_, ast::Expr::TupleExpr(_))
95 | (ast::Expr::EffectExpr(_), _) 98 | (_, ast::Expr::ArrayExpr(_))
96 | (_, ast::Expr::CallExpr(_)) 99 | (_, ast::Expr::ParenExpr(_))
97 | (_, ast::Expr::TupleExpr(_)) 100 | (_, ast::Expr::ForExpr(_))
98 | (_, ast::Expr::ArrayExpr(_)) 101 | (_, ast::Expr::WhileExpr(_))
99 | (_, ast::Expr::ParenExpr(_)) 102 | (_, ast::Expr::BreakExpr(_))
100 | (_, ast::Expr::ForExpr(_)) 103 | (_, ast::Expr::ReturnExpr(_))
101 | (_, ast::Expr::WhileExpr(_)) 104 | (_, ast::Expr::MatchExpr(_))
102 | (_, ast::Expr::BreakExpr(_)) 105 ))
103 | (_, ast::Expr::ReturnExpr(_)) 106 })
104 | (_, ast::Expr::MatchExpr(_)) => false, 107 .collect::<Result<Vec<_>, _>>()?;
105 _ => true,
106 };
107 }
108 108
109 let init_str = initializer_expr.syntax().text().to_string(); 109 let init_str = initializer_expr.syntax().text().to_string();
110 let init_in_paren = format!("({})", &init_str); 110 let init_in_paren = format!("({})", &init_str);
@@ -116,15 +116,16 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116 target, 116 target,
117 move |builder| { 117 move |builder| {
118 builder.delete(delete_range); 118 builder.delete(delete_range);
119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 119 for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens)
120 {
120 let replacement = 121 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 122 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 match desc.kind { 123 match reference.kind {
123 ReferenceKind::FieldShorthandForLocal => { 124 ReferenceKind::FieldShorthandForLocal => {
124 mark::hit!(inline_field_shorthand); 125 mark::hit!(inline_field_shorthand);
125 builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) 126 builder.insert(reference.range.end(), format!(": {}", replacement))
126 } 127 }
127 _ => builder.replace(desc.file_range.range, replacement), 128 _ => builder.replace(reference.range, replacement),
128 } 129 }
129 } 130 }
130 }, 131 },
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index 0320c2f12..6114091f2 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T, 3 match_ast, SyntaxElement, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -136,14 +136,14 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -
136 symbol_kind => { 136 symbol_kind => {
137 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); 137 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
138 if symbol_not_in_bracket 138 if symbol_not_in_bracket
139 && symbol_kind != SyntaxKind::COLON // paths 139 && symbol_kind != T![:] // paths
140 && (symbol_kind != SyntaxKind::DOT // field/method access 140 && (symbol_kind != T![.] // field/method access
141 || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations 141 || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations
142 .peek() 142 .peek()
143 .map(|element| element.kind() == SyntaxKind::DOT) 143 .map(|element| element.kind() == T![.])
144 .unwrap_or(false)) 144 .unwrap_or(false))
145 && symbol_kind != SyntaxKind::QUESTION // try operator 145 && symbol_kind != T![?] // try operator
146 && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW) 146 && (symbol_kind.is_punct() || symbol_kind == T![as])
147 { 147 {
148 return true; 148 return true;
149 } 149 }
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 56e8b5229..c961680e2 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -1,8 +1,8 @@
1use ide_db::{defs::Definition, search::Reference}; 1use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
2use syntax::{ 2use syntax::{
3 algo::find_node_at_range, 3 algo::find_node_at_range,
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7use test_utils::mark;
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
@@ -58,32 +58,41 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
58 param.syntax().text_range(), 58 param.syntax().text_range(),
59 |builder| { 59 |builder| {
60 builder.delete(range_to_remove(param.syntax())); 60 builder.delete(range_to_remove(param.syntax()));
61 for usage in fn_def.usages(&ctx.sema).all() { 61 for (file_id, references) in fn_def.usages(&ctx.sema).all() {
62 process_usage(ctx, builder, usage, param_position); 62 process_usages(ctx, builder, file_id, references, param_position);
63 } 63 }
64 }, 64 },
65 ) 65 )
66} 66}
67 67
68fn process_usage( 68fn process_usages(
69 ctx: &AssistContext, 69 ctx: &AssistContext,
70 builder: &mut AssistBuilder, 70 builder: &mut AssistBuilder,
71 usage: Reference, 71 file_id: FileId,
72 references: Vec<FileReference>,
72 arg_to_remove: usize, 73 arg_to_remove: usize,
73) -> Option<()> { 74) {
74 let source_file = ctx.sema.parse(usage.file_range.file_id); 75 let source_file = ctx.sema.parse(file_id);
75 let call_expr: ast::CallExpr = 76 builder.edit_file(file_id);
76 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 77 for usage in references {
78 if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) {
79 builder.delete(text_range);
80 }
81 }
82}
83
84fn process_usage(
85 source_file: &SourceFile,
86 FileReference { range, .. }: FileReference,
87 arg_to_remove: usize,
88) -> Option<TextRange> {
89 let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?;
77 let call_expr_range = call_expr.expr()?.syntax().text_range(); 90 let call_expr_range = call_expr.expr()?.syntax().text_range();
78 if !call_expr_range.contains_range(usage.file_range.range) { 91 if !call_expr_range.contains_range(range) {
79 return None; 92 return None;
80 } 93 }
81 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 94 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
82 95 Some(range_to_remove(arg.syntax()))
83 builder.edit_file(usage.file_range.file_id);
84 builder.delete(range_to_remove(arg.syntax()));
85
86 Some(())
87} 96}
88 97
89fn range_to_remove(node: &SyntaxNode) -> TextRange { 98fn range_to_remove(node: &SyntaxNode) -> TextRange {
diff --git a/crates/assists/src/handlers/reorder_impl.rs b/crates/assists/src/handlers/reorder_impl.rs
new file mode 100644
index 000000000..309f291c8
--- /dev/null
+++ b/crates/assists/src/handlers/reorder_impl.rs
@@ -0,0 +1,201 @@
1use itertools::Itertools;
2use rustc_hash::FxHashMap;
3
4use hir::{PathResolution, Semantics};
5use ide_db::RootDatabase;
6use syntax::{
7 algo,
8 ast::{self, NameOwner},
9 AstNode,
10};
11use test_utils::mark;
12
13use crate::{AssistContext, AssistId, AssistKind, Assists};
14
15// Assist: reorder_impl
16//
17// Reorder the methods of an `impl Trait`. The methods will be ordered
18// in the same order as in the trait definition.
19//
20// ```
21// trait Foo {
22// fn a() {}
23// fn b() {}
24// fn c() {}
25// }
26//
27// struct Bar;
28// $0impl Foo for Bar {
29// fn b() {}
30// fn c() {}
31// fn a() {}
32// }
33// ```
34// ->
35// ```
36// trait Foo {
37// fn a() {}
38// fn b() {}
39// fn c() {}
40// }
41//
42// struct Bar;
43// impl Foo for Bar {
44// fn a() {}
45// fn b() {}
46// fn c() {}
47// }
48// ```
49//
50pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
51 let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
52 let items = impl_ast.assoc_item_list()?;
53 let methods = get_methods(&items);
54
55 let path = impl_ast
56 .trait_()
57 .and_then(|t| match t {
58 ast::Type::PathType(path) => Some(path),
59 _ => None,
60 })?
61 .path()?;
62
63 let ranks = compute_method_ranks(&path, ctx)?;
64 let sorted: Vec<_> = methods
65 .iter()
66 .cloned()
67 .sorted_by_key(|f| {
68 f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
69 })
70 .collect();
71
72 // Don't edit already sorted methods:
73 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted);
75 return None;
76 }
77
78 let target = items.syntax().text_range();
79 acc.add(AssistId("reorder_impl", AssistKind::RefactorRewrite), "Sort methods", target, |edit| {
80 let mut rewriter = algo::SyntaxRewriter::default();
81 for (old, new) in methods.iter().zip(&sorted) {
82 rewriter.replace(old.syntax(), new.syntax());
83 }
84 edit.rewrite(rewriter);
85 })
86}
87
88fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
89 let td = trait_definition(path, &ctx.sema)?;
90
91 Some(
92 td.items(ctx.db())
93 .iter()
94 .flat_map(|i| match i {
95 hir::AssocItem::Function(f) => Some(f),
96 _ => None,
97 })
98 .enumerate()
99 .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx)))
100 .collect(),
101 )
102}
103
104fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> {
105 match sema.resolve_path(path)? {
106 PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
107 _ => None,
108 }
109}
110
111fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
112 items
113 .assoc_items()
114 .flat_map(|i| match i {
115 ast::AssocItem::Fn(f) => Some(f),
116 _ => None,
117 })
118 .filter(|f| f.name().is_some())
119 .collect()
120}
121
122#[cfg(test)]
123mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable};
127
128 use super::*;
129
130 #[test]
131 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable(
134 reorder_impl,
135 r#"
136trait Bar {
137 fn a() {}
138 fn z() {}
139 fn b() {}
140}
141struct Foo;
142$0impl Bar for Foo {
143 fn a() {}
144 fn z() {}
145 fn b() {}
146}
147 "#,
148 )
149 }
150
151 #[test]
152 fn not_applicable_if_empty() {
153 check_assist_not_applicable(
154 reorder_impl,
155 r#"
156trait Bar {};
157struct Foo;
158$0impl Bar for Foo {}
159 "#,
160 )
161 }
162
163 #[test]
164 fn reorder_impl_trait_methods() {
165 check_assist(
166 reorder_impl,
167 r#"
168trait Bar {
169 fn a() {}
170 fn c() {}
171 fn b() {}
172 fn d() {}
173}
174
175struct Foo;
176$0impl Bar for Foo {
177 fn d() {}
178 fn b() {}
179 fn c() {}
180 fn a() {}
181}
182 "#,
183 r#"
184trait Bar {
185 fn a() {}
186 fn c() {}
187 fn b() {}
188 fn d() {}
189}
190
191struct Foo;
192impl Bar for Foo {
193 fn a() {}
194 fn c() {}
195 fn b() {}
196 fn d() {}
197}
198 "#,
199 )
200 }
201}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 90009c55a..1080294ab 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -146,6 +146,7 @@ mod handlers {
146 mod remove_mut; 146 mod remove_mut;
147 mod remove_unused_param; 147 mod remove_unused_param;
148 mod reorder_fields; 148 mod reorder_fields;
149 mod reorder_impl;
149 mod replace_derive_with_manual_impl; 150 mod replace_derive_with_manual_impl;
150 mod replace_if_let_with_match; 151 mod replace_if_let_with_match;
151 mod replace_impl_trait_with_generic; 152 mod replace_impl_trait_with_generic;
@@ -202,6 +203,7 @@ mod handlers {
202 remove_mut::remove_mut, 203 remove_mut::remove_mut,
203 remove_unused_param::remove_unused_param, 204 remove_unused_param::remove_unused_param,
204 reorder_fields::reorder_fields, 205 reorder_fields::reorder_fields,
206 reorder_impl::reorder_impl,
205 replace_derive_with_manual_impl::replace_derive_with_manual_impl, 207 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
206 replace_if_let_with_match::replace_if_let_with_match, 208 replace_if_let_with_match::replace_if_let_with_match,
207 replace_if_let_with_match::replace_match_with_if_let, 209 replace_if_let_with_match::replace_match_with_if_let,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e28837b53..217f577eb 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -896,6 +896,41 @@ const test: Foo = Foo {foo: 1, bar: 0}
896} 896}
897 897
898#[test] 898#[test]
899fn doctest_reorder_impl() {
900 check_doc_test(
901 "reorder_impl",
902 r#####"
903trait Foo {
904 fn a() {}
905 fn b() {}
906 fn c() {}
907}
908
909struct Bar;
910$0impl Foo for Bar {
911 fn b() {}
912 fn c() {}
913 fn a() {}
914}
915"#####,
916 r#####"
917trait Foo {
918 fn a() {}
919 fn b() {}
920 fn c() {}
921}
922
923struct Bar;
924impl Foo for Bar {
925 fn a() {}
926 fn b() {}
927 fn c() {}
928}
929"#####,
930 )
931}
932
933#[test]
899fn doctest_replace_derive_with_manual_impl() { 934fn doctest_replace_derive_with_manual_impl() {
900 check_doc_test( 935 check_doc_test(
901 "replace_derive_with_manual_impl", 936 "replace_derive_with_manual_impl",
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index 3a29b5203..e5522980d 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -5,7 +5,7 @@
5 5
6use itertools::Itertools; 6use itertools::Itertools;
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8use syntax::{ast, AstNode, SyntaxKind}; 8use syntax::{ast, AstNode, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -21,15 +21,17 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
21 21
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 23 match (attribute.path(), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => match path.to_string().as_str() { 24 (Some(path), Some(token_tree)) => {
25 "derive" => complete_derive(acc, ctx, token_tree), 25 let path = path.syntax().text();
26 "feature" => complete_lint(acc, ctx, token_tree, FEATURES), 26 if path == "derive" {
27 "allow" | "warn" | "deny" | "forbid" => { 27 complete_derive(acc, ctx, token_tree)
28 } else if path == "feature" {
29 complete_lint(acc, ctx, token_tree, FEATURES)
30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
28 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); 31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
29 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); 32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } 33 }
31 _ => {} 34 }
32 },
33 (_, Some(_token_tree)) => {} 35 (_, Some(_token_tree)) => {}
34 _ => complete_attribute_start(acc, ctx, attribute), 36 _ => complete_attribute_start(acc, ctx, attribute),
35 } 37 }
@@ -203,8 +205,7 @@ fn complete_lint(
203fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 205fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
204 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { 206 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
205 (Some(left_paren), Some(right_paren)) 207 (Some(left_paren), Some(right_paren))
206 if left_paren.kind() == SyntaxKind::L_PAREN 208 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
207 && right_paren.kind() == SyntaxKind::R_PAREN =>
208 { 209 {
209 let mut input_derives = FxHashSet::default(); 210 let mut input_derives = FxHashSet::default();
210 let mut current_derive = String::new(); 211 let mut current_derive = String::new();
@@ -216,7 +217,7 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin
216 .skip(1) 217 .skip(1)
217 .take_while(|token| token != &right_paren) 218 .take_while(|token| token != &right_paren)
218 { 219 {
219 if SyntaxKind::COMMA == token.kind() { 220 if T![,] == token.kind() {
220 if !current_derive.is_empty() { 221 if !current_derive.is_empty() {
221 input_derives.insert(current_derive); 222 input_derives.insert(current_derive);
222 current_derive = String::new(); 223 current_derive = String::new();
diff --git a/crates/completion/src/completions/dot.rs b/crates/completion/src/completions/dot.rs
index 2e25c8ba2..d04eef65a 100644
--- a/crates/completion/src/completions/dot.rs
+++ b/crates/completion/src/completions/dot.rs
@@ -373,20 +373,20 @@ fn foo(a: A) {
373 fn macro_expansion_resilient() { 373 fn macro_expansion_resilient() {
374 check( 374 check(
375 r#" 375 r#"
376macro_rules! dbg { 376macro_rules! d {
377 () => {}; 377 () => {};
378 ($val:expr) => { 378 ($val:expr) => {
379 match $val { tmp => { tmp } } 379 match $val { tmp => { tmp } }
380 }; 380 };
381 // Trailing comma with single argument is ignored 381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::dbg!($val) }; 382 ($val:expr,) => { $crate::d!($val) };
383 ($($val:expr),+ $(,)?) => { 383 ($($val:expr),+ $(,)?) => {
384 ($($crate::dbg!($val)),+,) 384 ($($crate::d!($val)),+,)
385 }; 385 };
386} 386}
387struct A { the_field: u32 } 387struct A { the_field: u32 }
388fn foo(a: A) { 388fn foo(a: A) {
389 dbg!(a.$0) 389 d!(a.$0)
390} 390}
391"#, 391"#,
392 expect![[r#" 392 expect![[r#"
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 87f0c0b2a..9c34ed0b6 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -35,11 +35,14 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 let ref_removed_ty =
39 std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap();
40
38 let cap = match ctx.config.snippet_cap { 41 let cap = match ctx.config.snippet_cap {
39 Some(it) => it, 42 Some(it) => it,
40 None => return, 43 None => return,
41 }; 44 };
42 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty); 45 let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty);
43 if let Some(try_enum) = &try_enum { 46 if let Some(try_enum) = &try_enum {
44 match try_enum { 47 match try_enum {
45 TryEnum::Result => { 48 TryEnum::Result => {
@@ -496,6 +499,27 @@ fn main() {
496 fn postfix_completion_for_references() { 499 fn postfix_completion_for_references() {
497 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#); 500 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
498 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#); 501 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
502 check_edit(
503 "ifl",
504 r#"
505enum Option<T> { Some(T), None }
506
507fn main() {
508 let bar = &Option::Some(true);
509 bar.$0
510}
511"#,
512 r#"
513enum Option<T> { Some(T), None }
514
515fn main() {
516 let bar = &Option::Some(true);
517 if let Some($1) = bar {
518 $0
519}
520}
521"#,
522 )
499 } 523 }
500 524
501 #[test] 525 #[test]
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index aa9c845da..135ae49dc 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -93,11 +93,11 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
93 // `impl .. { const $0 }` 93 // `impl .. { const $0 }`
94 // ERROR 0 94 // ERROR 0
95 // CONST_KW <- * 95 // CONST_KW <- *
96 SyntaxKind::CONST_KW => 0, 96 T![const] => 0,
97 // `impl .. { fn/type $0 }` 97 // `impl .. { fn/type $0 }`
98 // FN/TYPE_ALIAS 0 98 // FN/TYPE_ALIAS 0
99 // FN_KW <- * 99 // FN_KW <- *
100 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0, 100 T![fn] | T![type] => 0,
101 // `impl .. { fn/type/const foo$0 }` 101 // `impl .. { fn/type/const foo$0 }`
102 // FN/TYPE_ALIAS/CONST 1 102 // FN/TYPE_ALIAS/CONST 1
103 // NAME 0 103 // NAME 0
@@ -121,7 +121,7 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; 121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
122 let kind = match impl_item.kind() { 122 let kind = match impl_item.kind() {
123 // `impl ... { const $0 fn/type/const }` 123 // `impl ... { const $0 fn/type/const }`
124 _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const, 124 _ if token.kind() == T![const] => ImplCompletionKind::Const,
125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, 125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, 126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
127 SyntaxKind::FN => ImplCompletionKind::Fn, 127 SyntaxKind::FN => ImplCompletionKind::Fn,
diff --git a/crates/completion/src/patterns.rs b/crates/completion/src/patterns.rs
index f148b9402..f3ce91dd1 100644
--- a/crates/completion/src/patterns.rs
+++ b/crates/completion/src/patterns.rs
@@ -5,7 +5,7 @@ use syntax::{
5 ast::{self, LoopBodyOwner}, 5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, 6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*, 7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, 8 SyntaxNode, SyntaxToken, T,
9}; 9};
10 10
11#[cfg(test)] 11#[cfg(test)]
@@ -119,7 +119,7 @@ pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element 119 element
120 .into_token() 120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it)) 121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == UNSAFE_KW) 122 .filter(|it| it.kind() == T![unsafe])
123 .is_some() 123 .is_some()
124} 124}
125#[test] 125#[test]
@@ -131,7 +131,7 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element 131 element
132 .into_token() 132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it)) 133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == IF_KW) 134 .filter(|it| it.kind() == T![if])
135 .is_some() 135 .is_some()
136} 136}
137 137
@@ -139,7 +139,7 @@ pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element 139 element
140 .into_token() 140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it)) 141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == FN_KW) 142 .filter(|it| it.kind() == T![fn])
143 .is_some() 143 .is_some()
144} 144}
145#[test] 145#[test]
@@ -154,7 +154,7 @@ pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
154 .into_token() 154 .into_token()
155 .and_then(|it| previous_non_trivia_token(it)) 155 .and_then(|it| previous_non_trivia_token(it))
156 .and_then(|it| previous_non_trivia_token(it)) 156 .and_then(|it| previous_non_trivia_token(it))
157 .filter(|it| it.kind() == FOR_KW) 157 .filter(|it| it.kind() == T![for])
158 .is_some() 158 .is_some()
159} 159}
160#[test] 160#[test]
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 1bad64a1b..1d19c7886 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "=0.12.0" 15cargo_metadata = "0.12.2"
16serde_json = "1.0.48" 16serde_json = "1.0.48"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18 18
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index cc1938333..6cbf5cecf 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -1263,6 +1263,24 @@ pub enum GenericParam {
1263} 1263}
1264impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); 1264impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1265 1265
1266impl GenericParam {
1267 pub fn module(self, db: &dyn HirDatabase) -> Module {
1268 match self {
1269 GenericParam::TypeParam(it) => it.module(db),
1270 GenericParam::LifetimeParam(it) => it.module(db),
1271 GenericParam::ConstParam(it) => it.module(db),
1272 }
1273 }
1274
1275 pub fn name(self, db: &dyn HirDatabase) -> Name {
1276 match self {
1277 GenericParam::TypeParam(it) => it.name(db),
1278 GenericParam::LifetimeParam(it) => it.name(db),
1279 GenericParam::ConstParam(it) => it.name(db),
1280 }
1281 }
1282}
1283
1266#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1284#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1267pub struct TypeParam { 1285pub struct TypeParam {
1268 pub(crate) id: TypeParamId, 1286 pub(crate) id: TypeParamId,
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 3e47a5e9d..c8c5fecd7 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -1,6 +1,6 @@
1//! Utility module for converting between hir_def ids and code_model wrappers. 1//! Utility module for converting between hir_def ids and code_model wrappers.
2//! 2//!
3//! It's unclear if we need this long-term, but it's definitelly useful while we 3//! It's unclear if we need this long-term, but it's definitely useful while we
4//! are splitting the hir. 4//! are splitting the hir.
5 5
6use hir_def::{ 6use hir_def::{
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 6be1eaade..3dc33f248 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -581,7 +581,7 @@ impl ExprCollector<'_> {
581 match res.value { 581 match res.value {
582 Some((mark, expansion)) => { 582 Some((mark, expansion)) => {
583 // FIXME: Statements are too complicated to recover from error for now. 583 // FIXME: Statements are too complicated to recover from error for now.
584 // It is because we don't have any hygenine for local variable expansion right now. 584 // It is because we don't have any hygiene for local variable expansion right now.
585 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { 585 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() {
586 self.expander.exit(self.db, mark); 586 self.expander.exit(self.db, mark);
587 collector(self, None); 587 collector(self, None);
@@ -959,7 +959,7 @@ impl ExprCollector<'_> {
959 959
960 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) { 960 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
961 // Find the location of the `..`, if there is one. Note that we do not 961 // Find the location of the `..`, if there is one. Note that we do not
962 // consider the possiblity of there being multiple `..` here. 962 // consider the possibility of there being multiple `..` here.
963 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); 963 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
964 // We want to skip the `..` pattern here, since we account for it above. 964 // We want to skip the `..` pattern here, since we account for it above.
965 let args = args 965 let args = args
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 6a481769d..76f5721e5 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -1,6 +1,6 @@
1//! This module describes hir-level representation of expressions. 1//! This module describes hir-level representation of expressions.
2//! 2//!
3//! This representaion is: 3//! This representation is:
4//! 4//!
5//! 1. Identity-based. Each expression has an `id`, so we can distinguish 5//! 1. Identity-based. Each expression has an `id`, so we can distinguish
6//! between different `1` in `1 + 1`. 6//! between different `1` in `1 + 1`.
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 77017e4ea..f027fd48d 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -267,7 +267,7 @@ impl DefCollector<'_> {
267 267
268 // Resolve all indeterminate resolved imports again 268 // Resolve all indeterminate resolved imports again
269 // As some of the macros will expand newly import shadowing partial resolved imports 269 // As some of the macros will expand newly import shadowing partial resolved imports
270 // FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports` 270 // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports`
271 // correctly 271 // correctly
272 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| { 272 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| {
273 if let PartialResolvedImport::Indeterminate(_) = directive.status { 273 if let PartialResolvedImport::Indeterminate(_) = directive.status {
@@ -402,7 +402,7 @@ impl DefCollector<'_> {
402 402
403 /// Define a proc macro 403 /// Define a proc macro
404 /// 404 ///
405 /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped. 405 /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
406 /// And unconditionally exported. 406 /// And unconditionally exported.
407 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { 407 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
408 self.update( 408 self.update(
@@ -592,7 +592,7 @@ impl DefCollector<'_> {
592 // XXX: urgh, so this works by accident! Here, we look at 592 // XXX: urgh, so this works by accident! Here, we look at
593 // the enum data, and, in theory, this might require us to 593 // the enum data, and, in theory, this might require us to
594 // look back at the crate_def_map, creating a cycle. For 594 // look back at the crate_def_map, creating a cycle. For
595 // example, `enum E { crate::some_macro!(); }`. Luckely, the 595 // example, `enum E { crate::some_macro!(); }`. Luckily, the
596 // only kind of macro that is allowed inside enum is a 596 // only kind of macro that is allowed inside enum is a
597 // `cfg_macro`, and we don't need to run name resolution for 597 // `cfg_macro`, and we don't need to run name resolution for
598 // it, but this is sheer luck! 598 // it, but this is sheer luck!
@@ -655,7 +655,7 @@ impl DefCollector<'_> {
655 &mut self, 655 &mut self,
656 module_id: LocalModuleId, 656 module_id: LocalModuleId,
657 resolutions: &[(Option<Name>, PerNs)], 657 resolutions: &[(Option<Name>, PerNs)],
658 // All resolutions are imported with this visibility; the visibilies in 658 // All resolutions are imported with this visibility; the visibilities in
659 // the `PerNs` values are ignored and overwritten 659 // the `PerNs` values are ignored and overwritten
660 vis: Visibility, 660 vis: Visibility,
661 import_type: ImportType, 661 import_type: ImportType,
diff --git a/crates/hir_def/src/per_ns.rs b/crates/hir_def/src/per_ns.rs
index 74665c588..a594afce6 100644
--- a/crates/hir_def/src/per_ns.rs
+++ b/crates/hir_def/src/per_ns.rs
@@ -7,7 +7,7 @@ use hir_expand::MacroDefId;
7 7
8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; 8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
11pub struct PerNs { 11pub struct PerNs {
12 pub types: Option<(ModuleDefId, Visibility)>, 12 pub types: Option<(ModuleDefId, Visibility)>,
13 pub values: Option<(ModuleDefId, Visibility)>, 13 pub values: Option<(ModuleDefId, Visibility)>,
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 129f1dbac..61059c349 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -27,7 +27,7 @@ use crate::{
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
29pub struct Resolver { 29pub struct Resolver {
30 // FIXME: all usages generally call `.rev`, so maybe reverse once in consturciton? 30 // FIXME: all usages generally call `.rev`, so maybe reverse once in construction?
31 scopes: Vec<Scope>, 31 scopes: Vec<Scope>,
32} 32}
33 33
@@ -490,6 +490,7 @@ pub enum ScopeDef {
490 490
491impl Scope { 491impl Scope {
492 fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { 492 fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
493 let mut seen = FxHashSet::default();
493 match self { 494 match self {
494 Scope::ModuleScope(m) => { 495 Scope::ModuleScope(m) => {
495 // FIXME: should we provide `self` here? 496 // FIXME: should we provide `self` here?
@@ -503,7 +504,9 @@ impl Scope {
503 f(name.clone(), ScopeDef::PerNs(def)); 504 f(name.clone(), ScopeDef::PerNs(def));
504 }); 505 });
505 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { 506 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
506 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public))); 507 let scope = PerNs::macros(macro_, Visibility::Public);
508 seen.insert((name.clone(), scope));
509 f(name.clone(), ScopeDef::PerNs(scope));
507 }); 510 });
508 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { 511 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
509 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); 512 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
@@ -514,7 +517,10 @@ impl Scope {
514 if let Some(prelude) = m.crate_def_map.prelude { 517 if let Some(prelude) = m.crate_def_map.prelude {
515 let prelude_def_map = db.crate_def_map(prelude.krate); 518 let prelude_def_map = db.crate_def_map(prelude.krate);
516 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { 519 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
517 f(name.clone(), ScopeDef::PerNs(def)); 520 let seen_tuple = (name.clone(), def);
521 if !seen.contains(&seen_tuple) {
522 f(seen_tuple.0, ScopeDef::PerNs(def));
523 }
518 }); 524 });
519 } 525 }
520 } 526 }
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index e6e0853a3..f3bc9d680 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -85,7 +85,7 @@ impl RawVisibility {
85} 85}
86 86
87/// Visibility of an item, with the path resolved. 87/// Visibility of an item, with the path resolved.
88#[derive(Debug, Copy, Clone, PartialEq, Eq)] 88#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
89pub enum Visibility { 89pub enum Visibility {
90 /// Visibility is restricted to a certain module. 90 /// Visibility is restricted to a certain module.
91 Module(ModuleId), 91 Module(ModuleId),
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ab2637b8c..c62086390 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -40,7 +40,7 @@ impl TokenExpander {
40 // FIXME switch these to ExpandResult as well 40 // FIXME switch these to ExpandResult as well
41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
42 TokenExpander::ProcMacro(_) => { 42 TokenExpander::ProcMacro(_) => {
43 // We store the result in salsa db to prevent non-determinisc behavior in 43 // We store the result in salsa db to prevent non-deterministic behavior in
44 // some proc-macro implementation 44 // some proc-macro implementation
45 // See #4315 for details 45 // See #4315 for details
46 db.expand_proc_macro(id.into()).into() 46 db.expand_proc_macro(id.into()).into()
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index a1c484fdf..107417c27 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -379,7 +379,7 @@ pub fn record_literal_missing_fields(
379 id: ExprId, 379 id: ExprId,
380 expr: &Expr, 380 expr: &Expr,
381) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { 381) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
382 let (fields, exhausitve) = match expr { 382 let (fields, exhaustive) = match expr {
383 Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), 383 Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()),
384 _ => return None, 384 _ => return None,
385 }; 385 };
@@ -400,7 +400,7 @@ pub fn record_literal_missing_fields(
400 if missed_fields.is_empty() { 400 if missed_fields.is_empty() {
401 return None; 401 return None;
402 } 402 }
403 Some((variant_def, missed_fields, exhausitve)) 403 Some((variant_def, missed_fields, exhaustive))
404} 404}
405 405
406pub fn record_pattern_missing_fields( 406pub fn record_pattern_missing_fields(
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 62c329731..61c47eec8 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -14,7 +14,7 @@
14//! The algorithm implemented here is a modified version of the one described in 14//! The algorithm implemented here is a modified version of the one described in
15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>. 15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
16//! However, to save future implementors from reading the original paper, we 16//! However, to save future implementors from reading the original paper, we
17//! summarise the algorithm here to hopefully save time and be a little clearer 17//! summarize the algorithm here to hopefully save time and be a little clearer
18//! (without being so rigorous). 18//! (without being so rigorous).
19//! 19//!
20//! The core of the algorithm revolves about a "usefulness" check. In particular, we 20//! The core of the algorithm revolves about a "usefulness" check. In particular, we
@@ -132,7 +132,7 @@
132//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). 132//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
133//! That means we're going to check the components from left-to-right, so the algorithm 133//! That means we're going to check the components from left-to-right, so the algorithm
134//! operates principally on the first component of the matrix and new pattern-stack `p`. 134//! operates principally on the first component of the matrix and new pattern-stack `p`.
135//! This algorithm is realised in the `is_useful` function. 135//! This algorithm is realized in the `is_useful` function.
136//! 136//!
137//! Base case (`n = 0`, i.e., an empty tuple pattern): 137//! Base case (`n = 0`, i.e., an empty tuple pattern):
138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then 138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index e9e949c47..d2f1b4014 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1,14 +1,15 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::fmt; 3use std::{borrow::Cow, fmt};
4 4
5use crate::{ 5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, 6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate,
7 Lifetime, Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 7 Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use arrayvec::ArrayVec;
9use hir_def::{ 10use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, 11 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId,
11 Lookup, ModuleId, 12 AssocContainerId, HasModule, Lookup, ModuleId, TraitId,
12}; 13};
13use hir_expand::name::Name; 14use hir_expand::name::Name;
14 15
@@ -257,25 +258,45 @@ impl HirDisplay for ApplicationTy {
257 t.hir_fmt(f)?; 258 t.hir_fmt(f)?;
258 write!(f, "; _]")?; 259 write!(f, "; _]")?;
259 } 260 }
260 TypeCtor::RawPtr(m) => { 261 TypeCtor::RawPtr(m) | TypeCtor::Ref(m) => {
261 let t = self.parameters.as_single(); 262 let t = self.parameters.as_single();
263 let ty_display =
264 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
262 265
263 write!(f, "*{}", m.as_keyword_for_ptr())?; 266 if matches!(self.ctor, TypeCtor::RawPtr(_)) {
264 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { 267 write!(f, "*{}", m.as_keyword_for_ptr())?;
265 write!(f, "(")?;
266 t.hir_fmt(f)?;
267 write!(f, ")")?;
268 } else { 268 } else {
269 t.hir_fmt(f)?; 269 write!(f, "&{}", m.as_keyword_for_ref())?;
270 }
271
272 let datas;
273 let predicates = match t {
274 Ty::Dyn(predicates) if predicates.len() > 1 => {
275 Cow::Borrowed(predicates.as_ref())
276 }
277 &Ty::Opaque(OpaqueTy {
278 opaque_ty_id: OpaqueTyId::ReturnTypeImplTrait(func, idx),
279 ref parameters,
280 }) => {
281 datas =
282 f.db.return_type_impl_traits(func).expect("impl trait id without data");
283 let data = (*datas)
284 .as_ref()
285 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
286 let bounds = data.subst(parameters);
287 Cow::Owned(bounds.value)
288 }
289 _ => Cow::Borrowed(&[][..]),
290 };
291
292 if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() {
293 let trait_ = trait_ref.trait_;
294 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) {
295 return write!(f, "{}", ty_display);
296 }
270 } 297 }
271 }
272 TypeCtor::Ref(m) => {
273 let t = self.parameters.as_single();
274 let ty_display =
275 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
276 298
277 write!(f, "&{}", m.as_keyword_for_ref())?; 299 if predicates.len() > 1 {
278 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
279 write!(f, "(")?; 300 write!(f, "(")?;
280 write!(f, "{}", ty_display)?; 301 write!(f, "{}", ty_display)?;
281 write!(f, ")")?; 302 write!(f, ")")?;
@@ -595,6 +616,17 @@ impl HirDisplay for FnSig {
595 } 616 }
596} 617}
597 618
619fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
620 let krate = trait_.lookup(db).container.module(db).krate;
621 let fn_traits = [
622 db.lang_item(krate, "fn".into()),
623 db.lang_item(krate, "fn_mut".into()),
624 db.lang_item(krate, "fn_once".into()),
625 ];
626 // FIXME: Replace ArrayVec when into_iter is a thing on arrays
627 ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait())
628}
629
598pub fn write_bounds_like_dyn_trait( 630pub fn write_bounds_like_dyn_trait(
599 predicates: &[GenericPredicate], 631 predicates: &[GenericPredicate],
600 f: &mut HirFormatter, 632 f: &mut HirFormatter,
@@ -607,10 +639,15 @@ pub fn write_bounds_like_dyn_trait(
607 // predicate for that trait). 639 // predicate for that trait).
608 let mut first = true; 640 let mut first = true;
609 let mut angle_open = false; 641 let mut angle_open = false;
642 let mut is_fn_trait = false;
610 for p in predicates.iter() { 643 for p in predicates.iter() {
611 match p { 644 match p {
612 GenericPredicate::Implemented(trait_ref) => { 645 GenericPredicate::Implemented(trait_ref) => {
613 if angle_open { 646 let trait_ = trait_ref.trait_;
647 if !is_fn_trait {
648 is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
649 }
650 if !is_fn_trait && angle_open {
614 write!(f, ">")?; 651 write!(f, ">")?;
615 angle_open = false; 652 angle_open = false;
616 } 653 }
@@ -620,14 +657,27 @@ pub fn write_bounds_like_dyn_trait(
620 // We assume that the self type is $0 (i.e. the 657 // We assume that the self type is $0 (i.e. the
621 // existential) here, which is the only thing that's 658 // existential) here, which is the only thing that's
622 // possible in actual Rust, and hence don't print it 659 // possible in actual Rust, and hence don't print it
623 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?; 660 write!(f, "{}", f.db.trait_data(trait_).name)?;
624 if trait_ref.substs.len() > 1 { 661 if let [_, params @ ..] = &*trait_ref.substs.0 {
625 write!(f, "<")?; 662 if is_fn_trait {
626 f.write_joined(&trait_ref.substs[1..], ", ")?; 663 if let Some(args) = params.first().and_then(|it| it.as_tuple()) {
627 // there might be assoc type bindings, so we leave the angle brackets open 664 write!(f, "(")?;
628 angle_open = true; 665 f.write_joined(&*args.0, ", ")?;
666 write!(f, ")")?;
667 }
668 } else if !params.is_empty() {
669 write!(f, "<")?;
670 f.write_joined(params, ", ")?;
671 // there might be assoc type bindings, so we leave the angle brackets open
672 angle_open = true;
673 }
629 } 674 }
630 } 675 }
676 GenericPredicate::Projection(projection_pred) if is_fn_trait => {
677 is_fn_trait = false;
678 write!(f, " -> ")?;
679 projection_pred.ty.hir_fmt(f)?;
680 }
631 GenericPredicate::Projection(projection_pred) => { 681 GenericPredicate::Projection(projection_pred) => {
632 // in types in actual Rust, these will always come 682 // in types in actual Rust, these will always come
633 // after the corresponding Implemented predicate 683 // after the corresponding Implemented predicate
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 222f61a11..9594cce8b 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -491,16 +491,16 @@ impl Ty {
491 fn from_hir_path_inner( 491 fn from_hir_path_inner(
492 ctx: &TyLoweringContext<'_>, 492 ctx: &TyLoweringContext<'_>,
493 segment: PathSegment<'_>, 493 segment: PathSegment<'_>,
494 typable: TyDefId, 494 typeable: TyDefId,
495 infer_args: bool, 495 infer_args: bool,
496 ) -> Ty { 496 ) -> Ty {
497 let generic_def = match typable { 497 let generic_def = match typeable {
498 TyDefId::BuiltinType(_) => None, 498 TyDefId::BuiltinType(_) => None,
499 TyDefId::AdtId(it) => Some(it.into()), 499 TyDefId::AdtId(it) => Some(it.into()),
500 TyDefId::TypeAliasId(it) => Some(it.into()), 500 TyDefId::TypeAliasId(it) => Some(it.into()),
501 }; 501 };
502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args); 502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
503 ctx.db.ty(typable).subst(&substs) 503 ctx.db.ty(typeable).subst(&substs)
504 } 504 }
505 505
506 /// Collect generic arguments from a path into a `Substs`. See also 506 /// Collect generic arguments from a path into a `Substs`. See also
diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs
index b502135d8..3d29021aa 100644
--- a/crates/hir_ty/src/tests/display_source_code.rs
+++ b/crates/hir_ty/src/tests/display_source_code.rs
@@ -39,3 +39,18 @@ fn main() {
39"#, 39"#,
40 ); 40 );
41} 41}
42
43#[test]
44fn render_raw_ptr_impl_ty() {
45 check_types_source_code(
46 r#"
47trait Sized {}
48trait Unpin {}
49fn foo() -> *const (impl Unpin + Sized) { loop {} }
50fn main() {
51 let foo = foo();
52 foo;
53} //^ *const (impl Unpin + Sized)
54"#,
55 );
56}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 41d097519..e5a3f95a6 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3038,16 +3038,16 @@ fn infer_box_fn_arg() {
3038 406..417 '&self.inner': &*mut T 3038 406..417 '&self.inner': &*mut T
3039 407..411 'self': &Box<T> 3039 407..411 'self': &Box<T>
3040 407..417 'self.inner': *mut T 3040 407..417 'self.inner': *mut T
3041 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> 3041 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
3042 488..489 's': Option<i32> 3042 488..489 's': Option<i32>
3043 492..504 'Option::None': Option<i32> 3043 492..504 'Option::None': Option<i32>
3044 514..515 'f': Box<dyn FnOnce<(&Option<i32>,)>> 3044 514..515 'f': Box<dyn FnOnce(&Option<i32>)>
3045 549..562 'box (|ps| {})': Box<|{unknown}| -> ()> 3045 549..562 'box (|ps| {})': Box<|{unknown}| -> ()>
3046 554..561 '|ps| {}': |{unknown}| -> () 3046 554..561 '|ps| {}': |{unknown}| -> ()
3047 555..557 'ps': {unknown} 3047 555..557 'ps': {unknown}
3048 559..561 '{}': () 3048 559..561 '{}': ()
3049 568..569 'f': Box<dyn FnOnce<(&Option<i32>,)>> 3049 568..569 'f': Box<dyn FnOnce(&Option<i32>)>
3050 568..573 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> 3050 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
3051 570..572 '&s': &Option<i32> 3051 570..572 '&s': &Option<i32>
3052 571..572 's': Option<i32> 3052 571..572 's': Option<i32>
3053 "#]], 3053 "#]],
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 4d8983cb2..e8999a7f3 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -5,7 +5,7 @@ use indexmap::IndexMap;
5use hir::Semantics; 5use hir::Semantics;
6use ide_db::call_info::FnCallNode; 6use ide_db::call_info::FnCallNode;
7use ide_db::RootDatabase; 7use ide_db::RootDatabase;
8use syntax::{ast, match_ast, AstNode, TextRange}; 8use syntax::{ast, AstNode, TextRange};
9 9
10use crate::{ 10use crate::{
11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, 11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
@@ -47,28 +47,23 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
47 47
48 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
49 49
50 for reference in refs.info.references() { 50 for (&file_id, references) in refs.info.references().iter() {
51 let file_id = reference.file_range.file_id;
52 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
53 let file = file.syntax(); 52 let file = file.syntax();
54 let token = file.token_at_offset(reference.file_range.range.start()).next()?; 53 for reference in references {
55 let token = sema.descend_into_macros(token); 54 let token = file.token_at_offset(reference.range.start()).next()?;
56 let syntax = token.parent(); 55 let token = sema.descend_into_macros(token);
57 56 let syntax = token.parent();
58 // This target is the containing function 57
59 if let Some(nav) = syntax.ancestors().find_map(|node| { 58 // This target is the containing function
60 match_ast! { 59 if let Some(nav) = syntax.ancestors().find_map(|node| {
61 match node { 60 let fn_ = ast::Fn::cast(node)?;
62 ast::Fn(it) => { 61 let def = sema.to_def(&fn_)?;
63 let def = sema.to_def(&it)?; 62 def.try_to_nav(sema.db)
64 def.try_to_nav(sema.db) 63 }) {
65 }, 64 let relative_range = reference.range;
66 _ => None, 65 calls.add(&nav, relative_range);
67 }
68 } 66 }
69 }) {
70 let relative_range = reference.file_range.range;
71 calls.add(&nav, relative_range);
72 } 67 }
73 } 68 }
74 69
@@ -91,17 +86,12 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
91 .filter_map(|node| FnCallNode::with_node_exact(&node)) 86 .filter_map(|node| FnCallNode::with_node_exact(&node))
92 .filter_map(|call_node| { 87 .filter_map(|call_node| {
93 let name_ref = call_node.name_ref()?; 88 let name_ref = call_node.name_ref()?;
94 89 let func_target = match call_node {
95 if let Some(func_target) = match &call_node {
96 FnCallNode::CallExpr(expr) => { 90 FnCallNode::CallExpr(expr) => {
97 //FIXME: Type::as_callable is broken 91 //FIXME: Type::as_callable is broken
98 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?; 92 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
99 match callable.kind() { 93 match callable.kind() {
100 hir::CallableKind::Function(it) => { 94 hir::CallableKind::Function(it) => it.try_to_nav(db),
101 let fn_def: hir::Function = it.into();
102 let nav = fn_def.try_to_nav(db)?;
103 Some(nav)
104 }
105 _ => None, 95 _ => None,
106 } 96 }
107 } 97 }
@@ -109,11 +99,8 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
109 let function = sema.resolve_method_call(&expr)?; 99 let function = sema.resolve_method_call(&expr)?;
110 function.try_to_nav(db) 100 function.try_to_nav(db)
111 } 101 }
112 } { 102 }?;
113 Some((func_target, name_ref.syntax().text_range())) 103 Some((func_target, name_ref.syntax().text_range()))
114 } else {
115 None
116 }
117 }) 104 })
118 .for_each(|(nav, range)| calls.add(&nav, range)); 105 .for_each(|(nav, range)| calls.add(&nav, range));
119 106
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index e24c78301..4eecae697 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -215,10 +215,8 @@ impl TryToNav for Definition {
215 Definition::ModuleDef(it) => it.try_to_nav(db), 215 Definition::ModuleDef(it) => it.try_to_nav(db),
216 Definition::SelfType(it) => it.try_to_nav(db), 216 Definition::SelfType(it) => it.try_to_nav(db),
217 Definition::Local(it) => Some(it.to_nav(db)), 217 Definition::Local(it) => Some(it.to_nav(db)),
218 Definition::TypeParam(it) => it.try_to_nav(db), 218 Definition::GenericParam(it) => it.try_to_nav(db),
219 Definition::LifetimeParam(it) => it.try_to_nav(db),
220 Definition::Label(it) => Some(it.to_nav(db)), 219 Definition::Label(it) => Some(it.to_nav(db)),
221 Definition::ConstParam(it) => it.try_to_nav(db),
222 } 220 }
223 } 221 }
224} 222}
@@ -389,6 +387,16 @@ impl TryToNav for hir::AssocItem {
389 } 387 }
390} 388}
391 389
390impl TryToNav for hir::GenericParam {
391 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
392 match self {
393 hir::GenericParam::TypeParam(it) => it.try_to_nav(db),
394 hir::GenericParam::ConstParam(it) => it.try_to_nav(db),
395 hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db),
396 }
397 }
398}
399
392impl ToNav for hir::Local { 400impl ToNav for hir::Local {
393 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 401 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
394 let src = self.source(db); 402 let src = self.source(db);
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 1ff818de2..de10406bc 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -1,6 +1,6 @@
1//! Resolves and rewrites links in markdown documentation. 1//! Resolves and rewrites links in markdown documentation.
2 2
3use std::{convert::TryFrom, iter::once}; 3use std::{convert::TryFrom, iter::once, ops::Range};
4 4
5use itertools::Itertools; 5use itertools::Itertools;
6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; 6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
@@ -39,7 +39,7 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
39 if target.contains("://") { 39 if target.contains("://") {
40 (target.to_string(), title.to_string()) 40 (target.to_string(), title.to_string())
41 } else { 41 } else {
42 // Two posibilities: 42 // Two possibilities:
43 // * path-based links: `../../module/struct.MyStruct.html` 43 // * path-based links: `../../module/struct.MyStruct.html`
44 // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` 44 // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
45 if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) { 45 if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) {
@@ -61,6 +61,30 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
61 out 61 out
62} 62}
63 63
64pub(crate) fn extract_definitions_from_markdown(
65 markdown: &str,
66) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> {
67 let mut res = vec![];
68 let mut cb = |link: BrokenLink| {
69 Some((
70 /*url*/ link.reference.to_owned().into(),
71 /*title*/ link.reference.to_owned().into(),
72 ))
73 };
74 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
75 for (event, range) in doc.into_offset_iter() {
76 match event {
77 Event::Start(Tag::Link(_link_type, ref target, ref title)) => {
78 let link = if target.is_empty() { title } else { target };
79 let (link, ns) = parse_link(link);
80 res.push((link.to_string(), ns, range));
81 }
82 _ => {}
83 }
84 }
85 res
86}
87
64/// Remove all links in markdown documentation. 88/// Remove all links in markdown documentation.
65pub(crate) fn remove_links(markdown: &str) -> String { 89pub(crate) fn remove_links(markdown: &str) -> String {
66 let mut drop_link = false; 90 let mut drop_link = false;
@@ -192,9 +216,7 @@ fn rewrite_intra_doc_link(
192 Definition::Field(it) => it.resolve_doc_path(db, link, ns), 216 Definition::Field(it) => it.resolve_doc_path(db, link, ns),
193 Definition::SelfType(_) 217 Definition::SelfType(_)
194 | Definition::Local(_) 218 | Definition::Local(_)
195 | Definition::TypeParam(_) 219 | Definition::GenericParam(_)
196 | Definition::ConstParam(_)
197 | Definition::LifetimeParam(_)
198 | Definition::Label(_) => return None, 220 | Definition::Label(_) => return None,
199 }?; 221 }?;
200 let krate = resolved.module(db)?.krate(); 222 let krate = resolved.module(db)?.krate();
@@ -420,7 +442,7 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem)
420 function.as_assoc_item(db).map(|assoc| assoc.container(db)), 442 function.as_assoc_item(db).map(|assoc| assoc.container(db)),
421 Some(AssocItemContainer::Trait(..)) 443 Some(AssocItemContainer::Trait(..))
422 ); 444 );
423 // This distinction may get more complicated when specialisation is available. 445 // This distinction may get more complicated when specialization is available.
424 // Rustdoc makes this decision based on whether a method 'has defaultness'. 446 // Rustdoc makes this decision based on whether a method 'has defaultness'.
425 // Currently this is only the case for provided trait methods. 447 // Currently this is only the case for provided trait methods.
426 if is_trait_method && !function.has_body(db) { 448 if is_trait_method && !function.has_body(db) {
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 95b4cb9e3..cd4afc804 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,14 +1,18 @@
1use either::Either; 1use either::Either;
2use hir::Semantics; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use ide_db::{
4 base_db::FileId, 4 base_db::FileId,
5 defs::{NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 symbol_index, RootDatabase, 6 symbol_index, RootDatabase,
7}; 7};
8use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 8use syntax::{
9 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
10};
9 11
10use crate::{ 12use crate::{
11 display::{ToNav, TryToNav}, 13 display::{ToNav, TryToNav},
14 doc_links::extract_definitions_from_markdown,
15 runnables::doc_owner_to_def,
12 FilePosition, NavigationTarget, RangeInfo, SymbolKind, 16 FilePosition, NavigationTarget, RangeInfo, SymbolKind,
13}; 17};
14 18
@@ -30,6 +34,10 @@ pub(crate) fn goto_definition(
30 let original_token = pick_best(file.token_at_offset(position.offset))?; 34 let original_token = pick_best(file.token_at_offset(position.offset))?;
31 let token = sema.descend_into_macros(original_token.clone()); 35 let token = sema.descend_into_macros(original_token.clone());
32 let parent = token.parent(); 36 let parent = token.parent();
37 if let Some(comment) = ast::Comment::cast(token.clone()) {
38 let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?;
39 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
40 }
33 41
34 let nav_targets = match_ast! { 42 let nav_targets = match_ast! {
35 match parent { 43 match parent {
@@ -68,11 +76,58 @@ pub(crate) fn goto_definition(
68 Some(RangeInfo::new(original_token.text_range(), nav_targets)) 76 Some(RangeInfo::new(original_token.text_range(), nav_targets))
69} 77}
70 78
79fn def_for_doc_comment(
80 sema: &Semantics<RootDatabase>,
81 position: FilePosition,
82 doc_comment: &ast::Comment,
83) -> Option<hir::ModuleDef> {
84 let parent = doc_comment.syntax().parent();
85 let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?;
86
87 let def = doc_owner_to_def(sema, parent)?;
88 match def {
89 Definition::ModuleDef(def) => match def {
90 ModuleDef::Module(it) => it.resolve_doc_path(sema.db, &link, ns),
91 ModuleDef::Function(it) => it.resolve_doc_path(sema.db, &link, ns),
92 ModuleDef::Adt(it) => it.resolve_doc_path(sema.db, &link, ns),
93 ModuleDef::Variant(it) => it.resolve_doc_path(sema.db, &link, ns),
94 ModuleDef::Const(it) => it.resolve_doc_path(sema.db, &link, ns),
95 ModuleDef::Static(it) => it.resolve_doc_path(sema.db, &link, ns),
96 ModuleDef::Trait(it) => it.resolve_doc_path(sema.db, &link, ns),
97 ModuleDef::TypeAlias(it) => it.resolve_doc_path(sema.db, &link, ns),
98 ModuleDef::BuiltinType(_) => return None,
99 },
100 Definition::Macro(it) => it.resolve_doc_path(sema.db, &link, ns),
101 Definition::Field(it) => it.resolve_doc_path(sema.db, &link, ns),
102 Definition::SelfType(_)
103 | Definition::Local(_)
104 | Definition::GenericParam(_)
105 | Definition::Label(_) => return None,
106 }
107}
108
109fn extract_positioned_link_from_comment(
110 position: FilePosition,
111 comment: &ast::Comment,
112) -> Option<(String, Option<hir::Namespace>)> {
113 let comment_range = comment.syntax().text_range();
114 let doc_comment = comment.doc_comment()?;
115 let def_links = extract_definitions_from_markdown(doc_comment);
116 let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| {
117 let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32);
118 match position.offset.checked_sub(matched_position) {
119 Some(distance) => distance,
120 None => comment_range.end(),
121 }
122 })?;
123 Some((def_link.to_string(), ns.clone()))
124}
125
71fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 126fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
72 return tokens.max_by_key(priority); 127 return tokens.max_by_key(priority);
73 fn priority(n: &SyntaxToken) -> usize { 128 fn priority(n: &SyntaxToken) -> usize {
74 match n.kind() { 129 match n.kind() {
75 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2, 130 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
76 kind if kind.is_trivia() => 0, 131 kind if kind.is_trivia() => 0,
77 _ => 1, 132 _ => 1,
78 } 133 }
@@ -1145,4 +1200,34 @@ fn foo<'foo>(_: &'foo ()) {
1145}"#, 1200}"#,
1146 ) 1201 )
1147 } 1202 }
1203
1204 #[test]
1205 fn goto_def_for_intra_doc_link_same_file() {
1206 check(
1207 r#"
1208/// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar)
1209pub fn bar() { }
1210
1211/// You might want to see [`std::fs::read()`] too.
1212pub fn foo() { }
1213 //^^^
1214
1215}"#,
1216 )
1217 }
1218
1219 #[test]
1220 fn goto_def_for_intra_doc_link_inner() {
1221 check(
1222 r#"
1223//- /main.rs
1224mod m;
1225struct S;
1226 //^
1227
1228//- /m.rs
1229//! [`super::S$0`]
1230"#,
1231 )
1232 }
1148} 1233}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8cb4a51d8..317b6f011 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,6 +1,6 @@
1use hir::{ 1use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module, 2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 ModuleDef, ModuleSource, Semantics, 3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
4}; 4};
5use ide_db::base_db::SourceDatabase; 5use ide_db::base_db::SourceDatabase;
6use ide_db::{ 6use ide_db::{
@@ -17,7 +17,7 @@ use crate::{
17 doc_links::{remove_links, rewrite_links}, 17 doc_links::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown, 18 markdown_remove::remove_markdown,
19 markup::Markup, 19 markup::Markup,
20 runnables::{runnable, runnable_fn}, 20 runnables::{runnable_fn, runnable_mod},
21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
22}; 22};
23 23
@@ -175,12 +175,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
175 Definition::SelfType(it) => it.target_ty(db).as_adt(), 175 Definition::SelfType(it) => it.target_ty(db).as_adt(),
176 _ => None, 176 _ => None,
177 }?; 177 }?;
178 match adt { 178 adt.try_to_nav(db).map(to_action)
179 Adt::Struct(it) => it.try_to_nav(db),
180 Adt::Union(it) => it.try_to_nav(db),
181 Adt::Enum(it) => it.try_to_nav(db),
182 }
183 .map(to_action)
184} 179}
185 180
186fn runnable_action( 181fn runnable_action(
@@ -192,7 +187,7 @@ fn runnable_action(
192 Definition::ModuleDef(it) => match it { 187 Definition::ModuleDef(it) => match it {
193 ModuleDef::Module(it) => match it.definition_source(sema.db).value { 188 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
194 ModuleSource::Module(it) => { 189 ModuleSource::Module(it) => {
195 runnable(&sema, it.syntax().clone()).map(|it| HoverAction::Runnable(it)) 190 runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it))
196 } 191 }
197 _ => None, 192 _ => None,
198 }, 193 },
@@ -220,12 +215,12 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
220 } 215 }
221 }; 216 };
222 217
223 if let Definition::TypeParam(it) = def { 218 if let Definition::GenericParam(GenericParam::TypeParam(it)) = def {
224 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); 219 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
225 } else { 220 } else {
226 let ty = match def { 221 let ty = match def {
227 Definition::Local(it) => it.ty(db), 222 Definition::Local(it) => it.ty(db),
228 Definition::ConstParam(it) => it.ty(db), 223 Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db),
229 _ => return None, 224 _ => return None,
230 }; 225 };
231 226
@@ -357,9 +352,11 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
357 }) 352 })
358 } 353 }
359 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 354 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
360 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), 355 Definition::GenericParam(it) => match it {
361 Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))), 356 GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))),
362 Definition::ConstParam(it) => from_def_source(db, it, None), 357 GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
358 GenericParam::ConstParam(it) => from_def_source(db, it, None),
359 },
363 }; 360 };
364 361
365 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 362 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
@@ -1951,16 +1948,16 @@ struct S {
1951/// Test cases: 1948/// Test cases:
1952/// case 1. bare URL: https://www.example.com/ 1949/// case 1. bare URL: https://www.example.com/
1953/// case 2. inline URL with title: [example](https://www.example.com/) 1950/// case 2. inline URL with title: [example](https://www.example.com/)
1954/// case 3. code refrence: [`Result`] 1951/// case 3. code reference: [`Result`]
1955/// case 4. code refrence but miss footnote: [`String`] 1952/// case 4. code reference but miss footnote: [`String`]
1956/// case 5. autolink: <http://www.example.com/> 1953/// case 5. autolink: <http://www.example.com/>
1957/// case 6. email address: <[email protected]> 1954/// case 6. email address: <[email protected]>
1958/// case 7. refrence: [example][example] 1955/// case 7. reference: [example][example]
1959/// case 8. collapsed link: [example][] 1956/// case 8. collapsed link: [example][]
1960/// case 9. shortcut link: [example] 1957/// case 9. shortcut link: [example]
1961/// case 10. inline without URL: [example]() 1958/// case 10. inline without URL: [example]()
1962/// case 11. refrence: [foo][foo] 1959/// case 11. reference: [foo][foo]
1963/// case 12. refrence: [foo][bar] 1960/// case 12. reference: [foo][bar]
1964/// case 13. collapsed link: [foo][] 1961/// case 13. collapsed link: [foo][]
1965/// case 14. shortcut link: [foo] 1962/// case 14. shortcut link: [foo]
1966/// case 15. inline without URL: [foo]() 1963/// case 15. inline without URL: [foo]()
@@ -1987,16 +1984,16 @@ pub fn fo$0o() {}
1987 Test cases: 1984 Test cases:
1988 case 1. bare URL: https://www.example.com/ 1985 case 1. bare URL: https://www.example.com/
1989 case 2. inline URL with title: [example](https://www.example.com/) 1986 case 2. inline URL with title: [example](https://www.example.com/)
1990 case 3. code refrence: `Result` 1987 case 3. code reference: `Result`
1991 case 4. code refrence but miss footnote: `String` 1988 case 4. code reference but miss footnote: `String`
1992 case 5. autolink: http://www.example.com/ 1989 case 5. autolink: http://www.example.com/
1993 case 6. email address: [email protected] 1990 case 6. email address: [email protected]
1994 case 7. refrence: example 1991 case 7. reference: example
1995 case 8. collapsed link: example 1992 case 8. collapsed link: example
1996 case 9. shortcut link: example 1993 case 9. shortcut link: example
1997 case 10. inline without URL: example 1994 case 10. inline without URL: example
1998 case 11. refrence: foo 1995 case 11. reference: foo
1999 case 12. refrence: foo 1996 case 12. reference: foo
2000 case 13. collapsed link: foo 1997 case 13. collapsed link: foo
2001 case 14. shortcut link: foo 1998 case 14. shortcut link: foo
2002 case 15. inline without URL: foo 1999 case 15. inline without URL: foo
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index fe60abfc8..a2039fcc7 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -353,9 +353,25 @@ fn is_argument_similar_to_param_name(
353 } 353 }
354 match get_string_representation(argument) { 354 match get_string_representation(argument) {
355 None => false, 355 None => false,
356 Some(repr) => { 356 Some(argument_string) => {
357 let argument_string = repr.trim_start_matches('_'); 357 let num_leading_underscores =
358 argument_string.starts_with(param_name) || argument_string.ends_with(param_name) 358 argument_string.bytes().take_while(|&c| c == b'_').count();
359
360 // Does the argument name begin with the parameter name? Ignore leading underscores.
361 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
362 let starts_with_pattern = param_name.bytes().all(
363 |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
364 );
365
366 if starts_with_pattern {
367 return true;
368 }
369
370 // Does the argument name end with the parameter name?
371 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
372 param_name.bytes().rev().all(
373 |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
374 )
359 } 375 }
360 } 376 }
361} 377}
@@ -901,6 +917,9 @@ fn main() {
901 twiddle(true); 917 twiddle(true);
902 doo(true); 918 doo(true);
903 919
920 const TWIDDLE_UPPERCASE: bool = true;
921 twiddle(TWIDDLE_UPPERCASE);
922
904 let mut param_begin: Param = Param {}; 923 let mut param_begin: Param = Param {};
905 different_order(&param_begin); 924 different_order(&param_begin);
906 different_order(&mut param_begin); 925 different_order(&mut param_begin);
@@ -1382,4 +1401,41 @@ fn main() {
1382"#, 1401"#,
1383 ) 1402 )
1384 } 1403 }
1404
1405 #[test]
1406 fn fn_hints() {
1407 check(
1408 r#"
1409trait Sized {}
1410
1411fn foo() -> impl Fn() { loop {} }
1412fn foo1() -> impl Fn(f64) { loop {} }
1413fn foo2() -> impl Fn(f64, f64) { loop {} }
1414fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
1415fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
1416fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1417fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1418fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1419
1420fn main() {
1421 let foo = foo();
1422 // ^^^ impl Fn()
1423 let foo = foo1();
1424 // ^^^ impl Fn(f64)
1425 let foo = foo2();
1426 // ^^^ impl Fn(f64, f64)
1427 let foo = foo3();
1428 // ^^^ impl Fn(f64, f64) -> u32
1429 let foo = foo4();
1430 // ^^^ &dyn Fn(f64, f64) -> u32
1431 let foo = foo5();
1432 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
1433 let foo = foo6();
1434 // ^^^ impl Fn(f64, f64) -> u32 + Sized
1435 let foo = foo7();
1436 // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized)
1437}
1438"#,
1439 )
1440 }
1385} 1441}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index cea2a13c8..1e03832ec 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -76,8 +76,8 @@ pub use crate::{
76 references::{rename::RenameError, Declaration, ReferenceSearchResult}, 76 references::{rename::RenameError, Declaration, ReferenceSearchResult},
77 runnables::{Runnable, RunnableKind, TestId}, 77 runnables::{Runnable, RunnableKind, TestId},
78 syntax_highlighting::{ 78 syntax_highlighting::{
79 tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}, 79 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
80 HighlightedRange, 80 HlRange,
81 }, 81 },
82}; 82};
83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig}; 83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig};
@@ -92,7 +92,7 @@ pub use ide_db::base_db::{
92}; 92};
93pub use ide_db::{ 93pub use ide_db::{
94 call_info::CallInfo, 94 call_info::CallInfo,
95 search::{Reference, ReferenceAccess, ReferenceKind}, 95 search::{FileReference, ReferenceAccess, ReferenceKind},
96}; 96};
97pub use ide_db::{ 97pub use ide_db::{
98 label::Label, 98 label::Label,
@@ -449,12 +449,12 @@ impl Analysis {
449 } 449 }
450 450
451 /// Computes syntax highlighting for the given file 451 /// Computes syntax highlighting for the given file
452 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 452 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> {
453 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) 453 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
454 } 454 }
455 455
456 /// Computes syntax highlighting for the given file range. 456 /// Computes syntax highlighting for the given file range.
457 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { 457 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> {
458 self.with_db(|db| { 458 self.with_db(|db| {
459 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) 459 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
460 }) 460 })
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index c95ed669c..7d4757e02 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -3,7 +3,7 @@
3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we 3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we
4//! try to resolve the direct tree parent of this element, otherwise we 4//! try to resolve the direct tree parent of this element, otherwise we
5//! already have a definition and just need to get its HIR together with 5//! already have a definition and just need to get its HIR together with
6//! some information that is needed for futher steps of searching. 6//! some information that is needed for further steps of searching.
7//! After that, we collect files that might contain references and look 7//! After that, we collect files that might contain references and look
8//! for text occurrences of the identifier. If there's an `ast::NameRef` 8//! for text occurrences of the identifier. If there's an `ast::NameRef`
9//! at the index that the match starts at and its tree parent is 9//! at the index that the match starts at and its tree parent is
@@ -13,15 +13,15 @@ pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::Semantics;
15use ide_db::{ 15use ide_db::{
16 base_db::FileId,
16 defs::{Definition, NameClass, NameRefClass}, 17 defs::{Definition, NameClass, NameRefClass},
17 search::Reference, 18 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult},
18 search::{ReferenceAccess, ReferenceKind, SearchScope},
19 RootDatabase, 19 RootDatabase,
20}; 20};
21use syntax::{ 21use syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, NameOwner}, 23 ast::{self, NameOwner},
24 match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, 24 match_ast, AstNode, SyntaxNode, TextRange, TokenAtOffset, T,
25}; 25};
26 26
27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; 27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind};
@@ -29,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI
29#[derive(Debug, Clone)] 29#[derive(Debug, Clone)]
30pub struct ReferenceSearchResult { 30pub struct ReferenceSearchResult {
31 declaration: Declaration, 31 declaration: Declaration,
32 references: Vec<Reference>, 32 references: UsageSearchResult,
33} 33}
34 34
35#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
@@ -48,10 +48,21 @@ impl ReferenceSearchResult {
48 &self.declaration.nav 48 &self.declaration.nav
49 } 49 }
50 50
51 pub fn references(&self) -> &[Reference] { 51 pub fn references(&self) -> &UsageSearchResult {
52 &self.references 52 &self.references
53 } 53 }
54 54
55 pub fn references_with_declaration(mut self) -> UsageSearchResult {
56 let decl_ref = FileReference {
57 range: self.declaration.nav.focus_or_full_range(),
58 kind: self.declaration.kind,
59 access: self.declaration.access,
60 };
61 let file_id = self.declaration.nav.file_id;
62 self.references.references.entry(file_id).or_default().push(decl_ref);
63 self.references
64 }
65
55 /// Total number of references 66 /// Total number of references
56 /// At least 1 since all valid references should 67 /// At least 1 since all valid references should
57 /// Have a declaration 68 /// Have a declaration
@@ -63,21 +74,11 @@ impl ReferenceSearchResult {
63// allow turning ReferenceSearchResult into an iterator 74// allow turning ReferenceSearchResult into an iterator
64// over References 75// over References
65impl IntoIterator for ReferenceSearchResult { 76impl IntoIterator for ReferenceSearchResult {
66 type Item = Reference; 77 type Item = (FileId, Vec<FileReference>);
67 type IntoIter = std::vec::IntoIter<Reference>; 78 type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>;
68 79
69 fn into_iter(mut self) -> Self::IntoIter { 80 fn into_iter(self) -> Self::IntoIter {
70 let mut v = Vec::with_capacity(self.len()); 81 self.references_with_declaration().into_iter()
71 v.push(Reference {
72 file_range: FileRange {
73 file_id: self.declaration.nav.file_id,
74 range: self.declaration.nav.focus_or_full_range(),
75 },
76 kind: self.declaration.kind,
77 access: self.declaration.access,
78 });
79 v.append(&mut self.references);
80 v.into_iter()
81 } 82 }
82} 83}
83 84
@@ -109,13 +110,12 @@ pub(crate) fn find_all_refs(
109 110
110 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 111 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
111 112
112 let references = def 113 let mut usages = def.usages(sema).set_scope(search_scope).all();
113 .usages(sema) 114 usages
114 .set_scope(search_scope) 115 .references
115 .all() 116 .values_mut()
116 .into_iter() 117 .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind));
117 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 118 usages.references.retain(|_, it| !it.is_empty());
118 .collect();
119 119
120 let nav = def.try_to_nav(sema.db)?; 120 let nav = def.try_to_nav(sema.db)?;
121 let decl_range = nav.focus_or_full_range(); 121 let decl_range = nav.focus_or_full_range();
@@ -130,13 +130,16 @@ pub(crate) fn find_all_refs(
130 kind = ReferenceKind::FieldShorthandForLocal; 130 kind = ReferenceKind::FieldShorthandForLocal;
131 } 131 }
132 } 132 }
133 } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { 133 } else if matches!(
134 def,
135 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
136 ) {
134 kind = ReferenceKind::Lifetime; 137 kind = ReferenceKind::Lifetime;
135 }; 138 };
136 139
137 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; 140 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
138 141
139 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) 142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages }))
140} 143}
141 144
142fn find_name( 145fn find_name(
@@ -200,7 +203,7 @@ fn get_struct_def_name_for_struct_literal_search(
200 position: FilePosition, 203 position: FilePosition,
201) -> Option<ast::Name> { 204) -> Option<ast::Name> {
202 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 205 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
203 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { 206 if right.kind() != T!['{'] && right.kind() != T!['('] {
204 return None; 207 return None;
205 } 208 }
206 if let Some(name) = 209 if let Some(name) =
@@ -227,7 +230,7 @@ fn get_enum_def_name_for_struct_literal_search(
227 position: FilePosition, 230 position: FilePosition,
228) -> Option<ast::Name> { 231) -> Option<ast::Name> {
229 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 232 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
230 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { 233 if right.kind() != T!['{'] && right.kind() != T!['('] {
231 return None; 234 return None;
232 } 235 }
233 if let Some(name) = 236 if let Some(name) =
@@ -252,8 +255,8 @@ fn try_find_self_references(
252 syntax: &SyntaxNode, 255 syntax: &SyntaxNode,
253 position: FilePosition, 256 position: FilePosition,
254) -> Option<RangeInfo<ReferenceSearchResult>> { 257) -> Option<RangeInfo<ReferenceSearchResult>> {
255 let self_token = 258 let FilePosition { file_id, offset } = position;
256 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?; 259 let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?;
257 let parent = self_token.parent(); 260 let parent = self_token.parent();
258 match_ast! { 261 match_ast! {
259 match parent { 262 match parent {
@@ -274,7 +277,7 @@ fn try_find_self_references(
274 277
275 let declaration = Declaration { 278 let declaration = Declaration {
276 nav: NavigationTarget { 279 nav: NavigationTarget {
277 file_id: position.file_id, 280 file_id,
278 full_range: self_param.syntax().text_range(), 281 full_range: self_param.syntax().text_range(),
279 focus_range: Some(param_self_token.text_range()), 282 focus_range: Some(param_self_token.text_range()),
280 name: param_self_token.text().clone(), 283 name: param_self_token.text().clone(),
@@ -290,7 +293,7 @@ fn try_find_self_references(
290 ReferenceAccess::Read 293 ReferenceAccess::Read
291 }), 294 }),
292 }; 295 };
293 let references = function 296 let refs = function
294 .body() 297 .body()
295 .map(|body| { 298 .map(|body| {
296 body.syntax() 299 body.syntax()
@@ -304,14 +307,16 @@ fn try_find_self_references(
304 None 307 None
305 } 308 }
306 }) 309 })
307 .map(|token| Reference { 310 .map(|token| FileReference {
308 file_range: FileRange { file_id: position.file_id, range: token.text_range() }, 311 range: token.text_range(),
309 kind: ReferenceKind::SelfKw, 312 kind: ReferenceKind::SelfKw,
310 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration 313 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
311 }) 314 })
312 .collect() 315 .collect()
313 }) 316 })
314 .unwrap_or_default(); 317 .unwrap_or_default();
318 let mut references = UsageSearchResult::default();
319 references.references.insert(file_id, refs);
315 320
316 Some(RangeInfo::new( 321 Some(RangeInfo::new(
317 param_self_token.text_range(), 322 param_self_token.text_range(),
@@ -1016,12 +1021,14 @@ impl Foo {
1016 actual += "\n\n"; 1021 actual += "\n\n";
1017 } 1022 }
1018 1023
1019 for r in &refs.references { 1024 for (file_id, references) in refs.references {
1020 format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); 1025 for r in references {
1021 if let Some(access) = r.access { 1026 format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind);
1022 format_to!(actual, " {:?}", access); 1027 if let Some(access) = r.access {
1028 format_to!(actual, " {:?}", access);
1029 }
1030 actual += "\n";
1023 } 1031 }
1024 actual += "\n";
1025 } 1032 }
1026 expect.assert_eq(&actual) 1033 expect.assert_eq(&actual)
1027 } 1034 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 53d79333c..c3ae568c2 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,29 +1,30 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::{ 2use std::{
3 convert::TryInto, 3 convert::TryInto,
4 error::Error,
5 fmt::{self, Display}, 4 fmt::{self, Display},
6}; 5};
7 6
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 7use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 8use ide_db::{
9 base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt},
11 defs::{Definition, NameClass, NameRefClass}, 10 defs::{Definition, NameClass, NameRefClass},
11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use syntax::{ 14use syntax::{
15 algo::find_node_at_offset, 15 algo::find_node_at_offset,
16 ast::{self, NameOwner}, 16 ast::{self, NameOwner},
17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T,
18}; 18};
19use test_utils::mark; 19use test_utils::mark;
20use text_edit::TextEdit; 20use text_edit::TextEdit;
21 21
22use crate::{ 22use crate::{
23 references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, 23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange,
24 SourceChange, SourceFileEdit, TextRange, TextSize, 24 SourceFileEdit, TextRange, TextSize,
25}; 25};
26 26
27type RenameResult<T> = Result<T, RenameError>;
27#[derive(Debug)] 28#[derive(Debug)]
28pub struct RenameError(pub(crate) String); 29pub struct RenameError(pub(crate) String);
29 30
@@ -33,26 +34,30 @@ impl fmt::Display for RenameError {
33 } 34 }
34} 35}
35 36
36impl Error for RenameError {} 37macro_rules! format_err {
38 ($fmt:expr) => {RenameError(format!($fmt))};
39 ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))}
40}
41
42macro_rules! bail {
43 ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))}
44}
37 45
38pub(crate) fn prepare_rename( 46pub(crate) fn prepare_rename(
39 db: &RootDatabase, 47 db: &RootDatabase,
40 position: FilePosition, 48 position: FilePosition,
41) -> Result<RangeInfo<()>, RenameError> { 49) -> RenameResult<RangeInfo<()>> {
42 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
43 let source_file = sema.parse(position.file_id); 51 let source_file = sema.parse(position.file_id);
44 let syntax = source_file.syntax(); 52 let syntax = source_file.syntax();
45 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 53 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
46 rename_mod(&sema, position, module, "dummy") 54 rename_mod(&sema, position, module, "dummy")
47 } else if let Some(self_token) = 55 } else if let Some(self_token) =
48 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 56 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
49 { 57 {
50 rename_self_to_param(&sema, position, self_token, "dummy") 58 rename_self_to_param(&sema, position, self_token, "dummy")
51 } else { 59 } else {
52 let range = match find_all_refs(&sema, position, None) { 60 let RangeInfo { range, .. } = find_all_refs(&sema, position)?;
53 Some(RangeInfo { range, .. }) => range,
54 None => return Err(RenameError("No references found at position".to_string())),
55 };
56 Ok(RangeInfo::new(range, SourceChange::from(vec![]))) 61 Ok(RangeInfo::new(range, SourceChange::from(vec![])))
57 } 62 }
58 .map(|info| RangeInfo::new(info.range, ())) 63 .map(|info| RangeInfo::new(info.range, ()))
@@ -62,7 +67,7 @@ pub(crate) fn rename(
62 db: &RootDatabase, 67 db: &RootDatabase,
63 position: FilePosition, 68 position: FilePosition,
64 new_name: &str, 69 new_name: &str,
65) -> Result<RangeInfo<SourceChange>, RenameError> { 70) -> RenameResult<RangeInfo<SourceChange>> {
66 let sema = Semantics::new(db); 71 let sema = Semantics::new(db);
67 rename_with_semantics(&sema, position, new_name) 72 rename_with_semantics(&sema, position, new_name)
68} 73}
@@ -71,42 +76,18 @@ pub(crate) fn rename_with_semantics(
71 sema: &Semantics<RootDatabase>, 76 sema: &Semantics<RootDatabase>,
72 position: FilePosition, 77 position: FilePosition,
73 new_name: &str, 78 new_name: &str,
74) -> Result<RangeInfo<SourceChange>, RenameError> { 79) -> RenameResult<RangeInfo<SourceChange>> {
75 let is_lifetime_name = match lex_single_syntax_kind(new_name) {
76 Some(res) => match res {
77 (SyntaxKind::IDENT, _) => false,
78 (SyntaxKind::UNDERSCORE, _) => false,
79 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
80 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true,
81 (SyntaxKind::LIFETIME_IDENT, _) => {
82 return Err(RenameError(format!(
83 "Invalid name `{0}`: Cannot rename lifetime to {0}",
84 new_name
85 )))
86 }
87 (_, Some(syntax_error)) => {
88 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
89 }
90 (_, None) => {
91 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
92 }
93 },
94 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
95 };
96
97 let source_file = sema.parse(position.file_id); 80 let source_file = sema.parse(position.file_id);
98 let syntax = source_file.syntax(); 81 let syntax = source_file.syntax();
99 // this is here to prevent lifetime renames from happening on modules and self 82
100 if is_lifetime_name { 83 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
101 rename_reference(&sema, position, new_name, is_lifetime_name)
102 } else if let Some(module) = find_module_at_offset(&sema, position, syntax) {
103 rename_mod(&sema, position, module, new_name) 84 rename_mod(&sema, position, module, new_name)
104 } else if let Some(self_token) = 85 } else if let Some(self_token) =
105 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 86 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
106 { 87 {
107 rename_self_to_param(&sema, position, self_token, new_name) 88 rename_self_to_param(&sema, position, self_token, new_name)
108 } else { 89 } else {
109 rename_reference(&sema, position, new_name, is_lifetime_name) 90 rename_reference(&sema, position, new_name)
110 } 91 }
111} 92}
112 93
@@ -127,6 +108,33 @@ pub(crate) fn will_rename_file(
127 Some(change) 108 Some(change)
128} 109}
129 110
111#[derive(Debug, PartialEq)]
112enum IdentifierKind {
113 Ident,
114 Lifetime,
115 ToSelf,
116 Underscore,
117}
118
119fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
120 match lex_single_syntax_kind(new_name) {
121 Some(res) => match res {
122 (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident),
123 (T![_], _) => Ok(IdentifierKind::Underscore),
124 (T![self], _) => Ok(IdentifierKind::ToSelf),
125 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
126 Ok(IdentifierKind::Lifetime)
127 }
128 (SyntaxKind::LIFETIME_IDENT, _) => {
129 bail!("Invalid name `{0}`: Cannot rename lifetime to {0}", new_name)
130 }
131 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
132 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
133 },
134 None => bail!("Invalid name `{}`: not an identifier", new_name),
135 }
136}
137
130fn find_module_at_offset( 138fn find_module_at_offset(
131 sema: &Semantics<RootDatabase>, 139 sema: &Semantics<RootDatabase>,
132 position: FilePosition, 140 position: FilePosition,
@@ -155,39 +163,54 @@ fn find_module_at_offset(
155 Some(module) 163 Some(module)
156} 164}
157 165
158fn source_edit_from_reference( 166fn find_all_refs(
167 sema: &Semantics<RootDatabase>,
168 position: FilePosition,
169) -> RenameResult<RangeInfo<ReferenceSearchResult>> {
170 crate::references::find_all_refs(sema, position, None)
171 .ok_or_else(|| format_err!("No references found at position"))
172}
173
174fn source_edit_from_references(
159 sema: &Semantics<RootDatabase>, 175 sema: &Semantics<RootDatabase>,
160 reference: Reference, 176 file_id: FileId,
177 references: &[FileReference],
161 new_name: &str, 178 new_name: &str,
162) -> SourceFileEdit { 179) -> SourceFileEdit {
163 let mut replacement_text = String::new(); 180 let mut edit = TextEdit::builder();
164 let range = match reference.kind { 181 for reference in references {
165 ReferenceKind::FieldShorthandForField => { 182 let mut replacement_text = String::new();
166 mark::hit!(test_rename_struct_field_for_shorthand); 183 let range = match reference.kind {
167 replacement_text.push_str(new_name); 184 ReferenceKind::FieldShorthandForField => {
168 replacement_text.push_str(": "); 185 mark::hit!(test_rename_struct_field_for_shorthand);
169 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) 186 replacement_text.push_str(new_name);
170 } 187 replacement_text.push_str(": ");
171 ReferenceKind::FieldShorthandForLocal => { 188 TextRange::new(reference.range.start(), reference.range.start())
172 mark::hit!(test_rename_local_for_field_shorthand); 189 }
173 replacement_text.push_str(": "); 190 ReferenceKind::FieldShorthandForLocal => {
174 replacement_text.push_str(new_name); 191 mark::hit!(test_rename_local_for_field_shorthand);
175 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 192 replacement_text.push_str(": ");
176 } 193 replacement_text.push_str(new_name);
177 ReferenceKind::RecordFieldExprOrPat => { 194 TextRange::new(reference.range.end(), reference.range.end())
178 mark::hit!(test_rename_field_expr_pat); 195 }
179 replacement_text.push_str(new_name); 196 ReferenceKind::RecordFieldExprOrPat => {
180 edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name) 197 mark::hit!(test_rename_field_expr_pat);
181 } 198 replacement_text.push_str(new_name);
182 _ => { 199 edit_text_range_for_record_field_expr_or_pat(
183 replacement_text.push_str(new_name); 200 sema,
184 reference.file_range.range 201 FileRange { file_id, range: reference.range },
185 } 202 new_name,
186 }; 203 )
187 SourceFileEdit { 204 }
188 file_id: reference.file_range.file_id, 205 _ => {
189 edit: TextEdit::replace(range, replacement_text), 206 replacement_text.push_str(new_name);
207 reference.range
208 }
209 };
210 edit.replace(range, replacement_text);
190 } 211 }
212
213 SourceFileEdit { file_id, edit: edit.finish() }
191} 214}
192 215
193fn edit_text_range_for_record_field_expr_or_pat( 216fn edit_text_range_for_record_field_expr_or_pat(
@@ -223,7 +246,10 @@ fn rename_mod(
223 position: FilePosition, 246 position: FilePosition,
224 module: Module, 247 module: Module,
225 new_name: &str, 248 new_name: &str,
226) -> Result<RangeInfo<SourceChange>, RenameError> { 249) -> RenameResult<RangeInfo<SourceChange>> {
250 if IdentifierKind::Ident != check_identifier(new_name)? {
251 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
252 }
227 let mut source_file_edits = Vec::new(); 253 let mut source_file_edits = Vec::new();
228 let mut file_system_edits = Vec::new(); 254 let mut file_system_edits = Vec::new();
229 255
@@ -254,12 +280,10 @@ fn rename_mod(
254 source_file_edits.push(edit); 280 source_file_edits.push(edit);
255 } 281 }
256 282
257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 283 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
258 .ok_or_else(|| RenameError("No references found at position".to_string()))?; 284 let ref_edits = refs.references().iter().map(|(&file_id, references)| {
259 let ref_edits = refs 285 source_edit_from_references(sema, file_id, references, new_name)
260 .references 286 });
261 .into_iter()
262 .map(|reference| source_edit_from_reference(sema, reference, new_name));
263 source_file_edits.extend(ref_edits); 287 source_file_edits.extend(ref_edits);
264 288
265 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 289 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -274,27 +298,26 @@ fn rename_to_self(
274 298
275 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) 299 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset)
276 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast))) 300 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
277 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 301 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
278 let param_range = fn_ast 302 let param_range = fn_ast
279 .param_list() 303 .param_list()
280 .and_then(|p| p.params().next()) 304 .and_then(|p| p.params().next())
281 .ok_or_else(|| RenameError("Method has no parameters".to_string()))? 305 .ok_or_else(|| format_err!("Method has no parameters"))?
282 .syntax() 306 .syntax()
283 .text_range(); 307 .text_range();
284 if !param_range.contains(position.offset) { 308 if !param_range.contains(position.offset) {
285 return Err(RenameError("Only the first parameter can be self".to_string())); 309 bail!("Only the first parameter can be self");
286 } 310 }
287 311
288 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) 312 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset)
289 .and_then(|def| sema.to_def(&def)) 313 .and_then(|def| sema.to_def(&def))
290 .ok_or_else(|| RenameError("No impl block found for function".to_string()))?; 314 .ok_or_else(|| format_err!("No impl block found for function"))?;
291 if fn_def.self_param(sema.db).is_some() { 315 if fn_def.self_param(sema.db).is_some() {
292 return Err(RenameError("Method already has a self parameter".to_string())); 316 bail!("Method already has a self parameter");
293 } 317 }
294 318
295 let params = fn_def.assoc_fn_params(sema.db); 319 let params = fn_def.assoc_fn_params(sema.db);
296 let first_param = 320 let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?;
297 params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?;
298 let first_param_ty = first_param.ty(); 321 let first_param_ty = first_param.ty();
299 let impl_ty = impl_block.target_ty(sema.db); 322 let impl_ty = impl_block.target_ty(sema.db);
300 let (ty, self_param) = if impl_ty.remove_ref().is_some() { 323 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
@@ -307,23 +330,17 @@ fn rename_to_self(
307 }; 330 };
308 331
309 if ty != impl_ty { 332 if ty != impl_ty {
310 return Err(RenameError("Parameter type differs from impl block type".to_string())); 333 bail!("Parameter type differs from impl block type");
311 } 334 }
312 335
313 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 336 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
314 .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
315
316 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
317 .into_iter()
318 .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
319
320 if param_ref.is_empty() {
321 return Err(RenameError("Parameter to rename not found".to_string()));
322 }
323 337
324 let mut edits = usages 338 let mut edits = refs
325 .into_iter() 339 .references()
326 .map(|reference| source_edit_from_reference(sema, reference, "self")) 340 .iter()
341 .map(|(&file_id, references)| {
342 source_edit_from_references(sema, file_id, references, "self")
343 })
327 .collect::<Vec<_>>(); 344 .collect::<Vec<_>>();
328 345
329 edits.push(SourceFileEdit { 346 edits.push(SourceFileEdit {
@@ -367,12 +384,22 @@ fn rename_self_to_param(
367 self_token: SyntaxToken, 384 self_token: SyntaxToken,
368 new_name: &str, 385 new_name: &str,
369) -> Result<RangeInfo<SourceChange>, RenameError> { 386) -> Result<RangeInfo<SourceChange>, RenameError> {
387 let ident_kind = check_identifier(new_name)?;
388 match ident_kind {
389 IdentifierKind::Lifetime => bail!("Invalid name `{}`: not an identifier", new_name),
390 IdentifierKind::ToSelf => {
391 // no-op
392 mark::hit!(rename_self_to_self);
393 return Ok(RangeInfo::new(self_token.text_range(), SourceChange::default()));
394 }
395 _ => (),
396 }
370 let source_file = sema.parse(position.file_id); 397 let source_file = sema.parse(position.file_id);
371 let syn = source_file.syntax(); 398 let syn = source_file.syntax();
372 399
373 let text = sema.db.file_text(position.file_id); 400 let text = sema.db.file_text(position.file_id);
374 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) 401 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
375 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 402 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
376 let search_range = fn_def.syntax().text_range(); 403 let search_range = fn_def.syntax().text_range();
377 404
378 let mut edits: Vec<SourceFileEdit> = vec![]; 405 let mut edits: Vec<SourceFileEdit> = vec![];
@@ -382,12 +409,10 @@ fn rename_self_to_param(
382 if !search_range.contains_inclusive(offset) { 409 if !search_range.contains_inclusive(offset) {
383 continue; 410 continue;
384 } 411 }
385 if let Some(ref usage) = 412 if let Some(ref usage) = syn.token_at_offset(offset).find(|t| t.kind() == T![self]) {
386 syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
387 {
388 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) { 413 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) {
389 text_edit_from_self_param(syn, self_param, new_name) 414 text_edit_from_self_param(syn, self_param, new_name)
390 .ok_or_else(|| RenameError("No target type found".to_string()))? 415 .ok_or_else(|| format_err!("No target type found"))?
391 } else { 416 } else {
392 TextEdit::replace(usage.text_range(), String::from(new_name)) 417 TextEdit::replace(usage.text_range(), String::from(new_name))
393 }; 418 };
@@ -395,6 +420,10 @@ fn rename_self_to_param(
395 } 420 }
396 } 421 }
397 422
423 if edits.len() > 1 && ident_kind == IdentifierKind::Underscore {
424 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
425 }
426
398 let range = ast::SelfParam::cast(self_token.parent()) 427 let range = ast::SelfParam::cast(self_token.parent())
399 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 428 .map_or(self_token.text_range(), |p| p.syntax().text_range());
400 429
@@ -405,35 +434,43 @@ fn rename_reference(
405 sema: &Semantics<RootDatabase>, 434 sema: &Semantics<RootDatabase>,
406 position: FilePosition, 435 position: FilePosition,
407 new_name: &str, 436 new_name: &str,
408 is_lifetime_name: bool,
409) -> Result<RangeInfo<SourceChange>, RenameError> { 437) -> Result<RangeInfo<SourceChange>, RenameError> {
410 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { 438 let ident_kind = check_identifier(new_name)?;
411 Some(range_info) => range_info, 439 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
412 None => return Err(RenameError("No references found at position".to_string())), 440
413 }; 441 match (ident_kind, &refs.declaration.kind) {
414 442 (IdentifierKind::ToSelf, ReferenceKind::Lifetime)
415 match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) { 443 | (IdentifierKind::Underscore, ReferenceKind::Lifetime)
416 (true, false) => { 444 | (IdentifierKind::Ident, ReferenceKind::Lifetime) => {
417 return Err(RenameError(format!( 445 mark::hit!(rename_not_a_lifetime_ident_ref);
418 "Invalid name `{}`: not a lifetime identifier", 446 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
419 new_name
420 )))
421 } 447 }
422 (false, true) => { 448 (IdentifierKind::Lifetime, ReferenceKind::Lifetime) => mark::hit!(rename_lifetime),
423 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) 449 (IdentifierKind::Lifetime, _) => {
450 mark::hit!(rename_not_an_ident_ref);
451 bail!("Invalid name `{}`: not an identifier", new_name)
424 } 452 }
425 _ => (), 453 (IdentifierKind::ToSelf, ReferenceKind::SelfKw) => {
454 unreachable!("rename_self_to_param should've been called instead")
455 }
456 (IdentifierKind::ToSelf, _) => {
457 mark::hit!(rename_to_self);
458 return rename_to_self(sema, position);
459 }
460 (IdentifierKind::Underscore, _) if !refs.references.is_empty() => {
461 mark::hit!(rename_underscore_multiple);
462 bail!("Cannot rename reference to `_` as it is being referenced multiple times")
463 }
464 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident),
426 } 465 }
427 466
428 let edit = refs 467 let edit = refs
429 .into_iter() 468 .into_iter()
430 .map(|reference| source_edit_from_reference(sema, reference, new_name)) 469 .map(|(file_id, references)| {
470 source_edit_from_references(sema, file_id, &references, new_name)
471 })
431 .collect::<Vec<_>>(); 472 .collect::<Vec<_>>();
432 473
433 if edit.is_empty() {
434 return Err(RenameError("No references found at position".to_string()));
435 }
436
437 Ok(RangeInfo::new(range, SourceChange::from(edit))) 474 Ok(RangeInfo::new(range, SourceChange::from(edit)))
438} 475}
439 476
@@ -462,9 +499,11 @@ mod tests {
462 text_edit_builder.replace(indel.delete, indel.insert); 499 text_edit_builder.replace(indel.delete, indel.insert);
463 } 500 }
464 } 501 }
465 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); 502 if let Some(file_id) = file_id {
466 text_edit_builder.finish().apply(&mut result); 503 let mut result = analysis.file_text(file_id).unwrap().to_string();
467 assert_eq_text!(ra_fixture_after, &*result); 504 text_edit_builder.finish().apply(&mut result);
505 assert_eq_text!(ra_fixture_after, &*result);
506 }
468 } 507 }
469 Err(err) => { 508 Err(err) => {
470 if ra_fixture_after.starts_with("error:") { 509 if ra_fixture_after.starts_with("error:") {
@@ -530,6 +569,7 @@ mod tests {
530 569
531 #[test] 570 #[test]
532 fn test_rename_to_invalid_identifier_lifetime() { 571 fn test_rename_to_invalid_identifier_lifetime() {
572 mark::check!(rename_not_an_ident_ref);
533 check( 573 check(
534 "'foo", 574 "'foo",
535 r#"fn main() { let i$0 = 1; }"#, 575 r#"fn main() { let i$0 = 1; }"#,
@@ -539,6 +579,7 @@ mod tests {
539 579
540 #[test] 580 #[test]
541 fn test_rename_to_invalid_identifier_lifetime2() { 581 fn test_rename_to_invalid_identifier_lifetime2() {
582 mark::check!(rename_not_a_lifetime_ident_ref);
542 check( 583 check(
543 "foo", 584 "foo",
544 r#"fn main<'a>(_: &'a$0 ()) {}"#, 585 r#"fn main<'a>(_: &'a$0 ()) {}"#,
@@ -547,7 +588,27 @@ mod tests {
547 } 588 }
548 589
549 #[test] 590 #[test]
591 fn test_rename_to_underscore_invalid() {
592 mark::check!(rename_underscore_multiple);
593 check(
594 "_",
595 r#"fn main(foo$0: ()) {foo;}"#,
596 "error: Cannot rename reference to `_` as it is being referenced multiple times",
597 );
598 }
599
600 #[test]
601 fn test_rename_mod_invalid() {
602 check(
603 "'foo",
604 r#"mod foo$0 {}"#,
605 "error: Invalid name `'foo`: cannot rename module to 'foo",
606 );
607 }
608
609 #[test]
550 fn test_rename_for_local() { 610 fn test_rename_for_local() {
611 mark::check!(rename_ident);
551 check( 612 check(
552 "k", 613 "k",
553 r#" 614 r#"
@@ -945,7 +1006,7 @@ use crate::foo$0::FooContent;
945//- /lib.rs 1006//- /lib.rs
946mod fo$0o; 1007mod fo$0o;
947//- /foo/mod.rs 1008//- /foo/mod.rs
948// emtpy 1009// empty
949"#, 1010"#,
950 expect![[r#" 1011 expect![[r#"
951 RangeInfo { 1012 RangeInfo {
@@ -995,7 +1056,7 @@ mod fo$0o;
995mod outer { mod fo$0o; } 1056mod outer { mod fo$0o; }
996 1057
997//- /outer/foo.rs 1058//- /outer/foo.rs
998// emtpy 1059// empty
999"#, 1060"#,
1000 expect![[r#" 1061 expect![[r#"
1001 RangeInfo { 1062 RangeInfo {
@@ -1178,6 +1239,7 @@ fn foo(f: foo::Foo) {
1178 1239
1179 #[test] 1240 #[test]
1180 fn test_parameter_to_self() { 1241 fn test_parameter_to_self() {
1242 mark::check!(rename_to_self);
1181 check( 1243 check(
1182 "self", 1244 "self",
1183 r#" 1245 r#"
@@ -1481,6 +1543,7 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1481 1543
1482 #[test] 1544 #[test]
1483 fn test_rename_lifetimes() { 1545 fn test_rename_lifetimes() {
1546 mark::check!(rename_lifetime);
1484 check( 1547 check(
1485 "'yeeee", 1548 "'yeeee",
1486 r#" 1549 r#"
@@ -1565,4 +1628,24 @@ fn foo<'a>() -> &'a () {
1565"#, 1628"#,
1566 ) 1629 )
1567 } 1630 }
1631
1632 #[test]
1633 fn test_self_to_self() {
1634 mark::check!(rename_self_to_self);
1635 check(
1636 "self",
1637 r#"
1638struct Foo;
1639impl Foo {
1640 fn foo(self$0) {}
1641}
1642"#,
1643 r#"
1644struct Foo;
1645impl Foo {
1646 fn foo(self) {}
1647}
1648"#,
1649 )
1650 }
1568} 1651}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 557563d7e..f5ee7de86 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -3,7 +3,7 @@ use std::fmt;
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::RootDatabase; 6use ide_db::{defs::Definition, RootDatabase};
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, 9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner},
@@ -96,21 +96,26 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 97 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 98 let source_file = sema.parse(file_id);
99 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() 99 source_file
100} 100 .syntax()
101 101 .descendants()
102pub(crate) fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 102 .filter_map(|item| {
103 let runnable_item = match_ast! { 103 let runnable = match_ast! {
104 match (item.clone()) { 104 match item {
105 ast::Fn(func) => { 105 ast::Fn(func) => {
106 let def = sema.to_def(&func)?; 106 let def = sema.to_def(&func)?;
107 runnable_fn(sema, def) 107 runnable_fn(&sema, def)
108 }, 108 },
109 ast::Module(it) => runnable_mod(sema, it), 109 ast::Module(it) => runnable_mod(&sema, it),
110 _ => None, 110 _ => None,
111 } 111 }
112 }; 112 };
113 runnable_item.or_else(|| runnable_doctest(sema, item)) 113 runnable.or_else(|| match doc_owner_to_def(&sema, item)? {
114 Definition::ModuleDef(def) => module_def_doctest(&sema, def),
115 _ => None,
116 })
117 })
118 .collect()
114} 119}
115 120
116pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 121pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -145,20 +150,49 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) ->
145 Some(Runnable { nav, kind, cfg }) 150 Some(Runnable { nav, kind, cfg })
146} 151}
147 152
148fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 153pub(crate) fn runnable_mod(
149 match_ast! { 154 sema: &Semantics<RootDatabase>,
155 module: ast::Module,
156) -> Option<Runnable> {
157 if !has_test_function_or_multiple_test_submodules(&module) {
158 return None;
159 }
160 let module_def = sema.to_def(&module)?;
161
162 let path = module_def
163 .path_to_root(sema.db)
164 .into_iter()
165 .rev()
166 .filter_map(|it| it.name(sema.db))
167 .join("::");
168
169 let def = sema.to_def(&module)?;
170 let attrs = def.attrs(sema.db);
171 let cfg = attrs.cfg();
172 let nav = module_def.to_nav(sema.db);
173 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
174}
175
176// FIXME: figure out a proper API here.
177pub(crate) fn doc_owner_to_def(
178 sema: &Semantics<RootDatabase>,
179 item: SyntaxNode,
180) -> Option<Definition> {
181 let res: hir::ModuleDef = match_ast! {
150 match item { 182 match item {
151 ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 183 ast::SourceFile(it) => sema.scope(&item).module()?.into(),
152 ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 184 ast::Fn(it) => sema.to_def(&it)?.into(),
153 ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 185 ast::Struct(it) => sema.to_def(&it)?.into(),
154 ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 186 ast::Enum(it) => sema.to_def(&it)?.into(),
155 ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 187 ast::Union(it) => sema.to_def(&it)?.into(),
156 ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 188 ast::Trait(it) => sema.to_def(&it)?.into(),
157 ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 189 ast::Const(it) => sema.to_def(&it)?.into(),
158 ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 190 ast::Static(it) => sema.to_def(&it)?.into(),
159 _ => None, 191 ast::TypeAlias(it) => sema.to_def(&it)?.into(),
192 _ => return None,
160 } 193 }
161 } 194 };
195 Some(Definition::ModuleDef(res))
162} 196}
163 197
164fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { 198fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
@@ -253,26 +287,6 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
253 }) 287 })
254} 288}
255 289
256fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
257 if !has_test_function_or_multiple_test_submodules(&module) {
258 return None;
259 }
260 let module_def = sema.to_def(&module)?;
261
262 let path = module_def
263 .path_to_root(sema.db)
264 .into_iter()
265 .rev()
266 .filter_map(|it| it.name(sema.db))
267 .join("::");
268
269 let def = sema.to_def(&module)?;
270 let attrs = def.attrs(sema.db);
271 let cfg = attrs.cfg();
272 let nav = module_def.to_nav(sema.db);
273 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
274}
275
276// We could create runnables for modules with number_of_test_submodules > 0, 290// We could create runnables for modules with number_of_test_submodules > 0,
277// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already 291// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
278fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { 292fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index ba0085244..f2d4da78d 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -1,35 +1,39 @@
1pub(crate) mod tags;
2
3mod highlights;
4mod injector;
5
6mod highlight;
1mod format; 7mod format;
2mod html;
3mod injection;
4mod macro_rules; 8mod macro_rules;
5pub(crate) mod tags; 9mod inject;
10
11mod html;
6#[cfg(test)] 12#[cfg(test)]
7mod tests; 13mod tests;
8 14
9use hir::{AsAssocItem, Local, Name, Semantics, VariantDef}; 15use hir::{Name, Semantics};
10use ide_db::{ 16use ide_db::RootDatabase;
11 defs::{Definition, NameClass, NameRefClass},
12 RootDatabase,
13};
14use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
15use syntax::{ 18use syntax::{
16 ast::{self, HasFormatSpecifier}, 19 ast::{self, HasFormatSpecifier},
17 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 20 AstNode, AstToken, Direction, NodeOrToken,
18 SyntaxKind::{self, *}, 21 SyntaxKind::*,
19 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, 22 SyntaxNode, TextRange, WalkEvent, T,
20}; 23};
21 24
22use crate::{ 25use crate::{
23 syntax_highlighting::{ 26 syntax_highlighting::{
24 format::FormatStringHighlighter, macro_rules::MacroRulesHighlighter, tags::Highlight, 27 format::highlight_format_string, highlights::Highlights,
28 macro_rules::MacroRulesHighlighter, tags::Highlight,
25 }, 29 },
26 FileId, HighlightModifier, HighlightTag, SymbolKind, 30 FileId, HlMod, HlTag, SymbolKind,
27}; 31};
28 32
29pub(crate) use html::highlight_as_html; 33pub(crate) use html::highlight_as_html;
30 34
31#[derive(Debug, Clone)] 35#[derive(Debug, Clone, Copy)]
32pub struct HighlightedRange { 36pub struct HlRange {
33 pub range: TextRange, 37 pub range: TextRange,
34 pub highlight: Highlight, 38 pub highlight: Highlight,
35 pub binding_hash: Option<u64>, 39 pub binding_hash: Option<u64>,
@@ -49,7 +53,7 @@ pub(crate) fn highlight(
49 file_id: FileId, 53 file_id: FileId,
50 range_to_highlight: Option<TextRange>, 54 range_to_highlight: Option<TextRange>,
51 syntactic_name_ref_highlighting: bool, 55 syntactic_name_ref_highlighting: bool,
52) -> Vec<HighlightedRange> { 56) -> Vec<HlRange> {
53 let _p = profile::span("highlight"); 57 let _p = profile::span("highlight");
54 let sema = Semantics::new(db); 58 let sema = Semantics::new(db);
55 59
@@ -68,28 +72,30 @@ pub(crate) fn highlight(
68 } 72 }
69 }; 73 };
70 74
75 let mut hl = highlights::Highlights::new(range_to_highlight);
76 traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting);
77 hl.to_vec()
78}
79
80fn traverse(
81 hl: &mut Highlights,
82 sema: &Semantics<RootDatabase>,
83 root: &SyntaxNode,
84 range_to_highlight: TextRange,
85 syntactic_name_ref_highlighting: bool,
86) {
71 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 87 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
72 // We use a stack for the DFS traversal below.
73 // When we leave a node, the we use it to flatten the highlighted ranges.
74 let mut stack = HighlightedRangeStack::new();
75 88
76 let mut current_macro_call: Option<ast::MacroCall> = None; 89 let mut current_macro_call: Option<ast::MacroCall> = None;
77 let mut current_macro_rules: Option<ast::MacroRules> = None; 90 let mut current_macro_rules: Option<ast::MacroRules> = None;
78 let mut format_string_highlighter = FormatStringHighlighter::default();
79 let mut macro_rules_highlighter = MacroRulesHighlighter::default(); 91 let mut macro_rules_highlighter = MacroRulesHighlighter::default();
80 let mut inside_attribute = false; 92 let mut inside_attribute = false;
81 93
82 // Walk all nodes, keeping track of whether we are inside a macro or not. 94 // Walk all nodes, keeping track of whether we are inside a macro or not.
83 // If in macro, expand it first and highlight the expanded code. 95 // If in macro, expand it first and highlight the expanded code.
84 for event in root.preorder_with_tokens() { 96 for event in root.preorder_with_tokens() {
85 match &event {
86 WalkEvent::Enter(_) => stack.push(),
87 WalkEvent::Leave(_) => stack.pop(),
88 };
89
90 let event_range = match &event { 97 let event_range = match &event {
91 WalkEvent::Enter(it) => it.text_range(), 98 WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(),
92 WalkEvent::Leave(it) => it.text_range(),
93 }; 99 };
94 100
95 // Element outside of the viewport, no need to highlight 101 // Element outside of the viewport, no need to highlight
@@ -101,9 +107,9 @@ pub(crate) fn highlight(
101 match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { 107 match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
102 WalkEvent::Enter(Some(mc)) => { 108 WalkEvent::Enter(Some(mc)) => {
103 if let Some(range) = macro_call_range(&mc) { 109 if let Some(range) = macro_call_range(&mc) {
104 stack.add(HighlightedRange { 110 hl.add(HlRange {
105 range, 111 range,
106 highlight: HighlightTag::Symbol(SymbolKind::Macro).into(), 112 highlight: HlTag::Symbol(SymbolKind::Macro).into(),
107 binding_hash: None, 113 binding_hash: None,
108 }); 114 });
109 } 115 }
@@ -113,7 +119,6 @@ pub(crate) fn highlight(
113 WalkEvent::Leave(Some(mc)) => { 119 WalkEvent::Leave(Some(mc)) => {
114 assert_eq!(current_macro_call, Some(mc)); 120 assert_eq!(current_macro_call, Some(mc));
115 current_macro_call = None; 121 current_macro_call = None;
116 format_string_highlighter = FormatStringHighlighter::default();
117 } 122 }
118 _ => (), 123 _ => (),
119 } 124 }
@@ -131,33 +136,24 @@ pub(crate) fn highlight(
131 } 136 }
132 _ => (), 137 _ => (),
133 } 138 }
134
135 match &event { 139 match &event {
136 // Check for Rust code in documentation
137 WalkEvent::Leave(NodeOrToken::Node(node)) => {
138 if ast::Attr::can_cast(node.kind()) {
139 inside_attribute = false
140 }
141 if let Some((doctest, range_mapping, new_comments)) =
142 injection::extract_doc_comments(node)
143 {
144 injection::highlight_doc_comment(
145 doctest,
146 range_mapping,
147 new_comments,
148 &mut stack,
149 );
150 }
151 }
152 WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { 140 WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
153 inside_attribute = true 141 inside_attribute = true
154 } 142 }
143 WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
144 inside_attribute = false
145 }
155 _ => (), 146 _ => (),
156 } 147 }
157 148
158 let element = match event { 149 let element = match event {
159 WalkEvent::Enter(it) => it, 150 WalkEvent::Enter(it) => it,
160 WalkEvent::Leave(_) => continue, 151 WalkEvent::Leave(it) => {
152 if let Some(node) = it.as_node() {
153 inject::doc_comment(hl, node);
154 }
155 continue;
156 }
161 }; 157 };
162 158
163 let range = element.text_range(); 159 let range = element.text_range();
@@ -177,8 +173,6 @@ pub(crate) fn highlight(
177 let token = sema.descend_into_macros(token.clone()); 173 let token = sema.descend_into_macros(token.clone());
178 let parent = token.parent(); 174 let parent = token.parent();
179 175
180 format_string_highlighter.check_for_format_string(&parent);
181
182 // We only care Name and Name_ref 176 // We only care Name and Name_ref
183 match (token.kind(), parent.kind()) { 177 match (token.kind(), parent.kind()) {
184 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), 178 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
@@ -191,213 +185,45 @@ pub(crate) fn highlight(
191 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { 185 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
192 if token.is_raw() { 186 if token.is_raw() {
193 let expanded = element_to_highlight.as_token().unwrap().clone(); 187 let expanded = element_to_highlight.as_token().unwrap().clone();
194 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() { 188 if inject::ra_fixture(hl, &sema, token, expanded).is_some() {
195 continue; 189 continue;
196 } 190 }
197 } 191 }
198 } 192 }
199 193
200 if let Some((mut highlight, binding_hash)) = highlight_element( 194 if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) {
195 continue;
196 }
197
198 if let Some((mut highlight, binding_hash)) = highlight::element(
201 &sema, 199 &sema,
202 &mut bindings_shadow_count, 200 &mut bindings_shadow_count,
203 syntactic_name_ref_highlighting, 201 syntactic_name_ref_highlighting,
204 element_to_highlight.clone(), 202 element_to_highlight.clone(),
205 ) { 203 ) {
206 if inside_attribute { 204 if inside_attribute {
207 highlight = highlight | HighlightModifier::Attribute; 205 highlight = highlight | HlMod::Attribute;
208 }
209
210 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() {
211 stack.add(HighlightedRange { range, highlight, binding_hash });
212 } 206 }
213 207
214 if let Some(string) = 208 hl.add(HlRange { range, highlight, binding_hash });
215 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
216 {
217 format_string_highlighter.highlight_format_string(&mut stack, &string, range);
218 // Highlight escape sequences
219 if let Some(char_ranges) = string.char_ranges() {
220 stack.push();
221 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
222 if string.text()[piece_range.start().into()..].starts_with('\\') {
223 stack.add(HighlightedRange {
224 range: piece_range + range.start(),
225 highlight: HighlightTag::EscapeSequence.into(),
226 binding_hash: None,
227 });
228 }
229 }
230 stack.pop_and_inject(None);
231 }
232 }
233 } 209 }
234 }
235
236 stack.flattened()
237}
238
239#[derive(Debug)]
240struct HighlightedRangeStack {
241 stack: Vec<Vec<HighlightedRange>>,
242}
243
244/// We use a stack to implement the flattening logic for the highlighted
245/// syntax ranges.
246impl HighlightedRangeStack {
247 fn new() -> Self {
248 Self { stack: vec![Vec::new()] }
249 }
250
251 fn push(&mut self) {
252 self.stack.push(Vec::new());
253 }
254
255 /// Flattens the highlighted ranges.
256 ///
257 /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
258 /// 1) parent-range: Attribute [0, 23)
259 /// 2) child-range: String [16, 21)
260 ///
261 /// The following code implements the flattening, for our example this results to:
262 /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
263 fn pop(&mut self) {
264 let children = self.stack.pop().unwrap();
265 let prev = self.stack.last_mut().unwrap();
266 let needs_flattening = !children.is_empty()
267 && !prev.is_empty()
268 && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
269 if !needs_flattening {
270 prev.extend(children);
271 } else {
272 let mut parent = prev.pop().unwrap();
273 for ele in children {
274 assert!(parent.range.contains_range(ele.range));
275
276 let cloned = Self::intersect(&mut parent, &ele);
277 if !parent.range.is_empty() {
278 prev.push(parent);
279 }
280 prev.push(ele);
281 parent = cloned;
282 }
283 if !parent.range.is_empty() {
284 prev.push(parent);
285 }
286 }
287 }
288
289 /// Intersects the `HighlightedRange` `parent` with `child`.
290 /// `parent` is mutated in place, becoming the range before `child`.
291 /// Returns the range (of the same type as `parent`) *after* `child`.
292 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
293 assert!(parent.range.contains_range(child.range));
294
295 let mut cloned = parent.clone();
296 parent.range = TextRange::new(parent.range.start(), child.range.start());
297 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
298 210
299 cloned 211 if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) {
300 } 212 highlight_format_string(hl, &string, range);
301 213 // Highlight escape sequences
302 /// Remove the `HighlightRange` of `parent` that's currently covered by `child`. 214 if let Some(char_ranges) = string.char_ranges() {
303 fn intersect_partial(parent: &mut HighlightedRange, child: &HighlightedRange) { 215 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
304 assert!( 216 if string.text()[piece_range.start().into()..].starts_with('\\') {
305 parent.range.start() <= child.range.start() 217 hl.add(HlRange {
306 && parent.range.end() >= child.range.start() 218 range: piece_range + range.start(),
307 && child.range.end() > parent.range.end() 219 highlight: HlTag::EscapeSequence.into(),
308 ); 220 binding_hash: None,
309 221 });
310 parent.range = TextRange::new(parent.range.start(), child.range.start());
311 }
312
313 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
314 /// can only modify the last range currently on the stack.
315 /// Can be used to do injections that span multiple ranges, like the
316 /// doctest injection below.
317 /// If `overwrite_parent` is non-optional, the highlighting of the parent range
318 /// is overwritten with the argument.
319 ///
320 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
321 /// latter is computationally more expensive.
322 fn pop_and_inject(&mut self, overwrite_parent: Option<Highlight>) {
323 let mut children = self.stack.pop().unwrap();
324 let prev = self.stack.last_mut().unwrap();
325 children.sort_by_key(|range| range.range.start());
326 prev.sort_by_key(|range| range.range.start());
327
328 for child in children {
329 if let Some(idx) =
330 prev.iter().position(|parent| parent.range.contains_range(child.range))
331 {
332 if let Some(tag) = overwrite_parent {
333 prev[idx].highlight = tag;
334 }
335
336 let cloned = Self::intersect(&mut prev[idx], &child);
337 let insert_idx = if prev[idx].range.is_empty() {
338 prev.remove(idx);
339 idx
340 } else {
341 idx + 1
342 };
343 prev.insert(insert_idx, child);
344 if !cloned.range.is_empty() {
345 prev.insert(insert_idx + 1, cloned);
346 }
347 } else {
348 let maybe_idx =
349 prev.iter().position(|parent| parent.range.contains(child.range.start()));
350 match (overwrite_parent, maybe_idx) {
351 (Some(_), Some(idx)) => {
352 Self::intersect_partial(&mut prev[idx], &child);
353 let insert_idx = if prev[idx].range.is_empty() {
354 prev.remove(idx);
355 idx
356 } else {
357 idx + 1
358 };
359 prev.insert(insert_idx, child);
360 }
361 (_, None) => {
362 let idx = prev
363 .binary_search_by_key(&child.range.start(), |range| range.range.start())
364 .unwrap_or_else(|x| x);
365 prev.insert(idx, child);
366 }
367 _ => {
368 unreachable!("child range should be completely contained in parent range");
369 } 222 }
370 } 223 }
371 } 224 }
372 } 225 }
373 } 226 }
374
375 fn add(&mut self, range: HighlightedRange) {
376 self.stack
377 .last_mut()
378 .expect("during DFS traversal, the stack must not be empty")
379 .push(range)
380 }
381
382 fn flattened(mut self) -> Vec<HighlightedRange> {
383 assert_eq!(
384 self.stack.len(),
385 1,
386 "after DFS traversal, the stack should only contain a single element"
387 );
388 let mut res = self.stack.pop().unwrap();
389 res.sort_by_key(|range| range.range.start());
390 // Check that ranges are sorted and disjoint
391 for (left, right) in res.iter().zip(res.iter().skip(1)) {
392 assert!(
393 left.range.end() <= right.range.start(),
394 "left: {:#?}, right: {:#?}",
395 left,
396 right
397 );
398 }
399 res
400 }
401} 227}
402 228
403fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { 229fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
@@ -415,524 +241,3 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
415 241
416 Some(TextRange::new(range_start, range_end)) 242 Some(TextRange::new(range_start, range_end))
417} 243}
418
419/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
420fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
421 while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
422 if parent.kind() != *kind {
423 return false;
424 }
425
426 // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
427 // in the same pattern is unstable: rust-lang/rust#68354.
428 node = node.parent().unwrap().into();
429 kinds = rest;
430 }
431
432 // Only true if we matched all expected kinds
433 kinds.len() == 0
434}
435
436fn is_consumed_lvalue(
437 node: NodeOrToken<SyntaxNode, SyntaxToken>,
438 local: &Local,
439 db: &RootDatabase,
440) -> bool {
441 // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
442 parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db)
443}
444
445fn highlight_element(
446 sema: &Semantics<RootDatabase>,
447 bindings_shadow_count: &mut FxHashMap<Name, u32>,
448 syntactic_name_ref_highlighting: bool,
449 element: SyntaxElement,
450) -> Option<(Highlight, Option<u64>)> {
451 let db = sema.db;
452 let mut binding_hash = None;
453 let highlight: Highlight = match element.kind() {
454 FN => {
455 bindings_shadow_count.clear();
456 return None;
457 }
458
459 // Highlight definitions depending on the "type" of the definition.
460 NAME => {
461 let name = element.into_node().and_then(ast::Name::cast).unwrap();
462 let name_kind = NameClass::classify(sema, &name);
463
464 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
465 if let Some(name) = local.name(db) {
466 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
467 *shadow_count += 1;
468 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
469 }
470 };
471
472 match name_kind {
473 Some(NameClass::ExternCrate(_)) => HighlightTag::Symbol(SymbolKind::Module).into(),
474 Some(NameClass::Definition(def)) => {
475 highlight_def(db, def) | HighlightModifier::Definition
476 }
477 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
478 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
479 let mut h = HighlightTag::Symbol(SymbolKind::Field).into();
480 if let Definition::Field(field) = field_ref {
481 if let VariantDef::Union(_) = field.parent_def(db) {
482 h |= HighlightModifier::Unsafe;
483 }
484 }
485
486 h
487 }
488 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
489 }
490 }
491
492 // Highlight references like the definitions they resolve to
493 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
494 // even though we track whether we are in an attribute or not we still need this special case
495 // as otherwise we would emit unresolved references for name refs inside attributes
496 Highlight::from(HighlightTag::Symbol(SymbolKind::Function))
497 }
498 NAME_REF => {
499 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
500 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
501 match NameRefClass::classify(sema, &name_ref) {
502 Some(name_kind) => match name_kind {
503 NameRefClass::ExternCrate(_) => {
504 HighlightTag::Symbol(SymbolKind::Module).into()
505 }
506 NameRefClass::Definition(def) => {
507 if let Definition::Local(local) = &def {
508 if let Some(name) = local.name(db) {
509 let shadow_count =
510 bindings_shadow_count.entry(name.clone()).or_default();
511 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
512 }
513 };
514
515 let mut h = highlight_def(db, def);
516
517 if let Definition::Local(local) = &def {
518 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) {
519 h |= HighlightModifier::Consuming;
520 }
521 }
522
523 if let Some(parent) = name_ref.syntax().parent() {
524 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
525 if let Definition::Field(field) = def {
526 if let VariantDef::Union(_) = field.parent_def(db) {
527 h |= HighlightModifier::Unsafe;
528 }
529 }
530 }
531 }
532
533 h
534 }
535 NameRefClass::FieldShorthand { .. } => {
536 HighlightTag::Symbol(SymbolKind::Field).into()
537 }
538 },
539 None if syntactic_name_ref_highlighting => {
540 highlight_name_ref_by_syntax(name_ref, sema)
541 }
542 None => HighlightTag::UnresolvedReference.into(),
543 }
544 })
545 }
546
547 // Simple token-based highlighting
548 COMMENT => {
549 let comment = element.into_token().and_then(ast::Comment::cast)?;
550 let h = HighlightTag::Comment;
551 match comment.kind().doc {
552 Some(_) => h | HighlightModifier::Documentation,
553 None => h.into(),
554 }
555 }
556 STRING | BYTE_STRING => HighlightTag::StringLiteral.into(),
557 ATTR => HighlightTag::Attribute.into(),
558 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
559 BYTE => HighlightTag::ByteLiteral.into(),
560 CHAR => HighlightTag::CharLiteral.into(),
561 QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow,
562 LIFETIME => {
563 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
564
565 match NameClass::classify_lifetime(sema, &lifetime) {
566 Some(NameClass::Definition(def)) => {
567 highlight_def(db, def) | HighlightModifier::Definition
568 }
569 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
570 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
571 _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)),
572 },
573 _ => {
574 Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam))
575 | HighlightModifier::Definition
576 }
577 }
578 }
579 p if p.is_punct() => match p {
580 T![&] => {
581 let h = HighlightTag::Operator.into();
582 let is_unsafe = element
583 .parent()
584 .and_then(ast::RefExpr::cast)
585 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr))
586 .unwrap_or(false);
587 if is_unsafe {
588 h | HighlightModifier::Unsafe
589 } else {
590 h
591 }
592 }
593 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
594 HighlightTag::Operator.into()
595 }
596 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
597 HighlightTag::Symbol(SymbolKind::Macro).into()
598 }
599 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
600 HighlightTag::BuiltinType.into()
601 }
602 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
603 HighlightTag::Keyword.into()
604 }
605 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
606 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
607
608 let expr = prefix_expr.expr()?;
609 let ty = sema.type_of_expr(&expr)?;
610 if ty.is_raw_ptr() {
611 HighlightTag::Operator | HighlightModifier::Unsafe
612 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
613 HighlightTag::Operator.into()
614 } else {
615 HighlightTag::Punctuation.into()
616 }
617 }
618 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
619 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
620
621 let expr = prefix_expr.expr()?;
622 match expr {
623 ast::Expr::Literal(_) => HighlightTag::NumericLiteral,
624 _ => HighlightTag::Operator,
625 }
626 .into()
627 }
628 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
629 HighlightTag::Operator.into()
630 }
631 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => {
632 HighlightTag::Operator.into()
633 }
634 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
635 HighlightTag::Operator.into()
636 }
637 _ if element.parent().and_then(ast::RangePat::cast).is_some() => {
638 HighlightTag::Operator.into()
639 }
640 _ if element.parent().and_then(ast::RestPat::cast).is_some() => {
641 HighlightTag::Operator.into()
642 }
643 _ if element.parent().and_then(ast::Attr::cast).is_some() => {
644 HighlightTag::Attribute.into()
645 }
646 _ => HighlightTag::Punctuation.into(),
647 },
648
649 k if k.is_keyword() => {
650 let h = Highlight::new(HighlightTag::Keyword);
651 match k {
652 T![break]
653 | T![continue]
654 | T![else]
655 | T![if]
656 | T![loop]
657 | T![match]
658 | T![return]
659 | T![while]
660 | T![in] => h | HighlightModifier::ControlFlow,
661 T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow,
662 T![unsafe] => h | HighlightModifier::Unsafe,
663 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
664 T![self] => {
665 let self_param_is_mut = element
666 .parent()
667 .and_then(ast::SelfParam::cast)
668 .and_then(|p| p.mut_token())
669 .is_some();
670 let self_path = &element
671 .parent()
672 .as_ref()
673 .and_then(SyntaxNode::parent)
674 .and_then(ast::Path::cast)
675 .and_then(|p| sema.resolve_path(&p));
676 let mut h = HighlightTag::Symbol(SymbolKind::SelfParam).into();
677 if self_param_is_mut
678 || matches!(self_path,
679 Some(hir::PathResolution::Local(local))
680 if local.is_self(db)
681 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
682 )
683 {
684 h |= HighlightModifier::Mutable
685 }
686
687 if let Some(hir::PathResolution::Local(local)) = self_path {
688 if is_consumed_lvalue(element, &local, db) {
689 h |= HighlightModifier::Consuming;
690 }
691 }
692
693 h
694 }
695 T![ref] => element
696 .parent()
697 .and_then(ast::IdentPat::cast)
698 .and_then(|ident_pat| {
699 if sema.is_unsafe_ident_pat(&ident_pat) {
700 Some(HighlightModifier::Unsafe)
701 } else {
702 None
703 }
704 })
705 .map(|modifier| h | modifier)
706 .unwrap_or(h),
707 _ => h,
708 }
709 }
710
711 _ => return None,
712 };
713
714 return Some((highlight, binding_hash));
715
716 fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 {
717 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
718 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
719
720 let mut hasher = DefaultHasher::new();
721 x.hash(&mut hasher);
722 hasher.finish()
723 }
724
725 hash((name, shadow_count))
726 }
727}
728
729fn is_child_of_impl(element: &SyntaxElement) -> bool {
730 match element.parent() {
731 Some(e) => e.kind() == IMPL,
732 _ => false,
733 }
734}
735
736fn highlight_func_by_name_ref(
737 sema: &Semantics<RootDatabase>,
738 name_ref: &ast::NameRef,
739) -> Option<Highlight> {
740 let method_call = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
741 highlight_method_call(sema, &method_call)
742}
743
744fn highlight_method_call(
745 sema: &Semantics<RootDatabase>,
746 method_call: &ast::MethodCallExpr,
747) -> Option<Highlight> {
748 let func = sema.resolve_method_call(&method_call)?;
749 let mut h = HighlightTag::Symbol(SymbolKind::Function).into();
750 h |= HighlightModifier::Associated;
751 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
752 h |= HighlightModifier::Unsafe;
753 }
754 if let Some(self_param) = func.self_param(sema.db) {
755 match self_param.access(sema.db) {
756 hir::Access::Shared => (),
757 hir::Access::Exclusive => h |= HighlightModifier::Mutable,
758 hir::Access::Owned => {
759 if let Some(receiver_ty) =
760 method_call.receiver().and_then(|it| sema.type_of_expr(&it))
761 {
762 if !receiver_ty.is_copy(sema.db) {
763 h |= HighlightModifier::Consuming
764 }
765 }
766 }
767 }
768 }
769 Some(h)
770}
771
772fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
773 match def {
774 Definition::Macro(_) => HighlightTag::Symbol(SymbolKind::Macro),
775 Definition::Field(_) => HighlightTag::Symbol(SymbolKind::Field),
776 Definition::ModuleDef(def) => match def {
777 hir::ModuleDef::Module(_) => HighlightTag::Symbol(SymbolKind::Module),
778 hir::ModuleDef::Function(func) => {
779 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Function));
780 if func.as_assoc_item(db).is_some() {
781 h |= HighlightModifier::Associated;
782 if func.self_param(db).is_none() {
783 h |= HighlightModifier::Static
784 }
785 }
786 if func.is_unsafe(db) {
787 h |= HighlightModifier::Unsafe;
788 }
789 return h;
790 }
791 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Symbol(SymbolKind::Struct),
792 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Symbol(SymbolKind::Enum),
793 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Symbol(SymbolKind::Union),
794 hir::ModuleDef::Variant(_) => HighlightTag::Symbol(SymbolKind::Variant),
795 hir::ModuleDef::Const(konst) => {
796 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Const));
797 if konst.as_assoc_item(db).is_some() {
798 h |= HighlightModifier::Associated
799 }
800 return h;
801 }
802 hir::ModuleDef::Trait(_) => HighlightTag::Symbol(SymbolKind::Trait),
803 hir::ModuleDef::TypeAlias(type_) => {
804 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::TypeAlias));
805 if type_.as_assoc_item(db).is_some() {
806 h |= HighlightModifier::Associated
807 }
808 return h;
809 }
810 hir::ModuleDef::BuiltinType(_) => HighlightTag::BuiltinType,
811 hir::ModuleDef::Static(s) => {
812 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Static));
813 if s.is_mut(db) {
814 h |= HighlightModifier::Mutable;
815 h |= HighlightModifier::Unsafe;
816 }
817 return h;
818 }
819 },
820 Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl),
821 Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam),
822 Definition::ConstParam(_) => HighlightTag::Symbol(SymbolKind::ConstParam),
823 Definition::Local(local) => {
824 let tag = if local.is_param(db) {
825 HighlightTag::Symbol(SymbolKind::ValueParam)
826 } else {
827 HighlightTag::Symbol(SymbolKind::Local)
828 };
829 let mut h = Highlight::new(tag);
830 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
831 h |= HighlightModifier::Mutable;
832 }
833 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) {
834 h |= HighlightModifier::Callable;
835 }
836 return h;
837 }
838 Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam),
839 Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label),
840 }
841 .into()
842}
843
844fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
845 let default = HighlightTag::UnresolvedReference;
846
847 let parent = match name.syntax().parent() {
848 Some(it) => it,
849 _ => return default.into(),
850 };
851
852 let tag = match parent.kind() {
853 STRUCT => HighlightTag::Symbol(SymbolKind::Struct),
854 ENUM => HighlightTag::Symbol(SymbolKind::Enum),
855 VARIANT => HighlightTag::Symbol(SymbolKind::Variant),
856 UNION => HighlightTag::Symbol(SymbolKind::Union),
857 TRAIT => HighlightTag::Symbol(SymbolKind::Trait),
858 TYPE_ALIAS => HighlightTag::Symbol(SymbolKind::TypeAlias),
859 TYPE_PARAM => HighlightTag::Symbol(SymbolKind::TypeParam),
860 RECORD_FIELD => HighlightTag::Symbol(SymbolKind::Field),
861 MODULE => HighlightTag::Symbol(SymbolKind::Module),
862 FN => HighlightTag::Symbol(SymbolKind::Function),
863 CONST => HighlightTag::Symbol(SymbolKind::Const),
864 STATIC => HighlightTag::Symbol(SymbolKind::Static),
865 IDENT_PAT => HighlightTag::Symbol(SymbolKind::Local),
866 _ => default,
867 };
868
869 tag.into()
870}
871
872fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight {
873 let default = HighlightTag::UnresolvedReference;
874
875 let parent = match name.syntax().parent() {
876 Some(it) => it,
877 _ => return default.into(),
878 };
879
880 match parent.kind() {
881 METHOD_CALL_EXPR => {
882 return ast::MethodCallExpr::cast(parent)
883 .and_then(|method_call| highlight_method_call(sema, &method_call))
884 .unwrap_or_else(|| HighlightTag::Symbol(SymbolKind::Function).into());
885 }
886 FIELD_EXPR => {
887 let h = HighlightTag::Symbol(SymbolKind::Field);
888 let is_union = ast::FieldExpr::cast(parent)
889 .and_then(|field_expr| {
890 let field = sema.resolve_field(&field_expr)?;
891 Some(if let VariantDef::Union(_) = field.parent_def(sema.db) {
892 true
893 } else {
894 false
895 })
896 })
897 .unwrap_or(false);
898 if is_union {
899 h | HighlightModifier::Unsafe
900 } else {
901 h.into()
902 }
903 }
904 PATH_SEGMENT => {
905 let path = match parent.parent().and_then(ast::Path::cast) {
906 Some(it) => it,
907 _ => return default.into(),
908 };
909 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
910 Some(it) => it,
911 _ => {
912 // within path, decide whether it is module or adt by checking for uppercase name
913 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
914 HighlightTag::Symbol(SymbolKind::Struct)
915 } else {
916 HighlightTag::Symbol(SymbolKind::Module)
917 }
918 .into();
919 }
920 };
921 let parent = match expr.syntax().parent() {
922 Some(it) => it,
923 None => return default.into(),
924 };
925
926 match parent.kind() {
927 CALL_EXPR => HighlightTag::Symbol(SymbolKind::Function).into(),
928 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
929 HighlightTag::Symbol(SymbolKind::Struct)
930 } else {
931 HighlightTag::Symbol(SymbolKind::Const)
932 }
933 .into(),
934 }
935 }
936 _ => default.into(),
937 }
938}
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 26416022b..a74ca844b 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -1,65 +1,51 @@
1//! Syntax highlighting for format macro strings. 1//! Syntax highlighting for format macro strings.
2use syntax::{ 2use syntax::{
3 ast::{self, FormatSpecifier, HasFormatSpecifier}, 3 ast::{self, FormatSpecifier, HasFormatSpecifier},
4 AstNode, AstToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, 4 AstNode, AstToken, TextRange,
5}; 5};
6 6
7use crate::{ 7use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag, SymbolKind};
8 syntax_highlighting::HighlightedRangeStack, HighlightTag, HighlightedRange, SymbolKind,
9};
10
11#[derive(Default)]
12pub(super) struct FormatStringHighlighter {
13 format_string: Option<SyntaxElement>,
14}
15 8
16impl FormatStringHighlighter { 9pub(super) fn highlight_format_string(
17 pub(super) fn check_for_format_string(&mut self, parent: &SyntaxNode) { 10 stack: &mut Highlights,
18 // Check if macro takes a format string and remember it for highlighting later. 11 string: &ast::String,
19 // The macros that accept a format string expand to a compiler builtin macros 12 range: TextRange,
20 // `format_args` and `format_args_nl`. 13) {
21 if let Some(name) = parent 14 if is_format_string(string).is_none() {
22 .parent() 15 return;
23 .and_then(ast::MacroCall::cast)
24 .and_then(|mc| mc.path())
25 .and_then(|p| p.segment())
26 .and_then(|s| s.name_ref())
27 {
28 match name.text().as_str() {
29 "format_args" | "format_args_nl" => {
30 self.format_string = parent
31 .children_with_tokens()
32 .filter(|t| t.kind() != SyntaxKind::WHITESPACE)
33 .nth(1)
34 .filter(|e| ast::String::can_cast(e.kind()))
35 }
36 _ => {}
37 }
38 }
39 } 16 }
40 pub(super) fn highlight_format_string( 17
41 &self, 18 string.lex_format_specifier(|piece_range, kind| {
42 range_stack: &mut HighlightedRangeStack, 19 if let Some(highlight) = highlight_format_specifier(kind) {
43 string: &impl HasFormatSpecifier, 20 stack.add(HlRange {
44 range: TextRange, 21 range: piece_range + range.start(),
45 ) { 22 highlight: highlight.into(),
46 if self.format_string.as_ref() == Some(&SyntaxElement::from(string.syntax().clone())) { 23 binding_hash: None,
47 range_stack.push();
48 string.lex_format_specifier(|piece_range, kind| {
49 if let Some(highlight) = highlight_format_specifier(kind) {
50 range_stack.add(HighlightedRange {
51 range: piece_range + range.start(),
52 highlight: highlight.into(),
53 binding_hash: None,
54 });
55 }
56 }); 24 });
57 range_stack.pop();
58 } 25 }
26 });
27}
28
29fn is_format_string(string: &ast::String) -> Option<()> {
30 let parent = string.syntax().parent();
31
32 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
33 if !matches!(name.text().as_str(), "format_args" | "format_args_nl") {
34 return None;
35 }
36
37 let first_literal = parent
38 .children_with_tokens()
39 .filter_map(|it| it.as_token().cloned().and_then(ast::String::cast))
40 .next()?;
41 if &first_literal != string {
42 return None;
59 } 43 }
44
45 Some(())
60} 46}
61 47
62fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { 48fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HlTag> {
63 Some(match kind { 49 Some(match kind {
64 FormatSpecifier::Open 50 FormatSpecifier::Open
65 | FormatSpecifier::Close 51 | FormatSpecifier::Close
@@ -71,8 +57,10 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
71 | FormatSpecifier::DollarSign 57 | FormatSpecifier::DollarSign
72 | FormatSpecifier::Dot 58 | FormatSpecifier::Dot
73 | FormatSpecifier::Asterisk 59 | FormatSpecifier::Asterisk
74 | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier, 60 | FormatSpecifier::QuestionMark => HlTag::FormatSpecifier,
75 FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, 61
76 FormatSpecifier::Identifier => HighlightTag::Symbol(SymbolKind::Local), 62 FormatSpecifier::Integer | FormatSpecifier::Zero => HlTag::NumericLiteral,
63
64 FormatSpecifier::Identifier => HlTag::Symbol(SymbolKind::Local),
77 }) 65 })
78} 66}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
new file mode 100644
index 000000000..34bae49a8
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -0,0 +1,530 @@
1//! Computes color for a single element.
2
3use hir::{AsAssocItem, Semantics, VariantDef};
4use ide_db::{
5 defs::{Definition, NameClass, NameRefClass},
6 RootDatabase,
7};
8use rustc_hash::FxHashMap;
9use syntax::{
10 ast, AstNode, AstToken, NodeOrToken, SyntaxElement,
11 SyntaxKind::{self, *},
12 SyntaxNode, SyntaxToken, T,
13};
14
15use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag, SymbolKind};
16
17pub(super) fn element(
18 sema: &Semantics<RootDatabase>,
19 bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
20 syntactic_name_ref_highlighting: bool,
21 element: SyntaxElement,
22) -> Option<(Highlight, Option<u64>)> {
23 let db = sema.db;
24 let mut binding_hash = None;
25 let highlight: Highlight = match element.kind() {
26 FN => {
27 bindings_shadow_count.clear();
28 return None;
29 }
30
31 // Highlight definitions depending on the "type" of the definition.
32 NAME => {
33 let name = element.into_node().and_then(ast::Name::cast).unwrap();
34 let name_kind = NameClass::classify(sema, &name);
35
36 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
37 if let Some(name) = local.name(db) {
38 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
39 *shadow_count += 1;
40 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
41 }
42 };
43
44 match name_kind {
45 Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(),
46 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
47 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
48 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
49 let mut h = HlTag::Symbol(SymbolKind::Field).into();
50 if let Definition::Field(field) = field_ref {
51 if let VariantDef::Union(_) = field.parent_def(db) {
52 h |= HlMod::Unsafe;
53 }
54 }
55
56 h
57 }
58 None => highlight_name_by_syntax(name) | HlMod::Definition,
59 }
60 }
61
62 // Highlight references like the definitions they resolve to
63 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
64 // even though we track whether we are in an attribute or not we still need this special case
65 // as otherwise we would emit unresolved references for name refs inside attributes
66 Highlight::from(HlTag::Symbol(SymbolKind::Function))
67 }
68 NAME_REF => {
69 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
70 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
71 match NameRefClass::classify(sema, &name_ref) {
72 Some(name_kind) => match name_kind {
73 NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(),
74 NameRefClass::Definition(def) => {
75 if let Definition::Local(local) = &def {
76 if let Some(name) = local.name(db) {
77 let shadow_count =
78 bindings_shadow_count.entry(name.clone()).or_default();
79 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
80 }
81 };
82
83 let mut h = highlight_def(db, def);
84
85 if let Definition::Local(local) = &def {
86 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) {
87 h |= HlMod::Consuming;
88 }
89 }
90
91 if let Some(parent) = name_ref.syntax().parent() {
92 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
93 if let Definition::Field(field) = def {
94 if let VariantDef::Union(_) = field.parent_def(db) {
95 h |= HlMod::Unsafe;
96 }
97 }
98 }
99 }
100
101 h
102 }
103 NameRefClass::FieldShorthand { .. } => {
104 HlTag::Symbol(SymbolKind::Field).into()
105 }
106 },
107 None if syntactic_name_ref_highlighting => {
108 highlight_name_ref_by_syntax(name_ref, sema)
109 }
110 None => HlTag::UnresolvedReference.into(),
111 }
112 })
113 }
114
115 // Simple token-based highlighting
116 COMMENT => {
117 let comment = element.into_token().and_then(ast::Comment::cast)?;
118 let h = HlTag::Comment;
119 match comment.kind().doc {
120 Some(_) => h | HlMod::Documentation,
121 None => h.into(),
122 }
123 }
124 STRING | BYTE_STRING => HlTag::StringLiteral.into(),
125 ATTR => HlTag::Attribute.into(),
126 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
127 BYTE => HlTag::ByteLiteral.into(),
128 CHAR => HlTag::CharLiteral.into(),
129 QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow,
130 LIFETIME => {
131 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
132
133 match NameClass::classify_lifetime(sema, &lifetime) {
134 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
135 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
136 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
137 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)),
138 },
139 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition,
140 }
141 }
142 p if p.is_punct() => match p {
143 T![&] => {
144 let h = HlTag::Operator.into();
145 let is_unsafe = element
146 .parent()
147 .and_then(ast::RefExpr::cast)
148 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr))
149 .unwrap_or(false);
150 if is_unsafe {
151 h | HlMod::Unsafe
152 } else {
153 h
154 }
155 }
156 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(),
157 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
158 HlTag::Symbol(SymbolKind::Macro).into()
159 }
160 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
161 HlTag::BuiltinType.into()
162 }
163 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
164 HlTag::Keyword.into()
165 }
166 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
167 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
168
169 let expr = prefix_expr.expr()?;
170 let ty = sema.type_of_expr(&expr)?;
171 if ty.is_raw_ptr() {
172 HlTag::Operator | HlMod::Unsafe
173 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
174 HlTag::Operator.into()
175 } else {
176 HlTag::Punctuation(HlPunct::Other).into()
177 }
178 }
179 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
180 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
181
182 let expr = prefix_expr.expr()?;
183 match expr {
184 ast::Expr::Literal(_) => HlTag::NumericLiteral,
185 _ => HlTag::Operator,
186 }
187 .into()
188 }
189 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
190 HlTag::Operator.into()
191 }
192 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(),
193 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
194 HlTag::Operator.into()
195 }
196 _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(),
197 _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(),
198 _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(),
199 kind => HlTag::Punctuation(match kind {
200 T!['['] | T![']'] => HlPunct::Bracket,
201 T!['{'] | T!['}'] => HlPunct::Brace,
202 T!['('] | T![')'] => HlPunct::Parenthesis,
203 T![<] | T![>] => HlPunct::Angle,
204 T![,] => HlPunct::Comma,
205 T![:] => HlPunct::Colon,
206 T![;] => HlPunct::Semi,
207 T![.] => HlPunct::Dot,
208 _ => HlPunct::Other,
209 })
210 .into(),
211 },
212
213 k if k.is_keyword() => {
214 let h = Highlight::new(HlTag::Keyword);
215 match k {
216 T![break]
217 | T![continue]
218 | T![else]
219 | T![if]
220 | T![loop]
221 | T![match]
222 | T![return]
223 | T![while]
224 | T![in] => h | HlMod::ControlFlow,
225 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow,
226 T![unsafe] => h | HlMod::Unsafe,
227 T![true] | T![false] => HlTag::BoolLiteral.into(),
228 T![self] => {
229 let self_param_is_mut = element
230 .parent()
231 .and_then(ast::SelfParam::cast)
232 .and_then(|p| p.mut_token())
233 .is_some();
234 let self_path = &element
235 .parent()
236 .as_ref()
237 .and_then(SyntaxNode::parent)
238 .and_then(ast::Path::cast)
239 .and_then(|p| sema.resolve_path(&p));
240 let mut h = HlTag::Symbol(SymbolKind::SelfParam).into();
241 if self_param_is_mut
242 || matches!(self_path,
243 Some(hir::PathResolution::Local(local))
244 if local.is_self(db)
245 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
246 )
247 {
248 h |= HlMod::Mutable
249 }
250
251 if let Some(hir::PathResolution::Local(local)) = self_path {
252 if is_consumed_lvalue(element, &local, db) {
253 h |= HlMod::Consuming;
254 }
255 }
256
257 h
258 }
259 T![ref] => element
260 .parent()
261 .and_then(ast::IdentPat::cast)
262 .and_then(|ident_pat| {
263 if sema.is_unsafe_ident_pat(&ident_pat) {
264 Some(HlMod::Unsafe)
265 } else {
266 None
267 }
268 })
269 .map(|modifier| h | modifier)
270 .unwrap_or(h),
271 _ => h,
272 }
273 }
274
275 _ => return None,
276 };
277
278 return Some((highlight, binding_hash));
279
280 fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 {
281 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
282 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
283
284 let mut hasher = DefaultHasher::new();
285 x.hash(&mut hasher);
286 hasher.finish()
287 }
288
289 hash((name, shadow_count))
290 }
291}
292
293fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
294 match def {
295 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro),
296 Definition::Field(_) => HlTag::Symbol(SymbolKind::Field),
297 Definition::ModuleDef(def) => match def {
298 hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module),
299 hir::ModuleDef::Function(func) => {
300 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
301 if func.as_assoc_item(db).is_some() {
302 h |= HlMod::Associated;
303 if func.self_param(db).is_none() {
304 h |= HlMod::Static
305 }
306 }
307 if func.is_unsafe(db) {
308 h |= HlMod::Unsafe;
309 }
310 return h;
311 }
312 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct),
313 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HlTag::Symbol(SymbolKind::Enum),
314 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HlTag::Symbol(SymbolKind::Union),
315 hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant),
316 hir::ModuleDef::Const(konst) => {
317 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const));
318 if konst.as_assoc_item(db).is_some() {
319 h |= HlMod::Associated
320 }
321 return h;
322 }
323 hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait),
324 hir::ModuleDef::TypeAlias(type_) => {
325 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
326 if type_.as_assoc_item(db).is_some() {
327 h |= HlMod::Associated
328 }
329 return h;
330 }
331 hir::ModuleDef::BuiltinType(_) => HlTag::BuiltinType,
332 hir::ModuleDef::Static(s) => {
333 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static));
334 if s.is_mut(db) {
335 h |= HlMod::Mutable;
336 h |= HlMod::Unsafe;
337 }
338 return h;
339 }
340 },
341 Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl),
342 Definition::GenericParam(it) => match it {
343 hir::GenericParam::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam),
344 hir::GenericParam::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam),
345 hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam),
346 },
347 Definition::Local(local) => {
348 let tag = if local.is_param(db) {
349 HlTag::Symbol(SymbolKind::ValueParam)
350 } else {
351 HlTag::Symbol(SymbolKind::Local)
352 };
353 let mut h = Highlight::new(tag);
354 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
355 h |= HlMod::Mutable;
356 }
357 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) {
358 h |= HlMod::Callable;
359 }
360 return h;
361 }
362 Definition::Label(_) => HlTag::Symbol(SymbolKind::Label),
363 }
364 .into()
365}
366
367fn highlight_func_by_name_ref(
368 sema: &Semantics<RootDatabase>,
369 name_ref: &ast::NameRef,
370) -> Option<Highlight> {
371 let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
372 highlight_method_call(sema, &mc)
373}
374
375fn highlight_method_call(
376 sema: &Semantics<RootDatabase>,
377 method_call: &ast::MethodCallExpr,
378) -> Option<Highlight> {
379 let func = sema.resolve_method_call(&method_call)?;
380 let mut h = HlTag::Symbol(SymbolKind::Function).into();
381 h |= HlMod::Associated;
382 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
383 h |= HlMod::Unsafe;
384 }
385 if let Some(self_param) = func.self_param(sema.db) {
386 match self_param.access(sema.db) {
387 hir::Access::Shared => (),
388 hir::Access::Exclusive => h |= HlMod::Mutable,
389 hir::Access::Owned => {
390 if let Some(receiver_ty) =
391 method_call.receiver().and_then(|it| sema.type_of_expr(&it))
392 {
393 if !receiver_ty.is_copy(sema.db) {
394 h |= HlMod::Consuming
395 }
396 }
397 }
398 }
399 }
400 Some(h)
401}
402
403fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
404 let default = HlTag::UnresolvedReference;
405
406 let parent = match name.syntax().parent() {
407 Some(it) => it,
408 _ => return default.into(),
409 };
410
411 let tag = match parent.kind() {
412 STRUCT => HlTag::Symbol(SymbolKind::Struct),
413 ENUM => HlTag::Symbol(SymbolKind::Enum),
414 VARIANT => HlTag::Symbol(SymbolKind::Variant),
415 UNION => HlTag::Symbol(SymbolKind::Union),
416 TRAIT => HlTag::Symbol(SymbolKind::Trait),
417 TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias),
418 TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam),
419 RECORD_FIELD => HlTag::Symbol(SymbolKind::Field),
420 MODULE => HlTag::Symbol(SymbolKind::Module),
421 FN => HlTag::Symbol(SymbolKind::Function),
422 CONST => HlTag::Symbol(SymbolKind::Const),
423 STATIC => HlTag::Symbol(SymbolKind::Static),
424 IDENT_PAT => HlTag::Symbol(SymbolKind::Local),
425 _ => default,
426 };
427
428 tag.into()
429}
430
431fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight {
432 let default = HlTag::UnresolvedReference;
433
434 let parent = match name.syntax().parent() {
435 Some(it) => it,
436 _ => return default.into(),
437 };
438
439 match parent.kind() {
440 METHOD_CALL_EXPR => {
441 return ast::MethodCallExpr::cast(parent)
442 .and_then(|it| highlight_method_call(sema, &it))
443 .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into());
444 }
445 FIELD_EXPR => {
446 let h = HlTag::Symbol(SymbolKind::Field);
447 let is_union = ast::FieldExpr::cast(parent)
448 .and_then(|field_expr| {
449 let field = sema.resolve_field(&field_expr)?;
450 Some(if let VariantDef::Union(_) = field.parent_def(sema.db) {
451 true
452 } else {
453 false
454 })
455 })
456 .unwrap_or(false);
457 if is_union {
458 h | HlMod::Unsafe
459 } else {
460 h.into()
461 }
462 }
463 PATH_SEGMENT => {
464 let path = match parent.parent().and_then(ast::Path::cast) {
465 Some(it) => it,
466 _ => return default.into(),
467 };
468 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
469 Some(it) => it,
470 _ => {
471 // within path, decide whether it is module or adt by checking for uppercase name
472 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
473 HlTag::Symbol(SymbolKind::Struct)
474 } else {
475 HlTag::Symbol(SymbolKind::Module)
476 }
477 .into();
478 }
479 };
480 let parent = match expr.syntax().parent() {
481 Some(it) => it,
482 None => return default.into(),
483 };
484
485 match parent.kind() {
486 CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(),
487 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
488 HlTag::Symbol(SymbolKind::Struct)
489 } else {
490 HlTag::Symbol(SymbolKind::Const)
491 }
492 .into(),
493 }
494 }
495 _ => default.into(),
496 }
497}
498
499fn is_consumed_lvalue(
500 node: NodeOrToken<SyntaxNode, SyntaxToken>,
501 local: &hir::Local,
502 db: &RootDatabase,
503) -> bool {
504 // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
505 parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db)
506}
507
508/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
509fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
510 while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
511 if parent.kind() != *kind {
512 return false;
513 }
514
515 // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
516 // in the same pattern is unstable: rust-lang/rust#68354.
517 node = node.parent().unwrap().into();
518 kinds = rest;
519 }
520
521 // Only true if we matched all expected kinds
522 kinds.len() == 0
523}
524
525fn is_child_of_impl(element: &SyntaxElement) -> bool {
526 match element.parent() {
527 Some(e) => e.kind() == IMPL,
528 _ => false,
529 }
530}
diff --git a/crates/ide/src/syntax_highlighting/highlights.rs b/crates/ide/src/syntax_highlighting/highlights.rs
new file mode 100644
index 000000000..882a685a5
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/highlights.rs
@@ -0,0 +1,92 @@
1//! Collects a tree of highlighted ranges and flattens it.
2use std::iter;
3
4use stdx::equal_range_by;
5use syntax::TextRange;
6
7use crate::{HlRange, HlTag};
8
9pub(super) struct Highlights {
10 root: Node,
11}
12
13struct Node {
14 hl_range: HlRange,
15 nested: Vec<Node>,
16}
17
18impl Highlights {
19 pub(super) fn new(range: TextRange) -> Highlights {
20 Highlights {
21 root: Node::new(HlRange { range, highlight: HlTag::None.into(), binding_hash: None }),
22 }
23 }
24
25 pub(super) fn add(&mut self, hl_range: HlRange) {
26 self.root.add(hl_range);
27 }
28
29 pub(super) fn to_vec(self) -> Vec<HlRange> {
30 let mut res = Vec::new();
31 self.root.flatten(&mut res);
32 res
33 }
34}
35
36impl Node {
37 fn new(hl_range: HlRange) -> Node {
38 Node { hl_range, nested: Vec::new() }
39 }
40
41 fn add(&mut self, hl_range: HlRange) {
42 assert!(self.hl_range.range.contains_range(hl_range.range));
43
44 // Fast path
45 if let Some(last) = self.nested.last_mut() {
46 if last.hl_range.range.contains_range(hl_range.range) {
47 return last.add(hl_range);
48 }
49 if last.hl_range.range.end() <= hl_range.range.start() {
50 return self.nested.push(Node::new(hl_range));
51 }
52 }
53
54 let overlapping =
55 equal_range_by(&self.nested, |n| TextRange::ordering(n.hl_range.range, hl_range.range));
56
57 if overlapping.len() == 1
58 && self.nested[overlapping.start].hl_range.range.contains_range(hl_range.range)
59 {
60 return self.nested[overlapping.start].add(hl_range);
61 }
62
63 let nested = self
64 .nested
65 .splice(overlapping.clone(), iter::once(Node::new(hl_range)))
66 .collect::<Vec<_>>();
67 self.nested[overlapping.start].nested = nested;
68 }
69
70 fn flatten(&self, acc: &mut Vec<HlRange>) {
71 let mut start = self.hl_range.range.start();
72 let mut nested = self.nested.iter();
73 loop {
74 let next = nested.next();
75 let end = next.map_or(self.hl_range.range.end(), |it| it.hl_range.range.start());
76 if start < end {
77 acc.push(HlRange {
78 range: TextRange::new(start, end),
79 highlight: self.hl_range.highlight,
80 binding_hash: self.hl_range.binding_hash,
81 });
82 }
83 start = match next {
84 Some(child) => {
85 child.flatten(acc);
86 child.hl_range.range.end()
87 }
88 None => break,
89 }
90 }
91 }
92}
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 99ba3a59d..0ee7bc96e 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -3,7 +3,7 @@
3use ide_db::base_db::SourceDatabase; 3use ide_db::base_db::SourceDatabase;
4use oorandom::Rand32; 4use oorandom::Rand32;
5use stdx::format_to; 5use stdx::format_to;
6use syntax::{AstNode, TextRange, TextSize}; 6use syntax::AstNode;
7 7
8use crate::{syntax_highlighting::highlight, FileId, RootDatabase}; 8use crate::{syntax_highlighting::highlight, FileId, RootDatabase};
9 9
@@ -20,35 +20,27 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
20 ) 20 )
21 } 21 }
22 22
23 let ranges = highlight(db, file_id, None, false); 23 let hl_ranges = highlight(db, file_id, None, false);
24 let text = parse.tree().syntax().to_string(); 24 let text = parse.tree().syntax().to_string();
25 let mut prev_pos = TextSize::from(0);
26 let mut buf = String::new(); 25 let mut buf = String::new();
27 buf.push_str(&STYLE); 26 buf.push_str(&STYLE);
28 buf.push_str("<pre><code>"); 27 buf.push_str("<pre><code>");
29 for range in &ranges { 28 for r in &hl_ranges {
30 if range.range.start() > prev_pos { 29 let chunk = html_escape(&text[r.range]);
31 let curr = &text[TextRange::new(prev_pos, range.range.start())]; 30 if r.highlight.is_empty() {
32 let text = html_escape(curr); 31 format_to!(buf, "{}", chunk);
33 buf.push_str(&text); 32 continue;
34 } 33 }
35 let curr = &text[TextRange::new(range.range.start(), range.range.end())];
36 34
37 let class = range.highlight.to_string().replace('.', " "); 35 let class = r.highlight.to_string().replace('.', " ");
38 let color = match (rainbow, range.binding_hash) { 36 let color = match (rainbow, r.binding_hash) {
39 (true, Some(hash)) => { 37 (true, Some(hash)) => {
40 format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)) 38 format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash))
41 } 39 }
42 _ => "".into(), 40 _ => "".into(),
43 }; 41 };
44 format_to!(buf, "<span class=\"{}\"{}>{}</span>", class, color, html_escape(curr)); 42 format_to!(buf, "<span class=\"{}\"{}>{}</span>", class, color, chunk);
45
46 prev_pos = range.range.end();
47 } 43 }
48 // Add the remaining (non-highlighted) text
49 let curr = &text[TextRange::new(prev_pos, TextSize::of(&text))];
50 let text = html_escape(curr);
51 buf.push_str(&text);
52 buf.push_str("</code></pre>"); 44 buf.push_str("</code></pre>");
53 buf 45 buf
54} 46}
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
new file mode 100644
index 000000000..281461493
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -0,0 +1,158 @@
1//! "Recursive" Syntax highlighting for code in doctests and fixtures.
2
3use hir::Semantics;
4use ide_db::call_info::ActiveParameter;
5use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
6
7use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase};
8
9use super::{highlights::Highlights, injector::Injector};
10
11pub(super) fn ra_fixture(
12 hl: &mut Highlights,
13 sema: &Semantics<RootDatabase>,
14 literal: ast::String,
15 expanded: SyntaxToken,
16) -> Option<()> {
17 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
18 if !active_parameter.name.starts_with("ra_fixture") {
19 return None;
20 }
21 let value = literal.value()?;
22
23 if let Some(range) = literal.open_quote_text_range() {
24 hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
25 }
26
27 let mut inj = Injector::default();
28
29 let mut text = &*value;
30 let mut offset: TextSize = 0.into();
31
32 while !text.is_empty() {
33 let marker = "$0";
34 let idx = text.find(marker).unwrap_or(text.len());
35 let (chunk, next) = text.split_at(idx);
36 inj.add(chunk, TextRange::at(offset, TextSize::of(chunk)));
37
38 text = next;
39 offset += TextSize::of(chunk);
40
41 if let Some(next) = text.strip_prefix(marker) {
42 if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
43 hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None });
44 }
45
46 text = next;
47
48 let marker_len = TextSize::of(marker);
49 offset += marker_len;
50 }
51 }
52
53 let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string());
54
55 for mut hl_range in analysis.highlight(tmp_file_id).unwrap() {
56 for range in inj.map_range_up(hl_range.range) {
57 if let Some(range) = literal.map_range_up(range) {
58 hl_range.range = range;
59 hl.add(hl_range.clone());
60 }
61 }
62 }
63
64 if let Some(range) = literal.close_quote_text_range() {
65 hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
66 }
67
68 Some(())
69}
70
71const RUSTDOC_FENCE: &'static str = "```";
72const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
73 "",
74 "rust",
75 "should_panic",
76 "ignore",
77 "no_run",
78 "compile_fail",
79 "edition2015",
80 "edition2018",
81 "edition2021",
82];
83
84/// Injection of syntax highlighting of doctests.
85pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
86 let doc_comments = node
87 .children_with_tokens()
88 .filter_map(|it| it.into_token().and_then(ast::Comment::cast))
89 .filter(|it| it.kind().doc.is_some());
90
91 if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) {
92 return;
93 }
94
95 let mut inj = Injector::default();
96 inj.add_unmapped("fn doctest() {\n");
97
98 let mut is_codeblock = false;
99 let mut is_doctest = false;
100
101 // Replace the original, line-spanning comment ranges by new, only comment-prefix
102 // spanning comment ranges.
103 let mut new_comments = Vec::new();
104 for comment in doc_comments {
105 match comment.text().find(RUSTDOC_FENCE) {
106 Some(idx) => {
107 is_codeblock = !is_codeblock;
108 // Check whether code is rust by inspecting fence guards
109 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
110 let is_rust =
111 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
112 is_doctest = is_codeblock && is_rust;
113 continue;
114 }
115 None if !is_doctest => continue,
116 None => (),
117 }
118
119 let line: &str = comment.text().as_str();
120 let range = comment.syntax().text_range();
121
122 let mut pos = TextSize::of(comment.prefix());
123 // whitespace after comment is ignored
124 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) {
125 pos += TextSize::of(ws);
126 }
127 // lines marked with `#` should be ignored in output, we skip the `#` char
128 if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') {
129 pos += TextSize::of(ws);
130 }
131
132 new_comments.push(TextRange::at(range.start(), pos));
133
134 inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end()));
135 inj.add_unmapped("\n");
136 }
137 inj.add_unmapped("\n}");
138
139 let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string());
140
141 for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
142 for r in inj.map_range_up(h.range) {
143 hl.add(HlRange {
144 range: r,
145 highlight: h.highlight | HlMod::Injected,
146 binding_hash: h.binding_hash,
147 });
148 }
149 }
150
151 for range in new_comments {
152 hl.add(HlRange {
153 range,
154 highlight: HlTag::Comment | HlMod::Documentation,
155 binding_hash: None,
156 });
157 }
158}
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs
deleted file mode 100644
index d6be9708d..000000000
--- a/crates/ide/src/syntax_highlighting/injection.rs
+++ /dev/null
@@ -1,240 +0,0 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use hir::Semantics;
6use ide_db::call_info::ActiveParameter;
7use itertools::Itertools;
8use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
9
10use crate::{Analysis, Highlight, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase};
11
12use super::HighlightedRangeStack;
13
14pub(super) fn highlight_injection(
15 acc: &mut HighlightedRangeStack,
16 sema: &Semantics<RootDatabase>,
17 literal: ast::String,
18 expanded: SyntaxToken,
19) -> Option<()> {
20 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
21 if !active_parameter.name.starts_with("ra_fixture") {
22 return None;
23 }
24 let value = literal.value()?;
25 let marker_info = MarkerInfo::new(&*value);
26 let (analysis, tmp_file_id) = Analysis::from_single_file(marker_info.cleaned_text.clone());
27
28 if let Some(range) = literal.open_quote_text_range() {
29 acc.add(HighlightedRange {
30 range,
31 highlight: HighlightTag::StringLiteral.into(),
32 binding_hash: None,
33 })
34 }
35
36 for mut h in analysis.highlight(tmp_file_id).unwrap() {
37 let range = marker_info.map_range_up(h.range);
38 if let Some(range) = literal.map_range_up(range) {
39 h.range = range;
40 acc.add(h);
41 }
42 }
43
44 if let Some(range) = literal.close_quote_text_range() {
45 acc.add(HighlightedRange {
46 range,
47 highlight: HighlightTag::StringLiteral.into(),
48 binding_hash: None,
49 })
50 }
51
52 Some(())
53}
54
55/// Data to remove `$0` from string and map ranges
56#[derive(Default, Debug)]
57struct MarkerInfo {
58 cleaned_text: String,
59 markers: Vec<TextRange>,
60}
61
62impl MarkerInfo {
63 fn new(mut text: &str) -> Self {
64 let marker = "$0";
65
66 let mut res = MarkerInfo::default();
67 let mut offset: TextSize = 0.into();
68 while !text.is_empty() {
69 let idx = text.find(marker).unwrap_or(text.len());
70 let (chunk, next) = text.split_at(idx);
71 text = next;
72 res.cleaned_text.push_str(chunk);
73 offset += TextSize::of(chunk);
74
75 if let Some(next) = text.strip_prefix(marker) {
76 text = next;
77
78 let marker_len = TextSize::of(marker);
79 res.markers.push(TextRange::at(offset, marker_len));
80 offset += marker_len;
81 }
82 }
83 res
84 }
85 fn map_range_up(&self, range: TextRange) -> TextRange {
86 TextRange::new(
87 self.map_offset_up(range.start(), true),
88 self.map_offset_up(range.end(), false),
89 )
90 }
91 fn map_offset_up(&self, mut offset: TextSize, start: bool) -> TextSize {
92 for r in &self.markers {
93 if r.start() < offset || (start && r.start() == offset) {
94 offset += r.len()
95 }
96 }
97 offset
98 }
99}
100
101/// Mapping from extracted documentation code to original code
102type RangesMap = BTreeMap<TextSize, TextSize>;
103
104const RUSTDOC_FENCE: &'static str = "```";
105const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
106 "",
107 "rust",
108 "should_panic",
109 "ignore",
110 "no_run",
111 "compile_fail",
112 "edition2015",
113 "edition2018",
114 "edition2021",
115];
116
117/// Extracts Rust code from documentation comments as well as a mapping from
118/// the extracted source code back to the original source ranges.
119/// Lastly, a vector of new comment highlight ranges (spanning only the
120/// comment prefix) is returned which is used in the syntax highlighting
121/// injection to replace the previous (line-spanning) comment ranges.
122pub(super) fn extract_doc_comments(
123 node: &SyntaxNode,
124) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
125 // wrap the doctest into function body to get correct syntax highlighting
126 let prefix = "fn doctest() {\n";
127 let suffix = "}\n";
128 // Mapping from extracted documentation code to original code
129 let mut range_mapping: RangesMap = BTreeMap::new();
130 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
131 let mut is_codeblock = false;
132 let mut is_doctest = false;
133 // Replace the original, line-spanning comment ranges by new, only comment-prefix
134 // spanning comment ranges.
135 let mut new_comments = Vec::new();
136 let doctest = node
137 .children_with_tokens()
138 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
139 .filter(|comment| comment.kind().doc.is_some())
140 .filter(|comment| {
141 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
142 is_codeblock = !is_codeblock;
143 // Check whether code is rust by inspecting fence guards
144 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
145 let is_rust =
146 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
147 is_doctest = is_codeblock && is_rust;
148 false
149 } else {
150 is_doctest
151 }
152 })
153 .map(|comment| {
154 let prefix_len = comment.prefix().len();
155 let line: &str = comment.text().as_str();
156 let range = comment.syntax().text_range();
157
158 // whitespace after comment is ignored
159 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
160 prefix_len + ws.len_utf8()
161 } else {
162 prefix_len
163 };
164
165 // lines marked with `#` should be ignored in output, we skip the `#` char
166 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
167 pos + ws.len_utf8()
168 } else {
169 pos
170 };
171
172 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
173 new_comments.push(HighlightedRange {
174 range: TextRange::new(
175 range.start(),
176 range.start() + TextSize::try_from(pos).unwrap(),
177 ),
178 highlight: HighlightTag::Comment | HighlightModifier::Documentation,
179 binding_hash: None,
180 });
181 line_start += range.len() - TextSize::try_from(pos).unwrap();
182 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
183
184 line[pos..].to_owned()
185 })
186 .join("\n");
187
188 if doctest.is_empty() {
189 return None;
190 }
191
192 let doctest = format!("{}{}{}", prefix, doctest, suffix);
193 Some((doctest, range_mapping, new_comments))
194}
195
196/// Injection of syntax highlighting of doctests.
197pub(super) fn highlight_doc_comment(
198 text: String,
199 range_mapping: RangesMap,
200 new_comments: Vec<HighlightedRange>,
201 stack: &mut HighlightedRangeStack,
202) {
203 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
204
205 stack.push();
206 for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
207 // Determine start offset and end offset in case of multi-line ranges
208 let mut start_offset = None;
209 let mut end_offset = None;
210 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
211 // It's possible for orig_line_start - line_start to be negative. Add h.range.start()
212 // here and remove it from the end range after the loop below so that the values are
213 // always non-negative.
214 let offset = h.range.start() + orig_line_start - line_start;
215 if line_start <= &h.range.start() {
216 start_offset.get_or_insert(offset);
217 break;
218 } else {
219 end_offset.get_or_insert(offset);
220 }
221 }
222 if let Some(start_offset) = start_offset {
223 h.range = TextRange::new(
224 start_offset,
225 h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(),
226 );
227
228 h.highlight |= HighlightModifier::Injected;
229 stack.add(h);
230 }
231 }
232
233 // Inject the comment prefix highlight ranges
234 stack.push();
235 for comment in new_comments {
236 stack.add(comment);
237 }
238 stack.pop_and_inject(None);
239 stack.pop_and_inject(Some(Highlight::from(HighlightTag::Dummy) | HighlightModifier::Injected));
240}
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs
new file mode 100644
index 000000000..24ff473ec
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/injector.rs
@@ -0,0 +1,78 @@
1//! Extracts a subsequence of a text document, remembering the mapping of ranges
2//! between original and extracted texts.
3use std::ops::{self, Sub};
4
5use stdx::equal_range_by;
6use syntax::{TextRange, TextSize};
7
8#[derive(Default)]
9pub(super) struct Injector {
10 buf: String,
11 ranges: Vec<(TextRange, Option<Delta<TextSize>>)>,
12}
13
14impl Injector {
15 pub(super) fn add(&mut self, text: &str, source_range: TextRange) {
16 let len = TextSize::of(text);
17 assert_eq!(len, source_range.len());
18 self.add_impl(text, Some(source_range.start()));
19 }
20 pub(super) fn add_unmapped(&mut self, text: &str) {
21 self.add_impl(text, None);
22 }
23 fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
24 let len = TextSize::of(text);
25 let target_range = TextRange::at(TextSize::of(&self.buf), len);
26 self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it))));
27 self.buf.push_str(text);
28 }
29
30 pub(super) fn text(&self) -> &str {
31 &self.buf
32 }
33 pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
34 equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| {
35 let (target_range, delta) = self.ranges[i];
36 let intersection = target_range.intersect(range).unwrap();
37 Some(intersection + delta?)
38 })
39 }
40}
41
42#[derive(Clone, Copy)]
43enum Delta<T> {
44 Add(T),
45 Sub(T),
46}
47
48impl<T> Delta<T> {
49 fn new(from: T, to: T) -> Delta<T>
50 where
51 T: Ord + Sub<Output = T>,
52 {
53 if to >= from {
54 Delta::Add(to - from)
55 } else {
56 Delta::Sub(from - to)
57 }
58 }
59}
60
61impl ops::Add<Delta<TextSize>> for TextSize {
62 type Output = TextSize;
63
64 fn add(self, rhs: Delta<TextSize>) -> TextSize {
65 match rhs {
66 Delta::Add(it) => self + it,
67 Delta::Sub(it) => self - it,
68 }
69 }
70}
71
72impl ops::Add<Delta<TextSize>> for TextRange {
73 type Output = TextRange;
74
75 fn add(self, rhs: Delta<TextSize>) -> TextRange {
76 TextRange::at(self.start() + rhs, self.len())
77 }
78}
diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_rules.rs
index 4462af47e..44620e912 100644
--- a/crates/ide/src/syntax_highlighting/macro_rules.rs
+++ b/crates/ide/src/syntax_highlighting/macro_rules.rs
@@ -1,7 +1,7 @@
1//! Syntax highlighting for macro_rules!. 1//! Syntax highlighting for macro_rules!.
2use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T}; 2use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T};
3 3
4use crate::{HighlightTag, HighlightedRange}; 4use crate::{HlRange, HlTag};
5 5
6#[derive(Default)] 6#[derive(Default)]
7pub(super) struct MacroRulesHighlighter { 7pub(super) struct MacroRulesHighlighter {
@@ -19,13 +19,13 @@ impl MacroRulesHighlighter {
19 } 19 }
20 } 20 }
21 21
22 pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HighlightedRange> { 22 pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HlRange> {
23 if let Some(state) = self.state.as_ref() { 23 if let Some(state) = self.state.as_ref() {
24 if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) { 24 if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) {
25 if let Some(range) = is_metavariable(element) { 25 if let Some(range) = is_metavariable(element) {
26 return Some(HighlightedRange { 26 return Some(HlRange {
27 range, 27 range,
28 highlight: HighlightTag::UnresolvedReference.into(), 28 highlight: HlTag::UnresolvedReference.into(),
29 binding_hash: None, 29 binding_hash: None,
30 }); 30 });
31 } 31 }
@@ -119,7 +119,7 @@ fn is_metavariable(element: SyntaxElement) -> Option<TextRange> {
119 let tok = element.as_token()?; 119 let tok = element.as_token()?;
120 match tok.kind() { 120 match tok.kind() {
121 kind if kind == SyntaxKind::IDENT || kind.is_keyword() => { 121 kind if kind == SyntaxKind::IDENT || kind.is_keyword() => {
122 if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == SyntaxKind::DOLLAR) { 122 if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == T![$]) {
123 return Some(tok.text_range()); 123 return Some(tok.text_range());
124 } 124 }
125 } 125 }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 8b8867079..8dd05ac52 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -7,15 +7,15 @@ use crate::SymbolKind;
7 7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
9pub struct Highlight { 9pub struct Highlight {
10 pub tag: HighlightTag, 10 pub tag: HlTag,
11 pub modifiers: HighlightModifiers, 11 pub mods: HlMods,
12} 12}
13 13
14#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 14#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct HighlightModifiers(u32); 15pub struct HlMods(u32);
16 16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub enum HighlightTag { 18pub enum HlTag {
19 Symbol(SymbolKind), 19 Symbol(SymbolKind),
20 20
21 BoolLiteral, 21 BoolLiteral,
@@ -29,17 +29,17 @@ pub enum HighlightTag {
29 EscapeSequence, 29 EscapeSequence,
30 FormatSpecifier, 30 FormatSpecifier,
31 Keyword, 31 Keyword,
32 Punctuation, 32 Punctuation(HlPunct),
33 Operator, 33 Operator,
34 UnresolvedReference, 34 UnresolvedReference,
35 35
36 // For things which don't have proper Tag, but want to use modifiers. 36 // For things which don't have a specific highlight.
37 Dummy, 37 None,
38} 38}
39 39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41#[repr(u8)] 41#[repr(u8)]
42pub enum HighlightModifier { 42pub enum HlMod {
43 /// Used to differentiate individual elements within attributes. 43 /// Used to differentiate individual elements within attributes.
44 Attribute = 0, 44 Attribute = 0,
45 /// Used with keywords like `if` and `break`. 45 /// Used with keywords like `if` and `break`.
@@ -61,10 +61,32 @@ pub enum HighlightModifier {
61 Unsafe, 61 Unsafe,
62} 62}
63 63
64impl HighlightTag { 64#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
65pub enum HlPunct {
66 /// []
67 Bracket,
68 /// {}
69 Brace,
70 /// ()
71 Parenthesis,
72 /// <>
73 Angle,
74 /// ,
75 Comma,
76 /// .
77 Dot,
78 /// :
79 Colon,
80 /// ;
81 Semi,
82 ///
83 Other,
84}
85
86impl HlTag {
65 fn as_str(self) -> &'static str { 87 fn as_str(self) -> &'static str {
66 match self { 88 match self {
67 HighlightTag::Symbol(symbol) => match symbol { 89 HlTag::Symbol(symbol) => match symbol {
68 SymbolKind::Const => "constant", 90 SymbolKind::Const => "constant",
69 SymbolKind::Static => "static", 91 SymbolKind::Static => "static",
70 SymbolKind::Enum => "enum", 92 SymbolKind::Enum => "enum",
@@ -86,59 +108,69 @@ impl HighlightTag {
86 SymbolKind::SelfParam => "self_keyword", 108 SymbolKind::SelfParam => "self_keyword",
87 SymbolKind::Impl => "self_type", 109 SymbolKind::Impl => "self_type",
88 }, 110 },
89 HighlightTag::Attribute => "attribute", 111 HlTag::Attribute => "attribute",
90 HighlightTag::BoolLiteral => "bool_literal", 112 HlTag::BoolLiteral => "bool_literal",
91 HighlightTag::BuiltinType => "builtin_type", 113 HlTag::BuiltinType => "builtin_type",
92 HighlightTag::ByteLiteral => "byte_literal", 114 HlTag::ByteLiteral => "byte_literal",
93 HighlightTag::CharLiteral => "char_literal", 115 HlTag::CharLiteral => "char_literal",
94 HighlightTag::Comment => "comment", 116 HlTag::Comment => "comment",
95 HighlightTag::EscapeSequence => "escape_sequence", 117 HlTag::EscapeSequence => "escape_sequence",
96 HighlightTag::FormatSpecifier => "format_specifier", 118 HlTag::FormatSpecifier => "format_specifier",
97 HighlightTag::Dummy => "dummy", 119 HlTag::Keyword => "keyword",
98 HighlightTag::Keyword => "keyword", 120 HlTag::Punctuation(punct) => match punct {
99 HighlightTag::Punctuation => "punctuation", 121 HlPunct::Bracket => "bracket",
100 HighlightTag::NumericLiteral => "numeric_literal", 122 HlPunct::Brace => "brace",
101 HighlightTag::Operator => "operator", 123 HlPunct::Parenthesis => "parenthesis",
102 HighlightTag::StringLiteral => "string_literal", 124 HlPunct::Angle => "angle",
103 HighlightTag::UnresolvedReference => "unresolved_reference", 125 HlPunct::Comma => "comma",
126 HlPunct::Dot => "dot",
127 HlPunct::Colon => "colon",
128 HlPunct::Semi => "semicolon",
129 HlPunct::Other => "punctuation",
130 },
131 HlTag::NumericLiteral => "numeric_literal",
132 HlTag::Operator => "operator",
133 HlTag::StringLiteral => "string_literal",
134 HlTag::UnresolvedReference => "unresolved_reference",
135 HlTag::None => "none",
104 } 136 }
105 } 137 }
106} 138}
107 139
108impl fmt::Display for HighlightTag { 140impl fmt::Display for HlTag {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 fmt::Display::fmt(self.as_str(), f) 142 fmt::Display::fmt(self.as_str(), f)
111 } 143 }
112} 144}
113 145
114impl HighlightModifier { 146impl HlMod {
115 const ALL: &'static [HighlightModifier; HighlightModifier::Unsafe as u8 as usize + 1] = &[ 147 const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
116 HighlightModifier::Attribute, 148 HlMod::Attribute,
117 HighlightModifier::ControlFlow, 149 HlMod::ControlFlow,
118 HighlightModifier::Definition, 150 HlMod::Definition,
119 HighlightModifier::Documentation, 151 HlMod::Documentation,
120 HighlightModifier::Injected, 152 HlMod::Injected,
121 HighlightModifier::Mutable, 153 HlMod::Mutable,
122 HighlightModifier::Consuming, 154 HlMod::Consuming,
123 HighlightModifier::Callable, 155 HlMod::Callable,
124 HighlightModifier::Static, 156 HlMod::Static,
125 HighlightModifier::Associated, 157 HlMod::Associated,
126 HighlightModifier::Unsafe, 158 HlMod::Unsafe,
127 ]; 159 ];
128 160
129 fn as_str(self) -> &'static str { 161 fn as_str(self) -> &'static str {
130 match self { 162 match self {
131 HighlightModifier::Attribute => "attribute", 163 HlMod::Attribute => "attribute",
132 HighlightModifier::ControlFlow => "control", 164 HlMod::ControlFlow => "control",
133 HighlightModifier::Definition => "declaration", 165 HlMod::Definition => "declaration",
134 HighlightModifier::Documentation => "documentation", 166 HlMod::Documentation => "documentation",
135 HighlightModifier::Injected => "injected", 167 HlMod::Injected => "injected",
136 HighlightModifier::Mutable => "mutable", 168 HlMod::Mutable => "mutable",
137 HighlightModifier::Consuming => "consuming", 169 HlMod::Consuming => "consuming",
138 HighlightModifier::Unsafe => "unsafe", 170 HlMod::Unsafe => "unsafe",
139 HighlightModifier::Callable => "callable", 171 HlMod::Callable => "callable",
140 HighlightModifier::Static => "static", 172 HlMod::Static => "static",
141 HighlightModifier::Associated => "associated", 173 HlMod::Associated => "associated",
142 } 174 }
143 } 175 }
144 176
@@ -147,7 +179,7 @@ impl HighlightModifier {
147 } 179 }
148} 180}
149 181
150impl fmt::Display for HighlightModifier { 182impl fmt::Display for HlMod {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 fmt::Display::fmt(self.as_str(), f) 184 fmt::Display::fmt(self.as_str(), f)
153 } 185 }
@@ -156,60 +188,63 @@ impl fmt::Display for HighlightModifier {
156impl fmt::Display for Highlight { 188impl fmt::Display for Highlight {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(f, "{}", self.tag)?; 190 write!(f, "{}", self.tag)?;
159 for modifier in self.modifiers.iter() { 191 for modifier in self.mods.iter() {
160 write!(f, ".{}", modifier)? 192 write!(f, ".{}", modifier)?
161 } 193 }
162 Ok(()) 194 Ok(())
163 } 195 }
164} 196}
165 197
166impl From<HighlightTag> for Highlight { 198impl From<HlTag> for Highlight {
167 fn from(tag: HighlightTag) -> Highlight { 199 fn from(tag: HlTag) -> Highlight {
168 Highlight::new(tag) 200 Highlight::new(tag)
169 } 201 }
170} 202}
171 203
172impl Highlight { 204impl Highlight {
173 pub(crate) fn new(tag: HighlightTag) -> Highlight { 205 pub(crate) fn new(tag: HlTag) -> Highlight {
174 Highlight { tag, modifiers: HighlightModifiers::default() } 206 Highlight { tag, mods: HlMods::default() }
207 }
208 pub fn is_empty(&self) -> bool {
209 self.tag == HlTag::None && self.mods == HlMods::default()
175 } 210 }
176} 211}
177 212
178impl ops::BitOr<HighlightModifier> for HighlightTag { 213impl ops::BitOr<HlMod> for HlTag {
179 type Output = Highlight; 214 type Output = Highlight;
180 215
181 fn bitor(self, rhs: HighlightModifier) -> Highlight { 216 fn bitor(self, rhs: HlMod) -> Highlight {
182 Highlight::new(self) | rhs 217 Highlight::new(self) | rhs
183 } 218 }
184} 219}
185 220
186impl ops::BitOrAssign<HighlightModifier> for HighlightModifiers { 221impl ops::BitOrAssign<HlMod> for HlMods {
187 fn bitor_assign(&mut self, rhs: HighlightModifier) { 222 fn bitor_assign(&mut self, rhs: HlMod) {
188 self.0 |= rhs.mask(); 223 self.0 |= rhs.mask();
189 } 224 }
190} 225}
191 226
192impl ops::BitOrAssign<HighlightModifier> for Highlight { 227impl ops::BitOrAssign<HlMod> for Highlight {
193 fn bitor_assign(&mut self, rhs: HighlightModifier) { 228 fn bitor_assign(&mut self, rhs: HlMod) {
194 self.modifiers |= rhs; 229 self.mods |= rhs;
195 } 230 }
196} 231}
197 232
198impl ops::BitOr<HighlightModifier> for Highlight { 233impl ops::BitOr<HlMod> for Highlight {
199 type Output = Highlight; 234 type Output = Highlight;
200 235
201 fn bitor(mut self, rhs: HighlightModifier) -> Highlight { 236 fn bitor(mut self, rhs: HlMod) -> Highlight {
202 self |= rhs; 237 self |= rhs;
203 self 238 self
204 } 239 }
205} 240}
206 241
207impl HighlightModifiers { 242impl HlMods {
208 pub fn contains(self, m: HighlightModifier) -> bool { 243 pub fn contains(self, m: HlMod) -> bool {
209 self.0 & m.mask() == m.mask() 244 self.0 & m.mask() == m.mask()
210 } 245 }
211 246
212 pub fn iter(self) -> impl Iterator<Item = HighlightModifier> { 247 pub fn iter(self) -> impl Iterator<Item = HlMod> {
213 HighlightModifier::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask()) 248 HlMod::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask())
214 } 249 }
215} 250}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 506ebe60e..e36e6fc3f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -36,22 +36,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40 40
41<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span> 41<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span>
42 42
43<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span> 43<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span>
44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
46<span class="punctuation">}</span> 46<span class="brace">}</span>
47 47
48<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span> 48<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
49 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 49 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
50 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 50 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51<span class="punctuation">}</span> 51<span class="brace">}</span>
52 52
53<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span> 53<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
56<span class="punctuation">}</span> 56<span class="brace">}</span>
57 </code></pre> \ No newline at end of file 57 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 4dd7413ba..6dadda1c1 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -37,67 +37,72 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="comment documentation">/// ```</span> 39<pre><code><span class="comment documentation">/// ```</span>
40<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="punctuation injected">_</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="punctuation injected">;</span><span class="punctuation injected"> 40<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span>
41</span><span class="comment documentation">/// ```</span> 41<span class="comment documentation">/// ```</span>
42<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> 42<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
43 <span class="field declaration">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span><span class="punctuation">,</span> 43 <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span>
44<span class="punctuation">}</span> 44<span class="brace">}</span>
45 45
46<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 46<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
47 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="punctuation">;</span> 47 <span class="comment documentation">/// ```</span>
48 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span>
49 <span class="comment">// KILLER WHALE</span>
50 <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span>
51 <span class="comment documentation">/// ```</span>
52 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span>
48 53
49 <span class="comment documentation">/// Constructs a new `Foo`.</span> 54 <span class="comment documentation">/// Constructs a new `Foo`.</span>
50 <span class="comment documentation">///</span> 55 <span class="comment documentation">///</span>
51 <span class="comment documentation">/// # Examples</span> 56 <span class="comment documentation">/// # Examples</span>
52 <span class="comment documentation">///</span> 57 <span class="comment documentation">///</span>
53 <span class="comment documentation">/// ```</span> 58 <span class="comment documentation">/// ```</span>
54 <span class="comment documentation">/// #</span><span class="dummy injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span> 59 <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span>
55 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="keyword injected">mut</span><span class="dummy injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 60 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
56</span> <span class="comment documentation">/// ```</span> 61 <span class="comment documentation">/// ```</span>
57 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 62 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span>
58 <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> 63 <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span>
59 <span class="punctuation">}</span> 64 <span class="brace">}</span>
60 65
61 <span class="comment documentation">/// `bar` method on `Foo`.</span> 66 <span class="comment documentation">/// `bar` method on `Foo`.</span>
62 <span class="comment documentation">///</span> 67 <span class="comment documentation">///</span>
63 <span class="comment documentation">/// # Examples</span> 68 <span class="comment documentation">/// # Examples</span>
64 <span class="comment documentation">///</span> 69 <span class="comment documentation">///</span>
65 <span class="comment documentation">/// ```</span> 70 <span class="comment documentation">/// ```</span>
66 <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="dummy injected"> </span><span class="module injected">x</span><span class="operator injected">::</span><span class="module injected">y</span><span class="punctuation injected">;</span> 71 <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="none injected"> </span><span class="module injected">x</span><span class="operator injected">::</span><span class="module injected">y</span><span class="semicolon injected">;</span>
67 <span class="comment documentation">///</span> 72 <span class="comment documentation">///</span>
68 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">foo</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> 73 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
69 <span class="comment documentation">///</span> 74 <span class="comment documentation">///</span>
70 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> 75 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
71 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="dummy injected">foo</span><span class="operator injected">.</span><span class="dummy injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> 76 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
72 <span class="comment documentation">///</span> 77 <span class="comment documentation">///</span>
73 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">bar</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="dummy injected"> </span><span class="operator injected">||</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span> 78 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
74 <span class="comment documentation">///</span> 79 <span class="comment documentation">///</span>
75 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line 80 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span>
76 </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span> 81 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
77 <span class="comment documentation">///</span> 82 <span class="comment documentation">///</span>
78 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="string_literal injected">"Foo 83 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span>
79 </span><span class="comment documentation">/// </span><span class="string_literal injected"> bar 84 <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span>
80 </span><span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="punctuation injected">;</span> 85 <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span>
81 <span class="comment documentation">///</span> 86 <span class="comment documentation">///</span>
82 <span class="comment documentation">/// ```</span> 87 <span class="comment documentation">/// ```</span>
83 <span class="comment documentation">///</span> 88 <span class="comment documentation">///</span>
84 <span class="comment documentation">/// ```rust,no_run</span> 89 <span class="comment documentation">/// ```rust,no_run</span>
85 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">foobar</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 90 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foobar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
86</span> <span class="comment documentation">/// ```</span> 91 <span class="comment documentation">/// ```</span>
87 <span class="comment documentation">///</span> 92 <span class="comment documentation">///</span>
88 <span class="comment documentation">/// ```sh</span> 93 <span class="comment documentation">/// ```sh</span>
89 <span class="comment documentation">/// echo 1</span> 94 <span class="comment documentation">/// echo 1</span>
90 <span class="comment documentation">/// ```</span> 95 <span class="comment documentation">/// ```</span>
91 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span> 96 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
92 <span class="bool_literal">true</span> 97 <span class="bool_literal">true</span>
93 <span class="punctuation">}</span> 98 <span class="brace">}</span>
94<span class="punctuation">}</span> 99<span class="brace">}</span>
95 100
96<span class="comment documentation">/// ```</span> 101<span class="comment documentation">/// ```</span>
97<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="punctuation injected">(</span><span class="numeric_literal injected">1</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 102<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
98</span><span class="comment documentation">/// ```</span> 103<span class="comment documentation">/// ```</span>
99<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span> 104<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
100 <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 105 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
101 <span class="punctuation">$</span>expr 106 <span class="punctuation">$</span>expr
102 <span class="punctuation">}</span> 107 <span class="brace">}</span>
103<span class="punctuation">}</span></code></pre> \ No newline at end of file 108<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index ed452586a..6f7a7ffff 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -36,6 +36,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="punctuation">;</span> 39<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="semicolon">;</span>
40<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="punctuation">;</span> 40<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="semicolon">;</span>
41</code></pre> \ No newline at end of file 41</code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 92e7dc3e4..753b535b5 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -36,14 +36,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40 40
41<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 41<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
42 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> 42 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
43 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> 43 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
44 <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 44 <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
45 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> 45 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
46 <span class="punctuation">}</span> 46 <span class="brace">}</span>
47 <span class="punctuation">}</span><span class="string_literal">"#</span> 47 <span class="brace">}</span><span class="string_literal">"#</span>
48 <span class="punctuation">)</span><span class="punctuation">;</span> 48 <span class="parenthesis">)</span><span class="semicolon">;</span>
49<span class="punctuation">}</span></code></pre> \ No newline at end of file 49<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 31dad5d42..66d80c4b6 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -36,64 +36,64 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span> 39<pre><code><span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
40 <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">{</span> 40 <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">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
41 <span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> 41 <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>
42 <span class="punctuation">}</span><span class="punctuation">)</span> 42 <span class="brace">}</span><span class="parenthesis">)</span>
43<span class="punctuation">}</span> 43<span class="brace">}</span>
44<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> 44<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
45<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="punctuation">{</span> 45<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span>
46 <span class="punctuation">(</span><span class="punctuation">$</span>fmt<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">{</span> <span class="comment">/* compiler built-in */</span> <span class="punctuation">}</span><span class="punctuation">}</span><span class="punctuation">;</span> 46 <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
47 <span class="punctuation">(</span><span class="punctuation">$</span>fmt<span class="punctuation">:</span>expr<span class="punctuation">,</span> <span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>args<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">{</span> <span class="comment">/* compiler built-in */</span> <span class="punctuation">}</span><span class="punctuation">}</span><span class="punctuation">;</span> 47 <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>args<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
48<span class="punctuation">}</span> 48<span class="brace">}</span>
49 49
50<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 50<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
51 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> 51 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
52 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "Hello"</span> 52 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
53 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"world"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "Hello, world!"</span> 53 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
54 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "The number is 1"</span> 54 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
55 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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">"</span><span class="punctuation">,</span> <span class="punctuation">(</span><span class="numeric_literal">3</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "(3, 4)"</span> 55 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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">"</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="numeric_literal">3</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
56 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "4"</span> 56 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
57 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "1 2"</span> 57 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
58 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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">"</span><span class="punctuation">,</span> <span class="numeric_literal">42</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span> 58 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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">"</span><span class="comma">,</span> <span class="numeric_literal">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
59 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "2 1 1 2"</span> 59 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
60 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "test"</span> 60 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
61 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "2 1"</span> 61 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
62 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="punctuation">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="punctuation">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "a 3 b"</span> 62 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="comma">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="comma">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
63 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "{2}"</span> 63 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
64 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 64 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
65 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 65 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
66 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 66 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
67 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 67 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
68 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 68 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
69 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 69 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
70 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 70 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
71 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 71 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
72 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 72 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </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">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
73 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">27</span><span class="punctuation">)</span><span class="punctuation">;</span> 73 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
74 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 74 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
75 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="punctuation">-</span><span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 75 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="punctuation">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
76 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">27</span><span class="punctuation">)</span><span class="punctuation">;</span> 76 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
77 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 77 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
78 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> 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">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 78 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> 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">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
79 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 79 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
80 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 80 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
81 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 81 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
82 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 82 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
83 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 fractional digits"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="punctuation">)</span><span class="punctuation">;</span> 83 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
84 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 characters"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span> 84 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
85 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</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">` has 3 right-aligned characters"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span> 85 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</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">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
86 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello {{}}"</span><span class="punctuation">)</span><span class="punctuation">;</span> 86 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello {{}}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
87 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"{{ Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span> 87 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"{{ Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
88 88
89 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"world"</span><span class="punctuation">)</span><span class="punctuation">;</span> 89 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
90 90
91 <span class="comment">// escape sequences</span> 91 <span class="comment">// escape sequences</span>
92 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="punctuation">)</span><span class="punctuation">;</span> 92 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
93 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="punctuation">)</span><span class="punctuation">;</span> 93 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
94 94
95 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> 95 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
96 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> 96 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
97 97
98 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="punctuation">,</span> thingy<span class="punctuation">,</span> n2<span class="punctuation">)</span><span class="punctuation">;</span> 98 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> thingy<span class="comma">,</span> n2<span class="parenthesis">)</span><span class="semicolon">;</span>
99<span class="punctuation">}</span></code></pre> \ No newline at end of file 99<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index e3a0aa317..9d4d6d4a0 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -36,65 +36,65 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40 40
41<span class="keyword">union</span> <span class="union declaration">Union</span> <span class="punctuation">{</span> 41<span class="keyword">union</span> <span class="union declaration">Union</span> <span class="brace">{</span>
42 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 42 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
43 <span class="field declaration">b</span><span class="punctuation">:</span> <span class="builtin_type">f32</span><span class="punctuation">,</span> 43 <span class="field declaration">b</span><span class="colon">:</span> <span class="builtin_type">f32</span><span class="comma">,</span>
44<span class="punctuation">}</span> 44<span class="brace">}</span>
45 45
46<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span> 46<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span>
47 47
48<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span> 48<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span>
49 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 49 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
50<span class="punctuation">}</span> 50<span class="brace">}</span>
51 51
52<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span> 52<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span>
53 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u8</span> 53 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u8</span>
54<span class="punctuation">}</span> 54<span class="brace">}</span>
55 55
56<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 56<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="colon">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
57 57
58<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="punctuation attribute">(</span><span class="attribute attribute">packed</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> 58<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
59<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> 59<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span>
60 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> 60 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span>
61<span class="punctuation">}</span> 61<span class="brace">}</span>
62 62
63<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span> 63<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
64 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span><span class="punctuation">;</span> 64 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
65<span class="punctuation">}</span> 65<span class="brace">}</span>
66 66
67<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="punctuation">{</span> 67<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
68 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 68 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
69<span class="punctuation">}</span> 69<span class="brace">}</span>
70 70
71<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 71<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
72 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span> 72 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
73 <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 73 <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
74 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> 74 <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
75 <span class="comment">// unsafe fn and method calls</span> 75 <span class="comment">// unsafe fn and method calls</span>
76 <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 76 <span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
77 <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> 77 <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="semicolon">;</span>
78 <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> 78 <span class="keyword control">match</span> <span class="variable">u</span> <span class="brace">{</span>
79 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 79 <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
80 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 80 <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">a</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
81 <span class="punctuation">}</span> 81 <span class="brace">}</span>
82 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 82 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
83 83
84 <span class="comment">// unsafe deref</span> 84 <span class="comment">// unsafe deref</span>
85 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span> 85 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span>
86 86
87 <span class="comment">// unsafe access to a static mut</span> 87 <span class="comment">// unsafe access to a static mut</span>
88 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> 88 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
89 89
90 <span class="comment">// unsafe ref of packed fields</span> 90 <span class="comment">// unsafe ref of packed fields</span>
91 <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 91 <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
92 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> 92 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
93 <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> 93 <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
94 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> 94 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
95 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> 95 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
96 96
97 <span class="comment">// unsafe auto ref of packed field</span> 97 <span class="comment">// unsafe auto ref of packed field</span>
98 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 98 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
99 <span class="punctuation">}</span> 99 <span class="brace">}</span>
100<span class="punctuation">}</span></code></pre> \ No newline at end of file 100<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 02270b077..6b7447c46 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -36,187 +36,187 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span> 39<pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span>
40<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span> 40<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
41 41
42<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> 42<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
43<span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 43<span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
44 44
45<span class="comment">// Needed for function consuming vs normal</span> 45<span class="comment">// Needed for function consuming vs normal</span>
46<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> 46<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="brace">{</span>
47 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> 47 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span>
48 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 48 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
49<span class="punctuation">}</span> 49<span class="brace">}</span>
50 50
51<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> 51<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="brace">{</span>
52 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span> 52 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
53 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 53 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
54 54
55 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span> 55 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
56 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 56 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
57 57
58 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span> 58 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
59 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 59 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
60<span class="punctuation">}</span> 60<span class="brace">}</span>
61 61
62 62
63<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> 63<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
64 <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> 64 <span class="keyword">pub</span> <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
65 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> 65 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
66<span class="punctuation">}</span> 66<span class="brace">}</span>
67 67
68<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span> 68<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
69 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span> 69 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
70<span class="punctuation">}</span> 70<span class="brace">}</span>
71 71
72<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 72<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
73 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 73 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
74 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 74 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
75 <span class="punctuation">}</span> 75 <span class="brace">}</span>
76<span class="punctuation">}</span> 76<span class="brace">}</span>
77 77
78<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 78<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
79 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 79 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
80 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> 80 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span>
81 <span class="punctuation">}</span> 81 <span class="brace">}</span>
82 82
83 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 83 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
84 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 84 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
85 <span class="punctuation">}</span> 85 <span class="brace">}</span>
86 86
87 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 87 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
88 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 88 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
89 <span class="punctuation">}</span> 89 <span class="brace">}</span>
90<span class="punctuation">}</span> 90<span class="brace">}</span>
91 91
92<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="punctuation attribute">(</span><span class="attribute attribute">Copy</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> 92<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="parenthesis attribute">(</span><span class="attribute attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
93<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> 93<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="brace">{</span>
94 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 94 <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
95<span class="punctuation">}</span> 95<span class="brace">}</span>
96 96
97<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> 97<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span>
98 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 98 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
99 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> 99 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span>
100 <span class="punctuation">}</span> 100 <span class="brace">}</span>
101 101
102 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 102 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
103 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 103 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
104 <span class="punctuation">}</span> 104 <span class="brace">}</span>
105 105
106 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 106 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
107 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 107 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
108 <span class="punctuation">}</span> 108 <span class="brace">}</span>
109<span class="punctuation">}</span> 109<span class="brace">}</span>
110 110
111<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 111<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
112 112
113<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">&lt;</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="punctuation">{</span> 113<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="angle">&lt;</span><span class="lifetime declaration">'a</span><span class="comma">,</span> <span class="type_param declaration">T</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="brace">{</span>
114 <span class="function">foo</span><span class="operator">::</span><span class="punctuation">&lt;</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> 114 <span class="function">foo</span><span class="operator">::</span><span class="angle">&lt;</span><span class="lifetime">'a</span><span class="comma">,</span> <span class="builtin_type">i32</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
115<span class="punctuation">}</span> 115<span class="brace">}</span>
116 116
117<span class="keyword">fn</span> <span class="function declaration">never</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="punctuation">{</span> 117<span class="keyword">fn</span> <span class="function declaration">never</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="brace">{</span>
118 <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span> 118 <span class="keyword control">loop</span> <span class="brace">{</span><span class="brace">}</span>
119<span class="punctuation">}</span> 119<span class="brace">}</span>
120 120
121<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="punctuation">&lt;</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="punctuation">:</span> <span class="builtin_type">usize</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="punctuation">{</span> 121<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle">&lt;</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="brace">{</span>
122 <span class="const_param">FOO</span> 122 <span class="const_param">FOO</span>
123<span class="punctuation">}</span> 123<span class="brace">}</span>
124 124
125<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span> 125<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span>
126<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span> 126<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param declaration callable">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
127 <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> 127 <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
128<span class="punctuation">}</span> 128<span class="brace">}</span>
129 129
130<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 130<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="brace">{</span><span class="brace">}</span>
131 131
132<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 132<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
133 <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 133 <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
134<span class="punctuation">}</span> 134<span class="brace">}</span>
135 135
136<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">def_fn</span> <span class="punctuation">{</span> 136<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
137 <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">}</span> 137 <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
138<span class="punctuation">}</span> 138<span class="brace">}</span>
139 139
140<span class="macro">def_fn!</span> <span class="punctuation">{</span> 140<span class="macro">def_fn!</span> <span class="brace">{</span>
141 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 141 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
142 <span class="numeric_literal">100</span> 142 <span class="numeric_literal">100</span>
143 <span class="punctuation">}</span> 143 <span class="brace">}</span>
144<span class="punctuation">}</span> 144<span class="brace">}</span>
145 145
146<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span> 146<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
147 <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 147 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
148 <span class="punctuation">$</span>expr 148 <span class="punctuation">$</span>expr
149 <span class="punctuation">}</span> 149 <span class="brace">}</span>
150<span class="punctuation">}</span> 150<span class="brace">}</span>
151 151
152<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">keyword_frag</span> <span class="punctuation">{</span> 152<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span>
153 <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">:</span>ty<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">)</span> 153 <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
154<span class="punctuation">}</span> 154<span class="brace">}</span>
155 155
156<span class="comment">// comment</span> 156<span class="comment">// comment</span>
157<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 157<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
158 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> 158 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
159 159
160 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 160 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
161 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span> 161 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span>
162 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span> 162 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="semicolon">;</span>
163 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span> 163 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="parenthesis">(</span><span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="numeric_literal">1</span> <span class="brace">}</span><span class="parenthesis">)</span><span class="semicolon">;</span>
164 <span class="punctuation">}</span> 164 <span class="brace">}</span>
165 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> 165 <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
166 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> 166 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="parenthesis">(</span><span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
167 <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> 167 <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
168 <span class="punctuation">}</span> 168 <span class="brace">}</span>
169 169
170 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="punctuation">{</span> 170 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="brace">{</span>
171 <span class="comment">// Do nothing</span> 171 <span class="comment">// Do nothing</span>
172 <span class="punctuation">}</span> 172 <span class="brace">}</span>
173 173
174 <span class="macro">noop!</span><span class="punctuation">(</span><span class="macro">noop</span><span class="macro">!</span><span class="punctuation">(</span><span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> 174 <span class="macro">noop!</span><span class="parenthesis">(</span><span class="macro">noop</span><span class="macro">!</span><span class="parenthesis">(</span><span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
175 175
176 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="punctuation">;</span> 176 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="semicolon">;</span>
177 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span> 177 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="semicolon">;</span>
178 <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="punctuation">;</span> 178 <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="semicolon">;</span>
179 179
180 <span class="keyword">let</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable declaration">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span><span class="punctuation">;</span> 180 <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="field">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable">z</span><span class="comma">,</span> <span class="field">y</span> <span class="brace">}</span><span class="semicolon">;</span>
181 181
182 <span class="variable">y</span><span class="punctuation">;</span> 182 <span class="variable">y</span><span class="semicolon">;</span>
183 183
184 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 184 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
185 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 185 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
186 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 186 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
187 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 187 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
188 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> 188 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
189 189
190 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 190 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span>
191 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 191 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
192 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 192 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
193 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> 193 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span>
194 194
195 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> 195 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
196 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="punctuation">;</span> 196 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span>
197 197
198 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="punctuation">;</span> 198 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span>
199 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> 199 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span>
200 200
201 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> 201 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
202 202
203 <span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span> 203 <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span>
204 <span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span> 204 <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span>
205 <span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span> 205 <span class="keyword control">continue</span> <span class="label">'foo</span><span class="semicolon">;</span>
206 <span class="punctuation">}</span> 206 <span class="brace">}</span>
207<span class="punctuation">}</span> 207<span class="brace">}</span>
208 208
209<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 209<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
210 <span class="enum_variant declaration">Some</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">)</span><span class="punctuation">,</span> 210 <span class="enum_variant declaration">Some</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="comma">,</span>
211 <span class="enum_variant declaration">None</span><span class="punctuation">,</span> 211 <span class="enum_variant declaration">None</span><span class="comma">,</span>
212<span class="punctuation">}</span> 212<span class="brace">}</span>
213<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span> 213<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
214 214
215<span class="keyword">impl</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 215<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
216 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="punctuation">&lt;</span><span class="type_param declaration">U</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">U</span><span class="punctuation">&gt;</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 216 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
217 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span> 217 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
218 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 218 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
219 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="punctuation">,</span> 219 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
220 <span class="punctuation">}</span> 220 <span class="brace">}</span>
221 <span class="punctuation">}</span> 221 <span class="brace">}</span>
222<span class="punctuation">}</span></code></pre> \ No newline at end of file 222<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html
index a54d303b4..78dfec951 100644
--- a/crates/ide/src/syntax_highlighting/test_data/injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/injection.html
@@ -36,13 +36,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">f</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword">fn</span> <span class="function declaration">f</span><span class="parenthesis">(</span><span class="value_param declaration">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
41 <span class="function">f</span><span class="punctuation">(</span><span class="string_literal">r"</span> 41 <span class="function">f</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
42<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 42<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
43 <span class="function">foo</span><span class="punctuation">(</span>$0<span class="punctuation">{</span> 43 <span class="function">foo</span><span class="parenthesis">(</span><span class="keyword">$0</span><span class="brace">{</span>
44 <span class="numeric_literal">92</span> 44 <span class="numeric_literal">92</span>
45 <span class="punctuation">}</span>$0<span class="punctuation">)</span> 45 <span class="brace">}</span><span class="keyword">$0</span><span class="parenthesis">)</span>
46<span class="punctuation">}</span><span class="string_literal">"</span><span class="punctuation">)</span><span class="punctuation">;</span> 46<span class="brace">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
47<span class="punctuation">}</span> 47<span class="brace">}</span>
48 </code></pre> \ No newline at end of file 48 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index 8b3dfa69f..e64f2e5e9 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -36,15 +36,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 39<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
42 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 42 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
43 43
44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> 44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span>
45 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 45 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
46<span class="punctuation">}</span> 46<span class="brace">}</span>
47 47
48<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 48<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
49 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 49 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
50<span class="punctuation">}</span></code></pre> \ No newline at end of file 50<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 9e1a3974c..a62704c39 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -446,6 +446,11 @@ struct Foo {
446} 446}
447 447
448impl Foo { 448impl Foo {
449 /// ```
450 /// let _ = "Call me
451 // KILLER WHALE
452 /// Ishmael.";
453 /// ```
449 pub const bar: bool = true; 454 pub const bar: bool = true;
450 455
451 /// Constructs a new `Foo`. 456 /// Constructs a new `Foo`.
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index be1c64b03..d68fe42b0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,8 +6,8 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, ConstParam, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, 9 db::HirDatabase, Crate, Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef,
10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, 10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode},
@@ -24,9 +24,7 @@ pub enum Definition {
24 ModuleDef(ModuleDef), 24 ModuleDef(ModuleDef),
25 SelfType(Impl), 25 SelfType(Impl),
26 Local(Local), 26 Local(Local),
27 TypeParam(TypeParam), 27 GenericParam(GenericParam),
28 LifetimeParam(LifetimeParam),
29 ConstParam(ConstParam),
30 Label(Label), 28 Label(Label),
31} 29}
32 30
@@ -38,9 +36,7 @@ impl Definition {
38 Definition::ModuleDef(it) => it.module(db), 36 Definition::ModuleDef(it) => it.module(db),
39 Definition::SelfType(it) => Some(it.module(db)), 37 Definition::SelfType(it) => Some(it.module(db)),
40 Definition::Local(it) => Some(it.module(db)), 38 Definition::Local(it) => Some(it.module(db)),
41 Definition::TypeParam(it) => Some(it.module(db)), 39 Definition::GenericParam(it) => Some(it.module(db)),
42 Definition::LifetimeParam(it) => Some(it.module(db)),
43 Definition::ConstParam(it) => Some(it.module(db)),
44 Definition::Label(it) => Some(it.module(db)), 40 Definition::Label(it) => Some(it.module(db)),
45 } 41 }
46 } 42 }
@@ -52,9 +48,7 @@ impl Definition {
52 Definition::ModuleDef(def) => def.definition_visibility(db), 48 Definition::ModuleDef(def) => def.definition_visibility(db),
53 Definition::SelfType(_) => None, 49 Definition::SelfType(_) => None,
54 Definition::Local(_) => None, 50 Definition::Local(_) => None,
55 Definition::TypeParam(_) => None, 51 Definition::GenericParam(_) => None,
56 Definition::LifetimeParam(_) => None,
57 Definition::ConstParam(_) => None,
58 Definition::Label(_) => None, 52 Definition::Label(_) => None,
59 } 53 }
60 } 54 }
@@ -80,9 +74,7 @@ impl Definition {
80 }, 74 },
81 Definition::SelfType(_) => return None, 75 Definition::SelfType(_) => return None,
82 Definition::Local(it) => it.name(db)?, 76 Definition::Local(it) => it.name(db)?,
83 Definition::TypeParam(it) => it.name(db), 77 Definition::GenericParam(it) => it.name(db),
84 Definition::LifetimeParam(it) => it.name(db),
85 Definition::ConstParam(it) => it.name(db),
86 Definition::Label(it) => it.name(db), 78 Definition::Label(it) => it.name(db),
87 }; 79 };
88 Some(name) 80 Some(name)
@@ -235,11 +227,11 @@ impl NameClass {
235 }, 227 },
236 ast::TypeParam(it) => { 228 ast::TypeParam(it) => {
237 let def = sema.to_def(&it)?; 229 let def = sema.to_def(&it)?;
238 Some(NameClass::Definition(Definition::TypeParam(def))) 230 Some(NameClass::Definition(Definition::GenericParam(def.into())))
239 }, 231 },
240 ast::ConstParam(it) => { 232 ast::ConstParam(it) => {
241 let def = sema.to_def(&it)?; 233 let def = sema.to_def(&it)?;
242 Some(NameClass::Definition(Definition::ConstParam(def))) 234 Some(NameClass::Definition(Definition::GenericParam(def.into())))
243 }, 235 },
244 _ => None, 236 _ => None,
245 } 237 }
@@ -257,7 +249,7 @@ impl NameClass {
257 match parent { 249 match parent {
258 ast::LifetimeParam(it) => { 250 ast::LifetimeParam(it) => {
259 let def = sema.to_def(&it)?; 251 let def = sema.to_def(&it)?;
260 Some(NameClass::Definition(Definition::LifetimeParam(def))) 252 Some(NameClass::Definition(Definition::GenericParam(def.into())))
261 }, 253 },
262 ast::Label(it) => { 254 ast::Label(it) => {
263 let def = sema.to_def(&it)?; 255 let def = sema.to_def(&it)?;
@@ -393,7 +385,8 @@ impl NameRefClass {
393 | SyntaxKind::WHERE_PRED 385 | SyntaxKind::WHERE_PRED
394 | SyntaxKind::REF_TYPE => sema 386 | SyntaxKind::REF_TYPE => sema
395 .resolve_lifetime_param(lifetime) 387 .resolve_lifetime_param(lifetime)
396 .map(Definition::LifetimeParam) 388 .map(GenericParam::LifetimeParam)
389 .map(Definition::GenericParam)
397 .map(NameRefClass::Definition), 390 .map(NameRefClass::Definition),
398 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check 391 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
399 // if our lifetime is in a LifetimeParam without being the constrained lifetime 392 // if our lifetime is in a LifetimeParam without being the constrained lifetime
@@ -401,7 +394,8 @@ impl NameRefClass {
401 != Some(lifetime) => 394 != Some(lifetime) =>
402 { 395 {
403 sema.resolve_lifetime_param(lifetime) 396 sema.resolve_lifetime_param(lifetime)
404 .map(Definition::LifetimeParam) 397 .map(GenericParam::LifetimeParam)
398 .map(Definition::GenericParam)
405 .map(NameRefClass::Definition) 399 .map(NameRefClass::Definition)
406 } 400 }
407 _ => None, 401 _ => None,
@@ -422,10 +416,10 @@ impl From<PathResolution> for Definition {
422 Definition::ModuleDef(def) 416 Definition::ModuleDef(def)
423 } 417 }
424 PathResolution::Local(local) => Definition::Local(local), 418 PathResolution::Local(local) => Definition::Local(local),
425 PathResolution::TypeParam(par) => Definition::TypeParam(par), 419 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
426 PathResolution::Macro(def) => Definition::Macro(def), 420 PathResolution::Macro(def) => Definition::Macro(def),
427 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), 421 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
428 PathResolution::ConstParam(par) => Definition::ConstParam(par), 422 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
429 } 423 }
430 } 424 }
431} 425}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index e3e5670f1..c6763ae36 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -38,94 +38,7 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
38 38
39#[allow(non_snake_case)] 39#[allow(non_snake_case)]
40impl FamousDefs<'_, '_> { 40impl FamousDefs<'_, '_> {
41 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core 41 pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");
42pub mod convert {
43 pub trait From<T> {
44 fn from(t: T) -> Self;
45 }
46}
47
48pub mod default {
49 pub trait Default {
50 fn default() -> Self;
51 }
52}
53
54pub mod iter {
55 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
56 mod traits {
57 pub(crate) mod iterator {
58 use crate::option::Option;
59 pub trait Iterator {
60 type Item;
61 fn next(&mut self) -> Option<Self::Item>;
62 fn by_ref(&mut self) -> &mut Self {
63 self
64 }
65 fn take(self, n: usize) -> crate::iter::Take<Self> {
66 crate::iter::Take { inner: self }
67 }
68 }
69
70 impl<I: Iterator> Iterator for &mut I {
71 type Item = I::Item;
72 fn next(&mut self) -> Option<I::Item> {
73 (**self).next()
74 }
75 }
76 }
77 pub(crate) mod collect {
78 pub trait IntoIterator {
79 type Item;
80 }
81 }
82 }
83
84 pub use self::sources::*;
85 pub(crate) mod sources {
86 use super::Iterator;
87 use crate::option::Option::{self, *};
88 pub struct Repeat<A> {
89 element: A,
90 }
91
92 pub fn repeat<T>(elt: T) -> Repeat<T> {
93 Repeat { element: elt }
94 }
95
96 impl<A> Iterator for Repeat<A> {
97 type Item = A;
98
99 fn next(&mut self) -> Option<A> {
100 None
101 }
102 }
103 }
104
105 pub use self::adapters::*;
106 pub(crate) mod adapters {
107 use super::Iterator;
108 use crate::option::Option::{self, *};
109 pub struct Take<I> { pub(crate) inner: I }
110 impl<I> Iterator for Take<I> where I: Iterator {
111 type Item = <I as Iterator>::Item;
112 fn next(&mut self) -> Option<<I as Iterator>::Item> {
113 None
114 }
115 }
116 }
117}
118
119pub mod option {
120 pub enum Option<T> { None, Some(T)}
121}
122
123pub mod prelude {
124 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
125}
126#[prelude_import]
127pub use prelude::*;
128"#;
129 42
130 pub fn core(&self) -> Option<Crate> { 43 pub fn core(&self) -> Option<Crate> {
131 self.find_crate("core") 44 self.find_crate("core")
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
new file mode 100644
index 000000000..5e88de64d
--- /dev/null
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -0,0 +1,120 @@
1//- /libcore.rs crate:core
2//! Signatures of traits, types and functions from the core lib for use in tests.
3pub mod convert {
4 pub trait From<T> {
5 fn from(t: T) -> Self;
6 }
7}
8
9pub mod default {
10 pub trait Default {
11 fn default() -> Self;
12 }
13}
14
15pub mod iter {
16 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
17 mod traits {
18 pub(crate) mod iterator {
19 use crate::option::Option;
20 pub trait Iterator {
21 type Item;
22 fn next(&mut self) -> Option<Self::Item>;
23 fn by_ref(&mut self) -> &mut Self {
24 self
25 }
26 fn take(self, n: usize) -> crate::iter::Take<Self> {
27 crate::iter::Take { inner: self }
28 }
29 }
30
31 impl<I: Iterator> Iterator for &mut I {
32 type Item = I::Item;
33 fn next(&mut self) -> Option<I::Item> {
34 (**self).next()
35 }
36 }
37 }
38 pub(crate) mod collect {
39 pub trait IntoIterator {
40 type Item;
41 }
42 }
43 }
44
45 pub use self::sources::*;
46 pub(crate) mod sources {
47 use super::Iterator;
48 use crate::option::Option::{self, *};
49 pub struct Repeat<A> {
50 element: A,
51 }
52
53 pub fn repeat<T>(elt: T) -> Repeat<T> {
54 Repeat { element: elt }
55 }
56
57 impl<A> Iterator for Repeat<A> {
58 type Item = A;
59
60 fn next(&mut self) -> Option<A> {
61 None
62 }
63 }
64 }
65
66 pub use self::adapters::*;
67 pub(crate) mod adapters {
68 use super::Iterator;
69 use crate::option::Option::{self, *};
70 pub struct Take<I> {
71 pub(crate) inner: I,
72 }
73 impl<I> Iterator for Take<I>
74 where
75 I: Iterator,
76 {
77 type Item = <I as Iterator>::Item;
78 fn next(&mut self) -> Option<<I as Iterator>::Item> {
79 None
80 }
81 }
82 }
83}
84
85pub mod ops {
86 #[lang = "fn"]
87 pub trait Fn<Args>: FnMut<Args> {
88 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
89 }
90
91 #[lang = "fn_mut"]
92 pub trait FnMut<Args>: FnOnce<Args> {
93 extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
94 }
95 #[lang = "fn_once"]
96 pub trait FnOnce<Args> {
97 #[lang = "fn_once_output"]
98 type Output;
99 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
100 }
101}
102
103pub mod option {
104 pub enum Option<T> {
105 None,
106 Some(T),
107 }
108}
109
110pub mod prelude {
111 pub use crate::{
112 convert::From,
113 default::Default,
114 iter::{IntoIterator, Iterator},
115 ops::{Fn, FnMut, FnOnce},
116 option::Option::{self, *},
117 };
118}
119#[prelude_import]
120pub use prelude::*;
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index 0782ab070..e9f23adf8 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,4 +1,4 @@
1//! This module contains an import search funcionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics};
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 37b06027c..b5fa46642 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -18,9 +18,43 @@ use crate::{
18 RootDatabase, 18 RootDatabase,
19}; 19};
20 20
21#[derive(Debug, Default, Clone)]
22pub struct UsageSearchResult {
23 pub references: FxHashMap<FileId, Vec<FileReference>>,
24}
25
26impl UsageSearchResult {
27 pub fn is_empty(&self) -> bool {
28 self.references.is_empty()
29 }
30
31 pub fn len(&self) -> usize {
32 self.references.len()
33 }
34
35 pub fn iter(&self) -> impl Iterator<Item = (&FileId, &Vec<FileReference>)> + '_ {
36 self.references.iter()
37 }
38
39 pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ {
40 self.references.iter().flat_map(|(&file_id, refs)| {
41 refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range })
42 })
43 }
44}
45
46impl IntoIterator for UsageSearchResult {
47 type Item = (FileId, Vec<FileReference>);
48 type IntoIter = <FxHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
49
50 fn into_iter(self) -> Self::IntoIter {
51 self.references.into_iter()
52 }
53}
54
21#[derive(Debug, Clone)] 55#[derive(Debug, Clone)]
22pub struct Reference { 56pub struct FileReference {
23 pub file_range: FileRange, 57 pub range: TextRange,
24 pub kind: ReferenceKind, 58 pub kind: ReferenceKind,
25 pub access: Option<ReferenceAccess>, 59 pub access: Option<ReferenceAccess>,
26} 60}
@@ -136,7 +170,7 @@ impl Definition {
136 return SearchScope::new(res); 170 return SearchScope::new(res);
137 } 171 }
138 172
139 if let Definition::LifetimeParam(param) = self { 173 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
140 let range = match param.parent(db) { 174 let range = match param.parent(db) {
141 hir::GenericDef::Function(it) => { 175 hir::GenericDef::Function(it) => {
142 it.source(db).and_then(|src| Some(src.value.syntax().text_range())) 176 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
@@ -252,23 +286,23 @@ impl<'a> FindUsages<'a> {
252 286
253 pub fn at_least_one(self) -> bool { 287 pub fn at_least_one(self) -> bool {
254 let mut found = false; 288 let mut found = false;
255 self.search(&mut |_reference| { 289 self.search(&mut |_, _| {
256 found = true; 290 found = true;
257 true 291 true
258 }); 292 });
259 found 293 found
260 } 294 }
261 295
262 pub fn all(self) -> Vec<Reference> { 296 pub fn all(self) -> UsageSearchResult {
263 let mut res = Vec::new(); 297 let mut res = UsageSearchResult::default();
264 self.search(&mut |reference| { 298 self.search(&mut |file_id, reference| {
265 res.push(reference); 299 res.references.entry(file_id).or_default().push(reference);
266 false 300 false
267 }); 301 });
268 res 302 res
269 } 303 }
270 304
271 fn search(self, sink: &mut dyn FnMut(Reference) -> bool) { 305 fn search(self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
272 let _p = profile::span("FindUsages:search"); 306 let _p = profile::span("FindUsages:search");
273 let sema = self.sema; 307 let sema = self.sema;
274 308
@@ -320,16 +354,14 @@ impl<'a> FindUsages<'a> {
320 fn found_lifetime( 354 fn found_lifetime(
321 &self, 355 &self,
322 lifetime: &ast::Lifetime, 356 lifetime: &ast::Lifetime,
323 sink: &mut dyn FnMut(Reference) -> bool, 357 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
324 ) -> bool { 358 ) -> bool {
325 match NameRefClass::classify_lifetime(self.sema, lifetime) { 359 match NameRefClass::classify_lifetime(self.sema, lifetime) {
326 Some(NameRefClass::Definition(def)) if &def == self.def => { 360 Some(NameRefClass::Definition(def)) if &def == self.def => {
327 let reference = Reference { 361 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
328 file_range: self.sema.original_range(lifetime.syntax()), 362 let reference =
329 kind: ReferenceKind::Lifetime, 363 FileReference { range, kind: ReferenceKind::Lifetime, access: None };
330 access: None, 364 sink(file_id, reference)
331 };
332 sink(reference)
333 } 365 }
334 _ => false, // not a usage 366 _ => false, // not a usage
335 } 367 }
@@ -338,7 +370,7 @@ impl<'a> FindUsages<'a> {
338 fn found_name_ref( 370 fn found_name_ref(
339 &self, 371 &self,
340 name_ref: &ast::NameRef, 372 name_ref: &ast::NameRef,
341 sink: &mut dyn FnMut(Reference) -> bool, 373 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
342 ) -> bool { 374 ) -> bool {
343 match NameRefClass::classify(self.sema, &name_ref) { 375 match NameRefClass::classify(self.sema, &name_ref) {
344 Some(NameRefClass::Definition(def)) if &def == self.def => { 376 Some(NameRefClass::Definition(def)) if &def == self.def => {
@@ -352,46 +384,50 @@ impl<'a> FindUsages<'a> {
352 ReferenceKind::Other 384 ReferenceKind::Other
353 }; 385 };
354 386
355 let reference = Reference { 387 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
356 file_range: self.sema.original_range(name_ref.syntax()), 388 let reference =
357 kind, 389 FileReference { range, kind, access: reference_access(&def, &name_ref) };
358 access: reference_access(&def, &name_ref), 390 sink(file_id, reference)
359 };
360 sink(reference)
361 } 391 }
362 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
393 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
363 let reference = match self.def { 394 let reference = match self.def {
364 Definition::Field(_) if &field == self.def => Reference { 395 Definition::Field(_) if &field == self.def => FileReference {
365 file_range: self.sema.original_range(name_ref.syntax()), 396 range,
366 kind: ReferenceKind::FieldShorthandForField, 397 kind: ReferenceKind::FieldShorthandForField,
367 access: reference_access(&field, &name_ref), 398 access: reference_access(&field, &name_ref),
368 }, 399 },
369 Definition::Local(l) if &local == l => Reference { 400 Definition::Local(l) if &local == l => FileReference {
370 file_range: self.sema.original_range(name_ref.syntax()), 401 range,
371 kind: ReferenceKind::FieldShorthandForLocal, 402 kind: ReferenceKind::FieldShorthandForLocal,
372 access: reference_access(&Definition::Local(local), &name_ref), 403 access: reference_access(&Definition::Local(local), &name_ref),
373 }, 404 },
374 _ => return false, // not a usage 405 _ => return false, // not a usage
375 }; 406 };
376 sink(reference) 407 sink(file_id, reference)
377 } 408 }
378 _ => false, // not a usage 409 _ => false, // not a usage
379 } 410 }
380 } 411 }
381 412
382 fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { 413 fn found_name(
414 &self,
415 name: &ast::Name,
416 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
417 ) -> bool {
383 match NameClass::classify(self.sema, name) { 418 match NameClass::classify(self.sema, name) {
384 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { 419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => {
385 let reference = match self.def { 420 if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) {
386 Definition::Field(_) if &field_ref == self.def => Reference { 421 return false;
387 file_range: self.sema.original_range(name.syntax()), 422 }
388 kind: ReferenceKind::FieldShorthandForField, 423 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
389 // FIXME: mutable patterns should have `Write` access 424 let reference = FileReference {
390 access: Some(ReferenceAccess::Read), 425 range,
391 }, 426 kind: ReferenceKind::FieldShorthandForField,
392 _ => return false, // not a usage 427 // FIXME: mutable patterns should have `Write` access
428 access: Some(ReferenceAccess::Read),
393 }; 429 };
394 sink(reference) 430 sink(file_id, reference)
395 } 431 }
396 _ => false, // not a usage 432 _ => false, // not a usage
397 } 433 }
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index b3472879d..19543d777 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -24,7 +24,7 @@ use crate::{
24#[derive(Debug, PartialEq, Eq)] 24#[derive(Debug, PartialEq, Eq)]
25pub enum ParseError { 25pub enum ParseError {
26 Expected(String), 26 Expected(String),
27 RepetitionEmtpyTokenTree, 27 RepetitionEmptyTokenTree,
28} 28}
29 29
30#[derive(Debug, PartialEq, Eq, Clone)] 30#[derive(Debug, PartialEq, Eq, Clone)]
@@ -270,7 +270,7 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
270 } 270 }
271 false 271 false
272 }) { 272 }) {
273 return Err(ParseError::RepetitionEmtpyTokenTree); 273 return Err(ParseError::RepetitionEmptyTokenTree);
274 } 274 }
275 } 275 }
276 validate(subtree)? 276 validate(subtree)?
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index c6d615c81..d32e60521 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -378,7 +378,7 @@ pub(super) fn match_repeat(
378 src: &mut TtIter, 378 src: &mut TtIter,
379) -> Result<(), ExpandError> { 379) -> Result<(), ExpandError> {
380 // Dirty hack to make macro-expansion terminate. 380 // Dirty hack to make macro-expansion terminate.
381 // This should be replaced by a propper macro-by-example implementation 381 // This should be replaced by a proper macro-by-example implementation
382 let mut limit = 65536; 382 let mut limit = 65536;
383 let mut counter = 0; 383 let mut counter = 0;
384 384
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs
index 27b2ac777..59a3c80a8 100644
--- a/crates/mbe/src/mbe_expander/transcriber.rs
+++ b/crates/mbe/src/mbe_expander/transcriber.rs
@@ -67,7 +67,7 @@ struct NestingState {
67 /// because there is no variable in use by the current repetition 67 /// because there is no variable in use by the current repetition
68 hit: bool, 68 hit: bool,
69 /// `at_end` is currently necessary to tell `expand_repeat` if it should stop 69 /// `at_end` is currently necessary to tell `expand_repeat` if it should stop
70 /// because there is no more value avaible for the current repetition 70 /// because there is no more value available for the current repetition
71 at_end: bool, 71 at_end: bool,
72} 72}
73 73
@@ -179,11 +179,7 @@ fn expand_repeat(
179 179
180 counter += 1; 180 counter += 1;
181 if counter == limit { 181 if counter == limit {
182 log::warn!( 182 log::warn!("expand_tt in repeat pattern exceed limit => {:#?}\n{:#?}", template, ctx);
183 "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
184 template,
185 ctx
186 );
187 break; 183 break;
188 } 184 }
189 185
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 671036e1c..e648519f9 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -149,7 +149,7 @@ impl TokenMap {
149 } 149 }
150 150
151 fn remove_delim(&mut self, idx: usize) { 151 fn remove_delim(&mut self, idx: usize) {
152 // FIXME: This could be accidently quadratic 152 // FIXME: This could be accidentally quadratic
153 self.entries.remove(idx); 153 self.entries.remove(idx);
154 } 154 }
155} 155}
@@ -476,14 +476,14 @@ impl Convertor {
476 476
477#[derive(Debug)] 477#[derive(Debug)]
478enum SynToken { 478enum SynToken {
479 Ordiniary(SyntaxToken), 479 Ordinary(SyntaxToken),
480 Punch(SyntaxToken, TextSize), 480 Punch(SyntaxToken, TextSize),
481} 481}
482 482
483impl SynToken { 483impl SynToken {
484 fn token(&self) -> &SyntaxToken { 484 fn token(&self) -> &SyntaxToken {
485 match self { 485 match self {
486 SynToken::Ordiniary(it) => it, 486 SynToken::Ordinary(it) => it,
487 SynToken::Punch(it, _) => it, 487 SynToken::Punch(it, _) => it,
488 } 488 }
489 } 489 }
@@ -495,7 +495,7 @@ impl SrcToken for SynToken {
495 } 495 }
496 fn to_char(&self) -> Option<char> { 496 fn to_char(&self) -> Option<char> {
497 match self { 497 match self {
498 SynToken::Ordiniary(_) => None, 498 SynToken::Ordinary(_) => None,
499 SynToken::Punch(it, i) => it.text().chars().nth((*i).into()), 499 SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
500 } 500 }
501 } 501 }
@@ -535,7 +535,7 @@ impl TokenConvertor for Convertor {
535 } else { 535 } else {
536 self.punct_offset = None; 536 self.punct_offset = None;
537 let range = curr.text_range(); 537 let range = curr.text_range();
538 (SynToken::Ordiniary(curr), range) 538 (SynToken::Ordinary(curr), range)
539 }; 539 };
540 540
541 Some(token) 541 Some(token)
@@ -557,7 +557,7 @@ impl TokenConvertor for Convertor {
557 let token = if curr.kind().is_punct() { 557 let token = if curr.kind().is_punct() {
558 SynToken::Punch(curr, 0.into()) 558 SynToken::Punch(curr, 0.into())
559 } else { 559 } else {
560 SynToken::Ordiniary(curr) 560 SynToken::Ordinary(curr)
561 }; 561 };
562 Some(token) 562 Some(token)
563 } 563 }
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 17ddd0a9c..9ff901e97 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -1988,7 +1988,7 @@ fn test_no_space_after_semi_colon() {
1988#[test] 1988#[test]
1989fn test_rustc_issue_57597() { 1989fn test_rustc_issue_57597() {
1990 fn test_error(fixture: &str) { 1990 fn test_error(fixture: &str) {
1991 assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree); 1991 assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmptyTokenTree);
1992 } 1992 }
1993 1993
1994 test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); 1994 test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }");
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index c7a3556a7..d61950b96 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -15,8 +15,16 @@ use super::*;
15// let _ = b"e"; 15// let _ = b"e";
16// let _ = br"f"; 16// let _ = br"f";
17// } 17// }
18pub(crate) const LITERAL_FIRST: TokenSet = 18pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
19 TokenSet::new(&[TRUE_KW, FALSE_KW, INT_NUMBER, FLOAT_NUMBER, BYTE, CHAR, STRING, BYTE_STRING]); 19 T![true],
20 T![false],
21 INT_NUMBER,
22 FLOAT_NUMBER,
23 BYTE,
24 CHAR,
25 STRING,
26 BYTE_STRING,
27]);
20 28
21pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> { 29pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
22 if !p.at_ts(LITERAL_FIRST) { 30 if !p.at_ts(LITERAL_FIRST) {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index cf4168d32..2070ce163 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -27,19 +27,19 @@ pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
27} 27}
28 28
29pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[ 29pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[
30 FN_KW, 30 T![fn],
31 STRUCT_KW, 31 T![struct],
32 ENUM_KW, 32 T![enum],
33 IMPL_KW, 33 T![impl],
34 TRAIT_KW, 34 T![trait],
35 CONST_KW, 35 T![const],
36 STATIC_KW, 36 T![static],
37 LET_KW, 37 T![let],
38 MOD_KW, 38 T![mod],
39 PUB_KW, 39 T![pub],
40 CRATE_KW, 40 T![crate],
41 USE_KW, 41 T![use],
42 MACRO_KW, 42 T![macro],
43 T![;], 43 T![;],
44]); 44]);
45 45
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs
index ab9a12b4d..d076974ed 100644
--- a/crates/parser/src/grammar/items/traits.rs
+++ b/crates/parser/src/grammar/items/traits.rs
@@ -110,7 +110,7 @@ fn choose_type_params_over_qpath(p: &Parser) -> bool {
110 if !p.at(T![<]) { 110 if !p.at(T![<]) {
111 return false; 111 return false;
112 } 112 }
113 if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == CONST_KW { 113 if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == T![const] {
114 return true; 114 return true;
115 } 115 }
116 (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT) 116 (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT)
diff --git a/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs
index 20e6a13cf..5cb8b08e7 100644
--- a/crates/parser/src/grammar/items/use_item.rs
+++ b/crates/parser/src/grammar/items/use_item.rs
@@ -46,7 +46,7 @@ fn use_tree(p: &mut Parser, top_level: bool) {
46 // test use_tree_list 46 // test use_tree_list
47 // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) 47 // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
48 // use {path::from::root}; // Rust 2015 48 // use {path::from::root}; // Rust 2015
49 // use ::{some::arbritrary::path}; // Rust 2015 49 // use ::{some::arbitrary::path}; // Rust 2015
50 // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting 50 // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
51 T!['{'] => { 51 T!['{'] => {
52 use_tree_list(p); 52 use_tree_list(p);
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index b53d5749f..da71498a8 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -83,7 +83,7 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) {
83} 83}
84 84
85const PAT_RECOVERY_SET: TokenSet = 85const PAT_RECOVERY_SET: TokenSet =
86 TokenSet::new(&[LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA]); 86 TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,]]);
87 87
88fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { 88fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
89 let m = match p.nth(0) { 89 let m = match p.nth(0) {
diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs
index a013c49b9..42cd426bd 100644
--- a/crates/parser/src/grammar/type_args.rs
+++ b/crates/parser/src/grammar/type_args.rs
@@ -26,7 +26,7 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
26} 26}
27 27
28// test type_arg 28// test type_arg
29// type A = B<'static, i32, 1, { 2 }, Item=u64>; 29// type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
30fn generic_arg(p: &mut Parser) { 30fn generic_arg(p: &mut Parser) {
31 let m = p.start(); 31 let m = p.start();
32 match p.current() { 32 match p.current() {
@@ -55,6 +55,10 @@ fn generic_arg(p: &mut Parser) {
55 expressions::literal(p); 55 expressions::literal(p);
56 m.complete(p, CONST_ARG); 56 m.complete(p, CONST_ARG);
57 } 57 }
58 T![true] | T![false] => {
59 expressions::literal(p);
60 m.complete(p, CONST_ARG);
61 }
58 _ => { 62 _ => {
59 types::type_(p); 63 types::type_(p);
60 m.complete(p, TYPE_ARG); 64 m.complete(p, TYPE_ARG);
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs
index 4aeccd193..3de5248da 100644
--- a/crates/parser/src/grammar/type_params.rs
+++ b/crates/parser/src/grammar/type_params.rs
@@ -25,7 +25,7 @@ fn generic_param_list(p: &mut Parser) {
25 match p.current() { 25 match p.current() {
26 LIFETIME_IDENT => lifetime_param(p, m), 26 LIFETIME_IDENT => lifetime_param(p, m),
27 IDENT => type_param(p, m), 27 IDENT => type_param(p, m),
28 CONST_KW => const_param(p, m), 28 T![const] => const_param(p, m),
29 _ => { 29 _ => {
30 m.abandon(p); 30 m.abandon(p);
31 p.err_and_bump("expected type parameter") 31 p.err_and_bump("expected type parameter")
@@ -66,7 +66,7 @@ fn type_param(p: &mut Parser, m: Marker) {
66// test const_param 66// test const_param
67// struct S<const N: u32>; 67// struct S<const N: u32>;
68fn const_param(p: &mut Parser, m: Marker) { 68fn const_param(p: &mut Parser, m: Marker) {
69 assert!(p.at(CONST_KW)); 69 assert!(p.at(T![const]));
70 p.bump(T![const]); 70 p.bump(T![const]);
71 name(p); 71 name(p);
72 types::ascription(p); 72 types::ascription(p);
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index 4cd572101..970f165ed 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -79,7 +79,7 @@ impl Message for Response {}
79fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { 79fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
80 let mut buf = String::new(); 80 let mut buf = String::new();
81 inp.read_line(&mut buf)?; 81 inp.read_line(&mut buf)?;
82 buf.pop(); // Remove traling '\n' 82 buf.pop(); // Remove trailing '\n'
83 Ok(match buf.len() { 83 Ok(match buf.len() {
84 0 => None, 84 0 => None,
85 _ => Some(buf), 85 _ => Some(buf),
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index f78c17194..83f9ead17 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -20,7 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" } 20test_utils = { path = "../test_utils", version = "0.0.0" }
21 21
22[dev-dependencies] 22[dev-dependencies]
23cargo_metadata = "=0.12.0" 23cargo_metadata = "0.12.2"
24 24
25# used as proc macro test targets 25# used as proc macro test targets
26serde_derive = "1.0.106" 26serde_derive = "1.0.106"
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
index 3528d5c99..bd1e7c2fc 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
@@ -251,7 +251,7 @@ impl<S> DecodeMut<'_, '_, S> for String {
251 } 251 }
252} 252}
253 253
254/// Simplied version of panic payloads, ignoring 254/// Simplified version of panic payloads, ignoring
255/// types other than `&'static str` and `String`. 255/// types other than `&'static str` and `String`.
256#[derive(Debug)] 256#[derive(Debug)]
257pub enum PanicMessage { 257pub enum PanicMessage {
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index b54aa1f3b..e6006a3c8 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -4,7 +4,7 @@
4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that 4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
5//! we could provide any TokenStream implementation. 5//! we could provide any TokenStream implementation.
6//! The original idea from fedochet is using proc-macro2 as backend, 6//! The original idea from fedochet is using proc-macro2 as backend,
7//! we use tt instead for better intergation with RA. 7//! we use tt instead for better integration with RA.
8//! 8//!
9//! FIXME: No span and source file information is implemented yet 9//! FIXME: No span and source file information is implemented yet
10 10
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index a65e42261..855fb83ea 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13log = "0.4.8" 13log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15cargo_metadata = "=0.12.0" 15cargo_metadata = "0.12.2"
16serde = { version = "1.0.106", features = ["derive"] } 16serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 3db0d55c5..f148521a2 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -75,14 +75,24 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
75 will_create: None, 75 will_create: None,
76 did_rename: None, 76 did_rename: None,
77 will_rename: Some(FileOperationRegistrationOptions { 77 will_rename: Some(FileOperationRegistrationOptions {
78 filters: vec![FileOperationFilter { 78 filters: vec![
79 scheme: Some(String::from("file")), 79 FileOperationFilter {
80 pattern: FileOperationPattern { 80 scheme: Some(String::from("file")),
81 glob: String::from("**/*.rs"), 81 pattern: FileOperationPattern {
82 matches: Some(FileOperationPatternKind::File), 82 glob: String::from("**/*.rs"),
83 options: None, 83 matches: Some(FileOperationPatternKind::File),
84 options: None,
85 },
84 }, 86 },
85 }], 87 FileOperationFilter {
88 scheme: Some(String::from("file")),
89 pattern: FileOperationPattern {
90 glob: String::from("**"),
91 matches: Some(FileOperationPatternKind::Folder),
92 options: None,
93 },
94 },
95 ],
86 }), 96 }),
87 did_delete: None, 97 did_delete: None,
88 will_delete: None, 98 will_delete: None,
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 9445aec07..fd1407e60 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -58,7 +58,7 @@ impl AnalysisStatsCmd {
58 let mut db_load_sw = self.stop_watch(); 58 let mut db_load_sw = self.stop_watch();
59 let (host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?; 59 let (host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?;
60 let db = host.raw_database(); 60 let db = host.raw_database();
61 eprintln!("Database loaded {}", db_load_sw.elapsed()); 61 eprintln!("{:<20} {}", "Database loaded:", db_load_sw.elapsed());
62 62
63 let mut analysis_sw = self.stop_watch(); 63 let mut analysis_sw = self.stop_watch();
64 let mut num_crates = 0; 64 let mut num_crates = 0;
@@ -85,7 +85,7 @@ impl AnalysisStatsCmd {
85 shuffle(&mut rng, &mut visit_queue); 85 shuffle(&mut rng, &mut visit_queue);
86 } 86 }
87 87
88 eprintln!("Crates in this dir: {}", num_crates); 88 eprint!(" crates: {}", num_crates);
89 let mut num_decls = 0; 89 let mut num_decls = 0;
90 let mut funcs = Vec::new(); 90 let mut funcs = Vec::new();
91 while let Some(module) = visit_queue.pop() { 91 while let Some(module) = visit_queue.pop() {
@@ -109,10 +109,8 @@ impl AnalysisStatsCmd {
109 } 109 }
110 } 110 }
111 } 111 }
112 eprintln!("Total modules found: {}", visited_modules.len()); 112 eprintln!(", mods: {}, decls: {}, fns: {}", visited_modules.len(), num_decls, funcs.len());
113 eprintln!("Total declarations: {}", num_decls); 113 eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed());
114 eprintln!("Total functions: {}", funcs.len());
115 eprintln!("Item Collection: {}", analysis_sw.elapsed());
116 114
117 if self.randomize { 115 if self.randomize {
118 shuffle(&mut rng, &mut funcs); 116 shuffle(&mut rng, &mut funcs);
@@ -135,7 +133,7 @@ impl AnalysisStatsCmd {
135 snap.0.infer(f_id.into()); 133 snap.0.infer(f_id.into());
136 }) 134 })
137 .count(); 135 .count();
138 eprintln!("Parallel Inference: {}", inference_sw.elapsed()); 136 eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
139 } 137 }
140 138
141 let mut inference_sw = self.stop_watch(); 139 let mut inference_sw = self.stop_watch();
@@ -273,27 +271,22 @@ impl AnalysisStatsCmd {
273 bar.inc(1); 271 bar.inc(1);
274 } 272 }
275 bar.finish_and_clear(); 273 bar.finish_and_clear();
276 eprintln!("Total expressions: {}", num_exprs);
277 eprintln!( 274 eprintln!(
278 "Expressions of unknown type: {} ({}%)", 275 " exprs: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
276 num_exprs,
279 num_exprs_unknown, 277 num_exprs_unknown,
280 if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 } 278 percentage(num_exprs_unknown, num_exprs),
281 );
282 report_metric("unknown type", num_exprs_unknown, "#");
283
284 eprintln!(
285 "Expressions of partially unknown type: {} ({}%)",
286 num_exprs_partially_unknown, 279 num_exprs_partially_unknown,
287 if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 } 280 percentage(num_exprs_partially_unknown, num_exprs),
281 num_type_mismatches
288 ); 282 );
289 283 report_metric("unknown type", num_exprs_unknown, "#");
290 eprintln!("Type mismatches: {}", num_type_mismatches);
291 report_metric("type mismatches", num_type_mismatches, "#"); 284 report_metric("type mismatches", num_type_mismatches, "#");
292 285
293 eprintln!("Inference: {}", inference_sw.elapsed()); 286 eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
294 287
295 let total_span = analysis_sw.elapsed(); 288 let total_span = analysis_sw.elapsed();
296 eprintln!("Total: {}", total_span); 289 eprintln!("{:<20} {}", "Total:", total_span);
297 report_metric("total time", total_span.time.as_millis() as u64, "ms"); 290 report_metric("total time", total_span.time.as_millis() as u64, "ms");
298 if let Some(instructions) = total_span.instructions { 291 if let Some(instructions) = total_span.instructions {
299 report_metric("total instructions", instructions, "#instr"); 292 report_metric("total instructions", instructions, "#instr");
@@ -302,7 +295,7 @@ impl AnalysisStatsCmd {
302 report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); 295 report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
303 } 296 }
304 297
305 if self.memory_usage { 298 if self.memory_usage && verbosity.is_verbose() {
306 print_memory_usage(host, vfs); 299 print_memory_usage(host, vfs);
307 } 300 }
308 301
@@ -325,3 +318,7 @@ fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
325 slice.swap(0, idx); 318 slice.swap(0, idx);
326 } 319 }
327} 320}
321
322fn percentage(n: u64, total: u64) -> u64 {
323 (n * 100).checked_div(total).unwrap_or(100)
324}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 2d3e25cbf..27b92a5a9 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -334,6 +334,18 @@ impl Config {
334 } 334 }
335 } 335 }
336 336
337 pub fn did_save_text_document_dynamic_registration(&self) -> bool {
338 let caps =
339 try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default());
340 caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
341 }
342 pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
343 try_or!(
344 self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?,
345 false
346 )
347 }
348
337 pub fn location_link(&self) -> bool { 349 pub fn location_link(&self) -> bool {
338 try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false) 350 try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false)
339 } 351 }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 1f6bf1c8c..442fbd14c 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -22,6 +22,7 @@ use crate::{
22 from_proto, 22 from_proto,
23 line_endings::LineEndings, 23 line_endings::LineEndings,
24 main_loop::Task, 24 main_loop::Task,
25 op_queue::OpQueue,
25 reload::SourceRootConfig, 26 reload::SourceRootConfig,
26 request_metrics::{LatestRequests, RequestMetrics}, 27 request_metrics::{LatestRequests, RequestMetrics},
27 thread_pool::TaskPool, 28 thread_pool::TaskPool,
@@ -78,6 +79,7 @@ pub(crate) struct GlobalState {
78 pub(crate) source_root_config: SourceRootConfig, 79 pub(crate) source_root_config: SourceRootConfig,
79 pub(crate) proc_macro_client: Option<ProcMacroClient>, 80 pub(crate) proc_macro_client: Option<ProcMacroClient>,
80 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 81 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
82 pub(crate) fetch_workspaces_queue: OpQueue,
81 latest_requests: Arc<RwLock<LatestRequests>>, 83 latest_requests: Arc<RwLock<LatestRequests>>,
82} 84}
83 85
@@ -130,6 +132,7 @@ impl GlobalState {
130 source_root_config: SourceRootConfig::default(), 132 source_root_config: SourceRootConfig::default(),
131 proc_macro_client: None, 133 proc_macro_client: None,
132 workspaces: Arc::new(Vec::new()), 134 workspaces: Arc::new(Vec::new()),
135 fetch_workspaces_queue: OpQueue::default(),
133 latest_requests: Default::default(), 136 latest_requests: Default::default(),
134 } 137 }
135 } 138 }
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 29cc9051e..dc81f55d6 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -432,9 +432,27 @@ pub(crate) fn handle_will_rename_files(
432 // Limit to single-level moves for now. 432 // Limit to single-level moves for now.
433 match (from_path.parent(), to_path.parent()) { 433 match (from_path.parent(), to_path.parent()) {
434 (Some(p1), Some(p2)) if p1 == p2 => { 434 (Some(p1), Some(p2)) if p1 == p2 => {
435 let new_name = to_path.file_stem()?; 435 if from_path.is_dir() {
436 let new_name = new_name.to_str()?; 436 // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
437 Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) 437 let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
438 old_folder_name.push('/');
439 let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
440
441 let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
442 let new_file_name = to_path.file_name()?.to_str()?;
443 Some((
444 snap.url_to_file_id(&imitate_from_url).ok()?,
445 new_file_name.to_string(),
446 ))
447 } else {
448 let old_name = from_path.file_stem()?.to_str()?;
449 let new_name = to_path.file_stem()?.to_str()?;
450 match (old_name, new_name) {
451 ("mod", _) => None,
452 (_, "mod") => None,
453 _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
454 }
455 }
438 } 456 }
439 _ => None, 457 _ => None,
440 } 458 }
@@ -773,7 +791,8 @@ pub(crate) fn handle_prepare_rename(
773 let _p = profile::span("handle_prepare_rename"); 791 let _p = profile::span("handle_prepare_rename");
774 let position = from_proto::file_position(&snap, params)?; 792 let position = from_proto::file_position(&snap, params)?;
775 793
776 let change = snap.analysis.prepare_rename(position)??; 794 let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
795
777 let line_index = snap.analysis.file_line_index(position.file_id)?; 796 let line_index = snap.analysis.file_line_index(position.file_id)?;
778 let range = to_proto::range(&line_index, change.range); 797 let range = to_proto::range(&line_index, change.range);
779 Ok(Some(PrepareRenameResponse::Range(range))) 798 Ok(Some(PrepareRenameResponse::Range(range)))
@@ -786,15 +805,8 @@ pub(crate) fn handle_rename(
786 let _p = profile::span("handle_rename"); 805 let _p = profile::span("handle_rename");
787 let position = from_proto::file_position(&snap, params.text_document_position)?; 806 let position = from_proto::file_position(&snap, params.text_document_position)?;
788 807
789 if params.new_name.is_empty() { 808 let change =
790 return Err(LspError::new( 809 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
791 ErrorCode::InvalidParams as i32,
792 "New Name cannot be empty".into(),
793 )
794 .into());
795 }
796
797 let change = snap.analysis.rename(position, &*params.new_name)??;
798 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; 810 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?;
799 Ok(Some(workspace_edit)) 811 Ok(Some(workspace_edit))
800} 812}
@@ -812,14 +824,15 @@ pub(crate) fn handle_references(
812 }; 824 };
813 825
814 let locations = if params.context.include_declaration { 826 let locations = if params.context.include_declaration {
815 refs.into_iter() 827 refs.references_with_declaration()
816 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) 828 .file_ranges()
829 .filter_map(|frange| to_proto::location(&snap, frange).ok())
817 .collect() 830 .collect()
818 } else { 831 } else {
819 // Only iterate over the references if include_declaration was false 832 // Only iterate over the references if include_declaration was false
820 refs.references() 833 refs.references()
821 .iter() 834 .file_ranges()
822 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) 835 .filter_map(|frange| to_proto::location(&snap, frange).ok())
823 .collect() 836 .collect()
824 }; 837 };
825 838
@@ -1175,8 +1188,8 @@ pub(crate) fn handle_code_lens_resolve(
1175 .unwrap_or(None) 1188 .unwrap_or(None)
1176 .map(|r| { 1189 .map(|r| {
1177 r.references() 1190 r.references()
1178 .iter() 1191 .file_ranges()
1179 .filter_map(|it| to_proto::location(&snap, it.file_range).ok()) 1192 .filter_map(|frange| to_proto::location(&snap, frange).ok())
1180 .collect_vec() 1193 .collect_vec()
1181 }) 1194 })
1182 .unwrap_or_default(); 1195 .unwrap_or_default();
@@ -1220,13 +1233,19 @@ pub(crate) fn handle_document_highlight(
1220 }; 1233 };
1221 1234
1222 let res = refs 1235 let res = refs
1223 .into_iter() 1236 .references_with_declaration()
1224 .filter(|reference| reference.file_range.file_id == position.file_id) 1237 .references
1225 .map(|reference| DocumentHighlight { 1238 .get(&position.file_id)
1226 range: to_proto::range(&line_index, reference.file_range.range), 1239 .map(|file_refs| {
1227 kind: reference.access.map(to_proto::document_highlight_kind), 1240 file_refs
1241 .into_iter()
1242 .map(|r| DocumentHighlight {
1243 range: to_proto::range(&line_index, r.range),
1244 kind: r.access.map(to_proto::document_highlight_kind),
1245 })
1246 .collect()
1228 }) 1247 })
1229 .collect(); 1248 .unwrap_or_default();
1230 Ok(Some(res)) 1249 Ok(Some(res))
1231} 1250}
1232 1251
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index c9494e300..2207b9a87 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -35,6 +35,7 @@ mod lsp_utils;
35mod thread_pool; 35mod thread_pool;
36mod document; 36mod document;
37mod diff; 37mod diff;
38mod op_queue;
38pub mod lsp_ext; 39pub mod lsp_ext;
39pub mod config; 40pub mod config;
40 41
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 40de56dad..2d06fe538 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -130,7 +130,7 @@ pub(crate) fn apply_document_changes(
130} 130}
131 131
132/// Checks that the edits inside the completion and the additional edits do not overlap. 132/// Checks that the edits inside the completion and the additional edits do not overlap.
133/// LSP explicitly forbits the additional edits to overlap both with the main edit and themselves. 133/// LSP explicitly forbids the additional edits to overlap both with the main edit and themselves.
134pub(crate) fn all_edits_are_disjoint( 134pub(crate) fn all_edits_are_disjoint(
135 completion: &lsp_types::CompletionItem, 135 completion: &lsp_types::CompletionItem,
136 additional_edits: &[lsp_types::TextEdit], 136 additional_edits: &[lsp_types::TextEdit],
@@ -290,7 +290,7 @@ mod tests {
290 Some(vec![disjoint_edit.clone(), joint_edit.clone()]); 290 Some(vec![disjoint_edit.clone(), joint_edit.clone()]);
291 assert!( 291 assert!(
292 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 292 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
293 "Completion with disjoint edits fails the validaton even with empty extra edits" 293 "Completion with disjoint edits fails the validation even with empty extra edits"
294 ); 294 );
295 295
296 completion_with_joint_edits.text_edit = 296 completion_with_joint_edits.text_edit =
@@ -298,7 +298,7 @@ mod tests {
298 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit.clone()]); 298 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit.clone()]);
299 assert!( 299 assert!(
300 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 300 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
301 "Completion with disjoint edits fails the validaton even with empty extra edits" 301 "Completion with disjoint edits fails the validation even with empty extra edits"
302 ); 302 );
303 303
304 completion_with_joint_edits.text_edit = 304 completion_with_joint_edits.text_edit =
@@ -310,7 +310,7 @@ mod tests {
310 completion_with_joint_edits.additional_text_edits = None; 310 completion_with_joint_edits.additional_text_edits = None;
311 assert!( 311 assert!(
312 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 312 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
313 "Completion with disjoint edits fails the validaton even with empty extra edits" 313 "Completion with disjoint edits fails the validation even with empty extra edits"
314 ); 314 );
315 315
316 completion_with_joint_edits.text_edit = 316 completion_with_joint_edits.text_edit =
@@ -322,7 +322,7 @@ mod tests {
322 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); 322 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]);
323 assert!( 323 assert!(
324 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 324 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
325 "Completion with disjoint edits fails the validaton even with empty extra edits" 325 "Completion with disjoint edits fails the validation even with empty extra edits"
326 ); 326 );
327 } 327 }
328 328
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 22ee96775..6d2475a59 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -11,7 +11,6 @@ use ide::{Canceled, FileId};
11use ide_db::base_db::VfsPath; 11use ide_db::base_db::VfsPath;
12use lsp_server::{Connection, Notification, Request, Response}; 12use lsp_server::{Connection, Notification, Request, Response};
13use lsp_types::notification::Notification as _; 13use lsp_types::notification::Notification as _;
14use project_model::ProjectWorkspace;
15use vfs::ChangeKind; 14use vfs::ChangeKind;
16 15
17use crate::{ 16use crate::{
@@ -62,7 +61,6 @@ enum Event {
62pub(crate) enum Task { 61pub(crate) enum Task {
63 Response(Response), 62 Response(Response),
64 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), 63 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
65 Workspaces(Vec<anyhow::Result<ProjectWorkspace>>),
66 PrimeCaches(PrimeCachesProgress), 64 PrimeCaches(PrimeCachesProgress),
67 FetchWorkspace(ProjectWorkspaceProgress), 65 FetchWorkspace(ProjectWorkspaceProgress),
68} 66}
@@ -110,40 +108,43 @@ impl GlobalState {
110 ); 108 );
111 }; 109 };
112 110
113 let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { 111 if self.config.did_save_text_document_dynamic_registration() {
114 include_text: Some(false), 112 let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
115 text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { 113 include_text: Some(false),
116 document_selector: Some(vec![ 114 text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
117 lsp_types::DocumentFilter { 115 document_selector: Some(vec![
118 language: None, 116 lsp_types::DocumentFilter {
119 scheme: None, 117 language: None,
120 pattern: Some("**/*.rs".into()), 118 scheme: None,
121 }, 119 pattern: Some("**/*.rs".into()),
122 lsp_types::DocumentFilter { 120 },
123 language: None, 121 lsp_types::DocumentFilter {
124 scheme: None, 122 language: None,
125 pattern: Some("**/Cargo.toml".into()), 123 scheme: None,
126 }, 124 pattern: Some("**/Cargo.toml".into()),
127 lsp_types::DocumentFilter { 125 },
128 language: None, 126 lsp_types::DocumentFilter {
129 scheme: None, 127 language: None,
130 pattern: Some("**/Cargo.lock".into()), 128 scheme: None,
131 }, 129 pattern: Some("**/Cargo.lock".into()),
132 ]), 130 },
133 }, 131 ]),
134 }; 132 },
135 133 };
136 let registration = lsp_types::Registration { 134
137 id: "textDocument/didSave".to_string(), 135 let registration = lsp_types::Registration {
138 method: "textDocument/didSave".to_string(), 136 id: "textDocument/didSave".to_string(),
139 register_options: Some(serde_json::to_value(save_registration_options).unwrap()), 137 method: "textDocument/didSave".to_string(),
140 }; 138 register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
141 self.send_request::<lsp_types::request::RegisterCapability>( 139 };
142 lsp_types::RegistrationParams { registrations: vec![registration] }, 140 self.send_request::<lsp_types::request::RegisterCapability>(
143 |_, _| (), 141 lsp_types::RegistrationParams { registrations: vec![registration] },
144 ); 142 |_, _| (),
143 );
144 }
145 145
146 self.fetch_workspaces(); 146 self.fetch_workspaces_request();
147 self.fetch_workspaces_if_needed();
147 148
148 while let Some(event) = self.next_event(&inbox) { 149 while let Some(event) = self.next_event(&inbox) {
149 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { 150 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
@@ -204,7 +205,6 @@ impl GlobalState {
204 self.diagnostics.set_native_diagnostics(file_id, diagnostics) 205 self.diagnostics.set_native_diagnostics(file_id, diagnostics)
205 } 206 }
206 } 207 }
207 Task::Workspaces(workspaces) => self.switch_workspaces(workspaces),
208 Task::PrimeCaches(progress) => match progress { 208 Task::PrimeCaches(progress) => match progress {
209 PrimeCachesProgress::Started => prime_caches_progress.push(progress), 209 PrimeCachesProgress::Started => prime_caches_progress.push(progress),
210 PrimeCachesProgress::StartedOnCrate { .. } => { 210 PrimeCachesProgress::StartedOnCrate { .. } => {
@@ -224,7 +224,11 @@ impl GlobalState {
224 ProjectWorkspaceProgress::Report(msg) => { 224 ProjectWorkspaceProgress::Report(msg) => {
225 (Progress::Report, Some(msg)) 225 (Progress::Report, Some(msg))
226 } 226 }
227 ProjectWorkspaceProgress::End => (Progress::End, None), 227 ProjectWorkspaceProgress::End(workspaces) => {
228 self.fetch_workspaces_completed();
229 self.switch_workspaces(workspaces);
230 (Progress::End, None)
231 }
228 }; 232 };
229 self.report_progress("fetching", state, msg, None); 233 self.report_progress("fetching", state, msg, None);
230 } 234 }
@@ -403,6 +407,8 @@ impl GlobalState {
403 } 407 }
404 } 408 }
405 409
410 self.fetch_workspaces_if_needed();
411
406 let loop_duration = loop_start.elapsed(); 412 let loop_duration = loop_start.elapsed();
407 if loop_duration > Duration::from_millis(100) { 413 if loop_duration > Duration::from_millis(100) {
408 log::warn!("overly long loop turn: {:?}", loop_duration); 414 log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -440,7 +446,7 @@ impl GlobalState {
440 } 446 }
441 447
442 RequestDispatcher { req: Some(req), global_state: self } 448 RequestDispatcher { req: Some(req), global_state: self }
443 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))? 449 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))?
444 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? 450 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
445 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? 451 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
446 .on_sync::<lsp_types::request::Shutdown>(|s, ()| { 452 .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
index a49a58c00..865eaae9b 100644
--- a/crates/rust-analyzer/src/markdown.rs
+++ b/crates/rust-analyzer/src/markdown.rs
@@ -106,7 +106,7 @@ mod tests {
106 106
107 #[test] 107 #[test]
108 fn test_format_docs_preserves_newlines() { 108 fn test_format_docs_preserves_newlines() {
109 let comment = "this\nis\nultiline"; 109 let comment = "this\nis\nmultiline";
110 assert_eq!(format_docs(comment), comment); 110 assert_eq!(format_docs(comment), comment);
111 } 111 }
112 112
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
new file mode 100644
index 000000000..51d66f4b3
--- /dev/null
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -0,0 +1,25 @@
1//! Bookkeeping to make sure only one long-running operation is executed.
2
3#[derive(Default)]
4pub(crate) struct OpQueue {
5 op_scheduled: bool,
6 op_in_progress: bool,
7}
8
9impl OpQueue {
10 pub(crate) fn request_op(&mut self) {
11 self.op_scheduled = true;
12 }
13 pub(crate) fn should_start_op(&mut self) -> bool {
14 if !self.op_in_progress && self.op_scheduled {
15 self.op_in_progress = true;
16 self.op_scheduled = false;
17 return true;
18 }
19 false
20 }
21 pub(crate) fn op_completed(&mut self) {
22 assert!(self.op_in_progress);
23 self.op_in_progress = false;
24 }
25}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index f4e084741..97e20362f 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -19,7 +19,7 @@ use lsp_ext::StatusParams;
19pub(crate) enum ProjectWorkspaceProgress { 19pub(crate) enum ProjectWorkspaceProgress {
20 Begin, 20 Begin,
21 Report(String), 21 Report(String),
22 End, 22 End(Vec<anyhow::Result<ProjectWorkspace>>),
23} 23}
24 24
25impl GlobalState { 25impl GlobalState {
@@ -30,7 +30,7 @@ impl GlobalState {
30 self.analysis_host.update_lru_capacity(self.config.lru_capacity()); 30 self.analysis_host.update_lru_capacity(self.config.lru_capacity());
31 } 31 }
32 if self.config.linked_projects() != old_config.linked_projects() { 32 if self.config.linked_projects() != old_config.linked_projects() {
33 self.fetch_workspaces() 33 self.fetch_workspaces_request()
34 } else if self.config.flycheck() != old_config.flycheck() { 34 } else if self.config.flycheck() != old_config.flycheck() {
35 self.reload_flycheck(); 35 self.reload_flycheck();
36 } 36 }
@@ -44,7 +44,7 @@ impl GlobalState {
44 Status::Ready | Status::Invalid => (), 44 Status::Ready | Status::Invalid => (),
45 } 45 }
46 if self.config.cargo_autoreload() { 46 if self.config.cargo_autoreload() {
47 self.fetch_workspaces(); 47 self.fetch_workspaces_request();
48 } else { 48 } else {
49 self.transition(Status::NeedsReload); 49 self.transition(Status::NeedsReload);
50 } 50 }
@@ -98,8 +98,15 @@ impl GlobalState {
98 }); 98 });
99 } 99 }
100 } 100 }
101 pub(crate) fn fetch_workspaces(&mut self) { 101
102 pub(crate) fn fetch_workspaces_request(&mut self) {
103 self.fetch_workspaces_queue.request_op()
104 }
105 pub(crate) fn fetch_workspaces_if_needed(&mut self) {
102 log::info!("will fetch workspaces"); 106 log::info!("will fetch workspaces");
107 if !self.fetch_workspaces_queue.should_start_op() {
108 return;
109 }
103 110
104 self.task_pool.handle.spawn_with_sender({ 111 self.task_pool.handle.spawn_with_sender({
105 let linked_projects = self.config.linked_projects(); 112 let linked_projects = self.config.linked_projects();
@@ -133,12 +140,17 @@ impl GlobalState {
133 }) 140 })
134 .collect::<Vec<_>>(); 141 .collect::<Vec<_>>();
135 142
136 sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::End)).unwrap();
137 log::info!("did fetch workspaces {:?}", workspaces); 143 log::info!("did fetch workspaces {:?}", workspaces);
138 sender.send(Task::Workspaces(workspaces)).unwrap() 144 sender
145 .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
146 .unwrap();
139 } 147 }
140 }); 148 });
141 } 149 }
150 pub(crate) fn fetch_workspaces_completed(&mut self) {
151 self.fetch_workspaces_queue.op_completed()
152 }
153
142 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { 154 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
143 let _p = profile::span("GlobalState::switch_workspaces"); 155 let _p = profile::span("GlobalState::switch_workspaces");
144 log::info!("will switch workspaces: {:?}", workspaces); 156 log::info!("will switch workspaces: {:?}", workspaces);
@@ -170,26 +182,31 @@ impl GlobalState {
170 } 182 }
171 183
172 if let FilesWatcher::Client = self.config.files().watcher { 184 if let FilesWatcher::Client = self.config.files().watcher {
173 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { 185 if self.config.did_change_watched_files_dynamic_registration() {
174 watchers: workspaces 186 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
175 .iter() 187 watchers: workspaces
176 .flat_map(ProjectWorkspace::to_roots) 188 .iter()
177 .filter(|it| it.is_member) 189 .flat_map(ProjectWorkspace::to_roots)
178 .flat_map(|root| { 190 .filter(|it| it.is_member)
179 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display())) 191 .flat_map(|root| {
180 }) 192 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
181 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) 193 })
182 .collect(), 194 .map(|glob_pattern| lsp_types::FileSystemWatcher {
183 }; 195 glob_pattern,
184 let registration = lsp_types::Registration { 196 kind: None,
185 id: "workspace/didChangeWatchedFiles".to_string(), 197 })
186 method: "workspace/didChangeWatchedFiles".to_string(), 198 .collect(),
187 register_options: Some(serde_json::to_value(registration_options).unwrap()), 199 };
188 }; 200 let registration = lsp_types::Registration {
189 self.send_request::<lsp_types::request::RegisterCapability>( 201 id: "workspace/didChangeWatchedFiles".to_string(),
190 lsp_types::RegistrationParams { registrations: vec![registration] }, 202 method: "workspace/didChangeWatchedFiles".to_string(),
191 |_, _| (), 203 register_options: Some(serde_json::to_value(registration_options).unwrap()),
192 ); 204 };
205 self.send_request::<lsp_types::request::RegisterCapability>(
206 lsp_types::RegistrationParams { registrations: vec![registration] },
207 |_, _| (),
208 );
209 }
193 } 210 }
194 211
195 let mut change = Change::new(); 212 let mut change = Change::new();
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 5c4366f16..7ce9a4ab6 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -38,17 +38,25 @@ macro_rules! define_semantic_token_types {
38} 38}
39 39
40define_semantic_token_types![ 40define_semantic_token_types![
41 (ANGLE, "angle"),
41 (ATTRIBUTE, "attribute"), 42 (ATTRIBUTE, "attribute"),
42 (BOOLEAN, "boolean"), 43 (BOOLEAN, "boolean"),
44 (BRACE, "brace"),
45 (BRACKET, "bracket"),
43 (BUILTIN_TYPE, "builtinType"), 46 (BUILTIN_TYPE, "builtinType"),
47 (COMMA, "comma"),
48 (COLON, "colon"),
49 (DOT, "dot"),
44 (ESCAPE_SEQUENCE, "escapeSequence"), 50 (ESCAPE_SEQUENCE, "escapeSequence"),
45 (FORMAT_SPECIFIER, "formatSpecifier"), 51 (FORMAT_SPECIFIER, "formatSpecifier"),
46 (GENERIC, "generic"), 52 (GENERIC, "generic"),
47 (CONST_PARAMETER, "constParameter"), 53 (CONST_PARAMETER, "constParameter"),
48 (LIFETIME, "lifetime"), 54 (LIFETIME, "lifetime"),
49 (LABEL, "label"), 55 (LABEL, "label"),
56 (PARENTHESIS, "parenthesis"),
50 (PUNCTUATION, "punctuation"), 57 (PUNCTUATION, "punctuation"),
51 (SELF_KEYWORD, "selfKeyword"), 58 (SELF_KEYWORD, "selfKeyword"),
59 (SEMICOLON, "semicolon"),
52 (TYPE_ALIAS, "typeAlias"), 60 (TYPE_ALIAS, "typeAlias"),
53 (UNION, "union"), 61 (UNION, "union"),
54 (UNRESOLVED_REFERENCE, "unresolvedReference"), 62 (UNRESOLVED_REFERENCE, "unresolvedReference"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index bc9999ddc..a7ff8975a 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -6,10 +6,10 @@ use std::{
6 6
7use ide::{ 7use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, 9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel,
10 HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, 10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess,
11 NavigationTarget, ReferenceAccess, Runnable, Severity, SourceChange, SourceFileEdit, 11 RenameError, Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange,
12 SymbolKind, TextEdit, TextRange, TextSize, 12 TextSize,
13}; 13};
14use itertools::Itertools; 14use itertools::Itertools;
15 15
@@ -337,12 +337,15 @@ static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
337pub(crate) fn semantic_tokens( 337pub(crate) fn semantic_tokens(
338 text: &str, 338 text: &str,
339 line_index: &LineIndex, 339 line_index: &LineIndex,
340 highlights: Vec<HighlightedRange>, 340 highlights: Vec<HlRange>,
341) -> lsp_types::SemanticTokens { 341) -> lsp_types::SemanticTokens {
342 let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); 342 let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
343 let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); 343 let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
344 344
345 for highlight_range in highlights { 345 for highlight_range in highlights {
346 if highlight_range.highlight.is_empty() {
347 continue;
348 }
346 let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); 349 let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
347 let token_index = semantic_tokens::type_index(type_); 350 let token_index = semantic_tokens::type_index(type_);
348 let modifier_bitset = mods.0; 351 let modifier_bitset = mods.0;
@@ -374,7 +377,7 @@ fn semantic_token_type_and_modifiers(
374) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { 377) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
375 let mut mods = semantic_tokens::ModifierSet::default(); 378 let mut mods = semantic_tokens::ModifierSet::default();
376 let type_ = match highlight.tag { 379 let type_ = match highlight.tag {
377 HighlightTag::Symbol(symbol) => match symbol { 380 HlTag::Symbol(symbol) => match symbol {
378 SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, 381 SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
379 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, 382 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE,
380 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, 383 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
@@ -386,7 +389,7 @@ fn semantic_token_type_and_modifiers(
386 SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, 389 SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
387 SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE, 390 SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,
388 SymbolKind::Function => { 391 SymbolKind::Function => {
389 if highlight.modifiers.contains(HighlightModifier::Associated) { 392 if highlight.mods.contains(HlMod::Associated) {
390 lsp_types::SemanticTokenType::METHOD 393 lsp_types::SemanticTokenType::METHOD
391 } else { 394 } else {
392 lsp_types::SemanticTokenType::FUNCTION 395 lsp_types::SemanticTokenType::FUNCTION
@@ -409,38 +412,44 @@ fn semantic_token_type_and_modifiers(
409 SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, 412 SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
410 SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, 413 SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
411 }, 414 },
412 HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, 415 HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
413 HighlightTag::Dummy => semantic_tokens::GENERIC, 416 HlTag::None => semantic_tokens::GENERIC,
414 HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { 417 HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
415 lsp_types::SemanticTokenType::NUMBER 418 HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
416 } 419 HlTag::CharLiteral | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
417 HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN, 420 HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
418 HighlightTag::CharLiteral | HighlightTag::StringLiteral => { 421 HlTag::Attribute => semantic_tokens::ATTRIBUTE,
419 lsp_types::SemanticTokenType::STRING 422 HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
420 } 423 HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
421 HighlightTag::Comment => lsp_types::SemanticTokenType::COMMENT, 424 HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
422 HighlightTag::Attribute => semantic_tokens::ATTRIBUTE, 425 HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
423 HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, 426 HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
424 HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, 427 HlTag::Punctuation(punct) => match punct {
425 HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, 428 HlPunct::Bracket => semantic_tokens::BRACKET,
426 HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR, 429 HlPunct::Brace => semantic_tokens::BRACE,
427 HighlightTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, 430 HlPunct::Parenthesis => semantic_tokens::PARENTHESIS,
428 HighlightTag::Punctuation => semantic_tokens::PUNCTUATION, 431 HlPunct::Angle => semantic_tokens::ANGLE,
432 HlPunct::Comma => semantic_tokens::COMMA,
433 HlPunct::Dot => semantic_tokens::DOT,
434 HlPunct::Colon => semantic_tokens::COLON,
435 HlPunct::Semi => semantic_tokens::SEMICOLON,
436 HlPunct::Other => semantic_tokens::PUNCTUATION,
437 },
429 }; 438 };
430 439
431 for modifier in highlight.modifiers.iter() { 440 for modifier in highlight.mods.iter() {
432 let modifier = match modifier { 441 let modifier = match modifier {
433 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, 442 HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
434 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, 443 HlMod::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
435 HighlightModifier::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION, 444 HlMod::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
436 HighlightModifier::Injected => semantic_tokens::INJECTED, 445 HlMod::Injected => semantic_tokens::INJECTED,
437 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, 446 HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
438 HighlightModifier::Mutable => semantic_tokens::MUTABLE, 447 HlMod::Mutable => semantic_tokens::MUTABLE,
439 HighlightModifier::Consuming => semantic_tokens::CONSUMING, 448 HlMod::Consuming => semantic_tokens::CONSUMING,
440 HighlightModifier::Unsafe => semantic_tokens::UNSAFE, 449 HlMod::Unsafe => semantic_tokens::UNSAFE,
441 HighlightModifier::Callable => semantic_tokens::CALLABLE, 450 HlMod::Callable => semantic_tokens::CALLABLE,
442 HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC, 451 HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
443 HighlightModifier::Associated => continue, 452 HlMod::Associated => continue,
444 }; 453 };
445 mods |= modifier; 454 mods |= modifier;
446 } 455 }
@@ -847,6 +856,10 @@ pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
847 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } 856 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
848} 857}
849 858
859pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
860 crate::LspError { code: lsp_server::ErrorCode::InvalidParams as i32, message: err.to_string() }
861}
862
850#[cfg(test)] 863#[cfg(test)]
851mod tests { 864mod tests {
852 use ide::Analysis; 865 use ide::Analysis;
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index f5c5c63e5..80bde29b9 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -16,11 +16,14 @@ use std::{collections::HashMap, path::PathBuf, time::Instant};
16use expect_test::expect; 16use expect_test::expect;
17use lsp_types::{ 17use lsp_types::{
18 notification::DidOpenTextDocument, 18 notification::DidOpenTextDocument,
19 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, 19 request::{
20 CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
21 WillRenameFiles,
22 },
20 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, 23 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
21 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, 24 DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
22 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, 25 PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
23 WorkDoneProgressParams, 26 TextDocumentPositionParams, WorkDoneProgressParams,
24}; 27};
25use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; 28use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
26use serde_json::json; 29use serde_json::json;
@@ -745,3 +748,136 @@ pub fn foo(_input: TokenStream) -> TokenStream {
745 ```"#]] 748 ```"#]]
746 .assert_eq(&value); 749 .assert_eq(&value);
747} 750}
751
752#[test]
753fn test_will_rename_files_same_level() {
754 if skip_slow_tests() {
755 return;
756 }
757
758 let tmp_dir = TestDir::new();
759 let tmp_dir_path = tmp_dir.path().to_owned();
760 let tmp_dir_str = tmp_dir_path.to_str().unwrap();
761 let base_path = PathBuf::from(format!("file://{}", tmp_dir_str));
762
763 let code = r#"
764//- /Cargo.toml
765[package]
766name = "foo"
767version = "0.0.0"
768
769//- /src/lib.rs
770mod old_file;
771mod from_mod;
772mod to_mod;
773mod old_folder;
774fn main() {}
775
776//- /src/old_file.rs
777
778//- /src/old_folder/mod.rs
779
780//- /src/from_mod/mod.rs
781
782//- /src/to_mod/foo.rs
783
784"#;
785 let server =
786 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
787
788 //rename same level file
789 server.request::<WillRenameFiles>(
790 RenameFilesParams {
791 files: vec![FileRename {
792 old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_string(),
793 new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_string(),
794 }],
795 },
796 json!({
797 "documentChanges": [
798 {
799 "textDocument": {
800 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")),
801 "version": null
802 },
803 "edits": [
804 {
805 "range": {
806 "start": {
807 "line": 0,
808 "character": 4
809 },
810 "end": {
811 "line": 0,
812 "character": 12
813 }
814 },
815 "newText": "new_file"
816 }
817 ]
818 }
819 ]
820 }),
821 );
822
823 //rename file from mod.rs to foo.rs
824 server.request::<WillRenameFiles>(
825 RenameFilesParams {
826 files: vec![FileRename {
827 old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_string(),
828 new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_string(),
829 }],
830 },
831 json!({
832 "documentChanges": []
833 }),
834 );
835
836 //rename file from foo.rs to mod.rs
837 server.request::<WillRenameFiles>(
838 RenameFilesParams {
839 files: vec![FileRename {
840 old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_string(),
841 new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_string(),
842 }],
843 },
844 json!({
845 "documentChanges": []
846 }),
847 );
848
849 //rename same level file
850 server.request::<WillRenameFiles>(
851 RenameFilesParams {
852 files: vec![FileRename {
853 old_uri: base_path.join("src/old_folder").to_str().unwrap().to_string(),
854 new_uri: base_path.join("src/new_folder").to_str().unwrap().to_string(),
855 }],
856 },
857 json!({
858 "documentChanges": [
859 {
860 "textDocument": {
861 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")),
862 "version": null
863 },
864 "edits": [
865 {
866 "range": {
867 "start": {
868 "line": 3,
869 "character": 4
870 },
871 "end": {
872 "line": 3,
873 "character": 14
874 }
875 },
876 "newText": "new_folder"
877 }
878 ]
879 }
880 ]
881 }),
882 );
883}
diff --git a/crates/ssr/src/search.rs b/crates/ssr/src/search.rs
index 44b5db029..836eb94b2 100644
--- a/crates/ssr/src/search.rs
+++ b/crates/ssr/src/search.rs
@@ -5,10 +5,10 @@ use crate::{
5 resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, 5 resolving::{ResolvedPath, ResolvedPattern, ResolvedRule},
6 Match, MatchFinder, 6 Match, MatchFinder,
7}; 7};
8use ide_db::base_db::{FileId, FileRange};
9use ide_db::{ 8use ide_db::{
9 base_db::{FileId, FileRange},
10 defs::Definition, 10 defs::Definition,
11 search::{Reference, SearchScope}, 11 search::{SearchScope, UsageSearchResult},
12}; 12};
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; 14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};
@@ -20,7 +20,7 @@ use test_utils::mark;
20/// them more than once. 20/// them more than once.
21#[derive(Default)] 21#[derive(Default)]
22pub(crate) struct UsageCache { 22pub(crate) struct UsageCache {
23 usages: Vec<(Definition, Vec<Reference>)>, 23 usages: Vec<(Definition, UsageSearchResult)>,
24} 24}
25 25
26impl<'db> MatchFinder<'db> { 26impl<'db> MatchFinder<'db> {
@@ -58,8 +58,8 @@ impl<'db> MatchFinder<'db> {
58 ) { 58 ) {
59 if let Some(resolved_path) = pick_path_for_usages(pattern) { 59 if let Some(resolved_path) = pick_path_for_usages(pattern) {
60 let definition: Definition = resolved_path.resolution.clone().into(); 60 let definition: Definition = resolved_path.resolution.clone().into();
61 for reference in self.find_usages(usage_cache, definition) { 61 for file_range in self.find_usages(usage_cache, definition).file_ranges() {
62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, reference) { 62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) {
63 if !is_search_permitted_ancestors(&node_to_match) { 63 if !is_search_permitted_ancestors(&node_to_match) {
64 mark::hit!(use_declaration_with_braces); 64 mark::hit!(use_declaration_with_braces);
65 continue; 65 continue;
@@ -73,11 +73,11 @@ impl<'db> MatchFinder<'db> {
73 fn find_node_to_match( 73 fn find_node_to_match(
74 &self, 74 &self,
75 resolved_path: &ResolvedPath, 75 resolved_path: &ResolvedPath,
76 reference: &Reference, 76 file_range: FileRange,
77 ) -> Option<SyntaxNode> { 77 ) -> Option<SyntaxNode> {
78 let file = self.sema.parse(reference.file_range.file_id); 78 let file = self.sema.parse(file_range.file_id);
79 let depth = resolved_path.depth as usize; 79 let depth = resolved_path.depth as usize;
80 let offset = reference.file_range.range.start(); 80 let offset = file_range.range.start();
81 if let Some(path) = 81 if let Some(path) =
82 self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset) 82 self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset)
83 { 83 {
@@ -108,7 +108,7 @@ impl<'db> MatchFinder<'db> {
108 &self, 108 &self,
109 usage_cache: &'a mut UsageCache, 109 usage_cache: &'a mut UsageCache,
110 definition: Definition, 110 definition: Definition,
111 ) -> &'a [Reference] { 111 ) -> &'a UsageSearchResult {
112 // Logically if a lookup succeeds we should just return it. Unfortunately returning it would 112 // Logically if a lookup succeeds we should just return it. Unfortunately returning it would
113 // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a 113 // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a
114 // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two 114 // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two
@@ -250,7 +250,7 @@ fn is_search_permitted(node: &SyntaxNode) -> bool {
250} 250}
251 251
252impl UsageCache { 252impl UsageCache {
253 fn find(&mut self, definition: &Definition) -> Option<&[Reference]> { 253 fn find(&mut self, definition: &Definition) -> Option<&UsageSearchResult> {
254 // We expect a very small number of cache entries (generally 1), so a linear scan should be 254 // We expect a very small number of cache entries (generally 1), so a linear scan should be
255 // fast enough and avoids the need to implement Hash for Definition. 255 // fast enough and avoids the need to implement Hash for Definition.
256 for (d, refs) in &self.usages { 256 for (d, refs) in &self.usages {
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 5332edb09..13aab1451 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,5 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::{ops, process, time::Instant}; 2use std::{cmp::Ordering, ops, process, time::Instant};
3 3
4mod macros; 4mod macros;
5pub mod panic_context; 5pub mod panic_context;
@@ -117,7 +117,12 @@ impl<'a> Iterator for LinesWithEnds<'a> {
117 } 117 }
118} 118}
119 119
120// https://github.com/rust-lang/rust/issues/73831 120/// Returns `idx` such that:
121///
122/// ∀ x in slice[..idx]: pred(x)
123/// && ∀ x in slice[idx..]: !pred(x)
124///
125/// https://github.com/rust-lang/rust/issues/73831
121pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize 126pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
122where 127where
123 P: FnMut(&T) -> bool, 128 P: FnMut(&T) -> bool,
@@ -147,6 +152,15 @@ where
147 left 152 left
148} 153}
149 154
155pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
156where
157 F: FnMut(&T) -> Ordering,
158{
159 let start = partition_point(slice, |it| key(it) == Ordering::Less);
160 let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
161 start..start + len
162}
163
150pub struct JodChild(pub process::Child); 164pub struct JodChild(pub process::Child);
151 165
152impl ops::Deref for JodChild { 166impl ops::Deref for JodChild {
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 22ab36cd2..384d031e7 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -88,8 +88,8 @@ pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNod
88 let keep = u_depth.min(v_depth); 88 let keep = u_depth.min(v_depth);
89 89
90 let u_candidates = u.ancestors().skip(u_depth - keep); 90 let u_candidates = u.ancestors().skip(u_depth - keep);
91 let v_canidates = v.ancestors().skip(v_depth - keep); 91 let v_candidates = v.ancestors().skip(v_depth - keep);
92 let (res, _) = u_candidates.zip(v_canidates).find(|(x, y)| x == y)?; 92 let (res, _) = u_candidates.zip(v_candidates).find(|(x, y)| x == y)?;
93 Some(res) 93 Some(res)
94} 94}
95 95
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index cafa4c198..1ed8a96e5 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -241,7 +241,7 @@ pub fn wildcard_pat() -> ast::WildcardPat {
241 } 241 }
242} 242}
243 243
244/// Creates a tuple of patterns from an interator of patterns. 244/// Creates a tuple of patterns from an iterator of patterns.
245/// 245///
246/// Invariant: `pats` must be length > 1 246/// Invariant: `pats` must be length > 1
247/// 247///
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 2aa472fb4..27381ba80 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -133,7 +133,7 @@ impl ast::Attr {
133 first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind); 133 first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind);
134 134
135 match (first_token_kind, second_token_kind) { 135 match (first_token_kind, second_token_kind) {
136 (Some(SyntaxKind::POUND), Some(T![!])) => AttrKind::Inner, 136 (Some(T![#]), Some(T![!])) => AttrKind::Inner,
137 _ => AttrKind::Outer, 137 _ => AttrKind::Outer,
138 } 138 }
139 } 139 }
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index 0cbba73c5..7c8d0a4c4 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -24,7 +24,7 @@ pub struct Token {
24/// Beware that it checks for shebang first and its length contributes to resulting 24/// Beware that it checks for shebang first and its length contributes to resulting
25/// tokens offsets. 25/// tokens offsets.
26pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) { 26pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) {
27 // non-empty string is a precondtion of `rustc_lexer::strip_shebang()`. 27 // non-empty string is a precondition of `rustc_lexer::strip_shebang()`.
28 if text.is_empty() { 28 if text.is_empty() {
29 return Default::default(); 29 return Default::default();
30 } 30 }
@@ -76,7 +76,7 @@ pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxEr
76} 76}
77 77
78/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and 78/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and
79/// returns `None` if any tokenization error occured. 79/// returns `None` if any tokenization error occurred.
80/// 80///
81/// Beware that unescape errors are not checked at tokenization time. 81/// Beware that unescape errors are not checked at tokenization time.
82pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { 82pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> {
@@ -96,7 +96,7 @@ pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> {
96/// 96///
97/// Beware that unescape errors are not checked at tokenization time. 97/// Beware that unescape errors are not checked at tokenization time.
98fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> { 98fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> {
99 // non-empty string is a precondtion of `rustc_lexer::first_token()`. 99 // non-empty string is a precondition of `rustc_lexer::first_token()`.
100 if text.is_empty() { 100 if text.is_empty() {
101 return None; 101 return None;
102 } 102 }
@@ -117,7 +117,7 @@ fn rustc_token_kind_to_syntax_kind(
117 token_text: &str, 117 token_text: &str,
118) -> (SyntaxKind, Option<&'static str>) { 118) -> (SyntaxKind, Option<&'static str>) {
119 // A note on an intended tradeoff: 119 // A note on an intended tradeoff:
120 // We drop some useful infromation here (see patterns with double dots `..`) 120 // We drop some useful information here (see patterns with double dots `..`)
121 // Storing that info in `SyntaxKind` is not possible due to its layout requirements of 121 // Storing that info in `SyntaxKind` is not possible due to its layout requirements of
122 // being `u16` that come from `rowan::SyntaxKind`. 122 // being `u16` that come from `rowan::SyntaxKind`.
123 123
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 2ddaeb176..bfa2dc4ba 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -173,7 +173,7 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
173 assert_eq!( 173 assert_eq!(
174 node.parent(), 174 node.parent(),
175 pair.parent(), 175 pair.parent(),
176 "\nunpaired curleys:\n{}\n{:#?}\n", 176 "\nunpaired curlys:\n{}\n{:#?}\n",
177 root.text(), 177 root.text(),
178 root, 178 root,
179 ); 179 );
@@ -344,9 +344,9 @@ fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
344 344
345 if tbl.bounds().count() > 1 { 345 if tbl.bounds().count() > 1 {
346 let dyn_token = ty.dyn_token()?; 346 let dyn_token = ty.dyn_token()?;
347 let potential_parentheses = 347 let potential_parenthesis =
348 algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; 348 algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
349 let kind = potential_parentheses.kind(); 349 let kind = potential_parenthesis.kind();
350 if !matches!(kind, T!['('] | T![<] | T![=]) { 350 if !matches!(kind, T!['('] | T![<] | T![=]) {
351 return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); 351 return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
352 } 352 }
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
index b1fb75ed1..f40500e38 100644
--- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
@@ -1,4 +1,4 @@
1[email protected]9 1[email protected]8
2 [email protected] 2 [email protected]
3 [email protected] "use" 3 [email protected] "use"
4 [email protected] " " 4 [email protected] " "
@@ -75,62 +75,62 @@ [email protected]
75 [email protected] "}" 75 [email protected] "}"
76 [email protected] ";" 76 [email protected] ";"
77 [email protected] " " 77 [email protected] " "
78 [email protected]6 78 [email protected]5
79 [email protected] "// Rust 2015" 79 [email protected] "// Rust 2015"
80 [email protected] "\n" 80 [email protected] "\n"
81 [email protected] "use" 81 [email protected] "use"
82 [email protected] " " 82 [email protected] " "
83 [email protected]5 83 [email protected]4
84 [email protected] "::" 84 [email protected] "::"
85 [email protected]5 85 [email protected]4
86 [email protected] "{" 86 [email protected] "{"
87 [email protected]4 87 [email protected]3
88 [email protected]4 88 [email protected]3
89 [email protected]8 89 [email protected]7
90 [email protected] 90 [email protected]
91 [email protected] 91 [email protected]
92 [email protected] 92 [email protected]
93 [email protected] "some" 93 [email protected] "some"
94 [email protected] "::" 94 [email protected] "::"
95 [email protected]8 95 [email protected]7
96 [email protected]8 96 [email protected]7
97 [email protected]8 "arbritrary" 97 [email protected]7 "arbitrary"
98 COLON2@158..160 "::" 98 COLON2@157..159 "::"
99 PATH_SEGMENT@160..164 99 PATH_SEGMENT@159..163
100 NAME_REF@160..164 100 NAME_REF@159..163
101 IDENT@160..164 "path" 101 IDENT@159..163 "path"
102 R_CURLY@164..165 "}" 102 R_CURLY@163..164 "}"
103 SEMICOLON@165..166 ";" 103 SEMICOLON@164..165 ";"
104 WHITESPACE@166..167 " " 104 WHITESPACE@165..166 " "
105 USE@167..205 105 USE@166..204
106 COMMENT@167..179 "// Rust 2015" 106 COMMENT@166..178 "// Rust 2015"
107 WHITESPACE@179..180 "\n" 107 WHITESPACE@178..179 "\n"
108 USE_KW@180..183 "use" 108 USE_KW@179..182 "use"
109 WHITESPACE@183..184 " " 109 WHITESPACE@182..183 " "
110 USE_TREE@184..204 110 USE_TREE@183..203
111 COLON2@184..186 "::" 111 COLON2@183..185 "::"
112 USE_TREE_LIST@186..204 112 USE_TREE_LIST@185..203
113 L_CURLY@186..187 "{" 113 L_CURLY@185..186 "{"
114 USE_TREE@187..203 114 USE_TREE@186..202
115 USE_TREE_LIST@187..203 115 USE_TREE_LIST@186..202
116 L_CURLY@187..188 "{" 116 L_CURLY@186..187 "{"
117 USE_TREE@188..202 117 USE_TREE@187..201
118 USE_TREE_LIST@188..202 118 USE_TREE_LIST@187..201
119 L_CURLY@188..189 "{" 119 L_CURLY@187..188 "{"
120 USE_TREE@189..201 120 USE_TREE@188..200
121 PATH@189..201 121 PATH@188..200
122 PATH@189..193 122 PATH@188..192
123 PATH_SEGMENT@189..193 123 PATH_SEGMENT@188..192
124 NAME_REF@189..193 124 NAME_REF@188..192
125 IDENT@189..193 "root" 125 IDENT@188..192 "root"
126 COLON2@193..195 "::" 126 COLON2@192..194 "::"
127 PATH_SEGMENT@195..201 127 PATH_SEGMENT@194..200
128 NAME_REF@195..201 128 NAME_REF@194..200
129 IDENT@195..201 "export" 129 IDENT@194..200 "export"
130 R_CURLY@201..202 "}" 130 R_CURLY@200..201 "}"
131 R_CURLY@202..203 "}" 131 R_CURLY@201..202 "}"
132 R_CURLY@203..204 "}" 132 R_CURLY@202..203 "}"
133 SEMICOLON@204..205 ";" 133 SEMICOLON@203..204 ";"
134 WHITESPACE@205..206 " " 134 WHITESPACE@204..205 " "
135 COMMENT@206..248 "// Nonsensical but pe ..." 135 COMMENT@205..247 "// Nonsensical but pe ..."
136 WHITESPACE@248..249 "\n" 136 WHITESPACE@247..248 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
index 381cba1e2..02af4b446 100644
--- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
@@ -1,4 +1,4 @@
1use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) 1use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
2use {path::from::root}; // Rust 2015 2use {path::from::root}; // Rust 2015
3use ::{some::arbritrary::path}; // Rust 2015 3use ::{some::arbitrary::path}; // Rust 2015
4use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting 4use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
index 51e881a8e..68c0f1c66 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
@@ -1,5 +1,5 @@
1SOURCE_FILE@0..46 1SOURCE_FILE@0..59
2 TYPE_ALIAS@0..45 2 [email protected]8
3 [email protected] "type" 3 [email protected] "type"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
@@ -7,12 +7,12 @@ [email protected]
7 [email protected] " " 7 [email protected] " "
8 [email protected] "=" 8 [email protected] "="
9 [email protected] " " 9 [email protected] " "
10 PATH_TYPE@9..44 10 PATH_TYPE@9..57
11 PATH@9..44 11 PATH@9..57
12 PATH_SEGMENT@9..44 12 PATH_SEGMENT@9..57
13 [email protected] 13 [email protected]
14 [email protected] "B" 14 [email protected] "B"
15 GENERIC_ARG_LIST@10..44 15 GENERIC_ARG_LIST@10..57
16 [email protected] "<" 16 [email protected] "<"
17 [email protected] 17 [email protected]
18 [email protected] 18 [email protected]
@@ -51,6 +51,16 @@ [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] "u64" 53 [email protected] "u64"
54 [email protected] ">" 54 [email protected] ","
55 [email protected] ";" 55 [email protected] " "
56 [email protected] "\n" 56 [email protected]
57 [email protected]
58 [email protected] "true"
59 [email protected] ","
60 [email protected] " "
61 [email protected]
62 [email protected]
63 [email protected] "false"
64 [email protected] ">"
65 [email protected] ";"
66 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
index 0d07d7651..6a8721a73 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
@@ -1 +1 @@
type A = B<'static, i32, 1, { 2 }, Item=u64>; type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 84c1d7ebb..e19d2ad61 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -63,7 +63,7 @@ pub fn extract_offset(text: &str) -> (TextSize, String) {
63 } 63 }
64} 64}
65 65
66/// Returns the offset of the first occurence of `$0` marker and the copy of `text` 66/// Returns the offset of the first occurrence of `$0` marker and the copy of `text`
67/// without the marker. 67/// without the marker.
68fn try_extract_offset(text: &str) -> Option<(TextSize, String)> { 68fn try_extract_offset(text: &str) -> Option<(TextSize, String)> {
69 let cursor_pos = text.find(CURSOR_MARKER)?; 69 let cursor_pos = text.find(CURSOR_MARKER)?;
diff --git a/crates/vfs/src/anchored_path.rs b/crates/vfs/src/anchored_path.rs
index 02720a32e..db15a2a21 100644
--- a/crates/vfs/src/anchored_path.rs
+++ b/crates/vfs/src/anchored_path.rs
@@ -26,14 +26,24 @@
26//! from the anchor than. 26//! from the anchor than.
27use crate::FileId; 27use crate::FileId;
28 28
29/// Path relative to a file.
30///
31/// Owned version of [`AnchoredPath`].
29#[derive(Clone, PartialEq, Eq, Debug)] 32#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct AnchoredPathBuf { 33pub struct AnchoredPathBuf {
34 /// File that this path is relative to.
31 pub anchor: FileId, 35 pub anchor: FileId,
36 /// Path relative to `anchor`'s containing directory.
32 pub path: String, 37 pub path: String,
33} 38}
34 39
40/// Path relative to a file.
41///
42/// Borrowed version of [`AnchoredPathBuf`].
35#[derive(Clone, Copy, PartialEq, Eq, Debug)] 43#[derive(Clone, Copy, PartialEq, Eq, Debug)]
36pub struct AnchoredPath<'a> { 44pub struct AnchoredPath<'a> {
45 /// File that this path is relative to.
37 pub anchor: FileId, 46 pub anchor: FileId,
47 /// Path relative to `anchor`'s containing directory.
38 pub path: &'a str, 48 pub path: &'a str,
39} 49}
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 49ca593ac..0a4590c8d 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -9,6 +9,7 @@ use rustc_hash::FxHashMap;
9 9
10use crate::{AnchoredPath, FileId, Vfs, VfsPath}; 10use crate::{AnchoredPath, FileId, Vfs, VfsPath};
11 11
12/// A set of [`VfsPath`]s identified by [`FileId`]s.
12#[derive(Default, Clone, Eq, PartialEq)] 13#[derive(Default, Clone, Eq, PartialEq)]
13pub struct FileSet { 14pub struct FileSet {
14 files: FxHashMap<VfsPath, FileId>, 15 files: FxHashMap<VfsPath, FileId>,
@@ -16,9 +17,15 @@ pub struct FileSet {
16} 17}
17 18
18impl FileSet { 19impl FileSet {
20 /// Returns the number of stored paths.
19 pub fn len(&self) -> usize { 21 pub fn len(&self) -> usize {
20 self.files.len() 22 self.files.len()
21 } 23 }
24
25 /// Get the id of the file corresponding to `path`.
26 ///
27 /// If either `path`'s [`anchor`](AnchoredPath::anchor) or the resolved path is not in
28 /// the set, returns [`None`].
22 pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { 29 pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
23 let mut base = self.paths[&path.anchor].clone(); 30 let mut base = self.paths[&path.anchor].clone();
24 base.pop(); 31 base.pop();
@@ -26,19 +33,26 @@ impl FileSet {
26 self.files.get(&path).copied() 33 self.files.get(&path).copied()
27 } 34 }
28 35
36 /// Get the id corresponding to `path` if it exists in the set.
29 pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> { 37 pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> {
30 self.files.get(path) 38 self.files.get(path)
31 } 39 }
32 40
41 /// Get the path corresponding to `file` if it exists in the set.
33 pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> { 42 pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> {
34 self.paths.get(file) 43 self.paths.get(file)
35 } 44 }
36 45
46 /// Insert the `file_id, path` pair into the set.
47 ///
48 /// # Note
49 /// Multiple [`FileId`] can be mapped to the same [`VfsPath`], and vice-versa.
37 pub fn insert(&mut self, file_id: FileId, path: VfsPath) { 50 pub fn insert(&mut self, file_id: FileId, path: VfsPath) {
38 self.files.insert(path.clone(), file_id); 51 self.files.insert(path.clone(), file_id);
39 self.paths.insert(file_id, path); 52 self.paths.insert(file_id, path);
40 } 53 }
41 54
55 /// Iterate over this set's ids.
42 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ { 56 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
43 self.paths.keys().copied() 57 self.paths.keys().copied()
44 } 58 }
@@ -50,9 +64,31 @@ impl fmt::Debug for FileSet {
50 } 64 }
51} 65}
52 66
67/// This contains path prefixes to partition a [`Vfs`] into [`FileSet`]s.
68///
69/// # Example
70/// ```rust
71/// # use vfs::{file_set::FileSetConfigBuilder, VfsPath, Vfs};
72/// let mut builder = FileSetConfigBuilder::default();
73/// builder.add_file_set(vec![VfsPath::new_virtual_path("/src".to_string())]);
74/// let config = builder.build();
75/// let mut file_system = Vfs::default();
76/// file_system.set_file_contents(VfsPath::new_virtual_path("/src/main.rs".to_string()), Some(vec![]));
77/// file_system.set_file_contents(VfsPath::new_virtual_path("/src/lib.rs".to_string()), Some(vec![]));
78/// file_system.set_file_contents(VfsPath::new_virtual_path("/build.rs".to_string()), Some(vec![]));
79/// // contains the sets :
80/// // { "/src/main.rs", "/src/lib.rs" }
81/// // { "build.rs" }
82/// let sets = config.partition(&file_system);
83/// ```
53#[derive(Debug)] 84#[derive(Debug)]
54pub struct FileSetConfig { 85pub struct FileSetConfig {
86 /// Number of sets that `self` can partition a [`Vfs`] into.
87 ///
88 /// This should be the number of sets in `self.map` + 1 for files that don't fit in any
89 /// defined set.
55 n_file_sets: usize, 90 n_file_sets: usize,
91 /// Map from encoded paths to the set they belong to.
56 map: fst::Map<Vec<u8>>, 92 map: fst::Map<Vec<u8>>,
57} 93}
58 94
@@ -63,9 +99,14 @@ impl Default for FileSetConfig {
63} 99}
64 100
65impl FileSetConfig { 101impl FileSetConfig {
102 /// Returns a builder for `FileSetConfig`.
66 pub fn builder() -> FileSetConfigBuilder { 103 pub fn builder() -> FileSetConfigBuilder {
67 FileSetConfigBuilder::default() 104 FileSetConfigBuilder::default()
68 } 105 }
106
107 /// Partition `vfs` into `FileSet`s.
108 ///
109 /// Creates a new [`FileSet`] for every set of prefixes in `self`.
69 pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> { 110 pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> {
70 let mut scratch_space = Vec::new(); 111 let mut scratch_space = Vec::new();
71 let mut res = vec![FileSet::default(); self.len()]; 112 let mut res = vec![FileSet::default(); self.len()];
@@ -75,9 +116,15 @@ impl FileSetConfig {
75 } 116 }
76 res 117 res
77 } 118 }
119
120 /// Number of sets that `self` can partition a [`Vfs`] into.
78 fn len(&self) -> usize { 121 fn len(&self) -> usize {
79 self.n_file_sets 122 self.n_file_sets
80 } 123 }
124
125 /// Returns the set index for the given `path`.
126 ///
127 /// `scratch_space` is used as a buffer and will be entirely replaced.
81 fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize { 128 fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize {
82 scratch_space.clear(); 129 scratch_space.clear();
83 path.encode(scratch_space); 130 path.encode(scratch_space);
@@ -91,6 +138,7 @@ impl FileSetConfig {
91 } 138 }
92} 139}
93 140
141/// Builder for [`FileSetConfig`].
94pub struct FileSetConfigBuilder { 142pub struct FileSetConfigBuilder {
95 roots: Vec<Vec<VfsPath>>, 143 roots: Vec<Vec<VfsPath>>,
96} 144}
@@ -102,12 +150,17 @@ impl Default for FileSetConfigBuilder {
102} 150}
103 151
104impl FileSetConfigBuilder { 152impl FileSetConfigBuilder {
153 /// Returns the number of sets currently held.
105 pub fn len(&self) -> usize { 154 pub fn len(&self) -> usize {
106 self.roots.len() 155 self.roots.len()
107 } 156 }
157
158 /// Add a new set of paths prefixes.
108 pub fn add_file_set(&mut self, roots: Vec<VfsPath>) { 159 pub fn add_file_set(&mut self, roots: Vec<VfsPath>) {
109 self.roots.push(roots) 160 self.roots.push(roots)
110 } 161 }
162
163 /// Build the `FileSetConfig`.
111 pub fn build(self) -> FileSetConfig { 164 pub fn build(self) -> FileSetConfig {
112 let n_file_sets = self.roots.len() + 1; 165 let n_file_sets = self.roots.len() + 1;
113 let map = { 166 let map = {
@@ -127,11 +180,15 @@ impl FileSetConfigBuilder {
127 } 180 }
128} 181}
129 182
183/// Implements [`fst::Automaton`]
184///
185/// It will match if `prefix_of` is a prefix of the given data.
130struct PrefixOf<'a> { 186struct PrefixOf<'a> {
131 prefix_of: &'a [u8], 187 prefix_of: &'a [u8],
132} 188}
133 189
134impl<'a> PrefixOf<'a> { 190impl<'a> PrefixOf<'a> {
191 /// Creates a new `PrefixOf` from the given slice.
135 fn new(prefix_of: &'a [u8]) -> Self { 192 fn new(prefix_of: &'a [u8]) -> Self {
136 Self { prefix_of } 193 Self { prefix_of }
137 } 194 }
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 2b7b14524..e075d752b 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -53,9 +53,15 @@ pub use crate::{
53}; 53};
54pub use paths::{AbsPath, AbsPathBuf}; 54pub use paths::{AbsPath, AbsPathBuf};
55 55
56/// Handle to a file in [`Vfs`]
57///
58/// Most functions in rust-analyzer use this when they need to refer to a file.
56#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] 59#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
57pub struct FileId(pub u32); 60pub struct FileId(pub u32);
58 61
62/// Storage for all files read by rust-analyzer.
63///
64/// For more informations see the [crate-level](crate) documentation.
59#[derive(Default)] 65#[derive(Default)]
60pub struct Vfs { 66pub struct Vfs {
61 interner: PathInterner, 67 interner: PathInterner,
@@ -63,40 +69,73 @@ pub struct Vfs {
63 changes: Vec<ChangedFile>, 69 changes: Vec<ChangedFile>,
64} 70}
65 71
72/// Changed file in the [`Vfs`].
66pub struct ChangedFile { 73pub struct ChangedFile {
74 /// Id of the changed file
67 pub file_id: FileId, 75 pub file_id: FileId,
76 /// Kind of change
68 pub change_kind: ChangeKind, 77 pub change_kind: ChangeKind,
69} 78}
70 79
71impl ChangedFile { 80impl ChangedFile {
81 /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete).
72 pub fn exists(&self) -> bool { 82 pub fn exists(&self) -> bool {
73 self.change_kind != ChangeKind::Delete 83 self.change_kind != ChangeKind::Delete
74 } 84 }
85
86 /// Returns `true` if the change is [`Create`](ChangeKind::Create) or
87 /// [`Delete`](ChangeKind::Delete).
75 pub fn is_created_or_deleted(&self) -> bool { 88 pub fn is_created_or_deleted(&self) -> bool {
76 matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) 89 matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
77 } 90 }
78} 91}
79 92
93/// Kind of [file change](ChangedFile).
80#[derive(Eq, PartialEq, Copy, Clone, Debug)] 94#[derive(Eq, PartialEq, Copy, Clone, Debug)]
81pub enum ChangeKind { 95pub enum ChangeKind {
96 /// The file was (re-)created
82 Create, 97 Create,
98 /// The file was modified
83 Modify, 99 Modify,
100 /// The file was deleted
84 Delete, 101 Delete,
85} 102}
86 103
87impl Vfs { 104impl Vfs {
105 /// Amount of files currently stored.
106 ///
107 /// Note that this includes deleted files.
88 pub fn len(&self) -> usize { 108 pub fn len(&self) -> usize {
89 self.data.len() 109 self.data.len()
90 } 110 }
111
112 /// Id of the given path if it exists in the `Vfs` and is not deleted.
91 pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { 113 pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
92 self.interner.get(path).filter(|&it| self.get(it).is_some()) 114 self.interner.get(path).filter(|&it| self.get(it).is_some())
93 } 115 }
116
117 /// File path corresponding to the given `file_id`.
118 ///
119 /// # Panics
120 ///
121 /// Panics if the id is not present in the `Vfs`.
94 pub fn file_path(&self, file_id: FileId) -> VfsPath { 122 pub fn file_path(&self, file_id: FileId) -> VfsPath {
95 self.interner.lookup(file_id).clone() 123 self.interner.lookup(file_id).clone()
96 } 124 }
125
126 /// File content corresponding to the given `file_id`.
127 ///
128 /// # Panics
129 ///
130 /// Panics if the id is not present in the `Vfs`, or if the corresponding file is
131 /// deleted.
97 pub fn file_contents(&self, file_id: FileId) -> &[u8] { 132 pub fn file_contents(&self, file_id: FileId) -> &[u8] {
98 self.get(file_id).as_deref().unwrap() 133 self.get(file_id).as_deref().unwrap()
99 } 134 }
135
136 /// Returns an iterator over the stored ids and their corresponding paths.
137 ///
138 /// This will skip deleted files.
100 pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { 139 pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
101 (0..self.data.len()) 140 (0..self.data.len())
102 .map(|it| FileId(it as u32)) 141 .map(|it| FileId(it as u32))
@@ -106,6 +145,13 @@ impl Vfs {
106 (file_id, path) 145 (file_id, path)
107 }) 146 })
108 } 147 }
148
149 /// Update the `path` with the given `contents`. `None` means the file was deleted.
150 ///
151 /// Returns `true` if the file was modified, and saves the [change](ChangedFile).
152 ///
153 /// If the path does not currently exists in the `Vfs`, allocates a new
154 /// [`FileId`] for it.
109 pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { 155 pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
110 let file_id = self.alloc_file_id(path); 156 let file_id = self.alloc_file_id(path);
111 let change_kind = match (&self.get(file_id), &contents) { 157 let change_kind = match (&self.get(file_id), &contents) {
@@ -120,12 +166,24 @@ impl Vfs {
120 self.changes.push(ChangedFile { file_id, change_kind }); 166 self.changes.push(ChangedFile { file_id, change_kind });
121 true 167 true
122 } 168 }
169
170 /// Returns `true` if the `Vfs` contains [changes](ChangedFile).
123 pub fn has_changes(&self) -> bool { 171 pub fn has_changes(&self) -> bool {
124 !self.changes.is_empty() 172 !self.changes.is_empty()
125 } 173 }
174
175 /// Drain and returns all the changes in the `Vfs`.
126 pub fn take_changes(&mut self) -> Vec<ChangedFile> { 176 pub fn take_changes(&mut self) -> Vec<ChangedFile> {
127 mem::take(&mut self.changes) 177 mem::take(&mut self.changes)
128 } 178 }
179
180 /// Returns the id associated with `path`
181 ///
182 /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a
183 /// deleted file;
184 /// - Else, returns `path`'s id.
185 ///
186 /// Does not record a change.
129 fn alloc_file_id(&mut self, path: VfsPath) -> FileId { 187 fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
130 let file_id = self.interner.intern(path); 188 let file_id = self.interner.intern(path);
131 let idx = file_id.0 as usize; 189 let idx = file_id.0 as usize;
@@ -133,9 +191,21 @@ impl Vfs {
133 self.data.resize_with(len, || None); 191 self.data.resize_with(len, || None);
134 file_id 192 file_id
135 } 193 }
194
195 /// Returns the content associated with the given `file_id`.
196 ///
197 /// # Panics
198 ///
199 /// Panics if no file is associated to that id.
136 fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { 200 fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
137 &self.data[file_id.0 as usize] 201 &self.data[file_id.0 as usize]
138 } 202 }
203
204 /// Mutably returns the content associated with the given `file_id`.
205 ///
206 /// # Panics
207 ///
208 /// Panics if no file is associated to that id.
139 fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { 209 fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
140 &mut self.data[file_id.0 as usize] 210 &mut self.data[file_id.0 as usize]
141 } 211 }
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs
index 40cf96020..d3bdae562 100644
--- a/crates/vfs/src/loader.rs
+++ b/crates/vfs/src/loader.rs
@@ -3,9 +3,12 @@ use std::fmt;
3 3
4use paths::{AbsPath, AbsPathBuf}; 4use paths::{AbsPath, AbsPathBuf};
5 5
6/// A set of files on the file system.
6#[derive(Debug, Clone)] 7#[derive(Debug, Clone)]
7pub enum Entry { 8pub enum Entry {
9 /// The `Entry` is represented by a raw set of files.
8 Files(Vec<AbsPathBuf>), 10 Files(Vec<AbsPathBuf>),
11 /// The `Entry` is represented by `Directories`.
9 Directories(Directories), 12 Directories(Directories),
10} 13}
11 14
@@ -17,6 +20,8 @@ pub enum Entry {
17/// * it is not under `exclude` path 20/// * it is not under `exclude` path
18/// 21///
19/// If many include/exclude paths match, the longest one wins. 22/// If many include/exclude paths match, the longest one wins.
23///
24/// If a path is in both `include` and `exclude`, the `exclude` one wins.
20#[derive(Debug, Clone, Default)] 25#[derive(Debug, Clone, Default)]
21pub struct Directories { 26pub struct Directories {
22 pub extensions: Vec<String>, 27 pub extensions: Vec<String>,
@@ -24,45 +29,99 @@ pub struct Directories {
24 pub exclude: Vec<AbsPathBuf>, 29 pub exclude: Vec<AbsPathBuf>,
25} 30}
26 31
32/// [`Handle`]'s configuration.
27#[derive(Debug)] 33#[derive(Debug)]
28pub struct Config { 34pub struct Config {
35 /// Set of initially loaded files.
29 pub load: Vec<Entry>, 36 pub load: Vec<Entry>,
37 /// Index of watched entries in `load`.
38 ///
39 /// If a path in a watched entry is modified,the [`Handle`] should notify it.
30 pub watch: Vec<usize>, 40 pub watch: Vec<usize>,
31} 41}
32 42
43/// Message about an action taken by a [`Handle`].
33pub enum Message { 44pub enum Message {
45 /// Indicate a gradual progress.
46 ///
47 /// This is supposed to be the number of loaded files.
34 Progress { n_total: usize, n_done: usize }, 48 Progress { n_total: usize, n_done: usize },
49 /// The handle loaded the following files' content.
35 Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, 50 Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
36} 51}
37 52
53/// Type that will receive [`Messages`](Message) from a [`Handle`].
38pub type Sender = Box<dyn Fn(Message) + Send>; 54pub type Sender = Box<dyn Fn(Message) + Send>;
39 55
56/// Interface for reading and watching files.
40pub trait Handle: fmt::Debug { 57pub trait Handle: fmt::Debug {
58 /// Spawn a new handle with the given `sender`.
41 fn spawn(sender: Sender) -> Self 59 fn spawn(sender: Sender) -> Self
42 where 60 where
43 Self: Sized; 61 Self: Sized;
62
63 /// Set this handle's configuration.
44 fn set_config(&mut self, config: Config); 64 fn set_config(&mut self, config: Config);
65
66 /// The file's content at `path` has been modified, and should be reloaded.
45 fn invalidate(&mut self, path: AbsPathBuf); 67 fn invalidate(&mut self, path: AbsPathBuf);
68
69 /// Load the content of the given file, returning [`None`] if it does not
70 /// exists.
46 fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>; 71 fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
47} 72}
48 73
49impl Entry { 74impl Entry {
75 /// Returns:
76 /// ```text
77 /// Entry::Directories(Directories {
78 /// extensions: ["rs"],
79 /// include: [base],
80 /// exclude: [base/.git],
81 /// })
82 /// ```
50 pub fn rs_files_recursively(base: AbsPathBuf) -> Entry { 83 pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
51 Entry::Directories(dirs(base, &[".git"])) 84 Entry::Directories(dirs(base, &[".git"]))
52 } 85 }
86
87 /// Returns:
88 /// ```text
89 /// Entry::Directories(Directories {
90 /// extensions: ["rs"],
91 /// include: [base],
92 /// exclude: [base/.git, base/target],
93 /// })
94 /// ```
53 pub fn local_cargo_package(base: AbsPathBuf) -> Entry { 95 pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
54 Entry::Directories(dirs(base, &[".git", "target"])) 96 Entry::Directories(dirs(base, &[".git", "target"]))
55 } 97 }
98
99 /// Returns:
100 /// ```text
101 /// Entry::Directories(Directories {
102 /// extensions: ["rs"],
103 /// include: [base],
104 /// exclude: [base/.git, /tests, /examples, /benches],
105 /// })
106 /// ```
56 pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry { 107 pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
57 Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"])) 108 Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
58 } 109 }
59 110
111 /// Returns `true` if `path` is included in `self`.
112 ///
113 /// See [`Directories::contains_file`].
60 pub fn contains_file(&self, path: &AbsPath) -> bool { 114 pub fn contains_file(&self, path: &AbsPath) -> bool {
61 match self { 115 match self {
62 Entry::Files(files) => files.iter().any(|it| it == path), 116 Entry::Files(files) => files.iter().any(|it| it == path),
63 Entry::Directories(dirs) => dirs.contains_file(path), 117 Entry::Directories(dirs) => dirs.contains_file(path),
64 } 118 }
65 } 119 }
120
121 /// Returns `true` if `path` is included in `self`.
122 ///
123 /// - If `self` is `Entry::Files`, returns `false`
124 /// - Else, see [`Directories::contains_dir`].
66 pub fn contains_dir(&self, path: &AbsPath) -> bool { 125 pub fn contains_dir(&self, path: &AbsPath) -> bool {
67 match self { 126 match self {
68 Entry::Files(_) => false, 127 Entry::Files(_) => false,
@@ -72,6 +131,7 @@ impl Entry {
72} 131}
73 132
74impl Directories { 133impl Directories {
134 /// Returns `true` if `path` is included in `self`.
75 pub fn contains_file(&self, path: &AbsPath) -> bool { 135 pub fn contains_file(&self, path: &AbsPath) -> bool {
76 let ext = path.extension().unwrap_or_default(); 136 let ext = path.extension().unwrap_or_default();
77 if self.extensions.iter().all(|it| it.as_str() != ext) { 137 if self.extensions.iter().all(|it| it.as_str() != ext) {
@@ -79,9 +139,21 @@ impl Directories {
79 } 139 }
80 self.includes_path(path) 140 self.includes_path(path)
81 } 141 }
142
143 /// Returns `true` if `path` is included in `self`.
144 ///
145 /// Since `path` is supposed to be a directory, this will not take extension
146 /// into account.
82 pub fn contains_dir(&self, path: &AbsPath) -> bool { 147 pub fn contains_dir(&self, path: &AbsPath) -> bool {
83 self.includes_path(path) 148 self.includes_path(path)
84 } 149 }
150
151 /// Returns `true` if `path` is included in `self`.
152 ///
153 /// It is included if
154 /// - An element in `self.include` is a prefix of `path`.
155 /// - This path is longer than any element in `self.exclude` that is a prefix
156 /// of `path`. In case of equality, exclusion wins.
85 fn includes_path(&self, path: &AbsPath) -> bool { 157 fn includes_path(&self, path: &AbsPath) -> bool {
86 let mut include: Option<&AbsPathBuf> = None; 158 let mut include: Option<&AbsPathBuf> = None;
87 for incl in &self.include { 159 for incl in &self.include {
@@ -105,6 +177,14 @@ impl Directories {
105 } 177 }
106} 178}
107 179
180/// Returns :
181/// ```text
182/// Directories {
183/// extensions: ["rs"],
184/// include: [base],
185/// exclude: [base/<exclude>],
186/// }
187/// ```
108fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories { 188fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
109 let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>(); 189 let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
110 Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude } 190 Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs
index 4f70d61e8..2189e5e25 100644
--- a/crates/vfs/src/path_interner.rs
+++ b/crates/vfs/src/path_interner.rs
@@ -5,6 +5,7 @@ use rustc_hash::FxHashMap;
5 5
6use crate::{FileId, VfsPath}; 6use crate::{FileId, VfsPath};
7 7
8/// Structure to map between [`VfsPath`] and [`FileId`].
8#[derive(Default)] 9#[derive(Default)]
9pub(crate) struct PathInterner { 10pub(crate) struct PathInterner {
10 map: FxHashMap<VfsPath, FileId>, 11 map: FxHashMap<VfsPath, FileId>,
@@ -12,9 +13,17 @@ pub(crate) struct PathInterner {
12} 13}
13 14
14impl PathInterner { 15impl PathInterner {
16 /// Get the id corresponding to `path`.
17 ///
18 /// If `path` does not exists in `self`, returns [`None`].
15 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { 19 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
16 self.map.get(path).copied() 20 self.map.get(path).copied()
17 } 21 }
22
23 /// Insert `path` in `self`.
24 ///
25 /// - If `path` already exists in `self`, returns its associated id;
26 /// - Else, returns a newly allocated id.
18 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { 27 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
19 if let Some(id) = self.get(&path) { 28 if let Some(id) = self.get(&path) {
20 return id; 29 return id;
@@ -25,6 +34,11 @@ impl PathInterner {
25 id 34 id
26 } 35 }
27 36
37 /// Returns the path corresponding to `id`.
38 ///
39 /// # Panics
40 ///
41 /// Panics if `id` does not exists in `self`.
28 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { 42 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
29 &self.vec[id.0 as usize] 43 &self.vec[id.0 as usize]
30 } 44 }
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
index bd14911c9..2b3d7fd84 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -3,25 +3,37 @@ use std::fmt;
3 3
4use paths::{AbsPath, AbsPathBuf}; 4use paths::{AbsPath, AbsPathBuf};
5 5
6/// Path in [`Vfs`].
7///
6/// Long-term, we want to support files which do not reside in the file-system, 8/// Long-term, we want to support files which do not reside in the file-system,
7/// so we treat VfsPaths as opaque identifiers. 9/// so we treat `VfsPath`s as opaque identifiers.
10///
11/// [`Vfs`]: crate::Vfs
8#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 12#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
9pub struct VfsPath(VfsPathRepr); 13pub struct VfsPath(VfsPathRepr);
10 14
11impl VfsPath { 15impl VfsPath {
12 /// Creates an "in-memory" path from `/`-separates string. 16 /// Creates an "in-memory" path from `/`-separated string.
17 ///
13 /// This is most useful for testing, to avoid windows/linux differences 18 /// This is most useful for testing, to avoid windows/linux differences
19 ///
20 /// # Panics
21 ///
22 /// Panics if `path` does not start with `'/'`.
14 pub fn new_virtual_path(path: String) -> VfsPath { 23 pub fn new_virtual_path(path: String) -> VfsPath {
15 assert!(path.starts_with('/')); 24 assert!(path.starts_with('/'));
16 VfsPath(VfsPathRepr::VirtualPath(VirtualPath(path))) 25 VfsPath(VfsPathRepr::VirtualPath(VirtualPath(path)))
17 } 26 }
18 27
28 /// Returns the `AbsPath` representation of `self` if `self` is on the file system.
19 pub fn as_path(&self) -> Option<&AbsPath> { 29 pub fn as_path(&self) -> Option<&AbsPath> {
20 match &self.0 { 30 match &self.0 {
21 VfsPathRepr::PathBuf(it) => Some(it.as_path()), 31 VfsPathRepr::PathBuf(it) => Some(it.as_path()),
22 VfsPathRepr::VirtualPath(_) => None, 32 VfsPathRepr::VirtualPath(_) => None,
23 } 33 }
24 } 34 }
35
36 /// Creates a new `VfsPath` with `path` adjoined to `self`.
25 pub fn join(&self, path: &str) -> Option<VfsPath> { 37 pub fn join(&self, path: &str) -> Option<VfsPath> {
26 match &self.0 { 38 match &self.0 {
27 VfsPathRepr::PathBuf(it) => { 39 VfsPathRepr::PathBuf(it) => {
@@ -34,12 +46,30 @@ impl VfsPath {
34 } 46 }
35 } 47 }
36 } 48 }
49
50 /// Remove the last component of `self` if there is one.
51 ///
52 /// If `self` has no component, returns `false`; else returns `true`.
53 ///
54 /// # Example
55 ///
56 /// ```
57 /// # use vfs::{AbsPathBuf, VfsPath};
58 /// let mut path = VfsPath::from(AbsPathBuf::assert("/foo/bar".into()));
59 /// assert!(path.pop());
60 /// assert_eq!(path, VfsPath::from(AbsPathBuf::assert("/foo".into())));
61 /// assert!(path.pop());
62 /// assert_eq!(path, VfsPath::from(AbsPathBuf::assert("/".into())));
63 /// assert!(!path.pop());
64 /// ```
37 pub fn pop(&mut self) -> bool { 65 pub fn pop(&mut self) -> bool {
38 match &mut self.0 { 66 match &mut self.0 {
39 VfsPathRepr::PathBuf(it) => it.pop(), 67 VfsPathRepr::PathBuf(it) => it.pop(),
40 VfsPathRepr::VirtualPath(it) => it.pop(), 68 VfsPathRepr::VirtualPath(it) => it.pop(),
41 } 69 }
42 } 70 }
71
72 /// Returns `true` if `other` is a prefix of `self`.
43 pub fn starts_with(&self, other: &VfsPath) -> bool { 73 pub fn starts_with(&self, other: &VfsPath) -> bool {
44 match (&self.0, &other.0) { 74 match (&self.0, &other.0) {
45 (VfsPathRepr::PathBuf(lhs), VfsPathRepr::PathBuf(rhs)) => lhs.starts_with(rhs), 75 (VfsPathRepr::PathBuf(lhs), VfsPathRepr::PathBuf(rhs)) => lhs.starts_with(rhs),
@@ -48,6 +78,10 @@ impl VfsPath {
48 (VfsPathRepr::VirtualPath(_), _) => false, 78 (VfsPathRepr::VirtualPath(_), _) => false,
49 } 79 }
50 } 80 }
81
82 /// Returns the `VfsPath` without its final component, if there is one.
83 ///
84 /// Returns [`None`] if the path is a root or prefix.
51 pub fn parent(&self) -> Option<VfsPath> { 85 pub fn parent(&self) -> Option<VfsPath> {
52 let mut parent = self.clone(); 86 let mut parent = self.clone();
53 if parent.pop() { 87 if parent.pop() {
@@ -57,6 +91,7 @@ impl VfsPath {
57 } 91 }
58 } 92 }
59 93
94 /// Returns `self`'s base name and file extension.
60 pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { 95 pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
61 match &self.0 { 96 match &self.0 {
62 VfsPathRepr::PathBuf(p) => Some(( 97 VfsPathRepr::PathBuf(p) => Some((
@@ -67,7 +102,14 @@ impl VfsPath {
67 } 102 }
68 } 103 }
69 104
70 // Don't make this `pub` 105 /// **Don't make this `pub`**
106 ///
107 /// Encode the path in the given buffer.
108 ///
109 /// The encoding will be `0` if [`AbsPathBuf`], `1` if [`VirtualPath`], followed
110 /// by `self`'s representation.
111 ///
112 /// Note that this encoding is dependent on the operating system.
71 pub(crate) fn encode(&self, buf: &mut Vec<u8>) { 113 pub(crate) fn encode(&self, buf: &mut Vec<u8>) {
72 let tag = match &self.0 { 114 let tag = match &self.0 {
73 VfsPathRepr::PathBuf(_) => 0, 115 VfsPathRepr::PathBuf(_) => 0,
@@ -224,6 +266,7 @@ mod windows_paths {
224 } 266 }
225} 267}
226 268
269/// Internal, private representation of [`VfsPath`].
227#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 270#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
228enum VfsPathRepr { 271enum VfsPathRepr {
229 PathBuf(AbsPathBuf), 272 PathBuf(AbsPathBuf),
@@ -260,13 +303,34 @@ impl fmt::Debug for VfsPathRepr {
260 } 303 }
261} 304}
262 305
306/// `/`-separated virtual path.
307///
308/// This is used to describe files that do not reside on the file system.
263#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 309#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
264struct VirtualPath(String); 310struct VirtualPath(String);
265 311
266impl VirtualPath { 312impl VirtualPath {
313 /// Returns `true` if `other` is a prefix of `self` (as strings).
267 fn starts_with(&self, other: &VirtualPath) -> bool { 314 fn starts_with(&self, other: &VirtualPath) -> bool {
268 self.0.starts_with(&other.0) 315 self.0.starts_with(&other.0)
269 } 316 }
317
318 /// Remove the last component of `self`.
319 ///
320 /// This will find the last `'/'` in `self`, and remove everything after it,
321 /// including the `'/'`.
322 ///
323 /// If `self` contains no `'/'`, returns `false`; else returns `true`.
324 ///
325 /// # Example
326 ///
327 /// ```rust,ignore
328 /// let mut path = VirtualPath("/foo/bar".to_string());
329 /// path.pop();
330 /// assert_eq!(path.0, "/foo");
331 /// path.pop();
332 /// assert_eq!(path.0, "");
333 /// ```
270 fn pop(&mut self) -> bool { 334 fn pop(&mut self) -> bool {
271 let pos = match self.0.rfind('/') { 335 let pos = match self.0.rfind('/') {
272 Some(pos) => pos, 336 Some(pos) => pos,
@@ -275,6 +339,17 @@ impl VirtualPath {
275 self.0 = self.0[..pos].to_string(); 339 self.0 = self.0[..pos].to_string();
276 true 340 true
277 } 341 }
342
343 /// Append the given *relative* path `path` to `self`.
344 ///
345 /// This will resolve any leading `"../"` in `path` before appending it.
346 ///
347 /// Returns [`None`] if `path` has more leading `"../"` than the number of
348 /// components in `self`.
349 ///
350 /// # Notes
351 ///
352 /// In practice, appending here means `self/path` as strings.
278 fn join(&self, mut path: &str) -> Option<VirtualPath> { 353 fn join(&self, mut path: &str) -> Option<VirtualPath> {
279 let mut res = self.clone(); 354 let mut res = self.clone();
280 while path.starts_with("../") { 355 while path.starts_with("../") {
@@ -287,7 +362,18 @@ impl VirtualPath {
287 Some(res) 362 Some(res)
288 } 363 }
289 364
290 pub(crate) fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { 365 /// Returns `self`'s base name and file extension.
366 ///
367 /// # Returns
368 /// - `None` if `self` ends with `"//"`.
369 /// - `Some((name, None))` if `self`'s base contains no `.`, or only one `.` at
370 /// the start.
371 /// - `Some((name, Some(extension))` else.
372 ///
373 /// # Note
374 /// The extension will not contains `.`. This means `"/foo/bar.baz.rs"` will
375 /// return `Some(("bar.baz", Some("rs"))`.
376 fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
291 let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; 377 let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 };
292 let file_name = match file_path.rfind('/') { 378 let file_name = match file_path.rfind('/') {
293 Some(position) => &file_path[position + 1..], 379 Some(position) => &file_path[position + 1..],
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 67cbc6744..9859f6148 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -78,6 +78,8 @@ Use original span for FileId
78 78
79This makes it easier to prepare a changelog. 79This makes it easier to prepare a changelog.
80 80
81If the change adds a new user-visible functionality, consider recording a GIF with [peek](https://github.com/phw/peek) and pasting it into the PR description.
82
81**Rationale:** clean history is potentially useful, but rarely used. 83**Rationale:** clean history is potentially useful, but rarely used.
82But many users read changelogs. 84But many users read changelogs.
83 85
@@ -643,6 +645,27 @@ assert!(x >= lo && x <= hi>);
643**Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line). 645**Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line).
644 646
645 647
648## Token names
649
650Use `T![foo]` instead of `SyntaxKind::FOO_KW`.
651
652```rust
653// GOOD
654match p.current() {
655 T![true] | T![false] => true,
656 _ => false,
657}
658
659// BAD
660
661match p.current() {
662 SyntaxKind::TRUE_KW | SyntaxKind::FALSE_KW => true,
663 _ => false,
664}
665```
666
667**Rationale:** The macro uses the familiar Rust syntax, avoiding ambiguities like "is this a brace or bracket?".
668
646## Documentation 669## Documentation
647 670
648For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. 671For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 3e07daae9..dec48629c 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -12,7 +12,7 @@ use std::env;
12 12
13use codegen::CodegenCmd; 13use codegen::CodegenCmd;
14use pico_args::Arguments; 14use pico_args::Arguments;
15use xshell::pushd; 15use xshell::{cmd, cp, pushd};
16use xtask::{ 16use xtask::{
17 codegen::{self, Mode}, 17 codegen::{self, Mode},
18 dist::DistCmd, 18 dist::DistCmd,
@@ -124,6 +124,13 @@ FLAGS:
124 args.finish()?; 124 args.finish()?;
125 MetricsCmd { dry_run }.run() 125 MetricsCmd { dry_run }.run()
126 } 126 }
127 "bb" => {
128 let suffix: String = args.free_from_str()?.unwrap();
129 args.finish()?;
130 cmd!("cargo build --release").run()?;
131 cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?;
132 Ok(())
133 }
127 _ => { 134 _ => {
128 eprintln!( 135 eprintln!(
129 "\ 136 "\
@@ -141,7 +148,8 @@ SUBCOMMANDS:
141 install 148 install
142 lint 149 lint
143 dist 150 dist
144 promote" 151 promote
152 bb"
145 ); 153 );
146 Ok(()) 154 Ok(())
147 } 155 }
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 7e5b1f7b5..6abad189a 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -85,6 +85,7 @@ fn rust_files_are_tidy() {
85 for path in rust_files(&project_root().join("crates")) { 85 for path in rust_files(&project_root().join("crates")) {
86 let text = read_file(&path).unwrap(); 86 let text = read_file(&path).unwrap();
87 check_todo(&path, &text); 87 check_todo(&path, &text);
88 check_dbg(&path, &text);
88 check_trailing_ws(&path, &text); 89 check_trailing_ws(&path, &text);
89 deny_clippy(&path, &text); 90 deny_clippy(&path, &text);
90 tidy_docs.visit(&path, &text); 91 tidy_docs.visit(&path, &text);
@@ -94,10 +95,9 @@ fn rust_files_are_tidy() {
94 95
95#[test] 96#[test]
96fn check_merge_commits() { 97fn check_merge_commits() {
97 let stdout = 98 let stdout = cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..")
98 dbg!(cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..")) 99 .read()
99 .read() 100 .unwrap();
100 .unwrap();
101 if !stdout.is_empty() { 101 if !stdout.is_empty() {
102 panic!( 102 panic!(
103 " 103 "
@@ -224,7 +224,7 @@ Zlib OR Apache-2.0 OR MIT
224fn check_todo(path: &Path, text: &str) { 224fn check_todo(path: &Path, text: &str) {
225 let need_todo = &[ 225 let need_todo = &[
226 // This file itself obviously needs to use todo (<- like this!). 226 // This file itself obviously needs to use todo (<- like this!).
227 "tests/cli.rs", 227 "tests/tidy.rs",
228 // Some of our assists generate `todo!()`. 228 // Some of our assists generate `todo!()`.
229 "handlers/add_turbo_fish.rs", 229 "handlers/add_turbo_fish.rs",
230 "handlers/generate_function.rs", 230 "handlers/generate_function.rs",
@@ -252,6 +252,32 @@ fn check_todo(path: &Path, text: &str) {
252 } 252 }
253} 253}
254 254
255fn check_dbg(path: &Path, text: &str) {
256 let need_dbg = &[
257 // This file itself obviously needs to use dbg.
258 "tests/tidy.rs",
259 // Assists to remove `dbg!()`
260 "handlers/remove_dbg.rs",
261 // We have .dbg postfix
262 "completion/src/completions/postfix.rs",
263 // The documentation in string literals may contain anything for its own purposes
264 "completion/src/lib.rs",
265 "completion/src/generated_lint_completions.rs",
266 // test for doc test for remove_dbg
267 "src/tests/generated.rs",
268 ];
269 if need_dbg.iter().any(|p| path.ends_with(p)) {
270 return;
271 }
272 if text.contains("dbg!") {
273 panic!(
274 "\ndbg! macros should not be committed to the master branch,\n\
275 {}\n",
276 path.display(),
277 )
278 }
279}
280
255fn check_trailing_ws(path: &Path, text: &str) { 281fn check_trailing_ws(path: &Path, text: &str) {
256 if is_exclude_dir(path, &["test_data"]) { 282 if is_exclude_dir(path, &["test_data"]) {
257 return; 283 return;
@@ -298,7 +324,7 @@ impl TidyDocs {
298 } 324 }
299 325
300 fn is_exclude_file(d: &Path) -> bool { 326 fn is_exclude_file(d: &Path) -> bool {
301 let file_names = ["tests.rs"]; 327 let file_names = ["tests.rs", "famous_defs_fixture.rs"];
302 328
303 d.file_name() 329 d.file_name()
304 .unwrap_or_default() 330 .unwrap_or_default()