aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-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
47 files changed, 3052 insertions, 1760 deletions
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;