aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock28
-rw-r--r--crates/completion/src/completions/attribute.rs42
-rw-r--r--crates/hir/src/code_model.rs4
-rw-r--r--crates/hir_def/src/body.rs20
-rw-r--r--crates/hir_def/src/body/tests.rs28
-rw-r--r--crates/hir_def/src/lib.rs51
-rw-r--r--crates/hir_expand/src/builtin_macro.rs71
-rw-r--r--crates/hir_expand/src/db.rs1
-rw-r--r--crates/hir_expand/src/eager.rs116
-rw-r--r--crates/hir_expand/src/lib.rs22
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/db.rs6
-rw-r--r--crates/hir_ty/src/method_resolution.rs8
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs19
-rw-r--r--crates/hir_ty/src/tests/regression.rs43
-rw-r--r--crates/hir_ty/src/traits.rs13
-rw-r--r--crates/hir_ty/src/traits/chalk.rs49
-rw-r--r--crates/hir_ty/src/traits/chalk/interner.rs16
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs13
-rw-r--r--crates/ide/src/diagnostics.rs5
-rw-r--r--crates/ide/src/syntax_highlighting.rs15
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html10
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html36
-rw-r--r--crates/ide_db/src/call_info.rs527
-rw-r--r--crates/ide_db/src/call_info/tests.rs523
-rw-r--r--crates/ide_db/src/defs.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs639
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs620
-rw-r--r--crates/ide_db/src/line_index.rs131
-rw-r--r--crates/ide_db/src/line_index/tests.rs128
-rw-r--r--crates/ide_db/src/traits.rs148
-rw-r--r--crates/ide_db/src/traits/tests.rs144
-rw-r--r--crates/proc_macro_api/src/process.rs61
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs29
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt164
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt60
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt138
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt138
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt138
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt82
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt303
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs189
-rw-r--r--crates/rust-analyzer/src/main_loop.rs33
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--docs/dev/README.md16
49 files changed, 3082 insertions, 1774 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d8838cf2b..f1847e769 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
168 168
169[[package]] 169[[package]]
170name = "chalk-derive" 170name = "chalk-derive"
171version = "0.37.0" 171version = "0.43.0"
172source = "registry+https://github.com/rust-lang/crates.io-index" 172source = "registry+https://github.com/rust-lang/crates.io-index"
173checksum = "564b529b0d620da43dc7ea46fa95b5c602e783e1870aeb07e8cbb6d7ff71bee6" 173checksum = "e2d9e0c8adcced1ab0fea5cb8a38647922893d5b495e363e1814299fd380469b"
174dependencies = [ 174dependencies = [
175 "proc-macro2", 175 "proc-macro2",
176 "quote", 176 "quote",
@@ -180,9 +180,9 @@ dependencies = [
180 180
181[[package]] 181[[package]]
182name = "chalk-ir" 182name = "chalk-ir"
183version = "0.37.0" 183version = "0.43.0"
184source = "registry+https://github.com/rust-lang/crates.io-index" 184source = "registry+https://github.com/rust-lang/crates.io-index"
185checksum = "e67d29482387f4cbed6d8f1b1f7d24f00ff10612c700c65fe4af220df11e4d24" 185checksum = "c5218266a5709bc4943de997e64d3fab41c9e9f68efd54a898de53135e987bd3"
186dependencies = [ 186dependencies = [
187 "chalk-derive", 187 "chalk-derive",
188 "lazy_static", 188 "lazy_static",
@@ -190,9 +190,9 @@ dependencies = [
190 190
191[[package]] 191[[package]]
192name = "chalk-recursive" 192name = "chalk-recursive"
193version = "0.37.0" 193version = "0.43.0"
194source = "registry+https://github.com/rust-lang/crates.io-index" 194source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "5c52032be6fbdf91b6a7df3cafba3a6683fdabeff88e7ab73eea96e28657d973" 195checksum = "ed8f34f13fd4f30251f9f6f1dc56f80363201390ecbcac2fdfc8e33036cd9c4a"
196dependencies = [ 196dependencies = [
197 "chalk-derive", 197 "chalk-derive",
198 "chalk-ir", 198 "chalk-ir",
@@ -203,9 +203,9 @@ dependencies = [
203 203
204[[package]] 204[[package]]
205name = "chalk-solve" 205name = "chalk-solve"
206version = "0.37.0" 206version = "0.43.0"
207source = "registry+https://github.com/rust-lang/crates.io-index" 207source = "registry+https://github.com/rust-lang/crates.io-index"
208checksum = "e0378bdfe1547b6fd545f518373b08c1e0c14920f7555a62d049021250a2b89b" 208checksum = "379c9f584488346044709d4c638c38d61a06fe593d4de2ac5f15fd2b0ba4cd9d"
209dependencies = [ 209dependencies = [
210 "chalk-derive", 210 "chalk-derive",
211 "chalk-ir", 211 "chalk-ir",
@@ -802,9 +802,9 @@ checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
802 802
803[[package]] 803[[package]]
804name = "libloading" 804name = "libloading"
805version = "0.6.5" 805version = "0.6.6"
806source = "registry+https://github.com/rust-lang/crates.io-index" 806source = "registry+https://github.com/rust-lang/crates.io-index"
807checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" 807checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
808dependencies = [ 808dependencies = [
809 "cfg-if 1.0.0", 809 "cfg-if 1.0.0",
810 "winapi 0.3.9", 810 "winapi 0.3.9",
@@ -956,9 +956,9 @@ dependencies = [
956 956
957[[package]] 957[[package]]
958name = "mio" 958name = "mio"
959version = "0.6.22" 959version = "0.6.23"
960source = "registry+https://github.com/rust-lang/crates.io-index" 960source = "registry+https://github.com/rust-lang/crates.io-index"
961checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 961checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
962dependencies = [ 962dependencies = [
963 "cfg-if 0.1.10", 963 "cfg-if 0.1.10",
964 "fuchsia-zircon", 964 "fuchsia-zircon",
@@ -1555,9 +1555,9 @@ dependencies = [
1555 1555
1556[[package]] 1556[[package]]
1557name = "serde_json" 1557name = "serde_json"
1558version = "1.0.59" 1558version = "1.0.60"
1559source = "registry+https://github.com/rust-lang/crates.io-index" 1559source = "registry+https://github.com/rust-lang/crates.io-index"
1560checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" 1560checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
1561dependencies = [ 1561dependencies = [
1562 "itoa", 1562 "itoa",
1563 "ryu", 1563 "ryu",
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index f3d669458..5404145d5 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -87,13 +87,23 @@ const fn attr(
87 AttrCompletion { label, lookup, snippet, prefer_inner: false } 87 AttrCompletion { label, lookup, snippet, prefer_inner: false }
88} 88}
89 89
90/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
90const ATTRIBUTES: &[AttrCompletion] = &[ 91const ATTRIBUTES: &[AttrCompletion] = &[
91 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 92 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
93 attr("automatically_derived", None, None),
92 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")), 94 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
93 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), 95 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
96 attr("cold", None, None),
97 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
98 .prefer_inner(),
94 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 99 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
95 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)), 100 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
96 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 101 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
102 attr(
103 r#"export_name = "…""#,
104 Some("export_name"),
105 Some(r#"export_name = "${0:exported_symbol_name}""#),
106 ),
97 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), 107 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
98 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 108 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
99 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 109 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
@@ -101,16 +111,24 @@ const ATTRIBUTES: &[AttrCompletion] = &[
101 attr("global_allocator", None, None).prefer_inner(), 111 attr("global_allocator", None, None).prefer_inner(),
102 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), 112 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
103 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")), 113 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
104 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
105 attr("link", None, None), 114 attr("link", None, None),
115 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
116 attr(
117 r#"link_section = "…""#,
118 Some("link_section"),
119 Some(r#"link_section = "${0:section_name}""#),
120 ),
106 attr("macro_export", None, None), 121 attr("macro_export", None, None),
107 attr("macro_use", None, None), 122 attr("macro_use", None, None),
108 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)), 123 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
124 attr("no_link", None, None).prefer_inner(),
125 attr("no_implicit_prelude", None, None).prefer_inner(),
126 attr("no_main", None, None).prefer_inner(),
109 attr("no_mangle", None, None), 127 attr("no_mangle", None, None),
110 attr("no_std", None, None).prefer_inner(), 128 attr("no_std", None, None).prefer_inner(),
111 attr("non_exhaustive", None, None), 129 attr("non_exhaustive", None, None),
112 attr("panic_handler", None, None).prefer_inner(), 130 attr("panic_handler", None, None).prefer_inner(),
113 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")), 131 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
114 attr("proc_macro", None, None), 132 attr("proc_macro", None, None),
115 attr("proc_macro_attribute", None, None), 133 attr("proc_macro_attribute", None, None),
116 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")), 134 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
@@ -125,9 +143,12 @@ const ATTRIBUTES: &[AttrCompletion] = &[
125 attr( 143 attr(
126 r#"target_feature = "…""#, 144 r#"target_feature = "…""#,
127 Some("target_feature"), 145 Some("target_feature"),
128 Some("target_feature = \"${0:feature}\""), 146 Some(r#"target_feature = "${0:feature}""#),
129 ), 147 ),
130 attr("test", None, None), 148 attr("test", None, None),
149 attr("track_caller", None, None),
150 attr("type_length_limit = …", Some("type_length_limit"), Some("type_length_limit = ${0:128}"))
151 .prefer_inner(),
131 attr("used", None, None), 152 attr("used", None, None),
132 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")), 153 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
133 attr( 154 attr(
@@ -449,17 +470,21 @@ struct Test {}
449 r#"#[<|>]"#, 470 r#"#[<|>]"#,
450 expect![[r#" 471 expect![[r#"
451 at allow(…) 472 at allow(…)
473 at automatically_derived
452 at cfg(…) 474 at cfg(…)
453 at cfg_attr(…) 475 at cfg_attr(…)
476 at cold
454 at deny(…) 477 at deny(…)
455 at deprecated = "…" 478 at deprecated = "…"
456 at derive(…) 479 at derive(…)
457 at doc = "…" 480 at doc = "…"
481 at export_name = "…"
458 at forbid(…) 482 at forbid(…)
459 at ignore = "…" 483 at ignore = "…"
460 at inline(…) 484 at inline(…)
461 at link 485 at link
462 at link_name = "…" 486 at link_name = "…"
487 at link_section = "…"
463 at macro_export 488 at macro_export
464 at macro_use 489 at macro_use
465 at must_use = "…" 490 at must_use = "…"
@@ -473,6 +498,7 @@ struct Test {}
473 at should_panic(…) 498 at should_panic(…)
474 at target_feature = "…" 499 at target_feature = "…"
475 at test 500 at test
501 at track_caller
476 at used 502 at used
477 at warn(…) 503 at warn(…)
478 "#]], 504 "#]],
@@ -490,12 +516,16 @@ struct Test {}
490 r"#![<|>]", 516 r"#![<|>]",
491 expect![[r#" 517 expect![[r#"
492 at allow(…) 518 at allow(…)
519 at automatically_derived
493 at cfg(…) 520 at cfg(…)
494 at cfg_attr(…) 521 at cfg_attr(…)
522 at cold
523 at crate_name = ""
495 at deny(…) 524 at deny(…)
496 at deprecated = "…" 525 at deprecated = "…"
497 at derive(…) 526 at derive(…)
498 at doc = "…" 527 at doc = "…"
528 at export_name = "…"
499 at feature(…) 529 at feature(…)
500 at forbid(…) 530 at forbid(…)
501 at global_allocator 531 at global_allocator
@@ -503,9 +533,13 @@ struct Test {}
503 at inline(…) 533 at inline(…)
504 at link 534 at link
505 at link_name = "…" 535 at link_name = "…"
536 at link_section = "…"
506 at macro_export 537 at macro_export
507 at macro_use 538 at macro_use
508 at must_use = "…" 539 at must_use = "…"
540 at no_implicit_prelude
541 at no_link
542 at no_main
509 at no_mangle 543 at no_mangle
510 at no_std 544 at no_std
511 at non_exhaustive 545 at non_exhaustive
@@ -519,6 +553,8 @@ struct Test {}
519 at should_panic(…) 553 at should_panic(…)
520 at target_feature = "…" 554 at target_feature = "…"
521 at test 555 at test
556 at track_caller
557 at type_length_limit = …
522 at used 558 at used
523 at warn(…) 559 at warn(…)
524 at windows_subsystem = "…" 560 at windows_subsystem = "…"
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 4500050f1..9a1e9ba49 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -362,7 +362,9 @@ impl Module {
362 } 362 }
363 363
364 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 364 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
365 let _p = profile::span("Module::diagnostics"); 365 let _p = profile::span("Module::diagnostics").detail(|| {
366 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
367 });
366 let crate_def_map = db.crate_def_map(self.id.krate); 368 let crate_def_map = db.crate_def_map(self.id.krate);
367 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); 369 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
368 for decl in self.declarations(db) { 370 for decl in self.declarations(db) {
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 33eb5e78c..92bcc1705 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -120,18 +120,24 @@ impl Expander {
120 self.resolve_path_as_macro(db, &path) 120 self.resolve_path_as_macro(db, &path)
121 }; 121 };
122 122
123 let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { 123 let mut err = None;
124 let call_id =
125 macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| {
126 err.get_or_insert(e);
127 });
128 let call_id = match call_id {
124 Some(it) => it, 129 Some(it) => it,
125 None => { 130 None => {
126 // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough 131 if err.is_none() {
127 // info. 132 eprintln!("no error despite `as_call_id_with_errors` returning `None`");
128 return ExpandResult::only_err(mbe::ExpandError::Other( 133 }
129 "failed to parse or resolve macro invocation".into(), 134 return ExpandResult { value: None, err };
130 ));
131 } 135 }
132 }; 136 };
133 137
134 let err = db.macro_expand_error(call_id); 138 if err.is_none() {
139 err = db.macro_expand_error(call_id);
140 }
135 141
136 let file_id = call_id.as_file(); 142 let file_id = call_id.as_file();
137 143
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index baf1179f1..6dba9817d 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -78,21 +78,41 @@ fn f() {
78fn macro_diag_builtin() { 78fn macro_diag_builtin() {
79 check_diagnostics( 79 check_diagnostics(
80 r#" 80 r#"
81#[rustc_builtin_macro]
82macro_rules! env {}
83
84#[rustc_builtin_macro]
85macro_rules! include {}
86
87#[rustc_builtin_macro]
88macro_rules! compile_error {}
89
90#[rustc_builtin_macro]
91macro_rules! format_args {
92 () => {}
93}
94
81fn f() { 95fn f() {
82 // Test a handful of built-in (eager) macros: 96 // Test a handful of built-in (eager) macros:
83 97
84 include!(invalid); 98 include!(invalid);
85 //^^^^^^^^^^^^^^^^^ failed to parse or resolve macro invocation 99 //^^^^^^^^^^^^^^^^^ could not convert tokens
86 include!("does not exist"); 100 include!("does not exist");
87 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to parse or resolve macro invocation 101 //^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens
88 102
89 env!(invalid); 103 env!(invalid);
90 //^^^^^^^^^^^^^ failed to parse or resolve macro invocation 104 //^^^^^^^^^^^^^ could not convert tokens
105
106 env!("OUT_DIR");
107 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
108
109 compile_error!("compile_error works");
110 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
91 111
92 // Lazy: 112 // Lazy:
93 113
94 format_args!(); 114 format_args!();
95 //^^^^^^^^^^^^^^ failed to parse or resolve macro invocation 115 //^^^^^^^^^^^^^^ no rule matches input tokens
96} 116}
97 "#, 117 "#,
98 ); 118 );
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 1b22d1eec..b41c5acb2 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -465,21 +465,37 @@ pub trait AsMacroCall {
465 db: &dyn db::DefDatabase, 465 db: &dyn db::DefDatabase,
466 krate: CrateId, 466 krate: CrateId,
467 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 467 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
468 ) -> Option<MacroCallId> {
469 self.as_call_id_with_errors(db, krate, resolver, &mut |_| ())
470 }
471
472 fn as_call_id_with_errors(
473 &self,
474 db: &dyn db::DefDatabase,
475 krate: CrateId,
476 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
477 error_sink: &mut dyn FnMut(mbe::ExpandError),
468 ) -> Option<MacroCallId>; 478 ) -> Option<MacroCallId>;
469} 479}
470 480
471impl AsMacroCall for InFile<&ast::MacroCall> { 481impl AsMacroCall for InFile<&ast::MacroCall> {
472 fn as_call_id( 482 fn as_call_id_with_errors(
473 &self, 483 &self,
474 db: &dyn db::DefDatabase, 484 db: &dyn db::DefDatabase,
475 krate: CrateId, 485 krate: CrateId,
476 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 486 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
487 error_sink: &mut dyn FnMut(mbe::ExpandError),
477 ) -> Option<MacroCallId> { 488 ) -> Option<MacroCallId> {
478 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 489 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
479 let h = Hygiene::new(db.upcast(), self.file_id); 490 let h = Hygiene::new(db.upcast(), self.file_id);
480 let path = path::ModPath::from_src(self.value.path()?, &h)?; 491 let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
492
493 if path.is_none() {
494 error_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
495 }
481 496
482 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver) 497 AstIdWithPath::new(ast_id.file_id, ast_id.value, path?)
498 .as_call_id_with_errors(db, krate, resolver, error_sink)
483 } 499 }
484} 500}
485 501
@@ -497,22 +513,32 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
497} 513}
498 514
499impl AsMacroCall for AstIdWithPath<ast::MacroCall> { 515impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
500 fn as_call_id( 516 fn as_call_id_with_errors(
501 &self, 517 &self,
502 db: &dyn db::DefDatabase, 518 db: &dyn db::DefDatabase,
503 krate: CrateId, 519 krate: CrateId,
504 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 520 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
521 error_sink: &mut dyn FnMut(mbe::ExpandError),
505 ) -> Option<MacroCallId> { 522 ) -> Option<MacroCallId> {
506 let def: MacroDefId = resolver(self.path.clone())?; 523 let def: MacroDefId = resolver(self.path.clone()).or_else(|| {
524 error_sink(mbe::ExpandError::Other(format!("could not resolve macro `{}`", self.path)));
525 None
526 })?;
507 527
508 if let MacroDefKind::BuiltInEager(_) = def.kind { 528 if let MacroDefKind::BuiltInEager(_) = def.kind {
509 let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast())); 529 let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast()));
510 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 530 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
511 531
512 Some( 532 Some(
513 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| { 533 expand_eager_macro(
514 resolver(path::ModPath::from_src(path, &hygiene)?) 534 db.upcast(),
515 })? 535 krate,
536 macro_call,
537 def,
538 &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?),
539 error_sink,
540 )
541 .ok()?
516 .into(), 542 .into(),
517 ) 543 )
518 } else { 544 } else {
@@ -522,13 +548,18 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
522} 548}
523 549
524impl AsMacroCall for AstIdWithPath<ast::Item> { 550impl AsMacroCall for AstIdWithPath<ast::Item> {
525 fn as_call_id( 551 fn as_call_id_with_errors(
526 &self, 552 &self,
527 db: &dyn db::DefDatabase, 553 db: &dyn db::DefDatabase,
528 krate: CrateId, 554 krate: CrateId,
529 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 555 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
556 error_sink: &mut dyn FnMut(mbe::ExpandError),
530 ) -> Option<MacroCallId> { 557 ) -> Option<MacroCallId> {
531 let def = resolver(self.path.clone())?; 558 let def: MacroDefId = resolver(self.path.clone()).or_else(|| {
559 error_sink(mbe::ExpandError::Other(format!("could not resolve macro `{}`", self.path)));
560 None
561 })?;
562
532 Some( 563 Some(
533 def.as_lazy_macro( 564 def.as_lazy_macro(
534 db.upcast(), 565 db.upcast(),
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 7f4db106d..44a5556b6 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -86,7 +86,6 @@ pub fn find_builtin_macro(
86register_builtin! { 86register_builtin! {
87 LAZY: 87 LAZY:
88 (column, Column) => column_expand, 88 (column, Column) => column_expand,
89 (compile_error, CompileError) => compile_error_expand,
90 (file, File) => file_expand, 89 (file, File) => file_expand,
91 (line, Line) => line_expand, 90 (line, Line) => line_expand,
92 (assert, Assert) => assert_expand, 91 (assert, Assert) => assert_expand,
@@ -97,6 +96,7 @@ register_builtin! {
97 (format_args_nl, FormatArgsNl) => format_args_expand, 96 (format_args_nl, FormatArgsNl) => format_args_expand,
98 97
99 EAGER: 98 EAGER:
99 (compile_error, CompileError) => compile_error_expand,
100 (concat, Concat) => concat_expand, 100 (concat, Concat) => concat_expand,
101 (include, Include) => include_expand, 101 (include, Include) => include_expand,
102 (include_bytes, IncludeBytes) => include_bytes_expand, 102 (include_bytes, IncludeBytes) => include_bytes_expand,
@@ -213,25 +213,6 @@ fn file_expand(
213 ExpandResult::ok(expanded) 213 ExpandResult::ok(expanded)
214} 214}
215 215
216fn compile_error_expand(
217 _db: &dyn AstDatabase,
218 _id: LazyMacroId,
219 tt: &tt::Subtree,
220) -> ExpandResult<tt::Subtree> {
221 if tt.count() == 1 {
222 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
223 let s = it.text.as_str();
224 if s.contains('"') {
225 return ExpandResult::ok(quote! { loop { #it }});
226 }
227 };
228 }
229
230 ExpandResult::only_err(mbe::ExpandError::BindingError(
231 "`compile_error!` argument be a string".into(),
232 ))
233}
234
235fn format_args_expand( 216fn format_args_expand(
236 _db: &dyn AstDatabase, 217 _db: &dyn AstDatabase,
237 _id: LazyMacroId, 218 _id: LazyMacroId,
@@ -280,6 +261,27 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
280 token.value().map(|it| it.into_owned()) 261 token.value().map(|it| it.into_owned())
281} 262}
282 263
264fn compile_error_expand(
265 _db: &dyn AstDatabase,
266 _id: EagerMacroId,
267 tt: &tt::Subtree,
268) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
269 let err = match &*tt.token_trees {
270 [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => {
271 let text = it.text.as_str();
272 if text.starts_with('"') && text.ends_with('"') {
273 // FIXME: does not handle raw strings
274 mbe::ExpandError::Other(text[1..text.len() - 1].to_string())
275 } else {
276 mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
277 }
278 }
279 _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
280 };
281
282 ExpandResult { value: Some((quote! {}, FragmentKind::Items)), err: Some(err) }
283}
284
283fn concat_expand( 285fn concat_expand(
284 _db: &dyn AstDatabase, 286 _db: &dyn AstDatabase,
285 _arg_id: EagerMacroId, 287 _arg_id: EagerMacroId,
@@ -417,17 +419,25 @@ fn env_expand(
417 Err(e) => return ExpandResult::only_err(e), 419 Err(e) => return ExpandResult::only_err(e),
418 }; 420 };
419 421
420 // FIXME: 422 let mut err = None;
421 // If the environment variable is not defined int rustc, then a compilation error will be emitted. 423 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
422 // We might do the same if we fully support all other stuffs. 424 // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
423 // But for now on, we should return some dummy string for better type infer purpose. 425 // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
424 // However, we cannot use an empty string here, because for 426 if key == "OUT_DIR" {
425 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become 427 err = Some(mbe::ExpandError::Other(
426 // `include!("foo.rs"), which might go to infinite loop 428 r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(),
427 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); 429 ));
430 }
431
432 // If the variable is unset, still return a dummy string to help type inference along.
433 // We cannot use an empty string here, because for
434 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
435 // `include!("foo.rs"), which might go to infinite loop
436 "__RA_UNIMPLEMENTED__".to_string()
437 });
428 let expanded = quote! { #s }; 438 let expanded = quote! { #s };
429 439
430 ExpandResult::ok(Some((expanded, FragmentKind::Expr))) 440 ExpandResult { value: Some((expanded, FragmentKind::Expr)), err }
431} 441}
432 442
433fn option_env_expand( 443fn option_env_expand(
@@ -638,7 +648,8 @@ mod tests {
638 "#, 648 "#,
639 ); 649 );
640 650
641 assert_eq!(expanded, r#"loop{"error!"}"#); 651 // This expands to nothing (since it's in item position), but emits an error.
652 assert_eq!(expanded, "");
642 } 653 }
643 654
644 #[test] 655 #[test]
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 4fd0ba290..842a177db 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -207,6 +207,7 @@ fn macro_expand_with_arg(
207 } else { 207 } else {
208 return ExpandResult { 208 return ExpandResult {
209 value: Some(db.lookup_intern_eager_expansion(id).subtree), 209 value: Some(db.lookup_intern_eager_expansion(id).subtree),
210 // FIXME: There could be errors here!
210 err: None, 211 err: None,
211 }; 212 };
212 } 213 }
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index ab6b4477c..0229a836e 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -26,19 +26,89 @@ use crate::{
26}; 26};
27 27
28use base_db::CrateId; 28use base_db::CrateId;
29use mbe::ExpandResult;
29use parser::FragmentKind; 30use parser::FragmentKind;
30use std::sync::Arc; 31use std::sync::Arc;
31use syntax::{algo::SyntaxRewriter, SyntaxNode}; 32use syntax::{algo::SyntaxRewriter, SyntaxNode};
32 33
34pub struct ErrorEmitted {
35 _private: (),
36}
37
38trait ErrorSink {
39 fn emit(&mut self, err: mbe::ExpandError);
40
41 fn option<T>(
42 &mut self,
43 opt: Option<T>,
44 error: impl FnOnce() -> mbe::ExpandError,
45 ) -> Result<T, ErrorEmitted> {
46 match opt {
47 Some(it) => Ok(it),
48 None => {
49 self.emit(error());
50 Err(ErrorEmitted { _private: () })
51 }
52 }
53 }
54
55 fn option_with<T>(
56 &mut self,
57 opt: impl FnOnce() -> Option<T>,
58 error: impl FnOnce() -> mbe::ExpandError,
59 ) -> Result<T, ErrorEmitted> {
60 self.option(opt(), error)
61 }
62
63 fn result<T>(&mut self, res: Result<T, mbe::ExpandError>) -> Result<T, ErrorEmitted> {
64 match res {
65 Ok(it) => Ok(it),
66 Err(e) => {
67 self.emit(e);
68 Err(ErrorEmitted { _private: () })
69 }
70 }
71 }
72
73 fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
74 match (res.value, res.err) {
75 (None, Some(err)) => {
76 self.emit(err);
77 Err(ErrorEmitted { _private: () })
78 }
79 (Some(value), opt_err) => {
80 if let Some(err) = opt_err {
81 self.emit(err);
82 }
83 Ok(value)
84 }
85 (None, None) => unreachable!("`ExpandResult` without value or error"),
86 }
87 }
88}
89
90impl ErrorSink for &'_ mut dyn FnMut(mbe::ExpandError) {
91 fn emit(&mut self, err: mbe::ExpandError) {
92 self(err);
93 }
94}
95
96fn err(msg: impl Into<String>) -> mbe::ExpandError {
97 mbe::ExpandError::Other(msg.into())
98}
99
33pub fn expand_eager_macro( 100pub fn expand_eager_macro(
34 db: &dyn AstDatabase, 101 db: &dyn AstDatabase,
35 krate: CrateId, 102 krate: CrateId,
36 macro_call: InFile<ast::MacroCall>, 103 macro_call: InFile<ast::MacroCall>,
37 def: MacroDefId, 104 def: MacroDefId,
38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 105 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
39) -> Option<EagerMacroId> { 106 mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
40 let args = macro_call.value.token_tree()?; 107) -> Result<EagerMacroId, ErrorEmitted> {
41 let parsed_args = mbe::ast_to_token_tree(&args)?.0; 108 let parsed_args = diagnostic_sink.option_with(
109 || Some(mbe::ast_to_token_tree(&macro_call.value.token_tree()?)?.0),
110 || err("malformed macro invocation"),
111 )?;
42 112
43 // Note: 113 // Note:
44 // When `lazy_expand` is called, its *parent* file must be already exists. 114 // When `lazy_expand` is called, its *parent* file must be already exists.
@@ -55,17 +125,22 @@ pub fn expand_eager_macro(
55 }); 125 });
56 let arg_file_id: MacroCallId = arg_id.into(); 126 let arg_file_id: MacroCallId = arg_id.into();
57 127
58 let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; 128 let parsed_args =
129 diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0;
59 let result = eager_macro_recur( 130 let result = eager_macro_recur(
60 db, 131 db,
61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 132 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate, 133 krate,
63 resolver, 134 resolver,
135 diagnostic_sink,
64 )?; 136 )?;
65 let subtree = to_subtree(&result)?; 137 let subtree =
138 diagnostic_sink.option(to_subtree(&result), || err("failed to parse macro result"))?;
66 139
67 if let MacroDefKind::BuiltInEager(eager) = def.kind { 140 if let MacroDefKind::BuiltInEager(eager) = def.kind {
68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?; 141 let res = eager.expand(db, arg_id, &subtree);
142
143 let (subtree, fragment) = diagnostic_sink.expand_result_option(res)?;
69 let eager = EagerCallLoc { 144 let eager = EagerCallLoc {
70 def, 145 def,
71 fragment, 146 fragment,
@@ -74,9 +149,9 @@ pub fn expand_eager_macro(
74 file_id: macro_call.file_id, 149 file_id: macro_call.file_id,
75 }; 150 };
76 151
77 Some(db.intern_eager_expansion(eager)) 152 Ok(db.intern_eager_expansion(eager))
78 } else { 153 } else {
79 None 154 panic!("called `expand_eager_macro` on non-eager macro def {:?}", def);
80 } 155 }
81} 156}
82 157
@@ -91,13 +166,16 @@ fn lazy_expand(
91 def: &MacroDefId, 166 def: &MacroDefId,
92 macro_call: InFile<ast::MacroCall>, 167 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId, 168 krate: CrateId,
94) -> Option<InFile<SyntaxNode>> { 169) -> ExpandResult<Option<InFile<SyntaxNode>>> {
95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 170 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
96 171
97 let id: MacroCallId = 172 let id: MacroCallId =
98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 173 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
99 174
100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 175 let err = db.macro_expand_error(id);
176 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
177
178 ExpandResult { value, err }
101} 179}
102 180
103fn eager_macro_recur( 181fn eager_macro_recur(
@@ -105,7 +183,8 @@ fn eager_macro_recur(
105 curr: InFile<SyntaxNode>, 183 curr: InFile<SyntaxNode>,
106 krate: CrateId, 184 krate: CrateId,
107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 185 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
108) -> Option<SyntaxNode> { 186 mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
187) -> Result<SyntaxNode, ErrorEmitted> {
109 let original = curr.value.clone(); 188 let original = curr.value.clone();
110 189
111 let children = curr.value.descendants().filter_map(ast::MacroCall::cast); 190 let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
@@ -113,7 +192,8 @@ fn eager_macro_recur(
113 192
114 // Collect replacement 193 // Collect replacement
115 for child in children { 194 for child in children {
116 let def: MacroDefId = macro_resolver(child.path()?)?; 195 let def = diagnostic_sink
196 .option_with(|| macro_resolver(child.path()?), || err("failed to resolve macro"))?;
117 let insert = match def.kind { 197 let insert = match def.kind {
118 MacroDefKind::BuiltInEager(_) => { 198 MacroDefKind::BuiltInEager(_) => {
119 let id: MacroCallId = expand_eager_macro( 199 let id: MacroCallId = expand_eager_macro(
@@ -122,17 +202,21 @@ fn eager_macro_recur(
122 curr.with_value(child.clone()), 202 curr.with_value(child.clone()),
123 def, 203 def,
124 macro_resolver, 204 macro_resolver,
205 diagnostic_sink,
125 )? 206 )?
126 .into(); 207 .into();
127 db.parse_or_expand(id.as_file())? 208 db.parse_or_expand(id.as_file())
209 .expect("successful macro expansion should be parseable")
128 } 210 }
129 MacroDefKind::Declarative 211 MacroDefKind::Declarative
130 | MacroDefKind::BuiltIn(_) 212 | MacroDefKind::BuiltIn(_)
131 | MacroDefKind::BuiltInDerive(_) 213 | MacroDefKind::BuiltInDerive(_)
132 | MacroDefKind::ProcMacro(_) => { 214 | MacroDefKind::ProcMacro(_) => {
133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; 215 let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
216 let val = diagnostic_sink.expand_result_option(res)?;
217
134 // replace macro inside 218 // replace macro inside
135 eager_macro_recur(db, expanded, krate, macro_resolver)? 219 eager_macro_recur(db, val, krate, macro_resolver, diagnostic_sink)?
136 } 220 }
137 }; 221 };
138 222
@@ -140,5 +224,5 @@ fn eager_macro_recur(
140 } 224 }
141 225
142 let res = rewriter.rewrite(&original); 226 let res = rewriter.rewrite(&original);
143 Some(res) 227 Ok(res)
144} 228}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 6dad2507b..2633fd8f7 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -143,7 +143,11 @@ impl HirFileId {
143 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id); 143 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
144 144
145 let arg_tt = loc.kind.arg(db)?; 145 let arg_tt = loc.kind.arg(db)?;
146 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; 146
147 let def = loc.def.ast_id.and_then(|id| {
148 let def_tt = id.to_node(db).token_tree()?;
149 Some(InFile::new(id.file_id, def_tt))
150 });
147 151
148 let macro_def = db.macro_def(loc.def)?; 152 let macro_def = db.macro_def(loc.def)?;
149 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; 153 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
@@ -152,7 +156,7 @@ impl HirFileId {
152 Some(ExpansionInfo { 156 Some(ExpansionInfo {
153 expanded: InFile::new(self, parse.syntax_node()), 157 expanded: InFile::new(self, parse.syntax_node()),
154 arg: InFile::new(loc.kind.file_id(), arg_tt), 158 arg: InFile::new(loc.kind.file_id(), arg_tt),
155 def: InFile::new(loc.def.ast_id?.file_id, def_tt), 159 def,
156 macro_arg, 160 macro_arg,
157 macro_def, 161 macro_def,
158 exp_map, 162 exp_map,
@@ -311,7 +315,8 @@ pub struct EagerCallLoc {
311pub struct ExpansionInfo { 315pub struct ExpansionInfo {
312 expanded: InFile<SyntaxNode>, 316 expanded: InFile<SyntaxNode>,
313 arg: InFile<SyntaxNode>, 317 arg: InFile<SyntaxNode>,
314 def: InFile<ast::TokenTree>, 318 /// The `macro_rules!` arguments.
319 def: Option<InFile<ast::TokenTree>>,
315 320
316 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, 321 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
317 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, 322 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
@@ -348,9 +353,14 @@ impl ExpansionInfo {
348 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 353 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
349 let (token_map, tt) = match origin { 354 let (token_map, tt) = match origin {
350 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), 355 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
351 mbe::Origin::Def => { 356 mbe::Origin::Def => (
352 (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone())) 357 &self.macro_def.1,
353 } 358 self.def
359 .as_ref()
360 .expect("`Origin::Def` used with non-`macro_rules!` macro")
361 .as_ref()
362 .map(|tt| tt.syntax().clone()),
363 ),
354 }; 364 };
355 365
356 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 366 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index cf5c38a23..289e812fe 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.37", default-features = false } 20chalk-solve = { version = "0.43", default-features = false }
21chalk-ir = "0.37" 21chalk-ir = "0.43"
22chalk-recursive = "0.37" 22chalk-recursive = "0.43"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 25cf9eb7f..66bdb8e88 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -99,6 +99,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
99 #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] 99 #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)]
100 fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>; 100 fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>;
101 101
102 #[salsa::invoke(crate::traits::chalk::fn_def_variance_query)]
103 fn fn_def_variance(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> chalk::Variances;
104
105 #[salsa::invoke(crate::traits::chalk::adt_variance_query)]
106 fn adt_variance(&self, krate: CrateId, adt_id: chalk::AdtId) -> chalk::Variances;
107
102 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] 108 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)]
103 fn associated_ty_value( 109 fn associated_ty_value(
104 &self, 110 &self,
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 5a6f0c67f..8a289f52a 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -720,7 +720,13 @@ fn transform_receiver_ty(
720 .push(self_ty.value.clone()) 720 .push(self_ty.value.clone())
721 .fill_with_unknown() 721 .fill_with_unknown()
722 .build(), 722 .build(),
723 AssocContainerId::ImplId(impl_id) => inherent_impl_substs(db, impl_id, &self_ty)?, 723 AssocContainerId::ImplId(impl_id) => {
724 let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?;
725 Substs::build_for_def(db, function_id)
726 .use_parent_substs(&impl_substs)
727 .fill_with_unknown()
728 .build()
729 }
724 AssocContainerId::ContainerId(_) => unreachable!(), 730 AssocContainerId::ContainerId(_) => unreachable!(),
725 }; 731 };
726 let sig = db.callable_item_signature(function_id.into()); 732 let sig = db.callable_item_signature(function_id.into());
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 0f17ff151..a6a54e542 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1087,3 +1087,22 @@ fn method_resolution_foreign_opaque_type() {
1087 "#]], 1087 "#]],
1088 ); 1088 );
1089} 1089}
1090
1091#[test]
1092fn method_with_allocator_box_self_type() {
1093 check_types(
1094 r#"
1095struct Slice<T> {}
1096struct Box<T, A> {}
1097
1098impl<T> Slice<T> {
1099 pub fn into_vec<A>(self: Box<Self, A>) { }
1100}
1101
1102fn main() {
1103 let foo: Slice<u32>;
1104 (foo.into_vec()); // we don't actually support arbitrary self types, but we shouldn't crash at least
1105} //^ {unknown}
1106"#,
1107 );
1108}
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 94d86b0d1..8cf4e7012 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -840,3 +840,46 @@ fn issue_4966() {
840 "#]], 840 "#]],
841 ); 841 );
842} 842}
843
844#[test]
845fn issue_6628() {
846 check_infer(
847 r#"
848 #[lang = "fn_once"]
849 pub trait FnOnce<Args> {
850 type Output;
851 }
852
853 struct S<T>();
854 impl<T> S<T> {
855 fn f(&self, _t: T) {}
856 fn g<F: FnOnce(&T)>(&self, _f: F) {}
857 }
858 fn main() {
859 let s = S();
860 s.g(|_x| {});
861 s.f(10);
862 }
863 "#,
864 expect![[r#"
865 105..109 'self': &S<T>
866 111..113 '_t': T
867 118..120 '{}': ()
868 146..150 'self': &S<T>
869 152..154 '_f': F
870 159..161 '{}': ()
871 174..225 '{ ...10); }': ()
872 184..185 's': S<i32>
873 188..189 'S': S<i32>() -> S<i32>
874 188..191 'S()': S<i32>
875 197..198 's': S<i32>
876 197..209 's.g(|_x| {})': ()
877 201..208 '|_x| {}': |&i32| -> ()
878 202..204 '_x': &i32
879 206..208 '{}': ()
880 215..216 's': S<i32>
881 215..222 's.f(10)': ()
882 219..221 '10': i32
883 "#]],
884 );
885}
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index ce1174cbe..dfa51896b 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -1,4 +1,5 @@
1//! Trait solving using Chalk. 1//! Trait solving using Chalk.
2use std::env::var;
2use std::sync::Arc; 3use std::sync::Arc;
3 4
4use base_db::CrateId; 5use base_db::CrateId;
@@ -15,12 +16,6 @@ use self::chalk::{from_chalk, Interner, ToChalk};
15 16
16pub(crate) mod chalk; 17pub(crate) mod chalk;
17 18
18// This controls the maximum size of types Chalk considers. If we set this too
19// high, we can run into slow edge cases; if we set it too low, Chalk won't
20// find some solutions.
21// FIXME this is currently hardcoded in the recursive solver
22// const CHALK_SOLVER_MAX_SIZE: usize = 10;
23
24/// This controls how much 'time' we give the Chalk solver before giving up. 19/// This controls how much 'time' we give the Chalk solver before giving up.
25const CHALK_SOLVER_FUEL: i32 = 100; 20const CHALK_SOLVER_FUEL: i32 = 100;
26 21
@@ -31,9 +26,11 @@ struct ChalkContext<'a> {
31} 26}
32 27
33fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { 28fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
34 let overflow_depth = 100; 29 let overflow_depth =
30 var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(100);
35 let caching_enabled = true; 31 let caching_enabled = true;
36 chalk_recursive::RecursiveSolver::new(overflow_depth, caching_enabled) 32 let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(30);
33 chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, caching_enabled)
37} 34}
38 35
39/// A set of clauses that we assume to be true. E.g. if we are inside this function: 36/// A set of clauses that we assume to be true. E.g. if we are inside this function:
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 55e2c3a3e..69eae6f79 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -104,7 +104,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
104 }; 104 };
105 105
106 // Note: Since we're using impls_for_trait, only impls where the trait 106 // Note: Since we're using impls_for_trait, only impls where the trait
107 // can be resolved should ever reach Chalk. `impl_datum` relies on that 107 // can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that
108 // and will panic if the trait can't be resolved. 108 // and will panic if the trait can't be resolved.
109 let in_deps = self.db.trait_impls_in_deps(self.krate); 109 let in_deps = self.db.trait_impls_in_deps(self.krate);
110 let in_self = self.db.trait_impls_in_crate(self.krate); 110 let in_self = self.db.trait_impls_in_crate(self.krate);
@@ -206,7 +206,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
206 Some((trait_, alias)) 206 Some((trait_, alias))
207 }) 207 })
208 { 208 {
209 // Making up `AsyncBlock<T>: Future<Output = T>` 209 // Making up Symbol’s value as variable is void: AsyncBlock<T>:
210 // 210 //
211 // |--------------------OpaqueTyDatum-------------------| 211 // |--------------------OpaqueTyDatum-------------------|
212 // |-------------OpaqueTyDatumBound--------------| 212 // |-------------OpaqueTyDatumBound--------------|
@@ -242,7 +242,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
242 // The opaque type has 1 parameter. 242 // The opaque type has 1 parameter.
243 make_binders(bound, 1) 243 make_binders(bound, 1)
244 } else { 244 } else {
245 // If failed to find `Future::Output`, return empty bounds as fallback. 245 // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
246 let bound = OpaqueTyDatumBound { 246 let bound = OpaqueTyDatumBound {
247 bounds: make_binders(vec![], 0), 247 bounds: make_binders(vec![], 0),
248 where_clauses: make_binders(vec![], 0), 248 where_clauses: make_binders(vec![], 0),
@@ -343,6 +343,23 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
343 // FIXME 343 // FIXME
344 unimplemented!() 344 unimplemented!()
345 } 345 }
346
347 fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
348 self
349 }
350}
351
352impl<'a> chalk_ir::UnificationDatabase<Interner> for ChalkContext<'a> {
353 fn fn_def_variance(
354 &self,
355 fn_def_id: chalk_ir::FnDefId<Interner>,
356 ) -> chalk_ir::Variances<Interner> {
357 self.db.fn_def_variance(self.krate, fn_def_id)
358 }
359
360 fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> {
361 self.db.adt_variance(self.krate, adt_id)
362 }
346} 363}
347 364
348pub(crate) fn program_clauses_for_chalk_env_query( 365pub(crate) fn program_clauses_for_chalk_env_query(
@@ -644,6 +661,32 @@ pub(crate) fn fn_def_datum_query(
644 Arc::new(datum) 661 Arc::new(datum)
645} 662}
646 663
664pub(crate) fn fn_def_variance_query(
665 db: &dyn HirDatabase,
666 _krate: CrateId,
667 fn_def_id: FnDefId,
668) -> Variances {
669 let callable_def: CallableDefId = from_chalk(db, fn_def_id);
670 let generic_params = generics(db.upcast(), callable_def.into());
671 Variances::from(
672 &Interner,
673 std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()),
674 )
675}
676
677pub(crate) fn adt_variance_query(
678 db: &dyn HirDatabase,
679 _krate: CrateId,
680 adt_id: AdtId,
681) -> Variances {
682 let adt: crate::AdtId = from_chalk(db, adt_id);
683 let generic_params = generics(db.upcast(), adt.into());
684 Variances::from(
685 &Interner,
686 std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()),
687 )
688}
689
647impl From<FnDefId> for crate::db::InternedCallableDefId { 690impl From<FnDefId> for crate::db::InternedCallableDefId {
648 fn from(fn_def_id: FnDefId) -> Self { 691 fn from(fn_def_id: FnDefId) -> Self {
649 InternKey::from_intern_id(fn_def_id.0) 692 InternKey::from_intern_id(fn_def_id.0)
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs
index 39569e690..6a4aa8333 100644
--- a/crates/hir_ty/src/traits/chalk/interner.rs
+++ b/crates/hir_ty/src/traits/chalk/interner.rs
@@ -25,6 +25,7 @@ pub(crate) type FnDefId = chalk_ir::FnDefId<Interner>;
25pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; 25pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
26pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; 26pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
27pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; 27pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
28pub(crate) type Variances = chalk_ir::Variances<Interner>;
28 29
29impl chalk_ir::interner::Interner for Interner { 30impl chalk_ir::interner::Interner for Interner {
30 type InternedType = Arc<chalk_ir::TyData<Self>>; 31 type InternedType = Arc<chalk_ir::TyData<Self>>;
@@ -41,6 +42,7 @@ impl chalk_ir::interner::Interner for Interner {
41 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; 42 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
42 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; 43 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
43 type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; 44 type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
45 type InternedVariances = Arc<[chalk_ir::Variance]>;
44 type DefId = InternId; 46 type DefId = InternId;
45 type InternedAdtId = hir_def::AdtId; 47 type InternedAdtId = hir_def::AdtId;
46 type Identifier = TypeAliasId; 48 type Identifier = TypeAliasId;
@@ -370,6 +372,20 @@ impl chalk_ir::interner::Interner for Interner {
370 ) -> Option<fmt::Result> { 372 ) -> Option<fmt::Result> {
371 None 373 None
372 } 374 }
375
376 fn intern_variances<E>(
377 &self,
378 data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>,
379 ) -> Result<Self::InternedVariances, E> {
380 data.into_iter().collect()
381 }
382
383 fn variances_data<'a>(
384 &self,
385 variances: &'a Self::InternedVariances,
386 ) -> &'a [chalk_ir::Variance] {
387 &variances
388 }
373} 389}
374 390
375impl chalk_ir::interner::HasInterner for Interner { 391impl chalk_ir::interner::HasInterner for Interner {
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index 86cbc4c7e..8700d664e 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -31,7 +31,8 @@ impl ToChalk for Ty {
31 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), 31 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
32 TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), 32 TypeCtor::Array => array_to_chalk(db, apply_ty.parameters),
33 TypeCtor::FnPtr { num_args: _, is_varargs } => { 33 TypeCtor::FnPtr { num_args: _, is_varargs } => {
34 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); 34 let substitution =
35 chalk_ir::FnSubst(apply_ty.parameters.to_chalk(db).shifted_in(&Interner));
35 chalk_ir::TyKind::Function(chalk_ir::FnPointer { 36 chalk_ir::TyKind::Function(chalk_ir::FnPointer {
36 num_binders: 0, 37 num_binders: 0,
37 sig: chalk_ir::FnSig { 38 sig: chalk_ir::FnSig {
@@ -183,7 +184,7 @@ impl ToChalk for Ty {
183 assert_eq!(num_binders, 0); 184 assert_eq!(num_binders, 0);
184 let parameters: Substs = from_chalk( 185 let parameters: Substs = from_chalk(
185 db, 186 db,
186 substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), 187 substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"),
187 ); 188 );
188 Ty::Apply(ApplicationTy { 189 Ty::Apply(ApplicationTy {
189 ctor: TypeCtor::FnPtr { 190 ctor: TypeCtor::FnPtr {
@@ -536,6 +537,7 @@ impl ToChalk for GenericPredicate {
536 // we don't produce any where clauses with binders and can't currently deal with them 537 // we don't produce any where clauses with binders and can't currently deal with them
537 match where_clause 538 match where_clause
538 .skip_binders() 539 .skip_binders()
540 .clone()
539 .shifted_out(&Interner) 541 .shifted_out(&Interner)
540 .expect("unexpected bound vars in where clause") 542 .expect("unexpected bound vars in where clause")
541 { 543 {
@@ -661,7 +663,12 @@ where
661 chalk_ir::TyVariableKind::Integer => TyKind::Integer, 663 chalk_ir::TyVariableKind::Integer => TyKind::Integer,
662 chalk_ir::TyVariableKind::Float => TyKind::Float, 664 chalk_ir::TyVariableKind::Float => TyKind::Float,
663 }, 665 },
664 chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"), 666 // HACK: Chalk can sometimes return new lifetime variables. We
667 // want to just skip them, but to not mess up the indices of
668 // other variables, we'll just create a new type variable in
669 // their place instead. This should not matter (we never see the
670 // actual *uses* of the lifetime variable).
671 chalk_ir::VariableKind::Lifetime => TyKind::General,
665 chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"), 672 chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"),
666 }) 673 })
667 .collect(); 674 .collect();
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 9d3d88289..c8453edb3 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -135,6 +135,11 @@ pub(crate) fn diagnostics(
135 res.borrow_mut().push(warning_with_fix(d, &sema)); 135 res.borrow_mut().push(warning_with_fix(d, &sema));
136 }) 136 })
137 .on::<hir::diagnostics::InactiveCode, _>(|d| { 137 .on::<hir::diagnostics::InactiveCode, _>(|d| {
138 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
139 if d.display_source().file_id.expansion_info(db).is_some() {
140 return;
141 }
142
138 // Override severity and mark as unused. 143 // Override severity and mark as unused.
139 res.borrow_mut().push( 144 res.borrow_mut().push(
140 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) 145 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 5150a970c..f5c6eabef 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -726,7 +726,7 @@ fn highlight_method_call(
726 method_call: &ast::MethodCallExpr, 726 method_call: &ast::MethodCallExpr,
727) -> Option<Highlight> { 727) -> Option<Highlight> {
728 let func = sema.resolve_method_call(&method_call)?; 728 let func = sema.resolve_method_call(&method_call)?;
729 let mut h = HighlightTag::Function.into(); 729 let mut h = HighlightTag::Method.into();
730 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 730 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
731 h |= HighlightModifier::Unsafe; 731 h |= HighlightModifier::Unsafe;
732 } 732 }
@@ -755,13 +755,18 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
755 Definition::ModuleDef(def) => match def { 755 Definition::ModuleDef(def) => match def {
756 hir::ModuleDef::Module(_) => HighlightTag::Module, 756 hir::ModuleDef::Module(_) => HighlightTag::Module,
757 hir::ModuleDef::Function(func) => { 757 hir::ModuleDef::Function(func) => {
758 let mut h = HighlightTag::Function.into(); 758 let mut h = if func.as_assoc_item(db).is_some() {
759 if func.self_param(db).is_none() {
760 Highlight::from(HighlightTag::Method) | HighlightModifier::Static
761 } else {
762 HighlightTag::Method.into()
763 }
764 } else {
765 HighlightTag::Function.into()
766 };
759 if func.is_unsafe(db) { 767 if func.is_unsafe(db) {
760 h |= HighlightModifier::Unsafe; 768 h |= HighlightModifier::Unsafe;
761 } 769 }
762 if func.as_assoc_item(db).is_some() && func.self_param(db).is_none() {
763 h |= HighlightModifier::Static;
764 }
765 return h; 770 return h;
766 } 771 }
767 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 772 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 65e0671a5..ffd9588b8 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -30,6 +30,7 @@ pub enum HighlightTag {
30 Keyword, 30 Keyword,
31 Lifetime, 31 Lifetime,
32 Macro, 32 Macro,
33 Method,
33 Module, 34 Module,
34 NumericLiteral, 35 NumericLiteral,
35 Punctuation, 36 Punctuation,
@@ -90,6 +91,7 @@ impl HighlightTag {
90 HighlightTag::Lifetime => "lifetime", 91 HighlightTag::Lifetime => "lifetime",
91 HighlightTag::Punctuation => "punctuation", 92 HighlightTag::Punctuation => "punctuation",
92 HighlightTag::Macro => "macro", 93 HighlightTag::Macro => "macro",
94 HighlightTag::Method => "method",
93 HighlightTag::Module => "module", 95 HighlightTag::Module => "module",
94 HighlightTag::NumericLiteral => "numeric_literal", 96 HighlightTag::NumericLiteral => "numeric_literal",
95 HighlightTag::Operator => "operator", 97 HighlightTag::Operator => "operator",
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 cd80d72b7..6fb606a47 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
@@ -40,17 +40,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span> 40<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span>
41 41
42<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span> 42<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span>
43 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 43 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration static">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">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> 44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration">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="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span> 47<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span>
48 <span class="keyword">fn</span> <span class="function declaration static">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 48 <span class="keyword">fn</span> <span class="method declaration static">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">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> 49 <span class="keyword">fn</span> <span class="method declaration">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="punctuation">}</span> 50<span class="punctuation">}</span>
51 51
52<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span> 52<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">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 53 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration static">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">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> 54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration">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="punctuation">}</span> 55<span class="punctuation">}</span>
56 </code></pre> \ No newline at end of file 56 </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 d79fa6dca..396fd46fb 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -53,7 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
53 <span class="comment documentation">/// #</span><span class="generic 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> 53 <span class="comment documentation">/// #</span><span class="generic 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>
54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic 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"> 54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic 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">
55</span> <span class="comment documentation">/// ```</span> 55</span> <span class="comment documentation">/// ```</span>
56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="method declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
57 <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> 57 <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>
58 <span class="punctuation">}</span> 58 <span class="punctuation">}</span>
59 59
@@ -87,7 +87,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
87 <span class="comment documentation">/// ```sh</span> 87 <span class="comment documentation">/// ```sh</span>
88 <span class="comment documentation">/// echo 1</span> 88 <span class="comment documentation">/// echo 1</span>
89 <span class="comment documentation">/// ```</span> 89 <span class="comment documentation">/// ```</span>
90 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">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> 90 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration">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>
91 <span class="bool_literal">true</span> 91 <span class="bool_literal">true</span>
92 <span class="punctuation">}</span> 92 <span class="punctuation">}</span>
93<span class="punctuation">}</span> 93<span class="punctuation">}</span>
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 57c178916..31daf2bd0 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -40,7 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
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="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
41 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> 41 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> 42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span>
43 <span class="keyword">fn</span> <span class="function declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 43 <span class="keyword">fn</span> <span class="method declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
44 <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> 44 <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="punctuation">}</span> 45 <span class="punctuation">}</span>
46 <span class="punctuation">}</span><span class="string_literal">"#</span> 46 <span class="punctuation">}</span><span class="string_literal">"#</span>
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 1d05b7713..67ec73f15 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
45<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span> 45<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span>
46 46
47<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span> 47<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span>
48 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration 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> 48 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="method declaration 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="punctuation">}</span> 49<span class="punctuation">}</span>
50 50
51<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span> 51<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span>
@@ -60,11 +60,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
60<span class="punctuation">}</span> 60<span class="punctuation">}</span>
61 61
62<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span> 62<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span>
63 <span class="keyword">fn</span> <span class="function declaration">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> 63 <span class="keyword">fn</span> <span class="method declaration">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="punctuation">}</span> 64<span class="punctuation">}</span>
65 65
66<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> 66<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">fn</span> <span class="function declaration">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> 67 <span class="keyword">fn</span> <span class="method declaration">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="punctuation">}</span> 68<span class="punctuation">}</span>
69 69
70<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 70<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
@@ -78,7 +78,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
78 <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> 78 <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="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> 79 <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="punctuation">}</span> 80 <span class="punctuation">}</span>
81 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 81 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="method unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
82 82
83 <span class="comment">// unsafe deref</span> 83 <span class="comment">// unsafe deref</span>
84 <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> 84 <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>
@@ -94,6 +94,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
94 <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> 94 <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 95
96 <span class="comment">// unsafe auto ref of packed field</span> 96 <span class="comment">// unsafe auto ref of packed field</span>
97 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 97 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="method unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
98 <span class="punctuation">}</span> 98 <span class="punctuation">}</span>
99<span class="punctuation">}</span></code></pre> \ No newline at end of file 99<span class="punctuation">}</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 15fbf2ce3..a3b4f20e8 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -62,25 +62,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62<span class="punctuation">}</span> 62<span class="punctuation">}</span>
63 63
64<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span> 64<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span>
65 <span class="keyword">fn</span> <span class="function declaration">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> 65 <span class="keyword">fn</span> <span class="method declaration">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>
66<span class="punctuation">}</span> 66<span class="punctuation">}</span>
67 67
68<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 68<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
69 <span class="keyword">fn</span> <span class="function declaration">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="method declaration">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>
70 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 70 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
71 <span class="punctuation">}</span> 71 <span class="punctuation">}</span>
72<span class="punctuation">}</span> 72<span class="punctuation">}</span>
73 73
74<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 74<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
75 <span class="keyword">fn</span> <span class="function declaration">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> 75 <span class="keyword">fn</span> <span class="method declaration">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>
76 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> 76 <span class="value_param">f</span><span class="operator">.</span><span class="method consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span>
77 <span class="punctuation">}</span> 77 <span class="punctuation">}</span>
78 78
79 <span class="keyword">fn</span> <span class="function declaration">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> 79 <span class="keyword">fn</span> <span class="method declaration">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>
80 <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> 80 <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>
81 <span class="punctuation">}</span> 81 <span class="punctuation">}</span>
82 82
83 <span class="keyword">fn</span> <span class="function declaration">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> 83 <span class="keyword">fn</span> <span class="method declaration">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>
84 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 84 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
85 <span class="punctuation">}</span> 85 <span class="punctuation">}</span>
86<span class="punctuation">}</span> 86<span class="punctuation">}</span>
@@ -91,15 +91,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
91<span class="punctuation">}</span> 91<span class="punctuation">}</span>
92 92
93<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> 93<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
94 <span class="keyword">fn</span> <span class="function declaration">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> 94 <span class="keyword">fn</span> <span class="method declaration">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>
95 <span class="value_param">f</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> 95 <span class="value_param">f</span><span class="operator">.</span><span class="method">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span>
96 <span class="punctuation">}</span> 96 <span class="punctuation">}</span>
97 97
98 <span class="keyword">fn</span> <span class="function declaration">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> 98 <span class="keyword">fn</span> <span class="method declaration">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>
99 <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> 99 <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>
100 <span class="punctuation">}</span> 100 <span class="punctuation">}</span>
101 101
102 <span class="keyword">fn</span> <span class="function declaration">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> 102 <span class="keyword">fn</span> <span class="method declaration">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>
103 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 103 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
104 <span class="punctuation">}</span> 104 <span class="punctuation">}</span>
105<span class="punctuation">}</span> 105<span class="punctuation">}</span>
@@ -175,17 +175,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
175 175
176 <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> 176 <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>
177 <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> 177 <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>
178 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 178 <span class="variable mutable">foo</span><span class="operator">.</span><span class="method">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
179 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 179 <span class="variable mutable">foo</span><span class="operator">.</span><span class="method mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
180 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> 180 <span class="variable mutable">foo</span><span class="operator">.</span><span class="method consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
181 181
182 <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> 182 <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>
183 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 183 <span class="variable mutable">copy</span><span class="operator">.</span><span class="method">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
184 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 184 <span class="variable mutable">copy</span><span class="operator">.</span><span class="method mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
185 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> 185 <span class="variable mutable">copy</span><span class="operator">.</span><span class="method">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span>
186 186
187 <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> 187 <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>
188 <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">baz</span><span class="punctuation">;</span> 188 <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="method">baz</span><span class="punctuation">;</span>
189 189
190 <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> 190 <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>
191 <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> 191 <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>
@@ -200,7 +200,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
200<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span> 200<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span>
201 201
202<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> 202<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>
203 <span class="keyword">fn</span> <span class="function declaration">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> 203 <span class="keyword">fn</span> <span class="method declaration">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>
204 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span> 204 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span>
205 <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> 205 <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>
206 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="punctuation">,</span> 206 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="punctuation">,</span>
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 83a602b9a..615fa7b0e 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -228,529 +228,4 @@ impl FnCallNode {
228} 228}
229 229
230#[cfg(test)] 230#[cfg(test)]
231mod tests { 231mod tests;
232 use crate::RootDatabase;
233 use base_db::{fixture::ChangeFixture, FilePosition};
234 use expect_test::{expect, Expect};
235 use test_utils::{mark, RangeOrOffset};
236
237 /// Creates analysis from a multi-file fixture, returns positions marked with <|>.
238 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
239 let change_fixture = ChangeFixture::parse(ra_fixture);
240 let mut database = RootDatabase::default();
241 database.apply_change(change_fixture.change);
242 let (file_id, range_or_offset) =
243 change_fixture.file_position.expect("expected a marker (<|>)");
244 let offset = match range_or_offset {
245 RangeOrOffset::Range(_) => panic!(),
246 RangeOrOffset::Offset(it) => it,
247 };
248 (database, FilePosition { file_id, offset })
249 }
250
251 fn check(ra_fixture: &str, expect: Expect) {
252 let (db, position) = position(ra_fixture);
253 let call_info = crate::call_info::call_info(&db, position);
254 let actual = match call_info {
255 Some(call_info) => {
256 let docs = match &call_info.doc {
257 None => "".to_string(),
258 Some(docs) => format!("{}\n------\n", docs.as_str()),
259 };
260 let params = call_info
261 .parameter_labels()
262 .enumerate()
263 .map(|(i, param)| {
264 if Some(i) == call_info.active_parameter {
265 format!("<{}>", param)
266 } else {
267 param.to_string()
268 }
269 })
270 .collect::<Vec<_>>()
271 .join(", ");
272 format!("{}{}\n({})\n", docs, call_info.signature, params)
273 }
274 None => String::new(),
275 };
276 expect.assert_eq(&actual);
277 }
278
279 #[test]
280 fn test_fn_signature_two_args() {
281 check(
282 r#"
283fn foo(x: u32, y: u32) -> u32 {x + y}
284fn bar() { foo(<|>3, ); }
285"#,
286 expect![[r#"
287 fn foo(x: u32, y: u32) -> u32
288 (<x: u32>, y: u32)
289 "#]],
290 );
291 check(
292 r#"
293fn foo(x: u32, y: u32) -> u32 {x + y}
294fn bar() { foo(3<|>, ); }
295"#,
296 expect![[r#"
297 fn foo(x: u32, y: u32) -> u32
298 (<x: u32>, y: u32)
299 "#]],
300 );
301 check(
302 r#"
303fn foo(x: u32, y: u32) -> u32 {x + y}
304fn bar() { foo(3,<|> ); }
305"#,
306 expect![[r#"
307 fn foo(x: u32, y: u32) -> u32
308 (x: u32, <y: u32>)
309 "#]],
310 );
311 check(
312 r#"
313fn foo(x: u32, y: u32) -> u32 {x + y}
314fn bar() { foo(3, <|>); }
315"#,
316 expect![[r#"
317 fn foo(x: u32, y: u32) -> u32
318 (x: u32, <y: u32>)
319 "#]],
320 );
321 }
322
323 #[test]
324 fn test_fn_signature_two_args_empty() {
325 check(
326 r#"
327fn foo(x: u32, y: u32) -> u32 {x + y}
328fn bar() { foo(<|>); }
329"#,
330 expect![[r#"
331 fn foo(x: u32, y: u32) -> u32
332 (<x: u32>, y: u32)
333 "#]],
334 );
335 }
336
337 #[test]
338 fn test_fn_signature_two_args_first_generics() {
339 check(
340 r#"
341fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
342 where T: Copy + Display, U: Debug
343{ x + y }
344
345fn bar() { foo(<|>3, ); }
346"#,
347 expect![[r#"
348 fn foo(x: i32, y: {unknown}) -> u32
349 (<x: i32>, y: {unknown})
350 "#]],
351 );
352 }
353
354 #[test]
355 fn test_fn_signature_no_params() {
356 check(
357 r#"
358fn foo<T>() -> T where T: Copy + Display {}
359fn bar() { foo(<|>); }
360"#,
361 expect![[r#"
362 fn foo() -> {unknown}
363 ()
364 "#]],
365 );
366 }
367
368 #[test]
369 fn test_fn_signature_for_impl() {
370 check(
371 r#"
372struct F;
373impl F { pub fn new() { } }
374fn bar() {
375 let _ : F = F::new(<|>);
376}
377"#,
378 expect![[r#"
379 fn new()
380 ()
381 "#]],
382 );
383 }
384
385 #[test]
386 fn test_fn_signature_for_method_self() {
387 check(
388 r#"
389struct S;
390impl S { pub fn do_it(&self) {} }
391
392fn bar() {
393 let s: S = S;
394 s.do_it(<|>);
395}
396"#,
397 expect![[r#"
398 fn do_it(&self)
399 ()
400 "#]],
401 );
402 }
403
404 #[test]
405 fn test_fn_signature_for_method_with_arg() {
406 check(
407 r#"
408struct S;
409impl S {
410 fn foo(&self, x: i32) {}
411}
412
413fn main() { S.foo(<|>); }
414"#,
415 expect![[r#"
416 fn foo(&self, x: i32)
417 (<x: i32>)
418 "#]],
419 );
420 }
421
422 #[test]
423 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
424 check(
425 r#"
426struct S;
427impl S {
428 fn foo(&self, x: i32) {}
429}
430
431fn main() { S::foo(<|>); }
432"#,
433 expect![[r#"
434 fn foo(self: &S, x: i32)
435 (<self: &S>, x: i32)
436 "#]],
437 );
438 }
439
440 #[test]
441 fn test_fn_signature_with_docs_simple() {
442 check(
443 r#"
444/// test
445// non-doc-comment
446fn foo(j: u32) -> u32 {
447 j
448}
449
450fn bar() {
451 let _ = foo(<|>);
452}
453"#,
454 expect![[r#"
455 test
456 ------
457 fn foo(j: u32) -> u32
458 (<j: u32>)
459 "#]],
460 );
461 }
462
463 #[test]
464 fn test_fn_signature_with_docs() {
465 check(
466 r#"
467/// Adds one to the number given.
468///
469/// # Examples
470///
471/// ```
472/// let five = 5;
473///
474/// assert_eq!(6, my_crate::add_one(5));
475/// ```
476pub fn add_one(x: i32) -> i32 {
477 x + 1
478}
479
480pub fn do() {
481 add_one(<|>
482}"#,
483 expect![[r##"
484 Adds one to the number given.
485
486 # Examples
487
488 ```
489 let five = 5;
490
491 assert_eq!(6, my_crate::add_one(5));
492 ```
493 ------
494 fn add_one(x: i32) -> i32
495 (<x: i32>)
496 "##]],
497 );
498 }
499
500 #[test]
501 fn test_fn_signature_with_docs_impl() {
502 check(
503 r#"
504struct addr;
505impl addr {
506 /// Adds one to the number given.
507 ///
508 /// # Examples
509 ///
510 /// ```
511 /// let five = 5;
512 ///
513 /// assert_eq!(6, my_crate::add_one(5));
514 /// ```
515 pub fn add_one(x: i32) -> i32 {
516 x + 1
517 }
518}
519
520pub fn do_it() {
521 addr {};
522 addr::add_one(<|>);
523}
524"#,
525 expect![[r##"
526 Adds one to the number given.
527
528 # Examples
529
530 ```
531 let five = 5;
532
533 assert_eq!(6, my_crate::add_one(5));
534 ```
535 ------
536 fn add_one(x: i32) -> i32
537 (<x: i32>)
538 "##]],
539 );
540 }
541
542 #[test]
543 fn test_fn_signature_with_docs_from_actix() {
544 check(
545 r#"
546struct WriteHandler<E>;
547
548impl<E> WriteHandler<E> {
549 /// Method is called when writer emits error.
550 ///
551 /// If this method returns `ErrorAction::Continue` writer processing
552 /// continues otherwise stream processing stops.
553 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
554 Running::Stop
555 }
556
557 /// Method is called when writer finishes.
558 ///
559 /// By default this method stops actor's `Context`.
560 fn finished(&mut self, ctx: &mut Self::Context) {
561 ctx.stop()
562 }
563}
564
565pub fn foo(mut r: WriteHandler<()>) {
566 r.finished(<|>);
567}
568"#,
569 expect![[r#"
570 Method is called when writer finishes.
571
572 By default this method stops actor's `Context`.
573 ------
574 fn finished(&mut self, ctx: &mut {unknown})
575 (<ctx: &mut {unknown}>)
576 "#]],
577 );
578 }
579
580 #[test]
581 fn call_info_bad_offset() {
582 mark::check!(call_info_bad_offset);
583 check(
584 r#"
585fn foo(x: u32, y: u32) -> u32 {x + y}
586fn bar() { foo <|> (3, ); }
587"#,
588 expect![[""]],
589 );
590 }
591
592 #[test]
593 fn test_nested_method_in_lambda() {
594 check(
595 r#"
596struct Foo;
597impl Foo { fn bar(&self, _: u32) { } }
598
599fn bar(_: u32) { }
600
601fn main() {
602 let foo = Foo;
603 std::thread::spawn(move || foo.bar(<|>));
604}
605"#,
606 expect![[r#"
607 fn bar(&self, _: u32)
608 (<_: u32>)
609 "#]],
610 );
611 }
612
613 #[test]
614 fn works_for_tuple_structs() {
615 check(
616 r#"
617/// A cool tuple struct
618struct S(u32, i32);
619fn main() {
620 let s = S(0, <|>);
621}
622"#,
623 expect![[r#"
624 A cool tuple struct
625 ------
626 struct S(u32, i32)
627 (u32, <i32>)
628 "#]],
629 );
630 }
631
632 #[test]
633 fn generic_struct() {
634 check(
635 r#"
636struct S<T>(T);
637fn main() {
638 let s = S(<|>);
639}
640"#,
641 expect![[r#"
642 struct S({unknown})
643 (<{unknown}>)
644 "#]],
645 );
646 }
647
648 #[test]
649 fn works_for_enum_variants() {
650 check(
651 r#"
652enum E {
653 /// A Variant
654 A(i32),
655 /// Another
656 B,
657 /// And C
658 C { a: i32, b: i32 }
659}
660
661fn main() {
662 let a = E::A(<|>);
663}
664"#,
665 expect![[r#"
666 A Variant
667 ------
668 enum E::A(i32)
669 (<i32>)
670 "#]],
671 );
672 }
673
674 #[test]
675 fn cant_call_struct_record() {
676 check(
677 r#"
678struct S { x: u32, y: i32 }
679fn main() {
680 let s = S(<|>);
681}
682"#,
683 expect![[""]],
684 );
685 }
686
687 #[test]
688 fn cant_call_enum_record() {
689 check(
690 r#"
691enum E {
692 /// A Variant
693 A(i32),
694 /// Another
695 B,
696 /// And C
697 C { a: i32, b: i32 }
698}
699
700fn main() {
701 let a = E::C(<|>);
702}
703"#,
704 expect![[""]],
705 );
706 }
707
708 #[test]
709 fn fn_signature_for_call_in_macro() {
710 check(
711 r#"
712macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
713fn foo() { }
714id! {
715 fn bar() { foo(<|>); }
716}
717"#,
718 expect![[r#"
719 fn foo()
720 ()
721 "#]],
722 );
723 }
724
725 #[test]
726 fn call_info_for_lambdas() {
727 check(
728 r#"
729struct S;
730fn foo(s: S) -> i32 { 92 }
731fn main() {
732 (|s| foo(s))(<|>)
733}
734 "#,
735 expect![[r#"
736 (S) -> i32
737 (<S>)
738 "#]],
739 )
740 }
741
742 #[test]
743 fn call_info_for_fn_ptr() {
744 check(
745 r#"
746fn main(f: fn(i32, f64) -> char) {
747 f(0, <|>)
748}
749 "#,
750 expect![[r#"
751 (i32, f64) -> char
752 (i32, <f64>)
753 "#]],
754 )
755 }
756}
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
new file mode 100644
index 000000000..9335aeaa5
--- /dev/null
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -0,0 +1,523 @@
1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect};
4use test_utils::{mark, RangeOrOffset};
5
6/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
8 let change_fixture = ChangeFixture::parse(ra_fixture);
9 let mut database = RootDatabase::default();
10 database.apply_change(change_fixture.change);
11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)");
12 let offset = match range_or_offset {
13 RangeOrOffset::Range(_) => panic!(),
14 RangeOrOffset::Offset(it) => it,
15 };
16 (database, FilePosition { file_id, offset })
17}
18
19fn check(ra_fixture: &str, expect: Expect) {
20 let (db, position) = position(ra_fixture);
21 let call_info = crate::call_info::call_info(&db, position);
22 let actual = match call_info {
23 Some(call_info) => {
24 let docs = match &call_info.doc {
25 None => "".to_string(),
26 Some(docs) => format!("{}\n------\n", docs.as_str()),
27 };
28 let params = call_info
29 .parameter_labels()
30 .enumerate()
31 .map(|(i, param)| {
32 if Some(i) == call_info.active_parameter {
33 format!("<{}>", param)
34 } else {
35 param.to_string()
36 }
37 })
38 .collect::<Vec<_>>()
39 .join(", ");
40 format!("{}{}\n({})\n", docs, call_info.signature, params)
41 }
42 None => String::new(),
43 };
44 expect.assert_eq(&actual);
45}
46
47#[test]
48fn test_fn_signature_two_args() {
49 check(
50 r#"
51fn foo(x: u32, y: u32) -> u32 {x + y}
52fn bar() { foo(<|>3, ); }
53"#,
54 expect![[r#"
55 fn foo(x: u32, y: u32) -> u32
56 (<x: u32>, y: u32)
57 "#]],
58 );
59 check(
60 r#"
61fn foo(x: u32, y: u32) -> u32 {x + y}
62fn bar() { foo(3<|>, ); }
63"#,
64 expect![[r#"
65 fn foo(x: u32, y: u32) -> u32
66 (<x: u32>, y: u32)
67 "#]],
68 );
69 check(
70 r#"
71fn foo(x: u32, y: u32) -> u32 {x + y}
72fn bar() { foo(3,<|> ); }
73"#,
74 expect![[r#"
75 fn foo(x: u32, y: u32) -> u32
76 (x: u32, <y: u32>)
77 "#]],
78 );
79 check(
80 r#"
81fn foo(x: u32, y: u32) -> u32 {x + y}
82fn bar() { foo(3, <|>); }
83"#,
84 expect![[r#"
85 fn foo(x: u32, y: u32) -> u32
86 (x: u32, <y: u32>)
87 "#]],
88 );
89}
90
91#[test]
92fn test_fn_signature_two_args_empty() {
93 check(
94 r#"
95fn foo(x: u32, y: u32) -> u32 {x + y}
96fn bar() { foo(<|>); }
97"#,
98 expect![[r#"
99 fn foo(x: u32, y: u32) -> u32
100 (<x: u32>, y: u32)
101 "#]],
102 );
103}
104
105#[test]
106fn test_fn_signature_two_args_first_generics() {
107 check(
108 r#"
109fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
110 where T: Copy + Display, U: Debug
111{ x + y }
112
113fn bar() { foo(<|>3, ); }
114"#,
115 expect![[r#"
116 fn foo(x: i32, y: {unknown}) -> u32
117 (<x: i32>, y: {unknown})
118 "#]],
119 );
120}
121
122#[test]
123fn test_fn_signature_no_params() {
124 check(
125 r#"
126fn foo<T>() -> T where T: Copy + Display {}
127fn bar() { foo(<|>); }
128"#,
129 expect![[r#"
130 fn foo() -> {unknown}
131 ()
132 "#]],
133 );
134}
135
136#[test]
137fn test_fn_signature_for_impl() {
138 check(
139 r#"
140struct F;
141impl F { pub fn new() { } }
142fn bar() {
143 let _ : F = F::new(<|>);
144}
145"#,
146 expect![[r#"
147 fn new()
148 ()
149 "#]],
150 );
151}
152
153#[test]
154fn test_fn_signature_for_method_self() {
155 check(
156 r#"
157struct S;
158impl S { pub fn do_it(&self) {} }
159
160fn bar() {
161 let s: S = S;
162 s.do_it(<|>);
163}
164"#,
165 expect![[r#"
166 fn do_it(&self)
167 ()
168 "#]],
169 );
170}
171
172#[test]
173fn test_fn_signature_for_method_with_arg() {
174 check(
175 r#"
176struct S;
177impl S {
178 fn foo(&self, x: i32) {}
179}
180
181fn main() { S.foo(<|>); }
182"#,
183 expect![[r#"
184 fn foo(&self, x: i32)
185 (<x: i32>)
186 "#]],
187 );
188}
189
190#[test]
191fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
192 check(
193 r#"
194struct S;
195impl S {
196 fn foo(&self, x: i32) {}
197}
198
199fn main() { S::foo(<|>); }
200"#,
201 expect![[r#"
202 fn foo(self: &S, x: i32)
203 (<self: &S>, x: i32)
204 "#]],
205 );
206}
207
208#[test]
209fn test_fn_signature_with_docs_simple() {
210 check(
211 r#"
212/// test
213// non-doc-comment
214fn foo(j: u32) -> u32 {
215 j
216}
217
218fn bar() {
219 let _ = foo(<|>);
220}
221"#,
222 expect![[r#"
223 test
224 ------
225 fn foo(j: u32) -> u32
226 (<j: u32>)
227 "#]],
228 );
229}
230
231#[test]
232fn test_fn_signature_with_docs() {
233 check(
234 r#"
235/// Adds one to the number given.
236///
237/// # Examples
238///
239/// ```
240/// let five = 5;
241///
242/// assert_eq!(6, my_crate::add_one(5));
243/// ```
244pub fn add_one(x: i32) -> i32 {
245 x + 1
246}
247
248pub fn do() {
249 add_one(<|>
250}"#,
251 expect![[r##"
252 Adds one to the number given.
253
254 # Examples
255
256 ```
257 let five = 5;
258
259 assert_eq!(6, my_crate::add_one(5));
260 ```
261 ------
262 fn add_one(x: i32) -> i32
263 (<x: i32>)
264 "##]],
265 );
266}
267
268#[test]
269fn test_fn_signature_with_docs_impl() {
270 check(
271 r#"
272struct addr;
273impl addr {
274 /// Adds one to the number given.
275 ///
276 /// # Examples
277 ///
278 /// ```
279 /// let five = 5;
280 ///
281 /// assert_eq!(6, my_crate::add_one(5));
282 /// ```
283 pub fn add_one(x: i32) -> i32 {
284 x + 1
285 }
286}
287
288pub fn do_it() {
289 addr {};
290 addr::add_one(<|>);
291}
292"#,
293 expect![[r##"
294 Adds one to the number given.
295
296 # Examples
297
298 ```
299 let five = 5;
300
301 assert_eq!(6, my_crate::add_one(5));
302 ```
303 ------
304 fn add_one(x: i32) -> i32
305 (<x: i32>)
306 "##]],
307 );
308}
309
310#[test]
311fn test_fn_signature_with_docs_from_actix() {
312 check(
313 r#"
314struct WriteHandler<E>;
315
316impl<E> WriteHandler<E> {
317 /// Method is called when writer emits error.
318 ///
319 /// If this method returns `ErrorAction::Continue` writer processing
320 /// continues otherwise stream processing stops.
321 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
322 Running::Stop
323 }
324
325 /// Method is called when writer finishes.
326 ///
327 /// By default this method stops actor's `Context`.
328 fn finished(&mut self, ctx: &mut Self::Context) {
329 ctx.stop()
330 }
331}
332
333pub fn foo(mut r: WriteHandler<()>) {
334 r.finished(<|>);
335}
336"#,
337 expect![[r#"
338 Method is called when writer finishes.
339
340 By default this method stops actor's `Context`.
341 ------
342 fn finished(&mut self, ctx: &mut {unknown})
343 (<ctx: &mut {unknown}>)
344 "#]],
345 );
346}
347
348#[test]
349fn call_info_bad_offset() {
350 mark::check!(call_info_bad_offset);
351 check(
352 r#"
353fn foo(x: u32, y: u32) -> u32 {x + y}
354fn bar() { foo <|> (3, ); }
355"#,
356 expect![[""]],
357 );
358}
359
360#[test]
361fn test_nested_method_in_lambda() {
362 check(
363 r#"
364struct Foo;
365impl Foo { fn bar(&self, _: u32) { } }
366
367fn bar(_: u32) { }
368
369fn main() {
370 let foo = Foo;
371 std::thread::spawn(move || foo.bar(<|>));
372}
373"#,
374 expect![[r#"
375 fn bar(&self, _: u32)
376 (<_: u32>)
377 "#]],
378 );
379}
380
381#[test]
382fn works_for_tuple_structs() {
383 check(
384 r#"
385/// A cool tuple struct
386struct S(u32, i32);
387fn main() {
388 let s = S(0, <|>);
389}
390"#,
391 expect![[r#"
392 A cool tuple struct
393 ------
394 struct S(u32, i32)
395 (u32, <i32>)
396 "#]],
397 );
398}
399
400#[test]
401fn generic_struct() {
402 check(
403 r#"
404struct S<T>(T);
405fn main() {
406 let s = S(<|>);
407}
408"#,
409 expect![[r#"
410 struct S({unknown})
411 (<{unknown}>)
412 "#]],
413 );
414}
415
416#[test]
417fn works_for_enum_variants() {
418 check(
419 r#"
420enum E {
421 /// A Variant
422 A(i32),
423 /// Another
424 B,
425 /// And C
426 C { a: i32, b: i32 }
427}
428
429fn main() {
430 let a = E::A(<|>);
431}
432"#,
433 expect![[r#"
434 A Variant
435 ------
436 enum E::A(i32)
437 (<i32>)
438 "#]],
439 );
440}
441
442#[test]
443fn cant_call_struct_record() {
444 check(
445 r#"
446struct S { x: u32, y: i32 }
447fn main() {
448 let s = S(<|>);
449}
450"#,
451 expect![[""]],
452 );
453}
454
455#[test]
456fn cant_call_enum_record() {
457 check(
458 r#"
459enum E {
460 /// A Variant
461 A(i32),
462 /// Another
463 B,
464 /// And C
465 C { a: i32, b: i32 }
466}
467
468fn main() {
469 let a = E::C(<|>);
470}
471"#,
472 expect![[""]],
473 );
474}
475
476#[test]
477fn fn_signature_for_call_in_macro() {
478 check(
479 r#"
480macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
481fn foo() { }
482id! {
483 fn bar() { foo(<|>); }
484}
485"#,
486 expect![[r#"
487 fn foo()
488 ()
489 "#]],
490 );
491}
492
493#[test]
494fn call_info_for_lambdas() {
495 check(
496 r#"
497struct S;
498fn foo(s: S) -> i32 { 92 }
499fn main() {
500 (|s| foo(s))(<|>)
501}
502 "#,
503 expect![[r#"
504 (S) -> i32
505 (<S>)
506 "#]],
507 )
508}
509
510#[test]
511fn call_info_for_fn_ptr() {
512 check(
513 r#"
514fn main(f: fn(i32, f64) -> char) {
515 f(0, <|>)
516}
517 "#,
518 expect![[r#"
519 (i32, f64) -> char
520 (i32, <f64>)
521 "#]],
522 )
523}
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 201a3d6fa..5d2cd30d1 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -258,7 +258,7 @@ impl NameRefClass {
258 sema: &Semantics<RootDatabase>, 258 sema: &Semantics<RootDatabase>,
259 name_ref: &ast::NameRef, 259 name_ref: &ast::NameRef,
260 ) -> Option<NameRefClass> { 260 ) -> Option<NameRefClass> {
261 let _p = profile::span("classify_name_ref"); 261 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
262 262
263 let parent = name_ref.syntax().parent()?; 263 let parent = name_ref.syntax().parent()?;
264 264
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 08d246c16..040843990 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -573,641 +573,4 @@ fn find_insert_position(
573} 573}
574 574
575#[cfg(test)] 575#[cfg(test)]
576mod tests { 576mod tests;
577 use super::*;
578
579 use test_utils::assert_eq_text;
580
581 #[test]
582 fn insert_existing() {
583 check_full("std::fs", "use std::fs;", "use std::fs;")
584 }
585
586 #[test]
587 fn insert_start() {
588 check_none(
589 "std::bar::AA",
590 r"
591use std::bar::B;
592use std::bar::D;
593use std::bar::F;
594use std::bar::G;",
595 r"
596use std::bar::AA;
597use std::bar::B;
598use std::bar::D;
599use std::bar::F;
600use std::bar::G;",
601 )
602 }
603
604 #[test]
605 fn insert_start_indent() {
606 mark::check!(insert_use_indent_after);
607 check_none(
608 "std::bar::AA",
609 r"
610 use std::bar::B;
611 use std::bar::D;",
612 r"
613 use std::bar::AA;
614 use std::bar::B;
615 use std::bar::D;",
616 )
617 }
618
619 #[test]
620 fn insert_middle() {
621 check_none(
622 "std::bar::EE",
623 r"
624use std::bar::A;
625use std::bar::D;
626use std::bar::F;
627use std::bar::G;",
628 r"
629use std::bar::A;
630use std::bar::D;
631use std::bar::EE;
632use std::bar::F;
633use std::bar::G;",
634 )
635 }
636
637 #[test]
638 fn insert_middle_indent() {
639 check_none(
640 "std::bar::EE",
641 r"
642 use std::bar::A;
643 use std::bar::D;
644 use std::bar::F;
645 use std::bar::G;",
646 r"
647 use std::bar::A;
648 use std::bar::D;
649 use std::bar::EE;
650 use std::bar::F;
651 use std::bar::G;",
652 )
653 }
654
655 #[test]
656 fn insert_end() {
657 check_none(
658 "std::bar::ZZ",
659 r"
660use std::bar::A;
661use std::bar::D;
662use std::bar::F;
663use std::bar::G;",
664 r"
665use std::bar::A;
666use std::bar::D;
667use std::bar::F;
668use std::bar::G;
669use std::bar::ZZ;",
670 )
671 }
672
673 #[test]
674 fn insert_end_indent() {
675 mark::check!(insert_use_indent_before);
676 check_none(
677 "std::bar::ZZ",
678 r"
679 use std::bar::A;
680 use std::bar::D;
681 use std::bar::F;
682 use std::bar::G;",
683 r"
684 use std::bar::A;
685 use std::bar::D;
686 use std::bar::F;
687 use std::bar::G;
688 use std::bar::ZZ;",
689 )
690 }
691
692 #[test]
693 fn insert_middle_nested() {
694 check_none(
695 "std::bar::EE",
696 r"
697use std::bar::A;
698use std::bar::{D, Z}; // example of weird imports due to user
699use std::bar::F;
700use std::bar::G;",
701 r"
702use std::bar::A;
703use std::bar::EE;
704use std::bar::{D, Z}; // example of weird imports due to user
705use std::bar::F;
706use std::bar::G;",
707 )
708 }
709
710 #[test]
711 fn insert_middle_groups() {
712 check_none(
713 "foo::bar::GG",
714 r"
715 use std::bar::A;
716 use std::bar::D;
717
718 use foo::bar::F;
719 use foo::bar::H;",
720 r"
721 use std::bar::A;
722 use std::bar::D;
723
724 use foo::bar::F;
725 use foo::bar::GG;
726 use foo::bar::H;",
727 )
728 }
729
730 #[test]
731 fn insert_first_matching_group() {
732 check_none(
733 "foo::bar::GG",
734 r"
735 use foo::bar::A;
736 use foo::bar::D;
737
738 use std;
739
740 use foo::bar::F;
741 use foo::bar::H;",
742 r"
743 use foo::bar::A;
744 use foo::bar::D;
745 use foo::bar::GG;
746
747 use std;
748
749 use foo::bar::F;
750 use foo::bar::H;",
751 )
752 }
753
754 #[test]
755 fn insert_missing_group_std() {
756 check_none(
757 "std::fmt",
758 r"
759 use foo::bar::A;
760 use foo::bar::D;",
761 r"
762 use std::fmt;
763
764 use foo::bar::A;
765 use foo::bar::D;",
766 )
767 }
768
769 #[test]
770 fn insert_missing_group_self() {
771 check_none(
772 "self::fmt",
773 r"
774use foo::bar::A;
775use foo::bar::D;",
776 r"
777use foo::bar::A;
778use foo::bar::D;
779
780use self::fmt;",
781 )
782 }
783
784 #[test]
785 fn insert_no_imports() {
786 check_full(
787 "foo::bar",
788 "fn main() {}",
789 r"use foo::bar;
790
791fn main() {}",
792 )
793 }
794
795 #[test]
796 fn insert_empty_file() {
797 // empty files will get two trailing newlines
798 // this is due to the test case insert_no_imports above
799 check_full(
800 "foo::bar",
801 "",
802 r"use foo::bar;
803
804",
805 )
806 }
807
808 #[test]
809 fn insert_empty_module() {
810 mark::check!(insert_use_no_indent_after);
811 check(
812 "foo::bar",
813 "mod x {}",
814 r"{
815 use foo::bar;
816}",
817 None,
818 true,
819 )
820 }
821
822 #[test]
823 fn insert_after_inner_attr() {
824 check_full(
825 "foo::bar",
826 r"#![allow(unused_imports)]",
827 r"#![allow(unused_imports)]
828
829use foo::bar;",
830 )
831 }
832
833 #[test]
834 fn insert_after_inner_attr2() {
835 check_full(
836 "foo::bar",
837 r"#![allow(unused_imports)]
838
839#![no_std]
840fn main() {}",
841 r"#![allow(unused_imports)]
842
843#![no_std]
844
845use foo::bar;
846fn main() {}",
847 );
848 }
849
850 #[test]
851 fn inserts_after_single_line_inner_comments() {
852 check_none(
853 "foo::bar::Baz",
854 "//! Single line inner comments do not allow any code before them.",
855 r#"//! Single line inner comments do not allow any code before them.
856
857use foo::bar::Baz;"#,
858 );
859 }
860
861 #[test]
862 fn inserts_after_multiline_inner_comments() {
863 check_none(
864 "foo::bar::Baz",
865 r#"/*! Multiline inner comments do not allow any code before them. */
866
867/*! Still an inner comment, cannot place any code before. */
868fn main() {}"#,
869 r#"/*! Multiline inner comments do not allow any code before them. */
870
871/*! Still an inner comment, cannot place any code before. */
872
873use foo::bar::Baz;
874fn main() {}"#,
875 )
876 }
877
878 #[test]
879 fn inserts_after_all_inner_items() {
880 check_none(
881 "foo::bar::Baz",
882 r#"#![allow(unused_imports)]
883/*! Multiline line comment 2 */
884
885
886//! Single line comment 1
887#![no_std]
888//! Single line comment 2
889fn main() {}"#,
890 r#"#![allow(unused_imports)]
891/*! Multiline line comment 2 */
892
893
894//! Single line comment 1
895#![no_std]
896//! Single line comment 2
897
898use foo::bar::Baz;
899fn main() {}"#,
900 )
901 }
902
903 #[test]
904 fn merge_groups() {
905 check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
906 }
907
908 #[test]
909 fn merge_groups_last() {
910 check_last(
911 "std::io",
912 r"use std::fmt::{Result, Display};",
913 r"use std::fmt::{Result, Display};
914use std::io;",
915 )
916 }
917
918 #[test]
919 fn merge_last_into_self() {
920 check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
921 }
922
923 #[test]
924 fn merge_groups_full() {
925 check_full(
926 "std::io",
927 r"use std::fmt::{Result, Display};",
928 r"use std::{fmt::{Result, Display}, io};",
929 )
930 }
931
932 #[test]
933 fn merge_groups_long_full() {
934 check_full(
935 "std::foo::bar::Baz",
936 r"use std::foo::bar::Qux;",
937 r"use std::foo::bar::{Baz, Qux};",
938 )
939 }
940
941 #[test]
942 fn merge_groups_long_last() {
943 check_last(
944 "std::foo::bar::Baz",
945 r"use std::foo::bar::Qux;",
946 r"use std::foo::bar::{Baz, Qux};",
947 )
948 }
949
950 #[test]
951 fn merge_groups_long_full_list() {
952 check_full(
953 "std::foo::bar::Baz",
954 r"use std::foo::bar::{Qux, Quux};",
955 r"use std::foo::bar::{Baz, Quux, Qux};",
956 )
957 }
958
959 #[test]
960 fn merge_groups_long_last_list() {
961 check_last(
962 "std::foo::bar::Baz",
963 r"use std::foo::bar::{Qux, Quux};",
964 r"use std::foo::bar::{Baz, Quux, Qux};",
965 )
966 }
967
968 #[test]
969 fn merge_groups_long_full_nested() {
970 check_full(
971 "std::foo::bar::Baz",
972 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
973 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
974 )
975 }
976
977 #[test]
978 fn merge_groups_long_last_nested() {
979 check_last(
980 "std::foo::bar::Baz",
981 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
982 r"use std::foo::bar::Baz;
983use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
984 )
985 }
986
987 #[test]
988 fn merge_groups_full_nested_deep() {
989 check_full(
990 "std::foo::bar::quux::Baz",
991 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
992 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
993 )
994 }
995
996 #[test]
997 fn merge_groups_full_nested_long() {
998 check_full(
999 "std::foo::bar::Baz",
1000 r"use std::{foo::bar::Qux};",
1001 r"use std::{foo::bar::{Baz, Qux}};",
1002 );
1003 }
1004
1005 #[test]
1006 fn merge_groups_last_nested_long() {
1007 check_full(
1008 "std::foo::bar::Baz",
1009 r"use std::{foo::bar::Qux};",
1010 r"use std::{foo::bar::{Baz, Qux}};",
1011 );
1012 }
1013
1014 #[test]
1015 fn merge_groups_skip_pub() {
1016 check_full(
1017 "std::io",
1018 r"pub use std::fmt::{Result, Display};",
1019 r"pub use std::fmt::{Result, Display};
1020use std::io;",
1021 )
1022 }
1023
1024 #[test]
1025 fn merge_groups_skip_pub_crate() {
1026 check_full(
1027 "std::io",
1028 r"pub(crate) use std::fmt::{Result, Display};",
1029 r"pub(crate) use std::fmt::{Result, Display};
1030use std::io;",
1031 )
1032 }
1033
1034 #[test]
1035 #[ignore] // FIXME: Support this
1036 fn split_out_merge() {
1037 check_last(
1038 "std::fmt::Result",
1039 r"use std::{fmt, io};",
1040 r"use std::fmt::{self, Result};
1041use std::io;",
1042 )
1043 }
1044
1045 #[test]
1046 fn merge_into_module_import() {
1047 check_full(
1048 "std::fmt::Result",
1049 r"use std::{fmt, io};",
1050 r"use std::{fmt::{self, Result}, io};",
1051 )
1052 }
1053
1054 #[test]
1055 fn merge_groups_self() {
1056 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
1057 }
1058
1059 #[test]
1060 fn merge_mod_into_glob() {
1061 check_full(
1062 "token::TokenKind",
1063 r"use token::TokenKind::*;",
1064 r"use token::TokenKind::{*, self};",
1065 )
1066 // FIXME: have it emit `use token::TokenKind::{self, *}`?
1067 }
1068
1069 #[test]
1070 fn merge_self_glob() {
1071 check_full("self", r"use self::*;", r"use self::{*, self};")
1072 // FIXME: have it emit `use {self, *}`?
1073 }
1074
1075 #[test]
1076 fn merge_glob_nested() {
1077 check_full(
1078 "foo::bar::quux::Fez",
1079 r"use foo::bar::{Baz, quux::*};",
1080 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
1081 )
1082 }
1083
1084 #[test]
1085 fn merge_nested_considers_first_segments() {
1086 check_full(
1087 "hir_ty::display::write_bounds_like_dyn_trait",
1088 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
1089 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
1090 );
1091 }
1092
1093 #[test]
1094 fn skip_merge_last_too_long() {
1095 check_last(
1096 "foo::bar",
1097 r"use foo::bar::baz::Qux;",
1098 r"use foo::bar;
1099use foo::bar::baz::Qux;",
1100 );
1101 }
1102
1103 #[test]
1104 fn skip_merge_last_too_long2() {
1105 check_last(
1106 "foo::bar::baz::Qux",
1107 r"use foo::bar;",
1108 r"use foo::bar;
1109use foo::bar::baz::Qux;",
1110 );
1111 }
1112
1113 #[test]
1114 fn insert_short_before_long() {
1115 check_none(
1116 "foo::bar",
1117 r"use foo::bar::baz::Qux;",
1118 r"use foo::bar;
1119use foo::bar::baz::Qux;",
1120 );
1121 }
1122
1123 #[test]
1124 fn merge_last_fail() {
1125 check_merge_only_fail(
1126 r"use foo::bar::{baz::{Qux, Fez}};",
1127 r"use foo::bar::{baaz::{Quux, Feez}};",
1128 MergeBehaviour::Last,
1129 );
1130 }
1131
1132 #[test]
1133 fn merge_last_fail1() {
1134 check_merge_only_fail(
1135 r"use foo::bar::{baz::{Qux, Fez}};",
1136 r"use foo::bar::baaz::{Quux, Feez};",
1137 MergeBehaviour::Last,
1138 );
1139 }
1140
1141 #[test]
1142 fn merge_last_fail2() {
1143 check_merge_only_fail(
1144 r"use foo::bar::baz::{Qux, Fez};",
1145 r"use foo::bar::{baaz::{Quux, Feez}};",
1146 MergeBehaviour::Last,
1147 );
1148 }
1149
1150 #[test]
1151 fn merge_last_fail3() {
1152 check_merge_only_fail(
1153 r"use foo::bar::baz::{Qux, Fez};",
1154 r"use foo::bar::baaz::{Quux, Feez};",
1155 MergeBehaviour::Last,
1156 );
1157 }
1158
1159 fn check(
1160 path: &str,
1161 ra_fixture_before: &str,
1162 ra_fixture_after: &str,
1163 mb: Option<MergeBehaviour>,
1164 module: bool,
1165 ) {
1166 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
1167 if module {
1168 syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone();
1169 }
1170 let file = super::ImportScope::from(syntax).unwrap();
1171 let path = ast::SourceFile::parse(&format!("use {};", path))
1172 .tree()
1173 .syntax()
1174 .descendants()
1175 .find_map(ast::Path::cast)
1176 .unwrap();
1177
1178 let rewriter = insert_use(&file, path, mb);
1179 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
1180 assert_eq_text!(&result, ra_fixture_after);
1181 }
1182
1183 fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
1184 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false)
1185 }
1186
1187 fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
1188 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false)
1189 }
1190
1191 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
1192 check(path, ra_fixture_before, ra_fixture_after, None, false)
1193 }
1194
1195 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
1196 let use0 = ast::SourceFile::parse(ra_fixture0)
1197 .tree()
1198 .syntax()
1199 .descendants()
1200 .find_map(ast::Use::cast)
1201 .unwrap();
1202
1203 let use1 = ast::SourceFile::parse(ra_fixture1)
1204 .tree()
1205 .syntax()
1206 .descendants()
1207 .find_map(ast::Use::cast)
1208 .unwrap();
1209
1210 let result = try_merge_imports(&use0, &use1, mb);
1211 assert_eq!(result.map(|u| u.to_string()), None);
1212 }
1213}
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
new file mode 100644
index 000000000..86bfa5b41
--- /dev/null
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -0,0 +1,620 @@
1use super::*;
2
3use test_utils::assert_eq_text;
4
5#[test]
6fn insert_existing() {
7 check_full("std::fs", "use std::fs;", "use std::fs;")
8}
9
10#[test]
11fn insert_start() {
12 check_none(
13 "std::bar::AA",
14 r"
15use std::bar::B;
16use std::bar::D;
17use std::bar::F;
18use std::bar::G;",
19 r"
20use std::bar::AA;
21use std::bar::B;
22use std::bar::D;
23use std::bar::F;
24use std::bar::G;",
25 )
26}
27
28#[test]
29fn insert_start_indent() {
30 mark::check!(insert_use_indent_after);
31 check_none(
32 "std::bar::AA",
33 r"
34 use std::bar::B;
35 use std::bar::D;",
36 r"
37 use std::bar::AA;
38 use std::bar::B;
39 use std::bar::D;",
40 )
41}
42
43#[test]
44fn insert_middle() {
45 check_none(
46 "std::bar::EE",
47 r"
48use std::bar::A;
49use std::bar::D;
50use std::bar::F;
51use std::bar::G;",
52 r"
53use std::bar::A;
54use std::bar::D;
55use std::bar::EE;
56use std::bar::F;
57use std::bar::G;",
58 )
59}
60
61#[test]
62fn insert_middle_indent() {
63 check_none(
64 "std::bar::EE",
65 r"
66 use std::bar::A;
67 use std::bar::D;
68 use std::bar::F;
69 use std::bar::G;",
70 r"
71 use std::bar::A;
72 use std::bar::D;
73 use std::bar::EE;
74 use std::bar::F;
75 use std::bar::G;",
76 )
77}
78
79#[test]
80fn insert_end() {
81 check_none(
82 "std::bar::ZZ",
83 r"
84use std::bar::A;
85use std::bar::D;
86use std::bar::F;
87use std::bar::G;",
88 r"
89use std::bar::A;
90use std::bar::D;
91use std::bar::F;
92use std::bar::G;
93use std::bar::ZZ;",
94 )
95}
96
97#[test]
98fn insert_end_indent() {
99 mark::check!(insert_use_indent_before);
100 check_none(
101 "std::bar::ZZ",
102 r"
103 use std::bar::A;
104 use std::bar::D;
105 use std::bar::F;
106 use std::bar::G;",
107 r"
108 use std::bar::A;
109 use std::bar::D;
110 use std::bar::F;
111 use std::bar::G;
112 use std::bar::ZZ;",
113 )
114}
115
116#[test]
117fn insert_middle_nested() {
118 check_none(
119 "std::bar::EE",
120 r"
121use std::bar::A;
122use std::bar::{D, Z}; // example of weird imports due to user
123use std::bar::F;
124use std::bar::G;",
125 r"
126use std::bar::A;
127use std::bar::EE;
128use std::bar::{D, Z}; // example of weird imports due to user
129use std::bar::F;
130use std::bar::G;",
131 )
132}
133
134#[test]
135fn insert_middle_groups() {
136 check_none(
137 "foo::bar::GG",
138 r"
139 use std::bar::A;
140 use std::bar::D;
141
142 use foo::bar::F;
143 use foo::bar::H;",
144 r"
145 use std::bar::A;
146 use std::bar::D;
147
148 use foo::bar::F;
149 use foo::bar::GG;
150 use foo::bar::H;",
151 )
152}
153
154#[test]
155fn insert_first_matching_group() {
156 check_none(
157 "foo::bar::GG",
158 r"
159 use foo::bar::A;
160 use foo::bar::D;
161
162 use std;
163
164 use foo::bar::F;
165 use foo::bar::H;",
166 r"
167 use foo::bar::A;
168 use foo::bar::D;
169 use foo::bar::GG;
170
171 use std;
172
173 use foo::bar::F;
174 use foo::bar::H;",
175 )
176}
177
178#[test]
179fn insert_missing_group_std() {
180 check_none(
181 "std::fmt",
182 r"
183 use foo::bar::A;
184 use foo::bar::D;",
185 r"
186 use std::fmt;
187
188 use foo::bar::A;
189 use foo::bar::D;",
190 )
191}
192
193#[test]
194fn insert_missing_group_self() {
195 check_none(
196 "self::fmt",
197 r"
198use foo::bar::A;
199use foo::bar::D;",
200 r"
201use foo::bar::A;
202use foo::bar::D;
203
204use self::fmt;",
205 )
206}
207
208#[test]
209fn insert_no_imports() {
210 check_full(
211 "foo::bar",
212 "fn main() {}",
213 r"use foo::bar;
214
215fn main() {}",
216 )
217}
218
219#[test]
220fn insert_empty_file() {
221 // empty files will get two trailing newlines
222 // this is due to the test case insert_no_imports above
223 check_full(
224 "foo::bar",
225 "",
226 r"use foo::bar;
227
228",
229 )
230}
231
232#[test]
233fn insert_empty_module() {
234 mark::check!(insert_use_no_indent_after);
235 check(
236 "foo::bar",
237 "mod x {}",
238 r"{
239 use foo::bar;
240}",
241 None,
242 true,
243 )
244}
245
246#[test]
247fn insert_after_inner_attr() {
248 check_full(
249 "foo::bar",
250 r"#![allow(unused_imports)]",
251 r"#![allow(unused_imports)]
252
253use foo::bar;",
254 )
255}
256
257#[test]
258fn insert_after_inner_attr2() {
259 check_full(
260 "foo::bar",
261 r"#![allow(unused_imports)]
262
263#![no_std]
264fn main() {}",
265 r"#![allow(unused_imports)]
266
267#![no_std]
268
269use foo::bar;
270fn main() {}",
271 );
272}
273
274#[test]
275fn inserts_after_single_line_inner_comments() {
276 check_none(
277 "foo::bar::Baz",
278 "//! Single line inner comments do not allow any code before them.",
279 r#"//! Single line inner comments do not allow any code before them.
280
281use foo::bar::Baz;"#,
282 );
283}
284
285#[test]
286fn inserts_after_multiline_inner_comments() {
287 check_none(
288 "foo::bar::Baz",
289 r#"/*! Multiline inner comments do not allow any code before them. */
290
291/*! Still an inner comment, cannot place any code before. */
292fn main() {}"#,
293 r#"/*! Multiline inner comments do not allow any code before them. */
294
295/*! Still an inner comment, cannot place any code before. */
296
297use foo::bar::Baz;
298fn main() {}"#,
299 )
300}
301
302#[test]
303fn inserts_after_all_inner_items() {
304 check_none(
305 "foo::bar::Baz",
306 r#"#![allow(unused_imports)]
307/*! Multiline line comment 2 */
308
309
310//! Single line comment 1
311#![no_std]
312//! Single line comment 2
313fn main() {}"#,
314 r#"#![allow(unused_imports)]
315/*! Multiline line comment 2 */
316
317
318//! Single line comment 1
319#![no_std]
320//! Single line comment 2
321
322use foo::bar::Baz;
323fn main() {}"#,
324 )
325}
326
327#[test]
328fn merge_groups() {
329 check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
330}
331
332#[test]
333fn merge_groups_last() {
334 check_last(
335 "std::io",
336 r"use std::fmt::{Result, Display};",
337 r"use std::fmt::{Result, Display};
338use std::io;",
339 )
340}
341
342#[test]
343fn merge_last_into_self() {
344 check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
345}
346
347#[test]
348fn merge_groups_full() {
349 check_full(
350 "std::io",
351 r"use std::fmt::{Result, Display};",
352 r"use std::{fmt::{Result, Display}, io};",
353 )
354}
355
356#[test]
357fn merge_groups_long_full() {
358 check_full("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
359}
360
361#[test]
362fn merge_groups_long_last() {
363 check_last("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
364}
365
366#[test]
367fn merge_groups_long_full_list() {
368 check_full(
369 "std::foo::bar::Baz",
370 r"use std::foo::bar::{Qux, Quux};",
371 r"use std::foo::bar::{Baz, Quux, Qux};",
372 )
373}
374
375#[test]
376fn merge_groups_long_last_list() {
377 check_last(
378 "std::foo::bar::Baz",
379 r"use std::foo::bar::{Qux, Quux};",
380 r"use std::foo::bar::{Baz, Quux, Qux};",
381 )
382}
383
384#[test]
385fn merge_groups_long_full_nested() {
386 check_full(
387 "std::foo::bar::Baz",
388 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
389 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
390 )
391}
392
393#[test]
394fn merge_groups_long_last_nested() {
395 check_last(
396 "std::foo::bar::Baz",
397 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
398 r"use std::foo::bar::Baz;
399use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
400 )
401}
402
403#[test]
404fn merge_groups_full_nested_deep() {
405 check_full(
406 "std::foo::bar::quux::Baz",
407 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
408 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
409 )
410}
411
412#[test]
413fn merge_groups_full_nested_long() {
414 check_full(
415 "std::foo::bar::Baz",
416 r"use std::{foo::bar::Qux};",
417 r"use std::{foo::bar::{Baz, Qux}};",
418 );
419}
420
421#[test]
422fn merge_groups_last_nested_long() {
423 check_full(
424 "std::foo::bar::Baz",
425 r"use std::{foo::bar::Qux};",
426 r"use std::{foo::bar::{Baz, Qux}};",
427 );
428}
429
430#[test]
431fn merge_groups_skip_pub() {
432 check_full(
433 "std::io",
434 r"pub use std::fmt::{Result, Display};",
435 r"pub use std::fmt::{Result, Display};
436use std::io;",
437 )
438}
439
440#[test]
441fn merge_groups_skip_pub_crate() {
442 check_full(
443 "std::io",
444 r"pub(crate) use std::fmt::{Result, Display};",
445 r"pub(crate) use std::fmt::{Result, Display};
446use std::io;",
447 )
448}
449
450#[test]
451#[ignore] // FIXME: Support this
452fn split_out_merge() {
453 check_last(
454 "std::fmt::Result",
455 r"use std::{fmt, io};",
456 r"use std::fmt::{self, Result};
457use std::io;",
458 )
459}
460
461#[test]
462fn merge_into_module_import() {
463 check_full("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};")
464}
465
466#[test]
467fn merge_groups_self() {
468 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
469}
470
471#[test]
472fn merge_mod_into_glob() {
473 check_full("token::TokenKind", r"use token::TokenKind::*;", r"use token::TokenKind::{*, self};")
474 // FIXME: have it emit `use token::TokenKind::{self, *}`?
475}
476
477#[test]
478fn merge_self_glob() {
479 check_full("self", r"use self::*;", r"use self::{*, self};")
480 // FIXME: have it emit `use {self, *}`?
481}
482
483#[test]
484fn merge_glob_nested() {
485 check_full(
486 "foo::bar::quux::Fez",
487 r"use foo::bar::{Baz, quux::*};",
488 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
489 )
490}
491
492#[test]
493fn merge_nested_considers_first_segments() {
494 check_full(
495 "hir_ty::display::write_bounds_like_dyn_trait",
496 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
497 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
498 );
499}
500
501#[test]
502fn skip_merge_last_too_long() {
503 check_last(
504 "foo::bar",
505 r"use foo::bar::baz::Qux;",
506 r"use foo::bar;
507use foo::bar::baz::Qux;",
508 );
509}
510
511#[test]
512fn skip_merge_last_too_long2() {
513 check_last(
514 "foo::bar::baz::Qux",
515 r"use foo::bar;",
516 r"use foo::bar;
517use foo::bar::baz::Qux;",
518 );
519}
520
521#[test]
522fn insert_short_before_long() {
523 check_none(
524 "foo::bar",
525 r"use foo::bar::baz::Qux;",
526 r"use foo::bar;
527use foo::bar::baz::Qux;",
528 );
529}
530
531#[test]
532fn merge_last_fail() {
533 check_merge_only_fail(
534 r"use foo::bar::{baz::{Qux, Fez}};",
535 r"use foo::bar::{baaz::{Quux, Feez}};",
536 MergeBehaviour::Last,
537 );
538}
539
540#[test]
541fn merge_last_fail1() {
542 check_merge_only_fail(
543 r"use foo::bar::{baz::{Qux, Fez}};",
544 r"use foo::bar::baaz::{Quux, Feez};",
545 MergeBehaviour::Last,
546 );
547}
548
549#[test]
550fn merge_last_fail2() {
551 check_merge_only_fail(
552 r"use foo::bar::baz::{Qux, Fez};",
553 r"use foo::bar::{baaz::{Quux, Feez}};",
554 MergeBehaviour::Last,
555 );
556}
557
558#[test]
559fn merge_last_fail3() {
560 check_merge_only_fail(
561 r"use foo::bar::baz::{Qux, Fez};",
562 r"use foo::bar::baaz::{Quux, Feez};",
563 MergeBehaviour::Last,
564 );
565}
566
567fn check(
568 path: &str,
569 ra_fixture_before: &str,
570 ra_fixture_after: &str,
571 mb: Option<MergeBehaviour>,
572 module: bool,
573) {
574 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
575 if module {
576 syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone();
577 }
578 let file = super::ImportScope::from(syntax).unwrap();
579 let path = ast::SourceFile::parse(&format!("use {};", path))
580 .tree()
581 .syntax()
582 .descendants()
583 .find_map(ast::Path::cast)
584 .unwrap();
585
586 let rewriter = insert_use(&file, path, mb);
587 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
588 assert_eq_text!(&result, ra_fixture_after);
589}
590
591fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
592 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false)
593}
594
595fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
596 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false)
597}
598
599fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
600 check(path, ra_fixture_before, ra_fixture_after, None, false)
601}
602
603fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
604 let use0 = ast::SourceFile::parse(ra_fixture0)
605 .tree()
606 .syntax()
607 .descendants()
608 .find_map(ast::Use::cast)
609 .unwrap();
610
611 let use1 = ast::SourceFile::parse(ra_fixture1)
612 .tree()
613 .syntax()
614 .descendants()
615 .find_map(ast::Use::cast)
616 .unwrap();
617
618 let result = try_merge_imports(&use0, &use1, mb);
619 assert_eq!(result.map(|u| u.to_string()), None);
620}
diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs
index a381f7fb8..41226305e 100644
--- a/crates/ide_db/src/line_index.rs
+++ b/crates/ide_db/src/line_index.rs
@@ -149,133 +149,4 @@ impl LineIndex {
149} 149}
150 150
151#[cfg(test)] 151#[cfg(test)]
152mod tests { 152mod tests;
153 use super::*;
154
155 #[test]
156 fn test_line_index() {
157 let text = "hello\nworld";
158 let index = LineIndex::new(text);
159 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
160 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
161 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
162 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
163 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
164 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
165 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
166 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
167 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
168
169 let text = "\nhello\nworld";
170 let index = LineIndex::new(text);
171 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
172 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
173 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
174 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
175 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
176 }
177
178 #[test]
179 fn test_char_len() {
180 assert_eq!('メ'.len_utf8(), 3);
181 assert_eq!('メ'.len_utf16(), 1);
182 }
183
184 #[test]
185 fn test_empty_index() {
186 let col_index = LineIndex::new(
187 "
188const C: char = 'x';
189",
190 );
191 assert_eq!(col_index.utf16_lines.len(), 0);
192 }
193
194 #[test]
195 fn test_single_char() {
196 let col_index = LineIndex::new(
197 "
198const C: char = 'メ';
199",
200 );
201
202 assert_eq!(col_index.utf16_lines.len(), 1);
203 assert_eq!(col_index.utf16_lines[&1].len(), 1);
204 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
205
206 // UTF-8 to UTF-16, no changes
207 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
208
209 // UTF-8 to UTF-16
210 assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
211
212 // UTF-16 to UTF-8, no changes
213 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
214
215 // UTF-16 to UTF-8
216 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21));
217
218 let col_index = LineIndex::new("a𐐏b");
219 assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5));
220 }
221
222 #[test]
223 fn test_string() {
224 let col_index = LineIndex::new(
225 "
226const C: char = \"メ メ\";
227",
228 );
229
230 assert_eq!(col_index.utf16_lines.len(), 1);
231 assert_eq!(col_index.utf16_lines[&1].len(), 2);
232 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
233 assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
234
235 // UTF-8 to UTF-16
236 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
237
238 assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
239 assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
240
241 assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
242
243 // UTF-16 to UTF-8
244 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
245
246 // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1
247 assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20
248 assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space
249 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24
250
251 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15));
252 }
253
254 #[test]
255 fn test_splitlines() {
256 fn r(lo: u32, hi: u32) -> TextRange {
257 TextRange::new(lo.into(), hi.into())
258 }
259
260 let text = "a\nbb\nccc\n";
261 let line_index = LineIndex::new(text);
262
263 let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
264 let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
265 assert_eq!(actual, expected);
266
267 let text = "";
268 let line_index = LineIndex::new(text);
269
270 let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
271 let expected = vec![];
272 assert_eq!(actual, expected);
273
274 let text = "\n";
275 let line_index = LineIndex::new(text);
276
277 let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
278 let expected = vec![r(0, 1)];
279 assert_eq!(actual, expected)
280 }
281}
diff --git a/crates/ide_db/src/line_index/tests.rs b/crates/ide_db/src/line_index/tests.rs
new file mode 100644
index 000000000..05f7484e8
--- /dev/null
+++ b/crates/ide_db/src/line_index/tests.rs
@@ -0,0 +1,128 @@
1use super::*;
2
3#[test]
4fn test_line_index() {
5 let text = "hello\nworld";
6 let index = LineIndex::new(text);
7 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
8 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
9 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
10 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
11 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
12 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
13 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
14 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
15 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
16
17 let text = "\nhello\nworld";
18 let index = LineIndex::new(text);
19 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
20 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
21 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
22 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
23 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
24}
25
26#[test]
27fn test_char_len() {
28 assert_eq!('メ'.len_utf8(), 3);
29 assert_eq!('メ'.len_utf16(), 1);
30}
31
32#[test]
33fn test_empty_index() {
34 let col_index = LineIndex::new(
35 "
36const C: char = 'x';
37",
38 );
39 assert_eq!(col_index.utf16_lines.len(), 0);
40}
41
42#[test]
43fn test_single_char() {
44 let col_index = LineIndex::new(
45 "
46const C: char = 'メ';
47",
48 );
49
50 assert_eq!(col_index.utf16_lines.len(), 1);
51 assert_eq!(col_index.utf16_lines[&1].len(), 1);
52 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
53
54 // UTF-8 to UTF-16, no changes
55 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
56
57 // UTF-8 to UTF-16
58 assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
59
60 // UTF-16 to UTF-8, no changes
61 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
62
63 // UTF-16 to UTF-8
64 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21));
65
66 let col_index = LineIndex::new("a𐐏b");
67 assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5));
68}
69
70#[test]
71fn test_string() {
72 let col_index = LineIndex::new(
73 "
74const C: char = \"メ メ\";
75",
76 );
77
78 assert_eq!(col_index.utf16_lines.len(), 1);
79 assert_eq!(col_index.utf16_lines[&1].len(), 2);
80 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
81 assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
82
83 // UTF-8 to UTF-16
84 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
85
86 assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
87 assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
88
89 assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
90
91 // UTF-16 to UTF-8
92 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
93
94 // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1
95 assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20
96 assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space
97 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24
98
99 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15));
100}
101
102#[test]
103fn test_splitlines() {
104 fn r(lo: u32, hi: u32) -> TextRange {
105 TextRange::new(lo.into(), hi.into())
106 }
107
108 let text = "a\nbb\nccc\n";
109 let line_index = LineIndex::new(text);
110
111 let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
112 let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
113 assert_eq!(actual, expected);
114
115 let text = "";
116 let line_index = LineIndex::new(text);
117
118 let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
119 let expected = vec![];
120 assert_eq!(actual, expected);
121
122 let text = "\n";
123 let line_index = LineIndex::new(text);
124
125 let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
126 let expected = vec![r(0, 1)];
127 assert_eq!(actual, expected)
128}
diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs
index f57b6dd91..78a43f587 100644
--- a/crates/ide_db/src/traits.rs
+++ b/crates/ide_db/src/traits.rs
@@ -78,150 +78,4 @@ pub fn get_missing_assoc_items(
78} 78}
79 79
80#[cfg(test)] 80#[cfg(test)]
81mod tests { 81mod tests;
82 use crate::RootDatabase;
83 use base_db::{fixture::ChangeFixture, FilePosition};
84 use expect_test::{expect, Expect};
85 use hir::Semantics;
86 use syntax::ast::{self, AstNode};
87 use test_utils::RangeOrOffset;
88
89 /// Creates analysis from a multi-file fixture, returns positions marked with <|>.
90 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
91 let change_fixture = ChangeFixture::parse(ra_fixture);
92 let mut database = RootDatabase::default();
93 database.apply_change(change_fixture.change);
94 let (file_id, range_or_offset) =
95 change_fixture.file_position.expect("expected a marker (<|>)");
96 let offset = match range_or_offset {
97 RangeOrOffset::Range(_) => panic!(),
98 RangeOrOffset::Offset(it) => it,
99 };
100 (database, FilePosition { file_id, offset })
101 }
102
103 fn check_trait(ra_fixture: &str, expect: Expect) {
104 let (db, position) = position(ra_fixture);
105 let sema = Semantics::new(&db);
106 let file = sema.parse(position.file_id);
107 let impl_block: ast::Impl =
108 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
109 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
110 let actual = match trait_ {
111 Some(trait_) => trait_.name(&db).to_string(),
112 None => String::new(),
113 };
114 expect.assert_eq(&actual);
115 }
116
117 fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
118 let (db, position) = position(ra_fixture);
119 let sema = Semantics::new(&db);
120 let file = sema.parse(position.file_id);
121 let impl_block: ast::Impl =
122 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
123 let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
124 let actual = items
125 .into_iter()
126 .map(|item| item.name(&db).unwrap().to_string())
127 .collect::<Vec<_>>()
128 .join("\n");
129 expect.assert_eq(&actual);
130 }
131
132 #[test]
133 fn resolve_trait() {
134 check_trait(
135 r#"
136pub trait Foo {
137 fn bar();
138}
139impl Foo for u8 {
140 <|>
141}
142 "#,
143 expect![["Foo"]],
144 );
145 check_trait(
146 r#"
147pub trait Foo {
148 fn bar();
149}
150impl Foo for u8 {
151 fn bar() {
152 fn baz() {
153 <|>
154 }
155 baz();
156 }
157}
158 "#,
159 expect![["Foo"]],
160 );
161 check_trait(
162 r#"
163pub trait Foo {
164 fn bar();
165}
166pub struct Bar;
167impl Bar {
168 <|>
169}
170 "#,
171 expect![[""]],
172 );
173 }
174
175 #[test]
176 fn missing_assoc_items() {
177 check_missing_assoc(
178 r#"
179pub trait Foo {
180 const FOO: u8;
181 fn bar();
182}
183impl Foo for u8 {
184 <|>
185}"#,
186 expect![[r#"
187 FOO
188 bar"#]],
189 );
190
191 check_missing_assoc(
192 r#"
193pub trait Foo {
194 const FOO: u8;
195 fn bar();
196}
197impl Foo for u8 {
198 const FOO: u8 = 10;
199 <|>
200}"#,
201 expect![[r#"
202 bar"#]],
203 );
204
205 check_missing_assoc(
206 r#"
207pub trait Foo {
208 const FOO: u8;
209 fn bar();
210}
211impl Foo for u8 {
212 const FOO: u8 = 10;
213 fn bar() {<|>}
214}"#,
215 expect![[r#""#]],
216 );
217
218 check_missing_assoc(
219 r#"
220pub struct Foo;
221impl Foo {
222 fn bar() {<|>}
223}"#,
224 expect![[r#""#]],
225 );
226 }
227}
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
new file mode 100644
index 000000000..09c7ac3ec
--- /dev/null
+++ b/crates/ide_db/src/traits/tests.rs
@@ -0,0 +1,144 @@
1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect};
4use hir::Semantics;
5use syntax::ast::{self, AstNode};
6use test_utils::RangeOrOffset;
7
8/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let change_fixture = ChangeFixture::parse(ra_fixture);
11 let mut database = RootDatabase::default();
12 database.apply_change(change_fixture.change);
13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)");
14 let offset = match range_or_offset {
15 RangeOrOffset::Range(_) => panic!(),
16 RangeOrOffset::Offset(it) => it,
17 };
18 (database, FilePosition { file_id, offset })
19}
20
21fn check_trait(ra_fixture: &str, expect: Expect) {
22 let (db, position) = position(ra_fixture);
23 let sema = Semantics::new(&db);
24 let file = sema.parse(position.file_id);
25 let impl_block: ast::Impl =
26 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
27 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
28 let actual = match trait_ {
29 Some(trait_) => trait_.name(&db).to_string(),
30 None => String::new(),
31 };
32 expect.assert_eq(&actual);
33}
34
35fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
36 let (db, position) = position(ra_fixture);
37 let sema = Semantics::new(&db);
38 let file = sema.parse(position.file_id);
39 let impl_block: ast::Impl =
40 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
41 let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
42 let actual = items
43 .into_iter()
44 .map(|item| item.name(&db).unwrap().to_string())
45 .collect::<Vec<_>>()
46 .join("\n");
47 expect.assert_eq(&actual);
48}
49
50#[test]
51fn resolve_trait() {
52 check_trait(
53 r#"
54pub trait Foo {
55 fn bar();
56}
57impl Foo for u8 {
58 <|>
59}
60 "#,
61 expect![["Foo"]],
62 );
63 check_trait(
64 r#"
65pub trait Foo {
66 fn bar();
67}
68impl Foo for u8 {
69 fn bar() {
70 fn baz() {
71 <|>
72 }
73 baz();
74 }
75}
76 "#,
77 expect![["Foo"]],
78 );
79 check_trait(
80 r#"
81pub trait Foo {
82 fn bar();
83}
84pub struct Bar;
85impl Bar {
86 <|>
87}
88 "#,
89 expect![[""]],
90 );
91}
92
93#[test]
94fn missing_assoc_items() {
95 check_missing_assoc(
96 r#"
97pub trait Foo {
98 const FOO: u8;
99 fn bar();
100}
101impl Foo for u8 {
102 <|>
103}"#,
104 expect![[r#"
105 FOO
106 bar"#]],
107 );
108
109 check_missing_assoc(
110 r#"
111pub trait Foo {
112 const FOO: u8;
113 fn bar();
114}
115impl Foo for u8 {
116 const FOO: u8 = 10;
117 <|>
118}"#,
119 expect![[r#"
120 bar"#]],
121 );
122
123 check_missing_assoc(
124 r#"
125pub trait Foo {
126 const FOO: u8;
127 fn bar();
128}
129impl Foo for u8 {
130 const FOO: u8 = 10;
131 fn bar() {<|>}
132}"#,
133 expect![[r#""#]],
134 );
135
136 check_missing_assoc(
137 r#"
138pub struct Foo;
139impl Foo {
140 fn bar() {<|>}
141}"#,
142 expect![[r#""#]],
143 );
144}
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index 907cb3db7..301888a0e 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -19,7 +19,7 @@ use crate::{
19 19
20#[derive(Debug, Default)] 20#[derive(Debug, Default)]
21pub(crate) struct ProcMacroProcessSrv { 21pub(crate) struct ProcMacroProcessSrv {
22 inner: Option<Weak<Sender<Task>>>, 22 inner: Weak<Sender<Task>>,
23} 23}
24 24
25#[derive(Debug)] 25#[derive(Debug)]
@@ -42,7 +42,7 @@ impl ProcMacroProcessSrv {
42 }); 42 });
43 43
44 let task_tx = Arc::new(task_tx); 44 let task_tx = Arc::new(task_tx);
45 let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) }; 45 let srv = ProcMacroProcessSrv { inner: Arc::downgrade(&task_tx) };
46 let thread = ProcMacroProcessThread { handle, sender: task_tx }; 46 let thread = ProcMacroProcessThread { handle, sender: task_tx };
47 47
48 Ok((thread, srv)) 48 Ok((thread, srv))
@@ -79,22 +79,18 @@ impl ProcMacroProcessSrv {
79 where 79 where
80 R: TryFrom<Response, Error = &'static str>, 80 R: TryFrom<Response, Error = &'static str>,
81 { 81 {
82 let sender = match &self.inner {
83 None => return Err(tt::ExpansionError::Unknown("No sender is found.".to_string())),
84 Some(it) => it,
85 };
86
87 let (result_tx, result_rx) = bounded(0); 82 let (result_tx, result_rx) = bounded(0);
88 let sender = match sender.upgrade() { 83 let sender = match self.inner.upgrade() {
89 None => { 84 None => return Err(tt::ExpansionError::Unknown("proc macro process is closed".into())),
90 return Err(tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
91 }
92 Some(it) => it, 85 Some(it) => it,
93 }; 86 };
94 sender.send(Task { req, result_tx }).unwrap(); 87 sender
88 .send(Task { req, result_tx })
89 .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?;
90
95 let res = result_rx 91 let res = result_rx
96 .recv() 92 .recv()
97 .map_err(|_| tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?; 93 .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?;
98 94
99 match res { 95 match res {
100 Some(Response::Error(err)) => { 96 Some(Response::Error(err)) => {
@@ -109,32 +105,23 @@ impl ProcMacroProcessSrv {
109} 105}
110 106
111fn client_loop(task_rx: Receiver<Task>, mut process: Process) { 107fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
112 let (mut stdin, mut stdout) = match process.stdio() { 108 let (mut stdin, mut stdout) = process.stdio().expect("couldn't access child stdio");
113 None => return,
114 Some(it) => it,
115 };
116
117 for task in task_rx {
118 let Task { req, result_tx } = task;
119 109
110 for Task { req, result_tx } in task_rx {
120 match send_request(&mut stdin, &mut stdout, req) { 111 match send_request(&mut stdin, &mut stdout, req) {
121 Ok(res) => result_tx.send(res).unwrap(), 112 Ok(res) => result_tx.send(res).unwrap(),
122 Err(_err) => { 113 Err(_err) => {
114 log::error!(
115 "proc macro server crashed, server process state: {:?}",
116 process.child.try_wait()
117 );
123 let res = Response::Error(ResponseError { 118 let res = Response::Error(ResponseError {
124 code: ErrorCode::ServerErrorEnd, 119 code: ErrorCode::ServerErrorEnd,
125 message: "Server closed".into(), 120 message: "proc macro server crashed".into(),
126 }); 121 });
127 result_tx.send(res.into()).unwrap(); 122 result_tx.send(res.into()).unwrap();
128 // Restart the process 123 // Exit the thread.
129 if process.restart().is_err() { 124 break;
130 break;
131 }
132 let stdio = match process.stdio() {
133 None => break,
134 Some(it) => it,
135 };
136 stdin = stdio.0;
137 stdout = stdio.1;
138 } 125 }
139 } 126 }
140 } 127 }
@@ -146,8 +133,6 @@ struct Task {
146} 133}
147 134
148struct Process { 135struct Process {
149 path: PathBuf,
150 args: Vec<OsString>,
151 child: Child, 136 child: Child,
152} 137}
153 138
@@ -162,15 +147,9 @@ impl Process {
162 path: PathBuf, 147 path: PathBuf,
163 args: impl IntoIterator<Item = impl AsRef<OsStr>>, 148 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
164 ) -> io::Result<Process> { 149 ) -> io::Result<Process> {
165 let args = args.into_iter().map(|s| s.as_ref().into()).collect(); 150 let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
166 let child = mk_child(&path, &args)?; 151 let child = mk_child(&path, &args)?;
167 Ok(Process { path, args, child }) 152 Ok(Process { child })
168 }
169
170 fn restart(&mut self) -> io::Result<()> {
171 let _ = self.child.kill();
172 self.child = mk_child(&self.path, &self.args)?;
173 Ok(())
174 } 153 }
175 154
176 fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { 155 fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 368f627ac..0090fd2c2 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -6,12 +6,25 @@ use std::path::Path;
6use anyhow::anyhow; 6use anyhow::anyhow;
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8 8
9use hir::Crate; 9use hir::{db::HirDatabase, Crate, Module};
10use ide::{DiagnosticsConfig, Severity}; 10use ide::{DiagnosticsConfig, Severity};
11use ide_db::base_db::SourceDatabaseExt; 11use ide_db::base_db::SourceDatabaseExt;
12 12
13use crate::cli::{load_cargo::load_cargo, Result}; 13use crate::cli::{load_cargo::load_cargo, Result};
14 14
15fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
16 let mut worklist: Vec<_> =
17 Crate::all(db).into_iter().map(|krate| krate.root_module(db)).collect();
18 let mut modules = Vec::new();
19
20 while let Some(module) = worklist.pop() {
21 modules.push(module);
22 worklist.extend(module.children(db));
23 }
24
25 modules
26}
27
15pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -> Result<()> { 28pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -> Result<()> {
16 let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; 29 let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
17 let db = host.raw_database(); 30 let db = host.raw_database();
@@ -20,18 +33,12 @@ pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -
20 let mut found_error = false; 33 let mut found_error = false;
21 let mut visited_files = FxHashSet::default(); 34 let mut visited_files = FxHashSet::default();
22 35
23 let mut work = Vec::new(); 36 let work = all_modules(db).into_iter().filter(|module| {
24 let krates = Crate::all(db); 37 let file_id = module.definition_source(db).file_id.original_file(db);
25 for krate in krates {
26 let module = krate.root_module(db);
27 let file_id = module.definition_source(db).file_id;
28 let file_id = file_id.original_file(db);
29 let source_root = db.file_source_root(file_id); 38 let source_root = db.file_source_root(file_id);
30 let source_root = db.source_root(source_root); 39 let source_root = db.source_root(source_root);
31 if !source_root.is_library { 40 !source_root.is_library
32 work.push(module); 41 });
33 }
34 }
35 42
36 for module in work { 43 for module in work {
37 let file_id = module.definition_source(db).file_id.original_file(db); 44 let file_id = module.definition_source(db).file_id.original_file(db);
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 72f6c5725..7576097b3 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -104,4 +104,168 @@
104 }, 104 },
105 fixes: [], 105 fixes: [],
106 }, 106 },
107 MappedRustDiagnostic {
108 url: Url {
109 scheme: "file",
110 host: None,
111 port: None,
112 path: "/test/compiler/lib.rs",
113 query: None,
114 fragment: None,
115 },
116 diagnostic: Diagnostic {
117 range: Range {
118 start: Position {
119 line: 0,
120 character: 8,
121 },
122 end: Position {
123 line: 0,
124 character: 19,
125 },
126 },
127 severity: Some(
128 Hint,
129 ),
130 code: Some(
131 String(
132 "trivially_copy_pass_by_ref",
133 ),
134 ),
135 code_description: Some(
136 CodeDescription {
137 href: Url {
138 scheme: "https",
139 host: Some(
140 Domain(
141 "rust-lang.github.io",
142 ),
143 ),
144 port: None,
145 path: "/rust-clippy/master/index.html",
146 query: None,
147 fragment: Some(
148 "trivially_copy_pass_by_ref",
149 ),
150 },
151 },
152 ),
153 source: Some(
154 "clippy",
155 ),
156 message: "lint level defined here",
157 related_information: Some(
158 [
159 DiagnosticRelatedInformation {
160 location: Location {
161 uri: Url {
162 scheme: "file",
163 host: None,
164 port: None,
165 path: "/test/compiler/mir/tagset.rs",
166 query: None,
167 fragment: None,
168 },
169 range: Range {
170 start: Position {
171 line: 41,
172 character: 23,
173 },
174 end: Position {
175 line: 41,
176 character: 28,
177 },
178 },
179 },
180 message: "original diagnostic",
181 },
182 ],
183 ),
184 tags: None,
185 data: None,
186 },
187 fixes: [],
188 },
189 MappedRustDiagnostic {
190 url: Url {
191 scheme: "file",
192 host: None,
193 port: None,
194 path: "/test/compiler/mir/tagset.rs",
195 query: None,
196 fragment: None,
197 },
198 diagnostic: Diagnostic {
199 range: Range {
200 start: Position {
201 line: 41,
202 character: 23,
203 },
204 end: Position {
205 line: 41,
206 character: 28,
207 },
208 },
209 severity: Some(
210 Hint,
211 ),
212 code: Some(
213 String(
214 "trivially_copy_pass_by_ref",
215 ),
216 ),
217 code_description: Some(
218 CodeDescription {
219 href: Url {
220 scheme: "https",
221 host: Some(
222 Domain(
223 "rust-lang.github.io",
224 ),
225 ),
226 port: None,
227 path: "/rust-clippy/master/index.html",
228 query: None,
229 fragment: Some(
230 "trivially_copy_pass_by_ref",
231 ),
232 },
233 },
234 ),
235 source: Some(
236 "clippy",
237 ),
238 message: "consider passing by value instead",
239 related_information: Some(
240 [
241 DiagnosticRelatedInformation {
242 location: Location {
243 uri: Url {
244 scheme: "file",
245 host: None,
246 port: None,
247 path: "/test/compiler/mir/tagset.rs",
248 query: None,
249 fragment: None,
250 },
251 range: Range {
252 start: Position {
253 line: 41,
254 character: 23,
255 },
256 end: Position {
257 line: 41,
258 character: 28,
259 },
260 },
261 },
262 message: "original diagnostic",
263 },
264 ],
265 ),
266 tags: None,
267 data: None,
268 },
269 fixes: [],
270 },
107] 271]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index bbec6a796..bdcf2a38f 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -4,19 +4,19 @@
4 scheme: "file", 4 scheme: "file",
5 host: None, 5 host: None,
6 port: None, 6 port: None,
7 path: "/test/crates/hir_def/src/data.rs", 7 path: "/test/crates/hir_def/src/path.rs",
8 query: None, 8 query: None,
9 fragment: None, 9 fragment: None,
10 }, 10 },
11 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
12 range: Range { 12 range: Range {
13 start: Position { 13 start: Position {
14 line: 79, 14 line: 264,
15 character: 15, 15 character: 8,
16 }, 16 },
17 end: Position { 17 end: Position {
18 line: 79, 18 line: 264,
19 character: 41, 19 character: 76,
20 }, 20 },
21 }, 21 },
22 severity: Some( 22 severity: Some(
@@ -36,22 +36,22 @@
36 scheme: "file", 36 scheme: "file",
37 host: None, 37 host: None,
38 port: None, 38 port: None,
39 path: "/test/crates/hir_def/src/path.rs", 39 path: "/test/crates/hir_def/src/data.rs",
40 query: None, 40 query: None,
41 fragment: None, 41 fragment: None,
42 }, 42 },
43 range: Range { 43 range: Range {
44 start: Position { 44 start: Position {
45 line: 264, 45 line: 79,
46 character: 8, 46 character: 15,
47 }, 47 },
48 end: Position { 48 end: Position {
49 line: 264, 49 line: 79,
50 character: 76, 50 character: 41,
51 }, 51 },
52 }, 52 },
53 }, 53 },
54 message: "Error originated from macro here", 54 message: "Exact error occurred here",
55 }, 55 },
56 ], 56 ],
57 ), 57 ),
@@ -65,19 +65,19 @@
65 scheme: "file", 65 scheme: "file",
66 host: None, 66 host: None,
67 port: None, 67 port: None,
68 path: "/test/crates/hir_def/src/path.rs", 68 path: "/test/crates/hir_def/src/data.rs",
69 query: None, 69 query: None,
70 fragment: None, 70 fragment: None,
71 }, 71 },
72 diagnostic: Diagnostic { 72 diagnostic: Diagnostic {
73 range: Range { 73 range: Range {
74 start: Position { 74 start: Position {
75 line: 264, 75 line: 79,
76 character: 8, 76 character: 15,
77 }, 77 },
78 end: Position { 78 end: Position {
79 line: 264, 79 line: 79,
80 character: 76, 80 character: 41,
81 }, 81 },
82 }, 82 },
83 severity: Some( 83 severity: Some(
@@ -89,33 +89,7 @@
89 "rustc", 89 "rustc",
90 ), 90 ),
91 message: "Please register your known path in the path module", 91 message: "Please register your known path in the path module",
92 related_information: Some( 92 related_information: None,
93 [
94 DiagnosticRelatedInformation {
95 location: Location {
96 uri: Url {
97 scheme: "file",
98 host: None,
99 port: None,
100 path: "/test/crates/hir_def/src/data.rs",
101 query: None,
102 fragment: None,
103 },
104 range: Range {
105 start: Position {
106 line: 79,
107 character: 15,
108 },
109 end: Position {
110 line: 79,
111 character: 41,
112 },
113 },
114 },
115 message: "Exact error occured here",
116 },
117 ],
118 ),
119 tags: None, 93 tags: None,
120 data: None, 94 data: None,
121 }, 95 },
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index c709de95f..23d42b4d0 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -32,7 +32,33 @@
32 "rustc", 32 "rustc",
33 ), 33 ),
34 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", 34 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
35 related_information: None, 35 related_information: Some(
36 [
37 DiagnosticRelatedInformation {
38 location: Location {
39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/driver/subcommand/repl.rs",
44 query: None,
45 fragment: None,
46 },
47 range: Range {
48 start: Position {
49 line: 290,
50 character: 8,
51 },
52 end: Position {
53 line: 290,
54 character: 11,
55 },
56 },
57 },
58 message: "consider prefixing with an underscore",
59 },
60 ],
61 ),
36 tags: Some( 62 tags: Some(
37 [ 63 [
38 Unnecessary, 64 Unnecessary,
@@ -87,4 +113,114 @@
87 }, 113 },
88 ], 114 ],
89 }, 115 },
116 MappedRustDiagnostic {
117 url: Url {
118 scheme: "file",
119 host: None,
120 port: None,
121 path: "/test/driver/subcommand/repl.rs",
122 query: None,
123 fragment: None,
124 },
125 diagnostic: Diagnostic {
126 range: Range {
127 start: Position {
128 line: 290,
129 character: 8,
130 },
131 end: Position {
132 line: 290,
133 character: 11,
134 },
135 },
136 severity: Some(
137 Hint,
138 ),
139 code: Some(
140 String(
141 "unused_variables",
142 ),
143 ),
144 code_description: None,
145 source: Some(
146 "rustc",
147 ),
148 message: "consider prefixing with an underscore",
149 related_information: Some(
150 [
151 DiagnosticRelatedInformation {
152 location: Location {
153 uri: Url {
154 scheme: "file",
155 host: None,
156 port: None,
157 path: "/test/driver/subcommand/repl.rs",
158 query: None,
159 fragment: None,
160 },
161 range: Range {
162 start: Position {
163 line: 290,
164 character: 8,
165 },
166 end: Position {
167 line: 290,
168 character: 11,
169 },
170 },
171 },
172 message: "original diagnostic",
173 },
174 ],
175 ),
176 tags: None,
177 data: None,
178 },
179 fixes: [
180 CodeAction {
181 title: "consider prefixing with an underscore",
182 group: None,
183 kind: Some(
184 CodeActionKind(
185 "quickfix",
186 ),
187 ),
188 edit: Some(
189 SnippetWorkspaceEdit {
190 changes: Some(
191 {
192 Url {
193 scheme: "file",
194 host: None,
195 port: None,
196 path: "/test/driver/subcommand/repl.rs",
197 query: None,
198 fragment: None,
199 }: [
200 TextEdit {
201 range: Range {
202 start: Position {
203 line: 290,
204 character: 8,
205 },
206 end: Position {
207 line: 290,
208 character: 11,
209 },
210 },
211 new_text: "_foo",
212 },
213 ],
214 },
215 ),
216 document_changes: None,
217 },
218 ),
219 is_preferred: Some(
220 true,
221 ),
222 data: None,
223 },
224 ],
225 },
90] 226]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index 632f438d7..4e428bedc 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -32,7 +32,33 @@
32 "rustc", 32 "rustc",
33 ), 33 ),
34 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", 34 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
35 related_information: None, 35 related_information: Some(
36 [
37 DiagnosticRelatedInformation {
38 location: Location {
39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/driver/subcommand/repl.rs",
44 query: None,
45 fragment: None,
46 },
47 range: Range {
48 start: Position {
49 line: 290,
50 character: 8,
51 },
52 end: Position {
53 line: 290,
54 character: 11,
55 },
56 },
57 },
58 message: "consider prefixing with an underscore",
59 },
60 ],
61 ),
36 tags: Some( 62 tags: Some(
37 [ 63 [
38 Unnecessary, 64 Unnecessary,
@@ -87,4 +113,114 @@
87 }, 113 },
88 ], 114 ],
89 }, 115 },
116 MappedRustDiagnostic {
117 url: Url {
118 scheme: "file",
119 host: None,
120 port: None,
121 path: "/test/driver/subcommand/repl.rs",
122 query: None,
123 fragment: None,
124 },
125 diagnostic: Diagnostic {
126 range: Range {
127 start: Position {
128 line: 290,
129 character: 8,
130 },
131 end: Position {
132 line: 290,
133 character: 11,
134 },
135 },
136 severity: Some(
137 Hint,
138 ),
139 code: Some(
140 String(
141 "unused_variables",
142 ),
143 ),
144 code_description: None,
145 source: Some(
146 "rustc",
147 ),
148 message: "consider prefixing with an underscore",
149 related_information: Some(
150 [
151 DiagnosticRelatedInformation {
152 location: Location {
153 uri: Url {
154 scheme: "file",
155 host: None,
156 port: None,
157 path: "/test/driver/subcommand/repl.rs",
158 query: None,
159 fragment: None,
160 },
161 range: Range {
162 start: Position {
163 line: 290,
164 character: 8,
165 },
166 end: Position {
167 line: 290,
168 character: 11,
169 },
170 },
171 },
172 message: "original diagnostic",
173 },
174 ],
175 ),
176 tags: None,
177 data: None,
178 },
179 fixes: [
180 CodeAction {
181 title: "consider prefixing with an underscore",
182 group: None,
183 kind: Some(
184 CodeActionKind(
185 "quickfix",
186 ),
187 ),
188 edit: Some(
189 SnippetWorkspaceEdit {
190 changes: Some(
191 {
192 Url {
193 scheme: "file",
194 host: None,
195 port: None,
196 path: "/test/driver/subcommand/repl.rs",
197 query: None,
198 fragment: None,
199 }: [
200 TextEdit {
201 range: Range {
202 start: Position {
203 line: 290,
204 character: 8,
205 },
206 end: Position {
207 line: 290,
208 character: 11,
209 },
210 },
211 new_text: "_foo",
212 },
213 ],
214 },
215 ),
216 document_changes: None,
217 },
218 ),
219 is_preferred: Some(
220 true,
221 ),
222 data: None,
223 },
224 ],
225 },
90] 226]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index c0b79428d..4ddd7efae 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -32,7 +32,33 @@
32 "rustc", 32 "rustc",
33 ), 33 ),
34 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", 34 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
35 related_information: None, 35 related_information: Some(
36 [
37 DiagnosticRelatedInformation {
38 location: Location {
39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/driver/subcommand/repl.rs",
44 query: None,
45 fragment: None,
46 },
47 range: Range {
48 start: Position {
49 line: 290,
50 character: 8,
51 },
52 end: Position {
53 line: 290,
54 character: 11,
55 },
56 },
57 },
58 message: "consider prefixing with an underscore",
59 },
60 ],
61 ),
36 tags: Some( 62 tags: Some(
37 [ 63 [
38 Unnecessary, 64 Unnecessary,
@@ -87,4 +113,114 @@
87 }, 113 },
88 ], 114 ],
89 }, 115 },
116 MappedRustDiagnostic {
117 url: Url {
118 scheme: "file",
119 host: None,
120 port: None,
121 path: "/test/driver/subcommand/repl.rs",
122 query: None,
123 fragment: None,
124 },
125 diagnostic: Diagnostic {
126 range: Range {
127 start: Position {
128 line: 290,
129 character: 8,
130 },
131 end: Position {
132 line: 290,
133 character: 11,
134 },
135 },
136 severity: Some(
137 Hint,
138 ),
139 code: Some(
140 String(
141 "unused_variables",
142 ),
143 ),
144 code_description: None,
145 source: Some(
146 "rustc",
147 ),
148 message: "consider prefixing with an underscore",
149 related_information: Some(
150 [
151 DiagnosticRelatedInformation {
152 location: Location {
153 uri: Url {
154 scheme: "file",
155 host: None,
156 port: None,
157 path: "/test/driver/subcommand/repl.rs",
158 query: None,
159 fragment: None,
160 },
161 range: Range {
162 start: Position {
163 line: 290,
164 character: 8,
165 },
166 end: Position {
167 line: 290,
168 character: 11,
169 },
170 },
171 },
172 message: "original diagnostic",
173 },
174 ],
175 ),
176 tags: None,
177 data: None,
178 },
179 fixes: [
180 CodeAction {
181 title: "consider prefixing with an underscore",
182 group: None,
183 kind: Some(
184 CodeActionKind(
185 "quickfix",
186 ),
187 ),
188 edit: Some(
189 SnippetWorkspaceEdit {
190 changes: Some(
191 {
192 Url {
193 scheme: "file",
194 host: None,
195 port: None,
196 path: "/test/driver/subcommand/repl.rs",
197 query: None,
198 fragment: None,
199 }: [
200 TextEdit {
201 range: Range {
202 start: Position {
203 line: 290,
204 character: 8,
205 },
206 end: Position {
207 line: 290,
208 character: 11,
209 },
210 },
211 new_text: "_foo",
212 },
213 ],
214 },
215 ),
216 document_changes: None,
217 },
218 ),
219 is_preferred: Some(
220 true,
221 ),
222 data: None,
223 },
224 ],
225 },
90] 226]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
index b9650f3e4..f455cf25e 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
@@ -81,4 +81,86 @@
81 }, 81 },
82 fixes: [], 82 fixes: [],
83 }, 83 },
84 MappedRustDiagnostic {
85 url: Url {
86 scheme: "file",
87 host: None,
88 port: None,
89 path: "/test/compiler/ty/select.rs",
90 query: None,
91 fragment: None,
92 },
93 diagnostic: Diagnostic {
94 range: Range {
95 start: Position {
96 line: 218,
97 character: 4,
98 },
99 end: Position {
100 line: 230,
101 character: 5,
102 },
103 },
104 severity: Some(
105 Hint,
106 ),
107 code: Some(
108 String(
109 "E0061",
110 ),
111 ),
112 code_description: Some(
113 CodeDescription {
114 href: Url {
115 scheme: "https",
116 host: Some(
117 Domain(
118 "doc.rust-lang.org",
119 ),
120 ),
121 port: None,
122 path: "/error-index.html",
123 query: None,
124 fragment: Some(
125 "E0061",
126 ),
127 },
128 },
129 ),
130 source: Some(
131 "rustc",
132 ),
133 message: "defined here",
134 related_information: Some(
135 [
136 DiagnosticRelatedInformation {
137 location: Location {
138 uri: Url {
139 scheme: "file",
140 host: None,
141 port: None,
142 path: "/test/compiler/ty/select.rs",
143 query: None,
144 fragment: None,
145 },
146 range: Range {
147 start: Position {
148 line: 103,
149 character: 17,
150 },
151 end: Position {
152 line: 103,
153 character: 29,
154 },
155 },
156 },
157 message: "original diagnostic",
158 },
159 ],
160 ),
161 tags: None,
162 data: None,
163 },
164 fixes: [],
165 },
84] 166]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index c45f68a91..4cbdb3b92 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -74,6 +74,309 @@
74 }, 74 },
75 message: "unnecessary let binding", 75 message: "unnecessary let binding",
76 }, 76 },
77 DiagnosticRelatedInformation {
78 location: Location {
79 uri: Url {
80 scheme: "file",
81 host: None,
82 port: None,
83 path: "/test/src/main.rs",
84 query: None,
85 fragment: None,
86 },
87 range: Range {
88 start: Position {
89 line: 2,
90 character: 4,
91 },
92 end: Position {
93 line: 2,
94 character: 30,
95 },
96 },
97 },
98 message: "return the expression directly",
99 },
100 ],
101 ),
102 tags: None,
103 data: None,
104 },
105 fixes: [
106 CodeAction {
107 title: "return the expression directly",
108 group: None,
109 kind: Some(
110 CodeActionKind(
111 "quickfix",
112 ),
113 ),
114 edit: Some(
115 SnippetWorkspaceEdit {
116 changes: Some(
117 {
118 Url {
119 scheme: "file",
120 host: None,
121 port: None,
122 path: "/test/src/main.rs",
123 query: None,
124 fragment: None,
125 }: [
126 TextEdit {
127 range: Range {
128 start: Position {
129 line: 2,
130 character: 4,
131 },
132 end: Position {
133 line: 2,
134 character: 30,
135 },
136 },
137 new_text: "",
138 },
139 TextEdit {
140 range: Range {
141 start: Position {
142 line: 3,
143 character: 4,
144 },
145 end: Position {
146 line: 3,
147 character: 5,
148 },
149 },
150 new_text: "(0..10).collect()",
151 },
152 ],
153 },
154 ),
155 document_changes: None,
156 },
157 ),
158 is_preferred: Some(
159 true,
160 ),
161 data: None,
162 },
163 ],
164 },
165 MappedRustDiagnostic {
166 url: Url {
167 scheme: "file",
168 host: None,
169 port: None,
170 path: "/test/src/main.rs",
171 query: None,
172 fragment: None,
173 },
174 diagnostic: Diagnostic {
175 range: Range {
176 start: Position {
177 line: 2,
178 character: 4,
179 },
180 end: Position {
181 line: 2,
182 character: 30,
183 },
184 },
185 severity: Some(
186 Hint,
187 ),
188 code: Some(
189 String(
190 "let_and_return",
191 ),
192 ),
193 code_description: Some(
194 CodeDescription {
195 href: Url {
196 scheme: "https",
197 host: Some(
198 Domain(
199 "rust-lang.github.io",
200 ),
201 ),
202 port: None,
203 path: "/rust-clippy/master/index.html",
204 query: None,
205 fragment: Some(
206 "let_and_return",
207 ),
208 },
209 },
210 ),
211 source: Some(
212 "clippy",
213 ),
214 message: "unnecessary let binding",
215 related_information: Some(
216 [
217 DiagnosticRelatedInformation {
218 location: Location {
219 uri: Url {
220 scheme: "file",
221 host: None,
222 port: None,
223 path: "/test/src/main.rs",
224 query: None,
225 fragment: None,
226 },
227 range: Range {
228 start: Position {
229 line: 3,
230 character: 4,
231 },
232 end: Position {
233 line: 3,
234 character: 5,
235 },
236 },
237 },
238 message: "original diagnostic",
239 },
240 ],
241 ),
242 tags: None,
243 data: None,
244 },
245 fixes: [
246 CodeAction {
247 title: "return the expression directly",
248 group: None,
249 kind: Some(
250 CodeActionKind(
251 "quickfix",
252 ),
253 ),
254 edit: Some(
255 SnippetWorkspaceEdit {
256 changes: Some(
257 {
258 Url {
259 scheme: "file",
260 host: None,
261 port: None,
262 path: "/test/src/main.rs",
263 query: None,
264 fragment: None,
265 }: [
266 TextEdit {
267 range: Range {
268 start: Position {
269 line: 2,
270 character: 4,
271 },
272 end: Position {
273 line: 2,
274 character: 30,
275 },
276 },
277 new_text: "",
278 },
279 TextEdit {
280 range: Range {
281 start: Position {
282 line: 3,
283 character: 4,
284 },
285 end: Position {
286 line: 3,
287 character: 5,
288 },
289 },
290 new_text: "(0..10).collect()",
291 },
292 ],
293 },
294 ),
295 document_changes: None,
296 },
297 ),
298 is_preferred: Some(
299 true,
300 ),
301 data: None,
302 },
303 ],
304 },
305 MappedRustDiagnostic {
306 url: Url {
307 scheme: "file",
308 host: None,
309 port: None,
310 path: "/test/src/main.rs",
311 query: None,
312 fragment: None,
313 },
314 diagnostic: Diagnostic {
315 range: Range {
316 start: Position {
317 line: 2,
318 character: 4,
319 },
320 end: Position {
321 line: 2,
322 character: 30,
323 },
324 },
325 severity: Some(
326 Hint,
327 ),
328 code: Some(
329 String(
330 "let_and_return",
331 ),
332 ),
333 code_description: Some(
334 CodeDescription {
335 href: Url {
336 scheme: "https",
337 host: Some(
338 Domain(
339 "rust-lang.github.io",
340 ),
341 ),
342 port: None,
343 path: "/rust-clippy/master/index.html",
344 query: None,
345 fragment: Some(
346 "let_and_return",
347 ),
348 },
349 },
350 ),
351 source: Some(
352 "clippy",
353 ),
354 message: "return the expression directly",
355 related_information: Some(
356 [
357 DiagnosticRelatedInformation {
358 location: Location {
359 uri: Url {
360 scheme: "file",
361 host: None,
362 port: None,
363 path: "/test/src/main.rs",
364 query: None,
365 fragment: None,
366 },
367 range: Range {
368 start: Position {
369 line: 3,
370 character: 4,
371 },
372 end: Position {
373 line: 3,
374 character: 5,
375 },
376 },
377 },
378 message: "original diagnostic",
379 },
77 ], 380 ],
78 ), 381 ),
79 tags: None, 382 tags: None,
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 324019614..f16f97131 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -75,8 +75,10 @@ fn diagnostic_related_information(
75} 75}
76 76
77enum MappedRustChildDiagnostic { 77enum MappedRustChildDiagnostic {
78 Related(lsp_types::DiagnosticRelatedInformation), 78 Related {
79 SuggestedFix(lsp_ext::CodeAction), 79 related: lsp_types::DiagnosticRelatedInformation,
80 suggested_fix: Option<lsp_ext::CodeAction>,
81 },
80 MessageLine(String), 82 MessageLine(String),
81} 83}
82 84
@@ -103,23 +105,32 @@ fn map_rust_child_diagnostic(
103 } 105 }
104 106
105 if edit_map.is_empty() { 107 if edit_map.is_empty() {
106 MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation { 108 MappedRustChildDiagnostic::Related {
107 location: location(workspace_root, spans[0]), 109 related: lsp_types::DiagnosticRelatedInformation {
108 message: rd.message.clone(), 110 location: location(workspace_root, spans[0]),
109 }) 111 message: rd.message.clone(),
112 },
113 suggested_fix: None,
114 }
110 } else { 115 } else {
111 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 116 MappedRustChildDiagnostic::Related {
112 title: rd.message.clone(), 117 related: lsp_types::DiagnosticRelatedInformation {
113 group: None, 118 location: location(workspace_root, spans[0]),
114 kind: Some(lsp_types::CodeActionKind::QUICKFIX), 119 message: rd.message.clone(),
115 edit: Some(lsp_ext::SnippetWorkspaceEdit { 120 },
116 // FIXME: there's no good reason to use edit_map here.... 121 suggested_fix: Some(lsp_ext::CodeAction {
117 changes: Some(edit_map), 122 title: rd.message.clone(),
118 document_changes: None, 123 group: None,
124 kind: Some(lsp_types::CodeActionKind::QUICKFIX),
125 edit: Some(lsp_ext::SnippetWorkspaceEdit {
126 // FIXME: there's no good reason to use edit_map here....
127 changes: Some(edit_map),
128 document_changes: None,
129 }),
130 is_preferred: Some(true),
131 data: None,
119 }), 132 }),
120 is_preferred: Some(true), 133 }
121 data: None,
122 })
123 } 134 }
124} 135}
125 136
@@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
179 for child in &rd.children { 190 for child in &rd.children {
180 let child = map_rust_child_diagnostic(workspace_root, &child); 191 let child = map_rust_child_diagnostic(workspace_root, &child);
181 match child { 192 match child {
182 MappedRustChildDiagnostic::Related(related) => related_information.push(related), 193 MappedRustChildDiagnostic::Related { related, suggested_fix } => {
183 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), 194 related_information.push(related);
195 if let Some(code_action) = suggested_fix {
196 fixes.push(code_action);
197 }
198 }
184 MappedRustChildDiagnostic::MessageLine(message_line) => { 199 MappedRustChildDiagnostic::MessageLine(message_line) => {
185 format_to!(message, "\n{}", message_line); 200 format_to!(message, "\n{}", message_line);
186 201
@@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
219 234
220 primary_spans 235 primary_spans
221 .iter() 236 .iter()
222 .map(|primary_span| { 237 .flat_map(|primary_span| {
223 let location = location(workspace_root, &primary_span); 238 let location = location(workspace_root, &primary_span);
224 239
225 let mut message = message.clone(); 240 let mut message = message.clone();
@@ -229,72 +244,100 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
229 } 244 }
230 } 245 }
231 246
247 // Each primary diagnostic span may result in multiple LSP diagnostics.
248 let mut diagnostics = Vec::new();
249
250 let mut related_macro_info = None;
251
232 // If error occurs from macro expansion, add related info pointing to 252 // If error occurs from macro expansion, add related info pointing to
233 // where the error originated 253 // where the error originated
234 // Also, we would generate an additional diagnostic, so that exact place of macro 254 // Also, we would generate an additional diagnostic, so that exact place of macro
235 // will be highlighted in the error origin place. 255 // will be highlighted in the error origin place.
236 let additional_diagnostic = 256 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
237 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 257 let in_macro_location = location_naive(workspace_root, &primary_span);
238 let in_macro_location = location_naive(workspace_root, &primary_span);
239 258
240 // Add related information for the main disagnostic. 259 // Add related information for the main disagnostic.
241 related_information.push(lsp_types::DiagnosticRelatedInformation { 260 related_macro_info = Some(lsp_types::DiagnosticRelatedInformation {
242 location: in_macro_location.clone(), 261 location: in_macro_location.clone(),
243 message: "Error originated from macro here".to_string(), 262 message: "Error originated from macro here".to_string(),
244 }); 263 });
245 264
246 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. 265 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
247 let information_for_additional_diagnostic = 266 let information_for_additional_diagnostic =
248 vec![lsp_types::DiagnosticRelatedInformation { 267 vec![lsp_types::DiagnosticRelatedInformation {
249 location: location.clone(), 268 location: location.clone(),
250 message: "Exact error occured here".to_string(), 269 message: "Exact error occurred here".to_string(),
251 }]; 270 }];
252 271
253 let diagnostic = lsp_types::Diagnostic { 272 let diagnostic = lsp_types::Diagnostic {
254 range: in_macro_location.range, 273 range: in_macro_location.range,
255 severity, 274 severity,
256 code: code.clone().map(lsp_types::NumberOrString::String), 275 code: code.clone().map(lsp_types::NumberOrString::String),
257 code_description: code_description.clone(), 276 code_description: code_description.clone(),
258 source: Some(source.clone()), 277 source: Some(source.clone()),
259 message: message.clone(), 278 message: message.clone(),
260 related_information: Some(information_for_additional_diagnostic), 279 related_information: Some(information_for_additional_diagnostic),
261 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 280 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
262 data: None, 281 data: None,
263 };
264
265 Some(MappedRustDiagnostic {
266 url: in_macro_location.uri,
267 diagnostic,
268 fixes: fixes.clone(),
269 })
270 } else {
271 None
272 }; 282 };
273 283
274 let diagnostic = lsp_types::Diagnostic { 284 diagnostics.push(MappedRustDiagnostic {
275 range: location.range, 285 url: in_macro_location.uri,
276 severity, 286 diagnostic,
277 code: code.clone().map(lsp_types::NumberOrString::String), 287 fixes: fixes.clone(),
278 code_description: code_description.clone(), 288 });
279 source: Some(source.clone()), 289 }
280 message, 290
281 related_information: if related_information.is_empty() { 291 // Emit the primary diagnostic.
282 None 292 diagnostics.push(MappedRustDiagnostic {
283 } else { 293 url: location.uri.clone(),
284 Some(related_information.clone()) 294 diagnostic: lsp_types::Diagnostic {
295 range: location.range,
296 severity,
297 code: code.clone().map(lsp_types::NumberOrString::String),
298 code_description: code_description.clone(),
299 source: Some(source.clone()),
300 message,
301 related_information: if related_information.is_empty() {
302 None
303 } else {
304 let mut related = related_information.clone();
305 related.extend(related_macro_info);
306 Some(related)
307 },
308 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
309 data: None,
285 }, 310 },
286 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 311 fixes: fixes.clone(),
287 data: None, 312 });
288 };
289 313
290 let main_diagnostic = 314 // Emit hint-level diagnostics for all `related_information` entries such as "help"s.
291 MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }; 315 // This is useful because they will show up in the user's editor, unlike
292 match additional_diagnostic { 316 // `related_information`, which just produces hard-to-read links, at least in VS Code.
293 None => vec![main_diagnostic], 317 let back_ref = lsp_types::DiagnosticRelatedInformation {
294 Some(additional_diagnostic) => vec![main_diagnostic, additional_diagnostic], 318 location,
319 message: "original diagnostic".to_string(),
320 };
321 for info in &related_information {
322 diagnostics.push(MappedRustDiagnostic {
323 url: info.location.uri.clone(),
324 fixes: fixes.clone(), // share fixes to make them easier to apply
325 diagnostic: lsp_types::Diagnostic {
326 range: info.location.range,
327 severity: Some(lsp_types::DiagnosticSeverity::Hint),
328 code: code.clone().map(lsp_types::NumberOrString::String),
329 code_description: code_description.clone(),
330 source: Some(source.clone()),
331 message: info.message.clone(),
332 related_information: Some(vec![back_ref.clone()]),
333 tags: None, // don't apply modifiers again
334 data: None,
335 },
336 });
295 } 337 }
338
339 diagnostics
296 }) 340 })
297 .flatten()
298 .collect() 341 .collect()
299} 342}
300 343
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f349b0810..55d46b09e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -506,7 +506,7 @@ impl GlobalState {
506 .write() 506 .write()
507 .0 507 .0
508 .set_file_contents(path, Some(params.text_document.text.into_bytes())); 508 .set_file_contents(path, Some(params.text_document.text.into_bytes()));
509 this.update_file_notifications_on_threadpool(); 509 this.maybe_update_diagnostics();
510 } 510 }
511 Ok(()) 511 Ok(())
512 })? 512 })?
@@ -616,6 +616,23 @@ impl GlobalState {
616 Ok(()) 616 Ok(())
617 } 617 }
618 fn update_file_notifications_on_threadpool(&mut self) { 618 fn update_file_notifications_on_threadpool(&mut self) {
619 self.maybe_update_diagnostics();
620 self.task_pool.handle.spawn_with_sender({
621 let snap = self.snapshot();
622 move |sender| {
623 snap.analysis
624 .prime_caches(|progress| {
625 sender.send(Task::PrimeCaches(progress)).unwrap();
626 })
627 .unwrap_or_else(|_: Canceled| {
628 // Pretend that we're done, so that the progress bar is removed. Otherwise
629 // the editor may complain about it already existing.
630 sender.send(Task::PrimeCaches(PrimeCachesProgress::Finished)).unwrap()
631 });
632 }
633 });
634 }
635 fn maybe_update_diagnostics(&mut self) {
619 let subscriptions = self 636 let subscriptions = self
620 .mem_docs 637 .mem_docs
621 .keys() 638 .keys()
@@ -644,19 +661,5 @@ impl GlobalState {
644 Task::Diagnostics(diagnostics) 661 Task::Diagnostics(diagnostics)
645 }) 662 })
646 } 663 }
647 self.task_pool.handle.spawn_with_sender({
648 let snap = self.snapshot();
649 move |sender| {
650 snap.analysis
651 .prime_caches(|progress| {
652 sender.send(Task::PrimeCaches(progress)).unwrap();
653 })
654 .unwrap_or_else(|_: Canceled| {
655 // Pretend that we're done, so that the progress bar is removed. Otherwise
656 // the editor may complain about it already existing.
657 sender.send(Task::PrimeCaches(PrimeCachesProgress::Finished)).unwrap()
658 });
659 }
660 });
661 } 664 }
662} 665}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2052b800c..01eabe852 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -381,6 +381,7 @@ fn semantic_token_type_and_modifiers(
381 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, 381 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION,
382 HighlightTag::Generic => semantic_tokens::GENERIC, 382 HighlightTag::Generic => semantic_tokens::GENERIC,
383 HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE, 383 HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE,
384 HighlightTag::Method => lsp_types::SemanticTokenType::METHOD,
384 HighlightTag::Constant => { 385 HighlightTag::Constant => {
385 mods |= semantic_tokens::CONSTANT; 386 mods |= semantic_tokens::CONSTANT;
386 mods |= lsp_types::SemanticTokenModifier::STATIC; 387 mods |= lsp_types::SemanticTokenModifier::STATIC;
diff --git a/docs/dev/README.md b/docs/dev/README.md
index abb387e8e..ca324493f 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -170,6 +170,22 @@ In general, API is centered around UI concerns -- the result of the call is what
170The results are 100% Rust specific though. 170The results are 100% Rust specific though.
171Shout outs to LSP developers for popularizing the idea that "UI" is a good place to draw a boundary at. 171Shout outs to LSP developers for popularizing the idea that "UI" is a good place to draw a boundary at.
172 172
173## LSP is sateless
174
175The protocol is implemented in the mostly stateless way.
176A good mental model is HTTP, which doesn't store per-client state, and instead relies on devices like cookies to maintain an illusion of state.
177If some action requires multi-step protocol, each step should be self-contained.
178
179A good example here is code action resolving process.
180TO display the lightbulb, we compute the list of code actions without computing edits.
181Figuring out the edit is done in a separate `codeAction/resolve` call.
182Rather than storing some `lazy_edit: Box<dyn FnOnce() -> Edit>` somewhere, we use a string ID of action to re-compute the list of actions during the resolve process.
183(See [this post](https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html) for more details.)
184The benefit here is that, generally speaking, the state of the world might change between `codeAction` and `codeAction` resolve requests, so any closure we store might become invalid.
185
186While we don't currently implement any complicated refactors with complex GUI, I imagine we'd use the same techniques for refactors.
187After clicking each "Next" button during refactor, the client would send all the info which server needs to re-recreate the context from scratch.
188
173## CI 189## CI
174 190
175CI does not test rust-analyzer, CI is a core part of rust-analyzer, and is maintained with above average standard of quality. 191CI does not test rust-analyzer, CI is a core part of rust-analyzer, and is maintained with above average standard of quality.