aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock70
-rw-r--r--crates/base_db/src/fixture.rs39
-rw-r--r--crates/hir/src/has_source.rs2
-rw-r--r--crates/hir_def/src/generics.rs10
-rw-r--r--crates/hir_def/src/item_tree/lower.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs39
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/tests/coercion.rs650
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs99
-rw-r--r--crates/hir_ty/src/tests/regression.rs98
-rw-r--r--crates/hir_ty/src/tests/simple.rs767
-rw-r--r--crates/hir_ty/src/tests/traits.rs125
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/diagnostics.rs498
-rw-r--r--crates/ide/src/display/navigation_target.rs6
-rw-r--r--crates/ide/src/doc_links.rs18
-rw-r--r--crates/ide/src/fixture.rs8
-rw-r--r--crates/ide/src/goto_definition.rs10
-rw-r--r--crates/ide/src/goto_type_definition.rs62
-rw-r--r--crates/ide/src/hover.rs20
-rw-r--r--crates/ide/src/lib.rs26
-rw-r--r--crates/ide/src/references.rs2
-rw-r--r--crates/ide/src/rename.rs (renamed from crates/ide/src/references/rename.rs)459
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs50
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs5
-rw-r--r--crates/ide_assists/src/handlers/fix_visibility.rs34
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs49
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs1201
-rw-r--r--crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs5
-rw-r--r--crates/ide_assists/src/lib.rs163
-rw-r--r--crates/ide_assists/src/tests.rs26
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs49
-rw-r--r--crates/ide_completion/src/completions/keyword.rs20
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs18
-rw-r--r--crates/ide_completion/src/render.rs12
-rw-r--r--crates/ide_db/src/assists.rs136
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs7
-rw-r--r--crates/ide_db/src/lib.rs5
-rw-r--r--crates/ide_db/src/rename.rs468
-rw-r--r--crates/ide_diagnostics/Cargo.toml29
-rw-r--r--crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs (renamed from crates/ide/src/diagnostics/break_outside_of_loop.rs)8
-rw-r--r--crates/ide_diagnostics/src/handlers/field_shorthand.rs (renamed from crates/ide/src/diagnostics/field_shorthand.rs)6
-rw-r--r--crates/ide_diagnostics/src/handlers/inactive_code.rs (renamed from crates/ide/src/diagnostics/inactive_code.rs)35
-rw-r--r--crates/ide_diagnostics/src/handlers/incorrect_case.rs (renamed from crates/ide/src/diagnostics/incorrect_case.rs)115
-rw-r--r--crates/ide_diagnostics/src/handlers/macro_error.rs (renamed from crates/ide/src/diagnostics/macro_error.rs)26
-rw-r--r--crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs (renamed from crates/ide/src/diagnostics/mismatched_arg_count.rs)28
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_fields.rs (renamed from crates/ide/src/diagnostics/missing_fields.rs)42
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_match_arms.rs (renamed from crates/ide/src/diagnostics/missing_match_arms.rs)106
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs (renamed from crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs)9
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_unsafe.rs (renamed from crates/ide/src/diagnostics/missing_unsafe.rs)16
-rw-r--r--crates/ide_diagnostics/src/handlers/no_such_field.rs (renamed from crates/ide/src/diagnostics/no_such_field.rs)13
-rw-r--r--crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs (renamed from crates/ide/src/diagnostics/remove_this_semicolon.rs)11
-rw-r--r--crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs (renamed from crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs)25
-rw-r--r--crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs (renamed from crates/ide/src/diagnostics/unimplemented_builtin_macro.rs)7
-rw-r--r--crates/ide_diagnostics/src/handlers/unlinked_file.rs (renamed from crates/ide/src/diagnostics/unlinked_file.rs)30
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs (renamed from crates/ide/src/diagnostics/unresolved_extern_crate.rs)10
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_import.rs (renamed from crates/ide/src/diagnostics/unresolved_import.rs)24
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs (renamed from crates/ide/src/diagnostics/unresolved_macro_call.rs)14
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_module.rs (renamed from crates/ide/src/diagnostics/unresolved_module.rs)11
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs (renamed from crates/ide/src/diagnostics/unresolved_proc_macro.rs)7
-rw-r--r--crates/ide_diagnostics/src/handlers/useless_braces.rs148
-rw-r--r--crates/ide_diagnostics/src/lib.rs374
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs4
-rw-r--r--crates/syntax/src/ast/node_ext.rs9
-rw-r--r--crates/syntax/test_data/parser/ok/0011_outer_attribute.rast2
-rw-r--r--crates/syntax/test_data/parser/ok/0011_outer_attribute.rs2
-rw-r--r--crates/test_utils/src/fixture.rs168
-rw-r--r--crates/test_utils/src/lib.rs5
-rw-r--r--crates/test_utils/src/minicore.rs204
-rw-r--r--docs/dev/style.md7
-rw-r--r--docs/user/.gitignore1
-rw-r--r--docs/user/manual.adoc9
-rw-r--r--editors/code/package.json4
-rw-r--r--editors/code/src/main.ts14
-rw-r--r--editors/code/src/net.ts18
-rw-r--r--editors/code/src/toolchain.ts16
-rw-r--r--xtask/src/dist.rs4
-rw-r--r--xtask/src/tidy.rs31
89 files changed, 3612 insertions, 3260 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e47b87964..5066d5f0f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -216,20 +216,6 @@ dependencies = [
216 "petgraph", 216 "petgraph",
217 "rustc-hash", 217 "rustc-hash",
218 "tracing", 218 "tracing",
219 "tracing-subscriber",
220 "tracing-tree",
221]
222
223[[package]]
224name = "chrono"
225version = "0.4.19"
226source = "registry+https://github.com/rust-lang/crates.io-index"
227checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
228dependencies = [
229 "libc",
230 "num-integer",
231 "num-traits",
232 "winapi",
233] 219]
234 220
235[[package]] 221[[package]]
@@ -591,6 +577,7 @@ dependencies = [
591 "ide_assists", 577 "ide_assists",
592 "ide_completion", 578 "ide_completion",
593 "ide_db", 579 "ide_db",
580 "ide_diagnostics",
594 "ide_ssr", 581 "ide_ssr",
595 "indexmap", 582 "indexmap",
596 "itertools", 583 "itertools",
@@ -669,6 +656,25 @@ dependencies = [
669] 656]
670 657
671[[package]] 658[[package]]
659name = "ide_diagnostics"
660version = "0.0.0"
661dependencies = [
662 "cfg",
663 "cov-mark",
664 "either",
665 "expect-test",
666 "hir",
667 "ide_db",
668 "itertools",
669 "profile",
670 "rustc-hash",
671 "stdx",
672 "syntax",
673 "test_utils",
674 "text_edit",
675]
676
677[[package]]
672name = "ide_ssr" 678name = "ide_ssr"
673version = "0.0.0" 679version = "0.0.0"
674dependencies = [ 680dependencies = [
@@ -955,25 +961,6 @@ dependencies = [
955] 961]
956 962
957[[package]] 963[[package]]
958name = "num-integer"
959version = "0.1.44"
960source = "registry+https://github.com/rust-lang/crates.io-index"
961checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
962dependencies = [
963 "autocfg",
964 "num-traits",
965]
966
967[[package]]
968name = "num-traits"
969version = "0.2.14"
970source = "registry+https://github.com/rust-lang/crates.io-index"
971checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
972dependencies = [
973 "autocfg",
974]
975
976[[package]]
977name = "num_cpus" 964name = "num_cpus"
978version = "1.13.0" 965version = "1.13.0"
979source = "registry+https://github.com/rust-lang/crates.io-index" 966source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1742,35 +1729,18 @@ dependencies = [
1742] 1729]
1743 1730
1744[[package]] 1731[[package]]
1745name = "tracing-serde"
1746version = "0.1.2"
1747source = "registry+https://github.com/rust-lang/crates.io-index"
1748checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
1749dependencies = [
1750 "serde",
1751 "tracing-core",
1752]
1753
1754[[package]]
1755name = "tracing-subscriber" 1732name = "tracing-subscriber"
1756version = "0.2.18" 1733version = "0.2.18"
1757source = "registry+https://github.com/rust-lang/crates.io-index" 1734source = "registry+https://github.com/rust-lang/crates.io-index"
1758checksum = "aa5553bf0883ba7c9cbe493b085c29926bd41b66afc31ff72cf17ff4fb60dcd5" 1735checksum = "aa5553bf0883ba7c9cbe493b085c29926bd41b66afc31ff72cf17ff4fb60dcd5"
1759dependencies = [ 1736dependencies = [
1760 "ansi_term",
1761 "chrono",
1762 "lazy_static", 1737 "lazy_static",
1763 "matchers", 1738 "matchers",
1764 "regex", 1739 "regex",
1765 "serde",
1766 "serde_json",
1767 "sharded-slab", 1740 "sharded-slab",
1768 "smallvec",
1769 "thread_local", 1741 "thread_local",
1770 "tracing", 1742 "tracing",
1771 "tracing-core", 1743 "tracing-core",
1772 "tracing-log",
1773 "tracing-serde",
1774] 1744]
1775 1745
1776[[package]] 1746[[package]]
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index da4afb5eb..d56b20b83 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -9,8 +9,8 @@ use test_utils::{
9use vfs::{file_set::FileSet, VfsPath}; 9use vfs::{file_set::FileSet, VfsPath};
10 10
11use crate::{ 11use crate::{
12 input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, FileRange, 12 input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId,
13 SourceDatabaseExt, SourceRoot, SourceRootId, 13 FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId,
14}; 14};
15 15
16pub const WORKSPACE: SourceRootId = SourceRootId(0); 16pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -24,6 +24,14 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
24 (db, fixture.files[0]) 24 (db, fixture.files[0])
25 } 25 }
26 26
27 fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) {
28 let fixture = ChangeFixture::parse(ra_fixture);
29 let mut db = Self::default();
30 fixture.change.apply(&mut db);
31 assert!(fixture.file_position.is_none());
32 (db, fixture.files)
33 }
34
27 fn with_files(ra_fixture: &str) -> Self { 35 fn with_files(ra_fixture: &str) -> Self {
28 let fixture = ChangeFixture::parse(ra_fixture); 36 let fixture = ChangeFixture::parse(ra_fixture);
29 let mut db = Self::default(); 37 let mut db = Self::default();
@@ -73,7 +81,7 @@ pub struct ChangeFixture {
73 81
74impl ChangeFixture { 82impl ChangeFixture {
75 pub fn parse(ra_fixture: &str) -> ChangeFixture { 83 pub fn parse(ra_fixture: &str) -> ChangeFixture {
76 let fixture = Fixture::parse(ra_fixture); 84 let (mini_core, fixture) = Fixture::parse(ra_fixture);
77 let mut change = Change::new(); 85 let mut change = Change::new();
78 86
79 let mut files = Vec::new(); 87 let mut files = Vec::new();
@@ -158,6 +166,31 @@ impl ChangeFixture {
158 } 166 }
159 } 167 }
160 168
169 if let Some(mini_core) = mini_core {
170 let core_file = file_id;
171 file_id.0 += 1;
172
173 let mut fs = FileSet::default();
174 fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
175 roots.push(SourceRoot::new_library(fs));
176
177 change.change_file(core_file, Some(Arc::new(mini_core.source_code())));
178
179 let all_crates = crate_graph.crates_in_topological_order();
180
181 let core_crate = crate_graph.add_crate_root(
182 core_file,
183 Edition::Edition2021,
184 Some(CrateDisplayName::from_canonical_name("core".to_string())),
185 CfgOptions::default(),
186 Env::default(),
187 Vec::new(),
188 );
189
190 for krate in all_crates {
191 crate_graph.add_dep(krate, CrateName::new("core").unwrap(), core_crate).unwrap();
192 }
193 }
161 roots.push(SourceRoot::new_local(mem::take(&mut file_set))); 194 roots.push(SourceRoot::new_local(mem::take(&mut file_set)));
162 change.set_roots(roots); 195 change.set_roots(roots);
163 change.set_crate_graph(crate_graph); 196 change.set_crate_graph(crate_graph);
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index dc10a4d0f..197149c5e 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -127,7 +127,7 @@ impl HasSource for Impl {
127} 127}
128 128
129impl HasSource for TypeParam { 129impl HasSource for TypeParam {
130 type Ast = Either<ast::Trait, ast::TypeParam>; 130 type Ast = Either<ast::TypeParam, ast::Trait>;
131 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> { 131 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
132 let child_source = self.id.parent.child_source(db.upcast()); 132 let child_source = self.id.parent.child_source(db.upcast());
133 Some(child_source.map(|it| it[self.id.local_id].clone())) 133 Some(child_source.map(|it| it[self.id.local_id].clone()))
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 6933f6e3c..0f04b2bae 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -92,7 +92,7 @@ pub enum WherePredicateTypeTarget {
92 92
93#[derive(Default)] 93#[derive(Default)]
94pub(crate) struct SourceMap { 94pub(crate) struct SourceMap {
95 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>, 95 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::TypeParam, ast::Trait>>,
96 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, 96 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
97 const_params: ArenaMap<LocalConstParamId, ast::ConstParam>, 97 const_params: ArenaMap<LocalConstParamId, ast::ConstParam>,
98} 98}
@@ -199,7 +199,7 @@ impl GenericParams {
199 default: None, 199 default: None,
200 provenance: TypeParamProvenance::TraitSelf, 200 provenance: TypeParamProvenance::TraitSelf,
201 }); 201 });
202 sm.type_params.insert(self_param_id, Either::Left(src.value.clone())); 202 sm.type_params.insert(self_param_id, Either::Right(src.value.clone()));
203 // add super traits as bounds on Self 203 // add super traits as bounds on Self
204 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar 204 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
205 let self_param = TypeRef::Path(name![Self].into()); 205 let self_param = TypeRef::Path(name![Self].into());
@@ -277,7 +277,7 @@ impl GenericParams {
277 provenance: TypeParamProvenance::TypeParamList, 277 provenance: TypeParamProvenance::TypeParamList,
278 }; 278 };
279 let param_id = self.types.alloc(param); 279 let param_id = self.types.alloc(param);
280 sm.type_params.insert(param_id, Either::Right(type_param.clone())); 280 sm.type_params.insert(param_id, Either::Left(type_param.clone()));
281 281
282 let type_ref = TypeRef::Path(name.into()); 282 let type_ref = TypeRef::Path(name.into());
283 self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref)); 283 self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref));
@@ -413,7 +413,7 @@ impl GenericParams {
413} 413}
414 414
415impl HasChildSource<LocalTypeParamId> for GenericDefId { 415impl HasChildSource<LocalTypeParamId> for GenericDefId {
416 type Value = Either<ast::Trait, ast::TypeParam>; 416 type Value = Either<ast::TypeParam, ast::Trait>;
417 fn child_source( 417 fn child_source(
418 &self, 418 &self,
419 db: &dyn DefDatabase, 419 db: &dyn DefDatabase,
@@ -449,7 +449,7 @@ impl ChildBySource for GenericDefId {
449 let sm = sm.as_ref(); 449 let sm = sm.as_ref();
450 for (local_id, src) in sm.value.type_params.iter() { 450 for (local_id, src) in sm.value.type_params.iter() {
451 let id = TypeParamId { parent: *self, local_id }; 451 let id = TypeParamId { parent: *self, local_id };
452 if let Either::Right(type_param) = src { 452 if let Either::Left(type_param) = src {
453 res[keys::TYPE_PARAM].insert(sm.with_value(type_param.clone()), id) 453 res[keys::TYPE_PARAM].insert(sm.with_value(type_param.clone()), id)
454 } 454 }
455 } 455 }
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 3f90bda74..5b1386406 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -674,7 +674,7 @@ impl<'a> Ctx<'a> {
674 default: None, 674 default: None,
675 provenance: TypeParamProvenance::TraitSelf, 675 provenance: TypeParamProvenance::TraitSelf,
676 }); 676 });
677 sm.type_params.insert(self_param_id, Either::Left(trait_def.clone())); 677 sm.type_params.insert(self_param_id, Either::Right(trait_def.clone()));
678 // add super traits as bounds on Self 678 // add super traits as bounds on Self
679 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar 679 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
680 let self_param = TypeRef::Path(name![Self].into()); 680 let self_param = TypeRef::Path(name![Self].into());
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 6fab58f15..fc2c50fb8 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1992,8 +1992,8 @@ mod tests {
1992 collector.def_map 1992 collector.def_map
1993 } 1993 }
1994 1994
1995 fn do_resolve(code: &str) -> DefMap { 1995 fn do_resolve(not_ra_fixture: &str) -> DefMap {
1996 let (db, _file_id) = TestDB::with_single_file(code); 1996 let (db, _file_id) = TestDB::with_single_file(not_ra_fixture);
1997 let krate = db.test_crate(); 1997 let krate = db.test_crate();
1998 1998
1999 let edition = db.crate_graph()[krate].edition; 1999 let edition = db.crate_graph()[krate].edition;
@@ -2005,24 +2005,37 @@ mod tests {
2005 fn test_macro_expand_will_stop_1() { 2005 fn test_macro_expand_will_stop_1() {
2006 do_resolve( 2006 do_resolve(
2007 r#" 2007 r#"
2008 macro_rules! foo { 2008macro_rules! foo {
2009 ($($ty:ty)*) => { foo!($($ty)*); } 2009 ($($ty:ty)*) => { foo!($($ty)*); }
2010 } 2010}
2011 foo!(KABOOM); 2011foo!(KABOOM);
2012 "#, 2012"#,
2013 );
2014 do_resolve(
2015 r#"
2016macro_rules! foo {
2017 ($($ty:ty)*) => { foo!(() $($ty)*); }
2018}
2019foo!(KABOOM);
2020"#,
2013 ); 2021 );
2014 } 2022 }
2015 2023
2016 #[ignore] // this test does succeed, but takes quite a while :/ 2024 #[ignore]
2017 #[test] 2025 #[test]
2018 fn test_macro_expand_will_stop_2() { 2026 fn test_macro_expand_will_stop_2() {
2027 // FIXME: this test does succeed, but takes quite a while: 90 seconds in
2028 // the release mode. That's why the argument is not an ra_fixture --
2029 // otherwise injection highlighting gets stuck.
2030 //
2031 // We need to find a way to fail this faster.
2019 do_resolve( 2032 do_resolve(
2020 r#" 2033 r#"
2021 macro_rules! foo { 2034macro_rules! foo {
2022 ($($ty:ty)*) => { foo!($($ty)* $($ty)*); } 2035 ($($ty:ty)*) => { foo!($($ty)* $($ty)*); }
2023 } 2036}
2024 foo!(KABOOM); 2037foo!(KABOOM);
2025 "#, 2038"#,
2026 ); 2039 );
2027 } 2040 }
2028} 2041}
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index a1894e8d8..74129eb21 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -20,7 +20,7 @@ rustc-hash = "1.1.0"
20scoped-tls = "1" 20scoped-tls = "1"
21chalk-solve = { version = "0.68", default-features = false } 21chalk-solve = { version = "0.68", default-features = false }
22chalk-ir = "0.68" 22chalk-ir = "0.68"
23chalk-recursive = "0.68" 23chalk-recursive = { version = "0.68", default-features = false }
24la-arena = { version = "0.2.0", path = "../../lib/arena" } 24la-arena = { version = "0.2.0", path = "../../lib/arena" }
25once_cell = { version = "1.5.0" } 25once_cell = { version = "1.5.0" }
26 26
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 71047703d..713b74165 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -23,38 +23,29 @@ fn infer_block_expr_type_mismatch() {
23fn coerce_places() { 23fn coerce_places() {
24 check_infer( 24 check_infer(
25 r#" 25 r#"
26 struct S<T> { a: T } 26//- minicore: coerce_unsized
27struct S<T> { a: T }
27 28
28 fn f<T>(_: &[T]) -> T { loop {} } 29fn f<T>(_: &[T]) -> T { loop {} }
29 fn g<T>(_: S<&[T]>) -> T { loop {} } 30fn g<T>(_: S<&[T]>) -> T { loop {} }
30 31
31 fn gen<T>() -> *mut [T; 2] { loop {} } 32fn gen<T>() -> *mut [T; 2] { loop {} }
32 fn test1<U>() -> *mut [U] { 33fn test1<U>() -> *mut [U] {
33 gen() 34 gen()
34 } 35}
35
36 fn test2() {
37 let arr: &[u8; 1] = &[1];
38
39 let a: &[_] = arr;
40 let b = f(arr);
41 let c: &[_] = { arr };
42 let d = g(S { a: arr });
43 let e: [&[_]; 1] = [arr];
44 let f: [&[_]; 2] = [arr; 2];
45 let g: (&[_], &[_]) = (arr, arr);
46 }
47 36
48 #[lang = "sized"] 37fn test2() {
49 pub trait Sized {} 38 let arr: &[u8; 1] = &[1];
50 #[lang = "unsize"]
51 pub trait Unsize<T: ?Sized> {}
52 #[lang = "coerce_unsized"]
53 pub trait CoerceUnsized<T> {}
54 39
55 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} 40 let a: &[_] = arr;
56 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} 41 let b = f(arr);
57 "#, 42 let c: &[_] = { arr };
43 let d = g(S { a: arr });
44 let e: [&[_]; 1] = [arr];
45 let f: [&[_]; 2] = [arr; 2];
46 let g: (&[_], &[_]) = (arr, arr);
47}
48"#,
58 expect![[r#" 49 expect![[r#"
59 30..31 '_': &[T] 50 30..31 '_': &[T]
60 44..55 '{ loop {} }': T 51 44..55 '{ loop {} }': T
@@ -131,60 +122,52 @@ fn infer_let_stmt_coerce() {
131fn infer_custom_coerce_unsized() { 122fn infer_custom_coerce_unsized() {
132 check_infer( 123 check_infer(
133 r#" 124 r#"
134 struct A<T: ?Sized>(*const T); 125//- minicore: coerce_unsized
135 struct B<T: ?Sized>(*const T); 126use core::{marker::Unsize, ops::CoerceUnsized};
136 struct C<T: ?Sized> { inner: *const T }
137 127
138 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {} 128struct A<T: ?Sized>(*const T);
139 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {} 129struct B<T: ?Sized>(*const T);
130struct C<T: ?Sized> { inner: *const T }
140 131
141 fn foo1<T>(x: A<[T]>) -> A<[T]> { x } 132impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
142 fn foo2<T>(x: B<[T]>) -> B<[T]> { x } 133impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}
143 fn foo3<T>(x: C<[T]>) -> C<[T]> { x }
144 134
145 fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) { 135fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
146 let d = foo1(a); 136fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
147 let e = foo2(b); 137fn foo3<T>(x: C<[T]>) -> C<[T]> { x }
148 let f = foo3(c);
149 }
150
151
152 #[lang = "sized"]
153 pub trait Sized {}
154 #[lang = "unsize"]
155 pub trait Unsize<T: ?Sized> {}
156 #[lang = "coerce_unsized"]
157 pub trait CoerceUnsized<T> {}
158 138
159 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} 139fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
160 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} 140 let d = foo1(a);
161 "#, 141 let e = foo2(b);
142 let f = foo3(c);
143}
144"#,
162 expect![[r#" 145 expect![[r#"
163 257..258 'x': A<[T]> 146 306..307 'x': A<[T]>
164 278..283 '{ x }': A<[T]> 147 327..332 '{ x }': A<[T]>
165 280..281 'x': A<[T]> 148 329..330 'x': A<[T]>
166 295..296 'x': B<[T]> 149 344..345 'x': B<[T]>
167 316..321 '{ x }': B<[T]> 150 365..370 '{ x }': B<[T]>
168 318..319 'x': B<[T]> 151 367..368 'x': B<[T]>
169 333..334 'x': C<[T]> 152 382..383 'x': C<[T]>
170 354..359 '{ x }': C<[T]> 153 403..408 '{ x }': C<[T]>
171 356..357 'x': C<[T]> 154 405..406 'x': C<[T]>
172 369..370 'a': A<[u8; 2]> 155 418..419 'a': A<[u8; 2]>
173 384..385 'b': B<[u8; 2]> 156 433..434 'b': B<[u8; 2]>
174 399..400 'c': C<[u8; 2]> 157 448..449 'c': C<[u8; 2]>
175 414..480 '{ ...(c); }': () 158 463..529 '{ ...(c); }': ()
176 424..425 'd': A<[{unknown}]> 159 473..474 'd': A<[{unknown}]>
177 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> 160 477..481 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]>
178 428..435 'foo1(a)': A<[{unknown}]> 161 477..484 'foo1(a)': A<[{unknown}]>
179 433..434 'a': A<[u8; 2]> 162 482..483 'a': A<[u8; 2]>
180 445..446 'e': B<[u8]> 163 494..495 'e': B<[u8]>
181 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> 164 498..502 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]>
182 449..456 'foo2(b)': B<[u8]> 165 498..505 'foo2(b)': B<[u8]>
183 454..455 'b': B<[u8; 2]> 166 503..504 'b': B<[u8; 2]>
184 466..467 'f': C<[u8]> 167 515..516 'f': C<[u8]>
185 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> 168 519..523 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]>
186 470..477 'foo3(c)': C<[u8]> 169 519..526 'foo3(c)': C<[u8]>
187 475..476 'c': C<[u8; 2]> 170 524..525 'c': C<[u8; 2]>
188 "#]], 171 "#]],
189 ); 172 );
190} 173}
@@ -193,21 +176,16 @@ fn infer_custom_coerce_unsized() {
193fn infer_if_coerce() { 176fn infer_if_coerce() {
194 check_infer( 177 check_infer(
195 r#" 178 r#"
196 fn foo<T>(x: &[T]) -> &[T] { loop {} } 179//- minicore: unsize
197 fn test() { 180fn foo<T>(x: &[T]) -> &[T] { loop {} }
198 let x = if true { 181fn test() {
199 foo(&[1]) 182 let x = if true {
200 } else { 183 foo(&[1])
201 &[1] 184 } else {
202 }; 185 &[1]
203 } 186 };
204 187}
205 188"#,
206 #[lang = "sized"]
207 pub trait Sized {}
208 #[lang = "unsize"]
209 pub trait Unsize<T: ?Sized> {}
210 "#,
211 expect![[r#" 189 expect![[r#"
212 10..11 'x': &[T] 190 10..11 'x': &[T]
213 27..38 '{ loop {} }': &[T] 191 27..38 '{ loop {} }': &[T]
@@ -235,25 +213,16 @@ fn infer_if_coerce() {
235fn infer_if_else_coerce() { 213fn infer_if_else_coerce() {
236 check_infer( 214 check_infer(
237 r#" 215 r#"
238 fn foo<T>(x: &[T]) -> &[T] { loop {} } 216//- minicore: coerce_unsized
239 fn test() { 217fn foo<T>(x: &[T]) -> &[T] { loop {} }
240 let x = if true { 218fn test() {
241 &[1] 219 let x = if true {
242 } else { 220 &[1]
243 foo(&[1]) 221 } else {
244 }; 222 foo(&[1])
245 } 223 };
246 224}
247 #[lang = "sized"] 225"#,
248 pub trait Sized {}
249 #[lang = "unsize"]
250 pub trait Unsize<T: ?Sized> {}
251 #[lang = "coerce_unsized"]
252 pub trait CoerceUnsized<T> {}
253
254 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
255 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
256 "#,
257 expect![[r#" 226 expect![[r#"
258 10..11 'x': &[T] 227 10..11 'x': &[T]
259 27..38 '{ loop {} }': &[T] 228 27..38 '{ loop {} }': &[T]
@@ -281,20 +250,16 @@ fn infer_if_else_coerce() {
281fn infer_match_first_coerce() { 250fn infer_match_first_coerce() {
282 check_infer( 251 check_infer(
283 r#" 252 r#"
284 fn foo<T>(x: &[T]) -> &[T] { loop {} } 253//- minicore: unsize
285 fn test(i: i32) { 254fn foo<T>(x: &[T]) -> &[T] { loop {} }
286 let x = match i { 255fn test(i: i32) {
287 2 => foo(&[2]), 256 let x = match i {
288 1 => &[1], 257 2 => foo(&[2]),
289 _ => &[3], 258 1 => &[1],
290 }; 259 _ => &[3],
291 } 260 };
292 261}
293 #[lang = "sized"] 262"#,
294 pub trait Sized {}
295 #[lang = "unsize"]
296 pub trait Unsize<T: ?Sized> {}
297 "#,
298 expect![[r#" 263 expect![[r#"
299 10..11 'x': &[T] 264 10..11 'x': &[T]
300 27..38 '{ loop {} }': &[T] 265 27..38 '{ loop {} }': &[T]
@@ -329,25 +294,16 @@ fn infer_match_first_coerce() {
329fn infer_match_second_coerce() { 294fn infer_match_second_coerce() {
330 check_infer( 295 check_infer(
331 r#" 296 r#"
332 fn foo<T>(x: &[T]) -> &[T] { loop {} } 297//- minicore: coerce_unsized
333 fn test(i: i32) { 298fn foo<T>(x: &[T]) -> &[T] { loop {} }
334 let x = match i { 299fn test(i: i32) {
335 1 => &[1], 300 let x = match i {
336 2 => foo(&[2]), 301 1 => &[1],
337 _ => &[3], 302 2 => foo(&[2]),
338 }; 303 _ => &[3],
339 } 304 };
340 305}
341 #[lang = "sized"] 306"#,
342 pub trait Sized {}
343 #[lang = "unsize"]
344 pub trait Unsize<T: ?Sized> {}
345 #[lang = "coerce_unsized"]
346 pub trait CoerceUnsized<T> {}
347
348 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
349 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
350 "#,
351 expect![[r#" 307 expect![[r#"
352 10..11 'x': &[T] 308 10..11 'x': &[T]
353 27..38 '{ loop {} }': &[T] 309 27..38 '{ loop {} }': &[T]
@@ -470,15 +426,15 @@ fn coerce_autoderef() {
470#[test] 426#[test]
471fn coerce_autoderef_generic() { 427fn coerce_autoderef_generic() {
472 check_infer_with_mismatches( 428 check_infer_with_mismatches(
473 r" 429 r#"
474 struct Foo; 430struct Foo;
475 fn takes_ref<T>(x: &T) -> T { *x } 431fn takes_ref<T>(x: &T) -> T { *x }
476 fn test() { 432fn test() {
477 takes_ref(&Foo); 433 takes_ref(&Foo);
478 takes_ref(&&Foo); 434 takes_ref(&&Foo);
479 takes_ref(&&&Foo); 435 takes_ref(&&&Foo);
480 } 436}
481 ", 437"#,
482 expect![[r" 438 expect![[r"
483 28..29 'x': &T 439 28..29 'x': &T
484 40..46 '{ *x }': T 440 40..46 '{ *x }': T
@@ -508,30 +464,29 @@ fn coerce_autoderef_generic() {
508fn coerce_autoderef_block() { 464fn coerce_autoderef_block() {
509 check_infer_with_mismatches( 465 check_infer_with_mismatches(
510 r#" 466 r#"
511 struct String {} 467//- minicore: deref
512 #[lang = "deref"] 468struct String {}
513 trait Deref { type Target; } 469impl core::ops::Deref for String { type Target = str; }
514 impl Deref for String { type Target = str; } 470fn takes_ref_str(x: &str) {}
515 fn takes_ref_str(x: &str) {} 471fn returns_string() -> String { loop {} }
516 fn returns_string() -> String { loop {} } 472fn test() {
517 fn test() { 473 takes_ref_str(&{ returns_string() });
518 takes_ref_str(&{ returns_string() }); 474}
519 } 475"#,
520 "#, 476 expect![[r#"
521 expect![[r" 477 90..91 'x': &str
522 126..127 'x': &str 478 99..101 '{}': ()
523 135..137 '{}': () 479 132..143 '{ loop {} }': String
524 168..179 '{ loop {} }': String 480 134..141 'loop {}': !
525 170..177 'loop {}': ! 481 139..141 '{}': ()
526 175..177 '{}': () 482 154..199 '{ ... }); }': ()
527 190..235 '{ ... }); }': () 483 160..173 'takes_ref_str': fn takes_ref_str(&str)
528 196..209 'takes_ref_str': fn takes_ref_str(&str) 484 160..196 'takes_...g() })': ()
529 196..232 'takes_...g() })': () 485 174..195 '&{ ret...ng() }': &String
530 210..231 '&{ ret...ng() }': &String 486 175..195 '{ retu...ng() }': String
531 211..231 '{ retu...ng() }': String 487 177..191 'returns_string': fn returns_string() -> String
532 213..227 'returns_string': fn returns_string() -> String 488 177..193 'return...ring()': String
533 213..229 'return...ring()': String 489 "#]],
534 "]],
535 ); 490 );
536} 491}
537 492
@@ -674,25 +629,19 @@ fn coerce_placeholder_ref() {
674fn coerce_unsize_array() { 629fn coerce_unsize_array() {
675 check_infer_with_mismatches( 630 check_infer_with_mismatches(
676 r#" 631 r#"
677 #[lang = "unsize"] 632//- minicore: coerce_unsized
678 pub trait Unsize<T> {} 633fn test() {
679 #[lang = "coerce_unsized"] 634 let f: &[usize] = &[1, 2, 3];
680 pub trait CoerceUnsized<T> {} 635}
681
682 impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
683
684 fn test() {
685 let f: &[usize] = &[1, 2, 3];
686 }
687 "#, 636 "#,
688 expect![[r#" 637 expect![[r#"
689 161..198 '{ ... 3]; }': () 638 10..47 '{ ... 3]; }': ()
690 171..172 'f': &[usize] 639 20..21 'f': &[usize]
691 185..195 '&[1, 2, 3]': &[usize; 3] 640 34..44 '&[1, 2, 3]': &[usize; 3]
692 186..195 '[1, 2, 3]': [usize; 3] 641 35..44 '[1, 2, 3]': [usize; 3]
693 187..188 '1': usize 642 36..37 '1': usize
694 190..191 '2': usize 643 39..40 '2': usize
695 193..194 '3': usize 644 42..43 '3': usize
696 "#]], 645 "#]],
697 ); 646 );
698} 647}
@@ -701,93 +650,94 @@ fn coerce_unsize_array() {
701fn coerce_unsize_trait_object_simple() { 650fn coerce_unsize_trait_object_simple() {
702 check_infer_with_mismatches( 651 check_infer_with_mismatches(
703 r#" 652 r#"
704 #[lang = "sized"] 653//- minicore: coerce_unsized
705 pub trait Sized {} 654trait Foo<T, U> {}
706 #[lang = "unsize"] 655trait Bar<U, T, X>: Foo<T, U> {}
707 pub trait Unsize<T> {} 656trait Baz<T, X>: Bar<usize, T, X> {}
708 #[lang = "coerce_unsized"] 657
709 pub trait CoerceUnsized<T> {} 658struct S<T, X>;
710 659impl<T, X> Foo<T, usize> for S<T, X> {}
711 impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} 660impl<T, X> Bar<usize, T, X> for S<T, X> {}
712 661impl<T, X> Baz<T, X> for S<T, X> {}
713 trait Foo<T, U> {} 662
714 trait Bar<U, T, X>: Foo<T, U> {} 663fn test() {
715 trait Baz<T, X>: Bar<usize, T, X> {} 664 let obj: &dyn Baz<i8, i16> = &S;
716 665 let obj: &dyn Bar<_, i8, i16> = &S;
717 struct S<T, X>; 666 let obj: &dyn Foo<i8, _> = &S;
718 impl<T, X> Foo<T, usize> for S<T, X> {} 667}
719 impl<T, X> Bar<usize, T, X> for S<T, X> {} 668"#,
720 impl<T, X> Baz<T, X> for S<T, X> {} 669 expect![[r#"
721 670 236..351 '{ ... &S; }': ()
722 fn test() { 671 246..249 'obj': &dyn Baz<i8, i16>
723 let obj: &dyn Baz<i8, i16> = &S; 672 271..273 '&S': &S<i8, i16>
724 let obj: &dyn Bar<_, i8, i16> = &S; 673 272..273 'S': S<i8, i16>
725 let obj: &dyn Foo<i8, _> = &S; 674 283..286 'obj': &dyn Bar<usize, i8, i16>
726 } 675 311..313 '&S': &S<i8, i16>
727 "#, 676 312..313 'S': S<i8, i16>
728 expect![[r" 677 323..326 'obj': &dyn Foo<i8, usize>
729 424..539 '{ ... &S; }': () 678 346..348 '&S': &S<i8, {unknown}>
730 434..437 'obj': &dyn Baz<i8, i16> 679 347..348 'S': S<i8, {unknown}>
731 459..461 '&S': &S<i8, i16> 680 "#]],
732 460..461 'S': S<i8, i16>
733 471..474 'obj': &dyn Bar<usize, i8, i16>
734 499..501 '&S': &S<i8, i16>
735 500..501 'S': S<i8, i16>
736 511..514 'obj': &dyn Foo<i8, usize>
737 534..536 '&S': &S<i8, {unknown}>
738 535..536 'S': S<i8, {unknown}>
739 "]],
740 ); 681 );
741} 682}
742 683
743#[test] 684#[test]
744// The rust reference says this should be possible, but rustc doesn't implement
745// it. We used to support it, but Chalk doesn't.
746#[ignore]
747fn coerce_unsize_trait_object_to_trait_object() { 685fn coerce_unsize_trait_object_to_trait_object() {
686 // FIXME: The rust reference says this should be possible, but rustc doesn't
687 // implement it. We used to support it, but Chalk doesn't. Here's the
688 // correct expect:
689 //
690 // 424..609 '{ ...bj2; }': ()
691 // 434..437 'obj': &dyn Baz<i8, i16>
692 // 459..461 '&S': &S<i8, i16>
693 // 460..461 'S': S<i8, i16>
694 // 471..474 'obj': &dyn Bar<usize, i8, i16>
695 // 496..499 'obj': &dyn Baz<i8, i16>
696 // 509..512 'obj': &dyn Foo<i8, usize>
697 // 531..534 'obj': &dyn Bar<usize, i8, i16>
698 // 544..548 'obj2': &dyn Baz<i8, i16>
699 // 570..572 '&S': &S<i8, i16>
700 // 571..572 'S': S<i8, i16>
701 // 582..583 '_': &dyn Foo<i8, usize>
702 // 602..606 'obj2': &dyn Baz<i8, i16>
748 check_infer_with_mismatches( 703 check_infer_with_mismatches(
749 r#" 704 r#"
750 #[lang = "sized"] 705//- minicore: coerce_unsized
751 pub trait Sized {} 706trait Foo<T, U> {}
752 #[lang = "unsize"] 707trait Bar<U, T, X>: Foo<T, U> {}
753 pub trait Unsize<T> {} 708trait Baz<T, X>: Bar<usize, T, X> {}
754 #[lang = "coerce_unsized"] 709
755 pub trait CoerceUnsized<T> {} 710struct S<T, X>;
756 711impl<T, X> Foo<T, usize> for S<T, X> {}
757 impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} 712impl<T, X> Bar<usize, T, X> for S<T, X> {}
758 713impl<T, X> Baz<T, X> for S<T, X> {}
759 trait Foo<T, U> {} 714
760 trait Bar<U, T, X>: Foo<T, U> {} 715fn test() {
761 trait Baz<T, X>: Bar<usize, T, X> {} 716 let obj: &dyn Baz<i8, i16> = &S;
762 717 let obj: &dyn Bar<_, _, _> = obj;
763 struct S<T, X>; 718 let obj: &dyn Foo<_, _> = obj;
764 impl<T, X> Foo<T, usize> for S<T, X> {} 719 let obj2: &dyn Baz<i8, i16> = &S;
765 impl<T, X> Bar<usize, T, X> for S<T, X> {} 720 let _: &dyn Foo<_, _> = obj2;
766 impl<T, X> Baz<T, X> for S<T, X> {} 721}
767 722"#,
768 fn test() { 723 expect![[r#"
769 let obj: &dyn Baz<i8, i16> = &S; 724 236..421 '{ ...bj2; }': ()
770 let obj: &dyn Bar<_, _, _> = obj; 725 246..249 'obj': &dyn Baz<i8, i16>
771 let obj: &dyn Foo<_, _> = obj; 726 271..273 '&S': &S<i8, i16>
772 let obj2: &dyn Baz<i8, i16> = &S; 727 272..273 'S': S<i8, i16>
773 let _: &dyn Foo<_, _> = obj2; 728 283..286 'obj': &dyn Bar<{unknown}, {unknown}, {unknown}>
774 } 729 308..311 'obj': &dyn Baz<i8, i16>
775 "#, 730 321..324 'obj': &dyn Foo<{unknown}, {unknown}>
776 expect![[r" 731 343..346 'obj': &dyn Bar<{unknown}, {unknown}, {unknown}>
777 424..609 '{ ...bj2; }': () 732 356..360 'obj2': &dyn Baz<i8, i16>
778 434..437 'obj': &dyn Baz<i8, i16> 733 382..384 '&S': &S<i8, i16>
779 459..461 '&S': &S<i8, i16> 734 383..384 'S': S<i8, i16>
780 460..461 'S': S<i8, i16> 735 394..395 '_': &dyn Foo<{unknown}, {unknown}>
781 471..474 'obj': &dyn Bar<usize, i8, i16> 736 414..418 'obj2': &dyn Baz<i8, i16>
782 496..499 'obj': &dyn Baz<i8, i16> 737 308..311: expected &dyn Bar<{unknown}, {unknown}, {unknown}>, got &dyn Baz<i8, i16>
783 509..512 'obj': &dyn Foo<i8, usize> 738 343..346: expected &dyn Foo<{unknown}, {unknown}>, got &dyn Bar<{unknown}, {unknown}, {unknown}>
784 531..534 'obj': &dyn Bar<usize, i8, i16> 739 414..418: expected &dyn Foo<{unknown}, {unknown}>, got &dyn Baz<i8, i16>
785 544..548 'obj2': &dyn Baz<i8, i16> 740 "#]],
786 570..572 '&S': &S<i8, i16>
787 571..572 'S': S<i8, i16>
788 582..583 '_': &dyn Foo<i8, usize>
789 602..606 'obj2': &dyn Baz<i8, i16>
790 "]],
791 ); 741 );
792} 742}
793 743
@@ -795,40 +745,32 @@ fn coerce_unsize_trait_object_to_trait_object() {
795fn coerce_unsize_super_trait_cycle() { 745fn coerce_unsize_super_trait_cycle() {
796 check_infer_with_mismatches( 746 check_infer_with_mismatches(
797 r#" 747 r#"
798 #[lang = "sized"] 748//- minicore: coerce_unsized
799 pub trait Sized {} 749trait A {}
800 #[lang = "unsize"] 750trait B: C + A {}
801 pub trait Unsize<T> {} 751trait C: B {}
802 #[lang = "coerce_unsized"] 752trait D: C
803 pub trait CoerceUnsized<T> {} 753
804 754struct S;
805 impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} 755impl A for S {}
806 756impl B for S {}
807 trait A {} 757impl C for S {}
808 trait B: C + A {} 758impl D for S {}
809 trait C: B {} 759
810 trait D: C 760fn test() {
811 761 let obj: &dyn D = &S;
812 struct S; 762 let obj: &dyn A = &S;
813 impl A for S {} 763}
814 impl B for S {} 764"#,
815 impl C for S {} 765 expect![[r#"
816 impl D for S {} 766 140..195 '{ ... &S; }': ()
817 767 150..153 'obj': &dyn D
818 fn test() { 768 164..166 '&S': &S
819 let obj: &dyn D = &S; 769 165..166 'S': S
820 let obj: &dyn A = &S; 770 176..179 'obj': &dyn A
821 } 771 190..192 '&S': &S
822 "#, 772 191..192 'S': S
823 expect![[r" 773 "#]],
824 328..383 '{ ... &S; }': ()
825 338..341 'obj': &dyn D
826 352..354 '&S': &S
827 353..354 'S': S
828 364..367 'obj': &dyn A
829 378..380 '&S': &S
830 379..380 'S': S
831 "]],
832 ); 774 );
833} 775}
834 776
@@ -837,41 +779,35 @@ fn coerce_unsize_generic() {
837 // FIXME: fix the type mismatches here 779 // FIXME: fix the type mismatches here
838 check_infer_with_mismatches( 780 check_infer_with_mismatches(
839 r#" 781 r#"
840 #[lang = "unsize"] 782//- minicore: coerce_unsized
841 pub trait Unsize<T> {} 783struct Foo<T> { t: T };
842 #[lang = "coerce_unsized"] 784struct Bar<T>(Foo<T>);
843 pub trait CoerceUnsized<T> {}
844
845 impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
846 785
847 struct Foo<T> { t: T }; 786fn test() {
848 struct Bar<T>(Foo<T>); 787 let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
849 788 let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
850 fn test() { 789}
851 let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; 790"#,
852 let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
853 }
854 "#,
855 expect![[r#" 791 expect![[r#"
856 209..317 '{ ... }); }': () 792 58..166 '{ ... }); }': ()
857 219..220 '_': &Foo<[usize]> 793 68..69 '_': &Foo<[usize]>
858 238..259 '&Foo {..., 3] }': &Foo<[usize]> 794 87..108 '&Foo {..., 3] }': &Foo<[usize]>
859 239..259 'Foo { ..., 3] }': Foo<[usize]> 795 88..108 'Foo { ..., 3] }': Foo<[usize]>
860 248..257 '[1, 2, 3]': [usize; 3] 796 97..106 '[1, 2, 3]': [usize; 3]
861 249..250 '1': usize 797 98..99 '1': usize
862 252..253 '2': usize 798 101..102 '2': usize
863 255..256 '3': usize 799 104..105 '3': usize
864 269..270 '_': &Bar<[usize]> 800 118..119 '_': &Bar<[usize]>
865 288..314 '&Bar(F... 3] })': &Bar<[i32; 3]> 801 137..163 '&Bar(F... 3] })': &Bar<[i32; 3]>
866 289..292 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]> 802 138..141 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]>
867 289..314 'Bar(Fo... 3] })': Bar<[i32; 3]> 803 138..163 'Bar(Fo... 3] })': Bar<[i32; 3]>
868 293..313 'Foo { ..., 3] }': Foo<[i32; 3]> 804 142..162 'Foo { ..., 3] }': Foo<[i32; 3]>
869 302..311 '[1, 2, 3]': [i32; 3] 805 151..160 '[1, 2, 3]': [i32; 3]
870 303..304 '1': i32 806 152..153 '1': i32
871 306..307 '2': i32 807 155..156 '2': i32
872 309..310 '3': i32 808 158..159 '3': i32
873 248..257: expected [usize], got [usize; 3] 809 97..106: expected [usize], got [usize; 3]
874 288..314: expected &Bar<[usize]>, got &Bar<[i32; 3]> 810 137..163: expected &Bar<[usize]>, got &Bar<[i32; 3]>
875 "#]], 811 "#]],
876 ); 812 );
877} 813}
@@ -881,15 +817,7 @@ fn coerce_unsize_apit() {
881 // FIXME: #8984 817 // FIXME: #8984
882 check_infer_with_mismatches( 818 check_infer_with_mismatches(
883 r#" 819 r#"
884#[lang = "sized"] 820//- minicore: coerce_unsized
885pub trait Sized {}
886#[lang = "unsize"]
887pub trait Unsize<T> {}
888#[lang = "coerce_unsized"]
889pub trait CoerceUnsized<T> {}
890
891impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
892
893trait Foo {} 821trait Foo {}
894 822
895fn test(f: impl Foo) { 823fn test(f: impl Foo) {
@@ -897,12 +825,12 @@ fn test(f: impl Foo) {
897} 825}
898 "#, 826 "#,
899 expect![[r#" 827 expect![[r#"
900 210..211 'f': impl Foo 828 22..23 'f': impl Foo
901 223..252 '{ ... &f; }': () 829 35..64 '{ ... &f; }': ()
902 233..234 '_': &dyn Foo 830 45..46 '_': &dyn Foo
903 247..249 '&f': &impl Foo 831 59..61 '&f': &impl Foo
904 248..249 'f': impl Foo 832 60..61 'f': impl Foo
905 247..249: expected &dyn Foo, got &impl Foo 833 59..61: expected &dyn Foo, got &impl Foo
906 "#]], 834 "#]],
907 ); 835 );
908} 836}
@@ -998,15 +926,7 @@ fn main() {
998fn coerce_unsize_expected_type() { 926fn coerce_unsize_expected_type() {
999 check_no_mismatches( 927 check_no_mismatches(
1000 r#" 928 r#"
1001#[lang = "sized"] 929//- minicore: coerce_unsized
1002pub trait Sized {}
1003#[lang = "unsize"]
1004pub trait Unsize<T> {}
1005#[lang = "coerce_unsized"]
1006pub trait CoerceUnsized<T> {}
1007
1008impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
1009
1010fn main() { 930fn main() {
1011 let foo: &[u32] = &[1, 2]; 931 let foo: &[u32] = &[1, 2];
1012 let foo: &[u32] = match true { 932 let foo: &[u32] = match true {
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index f26b2c8a7..d9b5ee9cf 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -780,10 +780,7 @@ fn test() { (&S).foo(); }
780fn method_resolution_unsize_array() { 780fn method_resolution_unsize_array() {
781 check_types( 781 check_types(
782 r#" 782 r#"
783#[lang = "slice"] 783//- minicore: slice
784impl<T> [T] {
785 fn len(&self) -> usize { loop {} }
786}
787fn test() { 784fn test() {
788 let a = [1, 2, 3]; 785 let a = [1, 2, 3];
789 a.len(); 786 a.len();
@@ -1178,11 +1175,7 @@ fn main() {
1178fn autoderef_visibility_field() { 1175fn autoderef_visibility_field() {
1179 check_infer( 1176 check_infer(
1180 r#" 1177 r#"
1181#[lang = "deref"] 1178//- minicore: deref
1182pub trait Deref {
1183 type Target;
1184 fn deref(&self) -> &Self::Target;
1185}
1186mod a { 1179mod a {
1187 pub struct Foo(pub char); 1180 pub struct Foo(pub char);
1188 pub struct Bar(i32); 1181 pub struct Bar(i32);
@@ -1191,7 +1184,7 @@ mod a {
1191 Self(0) 1184 Self(0)
1192 } 1185 }
1193 } 1186 }
1194 impl super::Deref for Bar { 1187 impl core::ops::Deref for Bar {
1195 type Target = Foo; 1188 type Target = Foo;
1196 fn deref(&self) -> &Foo { 1189 fn deref(&self) -> &Foo {
1197 &Foo('z') 1190 &Foo('z')
@@ -1205,22 +1198,21 @@ mod b {
1205} 1198}
1206 "#, 1199 "#,
1207 expect![[r#" 1200 expect![[r#"
1208 67..71 'self': &Self 1201 107..138 '{ ... }': Bar
1209 200..231 '{ ... }': Bar 1202 121..125 'Self': Bar(i32) -> Bar
1210 214..218 'Self': Bar(i32) -> Bar 1203 121..128 'Self(0)': Bar
1211 214..221 'Self(0)': Bar 1204 126..127 '0': i32
1212 219..220 '0': i32 1205 226..230 'self': &Bar
1213 315..319 'self': &Bar 1206 240..273 '{ ... }': &Foo
1214 329..362 '{ ... }': &Foo 1207 254..263 '&Foo('z')': &Foo
1215 343..352 '&Foo('z')': &Foo 1208 255..258 'Foo': Foo(char) -> Foo
1216 344..347 'Foo': Foo(char) -> Foo 1209 255..263 'Foo('z')': Foo
1217 344..352 'Foo('z')': Foo 1210 259..262 ''z'': char
1218 348..351 ''z'': char 1211 303..350 '{ ... }': ()
1219 392..439 '{ ... }': () 1212 317..318 'x': char
1220 406..407 'x': char 1213 321..339 'super:...r::new': fn new() -> Bar
1221 410..428 'super:...r::new': fn new() -> Bar 1214 321..341 'super:...:new()': Bar
1222 410..430 'super:...:new()': Bar 1215 321..343 'super:...ew().0': char
1223 410..432 'super:...ew().0': char
1224 "#]], 1216 "#]],
1225 ) 1217 )
1226} 1218}
@@ -1230,11 +1222,7 @@ fn autoderef_visibility_method() {
1230 cov_mark::check!(autoderef_candidate_not_visible); 1222 cov_mark::check!(autoderef_candidate_not_visible);
1231 check_infer( 1223 check_infer(
1232 r#" 1224 r#"
1233#[lang = "deref"] 1225//- minicore: deref
1234pub trait Deref {
1235 type Target;
1236 fn deref(&self) -> &Self::Target;
1237}
1238mod a { 1226mod a {
1239 pub struct Foo(pub char); 1227 pub struct Foo(pub char);
1240 impl Foo { 1228 impl Foo {
@@ -1251,7 +1239,7 @@ mod a {
1251 self.0 1239 self.0
1252 } 1240 }
1253 } 1241 }
1254 impl super::Deref for Bar { 1242 impl core::ops::Deref for Bar {
1255 type Target = Foo; 1243 type Target = Foo;
1256 fn deref(&self) -> &Foo { 1244 fn deref(&self) -> &Foo {
1257 &Foo('z') 1245 &Foo('z')
@@ -1265,30 +1253,29 @@ mod b {
1265} 1253}
1266 "#, 1254 "#,
1267 expect![[r#" 1255 expect![[r#"
1268 67..71 'self': &Self 1256 75..79 'self': &Foo
1269 168..172 'self': &Foo 1257 89..119 '{ ... }': char
1270 182..212 '{ ... }': char 1258 103..107 'self': &Foo
1271 196..200 'self': &Foo 1259 103..109 'self.0': char
1272 196..202 'self.0': char 1260 195..226 '{ ... }': Bar
1273 288..319 '{ ... }': Bar 1261 209..213 'Self': Bar(i32) -> Bar
1274 302..306 'Self': Bar(i32) -> Bar 1262 209..216 'Self(0)': Bar
1275 302..309 'Self(0)': Bar 1263 214..215 '0': i32
1276 307..308 '0': i32 1264 245..249 'self': &Bar
1277 338..342 'self': &Bar 1265 258..288 '{ ... }': i32
1278 351..381 '{ ... }': i32 1266 272..276 'self': &Bar
1279 365..369 'self': &Bar 1267 272..278 'self.0': i32
1280 365..371 'self.0': i32 1268 376..380 'self': &Bar
1281 465..469 'self': &Bar 1269 390..423 '{ ... }': &Foo
1282 479..512 '{ ... }': &Foo 1270 404..413 '&Foo('z')': &Foo
1283 493..502 '&Foo('z')': &Foo 1271 405..408 'Foo': Foo(char) -> Foo
1284 494..497 'Foo': Foo(char) -> Foo 1272 405..413 'Foo('z')': Foo
1285 494..502 'Foo('z')': Foo 1273 409..412 ''z'': char
1286 498..501 ''z'': char 1274 453..506 '{ ... }': ()
1287 542..595 '{ ... }': () 1275 467..468 'x': char
1288 556..557 'x': char 1276 471..489 'super:...r::new': fn new() -> Bar
1289 560..578 'super:...r::new': fn new() -> Bar 1277 471..491 'super:...:new()': Bar
1290 560..580 'super:...:new()': Bar 1278 471..499 'super:...ango()': char
1291 560..588 'super:...ango()': char
1292 "#]], 1279 "#]],
1293 ) 1280 )
1294} 1281}
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 1019e783b..abd9c385a 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -927,35 +927,33 @@ fn issue_6628() {
927fn issue_6852() { 927fn issue_6852() {
928 check_infer( 928 check_infer(
929 r#" 929 r#"
930 #[lang = "deref"] 930//- minicore: deref
931 pub trait Deref { 931use core::ops::Deref;
932 type Target;
933 }
934 932
935 struct BufWriter {} 933struct BufWriter {}
936 934
937 struct Mutex<T> {} 935struct Mutex<T> {}
938 struct MutexGuard<'a, T> {} 936struct MutexGuard<'a, T> {}
939 impl<T> Mutex<T> { 937impl<T> Mutex<T> {
940 fn lock(&self) -> MutexGuard<'_, T> {} 938 fn lock(&self) -> MutexGuard<'_, T> {}
941 } 939}
942 impl<'a, T: 'a> Deref for MutexGuard<'a, T> { 940impl<'a, T: 'a> Deref for MutexGuard<'a, T> {
943 type Target = T; 941 type Target = T;
944 } 942}
945 fn flush(&self) { 943fn flush(&self) {
946 let w: &Mutex<BufWriter>; 944 let w: &Mutex<BufWriter>;
947 *(w.lock()); 945 *(w.lock());
948 } 946}
949 "#, 947"#,
950 expect![[r#" 948 expect![[r#"
951 156..160 'self': &Mutex<T> 949 123..127 'self': &Mutex<T>
952 183..185 '{}': () 950 150..152 '{}': ()
953 267..271 'self': &{unknown} 951 234..238 'self': &{unknown}
954 273..323 '{ ...()); }': () 952 240..290 '{ ...()); }': ()
955 283..284 'w': &Mutex<BufWriter> 953 250..251 'w': &Mutex<BufWriter>
956 309..320 '*(w.lock())': BufWriter 954 276..287 '*(w.lock())': BufWriter
957 311..312 'w': &Mutex<BufWriter> 955 278..279 'w': &Mutex<BufWriter>
958 311..319 'w.lock()': MutexGuard<BufWriter> 956 278..286 'w.lock()': MutexGuard<BufWriter>
959 "#]], 957 "#]],
960 ); 958 );
961} 959}
@@ -977,37 +975,33 @@ fn param_overrides_fn() {
977fn lifetime_from_chalk_during_deref() { 975fn lifetime_from_chalk_during_deref() {
978 check_types( 976 check_types(
979 r#" 977 r#"
980 #[lang = "deref"] 978//- minicore: deref
981 pub trait Deref { 979struct Box<T: ?Sized> {}
982 type Target; 980impl<T> core::ops::Deref for Box<T> {
983 } 981 type Target = T;
984
985 struct Box<T: ?Sized> {}
986 impl<T> Deref for Box<T> {
987 type Target = T;
988 982
989 fn deref(&self) -> &Self::Target { 983 fn deref(&self) -> &Self::Target {
990 loop {} 984 loop {}
991 } 985 }
992 } 986}
993 987
994 trait Iterator { 988trait Iterator {
995 type Item; 989 type Item;
996 } 990}
997 991
998 pub struct Iter<'a, T: 'a> { 992pub struct Iter<'a, T: 'a> {
999 inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>, 993 inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>,
1000 } 994}
1001 995
1002 trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> { 996trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> {
1003 fn clone_box(&self); 997 fn clone_box(&self);
1004 } 998}
1005 999
1006 fn clone_iter<T>(s: Iter<T>) { 1000fn clone_iter<T>(s: Iter<T>) {
1007 s.inner.clone_box(); 1001 s.inner.clone_box();
1008 //^^^^^^^^^^^^^^^^^^^ () 1002 //^^^^^^^^^^^^^^^^^^^ ()
1009 } 1003}
1010 "#, 1004"#,
1011 ) 1005 )
1012} 1006}
1013 1007
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 3418ed21e..68776f3c0 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -113,7 +113,7 @@ fn type_alias_in_struct_lit() {
113fn infer_ranges() { 113fn infer_ranges() {
114 check_types( 114 check_types(
115 r#" 115 r#"
116//- /main.rs crate:main deps:core 116//- minicore: range
117fn test() { 117fn test() {
118 let a = ..; 118 let a = ..;
119 let b = 1..; 119 let b = 1..;
@@ -125,32 +125,6 @@ fn test() {
125 let t = (a, b, c, d, e, f); 125 let t = (a, b, c, d, e, f);
126 t; 126 t;
127} //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>) 127} //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)
128
129//- /core.rs crate:core
130#[prelude_import] use prelude::*;
131mod prelude {}
132
133pub mod ops {
134 pub struct Range<Idx> {
135 pub start: Idx,
136 pub end: Idx,
137 }
138 pub struct RangeFrom<Idx> {
139 pub start: Idx,
140 }
141 struct RangeFull;
142 pub struct RangeInclusive<Idx> {
143 start: Idx,
144 end: Idx,
145 is_empty: u8,
146 }
147 pub struct RangeTo<Idx> {
148 pub end: Idx,
149 }
150 pub struct RangeToInclusive<Idx> {
151 pub end: Idx,
152 }
153}
154"#, 128"#,
155 ); 129 );
156} 130}
@@ -175,16 +149,17 @@ fn test() {
175fn infer_basics() { 149fn infer_basics() {
176 check_infer( 150 check_infer(
177 r#" 151 r#"
178 fn test(a: u32, b: isize, c: !, d: &str) { 152fn test(a: u32, b: isize, c: !, d: &str) {
179 a; 153 a;
180 b; 154 b;
181 c; 155 c;
182 d; 156 d;
183 1usize; 157 1usize;
184 1isize; 158 1isize;
185 "test"; 159 "test";
186 1.0f32; 160 1.0f32;
187 }"#, 161}
162"#,
188 expect![[r#" 163 expect![[r#"
189 8..9 'a': u32 164 8..9 'a': u32
190 16..17 'b': isize 165 16..17 'b': isize
@@ -207,15 +182,15 @@ fn infer_basics() {
207fn infer_let() { 182fn infer_let() {
208 check_infer( 183 check_infer(
209 r#" 184 r#"
210 fn test() { 185fn test() {
211 let a = 1isize; 186 let a = 1isize;
212 let b: usize = 1; 187 let b: usize = 1;
213 let c = b; 188 let c = b;
214 let d: u32; 189 let d: u32;
215 let e; 190 let e;
216 let f: i32 = e; 191 let f: i32 = e;
217 } 192}
218 "#, 193"#,
219 expect![[r#" 194 expect![[r#"
220 10..117 '{ ...= e; }': () 195 10..117 '{ ...= e; }': ()
221 20..21 'a': isize 196 20..21 'a': isize
@@ -236,17 +211,17 @@ fn infer_let() {
236fn infer_paths() { 211fn infer_paths() {
237 check_infer( 212 check_infer(
238 r#" 213 r#"
239 fn a() -> u32 { 1 } 214fn a() -> u32 { 1 }
240 215
241 mod b { 216mod b {
242 fn c() -> u32 { 1 } 217 fn c() -> u32 { 1 }
243 } 218}
244 219
245 fn test() { 220fn test() {
246 a(); 221 a();
247 b::c(); 222 b::c();
248 } 223}
249 "#, 224"#,
250 expect![[r#" 225 expect![[r#"
251 14..19 '{ 1 }': u32 226 14..19 '{ 1 }': u32
252 16..17 '1': u32 227 16..17 '1': u32
@@ -265,17 +240,17 @@ fn infer_paths() {
265fn infer_path_type() { 240fn infer_path_type() {
266 check_infer( 241 check_infer(
267 r#" 242 r#"
268 struct S; 243struct S;
269 244
270 impl S { 245impl S {
271 fn foo() -> i32 { 1 } 246 fn foo() -> i32 { 1 }
272 } 247}
273 248
274 fn test() { 249fn test() {
275 S::foo(); 250 S::foo();
276 <S>::foo(); 251 <S>::foo();
277 } 252}
278 "#, 253"#,
279 expect![[r#" 254 expect![[r#"
280 40..45 '{ 1 }': i32 255 40..45 '{ 1 }': i32
281 42..43 '1': i32 256 42..43 '1': i32
@@ -292,21 +267,21 @@ fn infer_path_type() {
292fn infer_struct() { 267fn infer_struct() {
293 check_infer( 268 check_infer(
294 r#" 269 r#"
295 struct A { 270struct A {
296 b: B, 271 b: B,
297 c: C, 272 c: C,
298 } 273}
299 struct B; 274struct B;
300 struct C(usize); 275struct C(usize);
301 276
302 fn test() { 277fn test() {
303 let c = C(1); 278 let c = C(1);
304 B; 279 B;
305 let a: A = A { b: B, c: C(1) }; 280 let a: A = A { b: B, c: C(1) };
306 a.b; 281 a.b;
307 a.c; 282 a.c;
308 } 283}
309 "#, 284"#,
310 expect![[r#" 285 expect![[r#"
311 71..153 '{ ...a.c; }': () 286 71..153 '{ ...a.c; }': ()
312 81..82 'c': C 287 81..82 'c': C
@@ -332,14 +307,15 @@ fn infer_struct() {
332fn infer_enum() { 307fn infer_enum() {
333 check_infer( 308 check_infer(
334 r#" 309 r#"
335 enum E { 310enum E {
336 V1 { field: u32 }, 311 V1 { field: u32 },
337 V2 312 V2
338 } 313}
339 fn test() { 314fn test() {
340 E::V1 { field: 1 }; 315 E::V1 { field: 1 };
341 E::V2; 316 E::V2;
342 }"#, 317}
318"#,
343 expect![[r#" 319 expect![[r#"
344 51..89 '{ ...:V2; }': () 320 51..89 '{ ...:V2; }': ()
345 57..75 'E::V1 ...d: 1 }': E 321 57..75 'E::V1 ...d: 1 }': E
@@ -353,23 +329,23 @@ fn infer_enum() {
353fn infer_union() { 329fn infer_union() {
354 check_infer( 330 check_infer(
355 r#" 331 r#"
356 union MyUnion { 332union MyUnion {
357 foo: u32, 333 foo: u32,
358 bar: f32, 334 bar: f32,
359 } 335}
360 336
361 fn test() { 337fn test() {
362 let u = MyUnion { foo: 0 }; 338 let u = MyUnion { foo: 0 };
363 unsafe { baz(u); } 339 unsafe { baz(u); }
364 let u = MyUnion { bar: 0.0 }; 340 let u = MyUnion { bar: 0.0 };
365 unsafe { baz(u); } 341 unsafe { baz(u); }
366 } 342}
367 343
368 unsafe fn baz(u: MyUnion) { 344unsafe fn baz(u: MyUnion) {
369 let inner = u.foo; 345 let inner = u.foo;
370 let inner = u.bar; 346 let inner = u.bar;
371 } 347}
372 "#, 348"#,
373 expect![[r#" 349 expect![[r#"
374 57..172 '{ ...); } }': () 350 57..172 '{ ...); } }': ()
375 67..68 'u': MyUnion 351 67..68 'u': MyUnion
@@ -404,19 +380,19 @@ fn infer_union() {
404fn infer_refs() { 380fn infer_refs() {
405 check_infer( 381 check_infer(
406 r#" 382 r#"
407 fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { 383fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
408 a; 384 a;
409 *a; 385 *a;
410 &a; 386 &a;
411 &mut a; 387 &mut a;
412 b; 388 b;
413 *b; 389 *b;
414 &b; 390 &b;
415 c; 391 c;
416 *c; 392 *c;
417 d; 393 d;
418 *d; 394 *d;
419 } 395}
420 "#, 396 "#,
421 expect![[r#" 397 expect![[r#"
422 8..9 'a': &u32 398 8..9 'a': &u32
@@ -450,11 +426,11 @@ fn infer_refs() {
450fn infer_raw_ref() { 426fn infer_raw_ref() {
451 check_infer( 427 check_infer(
452 r#" 428 r#"
453 fn test(a: i32) { 429fn test(a: i32) {
454 &raw mut a; 430 &raw mut a;
455 &raw const a; 431 &raw const a;
456 } 432}
457 "#, 433"#,
458 expect![[r#" 434 expect![[r#"
459 8..9 'a': i32 435 8..9 'a': i32
460 16..53 '{ ...t a; }': () 436 16..53 '{ ...t a; }': ()
@@ -524,26 +500,26 @@ h";
524fn infer_unary_op() { 500fn infer_unary_op() {
525 check_infer( 501 check_infer(
526 r#" 502 r#"
527 enum SomeType {} 503enum SomeType {}
528 504
529 fn test(x: SomeType) { 505fn test(x: SomeType) {
530 let b = false; 506 let b = false;
531 let c = !b; 507 let c = !b;
532 let a = 100; 508 let a = 100;
533 let d: i128 = -a; 509 let d: i128 = -a;
534 let e = -100; 510 let e = -100;
535 let f = !!!true; 511 let f = !!!true;
536 let g = !42; 512 let g = !42;
537 let h = !10u32; 513 let h = !10u32;
538 let j = !a; 514 let j = !a;
539 -3.14; 515 -3.14;
540 !3; 516 !3;
541 -x; 517 -x;
542 !x; 518 !x;
543 -"hello"; 519 -"hello";
544 !"hello"; 520 !"hello";
545 } 521}
546 "#, 522"#,
547 expect![[r#" 523 expect![[r#"
548 26..27 'x': SomeType 524 26..27 'x': SomeType
549 39..271 '{ ...lo"; }': () 525 39..271 '{ ...lo"; }': ()
@@ -594,19 +570,19 @@ fn infer_unary_op() {
594fn infer_backwards() { 570fn infer_backwards() {
595 check_infer( 571 check_infer(
596 r#" 572 r#"
597 fn takes_u32(x: u32) {} 573fn takes_u32(x: u32) {}
598 574
599 struct S { i32_field: i32 } 575struct S { i32_field: i32 }
600 576
601 fn test() -> &mut &f64 { 577fn test() -> &mut &f64 {
602 let a = unknown_function(); 578 let a = unknown_function();
603 takes_u32(a); 579 takes_u32(a);
604 let b = unknown_function(); 580 let b = unknown_function();
605 S { i32_field: b }; 581 S { i32_field: b };
606 let c = unknown_function(); 582 let c = unknown_function();
607 &mut &c 583 &mut &c
608 } 584}
609 "#, 585"#,
610 expect![[r#" 586 expect![[r#"
611 13..14 'x': u32 587 13..14 'x': u32
612 21..23 '{}': () 588 21..23 '{}': ()
@@ -636,23 +612,23 @@ fn infer_backwards() {
636fn infer_self() { 612fn infer_self() {
637 check_infer( 613 check_infer(
638 r#" 614 r#"
639 struct S; 615struct S;
640 616
641 impl S { 617impl S {
642 fn test(&self) { 618 fn test(&self) {
643 self; 619 self;
644 } 620 }
645 fn test2(self: &Self) { 621 fn test2(self: &Self) {
646 self; 622 self;
647 } 623 }
648 fn test3() -> Self { 624 fn test3() -> Self {
649 S {} 625 S {}
650 } 626 }
651 fn test4() -> Self { 627 fn test4() -> Self {
652 Self {} 628 Self {}
653 } 629 }
654 } 630}
655 "#, 631"#,
656 expect![[r#" 632 expect![[r#"
657 33..37 'self': &S 633 33..37 'self': &S
658 39..60 '{ ... }': () 634 39..60 '{ ... }': ()
@@ -672,30 +648,30 @@ fn infer_self() {
672fn infer_self_as_path() { 648fn infer_self_as_path() {
673 check_infer( 649 check_infer(
674 r#" 650 r#"
675 struct S1; 651struct S1;
676 struct S2(isize); 652struct S2(isize);
677 enum E { 653enum E {
678 V1, 654 V1,
679 V2(u32), 655 V2(u32),
680 } 656}
681 657
682 impl S1 { 658impl S1 {
683 fn test() { 659 fn test() {
684 Self; 660 Self;
685 } 661 }
686 } 662}
687 impl S2 { 663impl S2 {
688 fn test() { 664 fn test() {
689 Self(1); 665 Self(1);
690 } 666 }
691 } 667}
692 impl E { 668impl E {
693 fn test() { 669 fn test() {
694 Self::V1; 670 Self::V1;
695 Self::V2(1); 671 Self::V2(1);
696 } 672 }
697 } 673}
698 "#, 674"#,
699 expect![[r#" 675 expect![[r#"
700 86..107 '{ ... }': () 676 86..107 '{ ... }': ()
701 96..100 'Self': S1 677 96..100 'Self': S1
@@ -716,26 +692,26 @@ fn infer_self_as_path() {
716fn infer_binary_op() { 692fn infer_binary_op() {
717 check_infer( 693 check_infer(
718 r#" 694 r#"
719 fn f(x: bool) -> i32 { 695fn f(x: bool) -> i32 {
720 0i32 696 0i32
721 } 697}
722 698
723 fn test() -> bool { 699fn test() -> bool {
724 let x = a && b; 700 let x = a && b;
725 let y = true || false; 701 let y = true || false;
726 let z = x == y; 702 let z = x == y;
727 let t = x != y; 703 let t = x != y;
728 let minus_forty: isize = -40isize; 704 let minus_forty: isize = -40isize;
729 let h = minus_forty <= CONST_2; 705 let h = minus_forty <= CONST_2;
730 let c = f(z || y) + 5; 706 let c = f(z || y) + 5;
731 let d = b; 707 let d = b;
732 let g = minus_forty ^= i; 708 let g = minus_forty ^= i;
733 let ten: usize = 10; 709 let ten: usize = 10;
734 let ten_is_eleven = ten == some_num; 710 let ten_is_eleven = ten == some_num;
735 711
736 ten < 3 712 ten < 3
737 } 713}
738 "#, 714"#,
739 expect![[r#" 715 expect![[r#"
740 5..6 'x': bool 716 5..6 'x': bool
741 21..33 '{ 0i32 }': i32 717 21..33 '{ 0i32 }': i32
@@ -795,11 +771,11 @@ fn infer_binary_op() {
795fn infer_shift_op() { 771fn infer_shift_op() {
796 check_infer( 772 check_infer(
797 r#" 773 r#"
798 fn test() { 774fn test() {
799 1u32 << 5u8; 775 1u32 << 5u8;
800 1u32 >> 5u8; 776 1u32 >> 5u8;
801 } 777}
802 "#, 778"#,
803 expect![[r#" 779 expect![[r#"
804 10..47 '{ ...5u8; }': () 780 10..47 '{ ...5u8; }': ()
805 16..20 '1u32': u32 781 16..20 '1u32': u32
@@ -816,29 +792,29 @@ fn infer_shift_op() {
816fn infer_field_autoderef() { 792fn infer_field_autoderef() {
817 check_infer( 793 check_infer(
818 r#" 794 r#"
819 struct A { 795struct A {
820 b: B, 796 b: B,
821 } 797}
822 struct B; 798struct B;
823
824 fn test1(a: A) {
825 let a1 = a;
826 a1.b;
827 let a2 = &a;
828 a2.b;
829 let a3 = &mut a;
830 a3.b;
831 let a4 = &&&&&&&a;
832 a4.b;
833 let a5 = &mut &&mut &&mut a;
834 a5.b;
835 }
836 799
837 fn test2(a1: *const A, a2: *mut A) { 800fn test1(a: A) {
838 a1.b; 801 let a1 = a;
839 a2.b; 802 a1.b;
840 } 803 let a2 = &a;
841 "#, 804 a2.b;
805 let a3 = &mut a;
806 a3.b;
807 let a4 = &&&&&&&a;
808 a4.b;
809 let a5 = &mut &&mut &&mut a;
810 a5.b;
811}
812
813fn test2(a1: *const A, a2: *mut A) {
814 a1.b;
815 a2.b;
816}
817"#,
842 expect![[r#" 818 expect![[r#"
843 43..44 'a': A 819 43..44 'a': A
844 49..212 '{ ...5.b; }': () 820 49..212 '{ ...5.b; }': ()
@@ -891,58 +867,53 @@ fn infer_field_autoderef() {
891fn infer_argument_autoderef() { 867fn infer_argument_autoderef() {
892 check_infer( 868 check_infer(
893 r#" 869 r#"
894 #[lang = "deref"] 870//- minicore: deref
895 pub trait Deref { 871use core::ops::Deref;
896 type Target; 872struct A<T>(T);
897 fn deref(&self) -> &Self::Target;
898 }
899
900 struct A<T>(T);
901 873
902 impl<T> A<T> { 874impl<T> A<T> {
903 fn foo(&self) -> &T { 875 fn foo(&self) -> &T {
904 &self.0 876 &self.0
905 } 877 }
906 } 878}
907 879
908 struct B<T>(T); 880struct B<T>(T);
909 881
910 impl<T> Deref for B<T> { 882impl<T> Deref for B<T> {
911 type Target = T; 883 type Target = T;
912 fn deref(&self) -> &Self::Target { 884 fn deref(&self) -> &Self::Target {
913 &self.0 885 &self.0
914 } 886 }
915 } 887}
916 888
917 fn test() { 889fn test() {
918 let t = A::foo(&&B(B(A(42)))); 890 let t = A::foo(&&B(B(A(42))));
919 } 891}
920 "#, 892"#,
921 expect![[r#" 893 expect![[r#"
922 67..71 'self': &Self 894 66..70 'self': &A<T>
923 138..142 'self': &A<T> 895 78..101 '{ ... }': &T
924 150..173 '{ ... }': &T 896 88..95 '&self.0': &T
925 160..167 '&self.0': &T 897 89..93 'self': &A<T>
926 161..165 'self': &A<T> 898 89..95 'self.0': T
927 161..167 'self.0': T 899 182..186 'self': &B<T>
928 254..258 'self': &B<T> 900 205..228 '{ ... }': &T
929 277..300 '{ ... }': &T 901 215..222 '&self.0': &T
930 287..294 '&self.0': &T 902 216..220 'self': &B<T>
931 288..292 'self': &B<T> 903 216..222 'self.0': T
932 288..294 'self.0': T 904 242..280 '{ ...))); }': ()
933 314..352 '{ ...))); }': () 905 252..253 't': &i32
934 324..325 't': &i32 906 256..262 'A::foo': fn foo<i32>(&A<i32>) -> &i32
935 328..334 'A::foo': fn foo<i32>(&A<i32>) -> &i32 907 256..277 'A::foo...42))))': &i32
936 328..349 'A::foo...42))))': &i32 908 263..276 '&&B(B(A(42)))': &&B<B<A<i32>>>
937 335..348 '&&B(B(A(42)))': &&B<B<A<i32>>> 909 264..276 '&B(B(A(42)))': &B<B<A<i32>>>
938 336..348 '&B(B(A(42)))': &B<B<A<i32>>> 910 265..266 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
939 337..338 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 911 265..276 'B(B(A(42)))': B<B<A<i32>>>
940 337..348 'B(B(A(42)))': B<B<A<i32>>> 912 267..268 'B': B<A<i32>>(A<i32>) -> B<A<i32>>
941 339..340 'B': B<A<i32>>(A<i32>) -> B<A<i32>> 913 267..275 'B(A(42))': B<A<i32>>
942 339..347 'B(A(42))': B<A<i32>> 914 269..270 'A': A<i32>(i32) -> A<i32>
943 341..342 'A': A<i32>(i32) -> A<i32> 915 269..274 'A(42)': A<i32>
944 341..346 'A(42)': A<i32> 916 271..273 '42': i32
945 343..345 '42': i32
946 "#]], 917 "#]],
947 ); 918 );
948} 919}
@@ -951,62 +922,57 @@ fn infer_argument_autoderef() {
951fn infer_method_argument_autoderef() { 922fn infer_method_argument_autoderef() {
952 check_infer( 923 check_infer(
953 r#" 924 r#"
954 #[lang = "deref"] 925//- minicore: deref
955 pub trait Deref { 926use core::ops::Deref;
956 type Target; 927struct A<T>(*mut T);
957 fn deref(&self) -> &Self::Target;
958 }
959 928
960 struct A<T>(*mut T); 929impl<T> A<T> {
961 930 fn foo(&self, x: &A<T>) -> &T {
962 impl<T> A<T> { 931 &*x.0
963 fn foo(&self, x: &A<T>) -> &T { 932 }
964 &*x.0 933}
965 }
966 }
967 934
968 struct B<T>(T); 935struct B<T>(T);
969 936
970 impl<T> Deref for B<T> { 937impl<T> Deref for B<T> {
971 type Target = T; 938 type Target = T;
972 fn deref(&self) -> &Self::Target { 939 fn deref(&self) -> &Self::Target {
973 &self.0 940 &self.0
974 } 941 }
975 } 942}
976 943
977 fn test(a: A<i32>) { 944fn test(a: A<i32>) {
978 let t = A(0 as *mut _).foo(&&B(B(a))); 945 let t = A(0 as *mut _).foo(&&B(B(a)));
979 } 946}
980 "#, 947"#,
981 expect![[r#" 948 expect![[r#"
982 67..71 'self': &Self 949 71..75 'self': &A<T>
983 143..147 'self': &A<T> 950 77..78 'x': &A<T>
984 149..150 'x': &A<T> 951 93..114 '{ ... }': &T
985 165..186 '{ ... }': &T 952 103..108 '&*x.0': &T
986 175..180 '&*x.0': &T 953 104..108 '*x.0': T
987 176..180 '*x.0': T 954 105..106 'x': &A<T>
988 177..178 'x': &A<T> 955 105..108 'x.0': *mut T
989 177..180 'x.0': *mut T 956 195..199 'self': &B<T>
990 267..271 'self': &B<T> 957 218..241 '{ ... }': &T
991 290..313 '{ ... }': &T 958 228..235 '&self.0': &T
992 300..307 '&self.0': &T 959 229..233 'self': &B<T>
993 301..305 'self': &B<T> 960 229..235 'self.0': T
994 301..307 'self.0': T 961 253..254 'a': A<i32>
995 325..326 'a': A<i32> 962 264..310 '{ ...))); }': ()
996 336..382 '{ ...))); }': () 963 274..275 't': &i32
997 346..347 't': &i32 964 278..279 'A': A<i32>(*mut i32) -> A<i32>
998 350..351 'A': A<i32>(*mut i32) -> A<i32> 965 278..292 'A(0 as *mut _)': A<i32>
999 350..364 'A(0 as *mut _)': A<i32> 966 278..307 'A(0 as...B(a)))': &i32
1000 350..379 'A(0 as...B(a)))': &i32 967 280..281 '0': i32
1001 352..353 '0': i32 968 280..291 '0 as *mut _': *mut i32
1002 352..363 '0 as *mut _': *mut i32 969 297..306 '&&B(B(a))': &&B<B<A<i32>>>
1003 369..378 '&&B(B(a))': &&B<B<A<i32>>> 970 298..306 '&B(B(a))': &B<B<A<i32>>>
1004 370..378 '&B(B(a))': &B<B<A<i32>>> 971 299..300 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
1005 371..372 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 972 299..306 'B(B(a))': B<B<A<i32>>>
1006 371..378 'B(B(a))': B<B<A<i32>>> 973 301..302 'B': B<A<i32>>(A<i32>) -> B<A<i32>>
1007 373..374 'B': B<A<i32>>(A<i32>) -> B<A<i32>> 974 301..305 'B(a)': B<A<i32>>
1008 373..377 'B(a)': B<A<i32>> 975 303..304 'a': A<i32>
1009 375..376 'a': A<i32>
1010 "#]], 976 "#]],
1011 ); 977 );
1012} 978}
@@ -1015,15 +981,15 @@ fn infer_method_argument_autoderef() {
1015fn infer_in_elseif() { 981fn infer_in_elseif() {
1016 check_infer( 982 check_infer(
1017 r#" 983 r#"
1018 struct Foo { field: i32 } 984struct Foo { field: i32 }
1019 fn main(foo: Foo) { 985fn main(foo: Foo) {
1020 if true { 986 if true {
1021 987
1022 } else if false { 988 } else if false {
1023 foo.field 989 foo.field
1024 } 990 }
1025 } 991}
1026 "#, 992"#,
1027 expect![[r#" 993 expect![[r#"
1028 34..37 'foo': Foo 994 34..37 'foo': Foo
1029 44..108 '{ ... } }': () 995 44..108 '{ ... } }': ()
@@ -1043,28 +1009,29 @@ fn infer_in_elseif() {
1043fn infer_if_match_with_return() { 1009fn infer_if_match_with_return() {
1044 check_infer( 1010 check_infer(
1045 r#" 1011 r#"
1046 fn foo() { 1012fn foo() {
1047 let _x1 = if true { 1013 let _x1 = if true {
1048 1 1014 1
1049 } else { 1015 } else {
1050 return; 1016 return;
1051 }; 1017 };
1052 let _x2 = if true { 1018 let _x2 = if true {
1053 2 1019 2
1054 } else { 1020 } else {
1055 return 1021 return
1056 }; 1022 };
1057 let _x3 = match true { 1023 let _x3 = match true {
1058 true => 3, 1024 true => 3,
1059 _ => { 1025 _ => {
1060 return; 1026 return;
1061 } 1027 }
1062 }; 1028 };
1063 let _x4 = match true { 1029 let _x4 = match true {
1064 true => 4, 1030 true => 4,
1065 _ => return 1031 _ => return
1066 }; 1032 };
1067 }"#, 1033}
1034"#,
1068 expect![[r#" 1035 expect![[r#"
1069 9..322 '{ ... }; }': () 1036 9..322 '{ ... }; }': ()
1070 19..22 '_x1': i32 1037 19..22 '_x1': i32
@@ -2639,11 +2606,8 @@ fn f() {
2639fn infer_boxed_self_receiver() { 2606fn infer_boxed_self_receiver() {
2640 check_infer( 2607 check_infer(
2641 r#" 2608 r#"
2642#[lang = "deref"] 2609//- minicore: deref
2643pub trait Deref { 2610use core::ops::Deref;
2644 type Target;
2645 fn deref(&self) -> &Self::Target;
2646}
2647 2611
2648struct Box<T>(T); 2612struct Box<T>(T);
2649 2613
@@ -2675,40 +2639,39 @@ fn main() {
2675} 2639}
2676 "#, 2640 "#,
2677 expect![[r#" 2641 expect![[r#"
2678 67..71 'self': &Self 2642 104..108 'self': &Box<T>
2679 175..179 'self': &Box<T> 2643 188..192 'self': &Box<Foo<T>>
2680 259..263 'self': &Box<Foo<T>> 2644 218..220 '{}': ()
2681 289..291 '{}': () 2645 242..246 'self': &Box<Foo<T>>
2682 313..317 'self': &Box<Foo<T>> 2646 275..277 '{}': ()
2683 346..348 '{}': () 2647 297..301 'self': Box<Foo<T>>
2684 368..372 'self': Box<Foo<T>> 2648 322..324 '{}': ()
2685 393..395 '{}': () 2649 338..559 '{ ...r(); }': ()
2686 409..630 '{ ...r(); }': () 2650 348..353 'boxed': Box<Foo<i32>>
2687 419..424 'boxed': Box<Foo<i32>> 2651 356..359 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
2688 427..430 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>> 2652 356..371 'Box(Foo(0_i32))': Box<Foo<i32>>
2689 427..442 'Box(Foo(0_i32))': Box<Foo<i32>> 2653 360..363 'Foo': Foo<i32>(i32) -> Foo<i32>
2690 431..434 'Foo': Foo<i32>(i32) -> Foo<i32> 2654 360..370 'Foo(0_i32)': Foo<i32>
2691 431..441 'Foo(0_i32)': Foo<i32> 2655 364..369 '0_i32': i32
2692 435..440 '0_i32': i32 2656 382..386 'bad1': &i32
2693 453..457 'bad1': &i32 2657 389..394 'boxed': Box<Foo<i32>>
2694 460..465 'boxed': Box<Foo<i32>> 2658 389..406 'boxed....nner()': &i32
2695 460..477 'boxed....nner()': &i32 2659 416..421 'good1': &i32
2696 487..492 'good1': &i32 2660 424..438 'Foo::get_inner': fn get_inner<i32>(&Box<Foo<i32>>) -> &i32
2697 495..509 'Foo::get_inner': fn get_inner<i32>(&Box<Foo<i32>>) -> &i32 2661 424..446 'Foo::g...boxed)': &i32
2698 495..517 'Foo::g...boxed)': &i32 2662 439..445 '&boxed': &Box<Foo<i32>>
2699 510..516 '&boxed': &Box<Foo<i32>> 2663 440..445 'boxed': Box<Foo<i32>>
2700 511..516 'boxed': Box<Foo<i32>> 2664 457..461 'bad2': &Foo<i32>
2701 528..532 'bad2': &Foo<i32> 2665 464..469 'boxed': Box<Foo<i32>>
2702 535..540 'boxed': Box<Foo<i32>> 2666 464..480 'boxed....self()': &Foo<i32>
2703 535..551 'boxed....self()': &Foo<i32> 2667 490..495 'good2': &Foo<i32>
2704 561..566 'good2': &Foo<i32> 2668 498..511 'Foo::get_self': fn get_self<i32>(&Box<Foo<i32>>) -> &Foo<i32>
2705 569..582 'Foo::get_self': fn get_self<i32>(&Box<Foo<i32>>) -> &Foo<i32> 2669 498..519 'Foo::g...boxed)': &Foo<i32>
2706 569..590 'Foo::g...boxed)': &Foo<i32> 2670 512..518 '&boxed': &Box<Foo<i32>>
2707 583..589 '&boxed': &Box<Foo<i32>> 2671 513..518 'boxed': Box<Foo<i32>>
2708 584..589 'boxed': Box<Foo<i32>> 2672 530..535 'inner': Foo<i32>
2709 601..606 'inner': Foo<i32> 2673 538..543 'boxed': Box<Foo<i32>>
2710 609..614 'boxed': Box<Foo<i32>> 2674 538..556 'boxed....nner()': Foo<i32>
2711 609..627 'boxed....nner()': Foo<i32>
2712 "#]], 2675 "#]],
2713 ); 2676 );
2714} 2677}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 6bcede4c4..65fed02d2 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -6,10 +6,10 @@ use super::{check_infer, check_infer_with_mismatches, check_types};
6fn infer_await() { 6fn infer_await() {
7 check_types( 7 check_types(
8 r#" 8 r#"
9//- /main.rs crate:main deps:core 9//- minicore: future
10struct IntFuture; 10struct IntFuture;
11 11
12impl Future for IntFuture { 12impl core::future::Future for IntFuture {
13 type Output = u64; 13 type Output = u64;
14} 14}
15 15
@@ -18,16 +18,6 @@ fn test() {
18 let v = r.await; 18 let v = r.await;
19 v; 19 v;
20} //^ u64 20} //^ u64
21
22//- /core.rs crate:core
23pub mod prelude {
24 pub mod rust_2018 {
25 #[lang = "future_trait"]
26 pub trait Future {
27 type Output;
28 }
29 }
30}
31"#, 21"#,
32 ); 22 );
33} 23}
@@ -36,25 +26,14 @@ pub mod prelude {
36fn infer_async() { 26fn infer_async() {
37 check_types( 27 check_types(
38 r#" 28 r#"
39//- /main.rs crate:main deps:core 29//- minicore: future
40async fn foo() -> u64 { 30async fn foo() -> u64 { 128 }
41 128
42}
43 31
44fn test() { 32fn test() {
45 let r = foo(); 33 let r = foo();
46 let v = r.await; 34 let v = r.await;
47 v; 35 v;
48} //^ u64 36} //^ u64
49
50//- /core.rs crate:core
51#[prelude_import] use future::*;
52mod future {
53 #[lang = "future_trait"]
54 trait Future {
55 type Output;
56 }
57}
58"#, 37"#,
59 ); 38 );
60} 39}
@@ -63,24 +42,13 @@ mod future {
63fn infer_desugar_async() { 42fn infer_desugar_async() {
64 check_types( 43 check_types(
65 r#" 44 r#"
66//- /main.rs crate:main deps:core 45//- minicore: future
67async fn foo() -> u64 { 46async fn foo() -> u64 { 128 }
68 128
69}
70 47
71fn test() { 48fn test() {
72 let r = foo(); 49 let r = foo();
73 r; 50 r;
74} //^ impl Future<Output = u64> 51} //^ impl Future<Output = u64>
75
76//- /core.rs crate:core
77#[prelude_import] use future::*;
78mod future {
79 trait Future {
80 type Output;
81 }
82}
83
84"#, 52"#,
85 ); 53 );
86} 54}
@@ -89,7 +57,7 @@ mod future {
89fn infer_async_block() { 57fn infer_async_block() {
90 check_types( 58 check_types(
91 r#" 59 r#"
92//- /main.rs crate:main deps:core 60//- minicore: future, option
93async fn test() { 61async fn test() {
94 let a = async { 42 }; 62 let a = async { 42 };
95 a; 63 a;
@@ -101,7 +69,7 @@ async fn test() {
101 b; 69 b;
102// ^ () 70// ^ ()
103 let c = async { 71 let c = async {
104 let y = Option::None; 72 let y = None;
105 y 73 y
106 // ^ Option<u64> 74 // ^ Option<u64>
107 }; 75 };
@@ -109,18 +77,6 @@ async fn test() {
109 c; 77 c;
110// ^ impl Future<Output = Option<u64>> 78// ^ impl Future<Output = Option<u64>>
111} 79}
112
113enum Option<T> { None, Some(T) }
114
115//- /core.rs crate:core
116#[prelude_import] use future::*;
117mod future {
118 #[lang = "future_trait"]
119 trait Future {
120 type Output;
121 }
122}
123
124"#, 80"#,
125 ); 81 );
126} 82}
@@ -704,14 +660,9 @@ mod ops {
704fn deref_trait() { 660fn deref_trait() {
705 check_types( 661 check_types(
706 r#" 662 r#"
707#[lang = "deref"] 663//- minicore: deref
708trait Deref {
709 type Target;
710 fn deref(&self) -> &Self::Target;
711}
712
713struct Arc<T>; 664struct Arc<T>;
714impl<T> Deref for Arc<T> { 665impl<T> core::ops::Deref for Arc<T> {
715 type Target = T; 666 type Target = T;
716} 667}
717 668
@@ -731,16 +682,10 @@ fn test(s: Arc<S>) {
731fn deref_trait_with_inference_var() { 682fn deref_trait_with_inference_var() {
732 check_types( 683 check_types(
733 r#" 684 r#"
734//- /main.rs 685//- minicore: deref
735#[lang = "deref"]
736trait Deref {
737 type Target;
738 fn deref(&self) -> &Self::Target;
739}
740
741struct Arc<T>; 686struct Arc<T>;
742fn new_arc<T>() -> Arc<T> {} 687fn new_arc<T>() -> Arc<T> {}
743impl<T> Deref for Arc<T> { 688impl<T> core::ops::Deref for Arc<T> {
744 type Target = T; 689 type Target = T;
745} 690}
746 691
@@ -761,15 +706,10 @@ fn test() {
761fn deref_trait_infinite_recursion() { 706fn deref_trait_infinite_recursion() {
762 check_types( 707 check_types(
763 r#" 708 r#"
764#[lang = "deref"] 709//- minicore: deref
765trait Deref {
766 type Target;
767 fn deref(&self) -> &Self::Target;
768}
769
770struct S; 710struct S;
771 711
772impl Deref for S { 712impl core::ops::Deref for S {
773 type Target = S; 713 type Target = S;
774} 714}
775 715
@@ -784,14 +724,9 @@ fn test(s: S) {
784fn deref_trait_with_question_mark_size() { 724fn deref_trait_with_question_mark_size() {
785 check_types( 725 check_types(
786 r#" 726 r#"
787#[lang = "deref"] 727//- minicore: deref
788trait Deref {
789 type Target;
790 fn deref(&self) -> &Self::Target;
791}
792
793struct Arc<T>; 728struct Arc<T>;
794impl<T> Deref for Arc<T> { 729impl<T: ?Sized> core::ops::Deref for Arc<T> {
795 type Target = T; 730 type Target = T;
796} 731}
797 732
@@ -1475,7 +1410,6 @@ fn test(
1475} 1410}
1476 1411
1477#[test] 1412#[test]
1478#[ignore]
1479fn error_bound_chalk() { 1413fn error_bound_chalk() {
1480 check_types( 1414 check_types(
1481 r#" 1415 r#"
@@ -2626,12 +2560,9 @@ fn test<T: Trait>() {
2626fn dyn_trait_through_chalk() { 2560fn dyn_trait_through_chalk() {
2627 check_types( 2561 check_types(
2628 r#" 2562 r#"
2563//- minicore: deref
2629struct Box<T> {} 2564struct Box<T> {}
2630#[lang = "deref"] 2565impl<T> core::ops::Deref for Box<T> {
2631trait Deref {
2632 type Target;
2633}
2634impl<T> Deref for Box<T> {
2635 type Target = T; 2566 type Target = T;
2636} 2567}
2637trait Trait { 2568trait Trait {
@@ -3696,16 +3627,7 @@ impl foo::Foo for u32 {
3696fn infer_async_ret_type() { 3627fn infer_async_ret_type() {
3697 check_types( 3628 check_types(
3698 r#" 3629 r#"
3699//- /main.rs crate:main deps:core 3630//- minicore: future, result
3700
3701enum Result<T, E> {
3702 Ok(T),
3703 Err(E),
3704}
3705
3706use Result::*;
3707
3708
3709struct Fooey; 3631struct Fooey;
3710 3632
3711impl Fooey { 3633impl Fooey {
@@ -3728,15 +3650,6 @@ async fn get_accounts() -> Result<u32, ()> {
3728 // ^ u32 3650 // ^ u32
3729 Ok(ret) 3651 Ok(ret)
3730} 3652}
3731
3732//- /core.rs crate:core
3733#[prelude_import] use future::*;
3734mod future {
3735 #[lang = "future_trait"]
3736 trait Future {
3737 type Output;
3738 }
3739}
3740"#, 3653"#,
3741 ); 3654 );
3742} 3655}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f12928225..0e8447394 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -29,6 +29,7 @@ ide_db = { path = "../ide_db", version = "0.0.0" }
29cfg = { path = "../cfg", version = "0.0.0" } 29cfg = { path = "../cfg", version = "0.0.0" }
30profile = { path = "../profile", version = "0.0.0" } 30profile = { path = "../profile", version = "0.0.0" }
31ide_assists = { path = "../ide_assists", version = "0.0.0" } 31ide_assists = { path = "../ide_assists", version = "0.0.0" }
32ide_diagnostics = { path = "../ide_diagnostics", version = "0.0.0" }
32ide_ssr = { path = "../ide_ssr", version = "0.0.0" } 33ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
33ide_completion = { path = "../ide_completion", version = "0.0.0" } 34ide_completion = { path = "../ide_completion", version = "0.0.0" }
34 35
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
deleted file mode 100644
index 815a633e5..000000000
--- a/crates/ide/src/diagnostics.rs
+++ /dev/null
@@ -1,498 +0,0 @@
1//! Collects diagnostics & fixits for a single file.
2//!
3//! The tricky bit here is that diagnostics are produced by hir in terms of
4//! macro-expanded files, but we need to present them to the users in terms of
5//! original files. So we need to map the ranges.
6
7mod break_outside_of_loop;
8mod inactive_code;
9mod incorrect_case;
10mod macro_error;
11mod mismatched_arg_count;
12mod missing_fields;
13mod missing_match_arms;
14mod missing_ok_or_some_in_tail_expr;
15mod missing_unsafe;
16mod no_such_field;
17mod remove_this_semicolon;
18mod replace_filter_map_next_with_find_map;
19mod unimplemented_builtin_macro;
20mod unlinked_file;
21mod unresolved_extern_crate;
22mod unresolved_import;
23mod unresolved_macro_call;
24mod unresolved_module;
25mod unresolved_proc_macro;
26
27mod field_shorthand;
28
29use hir::{diagnostics::AnyDiagnostic, Semantics};
30use ide_assists::AssistResolveStrategy;
31use ide_db::{base_db::SourceDatabase, RootDatabase};
32use itertools::Itertools;
33use rustc_hash::FxHashSet;
34use syntax::{
35 ast::{self, AstNode},
36 SyntaxNode, TextRange,
37};
38use text_edit::TextEdit;
39use unlinked_file::UnlinkedFile;
40
41use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
42
43#[derive(Copy, Clone, Debug, PartialEq)]
44pub struct DiagnosticCode(pub &'static str);
45
46impl DiagnosticCode {
47 pub fn as_str(&self) -> &str {
48 self.0
49 }
50}
51
52#[derive(Debug)]
53pub struct Diagnostic {
54 pub code: DiagnosticCode,
55 pub message: String,
56 pub range: TextRange,
57 pub severity: Severity,
58 pub unused: bool,
59 pub experimental: bool,
60 pub fixes: Option<Vec<Assist>>,
61}
62
63impl Diagnostic {
64 fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
65 let message = message.into();
66 Diagnostic {
67 code: DiagnosticCode(code),
68 message,
69 range,
70 severity: Severity::Error,
71 unused: false,
72 experimental: false,
73 fixes: None,
74 }
75 }
76
77 fn experimental(mut self) -> Diagnostic {
78 self.experimental = true;
79 self
80 }
81
82 fn severity(mut self, severity: Severity) -> Diagnostic {
83 self.severity = severity;
84 self
85 }
86
87 fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic {
88 self.fixes = fixes;
89 self
90 }
91
92 fn with_unused(mut self, unused: bool) -> Diagnostic {
93 self.unused = unused;
94 self
95 }
96}
97
98#[derive(Debug, Copy, Clone)]
99pub enum Severity {
100 Error,
101 WeakWarning,
102}
103
104#[derive(Default, Debug, Clone)]
105pub struct DiagnosticsConfig {
106 pub disable_experimental: bool,
107 pub disabled: FxHashSet<String>,
108}
109
110struct DiagnosticsContext<'a> {
111 config: &'a DiagnosticsConfig,
112 sema: Semantics<'a, RootDatabase>,
113 resolve: &'a AssistResolveStrategy,
114}
115
116pub(crate) fn diagnostics(
117 db: &RootDatabase,
118 config: &DiagnosticsConfig,
119 resolve: &AssistResolveStrategy,
120 file_id: FileId,
121) -> Vec<Diagnostic> {
122 let _p = profile::span("diagnostics");
123 let sema = Semantics::new(db);
124 let parse = db.parse(file_id);
125 let mut res = Vec::new();
126
127 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
128 res.extend(
129 parse.errors().iter().take(128).map(|err| {
130 Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range())
131 }),
132 );
133
134 for node in parse.tree().syntax().descendants() {
135 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
136 field_shorthand::check(&mut res, file_id, &node);
137 }
138
139 let mut diags = Vec::new();
140 let module = sema.to_module_def(file_id);
141 if let Some(m) = module {
142 m.diagnostics(db, &mut diags)
143 }
144
145 let ctx = DiagnosticsContext { config, sema, resolve };
146 if module.is_none() {
147 let d = UnlinkedFile { file: file_id };
148 let d = unlinked_file::unlinked_file(&ctx, &d);
149 res.push(d)
150 }
151
152 for diag in diags {
153 #[rustfmt::skip]
154 let d = match diag {
155 AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d),
156 AnyDiagnostic::IncorrectCase(d) => incorrect_case::incorrect_case(&ctx, &d),
157 AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
158 AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
159 AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
160 AnyDiagnostic::MissingMatchArms(d) => missing_match_arms::missing_match_arms(&ctx, &d),
161 AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d),
162 AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d),
163 AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d),
164 AnyDiagnostic::RemoveThisSemicolon(d) => remove_this_semicolon::remove_this_semicolon(&ctx, &d),
165 AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
166 AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
167 AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
168 AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
169 AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
170 AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
171 AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
172
173 AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
174 Some(it) => it,
175 None => continue,
176 }
177 };
178 res.push(d)
179 }
180
181 res.retain(|d| {
182 !ctx.config.disabled.contains(d.code.as_str())
183 && !(ctx.config.disable_experimental && d.experimental)
184 });
185
186 res
187}
188
189fn check_unnecessary_braces_in_use_statement(
190 acc: &mut Vec<Diagnostic>,
191 file_id: FileId,
192 node: &SyntaxNode,
193) -> Option<()> {
194 let use_tree_list = ast::UseTreeList::cast(node.clone())?;
195 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
196 // If there is a comment inside the bracketed `use`,
197 // assume it is a commented out module path and don't show diagnostic.
198 if use_tree_list.has_inner_comment() {
199 return Some(());
200 }
201
202 let use_range = use_tree_list.syntax().text_range();
203 let edit =
204 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
205 .unwrap_or_else(|| {
206 let to_replace = single_use_tree.syntax().text().to_string();
207 let mut edit_builder = TextEdit::builder();
208 edit_builder.delete(use_range);
209 edit_builder.insert(use_range.start(), to_replace);
210 edit_builder.finish()
211 });
212
213 acc.push(
214 Diagnostic::new(
215 "unnecessary-braces",
216 "Unnecessary braces in use statement".to_string(),
217 use_range,
218 )
219 .severity(Severity::WeakWarning)
220 .with_fixes(Some(vec![fix(
221 "remove_braces",
222 "Remove unnecessary braces",
223 SourceChange::from_text_edit(file_id, edit),
224 use_range,
225 )])),
226 );
227 }
228
229 Some(())
230}
231
232fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
233 single_use_tree: &ast::UseTree,
234) -> Option<TextEdit> {
235 let use_tree_list_node = single_use_tree.syntax().parent()?;
236 if single_use_tree.path()?.segment()?.self_token().is_some() {
237 let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
238 let end = use_tree_list_node.text_range().end();
239 return Some(TextEdit::delete(TextRange::new(start, end)));
240 }
241 None
242}
243
244fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
245 let mut res = unresolved_fix(id, label, target);
246 res.source_change = Some(source_change);
247 res
248}
249
250fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
251 assert!(!id.contains(' '));
252 Assist {
253 id: AssistId(id, AssistKind::QuickFix),
254 label: Label::new(label),
255 group: None,
256 target,
257 source_change: None,
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use expect_test::Expect;
264 use ide_assists::AssistResolveStrategy;
265 use stdx::trim_indent;
266 use test_utils::{assert_eq_text, extract_annotations};
267
268 use crate::{fixture, DiagnosticsConfig};
269
270 /// Takes a multi-file input fixture with annotated cursor positions,
271 /// and checks that:
272 /// * a diagnostic is produced
273 /// * the first diagnostic fix trigger range touches the input cursor position
274 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
275 #[track_caller]
276 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
277 check_nth_fix(0, ra_fixture_before, ra_fixture_after);
278 }
279 /// Takes a multi-file input fixture with annotated cursor positions,
280 /// and checks that:
281 /// * a diagnostic is produced
282 /// * every diagnostic fixes trigger range touches the input cursor position
283 /// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
284 pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
285 for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
286 check_nth_fix(i, ra_fixture_before, ra_fixture_after)
287 }
288 }
289
290 #[track_caller]
291 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
292 let after = trim_indent(ra_fixture_after);
293
294 let (analysis, file_position) = fixture::position(ra_fixture_before);
295 let diagnostic = analysis
296 .diagnostics(
297 &DiagnosticsConfig::default(),
298 AssistResolveStrategy::All,
299 file_position.file_id,
300 )
301 .unwrap()
302 .pop()
303 .expect("no diagnostics");
304 let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth];
305 let actual = {
306 let source_change = fix.source_change.as_ref().unwrap();
307 let file_id = *source_change.source_file_edits.keys().next().unwrap();
308 let mut actual = analysis.file_text(file_id).unwrap().to_string();
309
310 for edit in source_change.source_file_edits.values() {
311 edit.apply(&mut actual);
312 }
313 actual
314 };
315
316 assert_eq_text!(&after, &actual);
317 assert!(
318 fix.target.contains_inclusive(file_position.offset),
319 "diagnostic fix range {:?} does not touch cursor position {:?}",
320 fix.target,
321 file_position.offset
322 );
323 }
324
325 /// Checks that there's a diagnostic *without* fix at `$0`.
326 pub(crate) fn check_no_fix(ra_fixture: &str) {
327 let (analysis, file_position) = fixture::position(ra_fixture);
328 let diagnostic = analysis
329 .diagnostics(
330 &DiagnosticsConfig::default(),
331 AssistResolveStrategy::All,
332 file_position.file_id,
333 )
334 .unwrap()
335 .pop()
336 .unwrap();
337 assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
338 }
339
340 pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
341 let (analysis, file_id) = fixture::file(ra_fixture);
342 let diagnostics = analysis
343 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
344 .unwrap();
345 expect.assert_debug_eq(&diagnostics)
346 }
347
348 #[track_caller]
349 pub(crate) fn check_diagnostics(ra_fixture: &str) {
350 let mut config = DiagnosticsConfig::default();
351 config.disabled.insert("inactive-code".to_string());
352 check_diagnostics_with_config(config, ra_fixture)
353 }
354
355 #[track_caller]
356 pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
357 let (analysis, files) = fixture::files(ra_fixture);
358 for file_id in files {
359 let diagnostics =
360 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
361
362 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
363 let mut actual =
364 diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
365 actual.sort_by_key(|(range, _)| range.start());
366 assert_eq!(expected, actual);
367 }
368 }
369
370 #[test]
371 fn test_check_unnecessary_braces_in_use_statement() {
372 check_diagnostics(
373 r#"
374use a;
375use a::{c, d::e};
376
377mod a {
378 mod c {}
379 mod d {
380 mod e {}
381 }
382}
383"#,
384 );
385 check_diagnostics(
386 r#"
387use a;
388use a::{
389 c,
390 // d::e
391};
392
393mod a {
394 mod c {}
395 mod d {
396 mod e {}
397 }
398}
399"#,
400 );
401 check_fix(
402 r"
403 mod b {}
404 use {$0b};
405 ",
406 r"
407 mod b {}
408 use b;
409 ",
410 );
411 check_fix(
412 r"
413 mod b {}
414 use {b$0};
415 ",
416 r"
417 mod b {}
418 use b;
419 ",
420 );
421 check_fix(
422 r"
423 mod a { mod c {} }
424 use a::{c$0};
425 ",
426 r"
427 mod a { mod c {} }
428 use a::c;
429 ",
430 );
431 check_fix(
432 r"
433 mod a {}
434 use a::{self$0};
435 ",
436 r"
437 mod a {}
438 use a;
439 ",
440 );
441 check_fix(
442 r"
443 mod a { mod c {} mod d { mod e {} } }
444 use a::{c, d::{e$0}};
445 ",
446 r"
447 mod a { mod c {} mod d { mod e {} } }
448 use a::{c, d::e};
449 ",
450 );
451 }
452
453 #[test]
454 fn test_disabled_diagnostics() {
455 let mut config = DiagnosticsConfig::default();
456 config.disabled.insert("unresolved-module".into());
457
458 let (analysis, file_id) = fixture::file(r#"mod foo;"#);
459
460 let diagnostics =
461 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
462 assert!(diagnostics.is_empty());
463
464 let diagnostics = analysis
465 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
466 .unwrap();
467 assert!(!diagnostics.is_empty());
468 }
469
470 #[test]
471 fn import_extern_crate_clash_with_inner_item() {
472 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
473
474 check_diagnostics(
475 r#"
476//- /lib.rs crate:lib deps:jwt
477mod permissions;
478
479use permissions::jwt;
480
481fn f() {
482 fn inner() {}
483 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
484}
485
486//- /permissions.rs
487pub mod jwt {
488 pub struct Claims {}
489}
490
491//- /jwt/lib.rs crate:jwt
492pub struct Claims {
493 field: u8,
494}
495 "#,
496 );
497 }
498}
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index b75ec411c..455b32456 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -442,10 +442,10 @@ impl TryToNav for hir::TypeParam {
442 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 442 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
443 let src = self.source(db)?; 443 let src = self.source(db)?;
444 let full_range = match &src.value { 444 let full_range = match &src.value {
445 Either::Left(it) => it 445 Either::Left(type_param) => type_param.syntax().text_range(),
446 Either::Right(trait_) => trait_
446 .name() 447 .name()
447 .map_or_else(|| it.syntax().text_range(), |name| name.syntax().text_range()), 448 .map_or_else(|| trait_.syntax().text_range(), |name| name.syntax().text_range()),
448 Either::Right(it) => it.syntax().text_range(),
449 }; 449 };
450 let focus_range = match &src.value { 450 let focus_range = match &src.value {
451 Either::Left(it) => it.name(), 451 Either::Left(it) => it.name(),
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 57ae9455b..7ac0118fe 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -241,6 +241,10 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
241 Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(), 241 Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(),
242 _ => definition.module(db)?.krate(), 242 _ => definition.module(db)?.krate(),
243 }; 243 };
244 // FIXME: using import map doesn't make sense here. What we want here is
245 // canonical path. What import map returns is the shortest path suitable for
246 // import. See this test:
247 cov_mark::hit!(test_reexport_order);
244 let import_map = db.import_map(krate.into()); 248 let import_map = db.import_map(krate.into());
245 249
246 let mut base = krate.display_name(db)?.to_string(); 250 let mut base = krate.display_name(db)?.to_string();
@@ -642,13 +646,15 @@ pub mod foo {
642 ) 646 )
643 } 647 }
644 648
645 // FIXME: ImportMap will return re-export paths instead of public module
646 // paths. The correct path to documentation will never be a re-export.
647 // This problem stops us from resolving stdlib items included in the prelude
648 // such as `Option::Some` correctly.
649 #[ignore = "ImportMap may return re-exports"]
650 #[test] 649 #[test]
651 fn test_reexport_order() { 650 fn test_reexport_order() {
651 cov_mark::check!(test_reexport_order);
652 // FIXME: This should return
653 //
654 // https://docs.rs/test/*/test/wrapper/modulestruct.Item.html
655 //
656 // That is, we should point inside the module, rather than at the
657 // re-export.
652 check( 658 check(
653 r#" 659 r#"
654pub mod wrapper { 660pub mod wrapper {
@@ -663,7 +669,7 @@ fn foo() {
663 let bar: wrapper::It$0em; 669 let bar: wrapper::It$0em;
664} 670}
665 "#, 671 "#,
666 expect![[r#"https://docs.rs/test/*/test/wrapper/module/struct.Item.html"#]], 672 expect![[r#"https://docs.rs/test/*/test/wrapper/struct.Item.html"#]],
667 ) 673 )
668 } 674 }
669} 675}
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index 38e2e866b..cf679edd3 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -12,14 +12,6 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
12 (host.analysis(), change_fixture.files[0]) 12 (host.analysis(), change_fixture.files[0])
13} 13}
14 14
15/// Creates analysis for many files.
16pub(crate) fn files(ra_fixture: &str) -> (Analysis, Vec<FileId>) {
17 let mut host = AnalysisHost::default();
18 let change_fixture = ChangeFixture::parse(ra_fixture);
19 host.db.apply_change(change_fixture.change);
20 (host.analysis(), change_fixture.files)
21}
22
23/// Creates analysis from a multi-file fixture, returns positions marked with $0. 15/// Creates analysis from a multi-file fixture, returns positions marked with $0.
24pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { 16pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
25 let mut host = AnalysisHost::default(); 17 let mut host = AnalysisHost::default();
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 8dd643a0f..d8e0dc4d5 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1130,15 +1130,15 @@ fn foo<'foobar>(_: &'foobar ()) {
1130 } 1130 }
1131 1131
1132 #[test] 1132 #[test]
1133 #[ignore] // requires the HIR to somehow track these hrtb lifetimes
1134 fn goto_lifetime_hrtb() { 1133 fn goto_lifetime_hrtb() {
1135 check( 1134 // FIXME: requires the HIR to somehow track these hrtb lifetimes
1135 check_unresolved(
1136 r#"trait Foo<T> {} 1136 r#"trait Foo<T> {}
1137fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {} 1137fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1138 //^^ 1138 //^^
1139"#, 1139"#,
1140 ); 1140 );
1141 check( 1141 check_unresolved(
1142 r#"trait Foo<T> {} 1142 r#"trait Foo<T> {}
1143fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {} 1143fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1144 //^^ 1144 //^^
@@ -1147,9 +1147,9 @@ fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1147 } 1147 }
1148 1148
1149 #[test] 1149 #[test]
1150 #[ignore] // requires ForTypes to be implemented
1151 fn goto_lifetime_hrtb_for_type() { 1150 fn goto_lifetime_hrtb_for_type() {
1152 check( 1151 // FIXME: requires ForTypes to be implemented
1152 check_unresolved(
1153 r#"trait Foo<T> {} 1153 r#"trait Foo<T> {}
1154fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {} 1154fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
1155 //^^ 1155 //^^
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 004d9cb68..ca3c02bf6 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -25,7 +25,7 @@ pub(crate) fn goto_type_definition(
25 let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; 25 let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?;
26 let token: SyntaxToken = sema.descend_into_macros(token); 26 let token: SyntaxToken = sema.descend_into_macros(token);
27 27
28 let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| { 28 let (ty, node) = sema.token_ancestors_with_macros(token.clone()).find_map(|node| {
29 let ty = match_ast! { 29 let ty = match_ast! {
30 match node { 30 match node {
31 ast::Expr(it) => sema.type_of_expr(&it)?, 31 ast::Expr(it) => sema.type_of_expr(&it)?,
@@ -33,13 +33,23 @@ pub(crate) fn goto_type_definition(
33 ast::SelfParam(it) => sema.type_of_self(&it)?, 33 ast::SelfParam(it) => sema.type_of_self(&it)?,
34 ast::Type(it) => sema.resolve_type(&it)?, 34 ast::Type(it) => sema.resolve_type(&it)?,
35 ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, 35 ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
36 ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
37 // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
38 ast::NameRef(it) => {
39 if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
40 let (_, _, ty) = sema.resolve_record_field(&record_field)?;
41 ty
42 } else {
43 let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
44 sema.resolve_record_pat_field(&record_field)?.ty(db)
45 }
46 },
36 _ => return None, 47 _ => return None,
37 } 48 }
38 }; 49 };
39 50
40 Some((ty, node)) 51 Some((ty, node))
41 })?; 52 })?;
42
43 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; 53 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
44 54
45 let nav = adt_def.try_to_nav(db)?; 55 let nav = adt_def.try_to_nav(db)?;
@@ -88,6 +98,54 @@ fn foo() {
88 } 98 }
89 99
90 #[test] 100 #[test]
101 fn goto_type_definition_record_expr_field() {
102 check(
103 r#"
104struct Bar;
105 // ^^^
106struct Foo { foo: Bar }
107fn foo() {
108 Foo { foo$0 }
109}
110"#,
111 );
112 check(
113 r#"
114struct Bar;
115 // ^^^
116struct Foo { foo: Bar }
117fn foo() {
118 Foo { foo$0: Bar }
119}
120"#,
121 );
122 }
123
124 #[test]
125 fn goto_type_definition_record_pat_field() {
126 check(
127 r#"
128struct Bar;
129 // ^^^
130struct Foo { foo: Bar }
131fn foo() {
132 let Foo { foo$0 };
133}
134"#,
135 );
136 check(
137 r#"
138struct Bar;
139 // ^^^
140struct Foo { foo: Bar }
141fn foo() {
142 let Foo { foo$0: bar };
143}
144"#,
145 );
146 }
147
148 #[test]
91 fn goto_type_definition_works_simple_ref() { 149 fn goto_type_definition_works_simple_ref() {
92 check( 150 check(
93 r#" 151 r#"
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c08516805..14cf94d60 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1821,9 +1821,10 @@ pub struct B$0ar
1821 ); 1821 );
1822 } 1822 }
1823 1823
1824 #[ignore = "path based links currently only support documentation on ModuleDef items"]
1825 #[test] 1824 #[test]
1826 fn test_hover_path_link_field() { 1825 fn test_hover_path_link_field() {
1826 // FIXME: Should be
1827 // [Foo](https://docs.rs/test/*/test/struct.Foo.html)
1827 check( 1828 check(
1828 r#" 1829 r#"
1829pub struct Foo; 1830pub struct Foo;
@@ -1845,7 +1846,7 @@ pub struct Bar {
1845 1846
1846 --- 1847 ---
1847 1848
1848 [Foo](https://docs.rs/test/*/test/struct.Foo.html) 1849 [Foo](struct.Foo.html)
1849 "#]], 1850 "#]],
1850 ); 1851 );
1851 } 1852 }
@@ -2999,29 +3000,24 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
2999 fn test_hover_async_block_impl_trait_has_goto_type_action() { 3000 fn test_hover_async_block_impl_trait_has_goto_type_action() {
3000 check_actions( 3001 check_actions(
3001 r#" 3002 r#"
3003//- minicore: future
3002struct S; 3004struct S;
3003fn foo() { 3005fn foo() {
3004 let fo$0o = async { S }; 3006 let fo$0o = async { S };
3005} 3007}
3006
3007#[prelude_import] use future::*;
3008mod future {
3009 #[lang = "future_trait"]
3010 pub trait Future { type Output; }
3011}
3012"#, 3008"#,
3013 expect![[r#" 3009 expect![[r#"
3014 [ 3010 [
3015 GoToType( 3011 GoToType(
3016 [ 3012 [
3017 HoverGotoTypeData { 3013 HoverGotoTypeData {
3018 mod_path: "test::future::Future", 3014 mod_path: "core::future::Future",
3019 nav: NavigationTarget { 3015 nav: NavigationTarget {
3020 file_id: FileId( 3016 file_id: FileId(
3021 0, 3017 1,
3022 ), 3018 ),
3023 full_range: 101..163, 3019 full_range: 244..426,
3024 focus_range: 140..146, 3020 focus_range: 283..289,
3025 name: "Future", 3021 name: "Future",
3026 kind: Trait, 3022 kind: Trait,
3027 description: "pub trait Future", 3023 description: "pub trait Future",
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 0511efae3..4bd073cc3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -24,7 +24,6 @@ mod display;
24 24
25mod annotations; 25mod annotations;
26mod call_hierarchy; 26mod call_hierarchy;
27mod diagnostics;
28mod expand_macro; 27mod expand_macro;
29mod extend_selection; 28mod extend_selection;
30mod file_structure; 29mod file_structure;
@@ -40,6 +39,7 @@ mod matching_brace;
40mod move_item; 39mod move_item;
41mod parent_module; 40mod parent_module;
42mod references; 41mod references;
42mod rename;
43mod fn_references; 43mod fn_references;
44mod runnables; 44mod runnables;
45mod ssr; 45mod ssr;
@@ -71,7 +71,6 @@ use crate::display::ToNav;
71pub use crate::{ 71pub use crate::{
72 annotations::{Annotation, AnnotationConfig, AnnotationKind}, 72 annotations::{Annotation, AnnotationConfig, AnnotationKind},
73 call_hierarchy::CallItem, 73 call_hierarchy::CallItem,
74 diagnostics::{Diagnostic, DiagnosticsConfig, Severity},
75 display::navigation_target::NavigationTarget, 74 display::navigation_target::NavigationTarget,
76 expand_macro::ExpandedMacro, 75 expand_macro::ExpandedMacro,
77 file_structure::{StructureNode, StructureNodeKind}, 76 file_structure::{StructureNode, StructureNodeKind},
@@ -81,7 +80,8 @@ pub use crate::{
81 markup::Markup, 80 markup::Markup,
82 move_item::Direction, 81 move_item::Direction,
83 prime_caches::PrimeCachesProgress, 82 prime_caches::PrimeCachesProgress,
84 references::{rename::RenameError, ReferenceSearchResult}, 83 references::ReferenceSearchResult,
84 rename::RenameError,
85 runnables::{Runnable, RunnableKind, TestId}, 85 runnables::{Runnable, RunnableKind, TestId},
86 syntax_highlighting::{ 86 syntax_highlighting::{
87 tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, 87 tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
@@ -109,6 +109,7 @@ pub use ide_db::{
109 symbol_index::Query, 109 symbol_index::Query,
110 RootDatabase, SymbolKind, 110 RootDatabase, SymbolKind,
111}; 111};
112pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, Severity};
112pub use ide_ssr::SsrError; 113pub use ide_ssr::SsrError;
113pub use syntax::{TextRange, TextSize}; 114pub use syntax::{TextRange, TextSize};
114pub use text_edit::{Indel, TextEdit}; 115pub use text_edit::{Indel, TextEdit};
@@ -536,7 +537,7 @@ impl Analysis {
536 ) -> Cancellable<Vec<Assist>> { 537 ) -> Cancellable<Vec<Assist>> {
537 self.with_db(|db| { 538 self.with_db(|db| {
538 let ssr_assists = ssr::ssr_assists(db, &resolve, frange); 539 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
539 let mut acc = Assist::get(db, config, resolve, frange); 540 let mut acc = ide_assists::assists(db, config, resolve, frange);
540 acc.extend(ssr_assists.into_iter()); 541 acc.extend(ssr_assists.into_iter());
541 acc 542 acc
542 }) 543 })
@@ -549,7 +550,7 @@ impl Analysis {
549 resolve: AssistResolveStrategy, 550 resolve: AssistResolveStrategy,
550 file_id: FileId, 551 file_id: FileId,
551 ) -> Cancellable<Vec<Diagnostic>> { 552 ) -> Cancellable<Vec<Diagnostic>> {
552 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) 553 self.with_db(|db| ide_diagnostics::diagnostics(db, config, &resolve, file_id))
553 } 554 }
554 555
555 /// Convenience function to return assists + quick fixes for diagnostics 556 /// Convenience function to return assists + quick fixes for diagnostics
@@ -566,9 +567,8 @@ impl Analysis {
566 }; 567 };
567 568
568 self.with_db(|db| { 569 self.with_db(|db| {
569 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
570 let diagnostic_assists = if include_fixes { 570 let diagnostic_assists = if include_fixes {
571 diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) 571 ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id)
572 .into_iter() 572 .into_iter()
573 .flat_map(|it| it.fixes.unwrap_or_default()) 573 .flat_map(|it| it.fixes.unwrap_or_default())
574 .filter(|it| it.target.intersect(frange.range).is_some()) 574 .filter(|it| it.target.intersect(frange.range).is_some())
@@ -576,10 +576,12 @@ impl Analysis {
576 } else { 576 } else {
577 Vec::new() 577 Vec::new()
578 }; 578 };
579 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
580 let assists = ide_assists::assists(db, assist_config, resolve, frange);
579 581
580 let mut res = Assist::get(db, assist_config, resolve, frange); 582 let mut res = diagnostic_assists;
581 res.extend(ssr_assists.into_iter()); 583 res.extend(ssr_assists.into_iter());
582 res.extend(diagnostic_assists.into_iter()); 584 res.extend(assists.into_iter());
583 585
584 res 586 res
585 }) 587 })
@@ -592,14 +594,14 @@ impl Analysis {
592 position: FilePosition, 594 position: FilePosition,
593 new_name: &str, 595 new_name: &str,
594 ) -> Cancellable<Result<SourceChange, RenameError>> { 596 ) -> Cancellable<Result<SourceChange, RenameError>> {
595 self.with_db(|db| references::rename::rename(db, position, new_name)) 597 self.with_db(|db| rename::rename(db, position, new_name))
596 } 598 }
597 599
598 pub fn prepare_rename( 600 pub fn prepare_rename(
599 &self, 601 &self,
600 position: FilePosition, 602 position: FilePosition,
601 ) -> Cancellable<Result<RangeInfo<()>, RenameError>> { 603 ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
602 self.with_db(|db| references::rename::prepare_rename(db, position)) 604 self.with_db(|db| rename::prepare_rename(db, position))
603 } 605 }
604 606
605 pub fn will_rename_file( 607 pub fn will_rename_file(
@@ -607,7 +609,7 @@ impl Analysis {
607 file_id: FileId, 609 file_id: FileId,
608 new_name_stem: &str, 610 new_name_stem: &str,
609 ) -> Cancellable<Option<SourceChange>> { 611 ) -> Cancellable<Option<SourceChange>> {
610 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) 612 self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem))
611 } 613 }
612 614
613 pub fn structural_search_replace( 615 pub fn structural_search_replace(
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index a0fdead2c..945c9b9e1 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -9,8 +9,6 @@
9//! at the index that the match starts at and its tree parent is 9//! at the index that the match starts at and its tree parent is
10//! resolved to the search element definition, we get a reference. 10//! resolved to the search element definition, we get a reference.
11 11
12pub(crate) mod rename;
13
14use hir::{PathResolution, Semantics}; 12use hir::{PathResolution, Semantics};
15use ide_db::{ 13use ide_db::{
16 base_db::FileId, 14 base_db::FileId,
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/rename.rs
index 6b3d02bf4..8096dfa0e 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -1,45 +1,25 @@
1//! Renaming functionality 1//! Renaming functionality.
2//! 2//!
3//! All reference and file rename requests go through here where the corresponding [`SourceChange`]s 3//! This is mostly front-end for [`ide_db::rename`], but it also includes the
4//! will be calculated. 4//! tests. This module also implements a couple of magic tricks, like renaming
5use std::fmt::{self, Display}; 5//! `self` and to `self` (to switch between associated function and method).
6 6use hir::{AsAssocItem, InFile, Semantics};
7use either::Either;
8use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics};
9use ide_db::{ 7use ide_db::{
10 base_db::{AnchoredPathBuf, FileId, FileRange}, 8 base_db::FileId,
11 defs::{Definition, NameClass, NameRefClass}, 9 defs::{Definition, NameClass, NameRefClass},
12 search::FileReference, 10 rename::{bail, format_err, source_edit_from_references, IdentifierKind},
13 RootDatabase, 11 RootDatabase,
14}; 12};
15use stdx::never; 13use stdx::never;
16use syntax::{ 14use syntax::{ast, AstNode, SyntaxNode};
17 ast::{self, NameOwner},
18 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
19};
20 15
21use text_edit::TextEdit; 16use text_edit::TextEdit;
22 17
23use crate::{FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; 18use crate::{FilePosition, RangeInfo, SourceChange};
24
25type RenameResult<T> = Result<T, RenameError>;
26#[derive(Debug)]
27pub struct RenameError(String);
28
29impl fmt::Display for RenameError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 Display::fmt(&self.0, f)
32 }
33}
34 19
35macro_rules! format_err { 20pub use ide_db::rename::RenameError;
36 ($fmt:expr) => {RenameError(format!($fmt))};
37 ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))}
38}
39 21
40macro_rules! bail { 22type RenameResult<T> = Result<T, RenameError>;
41 ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))}
42}
43 23
44/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is 24/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
45/// being targeted for a rename. 25/// being targeted for a rename.
@@ -52,7 +32,8 @@ pub(crate) fn prepare_rename(
52 let syntax = source_file.syntax(); 32 let syntax = source_file.syntax();
53 33
54 let def = find_definition(&sema, syntax, position)?; 34 let def = find_definition(&sema, syntax, position)?;
55 let frange = def_name_range(&&sema, def) 35 let frange = def
36 .range_for_rename(&sema)
56 .ok_or_else(|| format_err!("No references found at position"))?; 37 .ok_or_else(|| format_err!("No references found at position"))?;
57 Ok(RangeInfo::new(frange.range, ())) 38 Ok(RangeInfo::new(frange.range, ()))
58} 39}
@@ -98,14 +79,7 @@ pub(crate) fn rename_with_semantics(
98 } 79 }
99 } 80 }
100 81
101 match def { 82 def.rename(sema, new_name)
102 Definition::ModuleDef(hir::ModuleDef::Module(module)) => rename_mod(sema, module, new_name),
103 Definition::SelfType(_) => bail!("Cannot rename `Self`"),
104 Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
105 bail!("Cannot rename builtin type")
106 }
107 def => rename_reference(sema, def, new_name),
108 }
109} 83}
110 84
111/// Called by the client when it is about to rename a file. 85/// Called by the client when it is about to rename a file.
@@ -116,38 +90,12 @@ pub(crate) fn will_rename_file(
116) -> Option<SourceChange> { 90) -> Option<SourceChange> {
117 let sema = Semantics::new(db); 91 let sema = Semantics::new(db);
118 let module = sema.to_module_def(file_id)?; 92 let module = sema.to_module_def(file_id)?;
119 let mut change = rename_mod(&sema, module, new_name_stem).ok()?; 93 let def = Definition::ModuleDef(module.into());
94 let mut change = def.rename(&sema, new_name_stem).ok()?;
120 change.file_system_edits.clear(); 95 change.file_system_edits.clear();
121 Some(change) 96 Some(change)
122} 97}
123 98
124#[derive(Copy, Clone, Debug, PartialEq)]
125enum IdentifierKind {
126 Ident,
127 Lifetime,
128 Underscore,
129}
130
131impl IdentifierKind {
132 fn classify(new_name: &str) -> RenameResult<IdentifierKind> {
133 match lex_single_syntax_kind(new_name) {
134 Some(res) => match res {
135 (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident),
136 (T![_], _) => Ok(IdentifierKind::Underscore),
137 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
138 Ok(IdentifierKind::Lifetime)
139 }
140 (SyntaxKind::LIFETIME_IDENT, _) => {
141 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
142 }
143 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
144 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
145 },
146 None => bail!("Invalid name `{}`: not an identifier", new_name),
147 }
148 }
149}
150
151fn find_definition( 99fn find_definition(
152 sema: &Semantics<RootDatabase>, 100 sema: &Semantics<RootDatabase>,
153 syntax: &SyntaxNode, 101 syntax: &SyntaxNode,
@@ -189,126 +137,6 @@ fn find_definition(
189 .ok_or_else(|| format_err!("No references found at position")) 137 .ok_or_else(|| format_err!("No references found at position"))
190} 138}
191 139
192fn rename_mod(
193 sema: &Semantics<RootDatabase>,
194 module: hir::Module,
195 new_name: &str,
196) -> RenameResult<SourceChange> {
197 if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
198 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
199 }
200
201 let mut source_change = SourceChange::default();
202
203 let InFile { file_id, value: def_source } = module.definition_source(sema.db);
204 let file_id = file_id.original_file(sema.db);
205 if let ModuleSource::SourceFile(..) = def_source {
206 // mod is defined in path/to/dir/mod.rs
207 let path = if module.is_mod_rs(sema.db) {
208 format!("../{}/mod.rs", new_name)
209 } else {
210 format!("{}.rs", new_name)
211 };
212 let dst = AnchoredPathBuf { anchor: file_id, path };
213 let move_file = FileSystemEdit::MoveFile { src: file_id, dst };
214 source_change.push_file_system_edit(move_file);
215 }
216
217 if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) {
218 let file_id = file_id.original_file(sema.db);
219 match decl_source.name() {
220 Some(name) => source_change.insert_source_edit(
221 file_id,
222 TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
223 ),
224 _ => never!("Module source node is missing a name"),
225 }
226 }
227 let def = Definition::ModuleDef(hir::ModuleDef::Module(module));
228 let usages = def.usages(sema).all();
229 let ref_edits = usages.iter().map(|(&file_id, references)| {
230 (file_id, source_edit_from_references(references, def, new_name))
231 });
232 source_change.extend(ref_edits);
233
234 Ok(source_change)
235}
236
237fn rename_reference(
238 sema: &Semantics<RootDatabase>,
239 mut def: Definition,
240 new_name: &str,
241) -> RenameResult<SourceChange> {
242 let ident_kind = IdentifierKind::classify(new_name)?;
243
244 if matches!(
245 def, // is target a lifetime?
246 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
247 ) {
248 match ident_kind {
249 IdentifierKind::Ident | IdentifierKind::Underscore => {
250 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
251 bail!("Invalid name `{}`: not a lifetime identifier", new_name);
252 }
253 IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime),
254 }
255 } else {
256 match (ident_kind, def) {
257 (IdentifierKind::Lifetime, _) => {
258 cov_mark::hit!(rename_not_an_ident_ref);
259 bail!("Invalid name `{}`: not an identifier", new_name);
260 }
261 (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local),
262 (IdentifierKind::Underscore, _) => (),
263 }
264 }
265
266 def = match def {
267 // HACK: resolve trait impl items to the item def of the trait definition
268 // so that we properly resolve all trait item references
269 Definition::ModuleDef(mod_def) => mod_def
270 .as_assoc_item(sema.db)
271 .and_then(|it| it.containing_trait_impl(sema.db))
272 .and_then(|it| {
273 it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) {
274 (hir::AssocItem::Function(trait_func), hir::ModuleDef::Function(func))
275 if trait_func.name(sema.db) == func.name(sema.db) =>
276 {
277 Some(Definition::ModuleDef(hir::ModuleDef::Function(trait_func)))
278 }
279 (hir::AssocItem::Const(trait_konst), hir::ModuleDef::Const(konst))
280 if trait_konst.name(sema.db) == konst.name(sema.db) =>
281 {
282 Some(Definition::ModuleDef(hir::ModuleDef::Const(trait_konst)))
283 }
284 (
285 hir::AssocItem::TypeAlias(trait_type_alias),
286 hir::ModuleDef::TypeAlias(type_alias),
287 ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => {
288 Some(Definition::ModuleDef(hir::ModuleDef::TypeAlias(trait_type_alias)))
289 }
290 _ => None,
291 })
292 })
293 .unwrap_or(def),
294 _ => def,
295 };
296 let usages = def.usages(sema).all();
297
298 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
299 cov_mark::hit!(rename_underscore_multiple);
300 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
301 }
302 let mut source_change = SourceChange::default();
303 source_change.extend(usages.iter().map(|(&file_id, references)| {
304 (file_id, source_edit_from_references(references, def, new_name))
305 }));
306
307 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
308 source_change.insert_source_edit(file_id, edit);
309 Ok(source_change)
310}
311
312fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { 140fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> {
313 if never!(local.is_self(sema.db)) { 141 if never!(local.is_self(sema.db)) {
314 bail!("rename_to_self invoked on self"); 142 bail!("rename_to_self invoked on self");
@@ -426,243 +254,6 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
426 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) 254 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
427} 255}
428 256
429fn source_edit_from_references(
430 references: &[FileReference],
431 def: Definition,
432 new_name: &str,
433) -> TextEdit {
434 let mut edit = TextEdit::builder();
435 for reference in references {
436 let (range, replacement) = match &reference.name {
437 // if the ranges differ then the node is inside a macro call, we can't really attempt
438 // to make special rewrites like shorthand syntax and such, so just rename the node in
439 // the macro input
440 ast::NameLike::NameRef(name_ref)
441 if name_ref.syntax().text_range() == reference.range =>
442 {
443 source_edit_from_name_ref(name_ref, new_name, def)
444 }
445 ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => {
446 source_edit_from_name(name, new_name)
447 }
448 _ => None,
449 }
450 .unwrap_or_else(|| (reference.range, new_name.to_string()));
451 edit.replace(range, replacement);
452 }
453 edit.finish()
454}
455
456fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
457 if let Some(_) = ast::RecordPatField::for_field_name(name) {
458 if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
459 return Some((
460 TextRange::empty(ident_pat.syntax().text_range().start()),
461 [new_name, ": "].concat(),
462 ));
463 }
464 }
465 None
466}
467
468fn source_edit_from_name_ref(
469 name_ref: &ast::NameRef,
470 new_name: &str,
471 def: Definition,
472) -> Option<(TextRange, String)> {
473 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
474 let rcf_name_ref = record_field.name_ref();
475 let rcf_expr = record_field.expr();
476 match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
477 // field: init-expr, check if we can use a field init shorthand
478 (Some(field_name), Some(init)) => {
479 if field_name == *name_ref {
480 if init.text() == new_name {
481 cov_mark::hit!(test_rename_field_put_init_shorthand);
482 // same names, we can use a shorthand here instead.
483 // we do not want to erase attributes hence this range start
484 let s = field_name.syntax().text_range().start();
485 let e = record_field.syntax().text_range().end();
486 return Some((TextRange::new(s, e), new_name.to_owned()));
487 }
488 } else if init == *name_ref {
489 if field_name.text() == new_name {
490 cov_mark::hit!(test_rename_local_put_init_shorthand);
491 // same names, we can use a shorthand here instead.
492 // we do not want to erase attributes hence this range start
493 let s = field_name.syntax().text_range().start();
494 let e = record_field.syntax().text_range().end();
495 return Some((TextRange::new(s, e), new_name.to_owned()));
496 }
497 }
498 None
499 }
500 // init shorthand
501 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
502 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
503 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
504 cov_mark::hit!(test_rename_field_in_field_shorthand);
505 let s = name_ref.syntax().text_range().start();
506 Some((TextRange::empty(s), format!("{}: ", new_name)))
507 }
508 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
509 cov_mark::hit!(test_rename_local_in_field_shorthand);
510 let s = name_ref.syntax().text_range().end();
511 Some((TextRange::empty(s), format!(": {}", new_name)))
512 }
513 _ => None,
514 }
515 } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
516 let rcf_name_ref = record_field.name_ref();
517 let rcf_pat = record_field.pat();
518 match (rcf_name_ref, rcf_pat) {
519 // field: rename
520 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
521 // field name is being renamed
522 if pat.name().map_or(false, |it| it.text() == new_name) {
523 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
524 // same names, we can use a shorthand here instead/
525 // we do not want to erase attributes hence this range start
526 let s = field_name.syntax().text_range().start();
527 let e = record_field.syntax().text_range().end();
528 Some((TextRange::new(s, e), pat.to_string()))
529 } else {
530 None
531 }
532 }
533 _ => None,
534 }
535 } else {
536 None
537 }
538}
539
540fn source_edit_from_def(
541 sema: &Semantics<RootDatabase>,
542 def: Definition,
543 new_name: &str,
544) -> RenameResult<(FileId, TextEdit)> {
545 let frange: FileRange = def_name_range(sema, def)
546 .ok_or_else(|| format_err!("No identifier available to rename"))?;
547
548 let mut replacement_text = String::new();
549 let mut repl_range = frange.range;
550 if let Definition::Local(local) = def {
551 if let Either::Left(pat) = local.source(sema.db).value {
552 if matches!(
553 pat.syntax().parent().and_then(ast::RecordPatField::cast),
554 Some(pat_field) if pat_field.name_ref().is_none()
555 ) {
556 replacement_text.push_str(": ");
557 replacement_text.push_str(new_name);
558 repl_range = TextRange::new(
559 pat.syntax().text_range().end(),
560 pat.syntax().text_range().end(),
561 );
562 }
563 }
564 }
565 if replacement_text.is_empty() {
566 replacement_text.push_str(new_name);
567 }
568 let edit = TextEdit::replace(repl_range, replacement_text);
569 Ok((frange.file_id, edit))
570}
571
572fn def_name_range(sema: &Semantics<RootDatabase>, def: Definition) -> Option<FileRange> {
573 // FIXME: the `original_file_range` calls here are wrong -- they never fail,
574 // and _fall back_ to the entirety of the macro call. Such fall back is
575 // incorrect for renames. The safe behavior would be to return an error for
576 // such cases. The correct behavior would be to return an auxiliary list of
577 // "can't rename these occurrences in macros" items, and then show some kind
578 // of a dialog to the user.
579
580 let res = match def {
581 Definition::Macro(mac) => {
582 let src = mac.source(sema.db)?;
583 let name = match &src.value {
584 Either::Left(it) => it.name()?,
585 Either::Right(it) => it.name()?,
586 };
587 src.with_value(name.syntax()).original_file_range(sema.db)
588 }
589 Definition::Field(field) => {
590 let src = field.source(sema.db)?;
591
592 match &src.value {
593 FieldSource::Named(record_field) => {
594 let name = record_field.name()?;
595 src.with_value(name.syntax()).original_file_range(sema.db)
596 }
597 FieldSource::Pos(_) => {
598 return None;
599 }
600 }
601 }
602 Definition::ModuleDef(module_def) => match module_def {
603 hir::ModuleDef::Module(module) => {
604 let src = module.declaration_source(sema.db)?;
605 let name = src.value.name()?;
606 src.with_value(name.syntax()).original_file_range(sema.db)
607 }
608 hir::ModuleDef::Function(it) => name_range(it, sema)?,
609 hir::ModuleDef::Adt(adt) => match adt {
610 hir::Adt::Struct(it) => name_range(it, sema)?,
611 hir::Adt::Union(it) => name_range(it, sema)?,
612 hir::Adt::Enum(it) => name_range(it, sema)?,
613 },
614 hir::ModuleDef::Variant(it) => name_range(it, sema)?,
615 hir::ModuleDef::Const(it) => name_range(it, sema)?,
616 hir::ModuleDef::Static(it) => name_range(it, sema)?,
617 hir::ModuleDef::Trait(it) => name_range(it, sema)?,
618 hir::ModuleDef::TypeAlias(it) => name_range(it, sema)?,
619 hir::ModuleDef::BuiltinType(_) => return None,
620 },
621 Definition::SelfType(_) => return None,
622 Definition::Local(local) => {
623 let src = local.source(sema.db);
624 let name = match &src.value {
625 Either::Left(bind_pat) => bind_pat.name()?,
626 Either::Right(_) => return None,
627 };
628 src.with_value(name.syntax()).original_file_range(sema.db)
629 }
630 Definition::GenericParam(generic_param) => match generic_param {
631 hir::GenericParam::TypeParam(type_param) => {
632 let src = type_param.source(sema.db)?;
633 let name = match &src.value {
634 Either::Left(_) => return None,
635 Either::Right(type_param) => type_param.name()?,
636 };
637 src.with_value(name.syntax()).original_file_range(sema.db)
638 }
639 hir::GenericParam::LifetimeParam(lifetime_param) => {
640 let src = lifetime_param.source(sema.db)?;
641 let lifetime = src.value.lifetime()?;
642 src.with_value(lifetime.syntax()).original_file_range(sema.db)
643 }
644 hir::GenericParam::ConstParam(it) => name_range(it, sema)?,
645 },
646 Definition::Label(label) => {
647 let src = label.source(sema.db);
648 let lifetime = src.value.lifetime()?;
649 src.with_value(lifetime.syntax()).original_file_range(sema.db)
650 }
651 };
652 return Some(res);
653
654 fn name_range<D>(def: D, sema: &Semantics<RootDatabase>) -> Option<FileRange>
655 where
656 D: HasSource,
657 D::Ast: ast::NameOwner,
658 {
659 let src = def.source(sema.db)?;
660 let name = src.value.name()?;
661 let res = src.with_value(name.syntax()).original_file_range(sema.db);
662 Some(res)
663 }
664}
665
666#[cfg(test)] 257#[cfg(test)]
667mod tests { 258mod tests {
668 use expect_test::{expect, Expect}; 259 use expect_test::{expect, Expect};
@@ -2178,4 +1769,22 @@ fn f() { <()>::BAR$0; }"#,
2178 res, 1769 res,
2179 ); 1770 );
2180 } 1771 }
1772
1773 #[test]
1774 fn macros_are_broken_lol() {
1775 cov_mark::check!(macros_are_broken_lol);
1776 check(
1777 "lol",
1778 r#"
1779macro_rules! m { () => { fn f() {} } }
1780m!();
1781fn main() { f$0() }
1782"#,
1783 r#"
1784macro_rules! m { () => { fn f() {} } }
1785lol
1786fn main() { lol() }
1787"#,
1788 )
1789 }
2181} 1790}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 7a53268e8..6834fe11a 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -48,7 +48,13 @@ pub(super) fn element(
48 match name_kind { 48 match name_kind {
49 Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(), 49 Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(),
50 Some(NameClass::Definition(def)) => { 50 Some(NameClass::Definition(def)) => {
51 highlight_def(db, krate, def) | HlMod::Definition 51 let mut h = highlight_def(db, krate, def) | HlMod::Definition;
52 if let Definition::ModuleDef(hir::ModuleDef::Trait(trait_)) = &def {
53 if trait_.is_unsafe(db) {
54 h |= HlMod::Unsafe;
55 }
56 }
57 h
52 } 58 }
53 Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def), 59 Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def),
54 Some(NameClass::PatFieldShorthand { field_ref, .. }) => { 60 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
@@ -87,20 +93,34 @@ pub(super) fn element(
87 93
88 let mut h = highlight_def(db, krate, def); 94 let mut h = highlight_def(db, krate, def);
89 95
90 if let Definition::Local(local) = &def { 96 match def {
91 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { 97 Definition::Local(local)
98 if is_consumed_lvalue(
99 name_ref.syntax().clone().into(),
100 &local,
101 db,
102 ) =>
103 {
92 h |= HlMod::Consuming; 104 h |= HlMod::Consuming;
93 } 105 }
94 } 106 Definition::ModuleDef(hir::ModuleDef::Trait(trait_))
95 107 if trait_.is_unsafe(db) =>
96 if let Some(parent) = name_ref.syntax().parent() { 108 {
97 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { 109 if ast::Impl::for_trait_name_ref(&name_ref).is_some() {
98 if let Definition::Field(field) = def { 110 h |= HlMod::Unsafe;
99 if let hir::VariantDef::Union(_) = field.parent_def(db) { 111 }
100 h |= HlMod::Unsafe; 112 }
113 Definition::Field(field) => {
114 if let Some(parent) = name_ref.syntax().parent() {
115 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
116 if let hir::VariantDef::Union(_) = field.parent_def(db)
117 {
118 h |= HlMod::Unsafe;
119 }
101 } 120 }
102 } 121 }
103 } 122 }
123 _ => (),
104 } 124 }
105 125
106 h 126 h
@@ -354,15 +374,7 @@ fn highlight_def(db: &RootDatabase, krate: Option<hir::Crate>, def: Definition)
354 374
355 h 375 h
356 } 376 }
357 hir::ModuleDef::Trait(trait_) => { 377 hir::ModuleDef::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)),
358 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Trait));
359
360 if trait_.is_unsafe(db) {
361 h |= HlMod::Unsafe;
362 }
363
364 h
365 }
366 hir::ModuleDef::TypeAlias(type_) => { 378 hir::ModuleDef::TypeAlias(type_) => {
367 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); 379 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
368 380
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 478facfee..21376a7ae 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -67,6 +67,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
67.field { color: #94BFF3; } 67.field { color: #94BFF3; }
68.function { color: #93E0E3; } 68.function { color: #93E0E3; }
69.function.unsafe { color: #BC8383; } 69.function.unsafe { color: #BC8383; }
70.trait.unsafe { color: #BC8383; }
70.operator.unsafe { color: #BC8383; } 71.operator.unsafe { color: #BC8383; }
71.parameter { color: #94BFF3; } 72.parameter { color: #94BFF3; }
72.text { color: #DCDCCC; } 73.text { color: #DCDCCC; }
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 a0ea1db34..4e85f7c0b 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
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
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 921a956e6..79a285107 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index ca9bb1e7d..13f589cc0 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
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 6202a03ce..50df376ae 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index e860d713e..96cb09642 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
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 68165bdbf..55453468b 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
@@ -61,6 +62,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
61 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span> 62 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span>
62<span class="brace">}</span> 63<span class="brace">}</span>
63 64
65<span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">UnsafeTrait</span> <span class="brace">{</span><span class="brace">}</span>
66<span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="struct">Packed</span> <span class="brace">{</span><span class="brace">}</span>
67
68<span class="keyword">fn</span> <span class="function declaration">require_unsafe_trait</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
69
64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> 70<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
65 <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> 71 <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
66<span class="brace">}</span> 72<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 59f1e8e4c..9232cf905 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html
index 9ab46d05c..082837328 100644
--- a/crates/ide/src/syntax_highlighting/test_data/injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/injection.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index 666b0b228..763917714 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
15.field { color: #94BFF3; } 15.field { color: #94BFF3; }
16.function { color: #93E0E3; } 16.function { color: #93E0E3; }
17.function.unsafe { color: #BC8383; } 17.function.unsafe { color: #BC8383; }
18.trait.unsafe { color: #BC8383; }
18.operator.unsafe { color: #BC8383; } 19.operator.unsafe { color: #BC8383; }
19.parameter { color: #94BFF3; } 20.parameter { color: #94BFF3; }
20.text { color: #DCDCCC; } 21.text { color: #DCDCCC; }
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index f7d8334a0..4f0b1ce85 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -527,6 +527,11 @@ struct Packed {
527 a: u16, 527 a: u16,
528} 528}
529 529
530unsafe trait UnsafeTrait {}
531unsafe impl UnsafeTrait for Packed {}
532
533fn require_unsafe_trait<T: UnsafeTrait>(_: T) {}
534
530trait DoTheAutoref { 535trait DoTheAutoref {
531 fn calls_autoref(&self); 536 fn calls_autoref(&self);
532} 537}
diff --git a/crates/ide_assists/src/handlers/fix_visibility.rs b/crates/ide_assists/src/handlers/fix_visibility.rs
index 9b432e92f..f834bf16a 100644
--- a/crates/ide_assists/src/handlers/fix_visibility.rs
+++ b/crates/ide_assists/src/handlers/fix_visibility.rs
@@ -361,8 +361,6 @@ pub struct Foo { pub bar: () }
361 } 361 }
362 362
363 #[test] 363 #[test]
364 #[ignore]
365 // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
366 fn fix_visibility_of_union_field() { 364 fn fix_visibility_of_union_field() {
367 check_assist( 365 check_assist(
368 fix_visibility, 366 fix_visibility,
@@ -583,25 +581,25 @@ pub struct Foo { pub(crate) bar: () }
583 } 581 }
584 582
585 #[test] 583 #[test]
586 #[ignore]
587 // FIXME handle reexports properly
588 fn fix_visibility_of_reexport() { 584 fn fix_visibility_of_reexport() {
585 // FIXME: broken test, this should fix visibility of the re-export
586 // rather than the struct.
589 check_assist( 587 check_assist(
590 fix_visibility, 588 fix_visibility,
591 r" 589 r#"
592 mod foo { 590mod foo {
593 use bar::Baz; 591 use bar::Baz;
594 mod bar { pub(super) struct Baz; } 592 mod bar { pub(super) struct Baz; }
595 } 593}
596 foo::Baz$0 594foo::Baz$0
597 ", 595"#,
598 r" 596 r#"
599 mod foo { 597mod foo {
600 $0pub(crate) use bar::Baz; 598 use bar::Baz;
601 mod bar { pub(super) struct Baz; } 599 mod bar { $0pub(crate) struct Baz; }
602 } 600}
603 foo::Baz 601foo::Baz
604 ", 602"#,
605 ) 603 )
606 } 604 }
607} 605}
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 706c995ac..6a658d4cf 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -811,9 +811,8 @@ fn bar(baz: Baz::Bof) ${0:-> ()} {
811 } 811 }
812 812
813 #[test] 813 #[test]
814 #[ignore]
815 // FIXME fix printing the generics of a `Ty` to make this test pass
816 fn add_function_with_generic_arg() { 814 fn add_function_with_generic_arg() {
815 // FIXME: This is wrong, generated `bar` should include generic parameter.
817 check_assist( 816 check_assist(
818 generate_function, 817 generate_function,
819 r" 818 r"
@@ -826,7 +825,7 @@ fn foo<T>(t: T) {
826 bar(t) 825 bar(t)
827} 826}
828 827
829fn bar<T>(t: T) ${0:-> ()} { 828fn bar(t: T) ${0:-> ()} {
830 todo!() 829 todo!()
831} 830}
832", 831",
@@ -834,9 +833,8 @@ fn bar<T>(t: T) ${0:-> ()} {
834 } 833 }
835 834
836 #[test] 835 #[test]
837 #[ignore]
838 // FIXME Fix function type printing to make this test pass
839 fn add_function_with_fn_arg() { 836 fn add_function_with_fn_arg() {
837 // FIXME: The argument in `bar` is wrong.
840 check_assist( 838 check_assist(
841 generate_function, 839 generate_function,
842 r" 840 r"
@@ -857,7 +855,7 @@ fn foo() {
857 bar(Baz::new); 855 bar(Baz::new);
858} 856}
859 857
860fn bar(arg: fn() -> Baz) ${0:-> ()} { 858fn bar(new: fn) ${0:-> ()} {
861 todo!() 859 todo!()
862} 860}
863", 861",
@@ -865,9 +863,8 @@ fn bar(arg: fn() -> Baz) ${0:-> ()} {
865 } 863 }
866 864
867 #[test] 865 #[test]
868 #[ignore]
869 // FIXME Fix closure type printing to make this test pass
870 fn add_function_with_closure_arg() { 866 fn add_function_with_closure_arg() {
867 // FIXME: The argument in `bar` is wrong.
871 check_assist( 868 check_assist(
872 generate_function, 869 generate_function,
873 r" 870 r"
@@ -882,7 +879,7 @@ fn foo() {
882 bar(closure) 879 bar(closure)
883} 880}
884 881
885fn bar(closure: impl Fn(i64) -> i64) ${0:-> ()} { 882fn bar(closure: ()) ${0:-> ()} {
886 todo!() 883 todo!()
887} 884}
888", 885",
@@ -986,13 +983,10 @@ fn foo() {
986 } 983 }
987 984
988 #[test] 985 #[test]
989 #[ignore]
990 // Ignored until local imports are supported.
991 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
992 fn qualified_path_uses_correct_scope() { 986 fn qualified_path_uses_correct_scope() {
993 check_assist( 987 check_assist(
994 generate_function, 988 generate_function,
995 " 989 r#"
996mod foo { 990mod foo {
997 pub struct Foo; 991 pub struct Foo;
998} 992}
@@ -1001,8 +995,8 @@ fn bar() {
1001 let foo = Foo; 995 let foo = Foo;
1002 baz$0(foo) 996 baz$0(foo)
1003} 997}
1004", 998"#,
1005 " 999 r#"
1006mod foo { 1000mod foo {
1007 pub struct Foo; 1001 pub struct Foo;
1008} 1002}
@@ -1015,7 +1009,7 @@ fn bar() {
1015fn baz(foo: foo::Foo) ${0:-> ()} { 1009fn baz(foo: foo::Foo) ${0:-> ()} {
1016 todo!() 1010 todo!()
1017} 1011}
1018", 1012"#,
1019 ) 1013 )
1020 } 1014 }
1021 1015
@@ -1141,40 +1135,29 @@ fn bar() {}
1141 // The assist is only active if the cursor is on an unresolved path, 1135 // The assist is only active if the cursor is on an unresolved path,
1142 // but the assist should only be offered if the path is a function call. 1136 // but the assist should only be offered if the path is a function call.
1143 generate_function, 1137 generate_function,
1144 r" 1138 r#"
1145fn foo() { 1139fn foo() {
1146 bar(b$0az); 1140 bar(b$0az);
1147} 1141}
1148 1142
1149fn bar(baz: ()) {} 1143fn bar(baz: ()) {}
1150", 1144"#,
1151 ) 1145 )
1152 } 1146 }
1153 1147
1154 #[test] 1148 #[test]
1155 #[ignore]
1156 fn create_method_with_no_args() { 1149 fn create_method_with_no_args() {
1157 check_assist( 1150 // FIXME: This is wrong, this should just work.
1151 check_assist_not_applicable(
1158 generate_function, 1152 generate_function,
1159 r" 1153 r#"
1160struct Foo; 1154struct Foo;
1161impl Foo { 1155impl Foo {
1162 fn foo(&self) { 1156 fn foo(&self) {
1163 self.bar()$0; 1157 self.bar()$0;
1164 } 1158 }
1165} 1159}
1166 ", 1160 "#,
1167 r"
1168struct Foo;
1169impl Foo {
1170 fn foo(&self) {
1171 self.bar();
1172 }
1173 fn bar(&self) {
1174 todo!();
1175 }
1176}
1177 ",
1178 ) 1161 )
1179 } 1162 }
1180} 1163}
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index f91770a76..1d7be183a 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -216,28 +216,28 @@ mod tests {
216 cov_mark::check!(qualify_path_unqualified_name); 216 cov_mark::check!(qualify_path_unqualified_name);
217 check_assist( 217 check_assist(
218 qualify_path, 218 qualify_path,
219 r" 219 r#"
220 mod std { 220mod std {
221 pub mod fmt { 221 pub mod fmt {
222 pub struct Formatter; 222 pub struct Formatter;
223 } 223 }
224 } 224}
225 225
226 use std::fmt; 226use std::fmt;
227 227
228 $0Formatter 228$0Formatter
229 ", 229"#,
230 r" 230 r#"
231 mod std { 231mod std {
232 pub mod fmt { 232 pub mod fmt {
233 pub struct Formatter; 233 pub struct Formatter;
234 } 234 }
235 } 235}
236 236
237 use std::fmt; 237use std::fmt;
238 238
239 fmt::Formatter 239fmt::Formatter
240 ", 240"#,
241 ); 241 );
242 } 242 }
243 243
@@ -245,20 +245,20 @@ mod tests {
245 fn applicable_when_found_an_import() { 245 fn applicable_when_found_an_import() {
246 check_assist( 246 check_assist(
247 qualify_path, 247 qualify_path,
248 r" 248 r#"
249 $0PubStruct 249$0PubStruct
250 250
251 pub mod PubMod { 251pub mod PubMod {
252 pub struct PubStruct; 252 pub struct PubStruct;
253 } 253}
254 ", 254"#,
255 r" 255 r#"
256 PubMod::PubStruct 256PubMod::PubStruct
257 257
258 pub mod PubMod { 258pub mod PubMod {
259 pub struct PubStruct; 259 pub struct PubStruct;
260 } 260}
261 ", 261"#,
262 ); 262 );
263 } 263 }
264 264
@@ -266,26 +266,26 @@ mod tests {
266 fn applicable_in_macros() { 266 fn applicable_in_macros() {
267 check_assist( 267 check_assist(
268 qualify_path, 268 qualify_path,
269 r" 269 r#"
270 macro_rules! foo { 270macro_rules! foo {
271 ($i:ident) => { fn foo(a: $i) {} } 271 ($i:ident) => { fn foo(a: $i) {} }
272 } 272}
273 foo!(Pub$0Struct); 273foo!(Pub$0Struct);
274 274
275 pub mod PubMod { 275pub mod PubMod {
276 pub struct PubStruct; 276 pub struct PubStruct;
277 } 277}
278 ", 278"#,
279 r" 279 r#"
280 macro_rules! foo { 280macro_rules! foo {
281 ($i:ident) => { fn foo(a: $i) {} } 281 ($i:ident) => { fn foo(a: $i) {} }
282 } 282}
283 foo!(PubMod::PubStruct); 283foo!(PubMod::PubStruct);
284 284
285 pub mod PubMod { 285pub mod PubMod {
286 pub struct PubStruct; 286 pub struct PubStruct;
287 } 287}
288 ", 288"#,
289 ); 289 );
290 } 290 }
291 291
@@ -293,32 +293,32 @@ mod tests {
293 fn applicable_when_found_multiple_imports() { 293 fn applicable_when_found_multiple_imports() {
294 check_assist( 294 check_assist(
295 qualify_path, 295 qualify_path,
296 r" 296 r#"
297 PubSt$0ruct 297PubSt$0ruct
298 298
299 pub mod PubMod1 { 299pub mod PubMod1 {
300 pub struct PubStruct; 300 pub struct PubStruct;
301 } 301}
302 pub mod PubMod2 { 302pub mod PubMod2 {
303 pub struct PubStruct; 303 pub struct PubStruct;
304 } 304}
305 pub mod PubMod3 { 305pub mod PubMod3 {
306 pub struct PubStruct; 306 pub struct PubStruct;
307 } 307}
308 ", 308"#,
309 r" 309 r#"
310 PubMod3::PubStruct 310PubMod3::PubStruct
311 311
312 pub mod PubMod1 { 312pub mod PubMod1 {
313 pub struct PubStruct; 313 pub struct PubStruct;
314 } 314}
315 pub mod PubMod2 { 315pub mod PubMod2 {
316 pub struct PubStruct; 316 pub struct PubStruct;
317 } 317}
318 pub mod PubMod3 { 318pub mod PubMod3 {
319 pub struct PubStruct; 319 pub struct PubStruct;
320 } 320}
321 ", 321"#,
322 ); 322 );
323 } 323 }
324 324
@@ -326,15 +326,15 @@ mod tests {
326 fn not_applicable_for_already_imported_types() { 326 fn not_applicable_for_already_imported_types() {
327 check_assist_not_applicable( 327 check_assist_not_applicable(
328 qualify_path, 328 qualify_path,
329 r" 329 r#"
330 use PubMod::PubStruct; 330use PubMod::PubStruct;
331 331
332 PubStruct$0 332PubStruct$0
333 333
334 pub mod PubMod { 334pub mod PubMod {
335 pub struct PubStruct; 335 pub struct PubStruct;
336 } 336}
337 ", 337"#,
338 ); 338 );
339 } 339 }
340 340
@@ -342,35 +342,32 @@ mod tests {
342 fn not_applicable_for_types_with_private_paths() { 342 fn not_applicable_for_types_with_private_paths() {
343 check_assist_not_applicable( 343 check_assist_not_applicable(
344 qualify_path, 344 qualify_path,
345 r" 345 r#"
346 PrivateStruct$0 346PrivateStruct$0
347 347
348 pub mod PubMod { 348pub mod PubMod {
349 struct PrivateStruct; 349 struct PrivateStruct;
350 } 350}
351 ", 351"#,
352 ); 352 );
353 } 353 }
354 354
355 #[test] 355 #[test]
356 fn not_applicable_when_no_imports_found() { 356 fn not_applicable_when_no_imports_found() {
357 check_assist_not_applicable( 357 check_assist_not_applicable(qualify_path, r#"PubStruct$0"#);
358 qualify_path,
359 "
360 PubStruct$0",
361 );
362 } 358 }
363 359
364 #[test] 360 #[test]
365 fn not_applicable_in_import_statements() { 361 fn not_applicable_in_import_statements() {
366 check_assist_not_applicable( 362 check_assist_not_applicable(
367 qualify_path, 363 qualify_path,
368 r" 364 r#"
369 use PubStruct$0; 365use PubStruct$0;
370 366
371 pub mod PubMod { 367pub mod PubMod {
372 pub struct PubStruct; 368 pub struct PubStruct;
373 }", 369}
370"#,
374 ); 371 );
375 } 372 }
376 373
@@ -378,20 +375,20 @@ mod tests {
378 fn qualify_function() { 375 fn qualify_function() {
379 check_assist( 376 check_assist(
380 qualify_path, 377 qualify_path,
381 r" 378 r#"
382 test_function$0 379test_function$0
383 380
384 pub mod PubMod { 381pub mod PubMod {
385 pub fn test_function() {}; 382 pub fn test_function() {};
386 } 383}
387 ", 384"#,
388 r" 385 r#"
389 PubMod::test_function 386PubMod::test_function
390 387
391 pub mod PubMod { 388pub mod PubMod {
392 pub fn test_function() {}; 389 pub fn test_function() {};
393 } 390}
394 ", 391"#,
395 ); 392 );
396 } 393 }
397 394
@@ -399,7 +396,7 @@ mod tests {
399 fn qualify_macro() { 396 fn qualify_macro() {
400 check_assist( 397 check_assist(
401 qualify_path, 398 qualify_path,
402 r" 399 r#"
403//- /lib.rs crate:crate_with_macro 400//- /lib.rs crate:crate_with_macro
404#[macro_export] 401#[macro_export]
405macro_rules! foo { 402macro_rules! foo {
@@ -410,12 +407,12 @@ macro_rules! foo {
410fn main() { 407fn main() {
411 foo$0 408 foo$0
412} 409}
413", 410"#,
414 r" 411 r#"
415fn main() { 412fn main() {
416 crate_with_macro::foo 413 crate_with_macro::foo
417} 414}
418", 415"#,
419 ); 416 );
420 } 417 }
421 418
@@ -423,13 +420,13 @@ fn main() {
423 fn qualify_path_target() { 420 fn qualify_path_target() {
424 check_assist_target( 421 check_assist_target(
425 qualify_path, 422 qualify_path,
426 r" 423 r#"
427 struct AssistInfo { 424struct AssistInfo {
428 group_label: Option<$0GroupLabel>, 425 group_label: Option<$0GroupLabel>,
429 } 426}
430 427
431 mod m { pub struct GroupLabel; } 428mod m { pub struct GroupLabel; }
432 ", 429"#,
433 "GroupLabel", 430 "GroupLabel",
434 ) 431 )
435 } 432 }
@@ -438,20 +435,20 @@ fn main() {
438 fn not_applicable_when_path_start_is_imported() { 435 fn not_applicable_when_path_start_is_imported() {
439 check_assist_not_applicable( 436 check_assist_not_applicable(
440 qualify_path, 437 qualify_path,
441 r" 438 r#"
442 pub mod mod1 { 439pub mod mod1 {
443 pub mod mod2 { 440 pub mod mod2 {
444 pub mod mod3 { 441 pub mod mod3 {
445 pub struct TestStruct; 442 pub struct TestStruct;
446 } 443 }
447 } 444 }
448 } 445}
449 446
450 use mod1::mod2; 447use mod1::mod2;
451 fn main() { 448fn main() {
452 mod2::mod3::TestStruct$0 449 mod2::mod3::TestStruct$0
453 } 450}
454 ", 451"#,
455 ); 452 );
456 } 453 }
457 454
@@ -459,16 +456,16 @@ fn main() {
459 fn not_applicable_for_imported_function() { 456 fn not_applicable_for_imported_function() {
460 check_assist_not_applicable( 457 check_assist_not_applicable(
461 qualify_path, 458 qualify_path,
462 r" 459 r#"
463 pub mod test_mod { 460pub mod test_mod {
464 pub fn test_function() {} 461 pub fn test_function() {}
465 } 462}
466 463
467 use test_mod::test_function; 464use test_mod::test_function;
468 fn main() { 465fn main() {
469 test_function$0 466 test_function$0
470 } 467}
471 ", 468"#,
472 ); 469 );
473 } 470 }
474 471
@@ -476,30 +473,30 @@ fn main() {
476 fn associated_struct_function() { 473 fn associated_struct_function() {
477 check_assist( 474 check_assist(
478 qualify_path, 475 qualify_path,
479 r" 476 r#"
480 mod test_mod { 477mod test_mod {
481 pub struct TestStruct {} 478 pub struct TestStruct {}
482 impl TestStruct { 479 impl TestStruct {
483 pub fn test_function() {} 480 pub fn test_function() {}
484 } 481 }
485 } 482}
486 483
487 fn main() { 484fn main() {
488 TestStruct::test_function$0 485 TestStruct::test_function$0
489 } 486}
490 ", 487"#,
491 r" 488 r#"
492 mod test_mod { 489mod test_mod {
493 pub struct TestStruct {} 490 pub struct TestStruct {}
494 impl TestStruct { 491 impl TestStruct {
495 pub fn test_function() {} 492 pub fn test_function() {}
496 } 493 }
497 } 494}
498 495
499 fn main() { 496fn main() {
500 test_mod::TestStruct::test_function 497 test_mod::TestStruct::test_function
501 } 498}
502 ", 499"#,
503 ); 500 );
504 } 501 }
505 502
@@ -508,62 +505,50 @@ fn main() {
508 cov_mark::check!(qualify_path_qualifier_start); 505 cov_mark::check!(qualify_path_qualifier_start);
509 check_assist( 506 check_assist(
510 qualify_path, 507 qualify_path,
511 r" 508 r#"
512 mod test_mod { 509mod test_mod {
513 pub struct TestStruct {} 510 pub struct TestStruct {}
514 impl TestStruct { 511 impl TestStruct {
515 const TEST_CONST: u8 = 42; 512 const TEST_CONST: u8 = 42;
516 } 513 }
517 } 514}
518 515
519 fn main() { 516fn main() {
520 TestStruct::TEST_CONST$0 517 TestStruct::TEST_CONST$0
521 } 518}
522 ", 519"#,
523 r" 520 r#"
524 mod test_mod { 521mod test_mod {
525 pub struct TestStruct {} 522 pub struct TestStruct {}
526 impl TestStruct { 523 impl TestStruct {
527 const TEST_CONST: u8 = 42; 524 const TEST_CONST: u8 = 42;
528 } 525 }
529 } 526}
530 527
531 fn main() { 528fn main() {
532 test_mod::TestStruct::TEST_CONST 529 test_mod::TestStruct::TEST_CONST
533 } 530}
534 ", 531"#,
535 ); 532 );
536 } 533 }
537 534
538 #[test] 535 #[test]
539 #[ignore = "FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details"]
540 fn associated_struct_const_unqualified() { 536 fn associated_struct_const_unqualified() {
541 check_assist( 537 // FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details
538 check_assist_not_applicable(
542 qualify_path, 539 qualify_path,
543 r" 540 r#"
544 mod test_mod { 541mod test_mod {
545 pub struct TestStruct {} 542 pub struct TestStruct {}
546 impl TestStruct { 543 impl TestStruct {
547 const TEST_CONST: u8 = 42; 544 const TEST_CONST: u8 = 42;
548 } 545 }
549 } 546}
550
551 fn main() {
552 TEST_CONST$0
553 }
554 ",
555 r"
556 mod test_mod {
557 pub struct TestStruct {}
558 impl TestStruct {
559 const TEST_CONST: u8 = 42;
560 }
561 }
562 547
563 fn main() { 548fn main() {
564 test_mod::TestStruct::TEST_CONST 549 TEST_CONST$0
565 } 550}
566 ", 551"#,
567 ); 552 );
568 } 553 }
569 554
@@ -571,36 +556,36 @@ fn main() {
571 fn associated_trait_function() { 556 fn associated_trait_function() {
572 check_assist( 557 check_assist(
573 qualify_path, 558 qualify_path,
574 r" 559 r#"
575 mod test_mod { 560mod test_mod {
576 pub trait TestTrait { 561 pub trait TestTrait {
577 fn test_function(); 562 fn test_function();
578 } 563 }
579 pub struct TestStruct {} 564 pub struct TestStruct {}
580 impl TestTrait for TestStruct { 565 impl TestTrait for TestStruct {
581 fn test_function() {} 566 fn test_function() {}
582 } 567 }
583 } 568}
584 569
585 fn main() { 570fn main() {
586 test_mod::TestStruct::test_function$0 571 test_mod::TestStruct::test_function$0
587 } 572}
588 ", 573"#,
589 r" 574 r#"
590 mod test_mod { 575mod test_mod {
591 pub trait TestTrait { 576 pub trait TestTrait {
592 fn test_function(); 577 fn test_function();
593 } 578 }
594 pub struct TestStruct {} 579 pub struct TestStruct {}
595 impl TestTrait for TestStruct { 580 impl TestTrait for TestStruct {
596 fn test_function() {} 581 fn test_function() {}
597 } 582 }
598 } 583}
599 584
600 fn main() { 585fn main() {
601 <test_mod::TestStruct as test_mod::TestTrait>::test_function 586 <test_mod::TestStruct as test_mod::TestTrait>::test_function
602 } 587}
603 ", 588"#,
604 ); 589 );
605 } 590 }
606 591
@@ -608,31 +593,31 @@ fn main() {
608 fn not_applicable_for_imported_trait_for_function() { 593 fn not_applicable_for_imported_trait_for_function() {
609 check_assist_not_applicable( 594 check_assist_not_applicable(
610 qualify_path, 595 qualify_path,
611 r" 596 r#"
612 mod test_mod { 597mod test_mod {
613 pub trait TestTrait { 598 pub trait TestTrait {
614 fn test_function(); 599 fn test_function();
615 } 600 }
616 pub trait TestTrait2 { 601 pub trait TestTrait2 {
617 fn test_function(); 602 fn test_function();
618 } 603 }
619 pub enum TestEnum { 604 pub enum TestEnum {
620 One, 605 One,
621 Two, 606 Two,
622 } 607 }
623 impl TestTrait2 for TestEnum { 608 impl TestTrait2 for TestEnum {
624 fn test_function() {} 609 fn test_function() {}
625 } 610 }
626 impl TestTrait for TestEnum { 611 impl TestTrait for TestEnum {
627 fn test_function() {} 612 fn test_function() {}
628 } 613 }
629 } 614}
630 615
631 use test_mod::TestTrait2; 616use test_mod::TestTrait2;
632 fn main() { 617fn main() {
633 test_mod::TestEnum::test_function$0; 618 test_mod::TestEnum::test_function$0;
634 } 619}
635 ", 620"#,
636 ) 621 )
637 } 622 }
638 623
@@ -641,36 +626,36 @@ fn main() {
641 cov_mark::check!(qualify_path_trait_assoc_item); 626 cov_mark::check!(qualify_path_trait_assoc_item);
642 check_assist( 627 check_assist(
643 qualify_path, 628 qualify_path,
644 r" 629 r#"
645 mod test_mod { 630mod test_mod {
646 pub trait TestTrait { 631 pub trait TestTrait {
647 const TEST_CONST: u8; 632 const TEST_CONST: u8;
648 } 633 }
649 pub struct TestStruct {} 634 pub struct TestStruct {}
650 impl TestTrait for TestStruct { 635 impl TestTrait for TestStruct {
651 const TEST_CONST: u8 = 42; 636 const TEST_CONST: u8 = 42;
652 } 637 }
653 } 638}
654 639
655 fn main() { 640fn main() {
656 test_mod::TestStruct::TEST_CONST$0 641 test_mod::TestStruct::TEST_CONST$0
657 } 642}
658 ", 643"#,
659 r" 644 r#"
660 mod test_mod { 645mod test_mod {
661 pub trait TestTrait { 646 pub trait TestTrait {
662 const TEST_CONST: u8; 647 const TEST_CONST: u8;
663 } 648 }
664 pub struct TestStruct {} 649 pub struct TestStruct {}
665 impl TestTrait for TestStruct { 650 impl TestTrait for TestStruct {
666 const TEST_CONST: u8 = 42; 651 const TEST_CONST: u8 = 42;
667 } 652 }
668 } 653}
669 654
670 fn main() { 655fn main() {
671 <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST 656 <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST
672 } 657}
673 ", 658"#,
674 ); 659 );
675 } 660 }
676 661
@@ -678,31 +663,31 @@ fn main() {
678 fn not_applicable_for_imported_trait_for_const() { 663 fn not_applicable_for_imported_trait_for_const() {
679 check_assist_not_applicable( 664 check_assist_not_applicable(
680 qualify_path, 665 qualify_path,
681 r" 666 r#"
682 mod test_mod { 667mod test_mod {
683 pub trait TestTrait { 668 pub trait TestTrait {
684 const TEST_CONST: u8; 669 const TEST_CONST: u8;
685 } 670 }
686 pub trait TestTrait2 { 671 pub trait TestTrait2 {
687 const TEST_CONST: f64; 672 const TEST_CONST: f64;
688 } 673 }
689 pub enum TestEnum { 674 pub enum TestEnum {
690 One, 675 One,
691 Two, 676 Two,
692 } 677 }
693 impl TestTrait2 for TestEnum { 678 impl TestTrait2 for TestEnum {
694 const TEST_CONST: f64 = 42.0; 679 const TEST_CONST: f64 = 42.0;
695 } 680 }
696 impl TestTrait for TestEnum { 681 impl TestTrait for TestEnum {
697 const TEST_CONST: u8 = 42; 682 const TEST_CONST: u8 = 42;
698 } 683 }
699 } 684}
700 685
701 use test_mod::TestTrait2; 686use test_mod::TestTrait2;
702 fn main() { 687fn main() {
703 test_mod::TestEnum::TEST_CONST$0; 688 test_mod::TestEnum::TEST_CONST$0;
704 } 689}
705 ", 690"#,
706 ) 691 )
707 } 692 }
708 693
@@ -711,38 +696,38 @@ fn main() {
711 cov_mark::check!(qualify_path_trait_method); 696 cov_mark::check!(qualify_path_trait_method);
712 check_assist( 697 check_assist(
713 qualify_path, 698 qualify_path,
714 r" 699 r#"
715 mod test_mod { 700mod test_mod {
716 pub trait TestTrait { 701 pub trait TestTrait {
717 fn test_method(&self); 702 fn test_method(&self);
718 } 703 }
719 pub struct TestStruct {} 704 pub struct TestStruct {}
720 impl TestTrait for TestStruct { 705 impl TestTrait for TestStruct {
721 fn test_method(&self) {} 706 fn test_method(&self) {}
722 } 707 }
723 } 708}
724 709
725 fn main() { 710fn main() {
726 let test_struct = test_mod::TestStruct {}; 711 let test_struct = test_mod::TestStruct {};
727 test_struct.test_meth$0od() 712 test_struct.test_meth$0od()
728 } 713}
729 ", 714"#,
730 r" 715 r#"
731 mod test_mod { 716mod test_mod {
732 pub trait TestTrait { 717 pub trait TestTrait {
733 fn test_method(&self); 718 fn test_method(&self);
734 } 719 }
735 pub struct TestStruct {} 720 pub struct TestStruct {}
736 impl TestTrait for TestStruct { 721 impl TestTrait for TestStruct {
737 fn test_method(&self) {} 722 fn test_method(&self) {}
738 } 723 }
739 } 724}
740 725
741 fn main() { 726fn main() {
742 let test_struct = test_mod::TestStruct {}; 727 let test_struct = test_mod::TestStruct {};
743 test_mod::TestTrait::test_method(&test_struct) 728 test_mod::TestTrait::test_method(&test_struct)
744 } 729}
745 ", 730"#,
746 ); 731 );
747 } 732 }
748 733
@@ -750,38 +735,38 @@ fn main() {
750 fn trait_method_multi_params() { 735 fn trait_method_multi_params() {
751 check_assist( 736 check_assist(
752 qualify_path, 737 qualify_path,
753 r" 738 r#"
754 mod test_mod { 739mod test_mod {
755 pub trait TestTrait { 740 pub trait TestTrait {
756 fn test_method(&self, test: i32); 741 fn test_method(&self, test: i32);
757 } 742 }
758 pub struct TestStruct {} 743 pub struct TestStruct {}
759 impl TestTrait for TestStruct { 744 impl TestTrait for TestStruct {
760 fn test_method(&self, test: i32) {} 745 fn test_method(&self, test: i32) {}
761 } 746 }
762 } 747}
763 748
764 fn main() { 749fn main() {
765 let test_struct = test_mod::TestStruct {}; 750 let test_struct = test_mod::TestStruct {};
766 test_struct.test_meth$0od(42) 751 test_struct.test_meth$0od(42)
767 } 752}
768 ", 753"#,
769 r" 754 r#"
770 mod test_mod { 755mod test_mod {
771 pub trait TestTrait { 756 pub trait TestTrait {
772 fn test_method(&self, test: i32); 757 fn test_method(&self, test: i32);
773 } 758 }
774 pub struct TestStruct {} 759 pub struct TestStruct {}
775 impl TestTrait for TestStruct { 760 impl TestTrait for TestStruct {
776 fn test_method(&self, test: i32) {} 761 fn test_method(&self, test: i32) {}
777 } 762 }
778 } 763}
779 764
780 fn main() { 765fn main() {
781 let test_struct = test_mod::TestStruct {}; 766 let test_struct = test_mod::TestStruct {};
782 test_mod::TestTrait::test_method(&test_struct, 42) 767 test_mod::TestTrait::test_method(&test_struct, 42)
783 } 768}
784 ", 769"#,
785 ); 770 );
786 } 771 }
787 772
@@ -789,38 +774,38 @@ fn main() {
789 fn trait_method_consume() { 774 fn trait_method_consume() {
790 check_assist( 775 check_assist(
791 qualify_path, 776 qualify_path,
792 r" 777 r#"
793 mod test_mod { 778mod test_mod {
794 pub trait TestTrait { 779 pub trait TestTrait {
795 fn test_method(self); 780 fn test_method(self);
796 } 781 }
797 pub struct TestStruct {} 782 pub struct TestStruct {}
798 impl TestTrait for TestStruct { 783 impl TestTrait for TestStruct {
799 fn test_method(self) {} 784 fn test_method(self) {}
800 } 785 }
801 } 786}
802 787
803 fn main() { 788fn main() {
804 let test_struct = test_mod::TestStruct {}; 789 let test_struct = test_mod::TestStruct {};
805 test_struct.test_meth$0od() 790 test_struct.test_meth$0od()
806 } 791}
807 ", 792"#,
808 r" 793 r#"
809 mod test_mod { 794mod test_mod {
810 pub trait TestTrait { 795 pub trait TestTrait {
811 fn test_method(self); 796 fn test_method(self);
812 } 797 }
813 pub struct TestStruct {} 798 pub struct TestStruct {}
814 impl TestTrait for TestStruct { 799 impl TestTrait for TestStruct {
815 fn test_method(self) {} 800 fn test_method(self) {}
816 } 801 }
817 } 802}
818 803
819 fn main() { 804fn main() {
820 let test_struct = test_mod::TestStruct {}; 805 let test_struct = test_mod::TestStruct {};
821 test_mod::TestTrait::test_method(test_struct) 806 test_mod::TestTrait::test_method(test_struct)
822 } 807}
823 ", 808"#,
824 ); 809 );
825 } 810 }
826 811
@@ -828,29 +813,29 @@ fn main() {
828 fn trait_method_cross_crate() { 813 fn trait_method_cross_crate() {
829 check_assist( 814 check_assist(
830 qualify_path, 815 qualify_path,
831 r" 816 r#"
832 //- /main.rs crate:main deps:dep 817//- /main.rs crate:main deps:dep
833 fn main() { 818fn main() {
834 let test_struct = dep::test_mod::TestStruct {}; 819 let test_struct = dep::test_mod::TestStruct {};
835 test_struct.test_meth$0od() 820 test_struct.test_meth$0od()
836 } 821}
837 //- /dep.rs crate:dep 822//- /dep.rs crate:dep
838 pub mod test_mod { 823pub mod test_mod {
839 pub trait TestTrait { 824 pub trait TestTrait {
840 fn test_method(&self); 825 fn test_method(&self);
841 } 826 }
842 pub struct TestStruct {} 827 pub struct TestStruct {}
843 impl TestTrait for TestStruct { 828 impl TestTrait for TestStruct {
844 fn test_method(&self) {} 829 fn test_method(&self) {}
845 } 830 }
846 } 831}
847 ", 832"#,
848 r" 833 r#"
849 fn main() { 834fn main() {
850 let test_struct = dep::test_mod::TestStruct {}; 835 let test_struct = dep::test_mod::TestStruct {};
851 dep::test_mod::TestTrait::test_method(&test_struct) 836 dep::test_mod::TestTrait::test_method(&test_struct)
852 } 837}
853 ", 838"#,
854 ); 839 );
855 } 840 }
856 841
@@ -858,27 +843,27 @@ fn main() {
858 fn assoc_fn_cross_crate() { 843 fn assoc_fn_cross_crate() {
859 check_assist( 844 check_assist(
860 qualify_path, 845 qualify_path,
861 r" 846 r#"
862 //- /main.rs crate:main deps:dep 847//- /main.rs crate:main deps:dep
863 fn main() { 848fn main() {
864 dep::test_mod::TestStruct::test_func$0tion 849 dep::test_mod::TestStruct::test_func$0tion
865 } 850}
866 //- /dep.rs crate:dep 851//- /dep.rs crate:dep
867 pub mod test_mod { 852pub mod test_mod {
868 pub trait TestTrait { 853 pub trait TestTrait {
869 fn test_function(); 854 fn test_function();
870 } 855 }
871 pub struct TestStruct {} 856 pub struct TestStruct {}
872 impl TestTrait for TestStruct { 857 impl TestTrait for TestStruct {
873 fn test_function() {} 858 fn test_function() {}
874 } 859 }
875 } 860}
876 ", 861"#,
877 r" 862 r#"
878 fn main() { 863fn main() {
879 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function 864 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function
880 } 865}
881 ", 866"#,
882 ); 867 );
883 } 868 }
884 869
@@ -886,27 +871,27 @@ fn main() {
886 fn assoc_const_cross_crate() { 871 fn assoc_const_cross_crate() {
887 check_assist( 872 check_assist(
888 qualify_path, 873 qualify_path,
889 r" 874 r#"
890 //- /main.rs crate:main deps:dep 875//- /main.rs crate:main deps:dep
891 fn main() { 876fn main() {
892 dep::test_mod::TestStruct::CONST$0 877 dep::test_mod::TestStruct::CONST$0
893 } 878}
894 //- /dep.rs crate:dep 879//- /dep.rs crate:dep
895 pub mod test_mod { 880pub mod test_mod {
896 pub trait TestTrait { 881 pub trait TestTrait {
897 const CONST: bool; 882 const CONST: bool;
898 } 883 }
899 pub struct TestStruct {} 884 pub struct TestStruct {}
900 impl TestTrait for TestStruct { 885 impl TestTrait for TestStruct {
901 const CONST: bool = true; 886 const CONST: bool = true;
902 } 887 }
903 } 888}
904 ", 889"#,
905 r" 890 r#"
906 fn main() { 891fn main() {
907 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST 892 <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST
908 } 893}
909 ", 894"#,
910 ); 895 );
911 } 896 }
912 897
@@ -914,23 +899,23 @@ fn main() {
914 fn assoc_fn_as_method_cross_crate() { 899 fn assoc_fn_as_method_cross_crate() {
915 check_assist_not_applicable( 900 check_assist_not_applicable(
916 qualify_path, 901 qualify_path,
917 r" 902 r#"
918 //- /main.rs crate:main deps:dep 903//- /main.rs crate:main deps:dep
919 fn main() { 904fn main() {
920 let test_struct = dep::test_mod::TestStruct {}; 905 let test_struct = dep::test_mod::TestStruct {};
921 test_struct.test_func$0tion() 906 test_struct.test_func$0tion()
922 } 907}
923 //- /dep.rs crate:dep 908//- /dep.rs crate:dep
924 pub mod test_mod { 909pub mod test_mod {
925 pub trait TestTrait { 910 pub trait TestTrait {
926 fn test_function(); 911 fn test_function();
927 } 912 }
928 pub struct TestStruct {} 913 pub struct TestStruct {}
929 impl TestTrait for TestStruct { 914 impl TestTrait for TestStruct {
930 fn test_function() {} 915 fn test_function() {}
931 } 916 }
932 } 917}
933 ", 918"#,
934 ); 919 );
935 } 920 }
936 921
@@ -938,23 +923,23 @@ fn main() {
938 fn private_trait_cross_crate() { 923 fn private_trait_cross_crate() {
939 check_assist_not_applicable( 924 check_assist_not_applicable(
940 qualify_path, 925 qualify_path,
941 r" 926 r#"
942 //- /main.rs crate:main deps:dep 927//- /main.rs crate:main deps:dep
943 fn main() { 928fn main() {
944 let test_struct = dep::test_mod::TestStruct {}; 929 let test_struct = dep::test_mod::TestStruct {};
945 test_struct.test_meth$0od() 930 test_struct.test_meth$0od()
946 } 931}
947 //- /dep.rs crate:dep 932//- /dep.rs crate:dep
948 pub mod test_mod { 933pub mod test_mod {
949 trait TestTrait { 934 trait TestTrait {
950 fn test_method(&self); 935 fn test_method(&self);
951 } 936 }
952 pub struct TestStruct {} 937 pub struct TestStruct {}
953 impl TestTrait for TestStruct { 938 impl TestTrait for TestStruct {
954 fn test_method(&self) {} 939 fn test_method(&self) {}
955 } 940 }
956 } 941}
957 ", 942"#,
958 ); 943 );
959 } 944 }
960 945
@@ -962,32 +947,32 @@ fn main() {
962 fn not_applicable_for_imported_trait_for_method() { 947 fn not_applicable_for_imported_trait_for_method() {
963 check_assist_not_applicable( 948 check_assist_not_applicable(
964 qualify_path, 949 qualify_path,
965 r" 950 r#"
966 mod test_mod { 951mod test_mod {
967 pub trait TestTrait { 952 pub trait TestTrait {
968 fn test_method(&self); 953 fn test_method(&self);
969 } 954 }
970 pub trait TestTrait2 { 955 pub trait TestTrait2 {
971 fn test_method(&self); 956 fn test_method(&self);
972 } 957 }
973 pub enum TestEnum { 958 pub enum TestEnum {
974 One, 959 One,
975 Two, 960 Two,
976 } 961 }
977 impl TestTrait2 for TestEnum { 962 impl TestTrait2 for TestEnum {
978 fn test_method(&self) {} 963 fn test_method(&self) {}
979 } 964 }
980 impl TestTrait for TestEnum { 965 impl TestTrait for TestEnum {
981 fn test_method(&self) {} 966 fn test_method(&self) {}
982 } 967 }
983 } 968}
984 969
985 use test_mod::TestTrait2; 970use test_mod::TestTrait2;
986 fn main() { 971fn main() {
987 let one = test_mod::TestEnum::One; 972 let one = test_mod::TestEnum::One;
988 one.test$0_method(); 973 one.test$0_method();
989 } 974}
990 ", 975"#,
991 ) 976 )
992 } 977 }
993 978
@@ -1114,7 +1099,7 @@ fn main() {}
1114 fn keep_generic_annotations_leading_colon() { 1099 fn keep_generic_annotations_leading_colon() {
1115 check_assist( 1100 check_assist(
1116 qualify_path, 1101 qualify_path,
1117 r" 1102 r#"
1118//- /lib.rs crate:dep 1103//- /lib.rs crate:dep
1119pub mod generic { pub struct Thing<'a, T>(&'a T); } 1104pub mod generic { pub struct Thing<'a, T>(&'a T); }
1120 1105
@@ -1122,7 +1107,7 @@ pub mod generic { pub struct Thing<'a, T>(&'a T); }
1122fn foo() -> Thin$0g::<'static, ()> {} 1107fn foo() -> Thin$0g::<'static, ()> {}
1123 1108
1124fn main() {} 1109fn main() {}
1125", 1110"#,
1126 r" 1111 r"
1127fn foo() -> dep::generic::Thing::<'static, ()> {} 1112fn foo() -> dep::generic::Thing::<'static, ()> {}
1128 1113
@@ -1135,30 +1120,30 @@ fn main() {}
1135 fn associated_struct_const_generic() { 1120 fn associated_struct_const_generic() {
1136 check_assist( 1121 check_assist(
1137 qualify_path, 1122 qualify_path,
1138 r" 1123 r#"
1139 mod test_mod { 1124mod test_mod {
1140 pub struct TestStruct<T> {} 1125 pub struct TestStruct<T> {}
1141 impl<T> TestStruct<T> { 1126 impl<T> TestStruct<T> {
1142 const TEST_CONST: u8 = 42; 1127 const TEST_CONST: u8 = 42;
1143 } 1128 }
1144 } 1129}
1145 1130
1146 fn main() { 1131fn main() {
1147 TestStruct::<()>::TEST_CONST$0 1132 TestStruct::<()>::TEST_CONST$0
1148 } 1133}
1149 ", 1134"#,
1150 r" 1135 r#"
1151 mod test_mod { 1136mod test_mod {
1152 pub struct TestStruct<T> {} 1137 pub struct TestStruct<T> {}
1153 impl<T> TestStruct<T> { 1138 impl<T> TestStruct<T> {
1154 const TEST_CONST: u8 = 42; 1139 const TEST_CONST: u8 = 42;
1155 } 1140 }
1156 } 1141}
1157 1142
1158 fn main() { 1143fn main() {
1159 test_mod::TestStruct::<()>::TEST_CONST 1144 test_mod::TestStruct::<()>::TEST_CONST
1160 } 1145}
1161 ", 1146"#,
1162 ); 1147 );
1163 } 1148 }
1164 1149
@@ -1166,36 +1151,36 @@ fn main() {}
1166 fn associated_trait_const_generic() { 1151 fn associated_trait_const_generic() {
1167 check_assist( 1152 check_assist(
1168 qualify_path, 1153 qualify_path,
1169 r" 1154 r#"
1170 mod test_mod { 1155mod test_mod {
1171 pub trait TestTrait { 1156 pub trait TestTrait {
1172 const TEST_CONST: u8; 1157 const TEST_CONST: u8;
1173 } 1158 }
1174 pub struct TestStruct<T> {} 1159 pub struct TestStruct<T> {}
1175 impl<T> TestTrait for TestStruct<T> { 1160 impl<T> TestTrait for TestStruct<T> {
1176 const TEST_CONST: u8 = 42; 1161 const TEST_CONST: u8 = 42;
1177 } 1162 }
1178 } 1163}
1179 1164
1180 fn main() { 1165fn main() {
1181 test_mod::TestStruct::<()>::TEST_CONST$0 1166 test_mod::TestStruct::<()>::TEST_CONST$0
1182 } 1167}
1183 ", 1168"#,
1184 r" 1169 r#"
1185 mod test_mod { 1170mod test_mod {
1186 pub trait TestTrait { 1171 pub trait TestTrait {
1187 const TEST_CONST: u8; 1172 const TEST_CONST: u8;
1188 } 1173 }
1189 pub struct TestStruct<T> {} 1174 pub struct TestStruct<T> {}
1190 impl<T> TestTrait for TestStruct<T> { 1175 impl<T> TestTrait for TestStruct<T> {
1191 const TEST_CONST: u8 = 42; 1176 const TEST_CONST: u8 = 42;
1192 } 1177 }
1193 } 1178}
1194 1179
1195 fn main() { 1180fn main() {
1196 <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST 1181 <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
1197 } 1182}
1198 ", 1183"#,
1199 ); 1184 );
1200 } 1185 }
1201 1186
@@ -1203,38 +1188,38 @@ fn main() {}
1203 fn trait_method_generic() { 1188 fn trait_method_generic() {
1204 check_assist( 1189 check_assist(
1205 qualify_path, 1190 qualify_path,
1206 r" 1191 r#"
1207 mod test_mod { 1192mod test_mod {
1208 pub trait TestTrait { 1193 pub trait TestTrait {
1209 fn test_method<T>(&self); 1194 fn test_method<T>(&self);
1210 } 1195 }
1211 pub struct TestStruct {} 1196 pub struct TestStruct {}
1212 impl TestTrait for TestStruct { 1197 impl TestTrait for TestStruct {
1213 fn test_method<T>(&self) {} 1198 fn test_method<T>(&self) {}
1214 } 1199 }
1215 } 1200}
1216 1201
1217 fn main() { 1202fn main() {
1218 let test_struct = test_mod::TestStruct {}; 1203 let test_struct = test_mod::TestStruct {};
1219 test_struct.test_meth$0od::<()>() 1204 test_struct.test_meth$0od::<()>()
1220 } 1205}
1221 ", 1206"#,
1222 r" 1207 r#"
1223 mod test_mod { 1208mod test_mod {
1224 pub trait TestTrait { 1209 pub trait TestTrait {
1225 fn test_method<T>(&self); 1210 fn test_method<T>(&self);
1226 } 1211 }
1227 pub struct TestStruct {} 1212 pub struct TestStruct {}
1228 impl TestTrait for TestStruct { 1213 impl TestTrait for TestStruct {
1229 fn test_method<T>(&self) {} 1214 fn test_method<T>(&self) {}
1230 } 1215 }
1231 } 1216}
1232 1217
1233 fn main() { 1218fn main() {
1234 let test_struct = test_mod::TestStruct {}; 1219 let test_struct = test_mod::TestStruct {};
1235 test_mod::TestTrait::test_method::<()>(&test_struct) 1220 test_mod::TestTrait::test_method::<()>(&test_struct)
1236 } 1221}
1237 ", 1222"#,
1238 ); 1223 );
1239 } 1224 }
1240} 1225}
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
index 540a905cc..a2af2035f 100644
--- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
+++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
@@ -105,12 +105,13 @@ fn foo<B: Bar
105 } 105 }
106 106
107 #[test] 107 #[test]
108 #[ignore = "This case is very rare but there is no simple solutions to fix it."]
109 fn replace_impl_trait_with_exist_generic_letter() { 108 fn replace_impl_trait_with_exist_generic_letter() {
109 // FIXME: This is wrong, we should pick a different name if the one we
110 // want is already bound.
110 check_assist( 111 check_assist(
111 replace_impl_trait_with_generic, 112 replace_impl_trait_with_generic,
112 r#"fn foo<B>(bar: $0impl Bar) {}"#, 113 r#"fn foo<B>(bar: $0impl Bar) {}"#,
113 r#"fn foo<B, C: Bar>(bar: C) {}"#, 114 r#"fn foo<B, B: Bar>(bar: B) {}"#,
114 ); 115 );
115 } 116 }
116 117
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 331a6df2b..fa378a622 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -17,156 +17,31 @@ mod tests;
17pub mod utils; 17pub mod utils;
18pub mod path_transform; 18pub mod path_transform;
19 19
20use std::str::FromStr;
21
22use hir::Semantics; 20use hir::Semantics;
23use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; 21use ide_db::{base_db::FileRange, RootDatabase};
24use syntax::TextRange; 22use syntax::TextRange;
25 23
26pub(crate) use crate::assist_context::{AssistContext, Assists}; 24pub(crate) use crate::assist_context::{AssistContext, Assists};
27 25
28pub use assist_config::AssistConfig; 26pub use assist_config::AssistConfig;
29 27pub use ide_db::assists::{
30#[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, SingleResolve,
31pub enum AssistKind { 29};
32 // FIXME: does the None variant make sense? Probably not. 30
33 None, 31/// Return all the assists applicable at the given position.
34 32pub fn assists(
35 QuickFix, 33 db: &RootDatabase,
36 Generate, 34 config: &AssistConfig,
37 Refactor, 35 resolve: AssistResolveStrategy,
38 RefactorExtract, 36 range: FileRange,
39 RefactorInline, 37) -> Vec<Assist> {
40 RefactorRewrite, 38 let sema = Semantics::new(db);
41} 39 let ctx = AssistContext::new(sema, config, range);
42 40 let mut acc = Assists::new(&ctx, resolve);
43impl AssistKind { 41 handlers::all().iter().for_each(|handler| {
44 pub fn contains(self, other: AssistKind) -> bool { 42 handler(&mut acc, &ctx);
45 if self == other { 43 });
46 return true; 44 acc.finish()
47 }
48
49 match self {
50 AssistKind::None | AssistKind::Generate => true,
51 AssistKind::Refactor => match other {
52 AssistKind::RefactorExtract
53 | AssistKind::RefactorInline
54 | AssistKind::RefactorRewrite => true,
55 _ => false,
56 },
57 _ => false,
58 }
59 }
60
61 pub fn name(&self) -> &str {
62 match self {
63 AssistKind::None => "None",
64 AssistKind::QuickFix => "QuickFix",
65 AssistKind::Generate => "Generate",
66 AssistKind::Refactor => "Refactor",
67 AssistKind::RefactorExtract => "RefactorExtract",
68 AssistKind::RefactorInline => "RefactorInline",
69 AssistKind::RefactorRewrite => "RefactorRewrite",
70 }
71 }
72}
73
74impl FromStr for AssistKind {
75 type Err = String;
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 match s {
79 "None" => Ok(AssistKind::None),
80 "QuickFix" => Ok(AssistKind::QuickFix),
81 "Generate" => Ok(AssistKind::Generate),
82 "Refactor" => Ok(AssistKind::Refactor),
83 "RefactorExtract" => Ok(AssistKind::RefactorExtract),
84 "RefactorInline" => Ok(AssistKind::RefactorInline),
85 "RefactorRewrite" => Ok(AssistKind::RefactorRewrite),
86 unknown => Err(format!("Unknown AssistKind: '{}'", unknown)),
87 }
88 }
89}
90
91/// Unique identifier of the assist, should not be shown to the user
92/// directly.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct AssistId(pub &'static str, pub AssistKind);
95
96/// A way to control how many asssist to resolve during the assist resolution.
97/// When an assist is resolved, its edits are calculated that might be costly to always do by default.
98#[derive(Debug)]
99pub enum AssistResolveStrategy {
100 /// No assists should be resolved.
101 None,
102 /// All assists should be resolved.
103 All,
104 /// Only a certain assist should be resolved.
105 Single(SingleResolve),
106}
107
108/// Hold the [`AssistId`] data of a certain assist to resolve.
109/// The original id object cannot be used due to a `'static` lifetime
110/// and the requirement to construct this struct dynamically during the resolve handling.
111#[derive(Debug)]
112pub struct SingleResolve {
113 /// The id of the assist.
114 pub assist_id: String,
115 // The kind of the assist.
116 pub assist_kind: AssistKind,
117}
118
119impl AssistResolveStrategy {
120 pub fn should_resolve(&self, id: &AssistId) -> bool {
121 match self {
122 AssistResolveStrategy::None => false,
123 AssistResolveStrategy::All => true,
124 AssistResolveStrategy::Single(single_resolve) => {
125 single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1
126 }
127 }
128 }
129}
130
131#[derive(Clone, Debug)]
132pub struct GroupLabel(pub String);
133
134#[derive(Debug, Clone)]
135pub struct Assist {
136 pub id: AssistId,
137 /// Short description of the assist, as shown in the UI.
138 pub label: Label,
139 pub group: Option<GroupLabel>,
140 /// Target ranges are used to sort assists: the smaller the target range,
141 /// the more specific assist is, and so it should be sorted first.
142 pub target: TextRange,
143 /// Computing source change sometimes is much more costly then computing the
144 /// other fields. Additionally, the actual change is not required to show
145 /// the lightbulb UI, it only is needed when the user tries to apply an
146 /// assist. So, we compute it lazily: the API allow requesting assists with
147 /// or without source change. We could (and in fact, used to) distinguish
148 /// between resolved and unresolved assists at the type level, but this is
149 /// cumbersome, especially if you want to embed an assist into another data
150 /// structure, such as a diagnostic.
151 pub source_change: Option<SourceChange>,
152}
153
154impl Assist {
155 /// Return all the assists applicable at the given position.
156 pub fn get(
157 db: &RootDatabase,
158 config: &AssistConfig,
159 resolve: AssistResolveStrategy,
160 range: FileRange,
161 ) -> Vec<Assist> {
162 let sema = Semantics::new(db);
163 let ctx = AssistContext::new(sema, config, range);
164 let mut acc = Assists::new(&ctx, resolve);
165 handlers::all().iter().for_each(|handler| {
166 handler(&mut acc, &ctx);
167 });
168 acc.finish()
169 }
170} 45}
171 46
172mod handlers { 47mod handlers {
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index bdf9cb71c..60cecd94c 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -16,8 +16,8 @@ use syntax::TextRange;
16use test_utils::{assert_eq_text, extract_offset}; 16use test_utils::{assert_eq_text, extract_offset};
17 17
18use crate::{ 18use crate::{
19 handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy, 19 assists, handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind,
20 Assists, SingleResolve, 20 AssistResolveStrategy, Assists, SingleResolve,
21}; 21};
22 22
23pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { 23pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
@@ -78,14 +78,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
78 let before = db.file_text(file_id).to_string(); 78 let before = db.file_text(file_id).to_string();
79 let frange = FileRange { file_id, range: selection.into() }; 79 let frange = FileRange { file_id, range: selection.into() };
80 80
81 let assist = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange) 81 let assist = assists(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange)
82 .into_iter() 82 .into_iter()
83 .find(|assist| assist.id.0 == assist_id) 83 .find(|assist| assist.id.0 == assist_id)
84 .unwrap_or_else(|| { 84 .unwrap_or_else(|| {
85 panic!( 85 panic!(
86 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 86 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
87 assist_id, 87 assist_id,
88 Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange) 88 assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange)
89 .into_iter() 89 .into_iter()
90 .map(|assist| assist.id.0) 90 .map(|assist| assist.id.0)
91 .collect::<Vec<_>>() 91 .collect::<Vec<_>>()
@@ -210,7 +210,7 @@ fn assist_order_field_struct() {
210 let (before_cursor_pos, before) = extract_offset(before); 210 let (before_cursor_pos, before) = extract_offset(before);
211 let (db, file_id) = with_single_file(&before); 211 let (db, file_id) = with_single_file(&before);
212 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 212 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
213 let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); 213 let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
214 let mut assists = assists.iter(); 214 let mut assists = assists.iter();
215 215
216 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 216 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
@@ -235,7 +235,7 @@ pub fn test_some_range(a: int) -> bool {
235"#, 235"#,
236 ); 236 );
237 237
238 let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); 238 let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
239 let expected = labels(&assists); 239 let expected = labels(&assists);
240 240
241 expect![[r#" 241 expect![[r#"
@@ -264,7 +264,7 @@ pub fn test_some_range(a: int) -> bool {
264 let mut cfg = TEST_CONFIG; 264 let mut cfg = TEST_CONFIG;
265 cfg.allowed = Some(vec![AssistKind::Refactor]); 265 cfg.allowed = Some(vec![AssistKind::Refactor]);
266 266
267 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); 267 let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
268 let expected = labels(&assists); 268 let expected = labels(&assists);
269 269
270 expect![[r#" 270 expect![[r#"
@@ -279,7 +279,7 @@ pub fn test_some_range(a: int) -> bool {
279 { 279 {
280 let mut cfg = TEST_CONFIG; 280 let mut cfg = TEST_CONFIG;
281 cfg.allowed = Some(vec![AssistKind::RefactorExtract]); 281 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
282 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); 282 let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
283 let expected = labels(&assists); 283 let expected = labels(&assists);
284 284
285 expect![[r#" 285 expect![[r#"
@@ -292,7 +292,7 @@ pub fn test_some_range(a: int) -> bool {
292 { 292 {
293 let mut cfg = TEST_CONFIG; 293 let mut cfg = TEST_CONFIG;
294 cfg.allowed = Some(vec![AssistKind::QuickFix]); 294 cfg.allowed = Some(vec![AssistKind::QuickFix]);
295 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); 295 let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
296 let expected = labels(&assists); 296 let expected = labels(&assists);
297 297
298 expect![[r#""#]].assert_eq(&expected); 298 expect![[r#""#]].assert_eq(&expected);
@@ -317,7 +317,7 @@ pub fn test_some_range(a: int) -> bool {
317 cfg.allowed = Some(vec![AssistKind::RefactorExtract]); 317 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
318 318
319 { 319 {
320 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); 320 let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange);
321 assert_eq!(2, assists.len()); 321 assert_eq!(2, assists.len());
322 let mut assists = assists.into_iter(); 322 let mut assists = assists.into_iter();
323 323
@@ -353,7 +353,7 @@ pub fn test_some_range(a: int) -> bool {
353 } 353 }
354 354
355 { 355 {
356 let assists = Assist::get( 356 let assists = assists(
357 &db, 357 &db,
358 &cfg, 358 &cfg,
359 AssistResolveStrategy::Single(SingleResolve { 359 AssistResolveStrategy::Single(SingleResolve {
@@ -397,7 +397,7 @@ pub fn test_some_range(a: int) -> bool {
397 } 397 }
398 398
399 { 399 {
400 let assists = Assist::get( 400 let assists = assists(
401 &db, 401 &db,
402 &cfg, 402 &cfg,
403 AssistResolveStrategy::Single(SingleResolve { 403 AssistResolveStrategy::Single(SingleResolve {
@@ -462,7 +462,7 @@ pub fn test_some_range(a: int) -> bool {
462 } 462 }
463 463
464 { 464 {
465 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::All, frange); 465 let assists = assists(&db, &cfg, AssistResolveStrategy::All, frange);
466 assert_eq!(2, assists.len()); 466 assert_eq!(2, assists.len());
467 let mut assists = assists.into_iter(); 467 let mut assists = assists.into_iter();
468 468
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index d526824fb..7b3133e53 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -93,57 +93,20 @@ mod tests {
93 } 93 }
94 94
95 #[test] 95 #[test]
96 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
97 fn empty_derive() { 96 fn empty_derive() {
98 check( 97 // FIXME: Add build-in derives to fixture.
99 r#"#[derive($0)] struct Test;"#, 98 check(r#"#[derive($0)] struct Test;"#, expect![[r#""#]]);
100 expect![[r#"
101 at Clone
102 at Clone, Copy
103 at Debug
104 at Default
105 at Hash
106 at PartialEq
107 at PartialEq, Eq
108 at PartialEq, PartialOrd
109 at PartialEq, Eq, PartialOrd, Ord
110 "#]],
111 );
112 } 99 }
113 100
114 #[test] 101 #[test]
115 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
116 fn derive_with_input() { 102 fn derive_with_input() {
117 check( 103 // FIXME: Add build-in derives to fixture.
118 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, 104 check(r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, expect![[r#""#]])
119 expect![[r#"
120 at Clone
121 at Clone, Copy
122 at Debug
123 at Default
124 at Hash
125 at Eq
126 at PartialOrd
127 at Eq, PartialOrd, Ord
128 "#]],
129 )
130 } 105 }
131 106
132 #[test] 107 #[test]
133 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
134 fn derive_with_input2() { 108 fn derive_with_input2() {
135 check( 109 // FIXME: Add build-in derives to fixture.
136 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, 110 check(r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, expect![[r#""#]])
137 expect![[r#"
138 at Clone
139 at Clone, Copy
140 at Debug
141 at Default
142 at Hash
143 at Eq
144 at PartialOrd
145 at Eq, PartialOrd, Ord
146 "#]],
147 )
148 } 111 }
149} 112}
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index ba13d3707..0fccbeccf 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -536,17 +536,11 @@ Some multi-line comment$0
536 fn test_completion_await_impls_future() { 536 fn test_completion_await_impls_future() {
537 check( 537 check(
538 r#" 538 r#"
539//- /main.rs crate:main deps:std 539//- minicore: future
540use std::future::*; 540use core::future::*;
541struct A {} 541struct A {}
542impl Future for A {} 542impl Future for A {}
543fn foo(a: A) { a.$0 } 543fn foo(a: A) { a.$0 }
544
545//- /std/lib.rs crate:std
546pub mod future {
547 #[lang = "future_trait"]
548 pub trait Future {}
549}
550"#, 544"#,
551 expect![[r#" 545 expect![[r#"
552 kw await expr.await 546 kw await expr.await
@@ -555,20 +549,12 @@ pub mod future {
555 549
556 check( 550 check(
557 r#" 551 r#"
558//- /main.rs crate:main deps:std 552//- minicore: future
559use std::future::*; 553use std::future::*;
560fn foo() { 554fn foo() {
561 let a = async {}; 555 let a = async {};
562 a.$0 556 a.$0
563} 557}
564
565//- /std/lib.rs crate:std
566pub mod future {
567 #[lang = "future_trait"]
568 pub trait Future {
569 type Output;
570 }
571}
572"#, 558"#,
573 expect![[r#" 559 expect![[r#"
574 kw await expr.await 560 kw await expr.await
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 952f052a1..f86b2d3f3 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -809,4 +809,22 @@ fn foo(_: impl Foo<B$0>) {}
809 "#]], 809 "#]],
810 ); 810 );
811 } 811 }
812
813 #[test]
814 fn completes_assoc_types_in_trait_bound() {
815 check(
816 r#"
817trait Foo {
818 type Bar;
819}
820
821fn foo<T: Foo<B$0>>(_: T) {}
822"#,
823 expect![[r#"
824 ta Bar = type Bar;
825 tp T
826 tt Foo
827 "#]],
828 );
829 }
812} 830}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index d8ca18c73..3eb51e80b 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -1152,16 +1152,11 @@ fn main() {
1152 fn suggest_deref() { 1152 fn suggest_deref() {
1153 check_relevance( 1153 check_relevance(
1154 r#" 1154 r#"
1155#[lang = "deref"] 1155//- minicore: deref
1156trait Deref {
1157 type Target;
1158 fn deref(&self) -> &Self::Target;
1159}
1160
1161struct S; 1156struct S;
1162struct T(S); 1157struct T(S);
1163 1158
1164impl Deref for T { 1159impl core::ops::Deref for T {
1165 type Target = S; 1160 type Target = S;
1166 1161
1167 fn deref(&self) -> &Self::Target { 1162 fn deref(&self) -> &Self::Target {
@@ -1185,8 +1180,9 @@ fn main() {
1185 st T [] 1180 st T []
1186 st S [] 1181 st S []
1187 fn main() [] 1182 fn main() []
1188 tt Deref []
1189 fn foo(…) [] 1183 fn foo(…) []
1184 md core []
1185 tt Sized []
1190 "#]], 1186 "#]],
1191 ) 1187 )
1192 } 1188 }
diff --git a/crates/ide_db/src/assists.rs b/crates/ide_db/src/assists.rs
new file mode 100644
index 000000000..7881d8369
--- /dev/null
+++ b/crates/ide_db/src/assists.rs
@@ -0,0 +1,136 @@
1//! This module defines the `Assist` data structure. The actual assist live in
2//! the `ide_assists` downstream crate. We want to define the data structures in
3//! this low-level crate though, because `ide_diagnostics` also need them
4//! (fixits for diagnostics and assists are the same thing under the hood). We
5//! want to compile `ide_assists` and `ide_diagnostics` in parallel though, so
6//! we pull the common definitions upstream, to this crate.
7
8use std::str::FromStr;
9
10use syntax::TextRange;
11
12use crate::{label::Label, source_change::SourceChange};
13
14#[derive(Debug, Clone)]
15pub struct Assist {
16 pub id: AssistId,
17 /// Short description of the assist, as shown in the UI.
18 pub label: Label,
19 pub group: Option<GroupLabel>,
20 /// Target ranges are used to sort assists: the smaller the target range,
21 /// the more specific assist is, and so it should be sorted first.
22 pub target: TextRange,
23 /// Computing source change sometimes is much more costly then computing the
24 /// other fields. Additionally, the actual change is not required to show
25 /// the lightbulb UI, it only is needed when the user tries to apply an
26 /// assist. So, we compute it lazily: the API allow requesting assists with
27 /// or without source change. We could (and in fact, used to) distinguish
28 /// between resolved and unresolved assists at the type level, but this is
29 /// cumbersome, especially if you want to embed an assist into another data
30 /// structure, such as a diagnostic.
31 pub source_change: Option<SourceChange>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum AssistKind {
36 // FIXME: does the None variant make sense? Probably not.
37 None,
38
39 QuickFix,
40 Generate,
41 Refactor,
42 RefactorExtract,
43 RefactorInline,
44 RefactorRewrite,
45}
46
47impl AssistKind {
48 pub fn contains(self, other: AssistKind) -> bool {
49 if self == other {
50 return true;
51 }
52
53 match self {
54 AssistKind::None | AssistKind::Generate => true,
55 AssistKind::Refactor => match other {
56 AssistKind::RefactorExtract
57 | AssistKind::RefactorInline
58 | AssistKind::RefactorRewrite => true,
59 _ => false,
60 },
61 _ => false,
62 }
63 }
64
65 pub fn name(&self) -> &str {
66 match self {
67 AssistKind::None => "None",
68 AssistKind::QuickFix => "QuickFix",
69 AssistKind::Generate => "Generate",
70 AssistKind::Refactor => "Refactor",
71 AssistKind::RefactorExtract => "RefactorExtract",
72 AssistKind::RefactorInline => "RefactorInline",
73 AssistKind::RefactorRewrite => "RefactorRewrite",
74 }
75 }
76}
77
78impl FromStr for AssistKind {
79 type Err = String;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 match s {
83 "None" => Ok(AssistKind::None),
84 "QuickFix" => Ok(AssistKind::QuickFix),
85 "Generate" => Ok(AssistKind::Generate),
86 "Refactor" => Ok(AssistKind::Refactor),
87 "RefactorExtract" => Ok(AssistKind::RefactorExtract),
88 "RefactorInline" => Ok(AssistKind::RefactorInline),
89 "RefactorRewrite" => Ok(AssistKind::RefactorRewrite),
90 unknown => Err(format!("Unknown AssistKind: '{}'", unknown)),
91 }
92 }
93}
94
95/// Unique identifier of the assist, should not be shown to the user
96/// directly.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct AssistId(pub &'static str, pub AssistKind);
99
100/// A way to control how many asssist to resolve during the assist resolution.
101/// When an assist is resolved, its edits are calculated that might be costly to always do by default.
102#[derive(Debug)]
103pub enum AssistResolveStrategy {
104 /// No assists should be resolved.
105 None,
106 /// All assists should be resolved.
107 All,
108 /// Only a certain assist should be resolved.
109 Single(SingleResolve),
110}
111
112/// Hold the [`AssistId`] data of a certain assist to resolve.
113/// The original id object cannot be used due to a `'static` lifetime
114/// and the requirement to construct this struct dynamically during the resolve handling.
115#[derive(Debug)]
116pub struct SingleResolve {
117 /// The id of the assist.
118 pub assist_id: String,
119 // The kind of the assist.
120 pub assist_kind: AssistKind,
121}
122
123impl AssistResolveStrategy {
124 pub fn should_resolve(&self, id: &AssistId) -> bool {
125 match self {
126 AssistResolveStrategy::None => false,
127 AssistResolveStrategy::All => true,
128 AssistResolveStrategy::Single(single_resolve) => {
129 single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1
130 }
131 }
132 }
133}
134
135#[derive(Clone, Debug)]
136pub struct GroupLabel(pub String);
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index 70b11bf81..5a88ec742 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -511,13 +511,14 @@ use std::io;
511} 511}
512 512
513#[test] 513#[test]
514#[ignore] // FIXME: Support this
515fn split_out_merge() { 514fn split_out_merge() {
515 // FIXME: This is suboptimal, we want to get `use std::fmt::{self, Result}`
516 // instead.
516 check_module( 517 check_module(
517 "std::fmt::Result", 518 "std::fmt::Result",
518 r"use std::{fmt, io};", 519 r"use std::{fmt, io};",
519 r"use std::fmt::{self, Result}; 520 r"use std::fmt::Result;
520use std::io;", 521use std::{fmt, io};",
521 ) 522 )
522} 523}
523 524
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 105607dca..7bbd08d6f 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -3,11 +3,11 @@
3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. 3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
4 4
5mod apply_change; 5mod apply_change;
6pub mod assists;
6pub mod label; 7pub mod label;
7pub mod line_index; 8pub mod line_index;
8pub mod symbol_index; 9pub mod symbol_index;
9pub mod defs; 10pub mod defs;
10pub mod search;
11pub mod items_locator; 11pub mod items_locator;
12pub mod source_change; 12pub mod source_change;
13pub mod ty_filter; 13pub mod ty_filter;
@@ -15,6 +15,9 @@ pub mod traits;
15pub mod call_info; 15pub mod call_info;
16pub mod helpers; 16pub mod helpers;
17 17
18pub mod search;
19pub mod rename;
20
18use std::{fmt, sync::Arc}; 21use std::{fmt, sync::Arc};
19 22
20use base_db::{ 23use base_db::{
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs
new file mode 100644
index 000000000..643e67781
--- /dev/null
+++ b/crates/ide_db/src/rename.rs
@@ -0,0 +1,468 @@
1//! Rename infrastructure for rust-analyzer. It is used primarily for the
2//! literal "rename" in the ide (look for tests there), but it is also available
3//! as a general-purpose service. For example, it is used by the fix for the
4//! "incorrect case" diagnostic.
5//!
6//! It leverages the [`crate::search`] functionality to find what needs to be
7//! renamed. The actual renames are tricky -- field shorthands need special
8//! attention, and, when renaming modules, you also want to rename files on the
9//! file system.
10//!
11//! Another can of worms are macros:
12//!
13//! ```
14//! macro_rules! m { () => { fn f() {} } }
15//! m!();
16//! fn main() {
17//! f() // <- rename me
18//! }
19//! ```
20//!
21//! The correct behavior in such cases is probably to show a dialog to the user.
22//! Our current behavior is ¯\_(ツ)_/¯.
23use std::fmt;
24
25use base_db::{AnchoredPathBuf, FileId, FileRange};
26use either::Either;
27use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics};
28use stdx::never;
29use syntax::{
30 ast::{self, NameOwner},
31 lex_single_syntax_kind, AstNode, SyntaxKind, TextRange, T,
32};
33use text_edit::TextEdit;
34
35use crate::{
36 defs::Definition,
37 search::FileReference,
38 source_change::{FileSystemEdit, SourceChange},
39 RootDatabase,
40};
41
42pub type Result<T, E = RenameError> = std::result::Result<T, E>;
43
44#[derive(Debug)]
45pub struct RenameError(pub String);
46
47impl fmt::Display for RenameError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 fmt::Display::fmt(&self.0, f)
50 }
51}
52
53#[macro_export]
54macro_rules! _format_err {
55 ($fmt:expr) => { RenameError(format!($fmt)) };
56 ($fmt:expr, $($arg:tt)+) => { RenameError(format!($fmt, $($arg)+)) }
57}
58pub use _format_err as format_err;
59
60#[macro_export]
61macro_rules! _bail {
62 ($($tokens:tt)*) => { return Err(format_err!($($tokens)*)) }
63}
64pub use _bail as bail;
65
66impl Definition {
67 pub fn rename(&self, sema: &Semantics<RootDatabase>, new_name: &str) -> Result<SourceChange> {
68 match *self {
69 Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
70 rename_mod(sema, module, new_name)
71 }
72 Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
73 bail!("Cannot rename builtin type")
74 }
75 Definition::SelfType(_) => bail!("Cannot rename `Self`"),
76 def => rename_reference(sema, def, new_name),
77 }
78 }
79
80 /// Textual range of the identifier which will change when renaming this
81 /// `Definition`. Note that some definitions, like buitin types, can't be
82 /// renamed.
83 pub fn range_for_rename(self, sema: &Semantics<RootDatabase>) -> Option<FileRange> {
84 // FIXME: the `original_file_range` calls here are wrong -- they never fail,
85 // and _fall back_ to the entirety of the macro call. Such fall back is
86 // incorrect for renames. The safe behavior would be to return an error for
87 // such cases. The correct behavior would be to return an auxiliary list of
88 // "can't rename these occurrences in macros" items, and then show some kind
89 // of a dialog to the user. See:
90 cov_mark::hit!(macros_are_broken_lol);
91
92 let res = match self {
93 Definition::Macro(mac) => {
94 let src = mac.source(sema.db)?;
95 let name = match &src.value {
96 Either::Left(it) => it.name()?,
97 Either::Right(it) => it.name()?,
98 };
99 src.with_value(name.syntax()).original_file_range(sema.db)
100 }
101 Definition::Field(field) => {
102 let src = field.source(sema.db)?;
103
104 match &src.value {
105 FieldSource::Named(record_field) => {
106 let name = record_field.name()?;
107 src.with_value(name.syntax()).original_file_range(sema.db)
108 }
109 FieldSource::Pos(_) => {
110 return None;
111 }
112 }
113 }
114 Definition::ModuleDef(module_def) => match module_def {
115 hir::ModuleDef::Module(module) => {
116 let src = module.declaration_source(sema.db)?;
117 let name = src.value.name()?;
118 src.with_value(name.syntax()).original_file_range(sema.db)
119 }
120 hir::ModuleDef::Function(it) => name_range(it, sema)?,
121 hir::ModuleDef::Adt(adt) => match adt {
122 hir::Adt::Struct(it) => name_range(it, sema)?,
123 hir::Adt::Union(it) => name_range(it, sema)?,
124 hir::Adt::Enum(it) => name_range(it, sema)?,
125 },
126 hir::ModuleDef::Variant(it) => name_range(it, sema)?,
127 hir::ModuleDef::Const(it) => name_range(it, sema)?,
128 hir::ModuleDef::Static(it) => name_range(it, sema)?,
129 hir::ModuleDef::Trait(it) => name_range(it, sema)?,
130 hir::ModuleDef::TypeAlias(it) => name_range(it, sema)?,
131 hir::ModuleDef::BuiltinType(_) => return None,
132 },
133 Definition::SelfType(_) => return None,
134 Definition::Local(local) => {
135 let src = local.source(sema.db);
136 let name = match &src.value {
137 Either::Left(bind_pat) => bind_pat.name()?,
138 Either::Right(_) => return None,
139 };
140 src.with_value(name.syntax()).original_file_range(sema.db)
141 }
142 Definition::GenericParam(generic_param) => match generic_param {
143 hir::GenericParam::TypeParam(type_param) => {
144 let src = type_param.source(sema.db)?;
145 let name = match &src.value {
146 Either::Left(type_param) => type_param.name()?,
147 Either::Right(_trait) => return None,
148 };
149 src.with_value(name.syntax()).original_file_range(sema.db)
150 }
151 hir::GenericParam::LifetimeParam(lifetime_param) => {
152 let src = lifetime_param.source(sema.db)?;
153 let lifetime = src.value.lifetime()?;
154 src.with_value(lifetime.syntax()).original_file_range(sema.db)
155 }
156 hir::GenericParam::ConstParam(it) => name_range(it, sema)?,
157 },
158 Definition::Label(label) => {
159 let src = label.source(sema.db);
160 let lifetime = src.value.lifetime()?;
161 src.with_value(lifetime.syntax()).original_file_range(sema.db)
162 }
163 };
164 return Some(res);
165
166 fn name_range<D>(def: D, sema: &Semantics<RootDatabase>) -> Option<FileRange>
167 where
168 D: HasSource,
169 D::Ast: ast::NameOwner,
170 {
171 let src = def.source(sema.db)?;
172 let name = src.value.name()?;
173 let res = src.with_value(name.syntax()).original_file_range(sema.db);
174 Some(res)
175 }
176 }
177}
178
179fn rename_mod(
180 sema: &Semantics<RootDatabase>,
181 module: hir::Module,
182 new_name: &str,
183) -> Result<SourceChange> {
184 if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
185 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
186 }
187
188 let mut source_change = SourceChange::default();
189
190 let InFile { file_id, value: def_source } = module.definition_source(sema.db);
191 let file_id = file_id.original_file(sema.db);
192 if let ModuleSource::SourceFile(..) = def_source {
193 // mod is defined in path/to/dir/mod.rs
194 let path = if module.is_mod_rs(sema.db) {
195 format!("../{}/mod.rs", new_name)
196 } else {
197 format!("{}.rs", new_name)
198 };
199 let dst = AnchoredPathBuf { anchor: file_id, path };
200 let move_file = FileSystemEdit::MoveFile { src: file_id, dst };
201 source_change.push_file_system_edit(move_file);
202 }
203
204 if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) {
205 let file_id = file_id.original_file(sema.db);
206 match decl_source.name() {
207 Some(name) => source_change.insert_source_edit(
208 file_id,
209 TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
210 ),
211 _ => never!("Module source node is missing a name"),
212 }
213 }
214 let def = Definition::ModuleDef(hir::ModuleDef::Module(module));
215 let usages = def.usages(sema).all();
216 let ref_edits = usages.iter().map(|(&file_id, references)| {
217 (file_id, source_edit_from_references(references, def, new_name))
218 });
219 source_change.extend(ref_edits);
220
221 Ok(source_change)
222}
223
224fn rename_reference(
225 sema: &Semantics<RootDatabase>,
226 mut def: Definition,
227 new_name: &str,
228) -> Result<SourceChange> {
229 let ident_kind = IdentifierKind::classify(new_name)?;
230
231 if matches!(
232 def, // is target a lifetime?
233 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
234 ) {
235 match ident_kind {
236 IdentifierKind::Ident | IdentifierKind::Underscore => {
237 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
238 bail!("Invalid name `{}`: not a lifetime identifier", new_name);
239 }
240 IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime),
241 }
242 } else {
243 match (ident_kind, def) {
244 (IdentifierKind::Lifetime, _) => {
245 cov_mark::hit!(rename_not_an_ident_ref);
246 bail!("Invalid name `{}`: not an identifier", new_name);
247 }
248 (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local),
249 (IdentifierKind::Underscore, _) => (),
250 }
251 }
252
253 def = match def {
254 // HACK: resolve trait impl items to the item def of the trait definition
255 // so that we properly resolve all trait item references
256 Definition::ModuleDef(mod_def) => mod_def
257 .as_assoc_item(sema.db)
258 .and_then(|it| it.containing_trait_impl(sema.db))
259 .and_then(|it| {
260 it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) {
261 (hir::AssocItem::Function(trait_func), hir::ModuleDef::Function(func))
262 if trait_func.name(sema.db) == func.name(sema.db) =>
263 {
264 Some(Definition::ModuleDef(hir::ModuleDef::Function(trait_func)))
265 }
266 (hir::AssocItem::Const(trait_konst), hir::ModuleDef::Const(konst))
267 if trait_konst.name(sema.db) == konst.name(sema.db) =>
268 {
269 Some(Definition::ModuleDef(hir::ModuleDef::Const(trait_konst)))
270 }
271 (
272 hir::AssocItem::TypeAlias(trait_type_alias),
273 hir::ModuleDef::TypeAlias(type_alias),
274 ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => {
275 Some(Definition::ModuleDef(hir::ModuleDef::TypeAlias(trait_type_alias)))
276 }
277 _ => None,
278 })
279 })
280 .unwrap_or(def),
281 _ => def,
282 };
283 let usages = def.usages(sema).all();
284
285 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
286 cov_mark::hit!(rename_underscore_multiple);
287 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
288 }
289 let mut source_change = SourceChange::default();
290 source_change.extend(usages.iter().map(|(&file_id, references)| {
291 (file_id, source_edit_from_references(references, def, new_name))
292 }));
293
294 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
295 source_change.insert_source_edit(file_id, edit);
296 Ok(source_change)
297}
298
299pub fn source_edit_from_references(
300 references: &[FileReference],
301 def: Definition,
302 new_name: &str,
303) -> TextEdit {
304 let mut edit = TextEdit::builder();
305 for reference in references {
306 let (range, replacement) = match &reference.name {
307 // if the ranges differ then the node is inside a macro call, we can't really attempt
308 // to make special rewrites like shorthand syntax and such, so just rename the node in
309 // the macro input
310 ast::NameLike::NameRef(name_ref)
311 if name_ref.syntax().text_range() == reference.range =>
312 {
313 source_edit_from_name_ref(name_ref, new_name, def)
314 }
315 ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => {
316 source_edit_from_name(name, new_name)
317 }
318 _ => None,
319 }
320 .unwrap_or_else(|| (reference.range, new_name.to_string()));
321 edit.replace(range, replacement);
322 }
323 edit.finish()
324}
325
326fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
327 if let Some(_) = ast::RecordPatField::for_field_name(name) {
328 if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
329 return Some((
330 TextRange::empty(ident_pat.syntax().text_range().start()),
331 [new_name, ": "].concat(),
332 ));
333 }
334 }
335 None
336}
337
338fn source_edit_from_name_ref(
339 name_ref: &ast::NameRef,
340 new_name: &str,
341 def: Definition,
342) -> Option<(TextRange, String)> {
343 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
344 let rcf_name_ref = record_field.name_ref();
345 let rcf_expr = record_field.expr();
346 match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
347 // field: init-expr, check if we can use a field init shorthand
348 (Some(field_name), Some(init)) => {
349 if field_name == *name_ref {
350 if init.text() == new_name {
351 cov_mark::hit!(test_rename_field_put_init_shorthand);
352 // same names, we can use a shorthand here instead.
353 // we do not want to erase attributes hence this range start
354 let s = field_name.syntax().text_range().start();
355 let e = record_field.syntax().text_range().end();
356 return Some((TextRange::new(s, e), new_name.to_owned()));
357 }
358 } else if init == *name_ref {
359 if field_name.text() == new_name {
360 cov_mark::hit!(test_rename_local_put_init_shorthand);
361 // same names, we can use a shorthand here instead.
362 // we do not want to erase attributes hence this range start
363 let s = field_name.syntax().text_range().start();
364 let e = record_field.syntax().text_range().end();
365 return Some((TextRange::new(s, e), new_name.to_owned()));
366 }
367 }
368 None
369 }
370 // init shorthand
371 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
372 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
373 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
374 cov_mark::hit!(test_rename_field_in_field_shorthand);
375 let s = name_ref.syntax().text_range().start();
376 Some((TextRange::empty(s), format!("{}: ", new_name)))
377 }
378 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
379 cov_mark::hit!(test_rename_local_in_field_shorthand);
380 let s = name_ref.syntax().text_range().end();
381 Some((TextRange::empty(s), format!(": {}", new_name)))
382 }
383 _ => None,
384 }
385 } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
386 let rcf_name_ref = record_field.name_ref();
387 let rcf_pat = record_field.pat();
388 match (rcf_name_ref, rcf_pat) {
389 // field: rename
390 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
391 // field name is being renamed
392 if pat.name().map_or(false, |it| it.text() == new_name) {
393 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
394 // same names, we can use a shorthand here instead/
395 // we do not want to erase attributes hence this range start
396 let s = field_name.syntax().text_range().start();
397 let e = record_field.syntax().text_range().end();
398 Some((TextRange::new(s, e), pat.to_string()))
399 } else {
400 None
401 }
402 }
403 _ => None,
404 }
405 } else {
406 None
407 }
408}
409
410fn source_edit_from_def(
411 sema: &Semantics<RootDatabase>,
412 def: Definition,
413 new_name: &str,
414) -> Result<(FileId, TextEdit)> {
415 let frange = def
416 .range_for_rename(sema)
417 .ok_or_else(|| format_err!("No identifier available to rename"))?;
418
419 let mut replacement_text = String::new();
420 let mut repl_range = frange.range;
421 if let Definition::Local(local) = def {
422 if let Either::Left(pat) = local.source(sema.db).value {
423 if matches!(
424 pat.syntax().parent().and_then(ast::RecordPatField::cast),
425 Some(pat_field) if pat_field.name_ref().is_none()
426 ) {
427 replacement_text.push_str(": ");
428 replacement_text.push_str(new_name);
429 repl_range = TextRange::new(
430 pat.syntax().text_range().end(),
431 pat.syntax().text_range().end(),
432 );
433 }
434 }
435 }
436 if replacement_text.is_empty() {
437 replacement_text.push_str(new_name);
438 }
439 let edit = TextEdit::replace(repl_range, replacement_text);
440 Ok((frange.file_id, edit))
441}
442
443#[derive(Copy, Clone, Debug, PartialEq)]
444pub enum IdentifierKind {
445 Ident,
446 Lifetime,
447 Underscore,
448}
449
450impl IdentifierKind {
451 pub fn classify(new_name: &str) -> Result<IdentifierKind> {
452 match lex_single_syntax_kind(new_name) {
453 Some(res) => match res {
454 (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident),
455 (T![_], _) => Ok(IdentifierKind::Underscore),
456 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
457 Ok(IdentifierKind::Lifetime)
458 }
459 (SyntaxKind::LIFETIME_IDENT, _) => {
460 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
461 }
462 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
463 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
464 },
465 None => bail!("Invalid name `{}`: not an identifier", new_name),
466 }
467 }
468}
diff --git a/crates/ide_diagnostics/Cargo.toml b/crates/ide_diagnostics/Cargo.toml
new file mode 100644
index 000000000..fa2adf212
--- /dev/null
+++ b/crates/ide_diagnostics/Cargo.toml
@@ -0,0 +1,29 @@
1[package]
2name = "ide_diagnostics"
3version = "0.0.0"
4description = "TBD"
5license = "MIT OR Apache-2.0"
6authors = ["rust-analyzer developers"]
7edition = "2018"
8
9[lib]
10doctest = false
11
12[dependencies]
13cov-mark = "2.0.0-pre.1"
14itertools = "0.10.0"
15rustc-hash = "1.1.0"
16either = "1.5.3"
17
18profile = { path = "../profile", version = "0.0.0" }
19stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" }
21text_edit = { path = "../text_edit", version = "0.0.0" }
22cfg = { path = "../cfg", version = "0.0.0" }
23hir = { path = "../hir", version = "0.0.0" }
24ide_db = { path = "../ide_db", version = "0.0.0" }
25
26[dev-dependencies]
27expect-test = "1.1"
28
29test_utils = { path = "../test_utils" }
diff --git a/crates/ide/src/diagnostics/break_outside_of_loop.rs b/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs
index 80e68f3cc..d12594a4c 100644
--- a/crates/ide/src/diagnostics/break_outside_of_loop.rs
+++ b/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs
@@ -1,9 +1,9 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 1use crate::{Diagnostic, DiagnosticsContext};
2 2
3// Diagnostic: break-outside-of-loop 3// Diagnostic: break-outside-of-loop
4// 4//
5// This diagnostic is triggered if the `break` keyword is used outside of a loop. 5// This diagnostic is triggered if the `break` keyword is used outside of a loop.
6pub(super) fn break_outside_of_loop( 6pub(crate) fn break_outside_of_loop(
7 ctx: &DiagnosticsContext<'_>, 7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::BreakOutsideOfLoop, 8 d: &hir::BreakOutsideOfLoop,
9) -> Diagnostic { 9) -> Diagnostic {
@@ -16,14 +16,14 @@ pub(super) fn break_outside_of_loop(
16 16
17#[cfg(test)] 17#[cfg(test)]
18mod tests { 18mod tests {
19 use crate::diagnostics::tests::check_diagnostics; 19 use crate::tests::check_diagnostics;
20 20
21 #[test] 21 #[test]
22 fn break_outside_of_loop() { 22 fn break_outside_of_loop() {
23 check_diagnostics( 23 check_diagnostics(
24 r#" 24 r#"
25fn foo() { break; } 25fn foo() { break; }
26 //^^^^^ break outside of loop 26 //^^^^^ error: break outside of loop
27"#, 27"#,
28 ); 28 );
29 } 29 }
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide_diagnostics/src/handlers/field_shorthand.rs
index c7f4dab8e..33152e284 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide_diagnostics/src/handlers/field_shorthand.rs
@@ -5,9 +5,9 @@ use ide_db::{base_db::FileId, source_change::SourceChange};
5use syntax::{ast, match_ast, AstNode, SyntaxNode}; 5use syntax::{ast, match_ast, AstNode, SyntaxNode};
6use text_edit::TextEdit; 6use text_edit::TextEdit;
7 7
8use crate::{diagnostics::fix, Diagnostic, Severity}; 8use crate::{fix, Diagnostic, Severity};
9 9
10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { 10pub(crate) fn field_shorthand(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
11 match_ast! { 11 match_ast! {
12 match node { 12 match node {
13 ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it), 13 ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it),
@@ -101,7 +101,7 @@ fn check_pat_field_shorthand(
101 101
102#[cfg(test)] 102#[cfg(test)]
103mod tests { 103mod tests {
104 use crate::diagnostics::tests::{check_diagnostics, check_fix}; 104 use crate::tests::{check_diagnostics, check_fix};
105 105
106 #[test] 106 #[test]
107 fn test_check_expr_field_shorthand() { 107 fn test_check_expr_field_shorthand() {
diff --git a/crates/ide/src/diagnostics/inactive_code.rs b/crates/ide_diagnostics/src/handlers/inactive_code.rs
index d9d3e88c1..dfd0e3351 100644
--- a/crates/ide/src/diagnostics/inactive_code.rs
+++ b/crates/ide_diagnostics/src/handlers/inactive_code.rs
@@ -1,15 +1,12 @@
1use cfg::DnfExpr; 1use cfg::DnfExpr;
2use stdx::format_to; 2use stdx::format_to;
3 3
4use crate::{ 4use crate::{Diagnostic, DiagnosticsContext, Severity};
5 diagnostics::{Diagnostic, DiagnosticsContext},
6 Severity,
7};
8 5
9// Diagnostic: inactive-code 6// Diagnostic: inactive-code
10// 7//
11// This diagnostic is shown for code with inactive `#[cfg]` attributes. 8// This diagnostic is shown for code with inactive `#[cfg]` attributes.
12pub(super) fn inactive_code( 9pub(crate) fn inactive_code(
13 ctx: &DiagnosticsContext<'_>, 10 ctx: &DiagnosticsContext<'_>,
14 d: &hir::InactiveCode, 11 d: &hir::InactiveCode,
15) -> Option<Diagnostic> { 12) -> Option<Diagnostic> {
@@ -37,7 +34,7 @@ pub(super) fn inactive_code(
37 34
38#[cfg(test)] 35#[cfg(test)]
39mod tests { 36mod tests {
40 use crate::{diagnostics::tests::check_diagnostics_with_config, DiagnosticsConfig}; 37 use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig};
41 38
42 pub(crate) fn check(ra_fixture: &str) { 39 pub(crate) fn check(ra_fixture: &str) {
43 let config = DiagnosticsConfig::default(); 40 let config = DiagnosticsConfig::default();
@@ -52,26 +49,26 @@ fn f() {
52 // The three g̶e̶n̶d̶e̶r̶s̶ statements: 49 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
53 50
54 #[cfg(a)] fn f() {} // Item statement 51 #[cfg(a)] fn f() {} // Item statement
55 //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 52 //^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
56 #[cfg(a)] {} // Expression statement 53 #[cfg(a)] {} // Expression statement
57 //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 54 //^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
58 #[cfg(a)] let x = 0; // let statement 55 #[cfg(a)] let x = 0; // let statement
59 //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 56 //^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
60 57
61 abc(#[cfg(a)] 0); 58 abc(#[cfg(a)] 0);
62 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 59 //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
63 let x = Struct { 60 let x = Struct {
64 #[cfg(a)] f: 0, 61 #[cfg(a)] f: 0,
65 //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 62 //^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
66 }; 63 };
67 match () { 64 match () {
68 () => (), 65 () => (),
69 #[cfg(a)] () => (), 66 #[cfg(a)] () => (),
70 //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 67 //^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
71 } 68 }
72 69
73 #[cfg(a)] 0 // Trailing expression of block 70 #[cfg(a)] 0 // Trailing expression of block
74 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 71 //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
75} 72}
76 "#, 73 "#,
77 ); 74 );
@@ -84,16 +81,16 @@ fn f() {
84 check( 81 check(
85 r#" 82 r#"
86 #[cfg(no)] pub fn f() {} 83 #[cfg(no)] pub fn f() {}
87 //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled 84 //^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
88 85
89 #[cfg(no)] #[cfg(no2)] mod m; 86 #[cfg(no)] #[cfg(no2)] mod m;
90 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled 87 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no and no2 are disabled
91 88
92 #[cfg(all(not(a), b))] enum E {} 89 #[cfg(all(not(a), b))] enum E {}
93 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled 90 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: b is disabled
94 91
95 #[cfg(feature = "std")] use std; 92 #[cfg(feature = "std")] use std;
96 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled 93 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: feature = "std" is disabled
97"#, 94"#,
98 ); 95 );
99 } 96 }
@@ -105,14 +102,14 @@ fn f() {
105 check( 102 check(
106 r#" 103 r#"
107 #[cfg_attr(not(never), cfg(no))] fn f() {} 104 #[cfg_attr(not(never), cfg(no))] fn f() {}
108 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled 105 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
109 106
110 #[cfg_attr(not(never), cfg(not(no)))] fn f() {} 107 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
111 108
112 #[cfg_attr(never, cfg(no))] fn g() {} 109 #[cfg_attr(never, cfg(no))] fn g() {}
113 110
114 #[cfg_attr(not(never), inline, cfg(no))] fn h() {} 111 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
115 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled 112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
116"#, 113"#,
117 ); 114 );
118 } 115 }
diff --git a/crates/ide/src/diagnostics/incorrect_case.rs b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
index 832394400..68f25f284 100644
--- a/crates/ide/src/diagnostics/incorrect_case.rs
+++ b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
@@ -1,18 +1,19 @@
1use hir::{db::AstDatabase, InFile}; 1use hir::{db::AstDatabase, InFile};
2use ide_assists::Assist; 2use ide_db::{assists::Assist, defs::NameClass};
3use ide_db::base_db::FilePosition;
4use syntax::AstNode; 3use syntax::AstNode;
5 4
6use crate::{ 5use crate::{
7 diagnostics::{unresolved_fix, Diagnostic, DiagnosticsContext}, 6 // references::rename::rename_with_semantics,
8 references::rename::rename_with_semantics, 7 unresolved_fix,
8 Diagnostic,
9 DiagnosticsContext,
9 Severity, 10 Severity,
10}; 11};
11 12
12// Diagnostic: incorrect-ident-case 13// Diagnostic: incorrect-ident-case
13// 14//
14// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. 15// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
15pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { 16pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
16 Diagnostic::new( 17 Diagnostic::new(
17 "incorrect-ident-case", 18 "incorrect-ident-case",
18 format!( 19 format!(
@@ -28,15 +29,15 @@ pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas
28fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> { 29fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
29 let root = ctx.sema.db.parse_or_expand(d.file)?; 30 let root = ctx.sema.db.parse_or_expand(d.file)?;
30 let name_node = d.ident.to_node(&root); 31 let name_node = d.ident.to_node(&root);
32 let def = NameClass::classify(&ctx.sema, &name_node)?.defined(ctx.sema.db)?;
31 33
32 let name_node = InFile::new(d.file, name_node.syntax()); 34 let name_node = InFile::new(d.file, name_node.syntax());
33 let frange = name_node.original_file_range(ctx.sema.db); 35 let frange = name_node.original_file_range(ctx.sema.db);
34 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
35 36
36 let label = format!("Rename to {}", d.suggested_text); 37 let label = format!("Rename to {}", d.suggested_text);
37 let mut res = unresolved_fix("change_case", &label, frange.range); 38 let mut res = unresolved_fix("change_case", &label, frange.range);
38 if ctx.resolve.should_resolve(&res.id) { 39 if ctx.resolve.should_resolve(&res.id) {
39 let source_change = rename_with_semantics(&ctx.sema, file_position, &d.suggested_text); 40 let source_change = def.rename(&ctx.sema, &d.suggested_text);
40 res.source_change = Some(source_change.ok().unwrap_or_default()); 41 res.source_change = Some(source_change.ok().unwrap_or_default());
41 } 42 }
42 43
@@ -45,10 +46,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Ass
45 46
46#[cfg(test)] 47#[cfg(test)]
47mod change_case { 48mod change_case {
48 use crate::{ 49 use crate::tests::{check_diagnostics, check_fix};
49 diagnostics::tests::{check_diagnostics, check_fix},
50 fixture, AssistResolveStrategy, DiagnosticsConfig,
51 };
52 50
53 #[test] 51 #[test]
54 fn test_rename_incorrect_case() { 52 fn test_rename_incorrect_case() {
@@ -116,7 +114,7 @@ fn some_fn() {
116 check_diagnostics( 114 check_diagnostics(
117 r#" 115 r#"
118fn foo() { 116fn foo() {
119 const ANOTHER_ITEM$0: &str = "some_item"; 117 const ANOTHER_ITEM: &str = "some_item";
120} 118}
121"#, 119"#,
122 ); 120 );
@@ -148,20 +146,13 @@ impl TestStruct {
148 146
149 #[test] 147 #[test]
150 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { 148 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
151 let input = r#"fn FOO$0() {}"#; 149 check_diagnostics(
152 let expected = r#"fn foo() {}"#; 150 r#"
153 151fn FOO() {}
154 let (analysis, file_position) = fixture::position(input); 152// ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo`
155 let diagnostics = analysis 153"#,
156 .diagnostics( 154 );
157 &DiagnosticsConfig::default(), 155 check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#);
158 AssistResolveStrategy::All,
159 file_position.file_id,
160 )
161 .unwrap();
162 assert_eq!(diagnostics.len(), 1);
163
164 check_fix(input, expected);
165 } 156 }
166 157
167 #[test] 158 #[test]
@@ -169,7 +160,7 @@ impl TestStruct {
169 check_diagnostics( 160 check_diagnostics(
170 r#" 161 r#"
171fn NonSnakeCaseName() {} 162fn NonSnakeCaseName() {}
172// ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` 163// ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
173"#, 164"#,
174 ); 165 );
175 } 166 }
@@ -179,10 +170,10 @@ fn NonSnakeCaseName() {}
179 check_diagnostics( 170 check_diagnostics(
180 r#" 171 r#"
181fn foo(SomeParam: u8) {} 172fn foo(SomeParam: u8) {}
182 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param` 173 // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param`
183 174
184fn foo2(ok_param: &str, CAPS_PARAM: u8) {} 175fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
185 // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` 176 // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
186"#, 177"#,
187 ); 178 );
188 } 179 }
@@ -193,9 +184,9 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
193 r#" 184 r#"
194fn foo() { 185fn foo() {
195 let SOME_VALUE = 10; 186 let SOME_VALUE = 10;
196 // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` 187 // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
197 let AnotherValue = 20; 188 let AnotherValue = 20;
198 // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value` 189 // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value`
199} 190}
200"#, 191"#,
201 ); 192 );
@@ -206,10 +197,10 @@ fn foo() {
206 check_diagnostics( 197 check_diagnostics(
207 r#" 198 r#"
208struct non_camel_case_name {} 199struct non_camel_case_name {}
209 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` 200 // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
210 201
211struct SCREAMING_CASE {} 202struct SCREAMING_CASE {}
212 // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` 203 // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
213"#, 204"#,
214 ); 205 );
215 } 206 }
@@ -228,7 +219,7 @@ struct AABB {}
228 check_diagnostics( 219 check_diagnostics(
229 r#" 220 r#"
230struct SomeStruct { SomeField: u8 } 221struct SomeStruct { SomeField: u8 }
231 // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field` 222 // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field`
232"#, 223"#,
233 ); 224 );
234 } 225 }
@@ -238,10 +229,10 @@ struct SomeStruct { SomeField: u8 }
238 check_diagnostics( 229 check_diagnostics(
239 r#" 230 r#"
240enum some_enum { Val(u8) } 231enum some_enum { Val(u8) }
241 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` 232 // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
242 233
243enum SOME_ENUM {} 234enum SOME_ENUM {}
244 // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` 235 // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
245"#, 236"#,
246 ); 237 );
247 } 238 }
@@ -260,7 +251,7 @@ enum AABB {}
260 check_diagnostics( 251 check_diagnostics(
261 r#" 252 r#"
262enum SomeEnum { SOME_VARIANT(u8) } 253enum SomeEnum { SOME_VARIANT(u8) }
263 // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` 254 // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
264"#, 255"#,
265 ); 256 );
266 } 257 }
@@ -270,7 +261,7 @@ enum SomeEnum { SOME_VARIANT(u8) }
270 check_diagnostics( 261 check_diagnostics(
271 r#" 262 r#"
272const some_weird_const: u8 = 10; 263const some_weird_const: u8 = 10;
273 // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` 264 // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
274"#, 265"#,
275 ); 266 );
276 } 267 }
@@ -280,7 +271,7 @@ const some_weird_const: u8 = 10;
280 check_diagnostics( 271 check_diagnostics(
281 r#" 272 r#"
282static some_weird_const: u8 = 10; 273static some_weird_const: u8 = 10;
283 // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` 274 // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
284"#, 275"#,
285 ); 276 );
286 } 277 }
@@ -290,13 +281,13 @@ static some_weird_const: u8 = 10;
290 check_diagnostics( 281 check_diagnostics(
291 r#" 282 r#"
292struct someStruct; 283struct someStruct;
293 // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` 284 // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
294 285
295impl someStruct { 286impl someStruct {
296 fn SomeFunc(&self) { 287 fn SomeFunc(&self) {
297 // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func` 288 // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func`
298 let WHY_VAR_IS_CAPS = 10; 289 let WHY_VAR_IS_CAPS = 10;
299 // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` 290 // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
300 } 291 }
301} 292}
302"#, 293"#,
@@ -328,7 +319,7 @@ enum Option { Some, None }
328fn main() { 319fn main() {
329 match Option::None { 320 match Option::None {
330 SOME_VAR @ None => (), 321 SOME_VAR @ None => (),
331 // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var` 322 // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
332 Some => (), 323 Some => (),
333 } 324 }
334} 325}
@@ -350,43 +341,27 @@ mod F {
350 } 341 }
351 342
352 #[test] 343 #[test]
353 #[ignore] 344 fn complex_ignore() {
354 fn bug_trait_inside_fn() { 345 // FIXME: this should trigger errors for the second case.
355 // FIXME:
356 // This is broken, and in fact, should not even be looked at by this
357 // lint in the first place. There's weird stuff going on in the
358 // collection phase.
359 // It's currently being brought in by:
360 // * validate_func on `a` recursing into modules
361 // * then it finds the trait and then the function while iterating
362 // through modules
363 // * then validate_func is called on Dirty
364 // * ... which then proceeds to look at some unknown module taking no
365 // attrs from either the impl or the fn a, and then finally to the root
366 // module
367 //
368 // It should find the attribute on the trait, but it *doesn't even see
369 // the trait* as far as I can tell.
370
371 check_diagnostics( 346 check_diagnostics(
372 r#" 347 r#"
373trait T { fn a(); } 348trait T { fn a(); }
374struct U {} 349struct U {}
375impl T for U { 350impl T for U {
376 fn a() { 351 fn a() {
377 // this comes out of bitflags, mostly
378 #[allow(non_snake_case)] 352 #[allow(non_snake_case)]
379 trait __BitFlags { 353 trait __BitFlagsOk {
380 const HiImAlsoBad: u8 = 2; 354 const HiImAlsoBad: u8 = 2;
381 #[inline] 355 fn Dirty(&self) -> bool { false }
382 fn Dirty(&self) -> bool {
383 false
384 }
385 } 356 }
386 357
358 trait __BitFlagsBad {
359 const HiImAlsoBad: u8 = 2;
360 fn Dirty(&self) -> bool { false }
361 }
387 } 362 }
388} 363}
389 "#, 364"#,
390 ); 365 );
391 } 366 }
392 367
@@ -423,18 +398,14 @@ extern {
423 } 398 }
424 399
425 #[test] 400 #[test]
426 #[ignore]
427 fn bug_traits_arent_checked() { 401 fn bug_traits_arent_checked() {
428 // FIXME: Traits and functions in traits aren't currently checked by 402 // FIXME: Traits and functions in traits aren't currently checked by
429 // r-a, even though rustc will complain about them. 403 // r-a, even though rustc will complain about them.
430 check_diagnostics( 404 check_diagnostics(
431 r#" 405 r#"
432trait BAD_TRAIT { 406trait BAD_TRAIT {
433 // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
434 fn BAD_FUNCTION(); 407 fn BAD_FUNCTION();
435 // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
436 fn BadFunction(); 408 fn BadFunction();
437 // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
438} 409}
439 "#, 410 "#,
440 ); 411 );
diff --git a/crates/ide/src/diagnostics/macro_error.rs b/crates/ide_diagnostics/src/handlers/macro_error.rs
index 5f97f190d..356f089b2 100644
--- a/crates/ide/src/diagnostics/macro_error.rs
+++ b/crates/ide_diagnostics/src/handlers/macro_error.rs
@@ -1,9 +1,9 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 1use crate::{Diagnostic, DiagnosticsContext};
2 2
3// Diagnostic: macro-error 3// Diagnostic: macro-error
4// 4//
5// This diagnostic is shown for macro expansion errors. 5// This diagnostic is shown for macro expansion errors.
6pub(super) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { 6pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
7 Diagnostic::new( 7 Diagnostic::new(
8 "macro-error", 8 "macro-error",
9 d.message.clone(), 9 d.message.clone(),
@@ -15,7 +15,7 @@ pub(super) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
15#[cfg(test)] 15#[cfg(test)]
16mod tests { 16mod tests {
17 use crate::{ 17 use crate::{
18 diagnostics::tests::{check_diagnostics, check_diagnostics_with_config}, 18 tests::{check_diagnostics, check_diagnostics_with_config},
19 DiagnosticsConfig, 19 DiagnosticsConfig,
20 }; 20 };
21 21
@@ -27,7 +27,7 @@ mod tests {
27macro_rules! include { () => {} } 27macro_rules! include { () => {} }
28 28
29 include!("doesntexist"); 29 include!("doesntexist");
30//^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist` 30//^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `doesntexist`
31 "#, 31 "#,
32 ); 32 );
33 } 33 }
@@ -66,7 +66,7 @@ macro_rules! env { () => {} }
66macro_rules! concat { () => {} } 66macro_rules! concat { () => {} }
67 67
68 include!(concat!(env!("OUT_DIR"), "/out.rs")); 68 include!(concat!(env!("OUT_DIR"), "/out.rs"));
69//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix 69//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, enable "run build scripts" to fix
70"#, 70"#,
71 ); 71 );
72 } 72 }
@@ -108,23 +108,23 @@ fn main() {
108 // Test a handful of built-in (eager) macros: 108 // Test a handful of built-in (eager) macros:
109 109
110 include!(invalid); 110 include!(invalid);
111 //^^^^^^^^^^^^^^^^^ could not convert tokens 111 //^^^^^^^^^^^^^^^^^ error: could not convert tokens
112 include!("does not exist"); 112 include!("does not exist");
113 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist` 113 //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
114 114
115 env!(invalid); 115 env!(invalid);
116 //^^^^^^^^^^^^^ could not convert tokens 116 //^^^^^^^^^^^^^ error: could not convert tokens
117 117
118 env!("OUT_DIR"); 118 env!("OUT_DIR");
119 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix 119 //^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, enable "run build scripts" to fix
120 120
121 compile_error!("compile_error works"); 121 compile_error!("compile_error works");
122 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works 122 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: compile_error works
123 123
124 // Lazy: 124 // Lazy:
125 125
126 format_args!(); 126 format_args!();
127 //^^^^^^^^^^^^^^ no rule matches input tokens 127 //^^^^^^^^^^^^^^ error: no rule matches input tokens
128} 128}
129"#, 129"#,
130 ); 130 );
@@ -141,7 +141,7 @@ fn f() {
141 m!(); 141 m!();
142 142
143 m!(hi); 143 m!(hi);
144 //^^^^^^ leftover tokens 144 //^^^^^^ error: leftover tokens
145} 145}
146 "#, 146 "#,
147 ); 147 );
@@ -166,7 +166,7 @@ macro_rules! outer {
166 166
167fn f() { 167fn f() {
168 outer!(); 168 outer!();
169} //^^^^^^^^ leftover tokens 169} //^^^^^^^^ error: leftover tokens
170"#, 170"#,
171 ) 171 )
172 } 172 }
diff --git a/crates/ide/src/diagnostics/mismatched_arg_count.rs b/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs
index 08e1cfa5f..a9b6d3870 100644
--- a/crates/ide/src/diagnostics/mismatched_arg_count.rs
+++ b/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs
@@ -1,9 +1,9 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 1use crate::{Diagnostic, DiagnosticsContext};
2 2
3// Diagnostic: mismatched-arg-count 3// Diagnostic: mismatched-arg-count
4// 4//
5// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. 5// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
6pub(super) fn mismatched_arg_count( 6pub(crate) fn mismatched_arg_count(
7 ctx: &DiagnosticsContext<'_>, 7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::MismatchedArgCount, 8 d: &hir::MismatchedArgCount,
9) -> Diagnostic { 9) -> Diagnostic {
@@ -18,7 +18,7 @@ pub(super) fn mismatched_arg_count(
18 18
19#[cfg(test)] 19#[cfg(test)]
20mod tests { 20mod tests {
21 use crate::diagnostics::tests::check_diagnostics; 21 use crate::tests::check_diagnostics;
22 22
23 #[test] 23 #[test]
24 fn simple_free_fn_zero() { 24 fn simple_free_fn_zero() {
@@ -26,7 +26,7 @@ mod tests {
26 r#" 26 r#"
27fn zero() {} 27fn zero() {}
28fn f() { zero(1); } 28fn f() { zero(1); }
29 //^^^^^^^ expected 0 arguments, found 1 29 //^^^^^^^ error: expected 0 arguments, found 1
30"#, 30"#,
31 ); 31 );
32 32
@@ -44,7 +44,7 @@ fn f() { zero(); }
44 r#" 44 r#"
45fn one(arg: u8) {} 45fn one(arg: u8) {}
46fn f() { one(); } 46fn f() { one(); }
47 //^^^^^ expected 1 argument, found 0 47 //^^^^^ error: expected 1 argument, found 0
48"#, 48"#,
49 ); 49 );
50 50
@@ -65,7 +65,7 @@ impl S { fn method(&self) {} }
65 65
66fn f() { 66fn f() {
67 S::method(); 67 S::method();
68} //^^^^^^^^^^^ expected 1 argument, found 0 68} //^^^^^^^^^^^ error: expected 1 argument, found 0
69"#, 69"#,
70 ); 70 );
71 71
@@ -91,7 +91,7 @@ impl S { fn method(&self, arg: u8) {} }
91 91
92 fn f() { 92 fn f() {
93 S.method(); 93 S.method();
94 } //^^^^^^^^^^ expected 1 argument, found 0 94 } //^^^^^^^^^^ error: expected 1 argument, found 0
95 "#, 95 "#,
96 ); 96 );
97 97
@@ -131,7 +131,7 @@ fn f() {
131struct Tup(u8, u16); 131struct Tup(u8, u16);
132fn f() { 132fn f() {
133 Tup(0); 133 Tup(0);
134} //^^^^^^ expected 2 arguments, found 1 134} //^^^^^^ error: expected 2 arguments, found 1
135"#, 135"#,
136 ) 136 )
137 } 137 }
@@ -143,7 +143,7 @@ fn f() {
143enum En { Variant(u8, u16), } 143enum En { Variant(u8, u16), }
144fn f() { 144fn f() {
145 En::Variant(0); 145 En::Variant(0);
146} //^^^^^^^^^^^^^^ expected 2 arguments, found 1 146} //^^^^^^^^^^^^^^ error: expected 2 arguments, found 1
147"#, 147"#,
148 ) 148 )
149 } 149 }
@@ -162,9 +162,9 @@ impl Foo {
162 fn new() { 162 fn new() {
163 Foo::Bar(0); 163 Foo::Bar(0);
164 Foo::Bar(0, 1); 164 Foo::Bar(0, 1);
165 //^^^^^^^^^^^^^^ expected 1 argument, found 2 165 //^^^^^^^^^^^^^^ error: expected 1 argument, found 2
166 Foo::Bar(); 166 Foo::Bar();
167 //^^^^^^^^^^ expected 1 argument, found 0 167 //^^^^^^^^^^ error: expected 1 argument, found 0
168 } 168 }
169} 169}
170 "#, 170 "#,
@@ -185,7 +185,7 @@ fn f() {
185 unsafe { 185 unsafe {
186 fixed(0); 186 fixed(0);
187 fixed(0, 1); 187 fixed(0, 1);
188 //^^^^^^^^^^^ expected 1 argument, found 2 188 //^^^^^^^^^^^ error: expected 1 argument, found 2
189 varargs(0); 189 varargs(0);
190 varargs(0, 1); 190 varargs(0, 1);
191 varargs2(); 191 varargs2();
@@ -204,10 +204,10 @@ fn f() {
204fn main() { 204fn main() {
205 let f = |()| (); 205 let f = |()| ();
206 f(); 206 f();
207 //^^^ expected 1 argument, found 0 207 //^^^ error: expected 1 argument, found 0
208 f(()); 208 f(());
209 f((), ()); 209 f((), ());
210 //^^^^^^^^^ expected 1 argument, found 2 210 //^^^^^^^^^ error: expected 1 argument, found 2
211} 211}
212"#, 212"#,
213 ) 213 )
diff --git a/crates/ide/src/diagnostics/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs
index d01f05041..bc56e0342 100644
--- a/crates/ide/src/diagnostics/missing_fields.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs
@@ -1,12 +1,11 @@
1use either::Either; 1use either::Either;
2use hir::{db::AstDatabase, InFile}; 2use hir::{db::AstDatabase, InFile};
3use ide_assists::Assist; 3use ide_db::{assists::Assist, source_change::SourceChange};
4use ide_db::source_change::SourceChange;
5use stdx::format_to; 4use stdx::format_to;
6use syntax::{algo, ast::make, AstNode, SyntaxNodePtr}; 5use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
7use text_edit::TextEdit; 6use text_edit::TextEdit;
8 7
9use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; 8use crate::{fix, Diagnostic, DiagnosticsContext};
10 9
11// Diagnostic: missing-fields 10// Diagnostic: missing-fields
12// 11//
@@ -19,8 +18,8 @@ use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
19// 18//
20// let a = A { a: 10 }; 19// let a = A { a: 10 };
21// ``` 20// ```
22pub(super) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { 21pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
23 let mut message = String::from("Missing structure fields:\n"); 22 let mut message = String::from("missing structure fields:\n");
24 for field in &d.missed_fields { 23 for field in &d.missed_fields {
25 format_to!(message, "- {}\n", field); 24 format_to!(message, "- {}\n", field);
26 } 25 }
@@ -77,7 +76,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
77 76
78#[cfg(test)] 77#[cfg(test)]
79mod tests { 78mod tests {
80 use crate::diagnostics::tests::{check_diagnostics, check_fix}; 79 use crate::tests::{check_diagnostics, check_fix};
81 80
82 #[test] 81 #[test]
83 fn missing_record_pat_field_diagnostic() { 82 fn missing_record_pat_field_diagnostic() {
@@ -86,7 +85,7 @@ mod tests {
86struct S { foo: i32, bar: () } 85struct S { foo: i32, bar: () }
87fn baz(s: S) { 86fn baz(s: S) {
88 let S { foo: _ } = s; 87 let S { foo: _ } = s;
89 //^ Missing structure fields: 88 //^ error: missing structure fields:
90 //| - bar 89 //| - bar
91} 90}
92"#, 91"#,
@@ -324,4 +323,33 @@ fn f() {
324"#, 323"#,
325 ); 324 );
326 } 325 }
326
327 #[test]
328 fn import_extern_crate_clash_with_inner_item() {
329 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
330
331 check_diagnostics(
332 r#"
333//- /lib.rs crate:lib deps:jwt
334mod permissions;
335
336use permissions::jwt;
337
338fn f() {
339 fn inner() {}
340 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
341}
342
343//- /permissions.rs
344pub mod jwt {
345 pub struct Claims {}
346}
347
348//- /jwt/lib.rs crate:jwt
349pub struct Claims {
350 field: u8,
351}
352 "#,
353 );
354 }
327} 355}
diff --git a/crates/ide/src/diagnostics/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
index b636489b3..947b0f2e2 100644
--- a/crates/ide/src/diagnostics/missing_match_arms.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
@@ -1,11 +1,11 @@
1use hir::InFile; 1use hir::InFile;
2 2
3use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 3use crate::{Diagnostic, DiagnosticsContext};
4 4
5// Diagnostic: missing-match-arm 5// Diagnostic: missing-match-arm
6// 6//
7// This diagnostic is triggered if `match` block is missing one or more match arms. 7// This diagnostic is triggered if `match` block is missing one or more match arms.
8pub(super) fn missing_match_arms( 8pub(crate) fn missing_match_arms(
9 ctx: &DiagnosticsContext<'_>, 9 ctx: &DiagnosticsContext<'_>,
10 d: &hir::MissingMatchArms, 10 d: &hir::MissingMatchArms,
11) -> Diagnostic { 11) -> Diagnostic {
@@ -17,12 +17,12 @@ pub(super) fn missing_match_arms(
17} 17}
18 18
19#[cfg(test)] 19#[cfg(test)]
20pub(super) mod tests { 20mod tests {
21 use crate::diagnostics::tests::check_diagnostics; 21 use crate::tests::check_diagnostics;
22 22
23 fn check_diagnostics_no_bails(ra_fixture: &str) { 23 fn check_diagnostics_no_bails(ra_fixture: &str) {
24 cov_mark::check_count!(validate_match_bailed_out, 0); 24 cov_mark::check_count!(validate_match_bailed_out, 0);
25 crate::diagnostics::tests::check_diagnostics(ra_fixture) 25 crate::tests::check_diagnostics(ra_fixture)
26 } 26 }
27 27
28 #[test] 28 #[test]
@@ -31,9 +31,9 @@ pub(super) mod tests {
31 r#" 31 r#"
32fn main() { 32fn main() {
33 match () { } 33 match () { }
34 //^^ missing match arm 34 //^^ error: missing match arm
35 match (()) { } 35 match (()) { }
36 //^^^^ missing match arm 36 //^^^^ error: missing match arm
37 37
38 match () { _ => (), } 38 match () { _ => (), }
39 match () { () => (), } 39 match () { () => (), }
@@ -49,7 +49,7 @@ fn main() {
49 r#" 49 r#"
50fn main() { 50fn main() {
51 match ((), ()) { } 51 match ((), ()) { }
52 //^^^^^^^^ missing match arm 52 //^^^^^^^^ error: missing match arm
53 53
54 match ((), ()) { ((), ()) => (), } 54 match ((), ()) { ((), ()) => (), }
55} 55}
@@ -63,21 +63,21 @@ fn main() {
63 r#" 63 r#"
64fn test_main() { 64fn test_main() {
65 match false { } 65 match false { }
66 //^^^^^ missing match arm 66 //^^^^^ error: missing match arm
67 match false { true => (), } 67 match false { true => (), }
68 //^^^^^ missing match arm 68 //^^^^^ error: missing match arm
69 match (false, true) {} 69 match (false, true) {}
70 //^^^^^^^^^^^^^ missing match arm 70 //^^^^^^^^^^^^^ error: missing match arm
71 match (false, true) { (true, true) => (), } 71 match (false, true) { (true, true) => (), }
72 //^^^^^^^^^^^^^ missing match arm 72 //^^^^^^^^^^^^^ error: missing match arm
73 match (false, true) { 73 match (false, true) {
74 //^^^^^^^^^^^^^ missing match arm 74 //^^^^^^^^^^^^^ error: missing match arm
75 (false, true) => (), 75 (false, true) => (),
76 (false, false) => (), 76 (false, false) => (),
77 (true, false) => (), 77 (true, false) => (),
78 } 78 }
79 match (false, true) { (true, _x) => (), } 79 match (false, true) { (true, _x) => (), }
80 //^^^^^^^^^^^^^ missing match arm 80 //^^^^^^^^^^^^^ error: missing match arm
81 81
82 match false { true => (), false => (), } 82 match false { true => (), false => (), }
83 match (false, true) { 83 match (false, true) {
@@ -116,11 +116,11 @@ fn test_main() {
116 r#" 116 r#"
117fn main() { 117fn main() {
118 match (false, ((), false)) {} 118 match (false, ((), false)) {}
119 //^^^^^^^^^^^^^^^^^^^^ missing match arm 119 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
120 match (false, ((), false)) { (true, ((), true)) => (), } 120 match (false, ((), false)) { (true, ((), true)) => (), }
121 //^^^^^^^^^^^^^^^^^^^^ missing match arm 121 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
122 match (false, ((), false)) { (true, _) => (), } 122 match (false, ((), false)) { (true, _) => (), }
123 //^^^^^^^^^^^^^^^^^^^^ missing match arm 123 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
124 124
125 match (false, ((), false)) { 125 match (false, ((), false)) {
126 (true, ((), true)) => (), 126 (true, ((), true)) => (),
@@ -146,12 +146,12 @@ enum Either { A, B, }
146 146
147fn main() { 147fn main() {
148 match Either::A { } 148 match Either::A { }
149 //^^^^^^^^^ missing match arm 149 //^^^^^^^^^ error: missing match arm
150 match Either::B { Either::A => (), } 150 match Either::B { Either::A => (), }
151 //^^^^^^^^^ missing match arm 151 //^^^^^^^^^ error: missing match arm
152 152
153 match &Either::B { 153 match &Either::B {
154 //^^^^^^^^^^ missing match arm 154 //^^^^^^^^^^ error: missing match arm
155 Either::A => (), 155 Either::A => (),
156 } 156 }
157 157
@@ -174,9 +174,9 @@ enum Either { A(bool), B }
174 174
175fn main() { 175fn main() {
176 match Either::B { } 176 match Either::B { }
177 //^^^^^^^^^ missing match arm 177 //^^^^^^^^^ error: missing match arm
178 match Either::B { 178 match Either::B {
179 //^^^^^^^^^ missing match arm 179 //^^^^^^^^^ error: missing match arm
180 Either::A(true) => (), Either::B => () 180 Either::A(true) => (), Either::B => ()
181 } 181 }
182 182
@@ -207,7 +207,7 @@ enum Either { A(bool), B(bool, bool) }
207 207
208fn main() { 208fn main() {
209 match Either::A(false) { 209 match Either::A(false) {
210 //^^^^^^^^^^^^^^^^ missing match arm 210 //^^^^^^^^^^^^^^^^ error: missing match arm
211 Either::A(_) => (), 211 Either::A(_) => (),
212 Either::B(false, _) => (), 212 Either::B(false, _) => (),
213 } 213 }
@@ -352,7 +352,7 @@ fn main() {
352 Either::A => (), 352 Either::A => (),
353 } 353 }
354 match loop { break Foo::A } { 354 match loop { break Foo::A } {
355 //^^^^^^^^^^^^^^^^^^^^^ missing match arm 355 //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm
356 Either::A => (), 356 Either::A => (),
357 } 357 }
358 match loop { break Foo::A } { 358 match loop { break Foo::A } {
@@ -390,19 +390,19 @@ enum Either { A { foo: bool }, B }
390fn main() { 390fn main() {
391 let a = Either::A { foo: true }; 391 let a = Either::A { foo: true };
392 match a { } 392 match a { }
393 //^ missing match arm 393 //^ error: missing match arm
394 match a { Either::A { foo: true } => () } 394 match a { Either::A { foo: true } => () }
395 //^ missing match arm 395 //^ error: missing match arm
396 match a { 396 match a {
397 Either::A { } => (), 397 Either::A { } => (),
398 //^^^^^^^^^ Missing structure fields: 398 //^^^^^^^^^ error: missing structure fields:
399 // | - foo 399 // | - foo
400 Either::B => (), 400 Either::B => (),
401 } 401 }
402 match a { 402 match a {
403 //^ missing match arm 403 //^ error: missing match arm
404 Either::A { } => (), 404 Either::A { } => (),
405 } //^^^^^^^^^ Missing structure fields: 405 } //^^^^^^^^^ error: missing structure fields:
406 // | - foo 406 // | - foo
407 407
408 match a { 408 match a {
@@ -431,7 +431,7 @@ enum Either {
431fn main() { 431fn main() {
432 let a = Either::A { foo: true, bar: () }; 432 let a = Either::A { foo: true, bar: () };
433 match a { 433 match a {
434 //^ missing match arm 434 //^ error: missing match arm
435 Either::A { bar: (), foo: false } => (), 435 Either::A { bar: (), foo: false } => (),
436 Either::A { foo: true, bar: () } => (), 436 Either::A { foo: true, bar: () } => (),
437 } 437 }
@@ -458,12 +458,12 @@ enum Either {
458fn main() { 458fn main() {
459 let a = Either::B; 459 let a = Either::B;
460 match a { 460 match a {
461 //^ missing match arm 461 //^ error: missing match arm
462 Either::A { foo: true, .. } => (), 462 Either::A { foo: true, .. } => (),
463 Either::B => (), 463 Either::B => (),
464 } 464 }
465 match a { 465 match a {
466 //^ missing match arm 466 //^ error: missing match arm
467 Either::A { .. } => (), 467 Either::A { .. } => (),
468 } 468 }
469 469
@@ -493,14 +493,14 @@ enum Either {
493 493
494fn main() { 494fn main() {
495 match Either::B { 495 match Either::B {
496 //^^^^^^^^^ missing match arm 496 //^^^^^^^^^ error: missing match arm
497 Either::A(true, .., true) => (), 497 Either::A(true, .., true) => (),
498 Either::A(true, .., false) => (), 498 Either::A(true, .., false) => (),
499 Either::A(false, .., false) => (), 499 Either::A(false, .., false) => (),
500 Either::B => (), 500 Either::B => (),
501 } 501 }
502 match Either::B { 502 match Either::B {
503 //^^^^^^^^^ missing match arm 503 //^^^^^^^^^ error: missing match arm
504 Either::A(true, .., true) => (), 504 Either::A(true, .., true) => (),
505 Either::A(true, .., false) => (), 505 Either::A(true, .., false) => (),
506 Either::A(.., true) => (), 506 Either::A(.., true) => (),
@@ -537,7 +537,7 @@ fn enum_(never: Never) {
537} 537}
538fn enum_ref(never: &Never) { 538fn enum_ref(never: &Never) {
539 match never {} 539 match never {}
540 //^^^^^ missing match arm 540 //^^^^^ error: missing match arm
541} 541}
542fn bang(never: !) { 542fn bang(never: !) {
543 match never {} 543 match never {}
@@ -561,7 +561,7 @@ fn main() {
561 Some(never) => match never {}, 561 Some(never) => match never {},
562 } 562 }
563 match Option::<Never>::None { 563 match Option::<Never>::None {
564 //^^^^^^^^^^^^^^^^^^^^^ missing match arm 564 //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm
565 Option::Some(_never) => {}, 565 Option::Some(_never) => {},
566 } 566 }
567} 567}
@@ -575,7 +575,7 @@ fn main() {
575 r#" 575 r#"
576fn main() { 576fn main() {
577 match (false, true, false) { 577 match (false, true, false) {
578 //^^^^^^^^^^^^^^^^^^^^ missing match arm 578 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
579 (false, ..) => (), 579 (false, ..) => (),
580 } 580 }
581}"#, 581}"#,
@@ -588,7 +588,7 @@ fn main() {
588 r#" 588 r#"
589fn main() { 589fn main() {
590 match (false, true, false) { 590 match (false, true, false) {
591 //^^^^^^^^^^^^^^^^^^^^ missing match arm 591 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
592 (.., false) => (), 592 (.., false) => (),
593 } 593 }
594}"#, 594}"#,
@@ -601,7 +601,7 @@ fn main() {
601 r#" 601 r#"
602fn main() { 602fn main() {
603 match (false, true, false) { 603 match (false, true, false) {
604 //^^^^^^^^^^^^^^^^^^^^ missing match arm 604 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
605 (true, .., false) => (), 605 (true, .., false) => (),
606 } 606 }
607}"#, 607}"#,
@@ -614,11 +614,11 @@ fn main() {
614 r#"struct Foo { a: bool } 614 r#"struct Foo { a: bool }
615fn main(f: Foo) { 615fn main(f: Foo) {
616 match f {} 616 match f {}
617 //^ missing match arm 617 //^ error: missing match arm
618 match f { Foo { a: true } => () } 618 match f { Foo { a: true } => () }
619 //^ missing match arm 619 //^ error: missing match arm
620 match &f { Foo { a: true } => () } 620 match &f { Foo { a: true } => () }
621 //^^ missing match arm 621 //^^ error: missing match arm
622 match f { Foo { a: _ } => () } 622 match f { Foo { a: _ } => () }
623 match f { 623 match f {
624 Foo { a: true } => (), 624 Foo { a: true } => (),
@@ -639,9 +639,9 @@ fn main(f: Foo) {
639 r#"struct Foo(bool); 639 r#"struct Foo(bool);
640fn main(f: Foo) { 640fn main(f: Foo) {
641 match f {} 641 match f {}
642 //^ missing match arm 642 //^ error: missing match arm
643 match f { Foo(true) => () } 643 match f { Foo(true) => () }
644 //^ missing match arm 644 //^ error: missing match arm
645 match f { 645 match f {
646 Foo(true) => (), 646 Foo(true) => (),
647 Foo(false) => (), 647 Foo(false) => (),
@@ -657,7 +657,7 @@ fn main(f: Foo) {
657 r#"struct Foo; 657 r#"struct Foo;
658fn main(f: Foo) { 658fn main(f: Foo) {
659 match f {} 659 match f {}
660 //^ missing match arm 660 //^ error: missing match arm
661 match f { Foo => () } 661 match f { Foo => () }
662} 662}
663"#, 663"#,
@@ -670,9 +670,9 @@ fn main(f: Foo) {
670 r#"struct Foo { foo: bool, bar: bool } 670 r#"struct Foo { foo: bool, bar: bool }
671fn main(f: Foo) { 671fn main(f: Foo) {
672 match f { Foo { foo: true, .. } => () } 672 match f { Foo { foo: true, .. } => () }
673 //^ missing match arm 673 //^ error: missing match arm
674 match f { 674 match f {
675 //^ missing match arm 675 //^ error: missing match arm
676 Foo { foo: true, .. } => (), 676 Foo { foo: true, .. } => (),
677 Foo { bar: false, .. } => () 677 Foo { bar: false, .. } => ()
678 } 678 }
@@ -693,7 +693,7 @@ fn main(f: Foo) {
693fn main() { 693fn main() {
694 enum Either { A(bool), B } 694 enum Either { A(bool), B }
695 match Either::B { 695 match Either::B {
696 //^^^^^^^^^ missing match arm 696 //^^^^^^^^^ error: missing match arm
697 Either::A(true | false) => (), 697 Either::A(true | false) => (),
698 } 698 }
699} 699}
@@ -715,7 +715,7 @@ fn main(v: S) {
715 match v { S{..} => {} } 715 match v { S{..} => {} }
716 match v { _ => {} } 716 match v { _ => {} }
717 match v { } 717 match v { }
718 //^ missing match arm 718 //^ error: missing match arm
719} 719}
720"#, 720"#,
721 ); 721 );
@@ -731,7 +731,7 @@ fn main() {
731 false => {} 731 false => {}
732 } 732 }
733 match true { _x @ true => {} } 733 match true { _x @ true => {} }
734 //^^^^ missing match arm 734 //^^^^ error: missing match arm
735} 735}
736"#, 736"#,
737 ); 737 );
@@ -786,12 +786,12 @@ use lib::E;
786fn main() { 786fn main() {
787 match E::A { _ => {} } 787 match E::A { _ => {} }
788 match E::A { 788 match E::A {
789 //^^^^ missing match arm 789 //^^^^ error: missing match arm
790 E::A => {} 790 E::A => {}
791 E::B => {} 791 E::B => {}
792 } 792 }
793 match E::A { 793 match E::A {
794 //^^^^ missing match arm 794 //^^^^ error: missing match arm
795 E::A | E::B => {} 795 E::A | E::B => {}
796 } 796 }
797} 797}
@@ -810,7 +810,7 @@ fn main() {
810 false => {} 810 false => {}
811 } 811 }
812 match true { 812 match true {
813 //^^^^ missing match arm 813 //^^^^ error: missing match arm
814 true if false => {} 814 true if false => {}
815 false => {} 815 false => {}
816 } 816 }
diff --git a/crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
index 06005d156..63de54570 100644
--- a/crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
@@ -1,10 +1,9 @@
1use hir::db::AstDatabase; 1use hir::db::AstDatabase;
2use ide_assists::Assist; 2use ide_db::{assists::Assist, source_change::SourceChange};
3use ide_db::source_change::SourceChange;
4use syntax::AstNode; 3use syntax::AstNode;
5use text_edit::TextEdit; 4use text_edit::TextEdit;
6 5
7use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; 6use crate::{fix, Diagnostic, DiagnosticsContext};
8 7
9// Diagnostic: missing-ok-or-some-in-tail-expr 8// Diagnostic: missing-ok-or-some-in-tail-expr
10// 9//
@@ -18,7 +17,7 @@ use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
18// 10 17// 10
19// } 18// }
20// ``` 19// ```
21pub(super) fn missing_ok_or_some_in_tail_expr( 20pub(crate) fn missing_ok_or_some_in_tail_expr(
22 ctx: &DiagnosticsContext<'_>, 21 ctx: &DiagnosticsContext<'_>,
23 d: &hir::MissingOkOrSomeInTailExpr, 22 d: &hir::MissingOkOrSomeInTailExpr,
24) -> Diagnostic { 23) -> Diagnostic {
@@ -44,7 +43,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Op
44 43
45#[cfg(test)] 44#[cfg(test)]
46mod tests { 45mod tests {
47 use crate::diagnostics::tests::{check_diagnostics, check_fix}; 46 use crate::tests::{check_diagnostics, check_fix};
48 47
49 #[test] 48 #[test]
50 fn test_wrap_return_type_option() { 49 fn test_wrap_return_type_option() {
diff --git a/crates/ide/src/diagnostics/missing_unsafe.rs b/crates/ide_diagnostics/src/handlers/missing_unsafe.rs
index 5c47e8d0a..7acd9228a 100644
--- a/crates/ide/src/diagnostics/missing_unsafe.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_unsafe.rs
@@ -1,9 +1,9 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 1use crate::{Diagnostic, DiagnosticsContext};
2 2
3// Diagnostic: missing-unsafe 3// Diagnostic: missing-unsafe
4// 4//
5// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. 5// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
6pub(super) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { 6pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
7 Diagnostic::new( 7 Diagnostic::new(
8 "missing-unsafe", 8 "missing-unsafe",
9 "this operation is unsafe and requires an unsafe function or block", 9 "this operation is unsafe and requires an unsafe function or block",
@@ -13,7 +13,7 @@ pub(super) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf
13 13
14#[cfg(test)] 14#[cfg(test)]
15mod tests { 15mod tests {
16 use crate::diagnostics::tests::check_diagnostics; 16 use crate::tests::check_diagnostics;
17 17
18 #[test] 18 #[test]
19 fn missing_unsafe_diagnostic_with_raw_ptr() { 19 fn missing_unsafe_diagnostic_with_raw_ptr() {
@@ -23,7 +23,7 @@ fn main() {
23 let x = &5 as *const usize; 23 let x = &5 as *const usize;
24 unsafe { let y = *x; } 24 unsafe { let y = *x; }
25 let z = *x; 25 let z = *x;
26} //^^ this operation is unsafe and requires an unsafe function or block 26} //^^ error: this operation is unsafe and requires an unsafe function or block
27"#, 27"#,
28 ) 28 )
29 } 29 }
@@ -48,9 +48,9 @@ unsafe fn unsafe_fn() {
48 48
49fn main() { 49fn main() {
50 unsafe_fn(); 50 unsafe_fn();
51 //^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block 51 //^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
52 HasUnsafe.unsafe_fn(); 52 HasUnsafe.unsafe_fn();
53 //^^^^^^^^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block 53 //^^^^^^^^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
54 unsafe { 54 unsafe {
55 unsafe_fn(); 55 unsafe_fn();
56 HasUnsafe.unsafe_fn(); 56 HasUnsafe.unsafe_fn();
@@ -72,7 +72,7 @@ static mut STATIC_MUT: Ty = Ty { a: 0 };
72 72
73fn main() { 73fn main() {
74 let x = STATIC_MUT.a; 74 let x = STATIC_MUT.a;
75 //^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block 75 //^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
76 unsafe { 76 unsafe {
77 let x = STATIC_MUT.a; 77 let x = STATIC_MUT.a;
78 } 78 }
@@ -93,7 +93,7 @@ extern "rust-intrinsic" {
93fn main() { 93fn main() {
94 let _ = bitreverse(12); 94 let _ = bitreverse(12);
95 let _ = floorf32(12.0); 95 let _ = floorf32(12.0);
96 //^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block 96 //^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
97} 97}
98"#, 98"#,
99 ); 99 );
diff --git a/crates/ide/src/diagnostics/no_such_field.rs b/crates/ide_diagnostics/src/handlers/no_such_field.rs
index edc63c246..92e8867f4 100644
--- a/crates/ide/src/diagnostics/no_such_field.rs
+++ b/crates/ide_diagnostics/src/handlers/no_such_field.rs
@@ -6,15 +6,12 @@ use syntax::{
6}; 6};
7use text_edit::TextEdit; 7use text_edit::TextEdit;
8 8
9use crate::{ 9use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
10 diagnostics::{fix, Diagnostic, DiagnosticsContext},
11 Assist,
12};
13 10
14// Diagnostic: no-such-field 11// Diagnostic: no-such-field
15// 12//
16// This diagnostic is triggered if created structure does not have field provided in record. 13// This diagnostic is triggered if created structure does not have field provided in record.
17pub(super) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { 14pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
18 Diagnostic::new( 15 Diagnostic::new(
19 "no-such-field", 16 "no-such-field",
20 "no such field", 17 "no such field",
@@ -112,7 +109,7 @@ fn missing_record_expr_field_fixes(
112 109
113#[cfg(test)] 110#[cfg(test)]
114mod tests { 111mod tests {
115 use crate::diagnostics::tests::{check_diagnostics, check_fix}; 112 use crate::tests::{check_diagnostics, check_fix};
116 113
117 #[test] 114 #[test]
118 fn no_such_field_diagnostics() { 115 fn no_such_field_diagnostics() {
@@ -122,11 +119,11 @@ struct S { foo: i32, bar: () }
122impl S { 119impl S {
123 fn new() -> S { 120 fn new() -> S {
124 S { 121 S {
125 //^ Missing structure fields: 122 //^ 💡 error: missing structure fields:
126 //| - bar 123 //| - bar
127 foo: 92, 124 foo: 92,
128 baz: 62, 125 baz: 62,
129 //^^^^^^^ no such field 126 //^^^^^^^ 💡 error: no such field
130 } 127 }
131 } 128 }
132} 129}
diff --git a/crates/ide/src/diagnostics/remove_this_semicolon.rs b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs
index 814cb0f8c..4e639f214 100644
--- a/crates/ide/src/diagnostics/remove_this_semicolon.rs
+++ b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs
@@ -3,15 +3,12 @@ use ide_db::source_change::SourceChange;
3use syntax::{ast, AstNode}; 3use syntax::{ast, AstNode};
4use text_edit::TextEdit; 4use text_edit::TextEdit;
5 5
6use crate::{ 6use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
7 diagnostics::{fix, Diagnostic, DiagnosticsContext},
8 Assist,
9};
10 7
11// Diagnostic: remove-this-semicolon 8// Diagnostic: remove-this-semicolon
12// 9//
13// This diagnostic is triggered when there's an erroneous `;` at the end of the block. 10// This diagnostic is triggered when there's an erroneous `;` at the end of the block.
14pub(super) fn remove_this_semicolon( 11pub(crate) fn remove_this_semicolon(
15 ctx: &DiagnosticsContext<'_>, 12 ctx: &DiagnosticsContext<'_>,
16 d: &hir::RemoveThisSemicolon, 13 d: &hir::RemoveThisSemicolon,
17) -> Diagnostic { 14) -> Diagnostic {
@@ -45,14 +42,14 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<V
45 42
46#[cfg(test)] 43#[cfg(test)]
47mod tests { 44mod tests {
48 use crate::diagnostics::tests::{check_diagnostics, check_fix}; 45 use crate::tests::{check_diagnostics, check_fix};
49 46
50 #[test] 47 #[test]
51 fn missing_semicolon() { 48 fn missing_semicolon() {
52 check_diagnostics( 49 check_diagnostics(
53 r#" 50 r#"
54fn test() -> i32 { 123; } 51fn test() -> i32 { 123; }
55 //^^^ remove this semicolon 52 //^^^ 💡 error: remove this semicolon
56"#, 53"#,
57 ); 54 );
58 } 55 }
diff --git a/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs b/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index f3b011495..cd87a10bb 100644
--- a/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs
+++ b/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -6,15 +6,12 @@ use syntax::{
6}; 6};
7use text_edit::TextEdit; 7use text_edit::TextEdit;
8 8
9use crate::{ 9use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity};
10 diagnostics::{fix, Diagnostic, DiagnosticsContext},
11 Assist, Severity,
12};
13 10
14// Diagnostic: replace-filter-map-next-with-find-map 11// Diagnostic: replace-filter-map-next-with-find-map
15// 12//
16// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. 13// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
17pub(super) fn replace_filter_map_next_with_find_map( 14pub(crate) fn replace_filter_map_next_with_find_map(
18 ctx: &DiagnosticsContext<'_>, 15 ctx: &DiagnosticsContext<'_>,
19 d: &hir::ReplaceFilterMapNextWithFindMap, 16 d: &hir::ReplaceFilterMapNextWithFindMap,
20) -> Diagnostic { 17) -> Diagnostic {
@@ -58,7 +55,7 @@ fn fixes(
58 55
59#[cfg(test)] 56#[cfg(test)]
60mod tests { 57mod tests {
61 use crate::diagnostics::tests::check_fix; 58 use crate::tests::check_fix;
62 59
63 // Register the required standard library types to make the tests work 60 // Register the required standard library types to make the tests work
64 #[track_caller] 61 #[track_caller]
@@ -86,7 +83,7 @@ pub mod iter {
86 } 83 }
87} 84}
88"#; 85"#;
89 crate::diagnostics::tests::check_diagnostics(&format!("{}{}{}", prefix, ra_fixture, suffix)) 86 crate::tests::check_diagnostics(&format!("{}{}{}", prefix, ra_fixture, suffix))
90 } 87 }
91 88
92 #[test] 89 #[test]
@@ -94,8 +91,8 @@ pub mod iter {
94 check_diagnostics( 91 check_diagnostics(
95 r#" 92 r#"
96 fn foo() { 93 fn foo() {
97 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); 94 let m = [1, 2, 3].iter().filter_map(|x| Some(92)).next();
98 } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..) 95 } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..)
99"#, 96"#,
100 ); 97 );
101 } 98 }
@@ -107,7 +104,7 @@ pub mod iter {
107fn foo() { 104fn foo() {
108 let m = [1, 2, 3] 105 let m = [1, 2, 3]
109 .iter() 106 .iter()
110 .filter_map(|x| if *x == 2 { Some (4) } else { None }) 107 .filter_map(|x| Some(92))
111 .len(); 108 .len();
112} 109}
113"#, 110"#,
@@ -121,7 +118,7 @@ fn foo() {
121fn foo() { 118fn foo() {
122 let m = [1, 2, 3] 119 let m = [1, 2, 3]
123 .iter() 120 .iter()
124 .filter_map(|x| if *x == 2 { Some (4) } else { None }) 121 .filter_map(|x| Some(92))
125 .map(|x| x + 2) 122 .map(|x| x + 2)
126 .len(); 123 .len();
127} 124}
@@ -136,7 +133,7 @@ fn foo() {
136fn foo() { 133fn foo() {
137 let m = [1, 2, 3] 134 let m = [1, 2, 3]
138 .iter() 135 .iter()
139 .filter_map(|x| if *x == 2 { Some (4) } else { None }); 136 .filter_map(|x| Some(92));
140 let n = m.next(); 137 let n = m.next();
141} 138}
142"#, 139"#,
@@ -151,7 +148,7 @@ fn foo() {
151use core::iter::Iterator; 148use core::iter::Iterator;
152use core::option::Option::{self, Some, None}; 149use core::option::Option::{self, Some, None};
153fn foo() { 150fn foo() {
154 let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); 151 let m = [1, 2, 3].iter().$0filter_map(|x| Some(92)).next();
155} 152}
156//- /core/lib.rs crate:core 153//- /core/lib.rs crate:core
157pub mod option { 154pub mod option {
@@ -174,7 +171,7 @@ pub mod iter {
174use core::iter::Iterator; 171use core::iter::Iterator;
175use core::option::Option::{self, Some, None}; 172use core::option::Option::{self, Some, None};
176fn foo() { 173fn foo() {
177 let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None }); 174 let m = [1, 2, 3].iter().find_map(|x| Some(92));
178} 175}
179"#, 176"#,
180 ) 177 )
diff --git a/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs b/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs
index 09faa3bbc..e879de75c 100644
--- a/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs
+++ b/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs
@@ -1,12 +1,9 @@
1use crate::{ 1use crate::{Diagnostic, DiagnosticsContext, Severity};
2 diagnostics::{Diagnostic, DiagnosticsContext},
3 Severity,
4};
5 2
6// Diagnostic: unimplemented-builtin-macro 3// Diagnostic: unimplemented-builtin-macro
7// 4//
8// This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer 5// This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer
9pub(super) fn unimplemented_builtin_macro( 6pub(crate) fn unimplemented_builtin_macro(
10 ctx: &DiagnosticsContext<'_>, 7 ctx: &DiagnosticsContext<'_>,
11 d: &hir::UnimplementedBuiltinMacro, 8 d: &hir::UnimplementedBuiltinMacro,
12) -> Diagnostic { 9) -> Diagnostic {
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide_diagnostics/src/handlers/unlinked_file.rs
index a5b2e3399..8e601fa48 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide_diagnostics/src/handlers/unlinked_file.rs
@@ -12,37 +12,31 @@ use syntax::{
12}; 12};
13use text_edit::TextEdit; 13use text_edit::TextEdit;
14 14
15use crate::{ 15use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
16 diagnostics::{fix, DiagnosticsContext},
17 Assist, Diagnostic,
18};
19
20#[derive(Debug)]
21pub(crate) struct UnlinkedFile {
22 pub(crate) file: FileId,
23}
24 16
25// Diagnostic: unlinked-file 17// Diagnostic: unlinked-file
26// 18//
27// This diagnostic is shown for files that are not included in any crate, or files that are part of 19// This diagnostic is shown for files that are not included in any crate, or files that are part of
28// crates rust-analyzer failed to discover. The file will not have IDE features available. 20// crates rust-analyzer failed to discover. The file will not have IDE features available.
29pub(super) fn unlinked_file(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Diagnostic { 21pub(crate) fn unlinked_file(ctx: &DiagnosticsContext, acc: &mut Vec<Diagnostic>, file_id: FileId) {
30 // Limit diagnostic to the first few characters in the file. This matches how VS Code 22 // Limit diagnostic to the first few characters in the file. This matches how VS Code
31 // renders it with the full span, but on other editors, and is less invasive. 23 // renders it with the full span, but on other editors, and is less invasive.
32 let range = ctx.sema.db.parse(d.file).syntax_node().text_range(); 24 let range = ctx.sema.db.parse(file_id).syntax_node().text_range();
33 // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`. 25 // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
34 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); 26 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
35 27
36 Diagnostic::new("unlinked-file", "file not included in module tree", range) 28 acc.push(
37 .with_fixes(fixes(ctx, d)) 29 Diagnostic::new("unlinked-file", "file not included in module tree", range)
30 .with_fixes(fixes(ctx, file_id)),
31 );
38} 32}
39 33
40fn fixes(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Option<Vec<Assist>> { 34fn fixes(ctx: &DiagnosticsContext, file_id: FileId) -> Option<Vec<Assist>> {
41 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, 35 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
42 // suggest that as a fix. 36 // suggest that as a fix.
43 37
44 let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(d.file)); 38 let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(file_id));
45 let our_path = source_root.path_for_file(&d.file)?; 39 let our_path = source_root.path_for_file(&file_id)?;
46 let module_name = our_path.name_and_extension()?.0; 40 let module_name = our_path.name_and_extension()?.0;
47 41
48 // Candidates to look for: 42 // Candidates to look for:
@@ -71,7 +65,7 @@ fn fixes(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Option<Vec<Assist>> {
71 } 65 }
72 66
73 if module.origin.file_id() == Some(*parent_id) { 67 if module.origin.file_id() == Some(*parent_id) {
74 return make_fixes(ctx.sema.db, *parent_id, module_name, d.file); 68 return make_fixes(ctx.sema.db, *parent_id, module_name, file_id);
75 } 69 }
76 } 70 }
77 } 71 }
@@ -164,7 +158,7 @@ fn make_fixes(
164 158
165#[cfg(test)] 159#[cfg(test)]
166mod tests { 160mod tests {
167 use crate::diagnostics::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix}; 161 use crate::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix};
168 162
169 #[test] 163 #[test]
170 fn unlinked_file_prepend_first_item() { 164 fn unlinked_file_prepend_first_item() {
diff --git a/crates/ide/src/diagnostics/unresolved_extern_crate.rs b/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs
index 2ea79c2ee..74e4a69c6 100644
--- a/crates/ide/src/diagnostics/unresolved_extern_crate.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs
@@ -1,9 +1,9 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 1use crate::{Diagnostic, DiagnosticsContext};
2 2
3// Diagnostic: unresolved-extern-crate 3// Diagnostic: unresolved-extern-crate
4// 4//
5// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. 5// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
6pub(super) fn unresolved_extern_crate( 6pub(crate) fn unresolved_extern_crate(
7 ctx: &DiagnosticsContext<'_>, 7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::UnresolvedExternCrate, 8 d: &hir::UnresolvedExternCrate,
9) -> Diagnostic { 9) -> Diagnostic {
@@ -16,7 +16,7 @@ pub(super) fn unresolved_extern_crate(
16 16
17#[cfg(test)] 17#[cfg(test)]
18mod tests { 18mod tests {
19 use crate::diagnostics::tests::check_diagnostics; 19 use crate::tests::check_diagnostics;
20 20
21 #[test] 21 #[test]
22 fn unresolved_extern_crate() { 22 fn unresolved_extern_crate() {
@@ -25,7 +25,7 @@ mod tests {
25//- /main.rs crate:main deps:core 25//- /main.rs crate:main deps:core
26extern crate core; 26extern crate core;
27 extern crate doesnotexist; 27 extern crate doesnotexist;
28//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate 28//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
29//- /lib.rs crate:core 29//- /lib.rs crate:core
30"#, 30"#,
31 ); 31 );
@@ -38,7 +38,7 @@ extern crate core;
38 r#" 38 r#"
39//- /lib.rs 39//- /lib.rs
40 extern crate doesnotexist; 40 extern crate doesnotexist;
41//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate 41//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
42// Should not error. 42// Should not error.
43extern crate self as foo; 43extern crate self as foo;
44struct Foo; 44struct Foo;
diff --git a/crates/ide/src/diagnostics/unresolved_import.rs b/crates/ide_diagnostics/src/handlers/unresolved_import.rs
index 1cbf96ba1..e52a88459 100644
--- a/crates/ide/src/diagnostics/unresolved_import.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_import.rs
@@ -1,10 +1,10 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 1use crate::{Diagnostic, DiagnosticsContext};
2 2
3// Diagnostic: unresolved-import 3// Diagnostic: unresolved-import
4// 4//
5// This diagnostic is triggered if rust-analyzer is unable to resolve a path in 5// This diagnostic is triggered if rust-analyzer is unable to resolve a path in
6// a `use` declaration. 6// a `use` declaration.
7pub(super) fn unresolved_import( 7pub(crate) fn unresolved_import(
8 ctx: &DiagnosticsContext<'_>, 8 ctx: &DiagnosticsContext<'_>,
9 d: &hir::UnresolvedImport, 9 d: &hir::UnresolvedImport,
10) -> Diagnostic { 10) -> Diagnostic {
@@ -22,7 +22,7 @@ pub(super) fn unresolved_import(
22 22
23#[cfg(test)] 23#[cfg(test)]
24mod tests { 24mod tests {
25 use crate::diagnostics::tests::check_diagnostics; 25 use crate::tests::check_diagnostics;
26 26
27 #[test] 27 #[test]
28 fn unresolved_import() { 28 fn unresolved_import() {
@@ -30,7 +30,7 @@ mod tests {
30 r#" 30 r#"
31use does_exist; 31use does_exist;
32use does_not_exist; 32use does_not_exist;
33 //^^^^^^^^^^^^^^ unresolved import 33 //^^^^^^^^^^^^^^ error: unresolved import
34 34
35mod does_exist {} 35mod does_exist {}
36"#, 36"#,
@@ -43,18 +43,18 @@ mod does_exist {}
43 check_diagnostics( 43 check_diagnostics(
44 r#" 44 r#"
45use does_exist::{Exists, DoesntExist}; 45use does_exist::{Exists, DoesntExist};
46 //^^^^^^^^^^^ unresolved import 46 //^^^^^^^^^^^ error: unresolved import
47 47
48use {does_not_exist::*, does_exist}; 48use {does_not_exist::*, does_exist};
49 //^^^^^^^^^^^^^^^^^ unresolved import 49 //^^^^^^^^^^^^^^^^^ error: unresolved import
50 50
51use does_not_exist::{ 51use does_not_exist::{
52 a, 52 a,
53 //^ unresolved import 53 //^ error: unresolved import
54 b, 54 b,
55 //^ unresolved import 55 //^ error: unresolved import
56 c, 56 c,
57 //^ unresolved import 57 //^ error: unresolved import
58}; 58};
59 59
60mod does_exist { 60mod does_exist {
@@ -71,18 +71,18 @@ mod does_exist {
71//- /main.rs crate:main 71//- /main.rs crate:main
72mod a { 72mod a {
73 extern crate doesnotexist; 73 extern crate doesnotexist;
74 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate 74 //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
75 75
76 // Should not error, since we already errored for the missing crate. 76 // Should not error, since we already errored for the missing crate.
77 use doesnotexist::{self, bla, *}; 77 use doesnotexist::{self, bla, *};
78 78
79 use crate::doesnotexist; 79 use crate::doesnotexist;
80 //^^^^^^^^^^^^^^^^^^^ unresolved import 80 //^^^^^^^^^^^^^^^^^^^ error: unresolved import
81} 81}
82 82
83mod m { 83mod m {
84 use super::doesnotexist; 84 use super::doesnotexist;
85 //^^^^^^^^^^^^^^^^^^^ unresolved import 85 //^^^^^^^^^^^^^^^^^^^ error: unresolved import
86} 86}
87"#, 87"#,
88 ); 88 );
diff --git a/crates/ide/src/diagnostics/unresolved_macro_call.rs b/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs
index 15b6a2730..f0f7725db 100644
--- a/crates/ide/src/diagnostics/unresolved_macro_call.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs
@@ -1,13 +1,13 @@
1use hir::{db::AstDatabase, InFile}; 1use hir::{db::AstDatabase, InFile};
2use syntax::{AstNode, SyntaxNodePtr}; 2use syntax::{AstNode, SyntaxNodePtr};
3 3
4use crate::diagnostics::{Diagnostic, DiagnosticsContext}; 4use crate::{Diagnostic, DiagnosticsContext};
5 5
6// Diagnostic: unresolved-macro-call 6// Diagnostic: unresolved-macro-call
7// 7//
8// This diagnostic is triggered if rust-analyzer is unable to resolve the path 8// This diagnostic is triggered if rust-analyzer is unable to resolve the path
9// to a macro in a macro invocation. 9// to a macro in a macro invocation.
10pub(super) fn unresolved_macro_call( 10pub(crate) fn unresolved_macro_call(
11 ctx: &DiagnosticsContext<'_>, 11 ctx: &DiagnosticsContext<'_>,
12 d: &hir::UnresolvedMacroCall, 12 d: &hir::UnresolvedMacroCall,
13) -> Diagnostic { 13) -> Diagnostic {
@@ -32,7 +32,7 @@ pub(super) fn unresolved_macro_call(
32 32
33#[cfg(test)] 33#[cfg(test)]
34mod tests { 34mod tests {
35 use crate::diagnostics::tests::check_diagnostics; 35 use crate::tests::check_diagnostics;
36 36
37 #[test] 37 #[test]
38 fn unresolved_macro_diag() { 38 fn unresolved_macro_diag() {
@@ -40,7 +40,7 @@ mod tests {
40 r#" 40 r#"
41fn f() { 41fn f() {
42 m!(); 42 m!();
43} //^ unresolved macro `m!` 43} //^ error: unresolved macro `m!`
44 44
45"#, 45"#,
46 ); 46 );
@@ -51,7 +51,7 @@ fn f() {
51 check_diagnostics( 51 check_diagnostics(
52 r#" 52 r#"
53foo::bar!(92); 53foo::bar!(92);
54 //^^^ unresolved macro `foo::bar!` 54 //^^^ error: unresolved macro `foo::bar!`
55"#, 55"#,
56 ); 56 );
57 } 57 }
@@ -63,7 +63,7 @@ foo::bar!(92);
63macro_rules! m { () => {} } 63macro_rules! m { () => {} }
64 64
65m!(); m2!(); 65m!(); m2!();
66 //^^ unresolved macro `self::m2!` 66 //^^ error: unresolved macro `self::m2!`
67"#, 67"#,
68 ); 68 );
69 } 69 }
@@ -77,7 +77,7 @@ mod mac {
77macro_rules! m { () => {} } } 77macro_rules! m { () => {} } }
78 78
79self::m!(); self::m2!(); 79self::m!(); self::m2!();
80 //^^ unresolved macro `self::m2!` 80 //^^ error: unresolved macro `self::m2!`
81"#, 81"#,
82 ); 82 );
83 } 83 }
diff --git a/crates/ide/src/diagnostics/unresolved_module.rs b/crates/ide_diagnostics/src/handlers/unresolved_module.rs
index 977b46414..61fc43604 100644
--- a/crates/ide/src/diagnostics/unresolved_module.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_module.rs
@@ -1,14 +1,13 @@
1use hir::db::AstDatabase; 1use hir::db::AstDatabase;
2use ide_assists::Assist; 2use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
3use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit};
4use syntax::AstNode; 3use syntax::AstNode;
5 4
6use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; 5use crate::{fix, Diagnostic, DiagnosticsContext};
7 6
8// Diagnostic: unresolved-module 7// Diagnostic: unresolved-module
9// 8//
10// This diagnostic is triggered if rust-analyzer is unable to discover referred module. 9// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
11pub(super) fn unresolved_module( 10pub(crate) fn unresolved_module(
12 ctx: &DiagnosticsContext<'_>, 11 ctx: &DiagnosticsContext<'_>,
13 d: &hir::UnresolvedModule, 12 d: &hir::UnresolvedModule,
14) -> Diagnostic { 13) -> Diagnostic {
@@ -42,7 +41,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<
42mod tests { 41mod tests {
43 use expect_test::expect; 42 use expect_test::expect;
44 43
45 use crate::diagnostics::tests::{check_diagnostics, check_expect}; 44 use crate::tests::{check_diagnostics, check_expect};
46 45
47 #[test] 46 #[test]
48 fn unresolved_module() { 47 fn unresolved_module() {
@@ -51,7 +50,7 @@ mod tests {
51//- /lib.rs 50//- /lib.rs
52mod foo; 51mod foo;
53 mod bar; 52 mod bar;
54//^^^^^^^^ unresolved module 53//^^^^^^^^ 💡 error: unresolved module
55mod baz {} 54mod baz {}
56//- /foo.rs 55//- /foo.rs
57"#, 56"#,
diff --git a/crates/ide/src/diagnostics/unresolved_proc_macro.rs b/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs
index 3dc6ab451..fde1d1323 100644
--- a/crates/ide/src/diagnostics/unresolved_proc_macro.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -1,7 +1,4 @@
1use crate::{ 1use crate::{Diagnostic, DiagnosticsContext, Severity};
2 diagnostics::{Diagnostic, DiagnosticsContext},
3 Severity,
4};
5 2
6// Diagnostic: unresolved-proc-macro 3// Diagnostic: unresolved-proc-macro
7// 4//
@@ -12,7 +9,7 @@ use crate::{
12// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the 9// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
13// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can 10// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
14// enable support for procedural macros (see `rust-analyzer.procMacro.enable`). 11// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
15pub(super) fn unresolved_proc_macro( 12pub(crate) fn unresolved_proc_macro(
16 ctx: &DiagnosticsContext<'_>, 13 ctx: &DiagnosticsContext<'_>,
17 d: &hir::UnresolvedProcMacro, 14 d: &hir::UnresolvedProcMacro,
18) -> Diagnostic { 15) -> Diagnostic {
diff --git a/crates/ide_diagnostics/src/handlers/useless_braces.rs b/crates/ide_diagnostics/src/handlers/useless_braces.rs
new file mode 100644
index 000000000..8b9330e04
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/useless_braces.rs
@@ -0,0 +1,148 @@
1use ide_db::{base_db::FileId, source_change::SourceChange};
2use itertools::Itertools;
3use syntax::{ast, AstNode, SyntaxNode, TextRange};
4use text_edit::TextEdit;
5
6use crate::{fix, Diagnostic, Severity};
7
8// Diagnostic: unnecessary-braces
9//
10// Diagnostic for unnecessary braces in `use` items.
11pub(crate) fn useless_braces(
12 acc: &mut Vec<Diagnostic>,
13 file_id: FileId,
14 node: &SyntaxNode,
15) -> Option<()> {
16 let use_tree_list = ast::UseTreeList::cast(node.clone())?;
17 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
18 // If there is a comment inside the bracketed `use`,
19 // assume it is a commented out module path and don't show diagnostic.
20 if use_tree_list.has_inner_comment() {
21 return Some(());
22 }
23
24 let use_range = use_tree_list.syntax().text_range();
25 let edit = remove_braces(&single_use_tree).unwrap_or_else(|| {
26 let to_replace = single_use_tree.syntax().text().to_string();
27 let mut edit_builder = TextEdit::builder();
28 edit_builder.delete(use_range);
29 edit_builder.insert(use_range.start(), to_replace);
30 edit_builder.finish()
31 });
32
33 acc.push(
34 Diagnostic::new(
35 "unnecessary-braces",
36 "Unnecessary braces in use statement".to_string(),
37 use_range,
38 )
39 .severity(Severity::WeakWarning)
40 .with_fixes(Some(vec![fix(
41 "remove_braces",
42 "Remove unnecessary braces",
43 SourceChange::from_text_edit(file_id, edit),
44 use_range,
45 )])),
46 );
47 }
48
49 Some(())
50}
51
52fn remove_braces(single_use_tree: &ast::UseTree) -> Option<TextEdit> {
53 let use_tree_list_node = single_use_tree.syntax().parent()?;
54 if single_use_tree.path()?.segment()?.self_token().is_some() {
55 let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
56 let end = use_tree_list_node.text_range().end();
57 return Some(TextEdit::delete(TextRange::new(start, end)));
58 }
59 None
60}
61
62#[cfg(test)]
63mod tests {
64 use crate::tests::{check_diagnostics, check_fix};
65
66 #[test]
67 fn test_check_unnecessary_braces_in_use_statement() {
68 check_diagnostics(
69 r#"
70use a;
71use a::{c, d::e};
72
73mod a {
74 mod c {}
75 mod d {
76 mod e {}
77 }
78}
79"#,
80 );
81 check_diagnostics(
82 r#"
83use a;
84use a::{
85 c,
86 // d::e
87};
88
89mod a {
90 mod c {}
91 mod d {
92 mod e {}
93 }
94}
95"#,
96 );
97 check_fix(
98 r#"
99mod b {}
100use {$0b};
101"#,
102 r#"
103mod b {}
104use b;
105"#,
106 );
107 check_fix(
108 r#"
109mod b {}
110use {b$0};
111"#,
112 r#"
113mod b {}
114use b;
115"#,
116 );
117 check_fix(
118 r#"
119mod a { mod c {} }
120use a::{c$0};
121"#,
122 r#"
123mod a { mod c {} }
124use a::c;
125"#,
126 );
127 check_fix(
128 r#"
129mod a {}
130use a::{self$0};
131"#,
132 r#"
133mod a {}
134use a;
135"#,
136 );
137 check_fix(
138 r#"
139mod a { mod c {} mod d { mod e {} } }
140use a::{c, d::{e$0}};
141"#,
142 r#"
143mod a { mod c {} mod d { mod e {} } }
144use a::{c, d::e};
145"#,
146 );
147 }
148}
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs
new file mode 100644
index 000000000..6ad1b4373
--- /dev/null
+++ b/crates/ide_diagnostics/src/lib.rs
@@ -0,0 +1,374 @@
1//! Diagnostics rendering and fixits.
2//!
3//! Most of the diagnostics originate from the dark depth of the compiler, and
4//! are originally expressed in term of IR. When we emit the diagnostic, we are
5//! usually not in the position to decide how to best "render" it in terms of
6//! user-authored source code. We are especially not in the position to offer
7//! fixits, as the compiler completely lacks the infrastructure to edit the
8//! source code.
9//!
10//! Instead, we "bubble up" raw, structured diagnostics until the `hir` crate,
11//! where we "cook" them so that each diagnostic is formulated in terms of `hir`
12//! types. Well, at least that's the aspiration, the "cooking" is somewhat
13//! ad-hoc at the moment. Anyways, we get a bunch of ide-friendly diagnostic
14//! structs from hir, and we want to render them to unified serializable
15//! representation (span, level, message) here. If we can, we also provide
16//! fixits. By the way, that's why we want to keep diagnostics structured
17//! internally -- so that we have all the info to make fixes.
18//!
19//! We have one "handler" module per diagnostic code. Such a module contains
20//! rendering, optional fixes and tests. It's OK if some low-level compiler
21//! functionality ends up being tested via a diagnostic.
22//!
23//! There are also a couple of ad-hoc diagnostics implemented directly here, we
24//! don't yet have a great pattern for how to do them properly.
25
26mod handlers {
27 pub(crate) mod break_outside_of_loop;
28 pub(crate) mod inactive_code;
29 pub(crate) mod incorrect_case;
30 pub(crate) mod macro_error;
31 pub(crate) mod mismatched_arg_count;
32 pub(crate) mod missing_fields;
33 pub(crate) mod missing_match_arms;
34 pub(crate) mod missing_ok_or_some_in_tail_expr;
35 pub(crate) mod missing_unsafe;
36 pub(crate) mod no_such_field;
37 pub(crate) mod remove_this_semicolon;
38 pub(crate) mod replace_filter_map_next_with_find_map;
39 pub(crate) mod unimplemented_builtin_macro;
40 pub(crate) mod unresolved_extern_crate;
41 pub(crate) mod unresolved_import;
42 pub(crate) mod unresolved_macro_call;
43 pub(crate) mod unresolved_module;
44 pub(crate) mod unresolved_proc_macro;
45
46 // The handlers bellow are unusual, the implement the diagnostics as well.
47 pub(crate) mod field_shorthand;
48 pub(crate) mod useless_braces;
49 pub(crate) mod unlinked_file;
50}
51
52use hir::{diagnostics::AnyDiagnostic, Semantics};
53use ide_db::{
54 assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
55 base_db::{FileId, SourceDatabase},
56 label::Label,
57 source_change::SourceChange,
58 RootDatabase,
59};
60use rustc_hash::FxHashSet;
61use syntax::{ast::AstNode, TextRange};
62
63#[derive(Copy, Clone, Debug, PartialEq)]
64pub struct DiagnosticCode(pub &'static str);
65
66impl DiagnosticCode {
67 pub fn as_str(&self) -> &str {
68 self.0
69 }
70}
71
72#[derive(Debug)]
73pub struct Diagnostic {
74 pub code: DiagnosticCode,
75 pub message: String,
76 pub range: TextRange,
77 pub severity: Severity,
78 pub unused: bool,
79 pub experimental: bool,
80 pub fixes: Option<Vec<Assist>>,
81}
82
83impl Diagnostic {
84 fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
85 let message = message.into();
86 Diagnostic {
87 code: DiagnosticCode(code),
88 message,
89 range,
90 severity: Severity::Error,
91 unused: false,
92 experimental: false,
93 fixes: None,
94 }
95 }
96
97 fn experimental(mut self) -> Diagnostic {
98 self.experimental = true;
99 self
100 }
101
102 fn severity(mut self, severity: Severity) -> Diagnostic {
103 self.severity = severity;
104 self
105 }
106
107 fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic {
108 self.fixes = fixes;
109 self
110 }
111
112 fn with_unused(mut self, unused: bool) -> Diagnostic {
113 self.unused = unused;
114 self
115 }
116}
117
118#[derive(Debug, Copy, Clone)]
119pub enum Severity {
120 Error,
121 // We don't actually emit this one yet, but we should at some point.
122 // Warning,
123 WeakWarning,
124}
125
126#[derive(Default, Debug, Clone)]
127pub struct DiagnosticsConfig {
128 pub disable_experimental: bool,
129 pub disabled: FxHashSet<String>,
130}
131
132struct DiagnosticsContext<'a> {
133 config: &'a DiagnosticsConfig,
134 sema: Semantics<'a, RootDatabase>,
135 resolve: &'a AssistResolveStrategy,
136}
137
138pub fn diagnostics(
139 db: &RootDatabase,
140 config: &DiagnosticsConfig,
141 resolve: &AssistResolveStrategy,
142 file_id: FileId,
143) -> Vec<Diagnostic> {
144 let _p = profile::span("diagnostics");
145 let sema = Semantics::new(db);
146 let parse = db.parse(file_id);
147 let mut res = Vec::new();
148
149 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
150 res.extend(
151 parse.errors().iter().take(128).map(|err| {
152 Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range())
153 }),
154 );
155
156 for node in parse.tree().syntax().descendants() {
157 handlers::useless_braces::useless_braces(&mut res, file_id, &node);
158 handlers::field_shorthand::field_shorthand(&mut res, file_id, &node);
159 }
160
161 let module = sema.to_module_def(file_id);
162
163 let ctx = DiagnosticsContext { config, sema, resolve };
164 if module.is_none() {
165 handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id);
166 }
167
168 let mut diags = Vec::new();
169 if let Some(m) = module {
170 m.diagnostics(db, &mut diags)
171 }
172
173 for diag in diags {
174 #[rustfmt::skip]
175 let d = match diag {
176 AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
177 AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
178 AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
179 AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
180 AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
181 AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
182 AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => handlers::missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d),
183 AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
184 AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
185 AnyDiagnostic::RemoveThisSemicolon(d) => handlers::remove_this_semicolon::remove_this_semicolon(&ctx, &d),
186 AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
187 AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
188 AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
189 AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
190 AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
191 AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
192 AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
193
194 AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
195 Some(it) => it,
196 None => continue,
197 }
198 };
199 res.push(d)
200 }
201
202 res.retain(|d| {
203 !ctx.config.disabled.contains(d.code.as_str())
204 && !(ctx.config.disable_experimental && d.experimental)
205 });
206
207 res
208}
209
210fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
211 let mut res = unresolved_fix(id, label, target);
212 res.source_change = Some(source_change);
213 res
214}
215
216fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
217 assert!(!id.contains(' '));
218 Assist {
219 id: AssistId(id, AssistKind::QuickFix),
220 label: Label::new(label),
221 group: None,
222 target,
223 source_change: None,
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use expect_test::Expect;
230 use ide_db::{
231 assists::AssistResolveStrategy,
232 base_db::{fixture::WithFixture, SourceDatabaseExt},
233 RootDatabase,
234 };
235 use stdx::trim_indent;
236 use test_utils::{assert_eq_text, extract_annotations};
237
238 use crate::{DiagnosticsConfig, Severity};
239
240 /// Takes a multi-file input fixture with annotated cursor positions,
241 /// and checks that:
242 /// * a diagnostic is produced
243 /// * the first diagnostic fix trigger range touches the input cursor position
244 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
245 #[track_caller]
246 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
247 check_nth_fix(0, ra_fixture_before, ra_fixture_after);
248 }
249 /// Takes a multi-file input fixture with annotated cursor positions,
250 /// and checks that:
251 /// * a diagnostic is produced
252 /// * every diagnostic fixes trigger range touches the input cursor position
253 /// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
254 pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
255 for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
256 check_nth_fix(i, ra_fixture_before, ra_fixture_after)
257 }
258 }
259
260 #[track_caller]
261 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
262 let after = trim_indent(ra_fixture_after);
263
264 let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
265 let diagnostic = super::diagnostics(
266 &db,
267 &DiagnosticsConfig::default(),
268 &AssistResolveStrategy::All,
269 file_position.file_id,
270 )
271 .pop()
272 .expect("no diagnostics");
273 let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth];
274 let actual = {
275 let source_change = fix.source_change.as_ref().unwrap();
276 let file_id = *source_change.source_file_edits.keys().next().unwrap();
277 let mut actual = db.file_text(file_id).to_string();
278
279 for edit in source_change.source_file_edits.values() {
280 edit.apply(&mut actual);
281 }
282 actual
283 };
284
285 assert_eq_text!(&after, &actual);
286 assert!(
287 fix.target.contains_inclusive(file_position.offset),
288 "diagnostic fix range {:?} does not touch cursor position {:?}",
289 fix.target,
290 file_position.offset
291 );
292 }
293
294 /// Checks that there's a diagnostic *without* fix at `$0`.
295 pub(crate) fn check_no_fix(ra_fixture: &str) {
296 let (db, file_position) = RootDatabase::with_position(ra_fixture);
297 let diagnostic = super::diagnostics(
298 &db,
299 &DiagnosticsConfig::default(),
300 &AssistResolveStrategy::All,
301 file_position.file_id,
302 )
303 .pop()
304 .unwrap();
305 assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
306 }
307
308 pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
309 let (db, file_id) = RootDatabase::with_single_file(ra_fixture);
310 let diagnostics = super::diagnostics(
311 &db,
312 &DiagnosticsConfig::default(),
313 &AssistResolveStrategy::All,
314 file_id,
315 );
316 expect.assert_debug_eq(&diagnostics)
317 }
318
319 #[track_caller]
320 pub(crate) fn check_diagnostics(ra_fixture: &str) {
321 let mut config = DiagnosticsConfig::default();
322 config.disabled.insert("inactive-code".to_string());
323 check_diagnostics_with_config(config, ra_fixture)
324 }
325
326 #[track_caller]
327 pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
328 let (db, files) = RootDatabase::with_many_files(ra_fixture);
329 for file_id in files {
330 let diagnostics =
331 super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
332
333 let expected = extract_annotations(&*db.file_text(file_id));
334 let mut actual = diagnostics
335 .into_iter()
336 .map(|d| {
337 let mut annotation = String::new();
338 if let Some(fixes) = &d.fixes {
339 assert!(!fixes.is_empty());
340 annotation.push_str("💡 ")
341 }
342 annotation.push_str(match d.severity {
343 Severity::Error => "error",
344 Severity::WeakWarning => "weak",
345 });
346 annotation.push_str(": ");
347 annotation.push_str(&d.message);
348 (d.range, annotation)
349 })
350 .collect::<Vec<_>>();
351 actual.sort_by_key(|(range, _)| range.start());
352 assert_eq!(expected, actual);
353 }
354 }
355
356 #[test]
357 fn test_disabled_diagnostics() {
358 let mut config = DiagnosticsConfig::default();
359 config.disabled.insert("unresolved-module".into());
360
361 let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#);
362
363 let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
364 assert!(diagnostics.is_empty());
365
366 let diagnostics = super::diagnostics(
367 &db,
368 &DiagnosticsConfig::default(),
369 &AssistResolveStrategy::All,
370 file_id,
371 );
372 assert!(!diagnostics.is_empty());
373 }
374}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3b20d741a..5588c15da 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1062,8 +1062,8 @@ mod tests {
1062 let package_json_path = project_root().join("editors/code/package.json"); 1062 let package_json_path = project_root().join("editors/code/package.json");
1063 let mut package_json = fs::read_to_string(&package_json_path).unwrap(); 1063 let mut package_json = fs::read_to_string(&package_json_path).unwrap();
1064 1064
1065 let start_marker = " \"$generated-start\": false,\n"; 1065 let start_marker = " \"$generated-start\": {},\n";
1066 let end_marker = " \"$generated-end\": false\n"; 1066 let end_marker = " \"$generated-end\": {}\n";
1067 1067
1068 let start = package_json.find(start_marker).unwrap() + start_marker.len(); 1068 let start = package_json.find(start_marker).unwrap() + start_marker.len();
1069 let end = package_json.find(end_marker).unwrap(); 1069 let end = package_json.find(end_marker).unwrap();
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index e22c295f9..260a504e7 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -75,7 +75,9 @@ impl<'a> Project<'a> {
75 profile::init_from(crate::PROFILE); 75 profile::init_from(crate::PROFILE);
76 }); 76 });
77 77
78 for entry in Fixture::parse(self.fixture) { 78 let (mini_core, fixtures) = Fixture::parse(self.fixture);
79 assert!(mini_core.is_none());
80 for entry in fixtures {
79 let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); 81 let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
80 fs::create_dir_all(path.parent().unwrap()).unwrap(); 82 fs::create_dir_all(path.parent().unwrap()).unwrap();
81 fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); 83 fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 3d27d2c1a..2bd9ad867 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -325,6 +325,15 @@ impl ast::Impl {
325 let second = types.next(); 325 let second = types.next();
326 (first, second) 326 (first, second)
327 } 327 }
328
329 pub fn for_trait_name_ref(name_ref: &ast::NameRef) -> Option<ast::Impl> {
330 let this = name_ref.syntax().ancestors().find_map(ast::Impl::cast)?;
331 if this.trait_()?.syntax().text_range().start() == name_ref.syntax().text_range().start() {
332 Some(this)
333 } else {
334 None
335 }
336 }
328} 337}
329 338
330#[derive(Debug, Clone, PartialEq, Eq)] 339#[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast
index ff5877a7b..31f76589d 100644
--- a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast
+++ b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast
@@ -21,7 +21,7 @@ [email protected]
21 [email protected] 21 [email protected]
22 [email protected] 22 [email protected]
23 [email protected] 23 [email protected]
24 [email protected] "ignore" 24 [email protected] "Ignore"
25 [email protected] "]" 25 [email protected] "]"
26 [email protected] "\n" 26 [email protected] "\n"
27 [email protected] "fn" 27 [email protected] "fn"
diff --git a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs
index 3d2e01d5c..6f04cb171 100644
--- a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs
+++ b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs
@@ -1,5 +1,5 @@
1#[cfg(test)] 1#[cfg(test)]
2#[ignore] 2#[Ignore]
3fn foo() {} 3fn foo() {}
4 4
5#[path = "a.rs"] 5#[path = "a.rs"]
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs
index d0bddf7d8..6ba112de8 100644
--- a/crates/test_utils/src/fixture.rs
+++ b/crates/test_utils/src/fixture.rs
@@ -77,6 +77,11 @@ pub struct Fixture {
77 pub introduce_new_source_root: bool, 77 pub introduce_new_source_root: bool,
78} 78}
79 79
80pub struct MiniCore {
81 activated_flags: Vec<String>,
82 valid_flags: Vec<String>,
83}
84
80impl Fixture { 85impl Fixture {
81 /// Parses text which looks like this: 86 /// Parses text which looks like this:
82 /// 87 ///
@@ -86,12 +91,28 @@ impl Fixture {
86 /// line 2 91 /// line 2
87 /// //- other meta 92 /// //- other meta
88 /// ``` 93 /// ```
89 pub fn parse(ra_fixture: &str) -> Vec<Fixture> { 94 ///
95 /// Fixture can also start with a minicore declaration:
96 ///
97 /// ```
98 /// //- minicore: sized
99 /// ```
100 ///
101 /// That will include a subset of `libcore` into the fixture, see
102 /// `minicore.rs` for what's available.
103 pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<Fixture>) {
90 let fixture = trim_indent(ra_fixture); 104 let fixture = trim_indent(ra_fixture);
91 105 let mut fixture = fixture.as_str();
106 let mut mini_core = None;
92 let mut res: Vec<Fixture> = Vec::new(); 107 let mut res: Vec<Fixture> = Vec::new();
93 108
94 let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") }; 109 if fixture.starts_with("//- minicore:") {
110 let first_line = fixture.split_inclusive('\n').next().unwrap();
111 mini_core = Some(MiniCore::parse(first_line));
112 fixture = &fixture[first_line.len()..];
113 }
114
115 let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
95 116
96 for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() { 117 for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() {
97 if line.contains("//-") { 118 if line.contains("//-") {
@@ -113,7 +134,7 @@ impl Fixture {
113 } 134 }
114 } 135 }
115 136
116 res 137 (mini_core, res)
117 } 138 }
118 139
119 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo 140 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -172,6 +193,133 @@ impl Fixture {
172 } 193 }
173} 194}
174 195
196impl MiniCore {
197 fn has_flag(&self, flag: &str) -> bool {
198 self.activated_flags.iter().any(|it| it == flag)
199 }
200
201 #[track_caller]
202 fn assert_valid_flag(&self, flag: &str) {
203 if !self.valid_flags.iter().any(|it| it == flag) {
204 panic!("invalid flag: {:?}, valid flags: {:?}", flag, self.valid_flags);
205 }
206 }
207
208 fn parse(line: &str) -> MiniCore {
209 let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() };
210
211 let line = line.strip_prefix("//- minicore:").unwrap().trim();
212 for entry in line.split(", ") {
213 if res.has_flag(entry) {
214 panic!("duplicate minicore flag: {:?}", entry)
215 }
216 res.activated_flags.push(entry.to_string())
217 }
218
219 res
220 }
221
222 /// Strips parts of minicore.rs which are flagged by inactive flags.
223 ///
224 /// This is probably over-engineered to support flags dependencies.
225 pub fn source_code(mut self) -> String {
226 let mut buf = String::new();
227 let raw_mini_core = include_str!("./minicore.rs");
228 let mut lines = raw_mini_core.split_inclusive('\n');
229
230 let mut parsing_flags = false;
231 let mut implications = Vec::new();
232
233 // Parse `//!` preamble and extract flags and dependencies.
234 for line in lines.by_ref() {
235 let line = match line.strip_prefix("//!") {
236 Some(it) => it,
237 None => {
238 assert!(line.trim().is_empty());
239 break;
240 }
241 };
242
243 if parsing_flags {
244 let (flag, deps) = line.split_once(':').unwrap();
245 let flag = flag.trim();
246 self.valid_flags.push(flag.to_string());
247 for dep in deps.split(", ") {
248 let dep = dep.trim();
249 if !dep.is_empty() {
250 self.assert_valid_flag(dep);
251 implications.push((flag, dep));
252 }
253 }
254 }
255
256 if line.contains("Available flags:") {
257 parsing_flags = true;
258 }
259 }
260
261 for flag in &self.activated_flags {
262 self.assert_valid_flag(flag);
263 }
264
265 // Fixed point loop to compute transitive closure of flags.
266 loop {
267 let mut changed = false;
268 for &(u, v) in implications.iter() {
269 if self.has_flag(u) && !self.has_flag(v) {
270 self.activated_flags.push(v.to_string());
271 changed = true;
272 }
273 }
274 if !changed {
275 break;
276 }
277 }
278
279 let mut curr_region = "";
280 let mut seen_regions = Vec::new();
281 for line in lines {
282 let trimmed = line.trim();
283 if let Some(region) = trimmed.strip_prefix("// region:") {
284 assert_eq!(curr_region, "");
285 curr_region = region;
286 continue;
287 }
288 if let Some(region) = trimmed.strip_prefix("// endregion:") {
289 assert_eq!(curr_region, region);
290 curr_region = "";
291 continue;
292 }
293 seen_regions.push(curr_region);
294
295 let mut flag = curr_region;
296 if let Some(idx) = trimmed.find("// :") {
297 flag = &trimmed[idx + "// :".len()..];
298 }
299
300 let skip = if flag == "" {
301 false
302 } else {
303 assert!(!flag.starts_with(' '), "region marker starts with a space: {:?}", flag);
304 self.assert_valid_flag(flag);
305 !self.has_flag(flag)
306 };
307
308 if !skip {
309 buf.push_str(line)
310 }
311 }
312
313 for flag in &self.valid_flags {
314 if !seen_regions.iter().any(|it| it == flag) {
315 panic!("unused minicore flag: {:?}", flag);
316 }
317 }
318
319 buf
320 }
321}
322
175#[test] 323#[test]
176#[should_panic] 324#[should_panic]
177fn parse_fixture_checks_further_indented_metadata() { 325fn parse_fixture_checks_further_indented_metadata() {
@@ -189,12 +337,14 @@ fn parse_fixture_checks_further_indented_metadata() {
189 337
190#[test] 338#[test]
191fn parse_fixture_gets_full_meta() { 339fn parse_fixture_gets_full_meta() {
192 let parsed = Fixture::parse( 340 let (mini_core, parsed) = Fixture::parse(
193 r" 341 r#"
194 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo 342//- minicore: coerce_unsized
195 mod m; 343//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
196 ", 344mod m;
345"#,
197 ); 346 );
347 assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
198 assert_eq!(1, parsed.len()); 348 assert_eq!(1, parsed.len());
199 349
200 let meta = &parsed[0]; 350 let meta = &parsed[0];
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index b2fe25f82..d55bae62a 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -23,7 +23,10 @@ use text_size::{TextRange, TextSize};
23pub use dissimilar::diff as __diff; 23pub use dissimilar::diff as __diff;
24pub use rustc_hash::FxHashMap; 24pub use rustc_hash::FxHashMap;
25 25
26pub use crate::{assert_linear::AssertLinear, fixture::Fixture}; 26pub use crate::{
27 assert_linear::AssertLinear,
28 fixture::{Fixture, MiniCore},
29};
27 30
28pub const CURSOR_MARKER: &str = "$0"; 31pub const CURSOR_MARKER: &str = "$0";
29pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; 32pub const ESCAPED_CURSOR_MARKER: &str = "\\$0";
diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs
new file mode 100644
index 000000000..5ff60178c
--- /dev/null
+++ b/crates/test_utils/src/minicore.rs
@@ -0,0 +1,204 @@
1//! This is a fixture we use for tests that need lang items.
2//!
3//! We want to include the minimal subset of core for each test, so this file
4//! supports "conditional compilation". Tests use the following syntax to include minicore:
5//!
6//! //- minicore: flag1, flag2
7//!
8//! We then strip all the code marked with other flags.
9//!
10//! Available flags:
11//! sized:
12//! slice:
13//! range:
14//! unsize: sized
15//! deref: sized
16//! coerce_unsized: unsize
17//! pin:
18//! future: pin
19//! option:
20//! result:
21
22pub mod marker {
23 // region:sized
24 #[lang = "sized"]
25 #[fundamental]
26 #[rustc_specialization_trait]
27 pub trait Sized {}
28 // endregion:sized
29
30 // region:unsize
31 #[lang = "unsize"]
32 pub trait Unsize<T: ?Sized> {}
33 // endregion:unsize
34}
35
36pub mod ops {
37 // region:coerce_unsized
38 mod unsize {
39 use crate::marker::Unsize;
40
41 #[lang = "coerce_unsized"]
42 pub trait CoerceUnsized<T: ?Sized> {}
43
44 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
45 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
46 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
47 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
48
49 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
50 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
51
52 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
53 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
54 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
55 }
56 pub use self::unsize::CoerceUnsized;
57 // endregion:coerce_unsized
58
59 // region:deref
60 mod deref {
61 #[lang = "deref"]
62 pub trait Deref {
63 #[lang = "deref_target"]
64 type Target: ?Sized;
65 fn deref(&self) -> &Self::Target;
66 }
67 }
68 pub use self::deref::Deref;
69 // endregion:deref
70
71 // region:range
72 mod range {
73 #[lang = "RangeFull"]
74 pub struct RangeFull;
75
76 #[lang = "Range"]
77 pub struct Range<Idx> {
78 pub start: Idx,
79 pub end: Idx,
80 }
81
82 #[lang = "RangeFrom"]
83 pub struct RangeFrom<Idx> {
84 pub start: Idx,
85 }
86
87 #[lang = "RangeTo"]
88 pub struct RangeTo<Idx> {
89 pub end: Idx,
90 }
91
92 #[lang = "RangeInclusive"]
93 pub struct RangeInclusive<Idx> {
94 pub(crate) start: Idx,
95 pub(crate) end: Idx,
96 pub(crate) exhausted: bool,
97 }
98
99 #[lang = "RangeToInclusive"]
100 pub struct RangeToInclusive<Idx> {
101 pub end: Idx,
102 }
103 }
104 pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
105 pub use self::range::{RangeInclusive, RangeToInclusive};
106 // endregion:range
107}
108
109// region:slice
110pub mod slice {
111 #[lang = "slice"]
112 impl<T> [T] {
113 pub fn len(&self) -> usize {
114 loop {}
115 }
116 }
117}
118// endregion:slice
119
120// region:option
121pub mod option {
122 pub enum Option<T> {
123 #[lang = "None"]
124 None,
125 #[lang = "Some"]
126 Some(T),
127 }
128}
129// endregion:option
130
131// region:result
132pub mod result {
133 pub enum Result<T, E> {
134 #[lang = "Ok"]
135 Ok(T),
136 #[lang = "Err"]
137 Err(E),
138 }
139}
140// endregion:result
141
142// region:pin
143pub mod pin {
144 #[lang = "pin"]
145 #[fundamental]
146 pub struct Pin<P> {
147 pointer: P,
148 }
149}
150// endregion:pin
151
152// region:future
153pub mod future {
154 use crate::{
155 pin::Pin,
156 task::{Context, Poll},
157 };
158
159 #[lang = "future_trait"]
160 pub trait Future {
161 type Output;
162 #[lang = "poll"]
163 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
164 }
165}
166pub mod task {
167 pub enum Poll<T> {
168 #[lang = "Ready"]
169 Ready(T),
170 #[lang = "Pending"]
171 Pending,
172 }
173
174 pub struct Context<'a> {
175 waker: &'a (),
176 }
177}
178// endregion:future
179
180pub mod prelude {
181 pub mod v1 {
182 pub use crate::{
183 marker::Sized, // :sized
184 option::Option::{self, None, Some}, // :option
185 result::Result::{self, Err, Ok}, // :result
186 };
187 }
188
189 pub mod rust_2015 {
190 pub use super::v1::*;
191 }
192
193 pub mod rust_2018 {
194 pub use super::v1::*;
195 }
196
197 pub mod rust_2021 {
198 pub use super::v1::*;
199 }
200}
201
202#[prelude_import]
203#[allow(unused)]
204use prelude::v1::*;
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 96dd684b3..84485ea28 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -174,6 +174,13 @@ Instead, explicitly check for `None`, `Err`, etc.
174`rust-analyzer` is not a library, we don't need to test for API misuse, and we have to handle any user input without panics. 174`rust-analyzer` is not a library, we don't need to test for API misuse, and we have to handle any user input without panics.
175Panic messages in the logs from the `#[should_panic]` tests are confusing. 175Panic messages in the logs from the `#[should_panic]` tests are confusing.
176 176
177## `#[ignore]`
178
179Do not `#[ignore]` tests.
180If the test currently does not work, assert the wrong behavior and add a fixme explaining why it is wrong.
181
182**Rationale:** noticing when the behavior is fixed, making sure that even the wrong behavior is acceptable (ie, not a panic).
183
177## Function Preconditions 184## Function Preconditions
178 185
179Express function preconditions in types and force the caller to provide them (rather than checking in callee): 186Express function preconditions in types and force the caller to provide them (rather than checking in callee):
diff --git a/docs/user/.gitignore b/docs/user/.gitignore
new file mode 100644
index 000000000..c32b1bcec
--- /dev/null
+++ b/docs/user/.gitignore
@@ -0,0 +1 @@
manual.html
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index d5f8dbb1d..816e094c2 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -201,6 +201,15 @@ $ eselect repository enable guru && emaint sync -r guru
201$ emerge rust-analyzer-bin 201$ emerge rust-analyzer-bin
202---- 202----
203 203
204==== macOS
205
206The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew].
207
208[source,bash]
209----
210$ brew install rust-analyzer
211----
212
204=== Emacs 213=== Emacs
205 214
206Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm]. 215Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm].
diff --git a/editors/code/package.json b/editors/code/package.json
index 1b85c39bd..aa47bd0ed 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -389,7 +389,7 @@
389 "default": {}, 389 "default": {},
390 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" 390 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`"
391 }, 391 },
392 "$generated-start": false, 392 "$generated-start": {},
393 "rust-analyzer.assist.importGranularity": { 393 "rust-analyzer.assist.importGranularity": {
394 "markdownDescription": "How imports should be grouped into use statements.", 394 "markdownDescription": "How imports should be grouped into use statements.",
395 "default": "crate", 395 "default": "crate",
@@ -841,7 +841,7 @@
841 "Search for all symbols kinds" 841 "Search for all symbols kinds"
842 ] 842 ]
843 }, 843 },
844 "$generated-end": false 844 "$generated-end": {}
845 } 845 }
846 }, 846 },
847 "problemPatterns": [ 847 "problemPatterns": [
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index f58d26215..15f2151ad 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -158,7 +158,7 @@ export async function deactivate() {
158} 158}
159 159
160async function bootstrap(config: Config, state: PersistentState): Promise<string> { 160async function bootstrap(config: Config, state: PersistentState): Promise<string> {
161 await vscode.workspace.fs.createDirectory(config.globalStorageUri); 161 await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
162 162
163 if (!config.currentExtensionIsNightly) { 163 if (!config.currentExtensionIsNightly) {
164 await state.updateNightlyReleaseId(undefined); 164 await state.updateNightlyReleaseId(undefined);
@@ -277,11 +277,11 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
277 ''; 277 '';
278 } 278 }
279 `; 279 `;
280 const origFile = vscode.Uri.file(dest.path + "-orig"); 280 const origFile = vscode.Uri.file(dest.fsPath + "-orig");
281 await vscode.workspace.fs.rename(dest, origFile); 281 await vscode.workspace.fs.rename(dest, origFile);
282 progress.report({ message: "Patching executable", increment: 20 }); 282 progress.report({ message: "Patching executable", increment: 20 });
283 await new Promise((resolve, reject) => { 283 await new Promise((resolve, reject) => {
284 const handle = exec(`nix-build -E - --argstr srcStr '${origFile.path}' -o '${dest.path}'`, 284 const handle = exec(`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
285 (err, stdout, stderr) => { 285 (err, stdout, stderr) => {
286 if (err != null) { 286 if (err != null) {
287 reject(Error(stderr)); 287 reject(Error(stderr));
@@ -338,14 +338,14 @@ async function getServer(config: Config, state: PersistentState): Promise<string
338 await state.updateServerVersion(undefined); 338 await state.updateServerVersion(undefined);
339 } 339 }
340 340
341 if (state.serverVersion === config.package.version) return dest.path; 341 if (state.serverVersion === config.package.version) return dest.fsPath;
342 342
343 if (config.askBeforeDownload) { 343 if (config.askBeforeDownload) {
344 const userResponse = await vscode.window.showInformationMessage( 344 const userResponse = await vscode.window.showInformationMessage(
345 `Language server version ${config.package.version} for rust-analyzer is not installed.`, 345 `Language server version ${config.package.version} for rust-analyzer is not installed.`,
346 "Download now" 346 "Download now"
347 ); 347 );
348 if (userResponse !== "Download now") return dest.path; 348 if (userResponse !== "Download now") return dest.fsPath;
349 } 349 }
350 350
351 const releaseTag = config.package.releaseTag; 351 const releaseTag = config.package.releaseTag;
@@ -372,7 +372,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
372 } 372 }
373 373
374 await state.updateServerVersion(config.package.version); 374 await state.updateServerVersion(config.package.version);
375 return dest.path; 375 return dest.fsPath;
376} 376}
377 377
378function serverPath(config: Config): string | null { 378function serverPath(config: Config): string | null {
@@ -383,7 +383,7 @@ async function isNixOs(): Promise<boolean> {
383 try { 383 try {
384 const contents = (await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))).toString(); 384 const contents = (await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))).toString();
385 return contents.indexOf("ID=nixos") !== -1; 385 return contents.indexOf("ID=nixos") !== -1;
386 } catch (e) { 386 } catch {
387 return false; 387 return false;
388 } 388 }
389} 389}
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 747c02db9..722dab756 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -91,7 +91,7 @@ export async function download(opts: DownloadOpts) {
91 // to prevent partially downloaded files when user kills vscode 91 // to prevent partially downloaded files when user kills vscode
92 // This also avoids overwriting running executables 92 // This also avoids overwriting running executables
93 const randomHex = crypto.randomBytes(5).toString("hex"); 93 const randomHex = crypto.randomBytes(5).toString("hex");
94 const rawDest = path.parse(opts.dest.path); 94 const rawDest = path.parse(opts.dest.fsPath);
95 const tempFilePath = vscode.Uri.joinPath(vscode.Uri.file(rawDest.dir), `${rawDest.name}${randomHex}`); 95 const tempFilePath = vscode.Uri.joinPath(vscode.Uri.file(rawDest.dir), `${rawDest.name}${randomHex}`);
96 96
97 await vscode.window.withProgress( 97 await vscode.window.withProgress(
@@ -116,7 +116,7 @@ export async function download(opts: DownloadOpts) {
116 } 116 }
117 ); 117 );
118 118
119 await vscode.workspace.fs.rename(tempFilePath, opts.dest); 119 await vscode.workspace.fs.rename(tempFilePath, opts.dest, { overwrite: true });
120} 120}
121 121
122async function downloadFile( 122async function downloadFile(
@@ -127,17 +127,19 @@ async function downloadFile(
127 httpProxy: string | null | undefined, 127 httpProxy: string | null | undefined,
128 onProgress: (readBytes: number, totalBytes: number) => void 128 onProgress: (readBytes: number, totalBytes: number) => void
129): Promise<void> { 129): Promise<void> {
130 const urlString = url.toString();
131
130 const res = await (() => { 132 const res = await (() => {
131 if (httpProxy) { 133 if (httpProxy) {
132 log.debug(`Downloading ${url.path} via proxy: ${httpProxy}`); 134 log.debug(`Downloading ${urlString} via proxy: ${httpProxy}`);
133 return fetch(url.path, { agent: new HttpsProxyAgent(httpProxy) }); 135 return fetch(urlString, { agent: new HttpsProxyAgent(httpProxy) });
134 } 136 }
135 137
136 return fetch(url.path); 138 return fetch(urlString);
137 })(); 139 })();
138 140
139 if (!res.ok) { 141 if (!res.ok) {
140 log.error("Error", res.status, "while downloading file from", url.path); 142 log.error("Error", res.status, "while downloading file from", urlString);
141 log.error({ body: await res.text(), headers: res.headers }); 143 log.error({ body: await res.text(), headers: res.headers });
142 144
143 throw new Error(`Got response ${res.status} when trying to download a file.`); 145 throw new Error(`Got response ${res.status} when trying to download a file.`);
@@ -146,7 +148,7 @@ async function downloadFile(
146 const totalBytes = Number(res.headers.get('content-length')); 148 const totalBytes = Number(res.headers.get('content-length'));
147 assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol"); 149 assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol");
148 150
149 log.debug("Downloading file of", totalBytes, "bytes size from", url.path, "to", destFilePath.path); 151 log.debug("Downloading file of", totalBytes, "bytes size from", urlString, "to", destFilePath.fsPath);
150 152
151 let readBytes = 0; 153 let readBytes = 0;
152 res.body.on("data", (chunk: Buffer) => { 154 res.body.on("data", (chunk: Buffer) => {
@@ -154,7 +156,7 @@ async function downloadFile(
154 onProgress(readBytes, totalBytes); 156 onProgress(readBytes, totalBytes);
155 }); 157 });
156 158
157 const destFileStream = fs.createWriteStream(destFilePath.path, { mode }); 159 const destFileStream = fs.createWriteStream(destFilePath.fsPath, { mode });
158 const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body; 160 const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body;
159 161
160 await pipeline(srcStream, destFileStream); 162 await pipeline(srcStream, destFileStream);
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index 902d0ddda..355dd76fe 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -159,7 +159,7 @@ export const getPathForExecutable = memoize(
159 // it is not mentioned in docs and cannot be infered by the type signature... 159 // it is not mentioned in docs and cannot be infered by the type signature...
160 const standardPath = vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo", "bin", executableName); 160 const standardPath = vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo", "bin", executableName);
161 161
162 if (isFile(standardPath.path)) return standardPath.path; 162 if (isFileAtUri(standardPath)) return standardPath.fsPath;
163 } catch (err) { 163 } catch (err) {
164 log.error("Failed to read the fs info", err); 164 log.error("Failed to read the fs info", err);
165 } 165 }
@@ -177,9 +177,17 @@ function lookupInPath(exec: string): boolean {
177 : [candidate]; 177 : [candidate];
178 }); 178 });
179 179
180 return candidates.some(isFile); 180 return candidates.some(isFileAtPath);
181} 181}
182 182
183async function isFile(path: string): Promise<boolean> { 183async function isFileAtPath(path: string): Promise<boolean> {
184 return ((await vscode.workspace.fs.stat(vscode.Uri.file(path))).type & vscode.FileType.File) !== 0; 184 return isFileAtUri(vscode.Uri.file(path));
185}
186
187async function isFileAtUri(uri: vscode.Uri): Promise<boolean> {
188 try {
189 return ((await vscode.workspace.fs.stat(uri)).type & vscode.FileType.File) !== 0;
190 } catch {
191 return false;
192 }
185} 193}
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index 3a67294c5..c7363688a 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -45,8 +45,8 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> {
45 patch 45 patch
46 .replace(r#""version": "0.4.0-dev""#, &format!(r#""version": "{}""#, version)) 46 .replace(r#""version": "0.4.0-dev""#, &format!(r#""version": "{}""#, version))
47 .replace(r#""releaseTag": null"#, &format!(r#""releaseTag": "{}""#, release_tag)) 47 .replace(r#""releaseTag": null"#, &format!(r#""releaseTag": "{}""#, release_tag))
48 .replace(r#""$generated-start": false,"#, "") 48 .replace(r#""$generated-start": {},"#, "")
49 .replace(",\n \"$generated-end\": false", ""); 49 .replace(",\n \"$generated-end\": {}", "");
50 50
51 if nightly { 51 if nightly {
52 patch.replace( 52 patch.replace(
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index f2ba8efef..06219d155 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -89,6 +89,7 @@ fn rust_files_are_tidy() {
89 let text = read_file(&path).unwrap(); 89 let text = read_file(&path).unwrap();
90 check_todo(&path, &text); 90 check_todo(&path, &text);
91 check_dbg(&path, &text); 91 check_dbg(&path, &text);
92 check_test_attrs(&path, &text);
92 check_trailing_ws(&path, &text); 93 check_trailing_ws(&path, &text);
93 deny_clippy(&path, &text); 94 deny_clippy(&path, &text);
94 tidy_docs.visit(&path, &text); 95 tidy_docs.visit(&path, &text);
@@ -334,6 +335,36 @@ fn check_dbg(path: &Path, text: &str) {
334 } 335 }
335} 336}
336 337
338fn check_test_attrs(path: &Path, text: &str) {
339 let ignore_rule =
340 "https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/style.md#ignore";
341 let need_ignore: &[&str] = &[
342 // Special case to run `#[ignore]` tests
343 "ide/src/runnables.rs",
344 // A legit test which needs to be ignored, as it takes too long to run
345 // :(
346 "hir_def/src/nameres/collector.rs",
347 // Obviously needs ignore.
348 "ide_assists/src/handlers/toggle_ignore.rs",
349 // See above.
350 "ide_assists/src/tests/generated.rs",
351 ];
352 if text.contains("#[ignore") && !need_ignore.iter().any(|p| path.ends_with(p)) {
353 panic!("\ndon't `#[ignore]` tests, see:\n\n {}\n\n {}\n", ignore_rule, path.display(),)
354 }
355
356 let panic_rule =
357 "https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/style.md#should_panic";
358 let need_panic: &[&str] = &["test_utils/src/fixture.rs"];
359 if text.contains("#[should_panic") && !need_panic.iter().any(|p| path.ends_with(p)) {
360 panic!(
361 "\ndon't add `#[should_panic]` tests, see:\n\n {}\n\n {}\n",
362 panic_rule,
363 path.display(),
364 )
365 }
366}
367
337fn check_trailing_ws(path: &Path, text: &str) { 368fn check_trailing_ws(path: &Path, text: &str) {
338 if is_exclude_dir(path, &["test_data"]) { 369 if is_exclude_dir(path, &["test_data"]) {
339 return; 370 return;