aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/base_db/src/fixture.rs41
-rw-r--r--crates/base_db/src/lib.rs2
-rw-r--r--crates/cfg/src/cfg_expr.rs2
-rw-r--r--crates/cfg/src/lib.rs2
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/diagnostics.rs297
-rw-r--r--crates/hir/src/has_source.rs2
-rw-r--r--crates/hir/src/lib.rs360
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/hir/src/source_analyzer.rs6
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/attr.rs6
-rw-r--r--crates/hir_def/src/body/lower.rs8
-rw-r--r--crates/hir_def/src/body/scope.rs2
-rw-r--r--crates/hir_def/src/body/tests.rs149
-rw-r--r--crates/hir_def/src/body/tests/block.rs10
-rw-r--r--crates/hir_def/src/generics.rs14
-rw-r--r--crates/hir_def/src/item_scope.rs2
-rw-r--r--crates/hir_def/src/item_tree/lower.rs8
-rw-r--r--crates/hir_def/src/item_tree/pretty.rs2
-rw-r--r--crates/hir_def/src/lib.rs70
-rw-r--r--crates/hir_def/src/nameres/collector.rs49
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs12
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs229
-rw-r--r--crates/hir_def/src/path/lower.rs4
-rw-r--r--crates/hir_def/src/resolver.rs10
-rw-r--r--crates/hir_def/src/test_db.rs152
-rw-r--r--crates/hir_def/src/type_ref.rs16
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs14
-rw-r--r--crates/hir_expand/src/db.rs4
-rw-r--r--crates/hir_expand/src/eager.rs19
-rw-r--r--crates/hir_expand/src/input.rs2
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs3
-rw-r--r--crates/hir_ty/Cargo.toml4
-rw-r--r--crates/hir_ty/src/builder.rs2
-rw-r--r--crates/hir_ty/src/chalk_db.rs39
-rw-r--r--crates/hir_ty/src/consteval.rs2
-rw-r--r--crates/hir_ty/src/db.rs7
-rw-r--r--crates/hir_ty/src/diagnostics.rs772
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs357
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs484
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs957
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/usefulness.rs8
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs151
-rw-r--r--crates/hir_ty/src/diagnostics_sink.rs109
-rw-r--r--crates/hir_ty/src/infer.rs96
-rw-r--r--crates/hir_ty/src/infer/coerce.rs8
-rw-r--r--crates/hir_ty/src/infer/expr.rs23
-rw-r--r--crates/hir_ty/src/infer/pat.rs4
-rw-r--r--crates/hir_ty/src/infer/path.rs6
-rw-r--r--crates/hir_ty/src/interner.rs6
-rw-r--r--crates/hir_ty/src/lib.rs3
-rw-r--r--crates/hir_ty/src/lower.rs10
-rw-r--r--crates/hir_ty/src/method_resolution.rs82
-rw-r--r--crates/hir_ty/src/tests.rs98
-rw-r--r--crates/hir_ty/src/tests/coercion.rs675
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs99
-rw-r--r--crates/hir_ty/src/tests/patterns.rs133
-rw-r--r--crates/hir_ty/src/tests/regression.rs162
-rw-r--r--crates/hir_ty/src/tests/simple.rs767
-rw-r--r--crates/hir_ty/src/tests/traits.rs578
-rw-r--r--crates/ide/Cargo.toml4
-rw-r--r--crates/ide/src/diagnostics.rs722
-rw-r--r--crates/ide/src/diagnostics/fixes.rs31
-rw-r--r--crates/ide/src/diagnostics/fixes/change_case.rs155
-rw-r--r--crates/ide/src/diagnostics/fixes/fill_missing_fields.rs217
-rw-r--r--crates/ide/src/diagnostics/fixes/remove_semicolon.rs41
-rw-r--r--crates/ide/src/diagnostics/fixes/replace_with_find_map.rs84
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs183
-rw-r--r--crates/ide/src/display/navigation_target.rs6
-rw-r--r--crates/ide/src/doc_links.rs40
-rw-r--r--crates/ide/src/extend_selection.rs2
-rw-r--r--crates/ide/src/fixture.rs20
-rw-r--r--crates/ide/src/goto_definition.rs26
-rw-r--r--crates/ide/src/goto_implementation.rs2
-rw-r--r--crates/ide/src/goto_type_definition.rs62
-rw-r--r--crates/ide/src/hover.rs30
-rw-r--r--crates/ide/src/inlay_hints.rs6
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/lib.rs38
-rw-r--r--crates/ide/src/references.rs4
-rw-r--r--crates/ide/src/rename.rs (renamed from crates/ide/src/references/rename.rs)434
-rw-r--r--crates/ide/src/runnables.rs8
-rw-r--r--crates/ide/src/syntax_highlighting.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs65
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs6
-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.html5
-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.rs9
-rw-r--r--crates/ide/src/typing/on_enter.rs6
-rw-r--r--crates/ide_assists/Cargo.toml2
-rw-r--r--crates/ide_assists/src/assist_context.rs3
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs4
-rw-r--r--crates/ide_assists/src/handlers/convert_comment_block.rs4
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs4
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs10
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs6
-rw-r--r--crates/ide_assists/src/handlers/fix_visibility.rs36
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_is_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_projection_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs57
-rw-r--r--crates/ide_assists/src/handlers/generate_getter.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_new.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_setter.rs2
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs1201
-rw-r--r--crates/ide_assists/src/handlers/remove_dbg.rs2
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs5
-rw-r--r--crates/ide_assists/src/handlers/unmerge_use.rs16
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs2
-rw-r--r--crates/ide_assists/src/lib.rs163
-rw-r--r--crates/ide_assists/src/tests.rs28
-rw-r--r--crates/ide_assists/src/utils.rs2
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs2
-rw-r--r--crates/ide_completion/Cargo.toml2
-rw-r--r--crates/ide_completion/src/completions.rs31
-rw-r--r--crates/ide_completion/src/completions/attribute.rs2
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs49
-rw-r--r--crates/ide_completion/src/completions/dot.rs2
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs18
-rw-r--r--crates/ide_completion/src/completions/keyword.rs20
-rw-r--r--crates/ide_completion/src/completions/postfix.rs20
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs4
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs87
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs72
-rw-r--r--crates/ide_completion/src/context.rs8
-rw-r--r--crates/ide_completion/src/patterns.rs19
-rw-r--r--crates/ide_completion/src/render.rs118
-rw-r--r--crates/ide_completion/src/render/macro_.rs4
-rw-r--r--crates/ide_completion/src/render/pattern.rs6
-rw-r--r--crates/ide_completion/src/render/type_alias.rs23
-rw-r--r--crates/ide_db/Cargo.toml2
-rw-r--r--crates/ide_db/src/assists.rs136
-rw-r--r--crates/ide_db/src/call_info.rs2
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs4
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs7
-rw-r--r--crates/ide_db/src/helpers/merge_imports.rs2
-rw-r--r--crates/ide_db/src/lib.rs5
-rw-r--r--crates/ide_db/src/rename.rs468
-rw-r--r--crates/ide_db/src/search.rs16
-rw-r--r--crates/ide_diagnostics/Cargo.toml29
-rw-r--r--crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs30
-rw-r--r--crates/ide_diagnostics/src/handlers/field_shorthand.rs (renamed from crates/ide/src/diagnostics/field_shorthand.rs)35
-rw-r--r--crates/ide_diagnostics/src/handlers/inactive_code.rs116
-rw-r--r--crates/ide_diagnostics/src/handlers/incorrect_case.rs459
-rw-r--r--crates/ide_diagnostics/src/handlers/macro_error.rs173
-rw-r--r--crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs272
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_fields.rs355
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_match_arms.rs929
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs (renamed from crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs)64
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_unsafe.rs101
-rw-r--r--crates/ide_diagnostics/src/handlers/no_such_field.rs (renamed from crates/ide/src/diagnostics/fixes/create_field.rs)165
-rw-r--r--crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs61
-rw-r--r--crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs179
-rw-r--r--crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs16
-rw-r--r--crates/ide_diagnostics/src/handlers/unlinked_file.rs298
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs49
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_import.rs90
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs84
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_module.rs (renamed from crates/ide/src/diagnostics/fixes/unresolved_module.rs)87
-rw-r--r--crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs27
-rw-r--r--crates/ide_diagnostics/src/handlers/useless_braces.rs148
-rw-r--r--crates/ide_diagnostics/src/lib.rs374
-rw-r--r--crates/ide_ssr/Cargo.toml2
-rw-r--r--crates/ide_ssr/src/matching.rs6
-rw-r--r--crates/ide_ssr/src/replacing.rs10
-rw-r--r--crates/ide_ssr/src/resolving.rs2
-rw-r--r--crates/ide_ssr/src/search.rs2
-rw-r--r--crates/ide_ssr/src/tests.rs4
-rw-r--r--crates/mbe/Cargo.toml2
-rw-r--r--crates/mbe/src/expander/matcher.rs22
-rw-r--r--crates/mbe/src/expander/transcriber.rs8
-rw-r--r--crates/mbe/src/lib.rs6
-rw-r--r--crates/mbe/src/parser.rs14
-rw-r--r--crates/mbe/src/subtree_source.rs6
-rw-r--r--crates/mbe/src/syntax_bridge.rs2
-rw-r--r--crates/mbe/src/tests/expand.rs2
-rw-r--r--crates/mbe/src/tt_iter.rs2
-rw-r--r--crates/parser/src/grammar/expressions.rs2
-rw-r--r--crates/paths/src/lib.rs2
-rw-r--r--crates/proc_macro_api/src/msg.rs2
-rw-r--r--crates/proc_macro_api/src/process.rs4
-rw-r--r--crates/proc_macro_api/src/version.rs2
-rw-r--r--crates/proc_macro_srv/src/lib.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/client.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/closure.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/handle.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/mod.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/server.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/diagnostic.rs11
-rw-r--r--crates/proc_macro_srv/src/proc_macro/mod.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs4
-rw-r--r--crates/profile/src/lib.rs6
-rw-r--r--crates/profile/src/memory_usage.rs35
-rw-r--r--crates/project_model/src/build_data.rs2
-rw-r--r--crates/project_model/src/cargo_workspace.rs2
-rw-r--r--crates/project_model/src/sysroot.rs4
-rw-r--r--crates/project_model/src/workspace.rs12
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs2
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs6
-rw-r--r--crates/rust-analyzer/src/dispatch.rs4
-rw-r--r--crates/rust-analyzer/src/global_state.rs10
-rw-r--r--crates/rust-analyzer/src/handlers.rs21
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs4
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs10
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs6
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs6
-rw-r--r--crates/stdx/src/panic_context.rs2
-rw-r--r--crates/stdx/src/process.rs4
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/edit.rs4
-rw-r--r--crates/syntax/src/ast/node_ext.rs9
-rw-r--r--crates/syntax/src/ast/token_ext.rs2
-rw-r--r--crates/syntax/src/parsing.rs4
-rw-r--r--crates/syntax/src/parsing/lexer.rs2
-rw-r--r--crates/syntax/src/parsing/reparsing.rs6
-rw-r--r--crates/syntax/src/tests.rs4
-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.rs192
-rw-r--r--crates/test_utils/src/lib.rs52
-rw-r--r--crates/test_utils/src/minicore.rs234
-rw-r--r--crates/tt/src/buffer.rs2
-rw-r--r--crates/tt/src/lib.rs4
-rw-r--r--crates/vfs/src/file_set.rs2
247 files changed, 8558 insertions, 8462 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 69ceba735..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);
@@ -190,7 +223,7 @@ impl From<Fixture> for FileMeta {
190 edition: f 223 edition: f
191 .edition 224 .edition
192 .as_ref() 225 .as_ref()
193 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), 226 .map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()),
194 env: f.env.into_iter().collect(), 227 env: f.env.into_iter().collect(),
195 introduce_new_source_root: f.introduce_new_source_root, 228 introduce_new_source_root: f.introduce_new_source_root,
196 } 229 }
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs
index 54baa3a63..d26f8f180 100644
--- a/crates/base_db/src/lib.rs
+++ b/crates/base_db/src/lib.rs
@@ -42,7 +42,7 @@ pub struct FilePosition {
42 pub offset: TextSize, 42 pub offset: TextSize,
43} 43}
44 44
45#[derive(Clone, Copy, Debug, Eq, PartialEq)] 45#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
46pub struct FileRange { 46pub struct FileRange {
47 pub file_id: FileId, 47 pub file_id: FileId,
48 pub range: TextRange, 48 pub range: TextRange,
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index 069fc01d0..8a1a51e6e 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -1,6 +1,6 @@
1//! The condition expression used in `#[cfg(..)]` attributes. 1//! The condition expression used in `#[cfg(..)]` attributes.
2//! 2//!
3//! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation 3//! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation>
4 4
5use std::{fmt, slice::Iter as SliceIter}; 5use std::{fmt, slice::Iter as SliceIter};
6 6
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 59fd38880..03b8dd767 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -22,7 +22,7 @@ pub use dnf::DnfExpr;
22/// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple 22/// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple
23/// of key and value in `key_values`. 23/// of key and value in `key_values`.
24/// 24///
25/// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options 25/// See: <https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options>
26#[derive(Debug, Clone, PartialEq, Eq, Default)] 26#[derive(Debug, Clone, PartialEq, Eq, Default)]
27pub struct CfgOptions { 27pub struct CfgOptions {
28 enabled: FxHashSet<CfgAtom>, 28 enabled: FxHashSet<CfgAtom>,
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 560b15238..7c148fd40 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -16,6 +16,7 @@ either = "1.5.3"
16arrayvec = "0.7" 16arrayvec = "0.7"
17itertools = "0.10.0" 17itertools = "0.10.0"
18smallvec = "1.4.0" 18smallvec = "1.4.0"
19once_cell = "1"
19 20
20stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
21syntax = { path = "../syntax", version = "0.0.0" } 22syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 2cdbd172a..b4c505898 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -3,251 +3,152 @@
3//! 3//!
4//! This probably isn't the best way to do this -- ideally, diagnistics should 4//! This probably isn't the best way to do this -- ideally, diagnistics should
5//! be expressed in terms of hir types themselves. 5//! be expressed in terms of hir types themselves.
6use std::any::Any; 6use cfg::{CfgExpr, CfgOptions};
7 7use either::Either;
8use cfg::{CfgExpr, CfgOptions, DnfExpr};
9use hir_def::path::ModPath; 8use hir_def::path::ModPath;
10use hir_expand::{HirFileId, InFile}; 9use hir_expand::{name::Name, HirFileId, InFile};
11use stdx::format_to;
12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; 10use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
13 11
14pub use hir_ty::{ 12macro_rules! diagnostics {
15 diagnostics::{ 13 ($($diag:ident,)*) => {
16 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, 14 pub enum AnyDiagnostic {$(
17 MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon, 15 $diag(Box<$diag>),
18 ReplaceFilterMapNextWithFindMap, 16 )*}
19 }, 17
20 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, 18 $(
21}; 19 impl From<$diag> for AnyDiagnostic {
22 20 fn from(d: $diag) -> AnyDiagnostic {
23// Diagnostic: unresolved-module 21 AnyDiagnostic::$diag(Box::new(d))
24// 22 }
25// This diagnostic is triggered if rust-analyzer is unable to discover referred module. 23 }
24 )*
25 };
26}
27
28diagnostics![
29 BreakOutsideOfLoop,
30 InactiveCode,
31 IncorrectCase,
32 MacroError,
33 MismatchedArgCount,
34 MissingFields,
35 MissingMatchArms,
36 MissingOkOrSomeInTailExpr,
37 MissingUnsafe,
38 NoSuchField,
39 RemoveThisSemicolon,
40 ReplaceFilterMapNextWithFindMap,
41 UnimplementedBuiltinMacro,
42 UnresolvedExternCrate,
43 UnresolvedImport,
44 UnresolvedMacroCall,
45 UnresolvedModule,
46 UnresolvedProcMacro,
47];
48
26#[derive(Debug)] 49#[derive(Debug)]
27pub struct UnresolvedModule { 50pub struct UnresolvedModule {
28 pub file: HirFileId, 51 pub decl: InFile<AstPtr<ast::Module>>,
29 pub decl: AstPtr<ast::Module>,
30 pub candidate: String, 52 pub candidate: String,
31} 53}
32 54
33impl Diagnostic for UnresolvedModule {
34 fn code(&self) -> DiagnosticCode {
35 DiagnosticCode("unresolved-module")
36 }
37 fn message(&self) -> String {
38 "unresolved module".to_string()
39 }
40 fn display_source(&self) -> InFile<SyntaxNodePtr> {
41 InFile::new(self.file, self.decl.clone().into())
42 }
43 fn as_any(&self) -> &(dyn Any + Send + 'static) {
44 self
45 }
46}
47
48// Diagnostic: unresolved-extern-crate
49//
50// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
51#[derive(Debug)] 55#[derive(Debug)]
52pub struct UnresolvedExternCrate { 56pub struct UnresolvedExternCrate {
53 pub file: HirFileId, 57 pub decl: InFile<AstPtr<ast::ExternCrate>>,
54 pub item: AstPtr<ast::ExternCrate>,
55}
56
57impl Diagnostic for UnresolvedExternCrate {
58 fn code(&self) -> DiagnosticCode {
59 DiagnosticCode("unresolved-extern-crate")
60 }
61 fn message(&self) -> String {
62 "unresolved extern crate".to_string()
63 }
64 fn display_source(&self) -> InFile<SyntaxNodePtr> {
65 InFile::new(self.file, self.item.clone().into())
66 }
67 fn as_any(&self) -> &(dyn Any + Send + 'static) {
68 self
69 }
70} 58}
71 59
72#[derive(Debug)] 60#[derive(Debug)]
73pub struct UnresolvedImport { 61pub struct UnresolvedImport {
74 pub file: HirFileId, 62 pub decl: InFile<AstPtr<ast::UseTree>>,
75 pub node: AstPtr<ast::UseTree>, 63}
76} 64
77
78impl Diagnostic for UnresolvedImport {
79 fn code(&self) -> DiagnosticCode {
80 DiagnosticCode("unresolved-import")
81 }
82 fn message(&self) -> String {
83 "unresolved import".to_string()
84 }
85 fn display_source(&self) -> InFile<SyntaxNodePtr> {
86 InFile::new(self.file, self.node.clone().into())
87 }
88 fn as_any(&self) -> &(dyn Any + Send + 'static) {
89 self
90 }
91 fn is_experimental(&self) -> bool {
92 // This currently results in false positives in the following cases:
93 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
94 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
95 // - proc macros and/or proc macro generated code
96 true
97 }
98}
99
100// Diagnostic: unresolved-macro-call
101//
102// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
103// macro in a macro invocation.
104#[derive(Debug, Clone, Eq, PartialEq)] 65#[derive(Debug, Clone, Eq, PartialEq)]
105pub struct UnresolvedMacroCall { 66pub struct UnresolvedMacroCall {
106 pub file: HirFileId, 67 pub macro_call: InFile<AstPtr<ast::MacroCall>>,
107 pub node: AstPtr<ast::MacroCall>,
108 pub path: ModPath, 68 pub path: ModPath,
109} 69}
110 70
111impl Diagnostic for UnresolvedMacroCall {
112 fn code(&self) -> DiagnosticCode {
113 DiagnosticCode("unresolved-macro-call")
114 }
115 fn message(&self) -> String {
116 format!("unresolved macro `{}!`", self.path)
117 }
118 fn display_source(&self) -> InFile<SyntaxNodePtr> {
119 InFile::new(self.file, self.node.clone().into())
120 }
121 fn as_any(&self) -> &(dyn Any + Send + 'static) {
122 self
123 }
124 fn is_experimental(&self) -> bool {
125 true
126 }
127}
128
129// Diagnostic: inactive-code
130//
131// This diagnostic is shown for code with inactive `#[cfg]` attributes.
132#[derive(Debug, Clone, Eq, PartialEq)] 71#[derive(Debug, Clone, Eq, PartialEq)]
133pub struct InactiveCode { 72pub struct InactiveCode {
134 pub file: HirFileId, 73 pub node: InFile<SyntaxNodePtr>,
135 pub node: SyntaxNodePtr,
136 pub cfg: CfgExpr, 74 pub cfg: CfgExpr,
137 pub opts: CfgOptions, 75 pub opts: CfgOptions,
138} 76}
139 77
140impl Diagnostic for InactiveCode {
141 fn code(&self) -> DiagnosticCode {
142 DiagnosticCode("inactive-code")
143 }
144 fn message(&self) -> String {
145 let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
146 let mut buf = "code is inactive due to #[cfg] directives".to_string();
147
148 if let Some(inactive) = inactive {
149 format_to!(buf, ": {}", inactive);
150 }
151
152 buf
153 }
154 fn display_source(&self) -> InFile<SyntaxNodePtr> {
155 InFile::new(self.file, self.node.clone())
156 }
157 fn as_any(&self) -> &(dyn Any + Send + 'static) {
158 self
159 }
160}
161
162// Diagnostic: unresolved-proc-macro
163//
164// This diagnostic is shown when a procedural macro can not be found. This usually means that
165// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
166// but can also indicate project setup problems.
167//
168// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
169// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
170// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
171#[derive(Debug, Clone, Eq, PartialEq)] 78#[derive(Debug, Clone, Eq, PartialEq)]
172pub struct UnresolvedProcMacro { 79pub struct UnresolvedProcMacro {
173 pub file: HirFileId, 80 pub node: InFile<SyntaxNodePtr>,
174 pub node: SyntaxNodePtr,
175 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` 81 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
176 /// to use instead. 82 /// to use instead.
177 pub precise_location: Option<TextRange>, 83 pub precise_location: Option<TextRange>,
178 pub macro_name: Option<String>, 84 pub macro_name: Option<String>,
179} 85}
180 86
181impl Diagnostic for UnresolvedProcMacro { 87#[derive(Debug, Clone, Eq, PartialEq)]
182 fn code(&self) -> DiagnosticCode { 88pub struct MacroError {
183 DiagnosticCode("unresolved-proc-macro") 89 pub node: InFile<SyntaxNodePtr>,
184 } 90 pub message: String,
91}
185 92
186 fn message(&self) -> String { 93#[derive(Debug)]
187 match &self.macro_name { 94pub struct UnimplementedBuiltinMacro {
188 Some(name) => format!("proc macro `{}` not expanded", name), 95 pub node: InFile<SyntaxNodePtr>,
189 None => "proc macro not expanded".to_string(), 96}
190 }
191 }
192 97
193 fn display_source(&self) -> InFile<SyntaxNodePtr> { 98#[derive(Debug)]
194 InFile::new(self.file, self.node.clone()) 99pub struct NoSuchField {
195 } 100 pub field: InFile<AstPtr<ast::RecordExprField>>,
101}
196 102
197 fn as_any(&self) -> &(dyn Any + Send + 'static) { 103#[derive(Debug)]
198 self 104pub struct BreakOutsideOfLoop {
199 } 105 pub expr: InFile<AstPtr<ast::Expr>>,
200} 106}
201 107
202// Diagnostic: macro-error 108#[derive(Debug)]
203// 109pub struct MissingUnsafe {
204// This diagnostic is shown for macro expansion errors. 110 pub expr: InFile<AstPtr<ast::Expr>>,
205#[derive(Debug, Clone, Eq, PartialEq)]
206pub struct MacroError {
207 pub file: HirFileId,
208 pub node: SyntaxNodePtr,
209 pub message: String,
210} 111}
211 112
212impl Diagnostic for MacroError { 113#[derive(Debug)]
213 fn code(&self) -> DiagnosticCode { 114pub struct MissingFields {
214 DiagnosticCode("macro-error") 115 pub file: HirFileId,
215 } 116 pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>,
216 fn message(&self) -> String { 117 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
217 self.message.clone() 118 pub missed_fields: Vec<Name>,
218 }
219 fn display_source(&self) -> InFile<SyntaxNodePtr> {
220 InFile::new(self.file, self.node.clone())
221 }
222 fn as_any(&self) -> &(dyn Any + Send + 'static) {
223 self
224 }
225 fn is_experimental(&self) -> bool {
226 // Newly added and not very well-tested, might contain false positives.
227 true
228 }
229} 119}
230 120
231#[derive(Debug)] 121#[derive(Debug)]
232pub struct UnimplementedBuiltinMacro { 122pub struct ReplaceFilterMapNextWithFindMap {
233 pub file: HirFileId, 123 pub file: HirFileId,
234 pub node: SyntaxNodePtr, 124 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
125 pub next_expr: AstPtr<ast::Expr>,
235} 126}
236 127
237impl Diagnostic for UnimplementedBuiltinMacro { 128#[derive(Debug)]
238 fn code(&self) -> DiagnosticCode { 129pub struct MismatchedArgCount {
239 DiagnosticCode("unimplemented-builtin-macro") 130 pub call_expr: InFile<AstPtr<ast::Expr>>,
240 } 131 pub expected: usize,
132 pub found: usize,
133}
241 134
242 fn message(&self) -> String { 135#[derive(Debug)]
243 "unimplemented built-in macro".to_string() 136pub struct RemoveThisSemicolon {
244 } 137 pub expr: InFile<AstPtr<ast::Expr>>,
138}
245 139
246 fn display_source(&self) -> InFile<SyntaxNodePtr> { 140#[derive(Debug)]
247 InFile::new(self.file, self.node.clone()) 141pub struct MissingOkOrSomeInTailExpr {
248 } 142 pub expr: InFile<AstPtr<ast::Expr>>,
143 // `Some` or `Ok` depending on whether the return type is Result or Option
144 pub required: String,
145}
249 146
250 fn as_any(&self) -> &(dyn Any + Send + 'static) { 147#[derive(Debug)]
251 self 148pub struct MissingMatchArms {
252 } 149 pub file: HirFileId,
150 pub match_expr: AstPtr<ast::Expr>,
151 pub arms: AstPtr<ast::MatchArmList>,
253} 152}
153
154pub use hir_ty::diagnostics::IncorrectCase;
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/src/lib.rs b/crates/hir/src/lib.rs
index 0bb3767c1..b7eabaabb 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -15,7 +15,7 @@
15//! 15//!
16//! `hir` is what insulates the "we don't know how to actually write an incremental compiler" 16//! `hir` is what insulates the "we don't know how to actually write an incremental compiler"
17//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary: 17//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary:
18//! https://www.tedinski.com/2018/02/06/system-boundaries.html. 18//! <https://www.tedinski.com/2018/02/06/system-boundaries.html>.
19 19
20#![recursion_limit = "512"] 20#![recursion_limit = "512"]
21 21
@@ -35,14 +35,10 @@ use std::{iter, sync::Arc};
35 35
36use arrayvec::ArrayVec; 36use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 37use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{
39 InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
40 UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
41};
42use either::Either; 38use either::Either;
43use hir_def::{ 39use hir_def::{
44 adt::{ReprKind, VariantData}, 40 adt::{ReprKind, VariantData},
45 body::BodyDiagnostic, 41 body::{BodyDiagnostic, SyntheticSyntax},
46 expr::{BindingAnnotation, LabelId, Pat, PatId}, 42 expr::{BindingAnnotation, LabelId, Pat, PatId},
47 item_tree::ItemTreeNode, 43 item_tree::ItemTreeNode,
48 lang_item::LangItemTarget, 44 lang_item::LangItemTarget,
@@ -60,8 +56,8 @@ use hir_ty::{
60 autoderef, 56 autoderef,
61 consteval::ConstExt, 57 consteval::ConstExt,
62 could_unify, 58 could_unify,
63 diagnostics_sink::DiagnosticSink, 59 diagnostics::BodyValidationDiagnostic,
64 method_resolution::{self, def_crates, TyFingerprint}, 60 method_resolution::{self, TyFingerprint},
65 primitive::UintTy, 61 primitive::UintTy,
66 subst_prefix, 62 subst_prefix,
67 traits::FnTrait, 63 traits::FnTrait,
@@ -72,6 +68,7 @@ use hir_ty::{
72}; 68};
73use itertools::Itertools; 69use itertools::Itertools;
74use nameres::diagnostics::DefDiagnosticKind; 70use nameres::diagnostics::DefDiagnosticKind;
71use once_cell::unsync::Lazy;
75use rustc_hash::FxHashSet; 72use rustc_hash::FxHashSet;
76use stdx::{format_to, impl_from}; 73use stdx::{format_to, impl_from};
77use syntax::{ 74use syntax::{
@@ -84,6 +81,13 @@ use crate::db::{DefDatabase, HirDatabase};
84 81
85pub use crate::{ 82pub use crate::{
86 attrs::{HasAttrs, Namespace}, 83 attrs::{HasAttrs, Namespace},
84 diagnostics::{
85 AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, MacroError,
86 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
87 MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
88 UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
89 UnresolvedModule, UnresolvedProcMacro,
90 },
87 has_source::HasSource, 91 has_source::HasSource,
88 semantics::{PathResolution, Semantics, SemanticsScope}, 92 semantics::{PathResolution, Semantics, SemanticsScope},
89}; 93};
@@ -332,7 +336,7 @@ impl ModuleDef {
332 } 336 }
333 } 337 }
334 338
335 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 339 pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec<AnyDiagnostic> {
336 let id = match self { 340 let id = match self {
337 ModuleDef::Adt(it) => match it { 341 ModuleDef::Adt(it) => match it {
338 Adt::Struct(it) => it.id.into(), 342 Adt::Struct(it) => it.id.into(),
@@ -345,15 +349,19 @@ impl ModuleDef {
345 ModuleDef::Module(it) => it.id.into(), 349 ModuleDef::Module(it) => it.id.into(),
346 ModuleDef::Const(it) => it.id.into(), 350 ModuleDef::Const(it) => it.id.into(),
347 ModuleDef::Static(it) => it.id.into(), 351 ModuleDef::Static(it) => it.id.into(),
348 _ => return, 352 _ => return Vec::new(),
349 }; 353 };
350 354
351 let module = match self.module(db) { 355 let module = match self.module(db) {
352 Some(it) => it, 356 Some(it) => it,
353 None => return, 357 None => return Vec::new(),
354 }; 358 };
355 359
356 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) 360 let mut acc = Vec::new();
361 for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) {
362 acc.push(diag.into())
363 }
364 acc
357 } 365 }
358} 366}
359 367
@@ -442,10 +450,10 @@ impl Module {
442 } 450 }
443 451
444 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { 452 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
445 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) 453 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of((*def).into())
446 } 454 }
447 455
448 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 456 pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
449 let _p = profile::span("Module::diagnostics").detail(|| { 457 let _p = profile::span("Module::diagnostics").detail(|| {
450 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) 458 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
451 }); 459 });
@@ -458,18 +466,22 @@ impl Module {
458 match &diag.kind { 466 match &diag.kind {
459 DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => { 467 DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => {
460 let decl = declaration.to_node(db.upcast()); 468 let decl = declaration.to_node(db.upcast());
461 sink.push(UnresolvedModule { 469 acc.push(
462 file: declaration.file_id, 470 UnresolvedModule {
463 decl: AstPtr::new(&decl), 471 decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
464 candidate: candidate.clone(), 472 candidate: candidate.clone(),
465 }) 473 }
474 .into(),
475 )
466 } 476 }
467 DefDiagnosticKind::UnresolvedExternCrate { ast } => { 477 DefDiagnosticKind::UnresolvedExternCrate { ast } => {
468 let item = ast.to_node(db.upcast()); 478 let item = ast.to_node(db.upcast());
469 sink.push(UnresolvedExternCrate { 479 acc.push(
470 file: ast.file_id, 480 UnresolvedExternCrate {
471 item: AstPtr::new(&item), 481 decl: InFile::new(ast.file_id, AstPtr::new(&item)),
472 }); 482 }
483 .into(),
484 );
473 } 485 }
474 486
475 DefDiagnosticKind::UnresolvedImport { id, index } => { 487 DefDiagnosticKind::UnresolvedImport { id, index } => {
@@ -478,25 +490,30 @@ impl Module {
478 let import = &item_tree[id.value]; 490 let import = &item_tree[id.value];
479 491
480 let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index); 492 let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
481 sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) }); 493 acc.push(
494 UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }
495 .into(),
496 );
482 } 497 }
483 498
484 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { 499 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
485 let item = ast.to_node(db.upcast()); 500 let item = ast.to_node(db.upcast());
486 sink.push(InactiveCode { 501 acc.push(
487 file: ast.file_id, 502 InactiveCode {
488 node: AstPtr::new(&item).into(), 503 node: ast.with_value(AstPtr::new(&item).into()),
489 cfg: cfg.clone(), 504 cfg: cfg.clone(),
490 opts: opts.clone(), 505 opts: opts.clone(),
491 }); 506 }
507 .into(),
508 );
492 } 509 }
493 510
494 DefDiagnosticKind::UnresolvedProcMacro { ast } => { 511 DefDiagnosticKind::UnresolvedProcMacro { ast } => {
495 let mut precise_location = None; 512 let mut precise_location = None;
496 let (file, ast, name) = match ast { 513 let (node, name) = match ast {
497 MacroCallKind::FnLike { ast_id, .. } => { 514 MacroCallKind::FnLike { ast_id, .. } => {
498 let node = ast_id.to_node(db.upcast()); 515 let node = ast_id.to_node(db.upcast());
499 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) 516 (ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), None)
500 } 517 }
501 MacroCallKind::Derive { ast_id, derive_name, .. } => { 518 MacroCallKind::Derive { ast_id, derive_name, .. } => {
502 let node = ast_id.to_node(db.upcast()); 519 let node = ast_id.to_node(db.upcast());
@@ -529,8 +546,7 @@ impl Module {
529 } 546 }
530 547
531 ( 548 (
532 ast_id.file_id, 549 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
533 SyntaxNodePtr::from(AstPtr::new(&node)),
534 Some(derive_name.clone()), 550 Some(derive_name.clone()),
535 ) 551 )
536 } 552 }
@@ -541,73 +557,73 @@ impl Module {
541 || panic!("cannot find attribute #{}", invoc_attr_index), 557 || panic!("cannot find attribute #{}", invoc_attr_index),
542 ); 558 );
543 ( 559 (
544 ast_id.file_id, 560 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
545 SyntaxNodePtr::from(AstPtr::new(&attr)),
546 Some(attr_name.clone()), 561 Some(attr_name.clone()),
547 ) 562 )
548 } 563 }
549 }; 564 };
550 sink.push(UnresolvedProcMacro { 565 acc.push(
551 file, 566 UnresolvedProcMacro { node, precise_location, macro_name: name }.into(),
552 node: ast, 567 );
553 precise_location,
554 macro_name: name,
555 });
556 } 568 }
557 569
558 DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { 570 DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
559 let node = ast.to_node(db.upcast()); 571 let node = ast.to_node(db.upcast());
560 sink.push(UnresolvedMacroCall { 572 acc.push(
561 file: ast.file_id, 573 UnresolvedMacroCall {
562 node: AstPtr::new(&node), 574 macro_call: InFile::new(ast.file_id, AstPtr::new(&node)),
563 path: path.clone(), 575 path: path.clone(),
564 }); 576 }
577 .into(),
578 );
565 } 579 }
566 580
567 DefDiagnosticKind::MacroError { ast, message } => { 581 DefDiagnosticKind::MacroError { ast, message } => {
568 let (file, ast) = match ast { 582 let node = match ast {
569 MacroCallKind::FnLike { ast_id, .. } => { 583 MacroCallKind::FnLike { ast_id, .. } => {
570 let node = ast_id.to_node(db.upcast()); 584 let node = ast_id.to_node(db.upcast());
571 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) 585 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node)))
572 } 586 }
573 MacroCallKind::Derive { ast_id, .. } 587 MacroCallKind::Derive { ast_id, .. }
574 | MacroCallKind::Attr { ast_id, .. } => { 588 | MacroCallKind::Attr { ast_id, .. } => {
575 // FIXME: point to the attribute instead, this creates very large diagnostics 589 // FIXME: point to the attribute instead, this creates very large diagnostics
576 let node = ast_id.to_node(db.upcast()); 590 let node = ast_id.to_node(db.upcast());
577 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) 591 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node)))
578 } 592 }
579 }; 593 };
580 sink.push(MacroError { file, node: ast, message: message.clone() }); 594 acc.push(MacroError { node, message: message.clone() }.into());
581 } 595 }
582 596
583 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { 597 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
584 let node = ast.to_node(db.upcast()); 598 let node = ast.to_node(db.upcast());
585 // Must have a name, otherwise we wouldn't emit it. 599 // Must have a name, otherwise we wouldn't emit it.
586 let name = node.name().expect("unimplemented builtin macro with no name"); 600 let name = node.name().expect("unimplemented builtin macro with no name");
587 let ptr = SyntaxNodePtr::from(AstPtr::new(&name)); 601 acc.push(
588 sink.push(UnimplementedBuiltinMacro { file: ast.file_id, node: ptr }); 602 UnimplementedBuiltinMacro {
603 node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))),
604 }
605 .into(),
606 );
589 } 607 }
590 } 608 }
591 } 609 }
592 for decl in self.declarations(db) { 610 for decl in self.declarations(db) {
593 match decl { 611 match decl {
594 crate::ModuleDef::Function(f) => f.diagnostics(db, sink), 612 ModuleDef::Function(f) => f.diagnostics(db, acc),
595 crate::ModuleDef::Module(m) => { 613 ModuleDef::Module(m) => {
596 // Only add diagnostics from inline modules 614 // Only add diagnostics from inline modules
597 if def_map[m.id.local_id].origin.is_inline() { 615 if def_map[m.id.local_id].origin.is_inline() {
598 m.diagnostics(db, sink) 616 m.diagnostics(db, acc)
599 } 617 }
600 } 618 }
601 _ => { 619 _ => acc.extend(decl.diagnostics(db)),
602 decl.diagnostics(db, sink);
603 }
604 } 620 }
605 } 621 }
606 622
607 for impl_def in self.impl_defs(db) { 623 for impl_def in self.impl_defs(db) {
608 for item in impl_def.items(db) { 624 for item in impl_def.items(db) {
609 if let AssocItem::Function(f) = item { 625 if let AssocItem::Function(f) = item {
610 f.diagnostics(db, sink); 626 f.diagnostics(db, acc);
611 } 627 }
612 } 628 }
613 } 629 }
@@ -1009,41 +1025,191 @@ impl Function {
1009 db.function_data(self.id).is_async() 1025 db.function_data(self.id).is_async()
1010 } 1026 }
1011 1027
1012 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 1028 pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
1013 let krate = self.module(db).id.krate(); 1029 let krate = self.module(db).id.krate();
1014 1030
1015 let source_map = db.body_with_source_map(self.id.into()).1; 1031 let source_map = db.body_with_source_map(self.id.into()).1;
1016 for diag in source_map.diagnostics() { 1032 for diag in source_map.diagnostics() {
1017 match diag { 1033 match diag {
1018 BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode { 1034 BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push(
1019 file: node.file_id, 1035 InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
1020 node: node.value.clone(), 1036 .into(),
1021 cfg: cfg.clone(), 1037 ),
1022 opts: opts.clone(), 1038 BodyDiagnostic::MacroError { node, message } => acc.push(
1023 }), 1039 MacroError {
1024 BodyDiagnostic::MacroError { node, message } => sink.push(MacroError { 1040 node: node.clone().map(|it| it.into()),
1025 file: node.file_id, 1041 message: message.to_string(),
1026 node: node.value.clone().into(), 1042 }
1027 message: message.to_string(), 1043 .into(),
1028 }), 1044 ),
1029 BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro { 1045 BodyDiagnostic::UnresolvedProcMacro { node } => acc.push(
1030 file: node.file_id, 1046 UnresolvedProcMacro {
1031 node: node.value.clone().into(), 1047 node: node.clone().map(|it| it.into()),
1032 precise_location: None, 1048 precise_location: None,
1033 macro_name: None, 1049 macro_name: None,
1034 }), 1050 }
1035 BodyDiagnostic::UnresolvedMacroCall { node, path } => { 1051 .into(),
1036 sink.push(UnresolvedMacroCall { 1052 ),
1037 file: node.file_id, 1053 BodyDiagnostic::UnresolvedMacroCall { node, path } => acc.push(
1038 node: node.value.clone(), 1054 UnresolvedMacroCall { macro_call: node.clone(), path: path.clone() }.into(),
1039 path: path.clone(), 1055 ),
1040 }) 1056 }
1057 }
1058
1059 let infer = db.infer(self.id.into());
1060 let source_map = Lazy::new(|| db.body_with_source_map(self.id.into()).1);
1061 for d in &infer.diagnostics {
1062 match d {
1063 hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
1064 let field = source_map.field_syntax(*expr);
1065 acc.push(NoSuchField { field }.into())
1066 }
1067 hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
1068 let expr = source_map
1069 .expr_syntax(*expr)
1070 .expect("break outside of loop in synthetic syntax");
1071 acc.push(BreakOutsideOfLoop { expr }.into())
1041 } 1072 }
1042 } 1073 }
1043 } 1074 }
1044 1075
1045 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); 1076 for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) {
1046 hir_ty::diagnostics::validate_body(db, self.id.into(), sink); 1077 match source_map.expr_syntax(expr) {
1078 Ok(expr) => acc.push(MissingUnsafe { expr }.into()),
1079 Err(SyntheticSyntax) => {
1080 // FIXME: Here and eslwhere in this file, the `expr` was
1081 // desugared, report or assert that this doesn't happen.
1082 }
1083 }
1084 }
1085
1086 for diagnostic in BodyValidationDiagnostic::collect(db, self.id.into()) {
1087 match diagnostic {
1088 BodyValidationDiagnostic::RecordMissingFields {
1089 record,
1090 variant,
1091 missed_fields,
1092 } => {
1093 let variant_data = variant.variant_data(db.upcast());
1094 let missed_fields = missed_fields
1095 .into_iter()
1096 .map(|idx| variant_data.fields()[idx].name.clone())
1097 .collect();
1098
1099 match record {
1100 Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
1101 Ok(source_ptr) => {
1102 let root = source_ptr.file_syntax(db.upcast());
1103 if let ast::Expr::RecordExpr(record_expr) =
1104 &source_ptr.value.to_node(&root)
1105 {
1106 if let Some(_) = record_expr.record_expr_field_list() {
1107 acc.push(
1108 MissingFields {
1109 file: source_ptr.file_id,
1110 field_list_parent: Either::Left(AstPtr::new(
1111 record_expr,
1112 )),
1113 field_list_parent_path: record_expr
1114 .path()
1115 .map(|path| AstPtr::new(&path)),
1116 missed_fields,
1117 }
1118 .into(),
1119 )
1120 }
1121 }
1122 }
1123 Err(SyntheticSyntax) => (),
1124 },
1125 Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
1126 Ok(source_ptr) => {
1127 if let Some(expr) = source_ptr.value.as_ref().left() {
1128 let root = source_ptr.file_syntax(db.upcast());
1129 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
1130 if let Some(_) = record_pat.record_pat_field_list() {
1131 acc.push(
1132 MissingFields {
1133 file: source_ptr.file_id,
1134 field_list_parent: Either::Right(AstPtr::new(
1135 &record_pat,
1136 )),
1137 field_list_parent_path: record_pat
1138 .path()
1139 .map(|path| AstPtr::new(&path)),
1140 missed_fields,
1141 }
1142 .into(),
1143 )
1144 }
1145 }
1146 }
1147 }
1148 Err(SyntheticSyntax) => (),
1149 },
1150 }
1151 }
1152 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
1153 if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
1154 acc.push(
1155 ReplaceFilterMapNextWithFindMap {
1156 file: next_source_ptr.file_id,
1157 next_expr: next_source_ptr.value,
1158 }
1159 .into(),
1160 );
1161 }
1162 }
1163 BodyValidationDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
1164 match source_map.expr_syntax(call_expr) {
1165 Ok(source_ptr) => acc.push(
1166 MismatchedArgCount { call_expr: source_ptr, expected, found }.into(),
1167 ),
1168 Err(SyntheticSyntax) => (),
1169 }
1170 }
1171 BodyValidationDiagnostic::RemoveThisSemicolon { expr } => {
1172 match source_map.expr_syntax(expr) {
1173 Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()),
1174 Err(SyntheticSyntax) => (),
1175 }
1176 }
1177 BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => {
1178 match source_map.expr_syntax(expr) {
1179 Ok(expr) => acc.push(MissingOkOrSomeInTailExpr { expr, required }.into()),
1180 Err(SyntheticSyntax) => (),
1181 }
1182 }
1183 BodyValidationDiagnostic::MissingMatchArms { match_expr } => {
1184 match source_map.expr_syntax(match_expr) {
1185 Ok(source_ptr) => {
1186 let root = source_ptr.file_syntax(db.upcast());
1187 if let ast::Expr::MatchExpr(match_expr) =
1188 &source_ptr.value.to_node(&root)
1189 {
1190 if let (Some(match_expr), Some(arms)) =
1191 (match_expr.expr(), match_expr.match_arm_list())
1192 {
1193 acc.push(
1194 MissingMatchArms {
1195 file: source_ptr.file_id,
1196 match_expr: AstPtr::new(&match_expr),
1197 arms: AstPtr::new(&arms),
1198 }
1199 .into(),
1200 )
1201 }
1202 }
1203 }
1204 Err(SyntheticSyntax) => (),
1205 }
1206 }
1207 }
1208 }
1209
1210 for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
1211 acc.push(diag.into())
1212 }
1047 } 1213 }
1048 1214
1049 /// Whether this function declaration has a definition. 1215 /// Whether this function declaration has a definition.
@@ -1762,7 +1928,7 @@ impl Impl {
1762 } 1928 }
1763 1929
1764 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { 1930 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> {
1765 let def_crates = match def_crates(db, &ty, krate) { 1931 let def_crates = match method_resolution::def_crates(db, &ty, krate) {
1766 Some(def_crates) => def_crates, 1932 Some(def_crates) => def_crates,
1767 None => return Vec::new(), 1933 None => return Vec::new(),
1768 }; 1934 };
@@ -2168,7 +2334,7 @@ impl Type {
2168 krate: Crate, 2334 krate: Crate,
2169 mut callback: impl FnMut(AssocItem) -> Option<T>, 2335 mut callback: impl FnMut(AssocItem) -> Option<T>,
2170 ) -> Option<T> { 2336 ) -> Option<T> {
2171 for krate in def_crates(db, &self.ty, krate.id)? { 2337 for krate in method_resolution::def_crates(db, &self.ty, krate.id)? {
2172 let impls = db.inherent_impls_in_crate(krate); 2338 let impls = db.inherent_impls_in_crate(krate);
2173 2339
2174 for impl_def in impls.for_self_ty(&self.ty) { 2340 for impl_def in impls.for_self_ty(&self.ty) {
@@ -2345,13 +2511,13 @@ impl Type {
2345 match ty.kind(&Interner) { 2511 match ty.kind(&Interner) {
2346 TyKind::Adt(_, substs) => { 2512 TyKind::Adt(_, substs) => {
2347 cb(type_.derived(ty.clone())); 2513 cb(type_.derived(ty.clone()));
2348 walk_substs(db, type_, &substs, cb); 2514 walk_substs(db, type_, substs, cb);
2349 } 2515 }
2350 TyKind::AssociatedType(_, substs) => { 2516 TyKind::AssociatedType(_, substs) => {
2351 if let Some(_) = ty.associated_type_parent_trait(db) { 2517 if let Some(_) = ty.associated_type_parent_trait(db) {
2352 cb(type_.derived(ty.clone())); 2518 cb(type_.derived(ty.clone()));
2353 } 2519 }
2354 walk_substs(db, type_, &substs, cb); 2520 walk_substs(db, type_, substs, cb);
2355 } 2521 }
2356 TyKind::OpaqueType(_, subst) => { 2522 TyKind::OpaqueType(_, subst) => {
2357 if let Some(bounds) = ty.impl_trait_bounds(db) { 2523 if let Some(bounds) = ty.impl_trait_bounds(db) {
@@ -2391,7 +2557,7 @@ impl Type {
2391 TyKind::FnDef(_, substs) 2557 TyKind::FnDef(_, substs)
2392 | TyKind::Tuple(_, substs) 2558 | TyKind::Tuple(_, substs)
2393 | TyKind::Closure(.., substs) => { 2559 | TyKind::Closure(.., substs) => {
2394 walk_substs(db, type_, &substs, cb); 2560 walk_substs(db, type_, substs, cb);
2395 } 2561 }
2396 TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { 2562 TyKind::Function(hir_ty::FnPointer { substitution, .. }) => {
2397 walk_substs(db, type_, &substitution.0, cb); 2563 walk_substs(db, type_, &substitution.0, cb);
@@ -2522,18 +2688,6 @@ impl ScopeDef {
2522 2688
2523 items 2689 items
2524 } 2690 }
2525
2526 pub fn is_value_def(&self) -> bool {
2527 matches!(
2528 self,
2529 ScopeDef::ModuleDef(ModuleDef::Function(_))
2530 | ScopeDef::ModuleDef(ModuleDef::Variant(_))
2531 | ScopeDef::ModuleDef(ModuleDef::Const(_))
2532 | ScopeDef::ModuleDef(ModuleDef::Static(_))
2533 | ScopeDef::GenericParam(GenericParam::ConstParam(_))
2534 | ScopeDef::Local(_)
2535 )
2536 }
2537} 2691}
2538 2692
2539impl From<ItemInNs> for ScopeDef { 2693impl From<ItemInNs> for ScopeDef {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d522d5245..613266e07 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -192,7 +192,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
192 node: &SyntaxNode, 192 node: &SyntaxNode,
193 offset: TextSize, 193 offset: TextSize,
194 ) -> Option<N> { 194 ) -> Option<N> {
195 if let Some(it) = find_node_at_offset(&node, offset) { 195 if let Some(it) = find_node_at_offset(node, offset) {
196 return Some(it); 196 return Some(it);
197 } 197 }
198 198
@@ -744,7 +744,7 @@ impl<'db> SemanticsImpl<'db> {
744 return None; 744 return None;
745 } 745 }
746 746
747 let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; 747 let func = self.resolve_method_call(method_call_expr).map(Function::from)?;
748 let res = match func.self_param(self.db)?.access(self.db) { 748 let res = match func.self_param(self.db)?.access(self.db) {
749 Access::Shared | Access::Exclusive => true, 749 Access::Shared | Access::Exclusive => true,
750 Access::Owned => false, 750 Access::Owned => false,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 37a050415..c9744d81d 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -222,7 +222,7 @@ impl SourceAnalyzer {
222 Pat::Path(path) => path, 222 Pat::Path(path) => path,
223 _ => return None, 223 _ => return None,
224 }; 224 };
225 let res = resolve_hir_path(db, &self.resolver, &path)?; 225 let res = resolve_hir_path(db, &self.resolver, path)?;
226 match res { 226 match res {
227 PathResolution::Def(def) => Some(def), 227 PathResolution::Def(def) => Some(def),
228 _ => None, 228 _ => None,
@@ -329,7 +329,7 @@ impl SourceAnalyzer {
329 329
330 let (variant, missing_fields, _exhaustive) = 330 let (variant, missing_fields, _exhaustive) =
331 record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; 331 record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
332 let res = self.missing_fields(db, krate, &substs, variant, missing_fields); 332 let res = self.missing_fields(db, krate, substs, variant, missing_fields);
333 Some(res) 333 Some(res)
334 } 334 }
335 335
@@ -347,7 +347,7 @@ impl SourceAnalyzer {
347 347
348 let (variant, missing_fields, _exhaustive) = 348 let (variant, missing_fields, _exhaustive) =
349 record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; 349 record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
350 let res = self.missing_fields(db, krate, &substs, variant, missing_fields); 350 let res = self.missing_fields(db, krate, substs, variant, missing_fields);
351 Some(res) 351 Some(res)
352 } 352 }
353 353
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 43324d8d9..bb86f6a73 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14dashmap = { version = "4.0.2", features = ["raw-api"] } 14dashmap = { version = "4.0.2", features = ["raw-api"] }
15log = "0.4.8" 15log = "0.4.8"
16once_cell = "1.3.1" 16once_cell = "1.3.1"
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index d9f9fadc1..d07adb084 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -583,13 +583,13 @@ impl AttrSourceMap {
583 .get(id.ast_index as usize) 583 .get(id.ast_index as usize)
584 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id)) 584 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
585 .clone() 585 .clone()
586 .map(|attr| Either::Right(attr)) 586 .map(Either::Right)
587 } else { 587 } else {
588 self.attrs 588 self.attrs
589 .get(id.ast_index as usize) 589 .get(id.ast_index as usize)
590 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id)) 590 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
591 .clone() 591 .clone()
592 .map(|attr| Either::Left(attr)) 592 .map(Either::Left)
593 } 593 }
594 } 594 }
595} 595}
@@ -606,7 +606,7 @@ pub struct DocsRangeMap {
606impl DocsRangeMap { 606impl DocsRangeMap {
607 pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { 607 pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
608 let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; 608 let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
609 let (line_docs_range, idx, original_line_src_range) = self.mapping[found].clone(); 609 let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
610 if !line_docs_range.contains_range(range) { 610 if !line_docs_range.contains_range(range) {
611 return None; 611 return None;
612 } 612 }
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index da1fdac33..bed4c4994 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1000,18 +1000,18 @@ impl From<ast::LiteralKind> for Literal {
1000 // FIXME: these should have actual values filled in, but unsure on perf impact 1000 // FIXME: these should have actual values filled in, but unsure on perf impact
1001 LiteralKind::IntNumber(lit) => { 1001 LiteralKind::IntNumber(lit) => {
1002 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { 1002 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
1003 return Literal::Float(Default::default(), builtin); 1003 Literal::Float(Default::default(), builtin)
1004 } else if let builtin @ Some(_) = 1004 } else if let builtin @ Some(_) =
1005 lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) 1005 lit.suffix().and_then(|it| BuiltinInt::from_suffix(it))
1006 { 1006 {
1007 Literal::Int(lit.value().unwrap_or(0) as i128, builtin) 1007 Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
1008 } else { 1008 } else {
1009 let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); 1009 let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(it));
1010 Literal::Uint(lit.value().unwrap_or(0), builtin) 1010 Literal::Uint(lit.value().unwrap_or(0), builtin)
1011 } 1011 }
1012 } 1012 }
1013 LiteralKind::FloatNumber(lit) => { 1013 LiteralKind::FloatNumber(lit) => {
1014 let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); 1014 let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(it));
1015 Literal::Float(Default::default(), ty) 1015 Literal::Float(Default::default(), ty)
1016 } 1016 }
1017 LiteralKind::ByteString(bs) => { 1017 LiteralKind::ByteString(bs) => {
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 6764de3a7..58a1fc81c 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -198,7 +198,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
198 } 198 }
199 Expr::Lambda { args, body: body_expr, .. } => { 199 Expr::Lambda { args, body: body_expr, .. } => {
200 let scope = scopes.new_scope(scope); 200 let scope = scopes.new_scope(scope);
201 scopes.add_params_bindings(body, scope, &args); 201 scopes.add_params_bindings(body, scope, args);
202 compute_expr_scopes(*body_expr, body, scopes, scope); 202 compute_expr_scopes(*body_expr, body, scopes, scope);
203 } 203 }
204 Expr::Match { expr, arms } => { 204 Expr::Match { expr, arms } => {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index d4fae05a6..27d837d47 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -3,7 +3,7 @@ mod block;
3use base_db::{fixture::WithFixture, SourceDatabase}; 3use base_db::{fixture::WithFixture, SourceDatabase};
4use expect_test::Expect; 4use expect_test::Expect;
5 5
6use crate::{test_db::TestDB, ModuleDefId}; 6use crate::ModuleDefId;
7 7
8use super::*; 8use super::*;
9 9
@@ -28,11 +28,6 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
28 db.body(fn_def.unwrap().into()) 28 db.body(fn_def.unwrap().into())
29} 29}
30 30
31fn check_diagnostics(ra_fixture: &str) {
32 let db: TestDB = TestDB::with_files(ra_fixture);
33 db.check_diagnostics();
34}
35
36fn block_def_map_at(ra_fixture: &str) -> String { 31fn block_def_map_at(ra_fixture: &str) -> String {
37 let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); 32 let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
38 33
@@ -57,7 +52,7 @@ fn check_at(ra_fixture: &str, expect: Expect) {
57fn your_stack_belongs_to_me() { 52fn your_stack_belongs_to_me() {
58 cov_mark::check!(your_stack_belongs_to_me); 53 cov_mark::check!(your_stack_belongs_to_me);
59 lower( 54 lower(
60 " 55 r#"
61macro_rules! n_nuple { 56macro_rules! n_nuple {
62 ($e:tt) => (); 57 ($e:tt) => ();
63 ($($rest:tt)*) => {{ 58 ($($rest:tt)*) => {{
@@ -65,7 +60,7 @@ macro_rules! n_nuple {
65 }}; 60 }};
66} 61}
67fn main() { n_nuple!(1,2,3); } 62fn main() { n_nuple!(1,2,3); }
68", 63"#,
69 ); 64 );
70} 65}
71 66
@@ -73,7 +68,7 @@ fn main() { n_nuple!(1,2,3); }
73fn macro_resolve() { 68fn macro_resolve() {
74 // Regression test for a path resolution bug introduced with inner item handling. 69 // Regression test for a path resolution bug introduced with inner item handling.
75 lower( 70 lower(
76 r" 71 r#"
77macro_rules! vec { 72macro_rules! vec {
78 () => { () }; 73 () => { () };
79 ($elem:expr; $n:expr) => { () }; 74 ($elem:expr; $n:expr) => { () };
@@ -84,140 +79,6 @@ mod m {
84 let _ = vec![FileSet::default(); self.len()]; 79 let _ = vec![FileSet::default(); self.len()];
85 } 80 }
86} 81}
87 ", 82"#,
88 );
89}
90
91#[test]
92fn cfg_diagnostics() {
93 check_diagnostics(
94 r"
95fn f() {
96 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
97
98 #[cfg(a)] fn f() {} // Item statement
99 //^^^^^^^^^^^^^^^^^^^ InactiveCode
100 #[cfg(a)] {} // Expression statement
101 //^^^^^^^^^^^^ InactiveCode
102 #[cfg(a)] let x = 0; // let statement
103 //^^^^^^^^^^^^^^^^^^^^ InactiveCode
104
105 abc(#[cfg(a)] 0);
106 //^^^^^^^^^^^ InactiveCode
107 let x = Struct {
108 #[cfg(a)] f: 0,
109 //^^^^^^^^^^^^^^ InactiveCode
110 };
111 match () {
112 () => (),
113 #[cfg(a)] () => (),
114 //^^^^^^^^^^^^^^^^^^ InactiveCode
115 }
116
117 #[cfg(a)] 0 // Trailing expression of block
118 //^^^^^^^^^^^ InactiveCode
119}
120 ",
121 );
122}
123
124#[test]
125fn macro_diag_builtin() {
126 check_diagnostics(
127 r#"
128#[rustc_builtin_macro]
129macro_rules! env {}
130
131#[rustc_builtin_macro]
132macro_rules! include {}
133
134#[rustc_builtin_macro]
135macro_rules! compile_error {}
136
137#[rustc_builtin_macro]
138macro_rules! format_args {
139 () => {}
140}
141
142fn f() {
143 // Test a handful of built-in (eager) macros:
144
145 include!(invalid);
146 //^^^^^^^^^^^^^^^^^ could not convert tokens
147 include!("does not exist");
148 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
149
150 env!(invalid);
151 //^^^^^^^^^^^^^ could not convert tokens
152
153 env!("OUT_DIR");
154 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
155
156 compile_error!("compile_error works");
157 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
158
159 // Lazy:
160
161 format_args!();
162 //^^^^^^^^^^^^^^ no rule matches input tokens
163}
164 "#,
165 );
166}
167
168#[test]
169fn macro_rules_diag() {
170 check_diagnostics(
171 r#"
172macro_rules! m {
173 () => {};
174}
175fn f() {
176 m!();
177
178 m!(hi);
179 //^^^^^^ leftover tokens
180}
181 "#,
182 ); 83 );
183} 84}
184
185#[test]
186fn unresolved_macro_diag() {
187 check_diagnostics(
188 r#"
189fn f() {
190 m!();
191 //^^^^ UnresolvedMacroCall
192}
193 "#,
194 );
195}
196
197#[test]
198fn dollar_crate_in_builtin_macro() {
199 check_diagnostics(
200 r#"
201#[macro_export]
202#[rustc_builtin_macro]
203macro_rules! format_args {}
204
205#[macro_export]
206macro_rules! arg {
207 () => {}
208}
209
210#[macro_export]
211macro_rules! outer {
212 () => {
213 $crate::format_args!( "", $crate::arg!(1) )
214 };
215}
216
217fn f() {
218 outer!();
219 //^^^^^^^^ leftover tokens
220}
221 "#,
222 )
223}
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index bc3d0f138..15c10d053 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -163,14 +163,14 @@ fn legacy_macro_items() {
163 // correctly. 163 // correctly.
164 check_at( 164 check_at(
165 r#" 165 r#"
166macro_rules! hit { 166macro_rules! mark {
167 () => { 167 () => {
168 struct Hit {} 168 struct Hit {}
169 } 169 }
170} 170}
171 171
172fn f() { 172fn f() {
173 hit!(); 173 mark!();
174 $0 174 $0
175} 175}
176"#, 176"#,
@@ -193,20 +193,20 @@ use core::cov_mark;
193 193
194fn f() { 194fn f() {
195 fn nested() { 195 fn nested() {
196 cov_mark::hit!(Hit); 196 cov_mark::mark!(Hit);
197 $0 197 $0
198 } 198 }
199} 199}
200//- /core.rs crate:core 200//- /core.rs crate:core
201pub mod cov_mark { 201pub mod cov_mark {
202 #[macro_export] 202 #[macro_export]
203 macro_rules! _hit { 203 macro_rules! _mark {
204 ($name:ident) => { 204 ($name:ident) => {
205 struct $name {} 205 struct $name {}
206 } 206 }
207 } 207 }
208 208
209 pub use crate::_hit as hit; 209 pub use crate::_mark as mark;
210} 210}
211"#, 211"#,
212 expect![[r#" 212 expect![[r#"
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 44d22b918..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,10 +277,10 @@ 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));
284 } 284 }
285 for lifetime_param in params.lifetime_params() { 285 for lifetime_param in params.lifetime_params() {
286 let name = 286 let name =
@@ -289,7 +289,7 @@ impl GenericParams {
289 let param_id = self.lifetimes.alloc(param); 289 let param_id = self.lifetimes.alloc(param);
290 sm.lifetime_params.insert(param_id, lifetime_param.clone()); 290 sm.lifetime_params.insert(param_id, lifetime_param.clone());
291 let lifetime_ref = LifetimeRef::new_name(name); 291 let lifetime_ref = LifetimeRef::new_name(name);
292 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); 292 self.fill_bounds(lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
293 } 293 }
294 for const_param in params.const_params() { 294 for const_param in params.const_params() {
295 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); 295 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -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_scope.rs b/crates/hir_def/src/item_scope.rs
index 0f74f050d..08407ebfa 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -59,7 +59,7 @@ pub struct ItemScope {
59pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { 59pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
60 BuiltinType::ALL 60 BuiltinType::ALL
61 .iter() 61 .iter()
62 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) 62 .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public)))
63 .collect() 63 .collect()
64}); 64});
65 65
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index cfda7cb32..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());
@@ -823,7 +823,7 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool {
823 known::type_name, 823 known::type_name,
824 known::variant_count, 824 known::variant_count,
825 ] 825 ]
826 .contains(&name) 826 .contains(name)
827} 827}
828 828
829fn lower_abi(abi: ast::Abi) -> Interned<str> { 829fn lower_abi(abi: ast::Abi) -> Interned<str> {
@@ -855,7 +855,7 @@ impl UseTreeLowering<'_> {
855 // E.g. `use something::{inner}` (prefix is `None`, path is `something`) 855 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
856 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) 856 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
857 Some(path) => { 857 Some(path) => {
858 match ModPath::from_src(self.db, path, &self.hygiene) { 858 match ModPath::from_src(self.db, path, self.hygiene) {
859 Some(it) => Some(it), 859 Some(it) => Some(it),
860 None => return None, // FIXME: report errors somewhere 860 None => return None, // FIXME: report errors somewhere
861 } 861 }
@@ -874,7 +874,7 @@ impl UseTreeLowering<'_> {
874 } else { 874 } else {
875 let is_glob = tree.star_token().is_some(); 875 let is_glob = tree.star_token().is_some();
876 let path = match tree.path() { 876 let path = match tree.path() {
877 Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?), 877 Some(path) => Some(ModPath::from_src(self.db, path, self.hygiene)?),
878 None => None, 878 None => None,
879 }; 879 };
880 let alias = tree.rename().map(|a| { 880 let alias = tree.rename().map(|a| {
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index cc9944a22..b1e1b70d0 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -426,7 +426,7 @@ impl<'a> Printer<'a> {
426 w!(self, " {{"); 426 w!(self, " {{");
427 self.indented(|this| { 427 self.indented(|this| {
428 for item in &**items { 428 for item in &**items {
429 this.print_mod_item((*item).into()); 429 this.print_mod_item(*item);
430 } 430 }
431 }); 431 });
432 wln!(self, "}}"); 432 wln!(self, "}}");
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 987485acc..bb174aec8 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -112,6 +112,10 @@ impl ModuleId {
112 self.def_map(db).containing_module(self.local_id) 112 self.def_map(db).containing_module(self.local_id)
113 } 113 }
114 114
115 pub fn containing_block(&self) -> Option<BlockId> {
116 self.block
117 }
118
115 /// Returns `true` if this module represents a block expression. 119 /// Returns `true` if this module represents a block expression.
116 /// 120 ///
117 /// Returns `false` if this module is a submodule *inside* a block expression 121 /// Returns `false` if this module is a submodule *inside* a block expression
@@ -581,6 +585,18 @@ impl HasModule for GenericDefId {
581 } 585 }
582} 586}
583 587
588impl HasModule for TypeAliasId {
589 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
590 self.lookup(db).module(db)
591 }
592}
593
594impl HasModule for TraitId {
595 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
596 self.lookup(db).container
597 }
598}
599
584impl HasModule for StaticLoc { 600impl HasModule for StaticLoc {
585 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { 601 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
586 self.container 602 self.container
@@ -731,13 +747,11 @@ fn macro_call_as_call_id(
731 ) 747 )
732 .map(MacroCallId::from) 748 .map(MacroCallId::from)
733 } else { 749 } else {
734 Ok(def 750 Ok(def.as_lazy_macro(
735 .as_lazy_macro( 751 db.upcast(),
736 db.upcast(), 752 krate,
737 krate, 753 MacroCallKind::FnLike { ast_id: call.ast_id, fragment },
738 MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, 754 ))
739 )
740 .into())
741 }; 755 };
742 Ok(res) 756 Ok(res)
743} 757}
@@ -756,17 +770,15 @@ fn derive_macro_as_call_id(
756 .segments() 770 .segments()
757 .last() 771 .last()
758 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; 772 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
759 let res = def 773 let res = def.as_lazy_macro(
760 .as_lazy_macro( 774 db.upcast(),
761 db.upcast(), 775 krate,
762 krate, 776 MacroCallKind::Derive {
763 MacroCallKind::Derive { 777 ast_id: item_attr.ast_id,
764 ast_id: item_attr.ast_id, 778 derive_name: last_segment.to_string(),
765 derive_name: last_segment.to_string(), 779 derive_attr_index: derive_attr.ast_index,
766 derive_attr_index: derive_attr.ast_index, 780 },
767 }, 781 );
768 )
769 .into();
770 Ok(res) 782 Ok(res)
771} 783}
772 784
@@ -794,17 +806,15 @@ fn attr_macro_as_call_id(
794 // The parentheses are always disposed here. 806 // The parentheses are always disposed here.
795 arg.delimiter = None; 807 arg.delimiter = None;
796 808
797 let res = def 809 let res = def.as_lazy_macro(
798 .as_lazy_macro( 810 db.upcast(),
799 db.upcast(), 811 krate,
800 krate, 812 MacroCallKind::Attr {
801 MacroCallKind::Attr { 813 ast_id: item_attr.ast_id,
802 ast_id: item_attr.ast_id, 814 attr_name: last_segment.to_string(),
803 attr_name: last_segment.to_string(), 815 attr_args: arg,
804 attr_args: arg, 816 invoc_attr_index: macro_attr.id.ast_index,
805 invoc_attr_index: macro_attr.id.ast_index, 817 },
806 }, 818 );
807 )
808 .into();
809 Ok(res) 819 Ok(res)
810} 820}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 93f30f23d..fc2c50fb8 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -500,7 +500,7 @@ impl DefCollector<'_> {
500 let (per_ns, _) = self.def_map.resolve_path( 500 let (per_ns, _) = self.def_map.resolve_path(
501 self.db, 501 self.db,
502 self.def_map.root, 502 self.def_map.root,
503 &path, 503 path,
504 BuiltinShadowMode::Other, 504 BuiltinShadowMode::Other,
505 ); 505 );
506 506
@@ -722,7 +722,7 @@ impl DefCollector<'_> {
722 if import.is_extern_crate { 722 if import.is_extern_crate {
723 let res = self.def_map.resolve_name_in_extern_prelude( 723 let res = self.def_map.resolve_name_in_extern_prelude(
724 self.db, 724 self.db,
725 &import 725 import
726 .path 726 .path
727 .as_ident() 727 .as_ident()
728 .expect("extern crate should have been desugared to one-element path"), 728 .expect("extern crate should have been desugared to one-element path"),
@@ -1351,7 +1351,7 @@ impl ModCollector<'_, '_> {
1351 let imports = Import::from_use( 1351 let imports = Import::from_use(
1352 self.def_collector.db, 1352 self.def_collector.db,
1353 krate, 1353 krate,
1354 &self.item_tree, 1354 self.item_tree,
1355 ItemTreeId::new(self.file_id, import_id), 1355 ItemTreeId::new(self.file_id, import_id),
1356 ); 1356 );
1357 self.def_collector.unresolved_imports.extend(imports.into_iter().map( 1357 self.def_collector.unresolved_imports.extend(imports.into_iter().map(
@@ -1368,7 +1368,7 @@ impl ModCollector<'_, '_> {
1368 import: Import::from_extern_crate( 1368 import: Import::from_extern_crate(
1369 self.def_collector.db, 1369 self.def_collector.db,
1370 krate, 1370 krate,
1371 &self.item_tree, 1371 self.item_tree,
1372 ItemTreeId::new(self.file_id, import_id), 1372 ItemTreeId::new(self.file_id, import_id),
1373 ), 1373 ),
1374 status: PartialResolvedImport::Unresolved, 1374 status: PartialResolvedImport::Unresolved,
@@ -1889,7 +1889,7 @@ impl ModCollector<'_, '_> {
1889 self.def_collector.def_map.with_ancestor_maps( 1889 self.def_collector.def_map.with_ancestor_maps(
1890 self.def_collector.db, 1890 self.def_collector.db,
1891 self.module_id, 1891 self.module_id,
1892 &mut |map, module| map[module].scope.get_legacy_macro(&name), 1892 &mut |map, module| map[module].scope.get_legacy_macro(name),
1893 ) 1893 )
1894 }) 1894 })
1895 }, 1895 },
@@ -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_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index c984148c3..629bc7952 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -93,7 +93,7 @@ impl DefMap {
93 let mut vis = match visibility { 93 let mut vis = match visibility {
94 RawVisibility::Module(path) => { 94 RawVisibility::Module(path) => {
95 let (result, remaining) = 95 let (result, remaining) =
96 self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module); 96 self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
97 if remaining.is_some() { 97 if remaining.is_some() {
98 return None; 98 return None;
99 } 99 }
@@ -205,7 +205,7 @@ impl DefMap {
205 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 205 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
206 }; 206 };
207 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); 207 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
208 self.resolve_name_in_crate_root_or_extern_prelude(db, &segment) 208 self.resolve_name_in_crate_root_or_extern_prelude(db, segment)
209 } 209 }
210 PathKind::Plain => { 210 PathKind::Plain => {
211 let (_, segment) = match segments.next() { 211 let (_, segment) = match segments.next() {
@@ -222,7 +222,7 @@ impl DefMap {
222 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; 222 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
223 223
224 log::debug!("resolving {:?} in module", segment); 224 log::debug!("resolving {:?} in module", segment);
225 self.resolve_name_in_module(db, original_module, &segment, prefer_module) 225 self.resolve_name_in_module(db, original_module, segment, prefer_module)
226 } 226 }
227 PathKind::Super(lvl) => { 227 PathKind::Super(lvl) => {
228 let mut module = original_module; 228 let mut module = original_module;
@@ -269,7 +269,7 @@ impl DefMap {
269 Some((_, segment)) => segment, 269 Some((_, segment)) => segment,
270 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 270 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
271 }; 271 };
272 if let Some(def) = self.extern_prelude.get(&segment) { 272 if let Some(def) = self.extern_prelude.get(segment) {
273 log::debug!("absolute path {:?} resolved to crate {:?}", path, def); 273 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
274 PerNs::types(*def, Visibility::Public) 274 PerNs::types(*def, Visibility::Public)
275 } else { 275 } else {
@@ -319,13 +319,13 @@ impl DefMap {
319 }; 319 };
320 320
321 // Since it is a qualified path here, it should not contains legacy macros 321 // Since it is a qualified path here, it should not contains legacy macros
322 module_data.scope.get(&segment) 322 module_data.scope.get(segment)
323 } 323 }
324 ModuleDefId::AdtId(AdtId::EnumId(e)) => { 324 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
325 // enum variant 325 // enum variant
326 cov_mark::hit!(can_import_enum_variant); 326 cov_mark::hit!(can_import_enum_variant);
327 let enum_data = db.enum_data(e); 327 let enum_data = db.enum_data(e);
328 match enum_data.variant(&segment) { 328 match enum_data.variant(segment) {
329 Some(local_id) => { 329 Some(local_id) => {
330 let variant = EnumVariantId { parent: e, local_id }; 330 let variant = EnumVariantId { parent: e, local_id };
331 match &*enum_data.variants[local_id].variant_data { 331 match &*enum_data.variants[local_id].variant_data {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 58c01354a..cf43f2a96 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -2,7 +2,6 @@ mod globs;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
6mod primitives; 5mod primitives;
7 6
8use std::sync::Arc; 7use std::sync::Arc;
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
deleted file mode 100644
index ec6670952..000000000
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ /dev/null
@@ -1,229 +0,0 @@
1use base_db::fixture::WithFixture;
2
3use crate::test_db::TestDB;
4
5fn check_diagnostics(ra_fixture: &str) {
6 let db: TestDB = TestDB::with_files(ra_fixture);
7 db.check_diagnostics();
8}
9
10fn check_no_diagnostics(ra_fixture: &str) {
11 let db: TestDB = TestDB::with_files(ra_fixture);
12 db.check_no_diagnostics();
13}
14
15#[test]
16fn unresolved_import() {
17 check_diagnostics(
18 r"
19 use does_exist;
20 use does_not_exist;
21 //^^^^^^^^^^^^^^^^^^^ UnresolvedImport
22
23 mod does_exist {}
24 ",
25 );
26}
27
28#[test]
29fn unresolved_extern_crate() {
30 check_diagnostics(
31 r"
32 //- /main.rs crate:main deps:core
33 extern crate core;
34 extern crate doesnotexist;
35 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
36 //- /lib.rs crate:core
37 ",
38 );
39}
40
41#[test]
42fn extern_crate_self_as() {
43 cov_mark::check!(extern_crate_self_as);
44 check_diagnostics(
45 r"
46 //- /lib.rs
47 extern crate doesnotexist;
48 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
49 // Should not error.
50 extern crate self as foo;
51 struct Foo;
52 use foo::Foo as Bar;
53 ",
54 );
55}
56
57#[test]
58fn dedup_unresolved_import_from_unresolved_crate() {
59 check_diagnostics(
60 r"
61 //- /main.rs crate:main
62 mod a {
63 extern crate doesnotexist;
64 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
65
66 // Should not error, since we already errored for the missing crate.
67 use doesnotexist::{self, bla, *};
68
69 use crate::doesnotexist;
70 //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
71 }
72
73 mod m {
74 use super::doesnotexist;
75 //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
76 }
77 ",
78 );
79}
80
81#[test]
82fn unresolved_module() {
83 check_diagnostics(
84 r"
85 //- /lib.rs
86 mod foo;
87 mod bar;
88 //^^^^^^^^ UnresolvedModule
89 mod baz {}
90 //- /foo.rs
91 ",
92 );
93}
94
95#[test]
96fn inactive_item() {
97 // Additional tests in `cfg` crate. This only tests disabled cfgs.
98
99 check_diagnostics(
100 r#"
101 //- /lib.rs
102 #[cfg(no)] pub fn f() {}
103 //^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
104
105 #[cfg(no)] #[cfg(no2)] mod m;
106 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
107
108 #[cfg(all(not(a), b))] enum E {}
109 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
110
111 #[cfg(feature = "std")] use std;
112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
113 "#,
114 );
115}
116
117/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
118#[test]
119fn inactive_via_cfg_attr() {
120 cov_mark::check!(cfg_attr_active);
121 check_diagnostics(
122 r#"
123 //- /lib.rs
124 #[cfg_attr(not(never), cfg(no))] fn f() {}
125 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
126
127 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
128
129 #[cfg_attr(never, cfg(no))] fn g() {}
130
131 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
132 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
133 "#,
134 );
135}
136
137#[test]
138fn unresolved_legacy_scope_macro() {
139 check_diagnostics(
140 r#"
141 //- /lib.rs
142 macro_rules! m { () => {} }
143
144 m!();
145 m2!();
146 //^^^^^^ UnresolvedMacroCall
147 "#,
148 );
149}
150
151#[test]
152fn unresolved_module_scope_macro() {
153 check_diagnostics(
154 r#"
155 //- /lib.rs
156 mod mac {
157 #[macro_export]
158 macro_rules! m { () => {} }
159 }
160
161 self::m!();
162 self::m2!();
163 //^^^^^^^^^^^^ UnresolvedMacroCall
164 "#,
165 );
166}
167
168#[test]
169fn builtin_macro_fails_expansion() {
170 check_diagnostics(
171 r#"
172 //- /lib.rs
173 #[rustc_builtin_macro]
174 macro_rules! include { () => {} }
175
176 include!("doesntexist");
177 //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
178 "#,
179 );
180}
181
182#[test]
183fn include_macro_should_allow_empty_content() {
184 check_no_diagnostics(
185 r#"
186 //- /lib.rs
187 #[rustc_builtin_macro]
188 macro_rules! include { () => {} }
189
190 include!("bar.rs");
191 //- /bar.rs
192 // empty
193 "#,
194 );
195}
196
197#[test]
198fn good_out_dir_diagnostic() {
199 check_diagnostics(
200 r#"
201 #[rustc_builtin_macro]
202 macro_rules! include { () => {} }
203 #[rustc_builtin_macro]
204 macro_rules! env { () => {} }
205 #[rustc_builtin_macro]
206 macro_rules! concat { () => {} }
207
208 include!(concat!(env!("OUT_DIR"), "/out.rs"));
209 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
210 "#,
211 );
212}
213
214#[test]
215fn register_attr_and_tool() {
216 cov_mark::check!(register_attr);
217 cov_mark::check!(register_tool);
218 check_no_diagnostics(
219 r#"
220#![register_tool(tool)]
221#![register_attr(attr)]
222
223#[tool::path]
224#[attr]
225struct S;
226 "#,
227 );
228 // NB: we don't currently emit diagnostics here
229}
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index f6220aa92..27345d07c 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -208,13 +208,13 @@ fn lower_generic_args_from_fn_path(
208 let params = params?; 208 let params = params?;
209 let mut param_types = Vec::new(); 209 let mut param_types = Vec::new();
210 for param in params.params() { 210 for param in params.params() {
211 let type_ref = TypeRef::from_ast_opt(&ctx, param.ty()); 211 let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
212 param_types.push(type_ref); 212 param_types.push(type_ref);
213 } 213 }
214 let arg = GenericArg::Type(TypeRef::Tuple(param_types)); 214 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
215 args.push(arg); 215 args.push(arg);
216 if let Some(ret_type) = ret_type { 216 if let Some(ret_type) = ret_type {
217 let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty()); 217 let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
218 bindings.push(AssociatedTypeBinding { 218 bindings.push(AssociatedTypeBinding {
219 name: name![Output], 219 name: name![Output],
220 type_ref: Some(type_ref), 220 type_ref: Some(type_ref),
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index fb8a6f260..d4681fa3e 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -133,7 +133,7 @@ impl Resolver {
133 Some(it) => it, 133 Some(it) => it,
134 None => return PerNs::none(), 134 None => return PerNs::none(),
135 }; 135 };
136 let (module_res, segment_index) = item_map.resolve_path(db, module, &path, shadow); 136 let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
137 if segment_index.is_some() { 137 if segment_index.is_some() {
138 return PerNs::none(); 138 return PerNs::none();
139 } 139 }
@@ -150,7 +150,7 @@ impl Resolver {
150 path: &ModPath, 150 path: &ModPath,
151 ) -> Option<TraitId> { 151 ) -> Option<TraitId> {
152 let (item_map, module) = self.module_scope()?; 152 let (item_map, module) = self.module_scope()?;
153 let (module_res, ..) = item_map.resolve_path(db, module, &path, BuiltinShadowMode::Module); 153 let (module_res, ..) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
154 match module_res.take_types()? { 154 match module_res.take_types()? {
155 ModuleDefId::TraitId(it) => Some(it), 155 ModuleDefId::TraitId(it) => Some(it),
156 _ => None, 156 _ => None,
@@ -325,7 +325,7 @@ impl Resolver {
325 path: &ModPath, 325 path: &ModPath,
326 ) -> Option<MacroDefId> { 326 ) -> Option<MacroDefId> {
327 let (item_map, module) = self.module_scope()?; 327 let (item_map, module) = self.module_scope()?;
328 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() 328 item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
329 } 329 }
330 330
331 pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { 331 pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
@@ -561,7 +561,7 @@ impl ModuleItemMap {
561 path: &ModPath, 561 path: &ModPath,
562 ) -> Option<ResolveValueResult> { 562 ) -> Option<ResolveValueResult> {
563 let (module_def, idx) = 563 let (module_def, idx) =
564 self.def_map.resolve_path_locally(db, self.module_id, &path, BuiltinShadowMode::Other); 564 self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
565 match idx { 565 match idx {
566 None => { 566 None => {
567 let value = to_value_ns(module_def)?; 567 let value = to_value_ns(module_def)?;
@@ -591,7 +591,7 @@ impl ModuleItemMap {
591 path: &ModPath, 591 path: &ModPath,
592 ) -> Option<(TypeNs, Option<usize>)> { 592 ) -> Option<(TypeNs, Option<usize>)> {
593 let (module_def, idx) = 593 let (module_def, idx) =
594 self.def_map.resolve_path_locally(db, self.module_id, &path, BuiltinShadowMode::Other); 594 self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
595 let res = to_type_ns(module_def)?; 595 let res = to_type_ns(module_def)?;
596 Some((res, idx)) 596 Some((res, idx))
597 } 597 }
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index b20b066e2..2635b556e 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -6,19 +6,16 @@ use std::{
6}; 6};
7 7
8use base_db::{ 8use base_db::{
9 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, FileRange, Upcast, 9 salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
10 SourceDatabase, Upcast,
10}; 11};
11use base_db::{AnchoredPath, SourceDatabase};
12use hir_expand::{db::AstDatabase, InFile}; 12use hir_expand::{db::AstDatabase, InFile};
13use rustc_hash::FxHashMap;
14use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
15use syntax::{algo, ast, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; 14use syntax::{algo, ast, AstNode};
16use test_utils::extract_annotations;
17 15
18use crate::{ 16use crate::{
19 body::BodyDiagnostic,
20 db::DefDatabase, 17 db::DefDatabase,
21 nameres::{diagnostics::DefDiagnosticKind, DefMap, ModuleSource}, 18 nameres::{DefMap, ModuleSource},
22 src::HasSource, 19 src::HasSource,
23 LocalModuleId, Lookup, ModuleDefId, ModuleId, 20 LocalModuleId, Lookup, ModuleDefId, ModuleId,
24}; 21};
@@ -245,145 +242,4 @@ impl TestDB {
245 }) 242 })
246 .collect() 243 .collect()
247 } 244 }
248
249 pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
250 let mut files = Vec::new();
251 let crate_graph = self.crate_graph();
252 for krate in crate_graph.iter() {
253 let crate_def_map = self.crate_def_map(krate);
254 for (module_id, _) in crate_def_map.modules() {
255 let file_id = crate_def_map[module_id].origin.file_id();
256 files.extend(file_id)
257 }
258 }
259 assert!(!files.is_empty());
260 files
261 .into_iter()
262 .filter_map(|file_id| {
263 let text = self.file_text(file_id);
264 let annotations = extract_annotations(&text);
265 if annotations.is_empty() {
266 return None;
267 }
268 Some((file_id, annotations))
269 })
270 .collect()
271 }
272
273 pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) {
274 let crate_graph = self.crate_graph();
275 for krate in crate_graph.iter() {
276 let crate_def_map = self.crate_def_map(krate);
277
278 for diag in crate_def_map.diagnostics() {
279 let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind {
280 DefDiagnosticKind::UnresolvedModule { ast, .. } => {
281 let node = ast.to_node(self.upcast());
282 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule")
283 }
284 DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => {
285 let node = ast.to_node(self.upcast());
286 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
287 }
288 DefDiagnosticKind::UnresolvedImport { id, .. } => {
289 let item_tree = id.item_tree(self.upcast());
290 let import = &item_tree[id.value];
291 let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast());
292 (InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport")
293 }
294 DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
295 let node = ast.to_node(self.upcast());
296 (InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode")
297 }
298 DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => {
299 (ast.to_node(self.upcast()), "UnresolvedProcMacro")
300 }
301 DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => {
302 let node = ast.to_node(self.upcast());
303 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall")
304 }
305 DefDiagnosticKind::MacroError { ast, message } => {
306 (ast.to_node(self.upcast()), message.as_str())
307 }
308 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
309 let node = ast.to_node(self.upcast());
310 (
311 InFile::new(ast.file_id, node.syntax().clone()),
312 "UnimplementedBuiltinMacro",
313 )
314 }
315 };
316
317 let frange = node.as_ref().original_file_range(self);
318 cb(frange, message.to_string())
319 }
320
321 for (_module_id, module) in crate_def_map.modules() {
322 for decl in module.scope.declarations() {
323 if let ModuleDefId::FunctionId(it) = decl {
324 let source_map = self.body_with_source_map(it.into()).1;
325 for diag in source_map.diagnostics() {
326 let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag {
327 BodyDiagnostic::InactiveCode { node, .. } => {
328 (node.clone().map(|it| it.into()), "InactiveCode")
329 }
330 BodyDiagnostic::MacroError { node, message } => {
331 (node.clone().map(|it| it.into()), message.as_str())
332 }
333 BodyDiagnostic::UnresolvedProcMacro { node } => {
334 (node.clone().map(|it| it.into()), "UnresolvedProcMacro")
335 }
336 BodyDiagnostic::UnresolvedMacroCall { node, .. } => {
337 (node.clone().map(|it| it.into()), "UnresolvedMacroCall")
338 }
339 };
340
341 let root = self.parse_or_expand(ptr.file_id).unwrap();
342 let node = ptr.map(|ptr| ptr.to_node(&root));
343 let frange = node.as_ref().original_file_range(self);
344 cb(frange, message.to_string())
345 }
346 }
347 }
348 }
349 }
350 }
351
352 pub(crate) fn check_diagnostics(&self) {
353 let db: &TestDB = self;
354 let annotations = db.extract_annotations();
355 assert!(!annotations.is_empty());
356
357 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
358 db.diagnostics(&mut |frange, message| {
359 actual.entry(frange.file_id).or_default().push((frange.range, message));
360 });
361
362 for (file_id, diags) in actual.iter_mut() {
363 diags.sort_by_key(|it| it.0.start());
364 let text = db.file_text(*file_id);
365 // For multiline spans, place them on line start
366 for (range, content) in diags {
367 if text[*range].contains('\n') {
368 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
369 *content = format!("... {}", content);
370 }
371 }
372 }
373
374 assert_eq!(annotations, actual);
375 }
376
377 pub(crate) fn check_no_diagnostics(&self) {
378 let db: &TestDB = self;
379 let annotations = db.extract_annotations();
380 assert!(annotations.is_empty());
381
382 let mut has_diagnostics = false;
383 db.diagnostics(&mut |_, _| {
384 has_diagnostics = true;
385 });
386
387 assert!(!has_diagnostics);
388 }
389} 245}
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index cbde6b940..ffe499973 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -128,7 +128,7 @@ impl TypeRef {
128 /// Converts an `ast::TypeRef` to a `hir::TypeRef`. 128 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
129 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { 129 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
130 match node { 130 match node {
131 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), 131 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
132 ast::Type::TupleType(inner) => { 132 ast::Type::TupleType(inner) => {
133 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect()) 133 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
134 } 134 }
@@ -142,7 +142,7 @@ impl TypeRef {
142 .unwrap_or(TypeRef::Error) 142 .unwrap_or(TypeRef::Error)
143 } 143 }
144 ast::Type::PtrType(inner) => { 144 ast::Type::PtrType(inner) => {
145 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty()); 145 let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
146 let mutability = Mutability::from_mutable(inner.mut_token().is_some()); 146 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
147 TypeRef::RawPtr(Box::new(inner_ty), mutability) 147 TypeRef::RawPtr(Box::new(inner_ty), mutability)
148 } 148 }
@@ -156,13 +156,13 @@ impl TypeRef {
156 .map(ConstScalar::usize_from_literal_expr) 156 .map(ConstScalar::usize_from_literal_expr)
157 .unwrap_or(ConstScalar::Unknown); 157 .unwrap_or(ConstScalar::Unknown);
158 158
159 TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) 159 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
160 } 160 }
161 ast::Type::SliceType(inner) => { 161 ast::Type::SliceType(inner) => {
162 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) 162 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
163 } 163 }
164 ast::Type::RefType(inner) => { 164 ast::Type::RefType(inner) => {
165 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty()); 165 let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
166 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt)); 166 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
167 let mutability = Mutability::from_mutable(inner.mut_token().is_some()); 167 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
168 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability) 168 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
@@ -180,7 +180,7 @@ impl TypeRef {
180 is_varargs = param.dotdotdot_token().is_some(); 180 is_varargs = param.dotdotdot_token().is_some();
181 } 181 }
182 182
183 pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect() 183 pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(ctx, it)).collect()
184 } else { 184 } else {
185 Vec::new() 185 Vec::new()
186 }; 186 };
@@ -188,7 +188,7 @@ impl TypeRef {
188 TypeRef::Fn(params, is_varargs) 188 TypeRef::Fn(params, is_varargs)
189 } 189 }
190 // for types are close enough for our purposes to the inner type for now... 190 // for types are close enough for our purposes to the inner type for now...
191 ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), 191 ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
192 ast::Type::ImplTraitType(inner) => { 192 ast::Type::ImplTraitType(inner) => {
193 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) 193 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
194 } 194 }
@@ -229,7 +229,7 @@ impl TypeRef {
229 TypeRef::RawPtr(type_ref, _) 229 TypeRef::RawPtr(type_ref, _)
230 | TypeRef::Reference(type_ref, ..) 230 | TypeRef::Reference(type_ref, ..)
231 | TypeRef::Array(type_ref, _) 231 | TypeRef::Array(type_ref, _)
232 | TypeRef::Slice(type_ref) => go(&type_ref, f), 232 | TypeRef::Slice(type_ref) => go(type_ref, f),
233 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { 233 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
234 for bound in bounds { 234 for bound in bounds {
235 match bound.as_ref() { 235 match bound.as_ref() {
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index fe9497b50..4610f6f91 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -325,7 +325,7 @@ $0
325 }, 325 },
326 }; 326 };
327 327
328 let id: MacroCallId = db.intern_macro(loc).into(); 328 let id: MacroCallId = db.intern_macro(loc);
329 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 329 let parsed = db.parse_or_expand(id.as_file()).unwrap();
330 330
331 // FIXME text() for syntax nodes parsed from token tree looks weird 331 // FIXME text() for syntax nodes parsed from token tree looks weird
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 0b310ba2f..f24d1d919 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -354,7 +354,7 @@ fn concat_expand(
354 // concat works with string and char literals, so remove any quotes. 354 // concat works with string and char literals, so remove any quotes.
355 // It also works with integer, float and boolean literals, so just use the rest 355 // It also works with integer, float and boolean literals, so just use the rest
356 // as-is. 356 // as-is.
357 let component = unquote_str(&it).unwrap_or_else(|| it.text.to_string()); 357 let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
358 text.push_str(&component); 358 text.push_str(&component);
359 } 359 }
360 // handle boolean literals 360 // handle boolean literals
@@ -417,7 +417,7 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
417 tt.token_trees 417 tt.token_trees
418 .get(0) 418 .get(0)
419 .and_then(|tt| match tt { 419 .and_then(|tt| match tt {
420 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it), 420 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
421 _ => None, 421 _ => None,
422 }) 422 })
423 .ok_or_else(|| mbe::ExpandError::ConversionError) 423 .ok_or_else(|| mbe::ExpandError::ConversionError)
@@ -430,7 +430,7 @@ fn include_expand(
430) -> ExpandResult<Option<ExpandedEager>> { 430) -> ExpandResult<Option<ExpandedEager>> {
431 let res = (|| { 431 let res = (|| {
432 let path = parse_string(tt)?; 432 let path = parse_string(tt)?;
433 let file_id = relative_file(db, arg_id.into(), &path, false)?; 433 let file_id = relative_file(db, arg_id, &path, false)?;
434 434
435 let subtree = parse_to_token_tree(&db.file_text(file_id)) 435 let subtree = parse_to_token_tree(&db.file_text(file_id))
436 .ok_or_else(|| mbe::ExpandError::ConversionError)? 436 .ok_or_else(|| mbe::ExpandError::ConversionError)?
@@ -480,7 +480,7 @@ fn include_str_expand(
480 // it's unusual to `include_str!` a Rust file), but we can return an empty string. 480 // it's unusual to `include_str!` a Rust file), but we can return an empty string.
481 // Ideally, we'd be able to offer a precise expansion if the user asks for macro 481 // Ideally, we'd be able to offer a precise expansion if the user asks for macro
482 // expansion. 482 // expansion.
483 let file_id = match relative_file(db, arg_id.into(), &path, true) { 483 let file_id = match relative_file(db, arg_id, &path, true) {
484 Ok(file_id) => file_id, 484 Ok(file_id) => file_id,
485 Err(_) => { 485 Err(_) => {
486 return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); 486 return ExpandResult::ok(Some(ExpandedEager::new(quote!(""))));
@@ -561,7 +561,7 @@ mod tests {
561 use syntax::ast::NameOwner; 561 use syntax::ast::NameOwner;
562 562
563 fn expand_builtin_macro(ra_fixture: &str) -> String { 563 fn expand_builtin_macro(ra_fixture: &str) -> String {
564 let (db, file_id) = TestDB::with_single_file(&ra_fixture); 564 let (db, file_id) = TestDB::with_single_file(ra_fixture);
565 let parsed = db.parse(file_id); 565 let parsed = db.parse(file_id);
566 let mut macro_rules: Vec<_> = 566 let mut macro_rules: Vec<_> =
567 parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); 567 parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect();
@@ -598,7 +598,7 @@ mod tests {
598 }, 598 },
599 }; 599 };
600 600
601 let id: MacroCallId = db.intern_macro(loc).into(); 601 let id: MacroCallId = db.intern_macro(loc);
602 id.as_file() 602 id.as_file()
603 } 603 }
604 Either::Right(expander) => { 604 Either::Right(expander) => {
@@ -635,7 +635,7 @@ mod tests {
635 kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, 635 kind: MacroCallKind::FnLike { ast_id: call_id, fragment },
636 }; 636 };
637 637
638 let id: MacroCallId = db.intern_macro(loc).into(); 638 let id: MacroCallId = db.intern_macro(loc);
639 id.as_file() 639 id.as_file()
640 } 640 }
641 }; 641 };
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 45e6e446a..66f44202b 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -57,7 +57,7 @@ impl TokenExpander {
57 // We store the result in salsa db to prevent non-deterministic behavior in 57 // We store the result in salsa db to prevent non-deterministic behavior in
58 // some proc-macro implementation 58 // some proc-macro implementation
59 // See #4315 for details 59 // See #4315 for details
60 db.expand_proc_macro(id.into()).into() 60 db.expand_proc_macro(id).into()
61 } 61 }
62 } 62 }
63 } 63 }
@@ -241,7 +241,7 @@ fn parse_macro_expansion(
241 } 241 }
242 }; 242 };
243 if is_self_replicating(&node, &call_node.value) { 243 if is_self_replicating(&node, &call_node.value) {
244 return ExpandResult::only_err(err); 244 ExpandResult::only_err(err)
245 } else { 245 } else {
246 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } 246 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
247 } 247 }
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 9093255f4..584ddcf9f 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -17,7 +17,7 @@
17//! > and we need to live with it because it's available on stable and widely relied upon. 17//! > and we need to live with it because it's available on stable and widely relied upon.
18//! 18//!
19//! 19//!
20//! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros 20//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
21 21
22use crate::{ 22use crate::{
23 ast::{self, AstNode}, 23 ast::{self, AstNode},
@@ -128,7 +128,7 @@ pub fn expand_eager_macro(
128 }), 128 }),
129 kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr }, 129 kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr },
130 }); 130 });
131 let arg_file_id: MacroCallId = arg_id.into(); 131 let arg_file_id: MacroCallId = arg_id;
132 132
133 let parsed_args = 133 let parsed_args =
134 diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; 134 diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0;
@@ -177,13 +177,11 @@ fn lazy_expand(
177 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 177 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
178 178
179 let fragment = crate::to_fragment_kind(&macro_call.value); 179 let fragment = crate::to_fragment_kind(&macro_call.value);
180 let id: MacroCallId = def 180 let id: MacroCallId = def.as_lazy_macro(
181 .as_lazy_macro( 181 db,
182 db, 182 krate,
183 krate, 183 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment },
184 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, 184 );
185 )
186 .into();
187 185
188 let err = db.macro_expand_error(id); 186 let err = db.macro_expand_error(id);
189 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); 187 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
@@ -216,8 +214,7 @@ fn eager_macro_recur(
216 def, 214 def,
217 macro_resolver, 215 macro_resolver,
218 diagnostic_sink, 216 diagnostic_sink,
219 )? 217 )?;
220 .into();
221 db.parse_or_expand(id.as_file()) 218 db.parse_or_expand(id.as_file())
222 .expect("successful macro expansion should be parseable") 219 .expect("successful macro expansion should be parseable")
223 .clone_for_update() 220 .clone_for_update()
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs
index 82dc7f326..bc3ecc593 100644
--- a/crates/hir_expand/src/input.rs
+++ b/crates/hir_expand/src/input.rs
@@ -78,7 +78,7 @@ mod tests {
78 use super::*; 78 use super::*;
79 79
80 fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { 80 fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) {
81 let (db, file_id) = TestDB::with_single_file(&ra_fixture); 81 let (db, file_id) = TestDB::with_single_file(ra_fixture);
82 let parsed = db.parse(file_id); 82 let parsed = db.parse(file_id);
83 83
84 let mut items: Vec<_> = 84 let mut items: Vec<_> =
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 623791b58..33107aa24 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -53,7 +53,7 @@ mod test_db;
53/// this is a recursive definition! However, the size_of of `HirFileId` is 53/// this is a recursive definition! However, the size_of of `HirFileId` is
54/// finite (because everything bottoms out at the real `FileId`) and small 54/// finite (because everything bottoms out at the real `FileId`) and small
55/// (`MacroCallId` uses the location interning. You can check details here: 55/// (`MacroCallId` uses the location interning. You can check details here:
56/// https://en.wikipedia.org/wiki/String_interning). 56/// <https://en.wikipedia.org/wiki/String_interning>).
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub struct HirFileId(HirFileIdRepr); 58pub struct HirFileId(HirFileIdRepr);
59 59
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index dbe1b446e..025e10239 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -45,13 +45,12 @@ impl ProcMacroExpander {
45 let proc_macro = krate_graph[self.krate] 45 let proc_macro = krate_graph[self.krate]
46 .proc_macro 46 .proc_macro
47 .get(id.0 as usize) 47 .get(id.0 as usize)
48 .clone()
49 .ok_or_else(|| err!("No derive macro found."))?; 48 .ok_or_else(|| err!("No derive macro found."))?;
50 49
51 // Proc macros have access to the environment variables of the invoking crate. 50 // Proc macros have access to the environment variables of the invoking crate.
52 let env = &krate_graph[calling_crate].env; 51 let env = &krate_graph[calling_crate].env;
53 52
54 proc_macro.expander.expand(&tt, attr_arg, &env).map_err(mbe::ExpandError::from) 53 proc_macro.expander.expand(tt, attr_arg, env).map_err(mbe::ExpandError::from)
55 } 54 }
56 None => Err(mbe::ExpandError::UnresolvedProcMacro), 55 None => Err(mbe::ExpandError::UnresolvedProcMacro),
57 } 56 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 4b714c6d8..74129eb21 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14itertools = "0.10.0" 14itertools = "0.10.0"
15arrayvec = "0.7" 15arrayvec = "0.7"
16smallvec = "1.2.0" 16smallvec = "1.2.0"
@@ -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/builder.rs b/crates/hir_ty/src/builder.rs
index 893e727c2..bb9d84246 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -202,7 +202,7 @@ impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>>
202 202
203impl TyBuilder<Binders<Ty>> { 203impl TyBuilder<Binders<Ty>> {
204 pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { 204 pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> {
205 TyBuilder::subst_binders(db.ty(def.into())) 205 TyBuilder::subst_binders(db.ty(def))
206 } 206 }
207 207
208 pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { 208 pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index 4e042bf42..a4c09c742 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -10,16 +10,16 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
10use base_db::CrateId; 10use base_db::CrateId;
11use hir_def::{ 11use hir_def::{
12 lang_item::{lang_attr, LangItemTarget}, 12 lang_item::{lang_attr, LangItemTarget},
13 AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId, 13 AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, ModuleId, TypeAliasId,
14}; 14};
15use hir_expand::name::name; 15use hir_expand::name::name;
16 16
17use crate::{ 17use crate::{
18 db::HirDatabase, 18 db::HirDatabase,
19 display::HirDisplay, 19 display::HirDisplay,
20 from_assoc_type_id, from_chalk_trait_id, make_only_type_binders, 20 from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders,
21 mapping::{from_chalk, ToChalk, TypeAliasAsValue}, 21 mapping::{from_chalk, ToChalk, TypeAliasAsValue},
22 method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, 22 method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
23 to_assoc_type_id, to_chalk_trait_id, 23 to_assoc_type_id, to_chalk_trait_id,
24 traits::ChalkContext, 24 traits::ChalkContext,
25 utils::generics, 25 utils::generics,
@@ -105,12 +105,30 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
105 _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), 105 _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
106 }; 106 };
107 107
108 fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
109 db.trait_impls_in_block(module.containing_block()?)
110 }
111
108 // Note: Since we're using impls_for_trait, only impls where the trait 112 // Note: Since we're using impls_for_trait, only impls where the trait
109 // can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that 113 // can be resolved should ever reach Chalk. impl_datum relies on that
110 // and will panic if the trait can't be resolved. 114 // and will panic if the trait can't be resolved.
111 let in_deps = self.db.trait_impls_in_deps(self.krate); 115 let in_deps = self.db.trait_impls_in_deps(self.krate);
112 let in_self = self.db.trait_impls_in_crate(self.krate); 116 let in_self = self.db.trait_impls_in_crate(self.krate);
113 let impl_maps = [in_deps, in_self]; 117 let trait_module = trait_.module(self.db.upcast());
118 let type_module = match self_ty_fp {
119 Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
120 Some(TyFingerprint::ForeignType(type_id)) => {
121 Some(from_foreign_def_id(type_id).module(self.db.upcast()))
122 }
123 Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
124 _ => None,
125 };
126 let impl_maps = [
127 Some(in_deps),
128 Some(in_self),
129 local_impls(self.db, trait_module),
130 type_module.and_then(|m| local_impls(self.db, m)),
131 ];
114 132
115 let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); 133 let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
116 134
@@ -118,14 +136,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
118 debug!("Unrestricted search for {:?} impls...", trait_); 136 debug!("Unrestricted search for {:?} impls...", trait_);
119 impl_maps 137 impl_maps
120 .iter() 138 .iter()
121 .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk)) 139 .filter_map(|o| o.as_ref())
140 .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
122 .collect() 141 .collect()
123 } else { 142 } else {
124 impl_maps 143 impl_maps
125 .iter() 144 .iter()
126 .flat_map(|crate_impl_defs| { 145 .filter_map(|o| o.as_ref())
146 .flat_map(|impls| {
127 fps.iter().flat_map(move |fp| { 147 fps.iter().flat_map(move |fp| {
128 crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) 148 impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
129 }) 149 })
130 }) 150 })
131 .collect() 151 .collect()
@@ -430,8 +450,7 @@ pub(crate) fn trait_datum_query(
430 fundamental: false, 450 fundamental: false,
431 }; 451 };
432 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); 452 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
433 let associated_ty_ids = 453 let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
434 trait_data.associated_types().map(|type_alias| to_assoc_type_id(type_alias)).collect();
435 let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; 454 let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
436 let well_known = 455 let well_known =
437 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); 456 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs
index e3ceb3d62..6f0bf8f8c 100644
--- a/crates/hir_ty/src/consteval.rs
+++ b/crates/hir_ty/src/consteval.rs
@@ -49,7 +49,7 @@ pub fn usize_const(value: Option<u64>) -> Const {
49 ConstData { 49 ConstData {
50 ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), 50 ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner),
51 value: ConstValue::Concrete(chalk_ir::ConcreteConst { 51 value: ConstValue::Concrete(chalk_ir::ConcreteConst {
52 interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), 52 interned: value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown),
53 }), 53 }),
54 } 54 }
55 .intern(&Interner) 55 .intern(&Interner)
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index be5b9110e..b9003c413 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -5,8 +5,8 @@ use std::sync::Arc;
5 5
6use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, 8 db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId,
9 LifetimeParamId, LocalFieldId, TypeParamId, VariantId, 9 ImplId, LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
10}; 10};
11use la_arena::ArenaMap; 11use la_arena::ArenaMap;
12 12
@@ -79,6 +79,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
79 #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] 79 #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
80 fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; 80 fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
81 81
82 #[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
83 fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
84
82 #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] 85 #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
83 fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; 86 fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
84 87
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 283894704..6339c9687 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -4,325 +4,31 @@ mod match_check;
4mod unsafe_check; 4mod unsafe_check;
5mod decl_check; 5mod decl_check;
6 6
7use std::{any::Any, fmt}; 7use std::fmt;
8 8
9use base_db::CrateId; 9use base_db::CrateId;
10use hir_def::{DefWithBodyId, ModuleDefId}; 10use hir_def::ModuleDefId;
11use hir_expand::{name::Name, HirFileId, InFile}; 11use hir_expand::HirFileId;
12use stdx::format_to; 12use syntax::{ast, AstPtr};
13use syntax::{ast, AstPtr, SyntaxNodePtr};
14 13
15use crate::{ 14use crate::db::HirDatabase;
16 db::HirDatabase,
17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
18};
19 15
20pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; 16pub use crate::diagnostics::{
17 expr::{
18 record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
19 },
20 unsafe_check::missing_unsafe,
21};
21 22
22pub fn validate_module_item( 23pub fn validate_module_item(
23 db: &dyn HirDatabase, 24 db: &dyn HirDatabase,
24 krate: CrateId, 25 krate: CrateId,
25 owner: ModuleDefId, 26 owner: ModuleDefId,
26 sink: &mut DiagnosticSink<'_>, 27) -> Vec<IncorrectCase> {
27) {
28 let _p = profile::span("validate_module_item"); 28 let _p = profile::span("validate_module_item");
29 let mut validator = decl_check::DeclValidator::new(db, krate, sink); 29 let mut validator = decl_check::DeclValidator::new(db, krate);
30 validator.validate_item(owner); 30 validator.validate_item(owner);
31} 31 validator.sink
32
33pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
34 let _p = profile::span("validate_body");
35 let infer = db.infer(owner);
36 infer.add_diagnostics(db, owner, sink);
37 let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
38 validator.validate_body(db);
39 let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
40 validator.validate_body(db);
41}
42
43// Diagnostic: no-such-field
44//
45// This diagnostic is triggered if created structure does not have field provided in record.
46#[derive(Debug)]
47pub struct NoSuchField {
48 pub file: HirFileId,
49 pub field: AstPtr<ast::RecordExprField>,
50}
51
52impl Diagnostic for NoSuchField {
53 fn code(&self) -> DiagnosticCode {
54 DiagnosticCode("no-such-field")
55 }
56
57 fn message(&self) -> String {
58 "no such field".to_string()
59 }
60
61 fn display_source(&self) -> InFile<SyntaxNodePtr> {
62 InFile::new(self.file, self.field.clone().into())
63 }
64
65 fn as_any(&self) -> &(dyn Any + Send + 'static) {
66 self
67 }
68}
69
70// Diagnostic: missing-structure-fields
71//
72// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
73//
74// Example:
75//
76// ```rust
77// struct A { a: u8, b: u8 }
78//
79// let a = A { a: 10 };
80// ```
81#[derive(Debug)]
82pub struct MissingFields {
83 pub file: HirFileId,
84 pub field_list_parent: AstPtr<ast::RecordExpr>,
85 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
86 pub missed_fields: Vec<Name>,
87}
88
89impl Diagnostic for MissingFields {
90 fn code(&self) -> DiagnosticCode {
91 DiagnosticCode("missing-structure-fields")
92 }
93 fn message(&self) -> String {
94 let mut buf = String::from("Missing structure fields:\n");
95 for field in &self.missed_fields {
96 format_to!(buf, "- {}\n", field);
97 }
98 buf
99 }
100
101 fn display_source(&self) -> InFile<SyntaxNodePtr> {
102 InFile {
103 file_id: self.file,
104 value: self
105 .field_list_parent_path
106 .clone()
107 .map(SyntaxNodePtr::from)
108 .unwrap_or_else(|| self.field_list_parent.clone().into()),
109 }
110 }
111
112 fn as_any(&self) -> &(dyn Any + Send + 'static) {
113 self
114 }
115}
116
117// Diagnostic: missing-pat-fields
118//
119// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
120//
121// Example:
122//
123// ```rust
124// struct A { a: u8, b: u8 }
125//
126// let a = A { a: 10, b: 20 };
127//
128// if let A { a } = a {
129// // ...
130// }
131// ```
132#[derive(Debug)]
133pub struct MissingPatFields {
134 pub file: HirFileId,
135 pub field_list_parent: AstPtr<ast::RecordPat>,
136 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
137 pub missed_fields: Vec<Name>,
138}
139
140impl Diagnostic for MissingPatFields {
141 fn code(&self) -> DiagnosticCode {
142 DiagnosticCode("missing-pat-fields")
143 }
144 fn message(&self) -> String {
145 let mut buf = String::from("Missing structure fields:\n");
146 for field in &self.missed_fields {
147 format_to!(buf, "- {}\n", field);
148 }
149 buf
150 }
151 fn display_source(&self) -> InFile<SyntaxNodePtr> {
152 InFile {
153 file_id: self.file,
154 value: self
155 .field_list_parent_path
156 .clone()
157 .map(SyntaxNodePtr::from)
158 .unwrap_or_else(|| self.field_list_parent.clone().into()),
159 }
160 }
161 fn as_any(&self) -> &(dyn Any + Send + 'static) {
162 self
163 }
164}
165
166// Diagnostic: missing-match-arm
167//
168// This diagnostic is triggered if `match` block is missing one or more match arms.
169#[derive(Debug)]
170pub struct MissingMatchArms {
171 pub file: HirFileId,
172 pub match_expr: AstPtr<ast::Expr>,
173 pub arms: AstPtr<ast::MatchArmList>,
174}
175
176impl Diagnostic for MissingMatchArms {
177 fn code(&self) -> DiagnosticCode {
178 DiagnosticCode("missing-match-arm")
179 }
180 fn message(&self) -> String {
181 String::from("Missing match arm")
182 }
183 fn display_source(&self) -> InFile<SyntaxNodePtr> {
184 InFile { file_id: self.file, value: self.match_expr.clone().into() }
185 }
186 fn as_any(&self) -> &(dyn Any + Send + 'static) {
187 self
188 }
189}
190
191// Diagnostic: missing-ok-or-some-in-tail-expr
192//
193// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
194// or if a block that should return `Option` returns a value not wrapped in `Some`.
195//
196// Example:
197//
198// ```rust
199// fn foo() -> Result<u8, ()> {
200// 10
201// }
202// ```
203#[derive(Debug)]
204pub struct MissingOkOrSomeInTailExpr {
205 pub file: HirFileId,
206 pub expr: AstPtr<ast::Expr>,
207 // `Some` or `Ok` depending on whether the return type is Result or Option
208 pub required: String,
209}
210
211impl Diagnostic for MissingOkOrSomeInTailExpr {
212 fn code(&self) -> DiagnosticCode {
213 DiagnosticCode("missing-ok-or-some-in-tail-expr")
214 }
215 fn message(&self) -> String {
216 format!("wrap return expression in {}", self.required)
217 }
218 fn display_source(&self) -> InFile<SyntaxNodePtr> {
219 InFile { file_id: self.file, value: self.expr.clone().into() }
220 }
221 fn as_any(&self) -> &(dyn Any + Send + 'static) {
222 self
223 }
224}
225
226#[derive(Debug)]
227pub struct RemoveThisSemicolon {
228 pub file: HirFileId,
229 pub expr: AstPtr<ast::Expr>,
230}
231
232impl Diagnostic for RemoveThisSemicolon {
233 fn code(&self) -> DiagnosticCode {
234 DiagnosticCode("remove-this-semicolon")
235 }
236
237 fn message(&self) -> String {
238 "Remove this semicolon".to_string()
239 }
240
241 fn display_source(&self) -> InFile<SyntaxNodePtr> {
242 InFile { file_id: self.file, value: self.expr.clone().into() }
243 }
244
245 fn as_any(&self) -> &(dyn Any + Send + 'static) {
246 self
247 }
248}
249
250// Diagnostic: break-outside-of-loop
251//
252// This diagnostic is triggered if the `break` keyword is used outside of a loop.
253#[derive(Debug)]
254pub struct BreakOutsideOfLoop {
255 pub file: HirFileId,
256 pub expr: AstPtr<ast::Expr>,
257}
258
259impl Diagnostic for BreakOutsideOfLoop {
260 fn code(&self) -> DiagnosticCode {
261 DiagnosticCode("break-outside-of-loop")
262 }
263 fn message(&self) -> String {
264 "break outside of loop".to_string()
265 }
266 fn display_source(&self) -> InFile<SyntaxNodePtr> {
267 InFile { file_id: self.file, value: self.expr.clone().into() }
268 }
269 fn as_any(&self) -> &(dyn Any + Send + 'static) {
270 self
271 }
272}
273
274// Diagnostic: missing-unsafe
275//
276// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
277#[derive(Debug)]
278pub struct MissingUnsafe {
279 pub file: HirFileId,
280 pub expr: AstPtr<ast::Expr>,
281}
282
283impl Diagnostic for MissingUnsafe {
284 fn code(&self) -> DiagnosticCode {
285 DiagnosticCode("missing-unsafe")
286 }
287 fn message(&self) -> String {
288 format!("This operation is unsafe and requires an unsafe function or block")
289 }
290 fn display_source(&self) -> InFile<SyntaxNodePtr> {
291 InFile { file_id: self.file, value: self.expr.clone().into() }
292 }
293 fn as_any(&self) -> &(dyn Any + Send + 'static) {
294 self
295 }
296}
297
298// Diagnostic: mismatched-arg-count
299//
300// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
301#[derive(Debug)]
302pub struct MismatchedArgCount {
303 pub file: HirFileId,
304 pub call_expr: AstPtr<ast::Expr>,
305 pub expected: usize,
306 pub found: usize,
307}
308
309impl Diagnostic for MismatchedArgCount {
310 fn code(&self) -> DiagnosticCode {
311 DiagnosticCode("mismatched-arg-count")
312 }
313 fn message(&self) -> String {
314 let s = if self.expected == 1 { "" } else { "s" };
315 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
316 }
317 fn display_source(&self) -> InFile<SyntaxNodePtr> {
318 InFile { file_id: self.file, value: self.call_expr.clone().into() }
319 }
320 fn as_any(&self) -> &(dyn Any + Send + 'static) {
321 self
322 }
323 fn is_experimental(&self) -> bool {
324 true
325 }
326} 32}
327 33
328#[derive(Debug)] 34#[derive(Debug)]
@@ -378,9 +84,6 @@ impl fmt::Display for IdentType {
378 } 84 }
379} 85}
380 86
381// Diagnostic: incorrect-ident-case
382//
383// 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].
384#[derive(Debug)] 87#[derive(Debug)]
385pub struct IncorrectCase { 88pub struct IncorrectCase {
386 pub file: HirFileId, 89 pub file: HirFileId,
@@ -390,450 +93,3 @@ pub struct IncorrectCase {
390 pub ident_text: String, 93 pub ident_text: String,
391 pub suggested_text: String, 94 pub suggested_text: String,
392} 95}
393
394impl Diagnostic for IncorrectCase {
395 fn code(&self) -> DiagnosticCode {
396 DiagnosticCode("incorrect-ident-case")
397 }
398
399 fn message(&self) -> String {
400 format!(
401 "{} `{}` should have {} name, e.g. `{}`",
402 self.ident_type,
403 self.ident_text,
404 self.expected_case.to_string(),
405 self.suggested_text
406 )
407 }
408
409 fn display_source(&self) -> InFile<SyntaxNodePtr> {
410 InFile::new(self.file, self.ident.clone().into())
411 }
412
413 fn as_any(&self) -> &(dyn Any + Send + 'static) {
414 self
415 }
416
417 fn is_experimental(&self) -> bool {
418 true
419 }
420}
421
422// Diagnostic: replace-filter-map-next-with-find-map
423//
424// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
425#[derive(Debug)]
426pub struct ReplaceFilterMapNextWithFindMap {
427 pub file: HirFileId,
428 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
429 pub next_expr: AstPtr<ast::Expr>,
430}
431
432impl Diagnostic for ReplaceFilterMapNextWithFindMap {
433 fn code(&self) -> DiagnosticCode {
434 DiagnosticCode("replace-filter-map-next-with-find-map")
435 }
436 fn message(&self) -> String {
437 "replace filter_map(..).next() with find_map(..)".to_string()
438 }
439 fn display_source(&self) -> InFile<SyntaxNodePtr> {
440 InFile { file_id: self.file, value: self.next_expr.clone().into() }
441 }
442 fn as_any(&self) -> &(dyn Any + Send + 'static) {
443 self
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
450 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
451 use hir_expand::db::AstDatabase;
452 use rustc_hash::FxHashMap;
453 use syntax::{TextRange, TextSize};
454
455 use crate::{
456 diagnostics::{validate_body, validate_module_item},
457 diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder},
458 test_db::TestDB,
459 };
460
461 impl TestDB {
462 fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
463 let crate_graph = self.crate_graph();
464 for krate in crate_graph.iter() {
465 let crate_def_map = self.crate_def_map(krate);
466
467 let mut fns = Vec::new();
468 for (module_id, _) in crate_def_map.modules() {
469 for decl in crate_def_map[module_id].scope.declarations() {
470 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
471 validate_module_item(self, krate, decl, &mut sink);
472
473 if let ModuleDefId::FunctionId(f) = decl {
474 fns.push(f)
475 }
476 }
477
478 for impl_id in crate_def_map[module_id].scope.impls() {
479 let impl_data = self.impl_data(impl_id);
480 for item in impl_data.items.iter() {
481 if let AssocItemId::FunctionId(f) = item {
482 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
483 validate_module_item(
484 self,
485 krate,
486 ModuleDefId::FunctionId(*f),
487 &mut sink,
488 );
489 fns.push(*f)
490 }
491 }
492 }
493 }
494
495 for f in fns {
496 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
497 validate_body(self, f.into(), &mut sink);
498 }
499 }
500 }
501 }
502
503 pub(crate) fn check_diagnostics(ra_fixture: &str) {
504 let db = TestDB::with_files(ra_fixture);
505 let annotations = db.extract_annotations();
506
507 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
508 db.diagnostics(|d| {
509 let src = d.display_source();
510 let root = db.parse_or_expand(src.file_id).unwrap();
511 // FIXME: macros...
512 let file_id = src.file_id.original_file(&db);
513 let range = src.value.to_node(&root).text_range();
514 let message = d.message();
515 actual.entry(file_id).or_default().push((range, message));
516 });
517
518 for (file_id, diags) in actual.iter_mut() {
519 diags.sort_by_key(|it| it.0.start());
520 let text = db.file_text(*file_id);
521 // For multiline spans, place them on line start
522 for (range, content) in diags {
523 if text[*range].contains('\n') {
524 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
525 *content = format!("... {}", content);
526 }
527 }
528 }
529
530 assert_eq!(annotations, actual);
531 }
532
533 #[test]
534 fn no_such_field_diagnostics() {
535 check_diagnostics(
536 r#"
537struct S { foo: i32, bar: () }
538impl S {
539 fn new() -> S {
540 S {
541 //^ Missing structure fields:
542 //| - bar
543 foo: 92,
544 baz: 62,
545 //^^^^^^^ no such field
546 }
547 }
548}
549"#,
550 );
551 }
552 #[test]
553 fn no_such_field_with_feature_flag_diagnostics() {
554 check_diagnostics(
555 r#"
556//- /lib.rs crate:foo cfg:feature=foo
557struct MyStruct {
558 my_val: usize,
559 #[cfg(feature = "foo")]
560 bar: bool,
561}
562
563impl MyStruct {
564 #[cfg(feature = "foo")]
565 pub(crate) fn new(my_val: usize, bar: bool) -> Self {
566 Self { my_val, bar }
567 }
568 #[cfg(not(feature = "foo"))]
569 pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
570 Self { my_val }
571 }
572}
573"#,
574 );
575 }
576
577 #[test]
578 fn no_such_field_enum_with_feature_flag_diagnostics() {
579 check_diagnostics(
580 r#"
581//- /lib.rs crate:foo cfg:feature=foo
582enum Foo {
583 #[cfg(not(feature = "foo"))]
584 Buz,
585 #[cfg(feature = "foo")]
586 Bar,
587 Baz
588}
589
590fn test_fn(f: Foo) {
591 match f {
592 Foo::Bar => {},
593 Foo::Baz => {},
594 }
595}
596"#,
597 );
598 }
599
600 #[test]
601 fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
602 check_diagnostics(
603 r#"
604//- /lib.rs crate:foo cfg:feature=foo
605struct S {
606 #[cfg(feature = "foo")]
607 foo: u32,
608 #[cfg(not(feature = "foo"))]
609 bar: u32,
610}
611
612impl S {
613 #[cfg(feature = "foo")]
614 fn new(foo: u32) -> Self {
615 Self { foo }
616 }
617 #[cfg(not(feature = "foo"))]
618 fn new(bar: u32) -> Self {
619 Self { bar }
620 }
621 fn new2(bar: u32) -> Self {
622 #[cfg(feature = "foo")]
623 { Self { foo: bar } }
624 #[cfg(not(feature = "foo"))]
625 { Self { bar } }
626 }
627 fn new2(val: u32) -> Self {
628 Self {
629 #[cfg(feature = "foo")]
630 foo: val,
631 #[cfg(not(feature = "foo"))]
632 bar: val,
633 }
634 }
635}
636"#,
637 );
638 }
639
640 #[test]
641 fn no_such_field_with_type_macro() {
642 check_diagnostics(
643 r#"
644macro_rules! Type { () => { u32 }; }
645struct Foo { bar: Type![] }
646
647impl Foo {
648 fn new() -> Self {
649 Foo { bar: 0 }
650 }
651}
652"#,
653 );
654 }
655
656 #[test]
657 fn missing_record_pat_field_diagnostic() {
658 check_diagnostics(
659 r#"
660struct S { foo: i32, bar: () }
661fn baz(s: S) {
662 let S { foo: _ } = s;
663 //^ Missing structure fields:
664 //| - bar
665}
666"#,
667 );
668 }
669
670 #[test]
671 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
672 check_diagnostics(
673 r"
674struct S { foo: i32, bar: () }
675fn baz(s: S) -> i32 {
676 match s {
677 S { foo, .. } => foo,
678 }
679}
680",
681 )
682 }
683
684 #[test]
685 fn missing_record_pat_field_box() {
686 check_diagnostics(
687 r"
688struct S { s: Box<u32> }
689fn x(a: S) {
690 let S { box s } = a;
691}
692",
693 )
694 }
695
696 #[test]
697 fn missing_record_pat_field_ref() {
698 check_diagnostics(
699 r"
700struct S { s: u32 }
701fn x(a: S) {
702 let S { ref s } = a;
703}
704",
705 )
706 }
707
708 #[test]
709 fn import_extern_crate_clash_with_inner_item() {
710 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
711
712 check_diagnostics(
713 r#"
714//- /lib.rs crate:lib deps:jwt
715mod permissions;
716
717use permissions::jwt;
718
719fn f() {
720 fn inner() {}
721 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
722}
723
724//- /permissions.rs
725pub mod jwt {
726 pub struct Claims {}
727}
728
729//- /jwt/lib.rs crate:jwt
730pub struct Claims {
731 field: u8,
732}
733 "#,
734 );
735 }
736
737 #[test]
738 fn break_outside_of_loop() {
739 check_diagnostics(
740 r#"
741fn foo() { break; }
742 //^^^^^ break outside of loop
743"#,
744 );
745 }
746
747 #[test]
748 fn missing_semicolon() {
749 check_diagnostics(
750 r#"
751 fn test() -> i32 { 123; }
752 //^^^ Remove this semicolon
753 "#,
754 );
755 }
756
757 // Register the required standard library types to make the tests work
758 fn add_filter_map_with_find_next_boilerplate(body: &str) -> String {
759 let prefix = r#"
760 //- /main.rs crate:main deps:core
761 use core::iter::Iterator;
762 use core::option::Option::{self, Some, None};
763 "#;
764 let suffix = r#"
765 //- /core/lib.rs crate:core
766 pub mod option {
767 pub enum Option<T> { Some(T), None }
768 }
769 pub mod iter {
770 pub trait Iterator {
771 type Item;
772 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
773 fn next(&mut self) -> Option<Self::Item>;
774 }
775 pub struct FilterMap {}
776 impl Iterator for FilterMap {
777 type Item = i32;
778 fn next(&mut self) -> i32 { 7 }
779 }
780 }
781 "#;
782 format!("{}{}{}", prefix, body, suffix)
783 }
784
785 #[test]
786 fn replace_filter_map_next_with_find_map2() {
787 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
788 r#"
789 fn foo() {
790 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
791 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
792 }
793 "#,
794 ));
795 }
796
797 #[test]
798 fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
799 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
800 r#"
801 fn foo() {
802 let m = [1, 2, 3]
803 .iter()
804 .filter_map(|x| if *x == 2 { Some (4) } else { None })
805 .len();
806 }
807 "#,
808 ));
809 }
810
811 #[test]
812 fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
813 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
814 r#"
815 fn foo() {
816 let m = [1, 2, 3]
817 .iter()
818 .filter_map(|x| if *x == 2 { Some (4) } else { None })
819 .map(|x| x + 2)
820 .len();
821 }
822 "#,
823 ));
824 }
825
826 #[test]
827 fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
828 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
829 r#"
830 fn foo() {
831 let m = [1, 2, 3]
832 .iter()
833 .filter_map(|x| if *x == 2 { Some (4) } else { None });
834 let n = m.next();
835 }
836 "#,
837 ));
838 }
839}
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index cfb5d7320..f26150b77 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -29,7 +29,6 @@ use syntax::{
29use crate::{ 29use crate::{
30 db::HirDatabase, 30 db::HirDatabase,
31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, 31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
32 diagnostics_sink::DiagnosticSink,
33}; 32};
34 33
35mod allow { 34mod allow {
@@ -40,10 +39,10 @@ mod allow {
40 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; 39 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
41} 40}
42 41
43pub(super) struct DeclValidator<'a, 'b> { 42pub(super) struct DeclValidator<'a> {
44 db: &'a dyn HirDatabase, 43 db: &'a dyn HirDatabase,
45 krate: CrateId, 44 krate: CrateId,
46 sink: &'a mut DiagnosticSink<'b>, 45 pub(super) sink: Vec<IncorrectCase>,
47} 46}
48 47
49#[derive(Debug)] 48#[derive(Debug)]
@@ -53,13 +52,9 @@ struct Replacement {
53 expected_case: CaseType, 52 expected_case: CaseType,
54} 53}
55 54
56impl<'a, 'b> DeclValidator<'a, 'b> { 55impl<'a> DeclValidator<'a> {
57 pub(super) fn new( 56 pub(super) fn new(db: &'a dyn HirDatabase, krate: CrateId) -> DeclValidator<'a> {
58 db: &'a dyn HirDatabase, 57 DeclValidator { db, krate, sink: Vec::new() }
59 krate: CrateId,
60 sink: &'a mut DiagnosticSink<'b>,
61 ) -> DeclValidator<'a, 'b> {
62 DeclValidator { db, krate, sink }
63 } 58 }
64 59
65 pub(super) fn validate_item(&mut self, item: ModuleDefId) { 60 pub(super) fn validate_item(&mut self, item: ModuleDefId) {
@@ -131,7 +126,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
131 for (_, block_def_map) in body.blocks(self.db.upcast()) { 126 for (_, block_def_map) in body.blocks(self.db.upcast()) {
132 for (_, module) in block_def_map.modules() { 127 for (_, module) in block_def_map.modules() {
133 for def_id in module.scope.declarations() { 128 for def_id in module.scope.declarations() {
134 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 129 let mut validator = DeclValidator::new(self.db, self.krate);
135 validator.validate_item(def_id); 130 validator.validate_item(def_id);
136 } 131 }
137 } 132 }
@@ -623,343 +618,3 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
623 self.sink.push(diagnostic); 618 self.sink.push(diagnostic);
624 } 619 }
625} 620}
626
627#[cfg(test)]
628mod tests {
629 use crate::diagnostics::tests::check_diagnostics;
630
631 #[test]
632 fn incorrect_function_name() {
633 check_diagnostics(
634 r#"
635fn NonSnakeCaseName() {}
636// ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
637"#,
638 );
639 }
640
641 #[test]
642 fn incorrect_function_params() {
643 check_diagnostics(
644 r#"
645fn foo(SomeParam: u8) {}
646 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
647
648fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
649 // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
650"#,
651 );
652 }
653
654 #[test]
655 fn incorrect_variable_names() {
656 check_diagnostics(
657 r#"
658fn foo() {
659 let SOME_VALUE = 10;
660 // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
661 let AnotherValue = 20;
662 // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value`
663}
664"#,
665 );
666 }
667
668 #[test]
669 fn incorrect_struct_names() {
670 check_diagnostics(
671 r#"
672struct non_camel_case_name {}
673 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
674
675struct SCREAMING_CASE {}
676 // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
677"#,
678 );
679 }
680
681 #[test]
682 fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
683 check_diagnostics(
684 r#"
685struct AABB {}
686"#,
687 );
688 }
689
690 #[test]
691 fn incorrect_struct_field() {
692 check_diagnostics(
693 r#"
694struct SomeStruct { SomeField: u8 }
695 // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field`
696"#,
697 );
698 }
699
700 #[test]
701 fn incorrect_enum_names() {
702 check_diagnostics(
703 r#"
704enum some_enum { Val(u8) }
705 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
706
707enum SOME_ENUM
708 // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
709"#,
710 );
711 }
712
713 #[test]
714 fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
715 check_diagnostics(
716 r#"
717enum AABB {}
718"#,
719 );
720 }
721
722 #[test]
723 fn incorrect_enum_variant_name() {
724 check_diagnostics(
725 r#"
726enum SomeEnum { SOME_VARIANT(u8) }
727 // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
728"#,
729 );
730 }
731
732 #[test]
733 fn incorrect_const_name() {
734 check_diagnostics(
735 r#"
736const some_weird_const: u8 = 10;
737 // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
738
739fn func() {
740 const someConstInFunc: &str = "hi there";
741 // ^^^^^^^^^^^^^^^ Constant `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
742
743}
744"#,
745 );
746 }
747
748 #[test]
749 fn incorrect_static_name() {
750 check_diagnostics(
751 r#"
752static some_weird_const: u8 = 10;
753 // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
754
755fn func() {
756 static someConstInFunc: &str = "hi there";
757 // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
758}
759"#,
760 );
761 }
762
763 #[test]
764 fn fn_inside_impl_struct() {
765 check_diagnostics(
766 r#"
767struct someStruct;
768 // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
769
770impl someStruct {
771 fn SomeFunc(&self) {
772 // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func`
773 static someConstInFunc: &str = "hi there";
774 // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
775 let WHY_VAR_IS_CAPS = 10;
776 // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
777 }
778}
779"#,
780 );
781 }
782
783 #[test]
784 fn no_diagnostic_for_enum_varinats() {
785 check_diagnostics(
786 r#"
787enum Option { Some, None }
788
789fn main() {
790 match Option::None {
791 None => (),
792 Some => (),
793 }
794}
795"#,
796 );
797 }
798
799 #[test]
800 fn non_let_bind() {
801 check_diagnostics(
802 r#"
803enum Option { Some, None }
804
805fn main() {
806 match Option::None {
807 SOME_VAR @ None => (),
808 // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
809 Some => (),
810 }
811}
812"#,
813 );
814 }
815
816 #[test]
817 fn allow_attributes() {
818 check_diagnostics(
819 r#"
820#[allow(non_snake_case)]
821fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
822 // cov_flags generated output from elsewhere in this file
823 extern "C" {
824 #[no_mangle]
825 static lower_case: u8;
826 }
827
828 let OtherVar = SOME_VAR + 1;
829 OtherVar
830}
831
832#[allow(nonstandard_style)]
833mod CheckNonstandardStyle {
834 fn HiImABadFnName() {}
835}
836
837#[allow(bad_style)]
838mod CheckBadStyle {
839 fn HiImABadFnName() {}
840}
841
842mod F {
843 #![allow(non_snake_case)]
844 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
845}
846
847#[allow(non_snake_case, non_camel_case_types)]
848pub struct some_type {
849 SOME_FIELD: u8,
850 SomeField: u16,
851}
852
853#[allow(non_upper_case_globals)]
854pub const some_const: u8 = 10;
855
856#[allow(non_upper_case_globals)]
857pub static SomeStatic: u8 = 10;
858 "#,
859 );
860 }
861
862 #[test]
863 fn allow_attributes_crate_attr() {
864 check_diagnostics(
865 r#"
866#![allow(non_snake_case)]
867
868mod F {
869 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
870}
871 "#,
872 );
873 }
874
875 #[test]
876 #[ignore]
877 fn bug_trait_inside_fn() {
878 // FIXME:
879 // This is broken, and in fact, should not even be looked at by this
880 // lint in the first place. There's weird stuff going on in the
881 // collection phase.
882 // It's currently being brought in by:
883 // * validate_func on `a` recursing into modules
884 // * then it finds the trait and then the function while iterating
885 // through modules
886 // * then validate_func is called on Dirty
887 // * ... which then proceeds to look at some unknown module taking no
888 // attrs from either the impl or the fn a, and then finally to the root
889 // module
890 //
891 // It should find the attribute on the trait, but it *doesn't even see
892 // the trait* as far as I can tell.
893
894 check_diagnostics(
895 r#"
896trait T { fn a(); }
897struct U {}
898impl T for U {
899 fn a() {
900 // this comes out of bitflags, mostly
901 #[allow(non_snake_case)]
902 trait __BitFlags {
903 const HiImAlsoBad: u8 = 2;
904 #[inline]
905 fn Dirty(&self) -> bool {
906 false
907 }
908 }
909
910 }
911}
912 "#,
913 );
914 }
915
916 #[test]
917 #[ignore]
918 fn bug_traits_arent_checked() {
919 // FIXME: Traits and functions in traits aren't currently checked by
920 // r-a, even though rustc will complain about them.
921 check_diagnostics(
922 r#"
923trait BAD_TRAIT {
924 // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
925 fn BAD_FUNCTION();
926 // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
927 fn BadFunction();
928 // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
929}
930 "#,
931 );
932 }
933
934 #[test]
935 fn ignores_extern_items() {
936 cov_mark::check!(extern_func_incorrect_case_ignored);
937 cov_mark::check!(extern_static_incorrect_case_ignored);
938 check_diagnostics(
939 r#"
940extern {
941 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
942 pub static SomeStatic: u8 = 10;
943}
944 "#,
945 );
946 }
947
948 #[test]
949 fn infinite_loop_inner_items() {
950 check_diagnostics(
951 r#"
952fn qualify() {
953 mod foo {
954 use super::*;
955 }
956}
957 "#,
958 )
959 }
960
961 #[test] // Issue #8809.
962 fn parenthesized_parameter() {
963 check_diagnostics(r#"fn f((O): _) {}"#)
964 }
965}
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index a2a4d61db..b809b96a0 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -8,20 +8,15 @@ use hir_def::{
8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, 8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
9}; 9};
10use hir_expand::name; 10use hir_expand::name;
11use itertools::Either;
11use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
12use syntax::{ast, AstPtr};
13 13
14use crate::{ 14use crate::{
15 db::HirDatabase, 15 db::HirDatabase,
16 diagnostics::{ 16 diagnostics::match_check::{
17 match_check::{ 17 self,
18 self, 18 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
19 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
20 },
21 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
22 MissingPatFields, RemoveThisSemicolon,
23 }, 19 },
24 diagnostics_sink::DiagnosticSink,
25 AdtId, InferenceResult, Interner, TyExt, TyKind, 20 AdtId, InferenceResult, Interner, TyExt, TyKind,
26}; 21};
27 22
@@ -31,38 +26,67 @@ pub(crate) use hir_def::{
31 LocalFieldId, VariantId, 26 LocalFieldId, VariantId,
32}; 27};
33 28
34use super::ReplaceFilterMapNextWithFindMap; 29pub enum BodyValidationDiagnostic {
30 RecordMissingFields {
31 record: Either<ExprId, PatId>,
32 variant: VariantId,
33 missed_fields: Vec<LocalFieldId>,
34 },
35 ReplaceFilterMapNextWithFindMap {
36 method_call_expr: ExprId,
37 },
38 MismatchedArgCount {
39 call_expr: ExprId,
40 expected: usize,
41 found: usize,
42 },
43 RemoveThisSemicolon {
44 expr: ExprId,
45 },
46 MissingOkOrSomeInTailExpr {
47 expr: ExprId,
48 required: String,
49 },
50 MissingMatchArms {
51 match_expr: ExprId,
52 },
53}
54
55impl BodyValidationDiagnostic {
56 pub fn collect(db: &dyn HirDatabase, owner: DefWithBodyId) -> Vec<BodyValidationDiagnostic> {
57 let _p = profile::span("BodyValidationDiagnostic::collect");
58 let infer = db.infer(owner);
59 let mut validator = ExprValidator::new(owner, infer.clone());
60 validator.validate_body(db);
61 validator.diagnostics
62 }
63}
35 64
36pub(super) struct ExprValidator<'a, 'b: 'a> { 65struct ExprValidator {
37 owner: DefWithBodyId, 66 owner: DefWithBodyId,
38 infer: Arc<InferenceResult>, 67 infer: Arc<InferenceResult>,
39 sink: &'a mut DiagnosticSink<'b>, 68 pub(super) diagnostics: Vec<BodyValidationDiagnostic>,
40} 69}
41 70
42impl<'a, 'b> ExprValidator<'a, 'b> { 71impl ExprValidator {
43 pub(super) fn new( 72 fn new(owner: DefWithBodyId, infer: Arc<InferenceResult>) -> ExprValidator {
44 owner: DefWithBodyId, 73 ExprValidator { owner, infer, diagnostics: Vec::new() }
45 infer: Arc<InferenceResult>,
46 sink: &'a mut DiagnosticSink<'b>,
47 ) -> ExprValidator<'a, 'b> {
48 ExprValidator { owner, infer, sink }
49 } 74 }
50 75
51 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 76 fn validate_body(&mut self, db: &dyn HirDatabase) {
52 self.check_for_filter_map_next(db); 77 self.check_for_filter_map_next(db);
53 78
54 let body = db.body(self.owner); 79 let body = db.body(self.owner);
55 80
56 for (id, expr) in body.exprs.iter() { 81 for (id, expr) in body.exprs.iter() {
57 if let Some((variant_def, missed_fields, true)) = 82 if let Some((variant, missed_fields, true)) =
58 record_literal_missing_fields(db, &self.infer, id, expr) 83 record_literal_missing_fields(db, &self.infer, id, expr)
59 { 84 {
60 self.create_record_literal_missing_fields_diagnostic( 85 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
61 id, 86 record: Either::Left(id),
62 db, 87 variant,
63 variant_def,
64 missed_fields, 88 missed_fields,
65 ); 89 });
66 } 90 }
67 91
68 match expr { 92 match expr {
@@ -76,15 +100,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
76 } 100 }
77 } 101 }
78 for (id, pat) in body.pats.iter() { 102 for (id, pat) in body.pats.iter() {
79 if let Some((variant_def, missed_fields, true)) = 103 if let Some((variant, missed_fields, true)) =
80 record_pattern_missing_fields(db, &self.infer, id, pat) 104 record_pattern_missing_fields(db, &self.infer, id, pat)
81 { 105 {
82 self.create_record_pattern_missing_fields_diagnostic( 106 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
83 id, 107 record: Either::Right(id),
84 db, 108 variant,
85 variant_def,
86 missed_fields, 109 missed_fields,
87 ); 110 });
88 } 111 }
89 } 112 }
90 let body_expr = &body[body.body_expr]; 113 let body_expr = &body[body.body_expr];
@@ -92,71 +115,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
92 if let Some(t) = tail { 115 if let Some(t) = tail {
93 self.validate_results_in_tail_expr(body.body_expr, *t, db); 116 self.validate_results_in_tail_expr(body.body_expr, *t, db);
94 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { 117 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
95 self.validate_missing_tail_expr(body.body_expr, *id, db); 118 self.validate_missing_tail_expr(body.body_expr, *id);
96 }
97 }
98 }
99
100 fn create_record_literal_missing_fields_diagnostic(
101 &mut self,
102 id: ExprId,
103 db: &dyn HirDatabase,
104 variant_def: VariantId,
105 missed_fields: Vec<LocalFieldId>,
106 ) {
107 // XXX: only look at source_map if we do have missing fields
108 let (_, source_map) = db.body_with_source_map(self.owner);
109
110 if let Ok(source_ptr) = source_map.expr_syntax(id) {
111 let root = source_ptr.file_syntax(db.upcast());
112 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
113 if let Some(_) = record_expr.record_expr_field_list() {
114 let variant_data = variant_def.variant_data(db.upcast());
115 let missed_fields = missed_fields
116 .into_iter()
117 .map(|idx| variant_data.fields()[idx].name.clone())
118 .collect();
119 self.sink.push(MissingFields {
120 file: source_ptr.file_id,
121 field_list_parent: AstPtr::new(&record_expr),
122 field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)),
123 missed_fields,
124 })
125 }
126 }
127 }
128 }
129
130 fn create_record_pattern_missing_fields_diagnostic(
131 &mut self,
132 id: PatId,
133 db: &dyn HirDatabase,
134 variant_def: VariantId,
135 missed_fields: Vec<LocalFieldId>,
136 ) {
137 // XXX: only look at source_map if we do have missing fields
138 let (_, source_map) = db.body_with_source_map(self.owner);
139
140 if let Ok(source_ptr) = source_map.pat_syntax(id) {
141 if let Some(expr) = source_ptr.value.as_ref().left() {
142 let root = source_ptr.file_syntax(db.upcast());
143 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
144 if let Some(_) = record_pat.record_pat_field_list() {
145 let variant_data = variant_def.variant_data(db.upcast());
146 let missed_fields = missed_fields
147 .into_iter()
148 .map(|idx| variant_data.fields()[idx].name.clone())
149 .collect();
150 self.sink.push(MissingPatFields {
151 file: source_ptr.file_id,
152 field_list_parent: AstPtr::new(&record_pat),
153 field_list_parent_path: record_pat
154 .path()
155 .map(|path| AstPtr::new(&path)),
156 missed_fields,
157 })
158 }
159 }
160 } 119 }
161 } 120 }
162 } 121 }
@@ -199,13 +158,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
199 if function_id == *next_function_id { 158 if function_id == *next_function_id {
200 if let Some(filter_map_id) = prev { 159 if let Some(filter_map_id) = prev {
201 if *receiver == filter_map_id { 160 if *receiver == filter_map_id {
202 let (_, source_map) = db.body_with_source_map(self.owner); 161 self.diagnostics.push(
203 if let Ok(next_source_ptr) = source_map.expr_syntax(id) { 162 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
204 self.sink.push(ReplaceFilterMapNextWithFindMap { 163 method_call_expr: id,
205 file: next_source_ptr.file_id, 164 },
206 next_expr: next_source_ptr.value, 165 );
207 });
208 }
209 } 166 }
210 } 167 }
211 } 168 }
@@ -266,19 +223,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
266 let mut arg_count = args.len(); 223 let mut arg_count = args.len();
267 224
268 if arg_count != param_count { 225 if arg_count != param_count {
269 let (_, source_map) = db.body_with_source_map(self.owner); 226 if is_method_call {
270 if let Ok(source_ptr) = source_map.expr_syntax(call_id) { 227 param_count -= 1;
271 if is_method_call { 228 arg_count -= 1;
272 param_count -= 1;
273 arg_count -= 1;
274 }
275 self.sink.push(MismatchedArgCount {
276 file: source_ptr.file_id,
277 call_expr: source_ptr.value,
278 expected: param_count,
279 found: arg_count,
280 });
281 } 229 }
230 self.diagnostics.push(BodyValidationDiagnostic::MismatchedArgCount {
231 call_expr: call_id,
232 expected: param_count,
233 found: arg_count,
234 });
282 } 235 }
283 } 236 }
284 237
@@ -346,8 +299,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
346 // fit the match expression, we skip this diagnostic. Skipping the entire 299 // fit the match expression, we skip this diagnostic. Skipping the entire
347 // diagnostic rather than just not including this match arm is preferred 300 // diagnostic rather than just not including this match arm is preferred
348 // to avoid the chance of false positives. 301 // to avoid the chance of false positives.
349 #[cfg(test)] 302 cov_mark::hit!(validate_match_bailed_out);
350 match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink);
351 return; 303 return;
352 } 304 }
353 305
@@ -382,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
382 // FIXME Report witnesses 334 // FIXME Report witnesses
383 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); 335 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
384 if !witnesses.is_empty() { 336 if !witnesses.is_empty() {
385 if let Ok(source_ptr) = source_map.expr_syntax(id) { 337 self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id });
386 let root = source_ptr.file_syntax(db.upcast());
387 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
388 if let (Some(match_expr), Some(arms)) =
389 (match_expr.expr(), match_expr.match_arm_list())
390 {
391 self.sink.push(MissingMatchArms {
392 file: source_ptr.file_id,
393 match_expr: AstPtr::new(&match_expr),
394 arms: AstPtr::new(&arms),
395 })
396 }
397 }
398 }
399 } 338 }
400 } 339 }
401 340
@@ -453,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
453 if params.len(&Interner) > 0 392 if params.len(&Interner) > 0
454 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) 393 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual)
455 { 394 {
456 let (_, source_map) = db.body_with_source_map(self.owner); 395 self.diagnostics
457 396 .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required });
458 if let Ok(source_ptr) = source_map.expr_syntax(id) {
459 self.sink.push(MissingOkOrSomeInTailExpr {
460 file: source_ptr.file_id,
461 expr: source_ptr.value,
462 required,
463 });
464 }
465 } 397 }
466 } 398 }
467 399
468 fn validate_missing_tail_expr( 400 fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) {
469 &mut self,
470 body_id: ExprId,
471 possible_tail_id: ExprId,
472 db: &dyn HirDatabase,
473 ) {
474 let mismatch = match self.infer.type_mismatch_for_expr(body_id) { 401 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
475 Some(m) => m, 402 Some(m) => m,
476 None => return, 403 None => return,
@@ -485,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
485 return; 412 return;
486 } 413 }
487 414
488 let (_, source_map) = db.body_with_source_map(self.owner); 415 self.diagnostics
489 416 .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id });
490 if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
491 self.sink
492 .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
493 }
494 } 417 }
495} 418}
496 419
@@ -568,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
568 walk(pat, body, infer, &mut has_type_mismatches); 491 walk(pat, body, infer, &mut has_type_mismatches);
569 !has_type_mismatches 492 !has_type_mismatches
570} 493}
571
572#[cfg(test)]
573mod tests {
574 use crate::diagnostics::tests::check_diagnostics;
575
576 #[test]
577 fn simple_free_fn_zero() {
578 check_diagnostics(
579 r#"
580fn zero() {}
581fn f() { zero(1); }
582 //^^^^^^^ Expected 0 arguments, found 1
583"#,
584 );
585
586 check_diagnostics(
587 r#"
588fn zero() {}
589fn f() { zero(); }
590"#,
591 );
592 }
593
594 #[test]
595 fn simple_free_fn_one() {
596 check_diagnostics(
597 r#"
598fn one(arg: u8) {}
599fn f() { one(); }
600 //^^^^^ Expected 1 argument, found 0
601"#,
602 );
603
604 check_diagnostics(
605 r#"
606fn one(arg: u8) {}
607fn f() { one(1); }
608"#,
609 );
610 }
611
612 #[test]
613 fn method_as_fn() {
614 check_diagnostics(
615 r#"
616struct S;
617impl S { fn method(&self) {} }
618
619fn f() {
620 S::method();
621} //^^^^^^^^^^^ Expected 1 argument, found 0
622"#,
623 );
624
625 check_diagnostics(
626 r#"
627struct S;
628impl S { fn method(&self) {} }
629
630fn f() {
631 S::method(&S);
632 S.method();
633}
634"#,
635 );
636 }
637
638 #[test]
639 fn method_with_arg() {
640 check_diagnostics(
641 r#"
642struct S;
643impl S { fn method(&self, arg: u8) {} }
644
645 fn f() {
646 S.method();
647 } //^^^^^^^^^^ Expected 1 argument, found 0
648 "#,
649 );
650
651 check_diagnostics(
652 r#"
653struct S;
654impl S { fn method(&self, arg: u8) {} }
655
656fn f() {
657 S::method(&S, 0);
658 S.method(1);
659}
660"#,
661 );
662 }
663
664 #[test]
665 fn method_unknown_receiver() {
666 // note: this is incorrect code, so there might be errors on this in the
667 // future, but we shouldn't emit an argument count diagnostic here
668 check_diagnostics(
669 r#"
670trait Foo { fn method(&self, arg: usize) {} }
671
672fn f() {
673 let x;
674 x.method();
675}
676"#,
677 );
678 }
679
680 #[test]
681 fn tuple_struct() {
682 check_diagnostics(
683 r#"
684struct Tup(u8, u16);
685fn f() {
686 Tup(0);
687} //^^^^^^ Expected 2 arguments, found 1
688"#,
689 )
690 }
691
692 #[test]
693 fn enum_variant() {
694 check_diagnostics(
695 r#"
696enum En { Variant(u8, u16), }
697fn f() {
698 En::Variant(0);
699} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
700"#,
701 )
702 }
703
704 #[test]
705 fn enum_variant_type_macro() {
706 check_diagnostics(
707 r#"
708macro_rules! Type {
709 () => { u32 };
710}
711enum Foo {
712 Bar(Type![])
713}
714impl Foo {
715 fn new() {
716 Foo::Bar(0);
717 Foo::Bar(0, 1);
718 //^^^^^^^^^^^^^^ Expected 1 argument, found 2
719 Foo::Bar();
720 //^^^^^^^^^^ Expected 1 argument, found 0
721 }
722}
723 "#,
724 );
725 }
726
727 #[test]
728 fn varargs() {
729 check_diagnostics(
730 r#"
731extern "C" {
732 fn fixed(fixed: u8);
733 fn varargs(fixed: u8, ...);
734 fn varargs2(...);
735}
736
737fn f() {
738 unsafe {
739 fixed(0);
740 fixed(0, 1);
741 //^^^^^^^^^^^ Expected 1 argument, found 2
742 varargs(0);
743 varargs(0, 1);
744 varargs2();
745 varargs2(0);
746 varargs2(0, 1);
747 }
748}
749 "#,
750 )
751 }
752
753 #[test]
754 fn arg_count_lambda() {
755 check_diagnostics(
756 r#"
757fn main() {
758 let f = |()| ();
759 f();
760 //^^^ Expected 1 argument, found 0
761 f(());
762 f((), ());
763 //^^^^^^^^^ Expected 1 argument, found 2
764}
765"#,
766 )
767 }
768
769 #[test]
770 fn cfgd_out_call_arguments() {
771 check_diagnostics(
772 r#"
773struct C(#[cfg(FALSE)] ());
774impl C {
775 fn new() -> Self {
776 Self(
777 #[cfg(FALSE)]
778 (),
779 )
780 }
781
782 fn method(&self) {}
783}
784
785fn main() {
786 C::new().method(#[cfg(FALSE)] 0);
787}
788 "#,
789 );
790 }
791
792 #[test]
793 fn cfgd_out_fn_params() {
794 check_diagnostics(
795 r#"
796fn foo(#[cfg(NEVER)] x: ()) {}
797
798struct S;
799
800impl S {
801 fn method(#[cfg(NEVER)] self) {}
802 fn method2(#[cfg(NEVER)] self, arg: u8) {}
803 fn method3(self, #[cfg(NEVER)] arg: u8) {}
804}
805
806extern "C" {
807 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
808 fn varargs(#[cfg(not(NEVER))] ...);
809}
810
811fn main() {
812 foo();
813 S::method();
814 S::method2(0);
815 S::method3(S);
816 S.method3();
817 unsafe {
818 fixed(0);
819 varargs(1, 2, 3);
820 }
821}
822 "#,
823 )
824 }
825}
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index c8e1b23de..a30e42699 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -364,960 +364,3 @@ impl PatternFoldable for PatKind {
364 } 364 }
365 } 365 }
366} 366}
367
368#[cfg(test)]
369pub(super) mod tests {
370 mod report {
371 use std::any::Any;
372
373 use hir_def::{expr::PatId, DefWithBodyId};
374 use hir_expand::{HirFileId, InFile};
375 use syntax::SyntaxNodePtr;
376
377 use crate::{
378 db::HirDatabase,
379 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
380 };
381
382 /// In tests, match check bails out loudly.
383 /// This helps to catch incorrect tests that pass due to false negatives.
384 pub(crate) fn report_bail_out(
385 db: &dyn HirDatabase,
386 def: DefWithBodyId,
387 pat: PatId,
388 sink: &mut DiagnosticSink,
389 ) {
390 let (_, source_map) = db.body_with_source_map(def);
391 if let Ok(source_ptr) = source_map.pat_syntax(pat) {
392 let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into);
393 sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr });
394 }
395 }
396
397 #[derive(Debug)]
398 struct BailedOut {
399 file: HirFileId,
400 pat_syntax_ptr: SyntaxNodePtr,
401 }
402
403 impl Diagnostic for BailedOut {
404 fn code(&self) -> DiagnosticCode {
405 DiagnosticCode("internal:match-check-bailed-out")
406 }
407 fn message(&self) -> String {
408 format!("Internal: match check bailed out")
409 }
410 fn display_source(&self) -> InFile<SyntaxNodePtr> {
411 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
412 }
413 fn as_any(&self) -> &(dyn Any + Send + 'static) {
414 self
415 }
416 }
417 }
418
419 use crate::diagnostics::tests::check_diagnostics;
420
421 pub(crate) use self::report::report_bail_out;
422
423 #[test]
424 fn empty_tuple() {
425 check_diagnostics(
426 r#"
427fn main() {
428 match () { }
429 //^^ Missing match arm
430 match (()) { }
431 //^^^^ Missing match arm
432
433 match () { _ => (), }
434 match () { () => (), }
435 match (()) { (()) => (), }
436}
437"#,
438 );
439 }
440
441 #[test]
442 fn tuple_of_two_empty_tuple() {
443 check_diagnostics(
444 r#"
445fn main() {
446 match ((), ()) { }
447 //^^^^^^^^ Missing match arm
448
449 match ((), ()) { ((), ()) => (), }
450}
451"#,
452 );
453 }
454
455 #[test]
456 fn boolean() {
457 check_diagnostics(
458 r#"
459fn test_main() {
460 match false { }
461 //^^^^^ Missing match arm
462 match false { true => (), }
463 //^^^^^ Missing match arm
464 match (false, true) {}
465 //^^^^^^^^^^^^^ Missing match arm
466 match (false, true) { (true, true) => (), }
467 //^^^^^^^^^^^^^ Missing match arm
468 match (false, true) {
469 //^^^^^^^^^^^^^ Missing match arm
470 (false, true) => (),
471 (false, false) => (),
472 (true, false) => (),
473 }
474 match (false, true) { (true, _x) => (), }
475 //^^^^^^^^^^^^^ Missing match arm
476
477 match false { true => (), false => (), }
478 match (false, true) {
479 (false, _) => (),
480 (true, false) => (),
481 (_, true) => (),
482 }
483 match (false, true) {
484 (true, true) => (),
485 (true, false) => (),
486 (false, true) => (),
487 (false, false) => (),
488 }
489 match (false, true) {
490 (true, _x) => (),
491 (false, true) => (),
492 (false, false) => (),
493 }
494 match (false, true, false) {
495 (false, ..) => (),
496 (true, ..) => (),
497 }
498 match (false, true, false) {
499 (.., false) => (),
500 (.., true) => (),
501 }
502 match (false, true, false) { (..) => (), }
503}
504"#,
505 );
506 }
507
508 #[test]
509 fn tuple_of_tuple_and_bools() {
510 check_diagnostics(
511 r#"
512fn main() {
513 match (false, ((), false)) {}
514 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
515 match (false, ((), false)) { (true, ((), true)) => (), }
516 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
517 match (false, ((), false)) { (true, _) => (), }
518 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
519
520 match (false, ((), false)) {
521 (true, ((), true)) => (),
522 (true, ((), false)) => (),
523 (false, ((), true)) => (),
524 (false, ((), false)) => (),
525 }
526 match (false, ((), false)) {
527 (true, ((), true)) => (),
528 (true, ((), false)) => (),
529 (false, _) => (),
530 }
531}
532"#,
533 );
534 }
535
536 #[test]
537 fn enums() {
538 check_diagnostics(
539 r#"
540enum Either { A, B, }
541
542fn main() {
543 match Either::A { }
544 //^^^^^^^^^ Missing match arm
545 match Either::B { Either::A => (), }
546 //^^^^^^^^^ Missing match arm
547
548 match &Either::B {
549 //^^^^^^^^^^ Missing match arm
550 Either::A => (),
551 }
552
553 match Either::B {
554 Either::A => (), Either::B => (),
555 }
556 match &Either::B {
557 Either::A => (), Either::B => (),
558 }
559}
560"#,
561 );
562 }
563
564 #[test]
565 fn enum_containing_bool() {
566 check_diagnostics(
567 r#"
568enum Either { A(bool), B }
569
570fn main() {
571 match Either::B { }
572 //^^^^^^^^^ Missing match arm
573 match Either::B {
574 //^^^^^^^^^ Missing match arm
575 Either::A(true) => (), Either::B => ()
576 }
577
578 match Either::B {
579 Either::A(true) => (),
580 Either::A(false) => (),
581 Either::B => (),
582 }
583 match Either::B {
584 Either::B => (),
585 _ => (),
586 }
587 match Either::B {
588 Either::A(_) => (),
589 Either::B => (),
590 }
591
592}
593 "#,
594 );
595 }
596
597 #[test]
598 fn enum_different_sizes() {
599 check_diagnostics(
600 r#"
601enum Either { A(bool), B(bool, bool) }
602
603fn main() {
604 match Either::A(false) {
605 //^^^^^^^^^^^^^^^^ Missing match arm
606 Either::A(_) => (),
607 Either::B(false, _) => (),
608 }
609
610 match Either::A(false) {
611 Either::A(_) => (),
612 Either::B(true, _) => (),
613 Either::B(false, _) => (),
614 }
615 match Either::A(false) {
616 Either::A(true) | Either::A(false) => (),
617 Either::B(true, _) => (),
618 Either::B(false, _) => (),
619 }
620}
621"#,
622 );
623 }
624
625 #[test]
626 fn tuple_of_enum_no_diagnostic() {
627 check_diagnostics(
628 r#"
629enum Either { A(bool), B(bool, bool) }
630enum Either2 { C, D }
631
632fn main() {
633 match (Either::A(false), Either2::C) {
634 (Either::A(true), _) | (Either::A(false), _) => (),
635 (Either::B(true, _), Either2::C) => (),
636 (Either::B(false, _), Either2::C) => (),
637 (Either::B(_, _), Either2::D) => (),
638 }
639}
640"#,
641 );
642 }
643
644 #[test]
645 fn or_pattern_no_diagnostic() {
646 check_diagnostics(
647 r#"
648enum Either {A, B}
649
650fn main() {
651 match (Either::A, Either::B) {
652 (Either::A | Either::B, _) => (),
653 }
654}"#,
655 )
656 }
657
658 #[test]
659 fn mismatched_types() {
660 // Match statements with arms that don't match the
661 // expression pattern do not fire this diagnostic.
662 check_diagnostics(
663 r#"
664enum Either { A, B }
665enum Either2 { C, D }
666
667fn main() {
668 match Either::A {
669 Either2::C => (),
670 // ^^^^^^^^^^ Internal: match check bailed out
671 Either2::D => (),
672 }
673 match (true, false) {
674 (true, false, true) => (),
675 // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out
676 (true) => (),
677 }
678 match (true, false) { (true,) => {} }
679 // ^^^^^^^ Internal: match check bailed out
680 match (0) { () => () }
681 // ^^ Internal: match check bailed out
682 match Unresolved::Bar { Unresolved::Baz => () }
683}
684 "#,
685 );
686 }
687
688 #[test]
689 fn mismatched_types_in_or_patterns() {
690 check_diagnostics(
691 r#"
692fn main() {
693 match false { true | () => {} }
694 // ^^^^^^^^^ Internal: match check bailed out
695 match (false,) { (true | (),) => {} }
696 // ^^^^^^^^^^^^ Internal: match check bailed out
697}
698"#,
699 );
700 }
701
702 #[test]
703 fn malformed_match_arm_tuple_enum_missing_pattern() {
704 // We are testing to be sure we don't panic here when the match
705 // arm `Either::B` is missing its pattern.
706 check_diagnostics(
707 r#"
708enum Either { A, B(u32) }
709
710fn main() {
711 match Either::A {
712 Either::A => (),
713 Either::B() => (),
714 }
715}
716"#,
717 );
718 }
719
720 #[test]
721 fn malformed_match_arm_extra_fields() {
722 check_diagnostics(
723 r#"
724enum A { B(isize, isize), C }
725fn main() {
726 match A::B(1, 2) {
727 A::B(_, _, _) => (),
728 // ^^^^^^^^^^^^^ Internal: match check bailed out
729 }
730 match A::B(1, 2) {
731 A::C(_) => (),
732 // ^^^^^^^ Internal: match check bailed out
733 }
734}
735"#,
736 );
737 }
738
739 #[test]
740 fn expr_diverges() {
741 check_diagnostics(
742 r#"
743enum Either { A, B }
744
745fn main() {
746 match loop {} {
747 Either::A => (),
748 // ^^^^^^^^^ Internal: match check bailed out
749 Either::B => (),
750 }
751 match loop {} {
752 Either::A => (),
753 // ^^^^^^^^^ Internal: match check bailed out
754 }
755 match loop { break Foo::A } {
756 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
757 Either::A => (),
758 }
759 match loop { break Foo::A } {
760 Either::A => (),
761 Either::B => (),
762 }
763}
764"#,
765 );
766 }
767
768 #[test]
769 fn expr_partially_diverges() {
770 check_diagnostics(
771 r#"
772enum Either<T> { A(T), B }
773
774fn foo() -> Either<!> { Either::B }
775fn main() -> u32 {
776 match foo() {
777 Either::A(val) => val,
778 Either::B => 0,
779 }
780}
781"#,
782 );
783 }
784
785 #[test]
786 fn enum_record() {
787 check_diagnostics(
788 r#"
789enum Either { A { foo: bool }, B }
790
791fn main() {
792 let a = Either::A { foo: true };
793 match a { }
794 //^ Missing match arm
795 match a { Either::A { foo: true } => () }
796 //^ Missing match arm
797 match a {
798 Either::A { } => (),
799 //^^^^^^^^^ Missing structure fields:
800 // | - foo
801 Either::B => (),
802 }
803 match a {
804 //^ Missing match arm
805 Either::A { } => (),
806 } //^^^^^^^^^ Missing structure fields:
807 // | - foo
808
809 match a {
810 Either::A { foo: true } => (),
811 Either::A { foo: false } => (),
812 Either::B => (),
813 }
814 match a {
815 Either::A { foo: _ } => (),
816 Either::B => (),
817 }
818}
819"#,
820 );
821 }
822
823 #[test]
824 fn enum_record_fields_out_of_order() {
825 check_diagnostics(
826 r#"
827enum Either {
828 A { foo: bool, bar: () },
829 B,
830}
831
832fn main() {
833 let a = Either::A { foo: true, bar: () };
834 match a {
835 //^ Missing match arm
836 Either::A { bar: (), foo: false } => (),
837 Either::A { foo: true, bar: () } => (),
838 }
839
840 match a {
841 Either::A { bar: (), foo: false } => (),
842 Either::A { foo: true, bar: () } => (),
843 Either::B => (),
844 }
845}
846"#,
847 );
848 }
849
850 #[test]
851 fn enum_record_ellipsis() {
852 check_diagnostics(
853 r#"
854enum Either {
855 A { foo: bool, bar: bool },
856 B,
857}
858
859fn main() {
860 let a = Either::B;
861 match a {
862 //^ Missing match arm
863 Either::A { foo: true, .. } => (),
864 Either::B => (),
865 }
866 match a {
867 //^ Missing match arm
868 Either::A { .. } => (),
869 }
870
871 match a {
872 Either::A { foo: true, .. } => (),
873 Either::A { foo: false, .. } => (),
874 Either::B => (),
875 }
876
877 match a {
878 Either::A { .. } => (),
879 Either::B => (),
880 }
881}
882"#,
883 );
884 }
885
886 #[test]
887 fn enum_tuple_partial_ellipsis() {
888 check_diagnostics(
889 r#"
890enum Either {
891 A(bool, bool, bool, bool),
892 B,
893}
894
895fn main() {
896 match Either::B {
897 //^^^^^^^^^ Missing match arm
898 Either::A(true, .., true) => (),
899 Either::A(true, .., false) => (),
900 Either::A(false, .., false) => (),
901 Either::B => (),
902 }
903 match Either::B {
904 //^^^^^^^^^ Missing match arm
905 Either::A(true, .., true) => (),
906 Either::A(true, .., false) => (),
907 Either::A(.., true) => (),
908 Either::B => (),
909 }
910
911 match Either::B {
912 Either::A(true, .., true) => (),
913 Either::A(true, .., false) => (),
914 Either::A(false, .., true) => (),
915 Either::A(false, .., false) => (),
916 Either::B => (),
917 }
918 match Either::B {
919 Either::A(true, .., true) => (),
920 Either::A(true, .., false) => (),
921 Either::A(.., true) => (),
922 Either::A(.., false) => (),
923 Either::B => (),
924 }
925}
926"#,
927 );
928 }
929
930 #[test]
931 fn never() {
932 check_diagnostics(
933 r#"
934enum Never {}
935
936fn enum_(never: Never) {
937 match never {}
938}
939fn enum_ref(never: &Never) {
940 match never {}
941 //^^^^^ Missing match arm
942}
943fn bang(never: !) {
944 match never {}
945}
946"#,
947 );
948 }
949
950 #[test]
951 fn unknown_type() {
952 check_diagnostics(
953 r#"
954enum Option<T> { Some(T), None }
955
956fn main() {
957 // `Never` is deliberately not defined so that it's an uninferred type.
958 match Option::<Never>::None {
959 None => (),
960 Some(never) => match never {},
961 // ^^^^^^^^^^^ Internal: match check bailed out
962 }
963 match Option::<Never>::None {
964 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
965 Option::Some(_never) => {},
966 }
967}
968"#,
969 );
970 }
971
972 #[test]
973 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
974 check_diagnostics(
975 r#"
976fn main() {
977 match (false, true, false) {
978 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
979 (false, ..) => (),
980 }
981}"#,
982 );
983 }
984
985 #[test]
986 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
987 check_diagnostics(
988 r#"
989fn main() {
990 match (false, true, false) {
991 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
992 (.., false) => (),
993 }
994}"#,
995 );
996 }
997
998 #[test]
999 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
1000 check_diagnostics(
1001 r#"
1002fn main() {
1003 match (false, true, false) {
1004 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1005 (true, .., false) => (),
1006 }
1007}"#,
1008 );
1009 }
1010
1011 #[test]
1012 fn record_struct() {
1013 check_diagnostics(
1014 r#"struct Foo { a: bool }
1015fn main(f: Foo) {
1016 match f {}
1017 //^ Missing match arm
1018 match f { Foo { a: true } => () }
1019 //^ Missing match arm
1020 match &f { Foo { a: true } => () }
1021 //^^ Missing match arm
1022 match f { Foo { a: _ } => () }
1023 match f {
1024 Foo { a: true } => (),
1025 Foo { a: false } => (),
1026 }
1027 match &f {
1028 Foo { a: true } => (),
1029 Foo { a: false } => (),
1030 }
1031}
1032"#,
1033 );
1034 }
1035
1036 #[test]
1037 fn tuple_struct() {
1038 check_diagnostics(
1039 r#"struct Foo(bool);
1040fn main(f: Foo) {
1041 match f {}
1042 //^ Missing match arm
1043 match f { Foo(true) => () }
1044 //^ Missing match arm
1045 match f {
1046 Foo(true) => (),
1047 Foo(false) => (),
1048 }
1049}
1050"#,
1051 );
1052 }
1053
1054 #[test]
1055 fn unit_struct() {
1056 check_diagnostics(
1057 r#"struct Foo;
1058fn main(f: Foo) {
1059 match f {}
1060 //^ Missing match arm
1061 match f { Foo => () }
1062}
1063"#,
1064 );
1065 }
1066
1067 #[test]
1068 fn record_struct_ellipsis() {
1069 check_diagnostics(
1070 r#"struct Foo { foo: bool, bar: bool }
1071fn main(f: Foo) {
1072 match f { Foo { foo: true, .. } => () }
1073 //^ Missing match arm
1074 match f {
1075 //^ Missing match arm
1076 Foo { foo: true, .. } => (),
1077 Foo { bar: false, .. } => ()
1078 }
1079 match f { Foo { .. } => () }
1080 match f {
1081 Foo { foo: true, .. } => (),
1082 Foo { foo: false, .. } => ()
1083 }
1084}
1085"#,
1086 );
1087 }
1088
1089 #[test]
1090 fn internal_or() {
1091 check_diagnostics(
1092 r#"
1093fn main() {
1094 enum Either { A(bool), B }
1095 match Either::B {
1096 //^^^^^^^^^ Missing match arm
1097 Either::A(true | false) => (),
1098 }
1099}
1100"#,
1101 );
1102 }
1103
1104 #[test]
1105 fn no_panic_at_unimplemented_subpattern_type() {
1106 check_diagnostics(
1107 r#"
1108struct S { a: char}
1109fn main(v: S) {
1110 match v { S{ a } => {} }
1111 match v { S{ a: _x } => {} }
1112 match v { S{ a: 'a' } => {} }
1113 //^^^^^^^^^^^ Internal: match check bailed out
1114 match v { S{..} => {} }
1115 match v { _ => {} }
1116 match v { }
1117 //^ Missing match arm
1118}
1119"#,
1120 );
1121 }
1122
1123 #[test]
1124 fn binding() {
1125 check_diagnostics(
1126 r#"
1127fn main() {
1128 match true {
1129 _x @ true => {}
1130 false => {}
1131 }
1132 match true { _x @ true => {} }
1133 //^^^^ Missing match arm
1134}
1135"#,
1136 );
1137 }
1138
1139 #[test]
1140 fn binding_ref_has_correct_type() {
1141 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
1142 // If that's not true match checking will panic with "incompatible constructors"
1143 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
1144 check_diagnostics(
1145 r#"
1146enum Foo { A }
1147fn main() {
1148 // FIXME: this should not bail out but current behavior is such as the old algorithm.
1149 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
1150 match Foo::A {
1151 ref _x => {}
1152 // ^^^^^^ Internal: match check bailed out
1153 Foo::A => {}
1154 }
1155 match (true,) {
1156 (ref _x,) => {}
1157 (true,) => {}
1158 }
1159}
1160"#,
1161 );
1162 }
1163
1164 #[test]
1165 fn enum_non_exhaustive() {
1166 check_diagnostics(
1167 r#"
1168//- /lib.rs crate:lib
1169#[non_exhaustive]
1170pub enum E { A, B }
1171fn _local() {
1172 match E::A { _ => {} }
1173 match E::A {
1174 E::A => {}
1175 E::B => {}
1176 }
1177 match E::A {
1178 E::A | E::B => {}
1179 }
1180}
1181
1182//- /main.rs crate:main deps:lib
1183use lib::E;
1184fn main() {
1185 match E::A { _ => {} }
1186 match E::A {
1187 //^^^^ Missing match arm
1188 E::A => {}
1189 E::B => {}
1190 }
1191 match E::A {
1192 //^^^^ Missing match arm
1193 E::A | E::B => {}
1194 }
1195}
1196"#,
1197 );
1198 }
1199
1200 #[test]
1201 fn match_guard() {
1202 check_diagnostics(
1203 r#"
1204fn main() {
1205 match true {
1206 true if false => {}
1207 true => {}
1208 false => {}
1209 }
1210 match true {
1211 //^^^^ Missing match arm
1212 true if false => {}
1213 false => {}
1214}
1215"#,
1216 );
1217 }
1218
1219 #[test]
1220 fn pattern_type_is_of_substitution() {
1221 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
1222 check_diagnostics(
1223 r#"
1224struct Foo<T>(T);
1225struct Bar;
1226fn main() {
1227 match Foo(Bar) {
1228 _ | Foo(Bar) => {}
1229 }
1230}
1231"#,
1232 );
1233 }
1234
1235 #[test]
1236 fn record_struct_no_such_field() {
1237 check_diagnostics(
1238 r#"
1239struct Foo { }
1240fn main(f: Foo) {
1241 match f { Foo { bar } => () }
1242 // ^^^^^^^^^^^ Internal: match check bailed out
1243}
1244"#,
1245 );
1246 }
1247
1248 #[test]
1249 fn match_ergonomics_issue_9095() {
1250 check_diagnostics(
1251 r#"
1252enum Foo<T> { A(T) }
1253fn main() {
1254 match &Foo::A(true) {
1255 _ => {}
1256 Foo::A(_) => {}
1257 }
1258}
1259"#,
1260 );
1261 }
1262
1263 mod false_negatives {
1264 //! The implementation of match checking here is a work in progress. As we roll this out, we
1265 //! prefer false negatives to false positives (ideally there would be no false positives). This
1266 //! test module should document known false negatives. Eventually we will have a complete
1267 //! implementation of match checking and this module will be empty.
1268 //!
1269 //! The reasons for documenting known false negatives:
1270 //!
1271 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1272 //! 2. It ensures the code doesn't panic when handling these cases.
1273 use super::*;
1274
1275 #[test]
1276 fn integers() {
1277 // We don't currently check integer exhaustiveness.
1278 check_diagnostics(
1279 r#"
1280fn main() {
1281 match 5 {
1282 10 => (),
1283 // ^^ Internal: match check bailed out
1284 11..20 => (),
1285 }
1286}
1287"#,
1288 );
1289 }
1290
1291 #[test]
1292 fn reference_patterns_at_top_level() {
1293 check_diagnostics(
1294 r#"
1295fn main() {
1296 match &false {
1297 &true => {}
1298 // ^^^^^ Internal: match check bailed out
1299 }
1300}
1301 "#,
1302 );
1303 }
1304
1305 #[test]
1306 fn reference_patterns_in_fields() {
1307 check_diagnostics(
1308 r#"
1309fn main() {
1310 match (&false,) {
1311 (true,) => {}
1312 // ^^^^^^^ Internal: match check bailed out
1313 }
1314 match (&false,) {
1315 (&true,) => {}
1316 // ^^^^^^^^ Internal: match check bailed out
1317 }
1318}
1319 "#,
1320 );
1321 }
1322 }
1323}
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
index 1f4219b42..471cd4921 100644
--- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -528,7 +528,7 @@ impl SplitWildcard {
528 smallvec![NonExhaustive] 528 smallvec![NonExhaustive]
529 } 529 }
530 TyKind::Never => SmallVec::new(), 530 TyKind::Never => SmallVec::new(),
531 _ if cx.is_uninhabited(&pcx.ty) => SmallVec::new(), 531 _ if cx.is_uninhabited(pcx.ty) => SmallVec::new(),
532 TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], 532 TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single],
533 // This type is one for which we cannot list constructors, like `str` or `f64`. 533 // This type is one for which we cannot list constructors, like `str` or `f64`.
534 _ => smallvec![NonExhaustive], 534 _ => smallvec![NonExhaustive],
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
index bd76a606c..8451f9df5 100644
--- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
@@ -1,5 +1,5 @@
1//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) 1//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22)
2//! https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs 2//! <https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs>
3//! 3//!
4//! ----- 4//! -----
5//! 5//!
@@ -645,7 +645,7 @@ impl SubPatSet {
645 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { 645 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => {
646 s_set.retain(|i, s_sub_set| { 646 s_set.retain(|i, s_sub_set| {
647 // Missing entries count as full. 647 // Missing entries count as full.
648 let o_sub_set = o_set.remove(&i).unwrap_or(Full); 648 let o_sub_set = o_set.remove(i).unwrap_or(Full);
649 s_sub_set.union(o_sub_set); 649 s_sub_set.union(o_sub_set);
650 // We drop full entries. 650 // We drop full entries.
651 !s_sub_set.is_full() 651 !s_sub_set.is_full()
@@ -656,7 +656,7 @@ impl SubPatSet {
656 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { 656 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => {
657 s_set.retain(|i, s_sub_set| { 657 s_set.retain(|i, s_sub_set| {
658 // Missing entries count as empty. 658 // Missing entries count as empty.
659 let o_sub_set = o_set.remove(&i).unwrap_or(Empty); 659 let o_sub_set = o_set.remove(i).unwrap_or(Empty);
660 s_sub_set.union(o_sub_set); 660 s_sub_set.union(o_sub_set);
661 // We drop empty entries. 661 // We drop empty entries.
662 !s_sub_set.is_empty() 662 !s_sub_set.is_empty()
@@ -898,7 +898,7 @@ impl Usefulness {
898 } else { 898 } else {
899 witnesses 899 witnesses
900 .into_iter() 900 .into_iter()
901 .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) 901 .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns))
902 .collect() 902 .collect()
903 }; 903 };
904 WithWitnesses(new_witnesses) 904 WithWitnesses(new_witnesses)
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index c3c483425..777f347b8 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -1,8 +1,6 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing 1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks. 2//! unsafe blocks.
3 3
4use std::sync::Arc;
5
6use hir_def::{ 4use hir_def::{
7 body::Body, 5 body::Body,
8 expr::{Expr, ExprId, UnaryOp}, 6 expr::{Expr, ExprId, UnaryOp},
@@ -10,60 +8,32 @@ use hir_def::{
10 DefWithBodyId, 8 DefWithBodyId,
11}; 9};
12 10
13use crate::{ 11use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16};
17 12
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 13pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
19 owner: DefWithBodyId, 14 let infer = db.infer(def);
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23 15
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 16 let is_unsafe = match def {
25 pub(super) fn new( 17 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
26 owner: DefWithBodyId, 18 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
27 infer: Arc<InferenceResult>, 19 };
28 sink: &'a mut DiagnosticSink<'b>, 20 if is_unsafe {
29 ) -> UnsafeValidator<'a, 'b> { 21 return Vec::new();
30 UnsafeValidator { owner, infer, sink }
31 } 22 }
32 23
33 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 24 unsafe_expressions(db, &infer, def)
34 let def = self.owner; 25 .into_iter()
35 let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); 26 .filter(|it| !it.inside_unsafe_block)
36 let is_unsafe = match self.owner { 27 .map(|it| it.expr)
37 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), 28 .collect()
38 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
39 };
40 if is_unsafe
41 || unsafe_expressions
42 .iter()
43 .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
44 .count()
45 == 0
46 {
47 return;
48 }
49
50 let (_, body_source) = db.body_with_source_map(def);
51 for unsafe_expr in unsafe_expressions {
52 if !unsafe_expr.inside_unsafe_block {
53 if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) {
54 self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
55 }
56 }
57 }
58 }
59} 29}
60 30
61pub(crate) struct UnsafeExpr { 31struct UnsafeExpr {
62 pub(crate) expr: ExprId, 32 pub(crate) expr: ExprId,
63 pub(crate) inside_unsafe_block: bool, 33 pub(crate) inside_unsafe_block: bool,
64} 34}
65 35
66pub(crate) fn unsafe_expressions( 36fn unsafe_expressions(
67 db: &dyn HirDatabase, 37 db: &dyn HirDatabase,
68 infer: &InferenceResult, 38 infer: &InferenceResult,
69 def: DefWithBodyId, 39 def: DefWithBodyId,
@@ -126,92 +96,3 @@ fn walk_unsafe(
126 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); 96 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
127 }); 97 });
128} 98}
129
130#[cfg(test)]
131mod tests {
132 use crate::diagnostics::tests::check_diagnostics;
133
134 #[test]
135 fn missing_unsafe_diagnostic_with_raw_ptr() {
136 check_diagnostics(
137 r#"
138fn main() {
139 let x = &5 as *const usize;
140 unsafe { let y = *x; }
141 let z = *x;
142} //^^ This operation is unsafe and requires an unsafe function or block
143"#,
144 )
145 }
146
147 #[test]
148 fn missing_unsafe_diagnostic_with_unsafe_call() {
149 check_diagnostics(
150 r#"
151struct HasUnsafe;
152
153impl HasUnsafe {
154 unsafe fn unsafe_fn(&self) {
155 let x = &5 as *const usize;
156 let y = *x;
157 }
158}
159
160unsafe fn unsafe_fn() {
161 let x = &5 as *const usize;
162 let y = *x;
163}
164
165fn main() {
166 unsafe_fn();
167 //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
168 HasUnsafe.unsafe_fn();
169 //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
170 unsafe {
171 unsafe_fn();
172 HasUnsafe.unsafe_fn();
173 }
174}
175"#,
176 );
177 }
178
179 #[test]
180 fn missing_unsafe_diagnostic_with_static_mut() {
181 check_diagnostics(
182 r#"
183struct Ty {
184 a: u8,
185}
186
187static mut STATIC_MUT: Ty = Ty { a: 0 };
188
189fn main() {
190 let x = STATIC_MUT.a;
191 //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
192 unsafe {
193 let x = STATIC_MUT.a;
194 }
195}
196"#,
197 );
198 }
199
200 #[test]
201 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
202 check_diagnostics(
203 r#"
204extern "rust-intrinsic" {
205 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207}
208
209fn main() {
210 let _ = bitreverse(12);
211 let _ = floorf32(12.0);
212 //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
213}
214"#,
215 );
216 }
217}
diff --git a/crates/hir_ty/src/diagnostics_sink.rs b/crates/hir_ty/src/diagnostics_sink.rs
deleted file mode 100644
index 084fa8b06..000000000
--- a/crates/hir_ty/src/diagnostics_sink.rs
+++ /dev/null
@@ -1,109 +0,0 @@
1//! Semantic errors and warnings.
2//!
3//! The `Diagnostic` trait defines a trait object which can represent any
4//! diagnostic.
5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is used internally).
9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them
11//! directly in salsa. For this reason, every hir subsytem defines it's own
12//! strongly-typed closed set of diagnostics which use hir ids internally, are
13//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a
14//! subsystem provides a separate, non-query-based API which can walk all stored
15//! values and transform them into instances of `Diagnostic`.
16
17use std::{any::Any, fmt};
18
19use hir_expand::InFile;
20use syntax::SyntaxNodePtr;
21
22#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct DiagnosticCode(pub &'static str);
24
25impl DiagnosticCode {
26 pub fn as_str(&self) -> &str {
27 self.0
28 }
29}
30
31pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
32 fn code(&self) -> DiagnosticCode;
33 fn message(&self) -> String;
34 /// Source element that triggered the diagnostics.
35 ///
36 /// Note that this should reflect "semantics", rather than specific span we
37 /// want to highlight. When rendering the diagnostics into an error message,
38 /// the IDE will fetch the `SyntaxNode` and will narrow the span
39 /// appropriately.
40 fn display_source(&self) -> InFile<SyntaxNodePtr>;
41 fn as_any(&self) -> &(dyn Any + Send + 'static);
42 fn is_experimental(&self) -> bool {
43 false
44 }
45}
46
47pub struct DiagnosticSink<'a> {
48 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
49 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
50 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
51}
52
53impl<'a> DiagnosticSink<'a> {
54 pub fn push(&mut self, d: impl Diagnostic) {
55 let d: &dyn Diagnostic = &d;
56 self._push(d);
57 }
58
59 fn _push(&mut self, d: &dyn Diagnostic) {
60 for filter in &mut self.filters {
61 if !filter(d) {
62 return;
63 }
64 }
65 for cb in &mut self.callbacks {
66 match cb(d) {
67 Ok(()) => return,
68 Err(()) => (),
69 }
70 }
71 (self.default_callback)(d)
72 }
73}
74
75pub struct DiagnosticSinkBuilder<'a> {
76 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
77 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
78}
79
80impl<'a> DiagnosticSinkBuilder<'a> {
81 pub fn new() -> Self {
82 Self { callbacks: Vec::new(), filters: Vec::new() }
83 }
84
85 pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self {
86 self.filters.push(Box::new(cb));
87 self
88 }
89
90 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
91 let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() {
92 Some(d) => {
93 cb(d);
94 Ok(())
95 }
96 None => Err(()),
97 };
98 self.callbacks.push(Box::new(cb));
99 self
100 }
101
102 pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> {
103 DiagnosticSink {
104 callbacks: self.callbacks,
105 filters: self.filters,
106 default_callback: Box::new(default_callback),
107 }
108 }
109}
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 0e9f777da..63f37c0ab 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -35,11 +35,9 @@ use stdx::impl_from;
35use syntax::SmolStr; 35use syntax::SmolStr;
36 36
37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; 37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
38use crate::diagnostics_sink::DiagnosticSink;
39use crate::{ 38use crate::{
40 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, 39 db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
41 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, 40 Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
42 TyBuilder, TyExt, TyKind,
43}; 41};
44 42
45// This lint has a false positive here. See the link below for details. 43// This lint has a false positive here. See the link below for details.
@@ -80,7 +78,7 @@ enum ExprOrPatId {
80impl_from!(ExprId, PatId for ExprOrPatId); 78impl_from!(ExprId, PatId for ExprOrPatId);
81 79
82/// Binding modes inferred for patterns. 80/// Binding modes inferred for patterns.
83/// https://doc.rust-lang.org/reference/patterns.html#binding-modes 81/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
84#[derive(Copy, Clone, Debug, Eq, PartialEq)] 82#[derive(Copy, Clone, Debug, Eq, PartialEq)]
85enum BindingMode { 83enum BindingMode {
86 Move, 84 Move,
@@ -111,6 +109,12 @@ pub(crate) struct InferOk {
111pub(crate) struct TypeError; 109pub(crate) struct TypeError;
112pub(crate) type InferResult = Result<InferOk, TypeError>; 110pub(crate) type InferResult = Result<InferOk, TypeError>;
113 111
112#[derive(Debug, PartialEq, Eq, Clone)]
113pub enum InferenceDiagnostic {
114 NoSuchField { expr: ExprId },
115 BreakOutsideOfLoop { expr: ExprId },
116}
117
114/// A mismatch between an expected and an inferred type. 118/// A mismatch between an expected and an inferred type.
115#[derive(Clone, PartialEq, Eq, Debug, Hash)] 119#[derive(Clone, PartialEq, Eq, Debug, Hash)]
116pub struct TypeMismatch { 120pub struct TypeMismatch {
@@ -140,7 +144,7 @@ pub struct InferenceResult {
140 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, 144 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
141 /// For each associated item record what it resolves to 145 /// For each associated item record what it resolves to
142 assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, 146 assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
143 diagnostics: Vec<InferenceDiagnostic>, 147 pub diagnostics: Vec<InferenceDiagnostic>,
144 pub type_of_expr: ArenaMap<ExprId, Ty>, 148 pub type_of_expr: ArenaMap<ExprId, Ty>,
145 /// For each pattern record the type it resolves to. 149 /// For each pattern record the type it resolves to.
146 /// 150 ///
@@ -191,14 +195,6 @@ impl InferenceResult {
191 _ => None, 195 _ => None,
192 }) 196 })
193 } 197 }
194 pub fn add_diagnostics(
195 &self,
196 db: &dyn HirDatabase,
197 owner: DefWithBodyId,
198 sink: &mut DiagnosticSink,
199 ) {
200 self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink))
201 }
202} 198}
203 199
204impl Index<ExprId> for InferenceResult { 200impl Index<ExprId> for InferenceResult {
@@ -765,6 +761,38 @@ impl Expectation {
765 Expectation::RValueLikeUnsized(_) | Expectation::None => None, 761 Expectation::RValueLikeUnsized(_) | Expectation::None => None,
766 } 762 }
767 } 763 }
764
765 /// Comment copied from rustc:
766 /// Disregard "castable to" expectations because they
767 /// can lead us astray. Consider for example `if cond
768 /// {22} else {c} as u8` -- if we propagate the
769 /// "castable to u8" constraint to 22, it will pick the
770 /// type 22u8, which is overly constrained (c might not
771 /// be a u8). In effect, the problem is that the
772 /// "castable to" expectation is not the tightest thing
773 /// we can say, so we want to drop it in this case.
774 /// The tightest thing we can say is "must unify with
775 /// else branch". Note that in the case of a "has type"
776 /// constraint, this limitation does not hold.
777 ///
778 /// If the expected type is just a type variable, then don't use
779 /// an expected type. Otherwise, we might write parts of the type
780 /// when checking the 'then' block which are incompatible with the
781 /// 'else' branch.
782 fn adjust_for_branches(&self, table: &mut unify::InferenceTable) -> Expectation {
783 match self {
784 Expectation::HasType(ety) => {
785 let ety = table.resolve_ty_shallow(ety);
786 if !ety.is_ty_var() {
787 Expectation::HasType(ety)
788 } else {
789 Expectation::None
790 }
791 }
792 Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()),
793 _ => Expectation::None,
794 }
795 }
768} 796}
769 797
770#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 798#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -804,43 +832,3 @@ impl std::ops::BitOrAssign for Diverges {
804 *self = *self | other; 832 *self = *self | other;
805 } 833 }
806} 834}
807
808mod diagnostics {
809 use hir_def::{expr::ExprId, DefWithBodyId};
810
811 use crate::{
812 db::HirDatabase,
813 diagnostics::{BreakOutsideOfLoop, NoSuchField},
814 diagnostics_sink::DiagnosticSink,
815 };
816
817 #[derive(Debug, PartialEq, Eq, Clone)]
818 pub(super) enum InferenceDiagnostic {
819 NoSuchField { expr: ExprId },
820 BreakOutsideOfLoop { expr: ExprId },
821 }
822
823 impl InferenceDiagnostic {
824 pub(super) fn add_to(
825 &self,
826 db: &dyn HirDatabase,
827 owner: DefWithBodyId,
828 sink: &mut DiagnosticSink,
829 ) {
830 match self {
831 InferenceDiagnostic::NoSuchField { expr } => {
832 let (_, source_map) = db.body_with_source_map(owner);
833 let field = source_map.field_syntax(*expr);
834 sink.push(NoSuchField { file: field.file_id, field: field.value })
835 }
836 InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
837 let (_, source_map) = db.body_with_source_map(owner);
838 let ptr = source_map
839 .expr_syntax(*expr)
840 .expect("break outside of loop in synthetic syntax");
841 sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
842 }
843 }
844 }
845 }
846}
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 03b97e7db..4b7f31521 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -2,8 +2,8 @@
2//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions 2//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions
3//! like going from `&Vec<T>` to `&[T]`. 3//! like going from `&Vec<T>` to `&[T]`.
4//! 4//!
5//! See https://doc.rust-lang.org/nomicon/coercions.html and 5//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
6//! librustc_typeck/check/coercion.rs. 6//! `librustc_typeck/check/coercion.rs`.
7 7
8use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; 8use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
9use hir_def::{expr::ExprId, lang_item::LangItemTarget}; 9use hir_def::{expr::ExprId, lang_item::LangItemTarget};
@@ -109,7 +109,7 @@ impl<'a> InferenceContext<'a> {
109 } 109 }
110 110
111 // Consider coercing the subtype to a DST 111 // Consider coercing the subtype to a DST
112 if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { 112 if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) {
113 return Ok(ret); 113 return Ok(ret);
114 } 114 }
115 115
@@ -331,7 +331,7 @@ impl<'a> InferenceContext<'a> {
331 331
332 /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` 332 /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
333 /// 333 ///
334 /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html 334 /// See: <https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html>
335 fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { 335 fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult {
336 // These 'if' statements require some explanation. 336 // These 'if' statements require some explanation.
337 // The `CoerceUnsized` trait is special - it is only 337 // The `CoerceUnsized` trait is special - it is only
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index f73bf43b2..5ea2e5934 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -54,7 +54,7 @@ impl<'a> InferenceContext<'a> {
54 /// Infer type of expression with possibly implicit coerce to the expected type. 54 /// Infer type of expression with possibly implicit coerce to the expected type.
55 /// Return the type after possible coercion. 55 /// Return the type after possible coercion.
56 pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { 56 pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
57 let ty = self.infer_expr_inner(expr, &expected); 57 let ty = self.infer_expr_inner(expr, expected);
58 let ty = if let Some(target) = expected.only_has_type(&mut self.table) { 58 let ty = if let Some(target) = expected.only_has_type(&mut self.table) {
59 if !self.coerce(&ty, &target) { 59 if !self.coerce(&ty, &target) {
60 self.result 60 self.result
@@ -135,11 +135,11 @@ impl<'a> InferenceContext<'a> {
135 let mut both_arms_diverge = Diverges::Always; 135 let mut both_arms_diverge = Diverges::Always;
136 136
137 let mut result_ty = self.table.new_type_var(); 137 let mut result_ty = self.table.new_type_var();
138 let then_ty = self.infer_expr_inner(*then_branch, &expected); 138 let then_ty = self.infer_expr_inner(*then_branch, expected);
139 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); 139 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
140 result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); 140 result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty);
141 let else_ty = match else_branch { 141 let else_ty = match else_branch {
142 Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), 142 Some(else_branch) => self.infer_expr_inner(*else_branch, expected),
143 None => TyBuilder::unit(), 143 None => TyBuilder::unit(),
144 }; 144 };
145 both_arms_diverge &= self.diverges; 145 both_arms_diverge &= self.diverges;
@@ -327,20 +327,19 @@ impl<'a> InferenceContext<'a> {
327 self.normalize_associated_types_in(ret_ty) 327 self.normalize_associated_types_in(ret_ty)
328 } 328 }
329 Expr::MethodCall { receiver, args, method_name, generic_args } => self 329 Expr::MethodCall { receiver, args, method_name, generic_args } => self
330 .infer_method_call( 330 .infer_method_call(tgt_expr, *receiver, args, method_name, generic_args.as_deref()),
331 tgt_expr,
332 *receiver,
333 &args,
334 &method_name,
335 generic_args.as_deref(),
336 ),
337 Expr::Match { expr, arms } => { 331 Expr::Match { expr, arms } => {
338 let input_ty = self.infer_expr(*expr, &Expectation::none()); 332 let input_ty = self.infer_expr(*expr, &Expectation::none());
339 333
334 let expected = expected.adjust_for_branches(&mut self.table);
335
340 let mut result_ty = if arms.is_empty() { 336 let mut result_ty = if arms.is_empty() {
341 TyKind::Never.intern(&Interner) 337 TyKind::Never.intern(&Interner)
342 } else { 338 } else {
343 self.table.new_type_var() 339 match &expected {
340 Expectation::HasType(ty) => ty.clone(),
341 _ => self.table.new_type_var(),
342 }
344 }; 343 };
345 344
346 let matchee_diverges = self.diverges; 345 let matchee_diverges = self.diverges;
@@ -988,7 +987,7 @@ impl<'a> InferenceContext<'a> {
988 } 987 }
989 988
990 fn register_obligations_for_call(&mut self, callable_ty: &Ty) { 989 fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
991 let callable_ty = self.resolve_ty_shallow(&callable_ty); 990 let callable_ty = self.resolve_ty_shallow(callable_ty);
992 if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { 991 if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) {
993 let def: CallableDefId = from_chalk(self.db, *fn_def); 992 let def: CallableDefId = from_chalk(self.db, *fn_def);
994 let generic_predicates = self.db.generic_predicates(def.into()); 993 let generic_predicates = self.db.generic_predicates(def.into());
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 25dff7e49..035f4ded6 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -192,7 +192,7 @@ impl<'a> InferenceContext<'a> {
192 Pat::Path(path) => { 192 Pat::Path(path) => {
193 // FIXME use correct resolver for the surrounding expression 193 // FIXME use correct resolver for the surrounding expression
194 let resolver = self.resolver.clone(); 194 let resolver = self.resolver.clone();
195 self.infer_path(&resolver, &path, pat.into()).unwrap_or(self.err_ty()) 195 self.infer_path(&resolver, path, pat.into()).unwrap_or(self.err_ty())
196 } 196 }
197 Pat::Bind { mode, name: _, subpat } => { 197 Pat::Bind { mode, name: _, subpat } => {
198 let mode = if mode == &BindingAnnotation::Unannotated { 198 let mode = if mode == &BindingAnnotation::Unannotated {
@@ -275,7 +275,7 @@ impl<'a> InferenceContext<'a> {
275 if !self.unify(&ty, &expected) { 275 if !self.unify(&ty, &expected) {
276 self.result 276 self.result
277 .type_mismatches 277 .type_mismatches
278 .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() }); 278 .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
279 } 279 }
280 self.write_pat_ty(pat, ty.clone()); 280 self.write_pat_ty(pat, ty.clone());
281 ty 281 ty
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 14c99eafd..056cdb5d5 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -43,11 +43,11 @@ impl<'a> InferenceContext<'a> {
43 } 43 }
44 let ty = self.make_ty(type_ref); 44 let ty = self.make_ty(type_ref);
45 let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); 45 let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
46 let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver); 46 let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
47 let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); 47 let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
48 self.resolve_ty_assoc_item( 48 self.resolve_ty_assoc_item(
49 ty, 49 ty,
50 &path.segments().last().expect("path had at least one segment").name, 50 path.segments().last().expect("path had at least one segment").name,
51 id, 51 id,
52 )? 52 )?
53 } else { 53 } else {
@@ -154,7 +154,7 @@ impl<'a> InferenceContext<'a> {
154 let segment = 154 let segment =
155 remaining_segments.last().expect("there should be at least one segment here"); 155 remaining_segments.last().expect("there should be at least one segment here");
156 156
157 self.resolve_ty_assoc_item(ty, &segment.name, id) 157 self.resolve_ty_assoc_item(ty, segment.name, id)
158 } 158 }
159 } 159 }
160 } 160 }
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs
index 29ffdd9b7..5fef878e8 100644
--- a/crates/hir_ty/src/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -331,7 +331,7 @@ impl chalk_ir::interner::Interner for Interner {
331 &self, 331 &self,
332 clauses: &'a Self::InternedProgramClauses, 332 clauses: &'a Self::InternedProgramClauses,
333 ) -> &'a [chalk_ir::ProgramClause<Self>] { 333 ) -> &'a [chalk_ir::ProgramClause<Self>] {
334 &clauses 334 clauses
335 } 335 }
336 336
337 fn intern_quantified_where_clauses<E>( 337 fn intern_quantified_where_clauses<E>(
@@ -373,7 +373,7 @@ impl chalk_ir::interner::Interner for Interner {
373 &self, 373 &self,
374 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, 374 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
375 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { 375 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
376 &canonical_var_kinds 376 canonical_var_kinds
377 } 377 }
378 378
379 fn intern_constraints<E>( 379 fn intern_constraints<E>(
@@ -413,7 +413,7 @@ impl chalk_ir::interner::Interner for Interner {
413 &self, 413 &self,
414 variances: &'a Self::InternedVariances, 414 variances: &'a Self::InternedVariances,
415 ) -> &'a [chalk_ir::Variance] { 415 ) -> &'a [chalk_ir::Variance] {
416 &variances 416 variances
417 } 417 }
418} 418}
419 419
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 50e0d6333..128cae830 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -21,7 +21,6 @@ mod utils;
21mod walk; 21mod walk;
22pub mod db; 22pub mod db;
23pub mod diagnostics; 23pub mod diagnostics;
24pub mod diagnostics_sink;
25pub mod display; 24pub mod display;
26pub mod method_resolution; 25pub mod method_resolution;
27pub mod primitive; 26pub mod primitive;
@@ -50,7 +49,7 @@ use crate::{db::HirDatabase, utils::generics};
50pub use autoderef::autoderef; 49pub use autoderef::autoderef;
51pub use builder::TyBuilder; 50pub use builder::TyBuilder;
52pub use chalk_ext::*; 51pub use chalk_ext::*;
53pub use infer::{could_unify, InferenceResult}; 52pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
54pub use interner::Interner; 53pub use interner::Interner;
55pub use lower::{ 54pub use lower::{
56 associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, 55 associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index c83933c73..817a65c20 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -238,7 +238,7 @@ impl<'a> TyLoweringContext<'a> {
238 // away instead of two. 238 // away instead of two.
239 let actual_opaque_type_data = self 239 let actual_opaque_type_data = self
240 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { 240 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
241 ctx.lower_impl_trait(&bounds) 241 ctx.lower_impl_trait(bounds)
242 }); 242 });
243 self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; 243 self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
244 244
@@ -421,7 +421,7 @@ impl<'a> TyLoweringContext<'a> {
421 let found = self 421 let found = self
422 .db 422 .db
423 .trait_data(trait_ref.hir_trait_id()) 423 .trait_data(trait_ref.hir_trait_id())
424 .associated_type_by_name(&segment.name); 424 .associated_type_by_name(segment.name);
425 match found { 425 match found {
426 Some(associated_ty) => { 426 Some(associated_ty) => {
427 // FIXME handle type parameters on the segment 427 // FIXME handle type parameters on the segment
@@ -505,7 +505,7 @@ impl<'a> TyLoweringContext<'a> {
505 pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { 505 pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) {
506 // Resolve the path (in type namespace) 506 // Resolve the path (in type namespace)
507 if let Some(type_ref) = path.type_anchor() { 507 if let Some(type_ref) = path.type_anchor() {
508 let (ty, res) = self.lower_ty_ext(&type_ref); 508 let (ty, res) = self.lower_ty_ext(type_ref);
509 return self.lower_ty_relative_path(ty, res, path.segments()); 509 return self.lower_ty_relative_path(ty, res, path.segments());
510 } 510 }
511 let (resolution, remaining_index) = 511 let (resolution, remaining_index) =
@@ -784,7 +784,7 @@ impl<'a> TyLoweringContext<'a> {
784 let trait_ref = match bound { 784 let trait_ref = match bound {
785 TypeBound::Path(path) => { 785 TypeBound::Path(path) => {
786 bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); 786 bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
787 bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b)) 787 bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
788 } 788 }
789 TypeBound::Lifetime(_) => None, 789 TypeBound::Lifetime(_) => None,
790 TypeBound::Error => None, 790 TypeBound::Error => None,
@@ -957,7 +957,7 @@ pub(crate) fn field_types_query(
957/// like `T::Item`. 957/// like `T::Item`.
958/// 958///
959/// See the analogous query in rustc and its comment: 959/// See the analogous query in rustc and its comment:
960/// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46 960/// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46>
961/// This is a query mostly to handle cycles somewhat gracefully; e.g. the 961/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
962/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but 962/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
963/// these are fine: `T: Foo<U::Item>, U: Foo<()>`. 963/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index a23527f7d..3d233b1e2 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -8,7 +8,7 @@ use arrayvec::ArrayVec;
8use base_db::{CrateId, Edition}; 8use base_db::{CrateId, Edition};
9use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; 9use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
10use hir_def::{ 10use hir_def::{
11 lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, 11 lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, BlockId, FunctionId,
12 GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, 12 GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
13}; 13};
14use hir_expand::name::Name; 14use hir_expand::name::Name;
@@ -60,7 +60,7 @@ impl TyFingerprint {
60 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), 60 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
61 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), 61 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
62 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), 62 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
63 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, 63 TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?,
64 _ => return None, 64 _ => return None,
65 }; 65 };
66 Some(fp) 66 Some(fp)
@@ -77,7 +77,7 @@ impl TyFingerprint {
77 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), 77 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
78 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), 78 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
79 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), 79 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
80 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, 80 TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?,
81 TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), 81 TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty),
82 TyKind::Tuple(_, subst) => { 82 TyKind::Tuple(_, subst) => {
83 let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); 83 let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner));
@@ -139,35 +139,47 @@ impl TraitImpls {
139 let mut impls = Self { map: FxHashMap::default() }; 139 let mut impls = Self { map: FxHashMap::default() };
140 140
141 let crate_def_map = db.crate_def_map(krate); 141 let crate_def_map = db.crate_def_map(krate);
142 collect_def_map(db, &crate_def_map, &mut impls); 142 impls.collect_def_map(db, &crate_def_map);
143 143
144 return Arc::new(impls); 144 return Arc::new(impls);
145 }
145 146
146 fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) { 147 pub(crate) fn trait_impls_in_block_query(
147 for (_module_id, module_data) in def_map.modules() { 148 db: &dyn HirDatabase,
148 for impl_id in module_data.scope.impls() { 149 block: BlockId,
149 let target_trait = match db.impl_trait(impl_id) { 150 ) -> Option<Arc<Self>> {
150 Some(tr) => tr.skip_binders().hir_trait_id(), 151 let _p = profile::span("trait_impls_in_block_query");
151 None => continue, 152 let mut impls = Self { map: FxHashMap::default() };
152 };
153 let self_ty = db.impl_self_ty(impl_id);
154 let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
155 impls
156 .map
157 .entry(target_trait)
158 .or_default()
159 .entry(self_ty_fp)
160 .or_default()
161 .push(impl_id);
162 }
163 153
164 // To better support custom derives, collect impls in all unnamed const items. 154 let block_def_map = db.block_def_map(block)?;
165 // const _: () = { ... }; 155 impls.collect_def_map(db, &block_def_map);
166 for konst in module_data.scope.unnamed_consts() { 156
167 let body = db.body(konst.into()); 157 return Some(Arc::new(impls));
168 for (_, block_def_map) in body.blocks(db.upcast()) { 158 }
169 collect_def_map(db, &block_def_map, impls); 159
170 } 160 fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
161 for (_module_id, module_data) in def_map.modules() {
162 for impl_id in module_data.scope.impls() {
163 let target_trait = match db.impl_trait(impl_id) {
164 Some(tr) => tr.skip_binders().hir_trait_id(),
165 None => continue,
166 };
167 let self_ty = db.impl_self_ty(impl_id);
168 let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
169 self.map
170 .entry(target_trait)
171 .or_default()
172 .entry(self_ty_fp)
173 .or_default()
174 .push(impl_id);
175 }
176
177 // To better support custom derives, collect impls in all unnamed const items.
178 // const _: () = { ... };
179 for konst in module_data.scope.unnamed_consts() {
180 let body = db.body(konst.into());
181 for (_, block_def_map) in body.blocks(db.upcast()) {
182 self.collect_def_map(db, &block_def_map);
171 } 183 }
172 } 184 }
173 } 185 }
@@ -372,7 +384,7 @@ pub(crate) fn lookup_method(
372 db, 384 db,
373 env, 385 env,
374 krate, 386 krate,
375 &traits_in_scope, 387 traits_in_scope,
376 visible_from_module, 388 visible_from_module,
377 Some(name), 389 Some(name),
378 LookupMode::MethodCall, 390 LookupMode::MethodCall,
@@ -484,7 +496,7 @@ fn iterate_method_candidates_impl(
484 LookupMode::Path => { 496 LookupMode::Path => {
485 // No autoderef for path lookups 497 // No autoderef for path lookups
486 iterate_method_candidates_for_self_ty( 498 iterate_method_candidates_for_self_ty(
487 &ty, 499 ty,
488 db, 500 db,
489 env, 501 env,
490 krate, 502 krate,
@@ -513,7 +525,7 @@ fn iterate_method_candidates_with_autoref(
513 db, 525 db,
514 env.clone(), 526 env.clone(),
515 krate, 527 krate,
516 &traits_in_scope, 528 traits_in_scope,
517 visible_from_module, 529 visible_from_module,
518 name, 530 name,
519 &mut callback, 531 &mut callback,
@@ -531,7 +543,7 @@ fn iterate_method_candidates_with_autoref(
531 db, 543 db,
532 env.clone(), 544 env.clone(),
533 krate, 545 krate,
534 &traits_in_scope, 546 traits_in_scope,
535 visible_from_module, 547 visible_from_module,
536 name, 548 name,
537 &mut callback, 549 &mut callback,
@@ -549,7 +561,7 @@ fn iterate_method_candidates_with_autoref(
549 db, 561 db,
550 env, 562 env,
551 krate, 563 krate,
552 &traits_in_scope, 564 traits_in_scope,
553 visible_from_module, 565 visible_from_module,
554 name, 566 name,
555 &mut callback, 567 &mut callback,
@@ -593,7 +605,7 @@ fn iterate_method_candidates_by_receiver(
593 db, 605 db,
594 env.clone(), 606 env.clone(),
595 krate, 607 krate,
596 &traits_in_scope, 608 traits_in_scope,
597 name, 609 name,
598 Some(receiver_ty), 610 Some(receiver_ty),
599 &mut callback, 611 &mut callback,
@@ -870,7 +882,7 @@ fn transform_receiver_ty(
870 .fill_with_unknown() 882 .fill_with_unknown()
871 .build(), 883 .build(),
872 AssocContainerId::ImplId(impl_id) => { 884 AssocContainerId::ImplId(impl_id) => {
873 let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?; 885 let impl_substs = inherent_impl_substs(db, env, impl_id, self_ty)?;
874 TyBuilder::subst_for_def(db, function_id) 886 TyBuilder::subst_for_def(db, function_id)
875 .use_parent_substs(&impl_substs) 887 .use_parent_substs(&impl_substs)
876 .fill_with_unknown() 888 .fill_with_unknown()
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 9d726b024..b873585c4 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -9,7 +9,7 @@ mod macros;
9mod display_source_code; 9mod display_source_code;
10mod incremental; 10mod incremental;
11 11
12use std::{env, sync::Arc}; 12use std::{collections::HashMap, env, sync::Arc};
13 13
14use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 14use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
15use expect_test::Expect; 15use expect_test::Expect;
@@ -83,9 +83,105 @@ fn check_types_impl(ra_fixture: &str, display_source: bool) {
83 checked_one = true; 83 checked_one = true;
84 } 84 }
85 } 85 }
86
86 assert!(checked_one, "no `//^` annotations found"); 87 assert!(checked_one, "no `//^` annotations found");
87} 88}
88 89
90fn check_no_mismatches(ra_fixture: &str) {
91 check_mismatches_impl(ra_fixture, true)
92}
93
94#[allow(unused)]
95fn check_mismatches(ra_fixture: &str) {
96 check_mismatches_impl(ra_fixture, false)
97}
98
99fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) {
100 let _tracing = setup_tracing();
101 let (db, file_id) = TestDB::with_single_file(ra_fixture);
102 let module = db.module_for_file(file_id);
103 let def_map = module.def_map(&db);
104
105 let mut defs: Vec<DefWithBodyId> = Vec::new();
106 visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it));
107 defs.sort_by_key(|def| match def {
108 DefWithBodyId::FunctionId(it) => {
109 let loc = it.lookup(&db);
110 loc.source(&db).value.syntax().text_range().start()
111 }
112 DefWithBodyId::ConstId(it) => {
113 let loc = it.lookup(&db);
114 loc.source(&db).value.syntax().text_range().start()
115 }
116 DefWithBodyId::StaticId(it) => {
117 let loc = it.lookup(&db);
118 loc.source(&db).value.syntax().text_range().start()
119 }
120 });
121 let mut mismatches = HashMap::new();
122 let mut push_mismatch = |src_ptr: InFile<SyntaxNode>, mismatch: TypeMismatch| {
123 let range = src_ptr.value.text_range();
124 if src_ptr.file_id.call_node(&db).is_some() {
125 panic!("type mismatch in macro expansion");
126 }
127 let file_range = FileRange { file_id: src_ptr.file_id.original_file(&db), range };
128 let actual = format!(
129 "expected {}, got {}",
130 mismatch.expected.display_test(&db),
131 mismatch.actual.display_test(&db)
132 );
133 mismatches.insert(file_range, actual);
134 };
135 for def in defs {
136 let (_body, body_source_map) = db.body_with_source_map(def);
137 let inference_result = db.infer(def);
138 for (pat, mismatch) in inference_result.pat_type_mismatches() {
139 let syntax_ptr = match body_source_map.pat_syntax(pat) {
140 Ok(sp) => {
141 let root = db.parse_or_expand(sp.file_id).unwrap();
142 sp.map(|ptr| {
143 ptr.either(
144 |it| it.to_node(&root).syntax().clone(),
145 |it| it.to_node(&root).syntax().clone(),
146 )
147 })
148 }
149 Err(SyntheticSyntax) => continue,
150 };
151 push_mismatch(syntax_ptr, mismatch.clone());
152 }
153 for (expr, mismatch) in inference_result.expr_type_mismatches() {
154 let node = match body_source_map.expr_syntax(expr) {
155 Ok(sp) => {
156 let root = db.parse_or_expand(sp.file_id).unwrap();
157 sp.map(|ptr| ptr.to_node(&root).syntax().clone())
158 }
159 Err(SyntheticSyntax) => continue,
160 };
161 push_mismatch(node, mismatch.clone());
162 }
163 }
164 let mut checked_one = false;
165 for (file_id, annotations) in db.extract_annotations() {
166 for (range, expected) in annotations {
167 let file_range = FileRange { file_id, range };
168 if let Some(mismatch) = mismatches.remove(&file_range) {
169 assert_eq!(mismatch, expected);
170 } else {
171 assert!(false, "Expected mismatch not encountered: {}\n", expected);
172 }
173 checked_one = true;
174 }
175 }
176 let mut buf = String::new();
177 for (range, mismatch) in mismatches {
178 format_to!(buf, "{:?}: {}\n", range.range, mismatch,);
179 }
180 assert!(buf.is_empty(), "Unexpected type mismatches:\n{}", buf);
181
182 assert!(checked_one || allow_none, "no `//^` annotations found");
183}
184
89fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { 185fn type_at_range(db: &TestDB, pos: FileRange) -> Ty {
90 let file = db.parse(pos.file_id).ok().unwrap(); 186 let file = db.parse(pos.file_id).ok().unwrap();
91 let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap(); 187 let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap();
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 6dac7e103..713b74165 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_block_expr_type_mismatch() { 6fn infer_block_expr_type_mismatch() {
@@ -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
145 fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
146 let d = foo1(a);
147 let e = foo2(b);
148 let f = foo3(c);
149 }
150 134
135fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
136fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
137fn foo3<T>(x: C<[T]>) -> C<[T]> { x }
151 138
152 #[lang = "sized"] 139fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
153 pub trait Sized {} 140 let d = foo1(a);
154 #[lang = "unsize"] 141 let e = foo2(b);
155 pub trait Unsize<T: ?Sized> {} 142 let f = foo3(c);
156 #[lang = "coerce_unsized"] 143}
157 pub trait CoerceUnsized<T> {} 144"#,
158
159 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
160 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
161 "#,
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}
@@ -963,7 +891,7 @@ fn test() -> i32 {
963 891
964#[test] 892#[test]
965fn panic_macro() { 893fn panic_macro() {
966 check_infer_with_mismatches( 894 check_no_mismatches(
967 r#" 895 r#"
968mod panic { 896mod panic {
969 #[macro_export] 897 #[macro_export]
@@ -991,15 +919,26 @@ fn main() {
991 panic!() 919 panic!()
992} 920}
993 "#, 921 "#,
994 expect![[r#" 922 );
995 174..185 '{ loop {} }': ! 923}
996 176..183 'loop {}': ! 924
997 181..183 '{}': () 925#[test]
998 !0..24 '$crate...:panic': fn panic() -> ! 926fn coerce_unsize_expected_type() {
999 !0..26 '$crate...anic()': ! 927 check_no_mismatches(
1000 !0..26 '$crate...anic()': ! 928 r#"
1001 !0..28 '$crate...015!()': ! 929//- minicore: coerce_unsized
1002 454..470 '{ ...c!() }': () 930fn main() {
1003 "#]], 931 let foo: &[u32] = &[1, 2];
932 let foo: &[u32] = match true {
933 true => &[1, 2],
934 false => &[1, 2, 3],
935 };
936 let foo: &[u32] = if true {
937 &[1, 2]
938 } else {
939 &[1, 2, 3]
940 };
941}
942 "#,
1004 ); 943 );
1005} 944}
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/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 7d00cee9b..5adbe9c45 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_pattern() { 6fn infer_pattern() {
@@ -518,47 +518,24 @@ fn infer_generics_in_patterns() {
518 518
519#[test] 519#[test]
520fn infer_const_pattern() { 520fn infer_const_pattern() {
521 check_infer_with_mismatches( 521 check_mismatches(
522 r#" 522 r#"
523 enum Option<T> { None } 523enum Option<T> { None }
524 use Option::None; 524use Option::None;
525 struct Foo; 525struct Foo;
526 const Bar: usize = 1; 526const Bar: usize = 1;
527 527
528 fn test() { 528fn test() {
529 let a: Option<u32> = None; 529 let a: Option<u32> = None;
530 let b: Option<i64> = match a { 530 let b: Option<i64> = match a {
531 None => None, 531 None => None,
532 }; 532 };
533 let _: () = match () { Foo => Foo }; // Expected mismatch 533 let _: () = match () { Foo => () };
534 let _: () = match () { Bar => Bar }; // Expected mismatch 534 // ^^^ expected (), got Foo
535 } 535 let _: () = match () { Bar => () };
536 // ^^^ expected (), got usize
537}
536 "#, 538 "#,
537 expect![[r#"
538 73..74 '1': usize
539 87..309 '{ ...atch }': ()
540 97..98 'a': Option<u32>
541 114..118 'None': Option<u32>
542 128..129 'b': Option<i64>
543 145..182 'match ... }': Option<i64>
544 151..152 'a': Option<u32>
545 163..167 'None': Option<u32>
546 171..175 'None': Option<i64>
547 192..193 '_': ()
548 200..223 'match ... Foo }': Foo
549 206..208 '()': ()
550 211..214 'Foo': Foo
551 218..221 'Foo': Foo
552 254..255 '_': ()
553 262..285 'match ... Bar }': usize
554 268..270 '()': ()
555 273..276 'Bar': usize
556 280..283 'Bar': usize
557 200..223: expected (), got Foo
558 211..214: expected (), got Foo
559 262..285: expected (), got usize
560 273..276: expected (), got usize
561 "#]],
562 ); 539 );
563} 540}
564 541
@@ -594,48 +571,44 @@ fn main() {
594fn match_ergonomics_in_closure_params() { 571fn match_ergonomics_in_closure_params() {
595 check_infer( 572 check_infer(
596 r#" 573 r#"
597 #[lang = "fn_once"] 574//- minicore: fn
598 trait FnOnce<Args> { 575fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} }
599 type Output;
600 }
601
602 fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} }
603 576
604 fn test() { 577fn test() {
605 foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics 578 foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics
606 foo(&(1, "a"), |(x, y)| x); 579 foo(&(1, "a"), |(x, y)| x);
607 } 580}
608 "#, 581"#,
609 expect![[r#" 582 expect![[r#"
610 93..94 't': T 583 32..33 't': T
611 99..100 'f': F 584 38..39 'f': F
612 110..121 '{ loop {} }': U 585 49..60 '{ loop {} }': U
613 112..119 'loop {}': ! 586 51..58 'loop {}': !
614 117..119 '{}': () 587 56..58 '{}': ()
615 133..232 '{ ... x); }': () 588 72..171 '{ ... x); }': ()
616 139..142 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 589 78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
617 139..166 'foo(&(...y)| x)': i32 590 78..105 'foo(&(...y)| x)': i32
618 143..152 '&(1, "a")': &(i32, &str) 591 82..91 '&(1, "a")': &(i32, &str)
619 144..152 '(1, "a")': (i32, &str) 592 83..91 '(1, "a")': (i32, &str)
620 145..146 '1': i32 593 84..85 '1': i32
621 148..151 '"a"': &str 594 87..90 '"a"': &str
622 154..165 '|&(x, y)| x': |&(i32, &str)| -> i32 595 93..104 '|&(x, y)| x': |&(i32, &str)| -> i32
623 155..162 '&(x, y)': &(i32, &str) 596 94..101 '&(x, y)': &(i32, &str)
624 156..162 '(x, y)': (i32, &str) 597 95..101 '(x, y)': (i32, &str)
625 157..158 'x': i32 598 96..97 'x': i32
626 160..161 'y': &str 599 99..100 'y': &str
627 164..165 'x': i32 600 103..104 'x': i32
628 203..206 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 601 142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
629 203..229 'foo(&(...y)| x)': &i32 602 142..168 'foo(&(...y)| x)': &i32
630 207..216 '&(1, "a")': &(i32, &str) 603 146..155 '&(1, "a")': &(i32, &str)
631 208..216 '(1, "a")': (i32, &str) 604 147..155 '(1, "a")': (i32, &str)
632 209..210 '1': i32 605 148..149 '1': i32
633 212..215 '"a"': &str 606 151..154 '"a"': &str
634 218..228 '|(x, y)| x': |&(i32, &str)| -> &i32 607 157..167 '|(x, y)| x': |&(i32, &str)| -> &i32
635 219..225 '(x, y)': (i32, &str) 608 158..164 '(x, y)': (i32, &str)
636 220..221 'x': &i32 609 159..160 'x': &i32
637 223..224 'y': &&str 610 162..163 'y': &&str
638 227..228 'x': &i32 611 166..167 'x': &i32
639 "#]], 612 "#]],
640 ); 613 );
641} 614}
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 1019e783b..1edec1615 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -884,41 +884,37 @@ fn issue_4966() {
884fn issue_6628() { 884fn issue_6628() {
885 check_infer( 885 check_infer(
886 r#" 886 r#"
887 #[lang = "fn_once"] 887//- minicore: fn
888 pub trait FnOnce<Args> { 888struct S<T>();
889 type Output; 889impl<T> S<T> {
890 } 890 fn f(&self, _t: T) {}
891 891 fn g<F: FnOnce(&T)>(&self, _f: F) {}
892 struct S<T>(); 892}
893 impl<T> S<T> { 893fn main() {
894 fn f(&self, _t: T) {} 894 let s = S();
895 fn g<F: FnOnce(&T)>(&self, _f: F) {} 895 s.g(|_x| {});
896 } 896 s.f(10);
897 fn main() { 897}
898 let s = S(); 898"#,
899 s.g(|_x| {});
900 s.f(10);
901 }
902 "#,
903 expect![[r#" 899 expect![[r#"
904 105..109 'self': &S<T> 900 40..44 'self': &S<T>
905 111..113 '_t': T 901 46..48 '_t': T
906 118..120 '{}': () 902 53..55 '{}': ()
907 146..150 'self': &S<T> 903 81..85 'self': &S<T>
908 152..154 '_f': F 904 87..89 '_f': F
909 159..161 '{}': () 905 94..96 '{}': ()
910 174..225 '{ ...10); }': () 906 109..160 '{ ...10); }': ()
911 184..185 's': S<i32> 907 119..120 's': S<i32>
912 188..189 'S': S<i32>() -> S<i32> 908 123..124 'S': S<i32>() -> S<i32>
913 188..191 'S()': S<i32> 909 123..126 'S()': S<i32>
914 197..198 's': S<i32> 910 132..133 's': S<i32>
915 197..209 's.g(|_x| {})': () 911 132..144 's.g(|_x| {})': ()
916 201..208 '|_x| {}': |&i32| -> () 912 136..143 '|_x| {}': |&i32| -> ()
917 202..204 '_x': &i32 913 137..139 '_x': &i32
918 206..208 '{}': () 914 141..143 '{}': ()
919 215..216 's': S<i32> 915 150..151 's': S<i32>
920 215..222 's.f(10)': () 916 150..157 's.f(10)': ()
921 219..221 '10': i32 917 154..156 '10': i32
922 "#]], 918 "#]],
923 ); 919 );
924} 920}
@@ -927,35 +923,33 @@ fn issue_6628() {
927fn issue_6852() { 923fn issue_6852() {
928 check_infer( 924 check_infer(
929 r#" 925 r#"
930 #[lang = "deref"] 926//- minicore: deref
931 pub trait Deref { 927use core::ops::Deref;
932 type Target;
933 }
934 928
935 struct BufWriter {} 929struct BufWriter {}
936 930
937 struct Mutex<T> {} 931struct Mutex<T> {}
938 struct MutexGuard<'a, T> {} 932struct MutexGuard<'a, T> {}
939 impl<T> Mutex<T> { 933impl<T> Mutex<T> {
940 fn lock(&self) -> MutexGuard<'_, T> {} 934 fn lock(&self) -> MutexGuard<'_, T> {}
941 } 935}
942 impl<'a, T: 'a> Deref for MutexGuard<'a, T> { 936impl<'a, T: 'a> Deref for MutexGuard<'a, T> {
943 type Target = T; 937 type Target = T;
944 } 938}
945 fn flush(&self) { 939fn flush(&self) {
946 let w: &Mutex<BufWriter>; 940 let w: &Mutex<BufWriter>;
947 *(w.lock()); 941 *(w.lock());
948 } 942}
949 "#, 943"#,
950 expect![[r#" 944 expect![[r#"
951 156..160 'self': &Mutex<T> 945 123..127 'self': &Mutex<T>
952 183..185 '{}': () 946 150..152 '{}': ()
953 267..271 'self': &{unknown} 947 234..238 'self': &{unknown}
954 273..323 '{ ...()); }': () 948 240..290 '{ ...()); }': ()
955 283..284 'w': &Mutex<BufWriter> 949 250..251 'w': &Mutex<BufWriter>
956 309..320 '*(w.lock())': BufWriter 950 276..287 '*(w.lock())': BufWriter
957 311..312 'w': &Mutex<BufWriter> 951 278..279 'w': &Mutex<BufWriter>
958 311..319 'w.lock()': MutexGuard<BufWriter> 952 278..286 'w.lock()': MutexGuard<BufWriter>
959 "#]], 953 "#]],
960 ); 954 );
961} 955}
@@ -977,37 +971,33 @@ fn param_overrides_fn() {
977fn lifetime_from_chalk_during_deref() { 971fn lifetime_from_chalk_during_deref() {
978 check_types( 972 check_types(
979 r#" 973 r#"
980 #[lang = "deref"] 974//- minicore: deref
981 pub trait Deref { 975struct Box<T: ?Sized> {}
982 type Target; 976impl<T> core::ops::Deref for Box<T> {
983 } 977 type Target = T;
984
985 struct Box<T: ?Sized> {}
986 impl<T> Deref for Box<T> {
987 type Target = T;
988 978
989 fn deref(&self) -> &Self::Target { 979 fn deref(&self) -> &Self::Target {
990 loop {} 980 loop {}
991 } 981 }
992 } 982}
993 983
994 trait Iterator { 984trait Iterator {
995 type Item; 985 type Item;
996 } 986}
997 987
998 pub struct Iter<'a, T: 'a> { 988pub struct Iter<'a, T: 'a> {
999 inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>, 989 inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>,
1000 } 990}
1001 991
1002 trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> { 992trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> {
1003 fn clone_box(&self); 993 fn clone_box(&self);
1004 } 994}
1005 995
1006 fn clone_iter<T>(s: Iter<T>) { 996fn clone_iter<T>(s: Iter<T>) {
1007 s.inner.clone_box(); 997 s.inner.clone_box();
1008 //^^^^^^^^^^^^^^^^^^^ () 998 //^^^^^^^^^^^^^^^^^^^ ()
1009 } 999}
1010 "#, 1000"#,
1011 ) 1001 )
1012} 1002}
1013 1003
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 588f0d1d4..065cca74f 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#"
@@ -1912,11 +1846,7 @@ fn test() {
1912fn closure_1() { 1846fn closure_1() {
1913 check_infer_with_mismatches( 1847 check_infer_with_mismatches(
1914 r#" 1848 r#"
1915#[lang = "fn_once"] 1849//- minicore: fn
1916trait FnOnce<Args> {
1917 type Output;
1918}
1919
1920enum Option<T> { Some(T), None } 1850enum Option<T> { Some(T), None }
1921impl<T> Option<T> { 1851impl<T> Option<T> {
1922 fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} } 1852 fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} }
@@ -1929,34 +1859,34 @@ fn test() {
1929 let y: Option<i64> = x.map(|_v| 1); 1859 let y: Option<i64> = x.map(|_v| 1);
1930}"#, 1860}"#,
1931 expect![[r#" 1861 expect![[r#"
1932 147..151 'self': Option<T> 1862 86..90 'self': Option<T>
1933 153..154 'f': F 1863 92..93 'f': F
1934 172..183 '{ loop {} }': Option<U> 1864 111..122 '{ loop {} }': Option<U>
1935 174..181 'loop {}': ! 1865 113..120 'loop {}': !
1936 179..181 '{}': () 1866 118..120 '{}': ()
1937 197..316 '{ ... 1); }': () 1867 136..255 '{ ... 1); }': ()
1938 207..208 'x': Option<u32> 1868 146..147 'x': Option<u32>
1939 211..223 'Option::Some': Some<u32>(u32) -> Option<u32> 1869 150..162 'Option::Some': Some<u32>(u32) -> Option<u32>
1940 211..229 'Option...(1u32)': Option<u32> 1870 150..168 'Option...(1u32)': Option<u32>
1941 224..228 '1u32': u32 1871 163..167 '1u32': u32
1942 235..236 'x': Option<u32> 1872 174..175 'x': Option<u32>
1943 235..251 'x.map(...v + 1)': Option<u32> 1873 174..190 'x.map(...v + 1)': Option<u32>
1944 241..250 '|v| v + 1': |u32| -> u32 1874 180..189 '|v| v + 1': |u32| -> u32
1945 242..243 'v': u32 1875 181..182 'v': u32
1946 245..246 'v': u32 1876 184..185 'v': u32
1947 245..250 'v + 1': u32 1877 184..189 'v + 1': u32
1948 249..250 '1': u32 1878 188..189 '1': u32
1949 257..258 'x': Option<u32> 1879 196..197 'x': Option<u32>
1950 257..273 'x.map(... 1u64)': Option<u64> 1880 196..212 'x.map(... 1u64)': Option<u64>
1951 263..272 '|_v| 1u64': |u32| -> u64 1881 202..211 '|_v| 1u64': |u32| -> u64
1952 264..266 '_v': u32 1882 203..205 '_v': u32
1953 268..272 '1u64': u64 1883 207..211 '1u64': u64
1954 283..284 'y': Option<i64> 1884 222..223 'y': Option<i64>
1955 300..301 'x': Option<u32> 1885 239..240 'x': Option<u32>
1956 300..313 'x.map(|_v| 1)': Option<i64> 1886 239..252 'x.map(|_v| 1)': Option<i64>
1957 306..312 '|_v| 1': |u32| -> i64 1887 245..251 '|_v| 1': |u32| -> i64
1958 307..309 '_v': u32 1888 246..248 '_v': u32
1959 311..312 '1': i64 1889 250..251 '1': i64
1960 "#]], 1890 "#]],
1961 ); 1891 );
1962} 1892}
@@ -2030,11 +1960,7 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
2030fn closure_as_argument_inference_order() { 1960fn closure_as_argument_inference_order() {
2031 check_infer_with_mismatches( 1961 check_infer_with_mismatches(
2032 r#" 1962 r#"
2033#[lang = "fn_once"] 1963//- minicore: fn
2034trait FnOnce<Args> {
2035 type Output;
2036}
2037
2038fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} } 1964fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} }
2039fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} } 1965fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} }
2040 1966
@@ -2053,62 +1979,62 @@ fn test() {
2053 let x4 = S.foo2(|s| s.method(), S); 1979 let x4 = S.foo2(|s| s.method(), S);
2054}"#, 1980}"#,
2055 expect![[r#" 1981 expect![[r#"
2056 94..95 'x': T 1982 33..34 'x': T
2057 100..101 'f': F 1983 39..40 'f': F
2058 111..122 '{ loop {} }': U 1984 50..61 '{ loop {} }': U
2059 113..120 'loop {}': ! 1985 52..59 'loop {}': !
2060 118..120 '{}': () 1986 57..59 '{}': ()
2061 156..157 'f': F 1987 95..96 'f': F
2062 162..163 'x': T 1988 101..102 'x': T
2063 173..184 '{ loop {} }': U 1989 112..123 '{ loop {} }': U
2064 175..182 'loop {}': ! 1990 114..121 'loop {}': !
2065 180..182 '{}': () 1991 119..121 '{}': ()
2066 219..223 'self': S 1992 158..162 'self': S
2067 271..275 'self': S 1993 210..214 'self': S
2068 277..278 'x': T 1994 216..217 'x': T
2069 283..284 'f': F 1995 222..223 'f': F
2070 294..305 '{ loop {} }': U 1996 233..244 '{ loop {} }': U
2071 296..303 'loop {}': ! 1997 235..242 'loop {}': !
2072 301..303 '{}': () 1998 240..242 '{}': ()
2073 343..347 'self': S 1999 282..286 'self': S
2074 349..350 'f': F 2000 288..289 'f': F
2075 355..356 'x': T 2001 294..295 'x': T
2076 366..377 '{ loop {} }': U 2002 305..316 '{ loop {} }': U
2077 368..375 'loop {}': ! 2003 307..314 'loop {}': !
2078 373..375 '{}': () 2004 312..314 '{}': ()
2079 391..550 '{ ... S); }': () 2005 330..489 '{ ... S); }': ()
2080 401..403 'x1': u64 2006 340..342 'x1': u64
2081 406..410 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64 2007 345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
2082 406..429 'foo1(S...hod())': u64 2008 345..368 'foo1(S...hod())': u64
2083 411..412 'S': S 2009 350..351 'S': S
2084 414..428 '|s| s.method()': |S| -> u64 2010 353..367 '|s| s.method()': |S| -> u64
2085 415..416 's': S 2011 354..355 's': S
2086 418..419 's': S 2012 357..358 's': S
2087 418..428 's.method()': u64 2013 357..367 's.method()': u64
2088 439..441 'x2': u64 2014 378..380 'x2': u64
2089 444..448 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64 2015 383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
2090 444..467 'foo2(|...(), S)': u64 2016 383..406 'foo2(|...(), S)': u64
2091 449..463 '|s| s.method()': |S| -> u64 2017 388..402 '|s| s.method()': |S| -> u64
2092 450..451 's': S 2018 389..390 's': S
2093 453..454 's': S 2019 392..393 's': S
2094 453..463 's.method()': u64 2020 392..402 's.method()': u64
2095 465..466 'S': S 2021 404..405 'S': S
2096 477..479 'x3': u64 2022 416..418 'x3': u64
2097 482..483 'S': S 2023 421..422 'S': S
2098 482..507 'S.foo1...hod())': u64 2024 421..446 'S.foo1...hod())': u64
2099 489..490 'S': S 2025 428..429 'S': S
2100 492..506 '|s| s.method()': |S| -> u64 2026 431..445 '|s| s.method()': |S| -> u64
2101 493..494 's': S 2027 432..433 's': S
2102 496..497 's': S 2028 435..436 's': S
2103 496..506 's.method()': u64 2029 435..445 's.method()': u64
2104 517..519 'x4': u64 2030 456..458 'x4': u64
2105 522..523 'S': S 2031 461..462 'S': S
2106 522..547 'S.foo2...(), S)': u64 2032 461..486 'S.foo2...(), S)': u64
2107 529..543 '|s| s.method()': |S| -> u64 2033 468..482 '|s| s.method()': |S| -> u64
2108 530..531 's': S 2034 469..470 's': S
2109 533..534 's': S 2035 472..473 's': S
2110 533..543 's.method()': u64 2036 472..482 's.method()': u64
2111 545..546 'S': S 2037 484..485 'S': S
2112 "#]], 2038 "#]],
2113 ); 2039 );
2114} 2040}
@@ -2117,11 +2043,7 @@ fn test() {
2117fn fn_item_fn_trait() { 2043fn fn_item_fn_trait() {
2118 check_types( 2044 check_types(
2119 r#" 2045 r#"
2120#[lang = "fn_once"] 2046//- minicore: fn
2121trait FnOnce<Args> {
2122 type Output;
2123}
2124
2125struct S; 2047struct S;
2126 2048
2127fn foo() -> S {} 2049fn foo() -> S {}
@@ -2560,12 +2482,7 @@ fn test() -> impl Trait<i32> {
2560fn assoc_types_from_bounds() { 2482fn assoc_types_from_bounds() {
2561 check_infer( 2483 check_infer(
2562 r#" 2484 r#"
2563//- /main.rs 2485//- minicore: fn
2564#[lang = "fn_once"]
2565trait FnOnce<Args> {
2566 type Output;
2567}
2568
2569trait T { 2486trait T {
2570 type O; 2487 type O;
2571} 2488}
@@ -2584,15 +2501,15 @@ fn main() {
2584 f::<(), _>(|z| { z; }); 2501 f::<(), _>(|z| { z; });
2585}"#, 2502}"#,
2586 expect![[r#" 2503 expect![[r#"
2587 133..135 '_v': F 2504 72..74 '_v': F
2588 178..181 '{ }': () 2505 117..120 '{ }': ()
2589 193..224 '{ ... }); }': () 2506 132..163 '{ ... }); }': ()
2590 199..209 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ()) 2507 138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
2591 199..221 'f::<()... z; })': () 2508 138..160 'f::<()... z; })': ()
2592 210..220 '|z| { z; }': |&()| -> () 2509 149..159 '|z| { z; }': |&()| -> ()
2593 211..212 'z': &() 2510 150..151 'z': &()
2594 214..220 '{ z; }': () 2511 153..159 '{ z; }': ()
2595 216..217 'z': &() 2512 155..156 'z': &()
2596 "#]], 2513 "#]],
2597 ); 2514 );
2598} 2515}
@@ -2626,12 +2543,9 @@ fn test<T: Trait>() {
2626fn dyn_trait_through_chalk() { 2543fn dyn_trait_through_chalk() {
2627 check_types( 2544 check_types(
2628 r#" 2545 r#"
2546//- minicore: deref
2629struct Box<T> {} 2547struct Box<T> {}
2630#[lang = "deref"] 2548impl<T> core::ops::Deref for Box<T> {
2631trait Deref {
2632 type Target;
2633}
2634impl<T> Deref for Box<T> {
2635 type Target = T; 2549 type Target = T;
2636} 2550}
2637trait Trait { 2551trait Trait {
@@ -2668,17 +2582,7 @@ fn test() {
2668fn iterator_chain() { 2582fn iterator_chain() {
2669 check_infer_with_mismatches( 2583 check_infer_with_mismatches(
2670 r#" 2584 r#"
2671//- /main.rs 2585//- minicore: fn, option
2672#[lang = "fn_once"]
2673trait FnOnce<Args> {
2674 type Output;
2675}
2676#[lang = "fn_mut"]
2677trait FnMut<Args>: FnOnce<Args> { }
2678
2679enum Option<T> { Some(T), None }
2680use Option::*;
2681
2682pub trait Iterator { 2586pub trait Iterator {
2683 type Item; 2587 type Item;
2684 2588
@@ -2738,46 +2642,46 @@ fn main() {
2738 .for_each(|y| { y; }); 2642 .for_each(|y| { y; });
2739}"#, 2643}"#,
2740 expect![[r#" 2644 expect![[r#"
2741 226..230 'self': Self 2645 61..65 'self': Self
2742 232..233 'f': F 2646 67..68 'f': F
2743 317..328 '{ loop {} }': FilterMap<Self, F> 2647 152..163 '{ loop {} }': FilterMap<Self, F>
2744 319..326 'loop {}': ! 2648 154..161 'loop {}': !
2745 324..326 '{}': () 2649 159..161 '{}': ()
2746 349..353 'self': Self 2650 184..188 'self': Self
2747 355..356 'f': F 2651 190..191 'f': F
2748 405..416 '{ loop {} }': () 2652 240..251 '{ loop {} }': ()
2749 407..414 'loop {}': ! 2653 242..249 'loop {}': !
2750 412..414 '{}': () 2654 247..249 '{}': ()
2751 525..529 'self': Self 2655 360..364 'self': Self
2752 854..858 'self': I 2656 689..693 'self': I
2753 865..885 '{ ... }': I 2657 700..720 '{ ... }': I
2754 875..879 'self': I 2658 710..714 'self': I
2755 944..955 '{ loop {} }': Vec<T> 2659 779..790 '{ loop {} }': Vec<T>
2756 946..953 'loop {}': ! 2660 781..788 'loop {}': !
2757 951..953 '{}': () 2661 786..788 '{}': ()
2758 1142..1269 '{ ... }); }': () 2662 977..1104 '{ ... }); }': ()
2759 1148..1163 'Vec::<i32>::new': fn new<i32>() -> Vec<i32> 2663 983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
2760 1148..1165 'Vec::<...:new()': Vec<i32> 2664 983..1000 'Vec::<...:new()': Vec<i32>
2761 1148..1177 'Vec::<...iter()': IntoIter<i32> 2665 983..1012 'Vec::<...iter()': IntoIter<i32>
2762 1148..1240 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>> 2666 983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
2763 1148..1266 'Vec::<... y; })': () 2667 983..1101 'Vec::<... y; })': ()
2764 1194..1239 '|x| if...None }': |i32| -> Option<u32> 2668 1029..1074 '|x| if...None }': |i32| -> Option<u32>
2765 1195..1196 'x': i32 2669 1030..1031 'x': i32
2766 1198..1239 'if x >...None }': Option<u32> 2670 1033..1074 'if x >...None }': Option<u32>
2767 1201..1202 'x': i32 2671 1036..1037 'x': i32
2768 1201..1206 'x > 0': bool 2672 1036..1041 'x > 0': bool
2769 1205..1206 '0': i32 2673 1040..1041 '0': i32
2770 1207..1225 '{ Some...u32) }': Option<u32> 2674 1042..1060 '{ Some...u32) }': Option<u32>
2771 1209..1213 'Some': Some<u32>(u32) -> Option<u32> 2675 1044..1048 'Some': Some<u32>(u32) -> Option<u32>
2772 1209..1223 'Some(x as u32)': Option<u32> 2676 1044..1058 'Some(x as u32)': Option<u32>
2773 1214..1215 'x': i32 2677 1049..1050 'x': i32
2774 1214..1222 'x as u32': u32 2678 1049..1057 'x as u32': u32
2775 1231..1239 '{ None }': Option<u32> 2679 1066..1074 '{ None }': Option<u32>
2776 1233..1237 'None': Option<u32> 2680 1068..1072 'None': Option<u32>
2777 1255..1265 '|y| { y; }': |u32| -> () 2681 1090..1100 '|y| { y; }': |u32| -> ()
2778 1256..1257 'y': u32 2682 1091..1092 'y': u32
2779 1259..1265 '{ y; }': () 2683 1094..1100 '{ y; }': ()
2780 1261..1262 'y': u32 2684 1096..1097 'y': u32
2781 "#]], 2685 "#]],
2782 ); 2686 );
2783} 2687}
@@ -3064,45 +2968,23 @@ fn foo() {
3064fn infer_fn_trait_arg() { 2968fn infer_fn_trait_arg() {
3065 check_infer_with_mismatches( 2969 check_infer_with_mismatches(
3066 r#" 2970 r#"
3067 //- /lib.rs deps:std 2971//- minicore: fn, option
3068 2972fn foo<F, T>(f: F) -> T
3069 #[lang = "fn_once"] 2973where
3070 pub trait FnOnce<Args> { 2974 F: Fn(Option<i32>) -> T,
3071 type Output; 2975{
3072 2976 let s = None;
3073 extern "rust-call" fn call_once(&self, args: Args) -> Self::Output; 2977 f(s)
3074 } 2978}
3075 2979"#,
3076 #[lang = "fn"]
3077 pub trait Fn<Args>:FnOnce<Args> {
3078 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
3079 }
3080
3081 enum Option<T> {
3082 None,
3083 Some(T)
3084 }
3085
3086 fn foo<F, T>(f: F) -> T
3087 where
3088 F: Fn(Option<i32>) -> T,
3089 {
3090 let s = None;
3091 f(s)
3092 }
3093 "#,
3094 expect![[r#" 2980 expect![[r#"
3095 101..105 'self': &Self 2981 13..14 'f': F
3096 107..111 'args': Args 2982 59..89 '{ ...f(s) }': T
3097 220..224 'self': &Self 2983 69..70 's': Option<i32>
3098 226..230 'args': Args 2984 73..77 'None': Option<i32>
3099 313..314 'f': F 2985 83..84 'f': F
3100 359..389 '{ ...f(s) }': T 2986 83..87 'f(s)': T
3101 369..370 's': Option<i32> 2987 85..86 's': Option<i32>
3102 373..377 'None': Option<i32>
3103 383..384 'f': F
3104 383..387 'f(s)': T
3105 385..386 's': Option<i32>
3106 "#]], 2988 "#]],
3107 ); 2989 );
3108} 2990}
@@ -3181,17 +3063,7 @@ fn foo() {
3181fn infer_dyn_fn_output() { 3063fn infer_dyn_fn_output() {
3182 check_types( 3064 check_types(
3183 r#" 3065 r#"
3184#[lang = "fn_once"] 3066//- minicore: fn
3185pub trait FnOnce<Args> {
3186 type Output;
3187 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3188}
3189
3190#[lang = "fn"]
3191pub trait Fn<Args>: FnOnce<Args> {
3192 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
3193}
3194
3195fn foo() { 3067fn foo() {
3196 let f: &dyn Fn() -> i32; 3068 let f: &dyn Fn() -> i32;
3197 f(); 3069 f();
@@ -3204,12 +3076,7 @@ fn foo() {
3204fn infer_dyn_fn_once_output() { 3076fn infer_dyn_fn_once_output() {
3205 check_types( 3077 check_types(
3206 r#" 3078 r#"
3207#[lang = "fn_once"] 3079//- minicore: fn
3208pub trait FnOnce<Args> {
3209 type Output;
3210 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3211}
3212
3213fn foo() { 3080fn foo() {
3214 let f: dyn FnOnce() -> i32; 3081 let f: dyn FnOnce() -> i32;
3215 f(); 3082 f();
@@ -3644,20 +3511,16 @@ fn main() {
3644fn fn_returning_unit() { 3511fn fn_returning_unit() {
3645 check_infer_with_mismatches( 3512 check_infer_with_mismatches(
3646 r#" 3513 r#"
3647#[lang = "fn_once"] 3514//- minicore: fn
3648trait FnOnce<Args> {
3649 type Output;
3650}
3651
3652fn test<F: FnOnce()>(f: F) { 3515fn test<F: FnOnce()>(f: F) {
3653 let _: () = f(); 3516 let _: () = f();
3654}"#, 3517}"#,
3655 expect![[r#" 3518 expect![[r#"
3656 82..83 'f': F 3519 21..22 'f': F
3657 88..112 '{ ...f(); }': () 3520 27..51 '{ ...f(); }': ()
3658 98..99 '_': () 3521 37..38 '_': ()
3659 106..107 'f': F 3522 45..46 'f': F
3660 106..109 'f()': () 3523 45..48 'f()': ()
3661 "#]], 3524 "#]],
3662 ); 3525 );
3663} 3526}
@@ -3696,16 +3559,7 @@ impl foo::Foo for u32 {
3696fn infer_async_ret_type() { 3559fn infer_async_ret_type() {
3697 check_types( 3560 check_types(
3698 r#" 3561 r#"
3699//- /main.rs crate:main deps:core 3562//- minicore: future, result
3700
3701enum Result<T, E> {
3702 Ok(T),
3703 Err(E),
3704}
3705
3706use Result::*;
3707
3708
3709struct Fooey; 3563struct Fooey;
3710 3564
3711impl Fooey { 3565impl Fooey {
@@ -3728,13 +3582,71 @@ async fn get_accounts() -> Result<u32, ()> {
3728 // ^ u32 3582 // ^ u32
3729 Ok(ret) 3583 Ok(ret)
3730} 3584}
3585"#,
3586 );
3587}
3731 3588
3732//- /core.rs crate:core 3589#[test]
3733#[prelude_import] use future::*; 3590fn local_impl_1() {
3734mod future { 3591 check_types(
3735 #[lang = "future_trait"] 3592 r#"
3736 trait Future { 3593trait Trait<T> {
3737 type Output; 3594 fn foo(&self) -> T;
3595}
3596
3597fn test() {
3598 struct S;
3599 impl Trait<u32> for S {
3600 fn foo(&self) { 0 }
3601 }
3602
3603 S.foo();
3604 // ^^^^^^^ u32
3605}
3606"#,
3607 );
3608}
3609
3610#[test]
3611fn local_impl_2() {
3612 check_types(
3613 r#"
3614struct S;
3615
3616fn test() {
3617 trait Trait<T> {
3618 fn foo(&self) -> T;
3619 }
3620 impl Trait<u32> for S {
3621 fn foo(&self) { 0 }
3622 }
3623
3624 S.foo();
3625 // ^^^^^^^ u32
3626}
3627"#,
3628 );
3629}
3630
3631#[test]
3632fn local_impl_3() {
3633 check_types(
3634 r#"
3635trait Trait<T> {
3636 fn foo(&self) -> T;
3637}
3638
3639fn test() {
3640 struct S1;
3641 {
3642 struct S2;
3643
3644 impl Trait<S1> for S2 {
3645 fn foo(&self) { S1 }
3646 }
3647
3648 S2.foo();
3649 // ^^^^^^^^ S1
3738 } 3650 }
3739} 3651}
3740"#, 3652"#,
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 88f3d09d3..0e8447394 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14either = "1.5.3" 14either = "1.5.3"
15indexmap = "1.4.0" 15indexmap = "1.4.0"
16itertools = "0.10.0" 16itertools = "0.10.0"
@@ -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
@@ -39,4 +40,3 @@ hir = { path = "../hir", version = "0.0.0" }
39[dev-dependencies] 40[dev-dependencies]
40test_utils = { path = "../test_utils" } 41test_utils = { path = "../test_utils" }
41expect-test = "1.1" 42expect-test = "1.1"
42cov-mark = "1.1.0"
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
deleted file mode 100644
index d5c954b8b..000000000
--- a/crates/ide/src/diagnostics.rs
+++ /dev/null
@@ -1,722 +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 fixes;
8mod field_shorthand;
9mod unlinked_file;
10
11use std::cell::RefCell;
12
13use hir::{
14 db::AstDatabase,
15 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
16 InFile, Semantics,
17};
18use ide_assists::AssistResolveStrategy;
19use ide_db::{base_db::SourceDatabase, RootDatabase};
20use itertools::Itertools;
21use rustc_hash::FxHashSet;
22use syntax::{
23 ast::{self, AstNode},
24 SyntaxNode, SyntaxNodePtr, TextRange, TextSize,
25};
26use text_edit::TextEdit;
27use unlinked_file::UnlinkedFile;
28
29use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
30
31use self::fixes::DiagnosticWithFixes;
32
33#[derive(Debug)]
34pub struct Diagnostic {
35 // pub name: Option<String>,
36 pub message: String,
37 pub range: TextRange,
38 pub severity: Severity,
39 pub fixes: Option<Vec<Assist>>,
40 pub unused: bool,
41 pub code: Option<DiagnosticCode>,
42}
43
44impl Diagnostic {
45 fn error(range: TextRange, message: String) -> Self {
46 Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None }
47 }
48
49 fn hint(range: TextRange, message: String) -> Self {
50 Self {
51 message,
52 range,
53 severity: Severity::WeakWarning,
54 fixes: None,
55 unused: false,
56 code: None,
57 }
58 }
59
60 fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self {
61 Self { fixes, ..self }
62 }
63
64 fn with_unused(self, unused: bool) -> Self {
65 Self { unused, ..self }
66 }
67
68 fn with_code(self, code: Option<DiagnosticCode>) -> Self {
69 Self { code, ..self }
70 }
71}
72
73#[derive(Debug, Copy, Clone)]
74pub enum Severity {
75 Error,
76 WeakWarning,
77}
78
79#[derive(Default, Debug, Clone)]
80pub struct DiagnosticsConfig {
81 pub disable_experimental: bool,
82 pub disabled: FxHashSet<String>,
83}
84
85pub(crate) fn diagnostics(
86 db: &RootDatabase,
87 config: &DiagnosticsConfig,
88 resolve: &AssistResolveStrategy,
89 file_id: FileId,
90) -> Vec<Diagnostic> {
91 let _p = profile::span("diagnostics");
92 let sema = Semantics::new(db);
93 let parse = db.parse(file_id);
94 let mut res = Vec::new();
95
96 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
97 res.extend(
98 parse
99 .errors()
100 .iter()
101 .take(128)
102 .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
103 );
104
105 for node in parse.tree().syntax().descendants() {
106 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
107 field_shorthand::check(&mut res, file_id, &node);
108 }
109 let res = RefCell::new(res);
110 let sink_builder = DiagnosticSinkBuilder::new()
111 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
112 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
113 })
114 .on::<hir::diagnostics::MissingFields, _>(|d| {
115 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
116 })
117 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
118 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
119 })
120 .on::<hir::diagnostics::NoSuchField, _>(|d| {
121 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
122 })
123 .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
124 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
125 })
126 .on::<hir::diagnostics::IncorrectCase, _>(|d| {
127 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
128 })
129 .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
130 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
131 })
132 .on::<hir::diagnostics::InactiveCode, _>(|d| {
133 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
134 if d.display_source().file_id.expansion_info(db).is_some() {
135 return;
136 }
137
138 // Override severity and mark as unused.
139 res.borrow_mut().push(
140 Diagnostic::hint(
141 sema.diagnostics_display_range(d.display_source()).range,
142 d.message(),
143 )
144 .with_unused(true)
145 .with_code(Some(d.code())),
146 );
147 })
148 .on::<UnlinkedFile, _>(|d| {
149 // Limit diagnostic to the first few characters in the file. This matches how VS Code
150 // renders it with the full span, but on other editors, and is less invasive.
151 let range = sema.diagnostics_display_range(d.display_source()).range;
152 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
153
154 // Override severity and mark as unused.
155 res.borrow_mut().push(
156 Diagnostic::hint(range, d.message())
157 .with_fixes(d.fixes(&sema, resolve))
158 .with_code(Some(d.code())),
159 );
160 })
161 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
162 // Use more accurate position if available.
163 let display_range = d
164 .precise_location
165 .unwrap_or_else(|| sema.diagnostics_display_range(d.display_source()).range);
166
167 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
168 res.borrow_mut()
169 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
170 })
171 .on::<hir::diagnostics::UnresolvedMacroCall, _>(|d| {
172 let last_path_segment = sema.db.parse_or_expand(d.file).and_then(|root| {
173 d.node
174 .to_node(&root)
175 .path()
176 .and_then(|it| it.segment())
177 .and_then(|it| it.name_ref())
178 .map(|it| InFile::new(d.file, SyntaxNodePtr::new(it.syntax())))
179 });
180 let diagnostics = last_path_segment.unwrap_or_else(|| d.display_source());
181 let display_range = sema.diagnostics_display_range(diagnostics).range;
182 res.borrow_mut()
183 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code())));
184 })
185 .on::<hir::diagnostics::UnimplementedBuiltinMacro, _>(|d| {
186 let display_range = sema.diagnostics_display_range(d.display_source()).range;
187 res.borrow_mut()
188 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
189 })
190 // Only collect experimental diagnostics when they're enabled.
191 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
192 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
193
194 // Finalize the `DiagnosticSink` building process.
195 let mut sink = sink_builder
196 // Diagnostics not handled above get no fix and default treatment.
197 .build(|d| {
198 res.borrow_mut().push(
199 Diagnostic::error(
200 sema.diagnostics_display_range(d.display_source()).range,
201 d.message(),
202 )
203 .with_code(Some(d.code())),
204 );
205 });
206
207 match sema.to_module_def(file_id) {
208 Some(m) => m.diagnostics(db, &mut sink),
209 None => {
210 sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(&parse.tree().syntax()) });
211 }
212 }
213
214 drop(sink);
215 res.into_inner()
216}
217
218fn diagnostic_with_fix<D: DiagnosticWithFixes>(
219 d: &D,
220 sema: &Semantics<RootDatabase>,
221 resolve: &AssistResolveStrategy,
222) -> Diagnostic {
223 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
224 .with_fixes(d.fixes(&sema, resolve))
225 .with_code(Some(d.code()))
226}
227
228fn warning_with_fix<D: DiagnosticWithFixes>(
229 d: &D,
230 sema: &Semantics<RootDatabase>,
231 resolve: &AssistResolveStrategy,
232) -> Diagnostic {
233 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
234 .with_fixes(d.fixes(&sema, resolve))
235 .with_code(Some(d.code()))
236}
237
238fn check_unnecessary_braces_in_use_statement(
239 acc: &mut Vec<Diagnostic>,
240 file_id: FileId,
241 node: &SyntaxNode,
242) -> Option<()> {
243 let use_tree_list = ast::UseTreeList::cast(node.clone())?;
244 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
245 // If there is a comment inside the bracketed `use`,
246 // assume it is a commented out module path and don't show diagnostic.
247 if use_tree_list.has_inner_comment() {
248 return Some(());
249 }
250
251 let use_range = use_tree_list.syntax().text_range();
252 let edit =
253 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
254 .unwrap_or_else(|| {
255 let to_replace = single_use_tree.syntax().text().to_string();
256 let mut edit_builder = TextEdit::builder();
257 edit_builder.delete(use_range);
258 edit_builder.insert(use_range.start(), to_replace);
259 edit_builder.finish()
260 });
261
262 acc.push(
263 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
264 .with_fixes(Some(vec![fix(
265 "remove_braces",
266 "Remove unnecessary braces",
267 SourceChange::from_text_edit(file_id, edit),
268 use_range,
269 )])),
270 );
271 }
272
273 Some(())
274}
275
276fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
277 single_use_tree: &ast::UseTree,
278) -> Option<TextEdit> {
279 let use_tree_list_node = single_use_tree.syntax().parent()?;
280 if single_use_tree.path()?.segment()?.self_token().is_some() {
281 let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
282 let end = use_tree_list_node.text_range().end();
283 return Some(TextEdit::delete(TextRange::new(start, end)));
284 }
285 None
286}
287
288fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
289 let mut res = unresolved_fix(id, label, target);
290 res.source_change = Some(source_change);
291 res
292}
293
294fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
295 assert!(!id.contains(' '));
296 Assist {
297 id: AssistId(id, AssistKind::QuickFix),
298 label: Label::new(label),
299 group: None,
300 target,
301 source_change: None,
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use expect_test::Expect;
308 use ide_assists::AssistResolveStrategy;
309 use stdx::trim_indent;
310 use test_utils::{assert_eq_text, extract_annotations};
311
312 use crate::{fixture, DiagnosticsConfig};
313
314 /// Takes a multi-file input fixture with annotated cursor positions,
315 /// and checks that:
316 /// * a diagnostic is produced
317 /// * the first diagnostic fix trigger range touches the input cursor position
318 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
319 #[track_caller]
320 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
321 check_nth_fix(0, ra_fixture_before, ra_fixture_after);
322 }
323 /// Takes a multi-file input fixture with annotated cursor positions,
324 /// and checks that:
325 /// * a diagnostic is produced
326 /// * every diagnostic fixes trigger range touches the input cursor position
327 /// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
328 pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
329 for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
330 check_nth_fix(i, ra_fixture_before, ra_fixture_after)
331 }
332 }
333
334 #[track_caller]
335 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
336 let after = trim_indent(ra_fixture_after);
337
338 let (analysis, file_position) = fixture::position(ra_fixture_before);
339 let diagnostic = analysis
340 .diagnostics(
341 &DiagnosticsConfig::default(),
342 AssistResolveStrategy::All,
343 file_position.file_id,
344 )
345 .unwrap()
346 .pop()
347 .unwrap();
348 let fix = &diagnostic.fixes.unwrap()[nth];
349 let actual = {
350 let source_change = fix.source_change.as_ref().unwrap();
351 let file_id = *source_change.source_file_edits.keys().next().unwrap();
352 let mut actual = analysis.file_text(file_id).unwrap().to_string();
353
354 for edit in source_change.source_file_edits.values() {
355 edit.apply(&mut actual);
356 }
357 actual
358 };
359
360 assert_eq_text!(&after, &actual);
361 assert!(
362 fix.target.contains_inclusive(file_position.offset),
363 "diagnostic fix range {:?} does not touch cursor position {:?}",
364 fix.target,
365 file_position.offset
366 );
367 }
368 /// Checks that there's a diagnostic *without* fix at `$0`.
369 fn check_no_fix(ra_fixture: &str) {
370 let (analysis, file_position) = fixture::position(ra_fixture);
371 let diagnostic = analysis
372 .diagnostics(
373 &DiagnosticsConfig::default(),
374 AssistResolveStrategy::All,
375 file_position.file_id,
376 )
377 .unwrap()
378 .pop()
379 .unwrap();
380 assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
381 }
382
383 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
384 /// apply to the file containing the cursor.
385 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
386 let (analysis, files) = fixture::files(ra_fixture);
387 let diagnostics = files
388 .into_iter()
389 .flat_map(|file_id| {
390 analysis
391 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
392 .unwrap()
393 })
394 .collect::<Vec<_>>();
395 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
396 }
397
398 pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
399 let (analysis, file_id) = fixture::file(ra_fixture);
400 let diagnostics = analysis
401 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
402 .unwrap();
403 expect.assert_debug_eq(&diagnostics)
404 }
405
406 pub(crate) fn check_diagnostics(ra_fixture: &str) {
407 let (analysis, file_id) = fixture::file(ra_fixture);
408 let diagnostics = analysis
409 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
410 .unwrap();
411
412 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
413 let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
414 assert_eq!(expected, actual);
415 }
416
417 #[test]
418 fn test_unresolved_macro_range() {
419 check_diagnostics(
420 r#"
421foo::bar!(92);
422 //^^^ unresolved macro `foo::bar!`
423"#,
424 );
425 }
426
427 #[test]
428 fn unresolved_import_in_use_tree() {
429 // Only the relevant part of a nested `use` item should be highlighted.
430 check_diagnostics(
431 r#"
432use does_exist::{Exists, DoesntExist};
433 //^^^^^^^^^^^ unresolved import
434
435use {does_not_exist::*, does_exist};
436 //^^^^^^^^^^^^^^^^^ unresolved import
437
438use does_not_exist::{
439 a,
440 //^ unresolved import
441 b,
442 //^ unresolved import
443 c,
444 //^ unresolved import
445};
446
447mod does_exist {
448 pub struct Exists;
449}
450"#,
451 );
452 }
453
454 #[test]
455 fn range_mapping_out_of_macros() {
456 // FIXME: this is very wrong, but somewhat tricky to fix.
457 check_fix(
458 r#"
459fn some() {}
460fn items() {}
461fn here() {}
462
463macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
464
465fn main() {
466 let _x = id![Foo { a: $042 }];
467}
468
469pub struct Foo { pub a: i32, pub b: i32 }
470"#,
471 r#"
472fn some(, b: () ) {}
473fn items() {}
474fn here() {}
475
476macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
477
478fn main() {
479 let _x = id![Foo { a: 42 }];
480}
481
482pub struct Foo { pub a: i32, pub b: i32 }
483"#,
484 );
485 }
486
487 #[test]
488 fn test_check_unnecessary_braces_in_use_statement() {
489 check_no_diagnostics(
490 r#"
491use a;
492use a::{c, d::e};
493
494mod a {
495 mod c {}
496 mod d {
497 mod e {}
498 }
499}
500"#,
501 );
502 check_no_diagnostics(
503 r#"
504use a;
505use a::{
506 c,
507 // d::e
508};
509
510mod a {
511 mod c {}
512 mod d {
513 mod e {}
514 }
515}
516"#,
517 );
518 check_fix(
519 r"
520 mod b {}
521 use {$0b};
522 ",
523 r"
524 mod b {}
525 use b;
526 ",
527 );
528 check_fix(
529 r"
530 mod b {}
531 use {b$0};
532 ",
533 r"
534 mod b {}
535 use b;
536 ",
537 );
538 check_fix(
539 r"
540 mod a { mod c {} }
541 use a::{c$0};
542 ",
543 r"
544 mod a { mod c {} }
545 use a::c;
546 ",
547 );
548 check_fix(
549 r"
550 mod a {}
551 use a::{self$0};
552 ",
553 r"
554 mod a {}
555 use a;
556 ",
557 );
558 check_fix(
559 r"
560 mod a { mod c {} mod d { mod e {} } }
561 use a::{c, d::{e$0}};
562 ",
563 r"
564 mod a { mod c {} mod d { mod e {} } }
565 use a::{c, d::e};
566 ",
567 );
568 }
569
570 #[test]
571 fn test_disabled_diagnostics() {
572 let mut config = DiagnosticsConfig::default();
573 config.disabled.insert("unresolved-module".into());
574
575 let (analysis, file_id) = fixture::file(r#"mod foo;"#);
576
577 let diagnostics =
578 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
579 assert!(diagnostics.is_empty());
580
581 let diagnostics = analysis
582 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
583 .unwrap();
584 assert!(!diagnostics.is_empty());
585 }
586
587 #[test]
588 fn unlinked_file_prepend_first_item() {
589 cov_mark::check!(unlinked_file_prepend_before_first_item);
590 // Only tests the first one for `pub mod` since the rest are the same
591 check_fixes(
592 r#"
593//- /main.rs
594fn f() {}
595//- /foo.rs
596$0
597"#,
598 vec![
599 r#"
600mod foo;
601
602fn f() {}
603"#,
604 r#"
605pub mod foo;
606
607fn f() {}
608"#,
609 ],
610 );
611 }
612
613 #[test]
614 fn unlinked_file_append_mod() {
615 cov_mark::check!(unlinked_file_append_to_existing_mods);
616 check_fix(
617 r#"
618//- /main.rs
619//! Comment on top
620
621mod preexisting;
622
623mod preexisting2;
624
625struct S;
626
627mod preexisting_bottom;)
628//- /foo.rs
629$0
630"#,
631 r#"
632//! Comment on top
633
634mod preexisting;
635
636mod preexisting2;
637mod foo;
638
639struct S;
640
641mod preexisting_bottom;)
642"#,
643 );
644 }
645
646 #[test]
647 fn unlinked_file_insert_in_empty_file() {
648 cov_mark::check!(unlinked_file_empty_file);
649 check_fix(
650 r#"
651//- /main.rs
652//- /foo.rs
653$0
654"#,
655 r#"
656mod foo;
657"#,
658 );
659 }
660
661 #[test]
662 fn unlinked_file_old_style_modrs() {
663 check_fix(
664 r#"
665//- /main.rs
666mod submod;
667//- /submod/mod.rs
668// in mod.rs
669//- /submod/foo.rs
670$0
671"#,
672 r#"
673// in mod.rs
674mod foo;
675"#,
676 );
677 }
678
679 #[test]
680 fn unlinked_file_new_style_mod() {
681 check_fix(
682 r#"
683//- /main.rs
684mod submod;
685//- /submod.rs
686//- /submod/foo.rs
687$0
688"#,
689 r#"
690mod foo;
691"#,
692 );
693 }
694
695 #[test]
696 fn unlinked_file_with_cfg_off() {
697 cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists);
698 check_no_fix(
699 r#"
700//- /main.rs
701#[cfg(never)]
702mod foo;
703
704//- /foo.rs
705$0
706"#,
707 );
708 }
709
710 #[test]
711 fn unlinked_file_with_cfg_on() {
712 check_no_diagnostics(
713 r#"
714//- /main.rs
715#[cfg(not(never))]
716mod foo;
717
718//- /foo.rs
719"#,
720 );
721 }
722}
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
deleted file mode 100644
index 258ac6974..000000000
--- a/crates/ide/src/diagnostics/fixes.rs
+++ /dev/null
@@ -1,31 +0,0 @@
1//! Provides a way to attach fixes to the diagnostics.
2//! The same module also has all curret custom fixes for the diagnostics implemented.
3mod change_case;
4mod create_field;
5mod fill_missing_fields;
6mod remove_semicolon;
7mod replace_with_find_map;
8mod unresolved_module;
9mod wrap_tail_expr;
10
11use hir::{diagnostics::Diagnostic, Semantics};
12use ide_assists::AssistResolveStrategy;
13use ide_db::RootDatabase;
14
15use crate::Assist;
16
17/// A [Diagnostic] that potentially has some fixes available.
18///
19/// [Diagnostic]: hir::diagnostics::Diagnostic
20pub(crate) trait DiagnosticWithFixes: Diagnostic {
21 /// `resolve` determines if the diagnostic should fill in the `edit` field
22 /// of the assist.
23 ///
24 /// If `resolve` is false, the edit will be computed later, on demand, and
25 /// can be omitted.
26 fn fixes(
27 &self,
28 sema: &Semantics<RootDatabase>,
29 _resolve: &AssistResolveStrategy,
30 ) -> Option<Vec<Assist>>;
31}
diff --git a/crates/ide/src/diagnostics/fixes/change_case.rs b/crates/ide/src/diagnostics/fixes/change_case.rs
deleted file mode 100644
index 42be3375f..000000000
--- a/crates/ide/src/diagnostics/fixes/change_case.rs
+++ /dev/null
@@ -1,155 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics};
2use ide_assists::{Assist, AssistResolveStrategy};
3use ide_db::{base_db::FilePosition, RootDatabase};
4use syntax::AstNode;
5
6use crate::{
7 diagnostics::{unresolved_fix, DiagnosticWithFixes},
8 references::rename::rename_with_semantics,
9};
10
11impl DiagnosticWithFixes for IncorrectCase {
12 fn fixes(
13 &self,
14 sema: &Semantics<RootDatabase>,
15 resolve: &AssistResolveStrategy,
16 ) -> Option<Vec<Assist>> {
17 let root = sema.db.parse_or_expand(self.file)?;
18 let name_node = self.ident.to_node(&root);
19
20 let name_node = InFile::new(self.file, name_node.syntax());
21 let frange = name_node.original_file_range(sema.db);
22 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
23
24 let label = format!("Rename to {}", self.suggested_text);
25 let mut res = unresolved_fix("change_case", &label, frange.range);
26 if resolve.should_resolve(&res.id) {
27 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
28 res.source_change = Some(source_change.ok().unwrap_or_default());
29 }
30
31 Some(vec![res])
32 }
33}
34
35#[cfg(test)]
36mod change_case {
37 use crate::{
38 diagnostics::tests::{check_fix, check_no_diagnostics},
39 fixture, AssistResolveStrategy, DiagnosticsConfig,
40 };
41
42 #[test]
43 fn test_rename_incorrect_case() {
44 check_fix(
45 r#"
46pub struct test_struct$0 { one: i32 }
47
48pub fn some_fn(val: test_struct) -> test_struct {
49 test_struct { one: val.one + 1 }
50}
51"#,
52 r#"
53pub struct TestStruct { one: i32 }
54
55pub fn some_fn(val: TestStruct) -> TestStruct {
56 TestStruct { one: val.one + 1 }
57}
58"#,
59 );
60
61 check_fix(
62 r#"
63pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
64 NonSnakeCase
65}
66"#,
67 r#"
68pub fn some_fn(non_snake_case: u8) -> u8 {
69 non_snake_case
70}
71"#,
72 );
73
74 check_fix(
75 r#"
76pub fn SomeFn$0(val: u8) -> u8 {
77 if val != 0 { SomeFn(val - 1) } else { val }
78}
79"#,
80 r#"
81pub fn some_fn(val: u8) -> u8 {
82 if val != 0 { some_fn(val - 1) } else { val }
83}
84"#,
85 );
86
87 check_fix(
88 r#"
89fn some_fn() {
90 let whatAWeird_Formatting$0 = 10;
91 another_func(whatAWeird_Formatting);
92}
93"#,
94 r#"
95fn some_fn() {
96 let what_a_weird_formatting = 10;
97 another_func(what_a_weird_formatting);
98}
99"#,
100 );
101 }
102
103 #[test]
104 fn test_uppercase_const_no_diagnostics() {
105 check_no_diagnostics(
106 r#"
107fn foo() {
108 const ANOTHER_ITEM$0: &str = "some_item";
109}
110"#,
111 );
112 }
113
114 #[test]
115 fn test_rename_incorrect_case_struct_method() {
116 check_fix(
117 r#"
118pub struct TestStruct;
119
120impl TestStruct {
121 pub fn SomeFn$0() -> TestStruct {
122 TestStruct
123 }
124}
125"#,
126 r#"
127pub struct TestStruct;
128
129impl TestStruct {
130 pub fn some_fn() -> TestStruct {
131 TestStruct
132 }
133}
134"#,
135 );
136 }
137
138 #[test]
139 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
140 let input = r#"fn FOO$0() {}"#;
141 let expected = r#"fn foo() {}"#;
142
143 let (analysis, file_position) = fixture::position(input);
144 let diagnostics = analysis
145 .diagnostics(
146 &DiagnosticsConfig::default(),
147 AssistResolveStrategy::All,
148 file_position.file_id,
149 )
150 .unwrap();
151 assert_eq!(diagnostics.len(), 1);
152
153 check_fix(input, expected);
154 }
155}
diff --git a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs b/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
deleted file mode 100644
index b5dd64c08..000000000
--- a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
+++ /dev/null
@@ -1,217 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::MissingFields, Semantics};
2use ide_assists::AssistResolveStrategy;
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::{algo, ast::make, AstNode};
5use text_edit::TextEdit;
6
7use crate::{
8 diagnostics::{fix, fixes::DiagnosticWithFixes},
9 Assist,
10};
11
12impl DiagnosticWithFixes for MissingFields {
13 fn fixes(
14 &self,
15 sema: &Semantics<RootDatabase>,
16 _resolve: &AssistResolveStrategy,
17 ) -> Option<Vec<Assist>> {
18 // Note that although we could add a diagnostics to
19 // fill the missing tuple field, e.g :
20 // `struct A(usize);`
21 // `let a = A { 0: () }`
22 // but it is uncommon usage and it should not be encouraged.
23 if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
24 return None;
25 }
26
27 let root = sema.db.parse_or_expand(self.file)?;
28 let field_list_parent = self.field_list_parent.to_node(&root);
29 let old_field_list = field_list_parent.record_expr_field_list()?;
30 let new_field_list = old_field_list.clone_for_update();
31 for f in self.missed_fields.iter() {
32 let field =
33 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
34 .clone_for_update();
35 new_field_list.add_field(field);
36 }
37
38 let edit = {
39 let mut builder = TextEdit::builder();
40 algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
41 .into_text_edit(&mut builder);
42 builder.finish()
43 };
44 Some(vec![fix(
45 "fill_missing_fields",
46 "Fill struct fields",
47 SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
48 sema.original_range(&field_list_parent.syntax()).range,
49 )])
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
56
57 #[test]
58 fn test_fill_struct_fields_empty() {
59 check_fix(
60 r#"
61struct TestStruct { one: i32, two: i64 }
62
63fn test_fn() {
64 let s = TestStruct {$0};
65}
66"#,
67 r#"
68struct TestStruct { one: i32, two: i64 }
69
70fn test_fn() {
71 let s = TestStruct { one: (), two: () };
72}
73"#,
74 );
75 }
76
77 #[test]
78 fn test_fill_struct_fields_self() {
79 check_fix(
80 r#"
81struct TestStruct { one: i32 }
82
83impl TestStruct {
84 fn test_fn() { let s = Self {$0}; }
85}
86"#,
87 r#"
88struct TestStruct { one: i32 }
89
90impl TestStruct {
91 fn test_fn() { let s = Self { one: () }; }
92}
93"#,
94 );
95 }
96
97 #[test]
98 fn test_fill_struct_fields_enum() {
99 check_fix(
100 r#"
101enum Expr {
102 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
103}
104
105impl Expr {
106 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
107 Expr::Bin {$0 }
108 }
109}
110"#,
111 r#"
112enum Expr {
113 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
114}
115
116impl Expr {
117 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
118 Expr::Bin { lhs: (), rhs: () }
119 }
120}
121"#,
122 );
123 }
124
125 #[test]
126 fn test_fill_struct_fields_partial() {
127 check_fix(
128 r#"
129struct TestStruct { one: i32, two: i64 }
130
131fn test_fn() {
132 let s = TestStruct{ two: 2$0 };
133}
134"#,
135 r"
136struct TestStruct { one: i32, two: i64 }
137
138fn test_fn() {
139 let s = TestStruct{ two: 2, one: () };
140}
141",
142 );
143 }
144
145 #[test]
146 fn test_fill_struct_fields_raw_ident() {
147 check_fix(
148 r#"
149struct TestStruct { r#type: u8 }
150
151fn test_fn() {
152 TestStruct { $0 };
153}
154"#,
155 r"
156struct TestStruct { r#type: u8 }
157
158fn test_fn() {
159 TestStruct { r#type: () };
160}
161",
162 );
163 }
164
165 #[test]
166 fn test_fill_struct_fields_no_diagnostic() {
167 check_no_diagnostics(
168 r#"
169struct TestStruct { one: i32, two: i64 }
170
171fn test_fn() {
172 let one = 1;
173 let s = TestStruct{ one, two: 2 };
174}
175 "#,
176 );
177 }
178
179 #[test]
180 fn test_fill_struct_fields_no_diagnostic_on_spread() {
181 check_no_diagnostics(
182 r#"
183struct TestStruct { one: i32, two: i64 }
184
185fn test_fn() {
186 let one = 1;
187 let s = TestStruct{ ..a };
188}
189"#,
190 );
191 }
192
193 #[test]
194 fn test_fill_struct_fields_blank_line() {
195 check_fix(
196 r#"
197struct S { a: (), b: () }
198
199fn f() {
200 S {
201 $0
202 };
203}
204"#,
205 r#"
206struct S { a: (), b: () }
207
208fn f() {
209 S {
210 a: (),
211 b: (),
212 };
213}
214"#,
215 );
216 }
217}
diff --git a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs
deleted file mode 100644
index f1724d479..000000000
--- a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::RemoveThisSemicolon, Semantics};
2use ide_assists::{Assist, AssistResolveStrategy};
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::{ast, AstNode};
5use text_edit::TextEdit;
6
7use crate::diagnostics::{fix, DiagnosticWithFixes};
8
9impl DiagnosticWithFixes for RemoveThisSemicolon {
10 fn fixes(
11 &self,
12 sema: &Semantics<RootDatabase>,
13 _resolve: &AssistResolveStrategy,
14 ) -> Option<Vec<Assist>> {
15 let root = sema.db.parse_or_expand(self.file)?;
16
17 let semicolon = self
18 .expr
19 .to_node(&root)
20 .syntax()
21 .parent()
22 .and_then(ast::ExprStmt::cast)
23 .and_then(|expr| expr.semicolon_token())?
24 .text_range();
25
26 let edit = TextEdit::delete(semicolon);
27 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
28
29 Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)])
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use crate::diagnostics::tests::check_fix;
36
37 #[test]
38 fn remove_semicolon() {
39 check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
40 }
41}
diff --git a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs b/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
deleted file mode 100644
index 444bf563b..000000000
--- a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
+++ /dev/null
@@ -1,84 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::ReplaceFilterMapNextWithFindMap, Semantics};
2use ide_assists::{Assist, AssistResolveStrategy};
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::{
5 ast::{self, ArgListOwner},
6 AstNode, TextRange,
7};
8use text_edit::TextEdit;
9
10use crate::diagnostics::{fix, DiagnosticWithFixes};
11
12impl DiagnosticWithFixes for ReplaceFilterMapNextWithFindMap {
13 fn fixes(
14 &self,
15 sema: &Semantics<RootDatabase>,
16 _resolve: &AssistResolveStrategy,
17 ) -> Option<Vec<Assist>> {
18 let root = sema.db.parse_or_expand(self.file)?;
19 let next_expr = self.next_expr.to_node(&root);
20 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
21
22 let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
23 let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
24 let filter_map_args = filter_map_call.arg_list()?;
25
26 let range_to_replace =
27 TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
28 let replacement = format!("find_map{}", filter_map_args.syntax().text());
29 let trigger_range = next_expr.syntax().text_range();
30
31 let edit = TextEdit::replace(range_to_replace, replacement);
32
33 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
34
35 Some(vec![fix(
36 "replace_with_find_map",
37 "Replace filter_map(..).next() with find_map()",
38 source_change,
39 trigger_range,
40 )])
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::diagnostics::tests::check_fix;
47
48 #[test]
49 fn replace_with_wind_map() {
50 check_fix(
51 r#"
52//- /main.rs crate:main deps:core
53use core::iter::Iterator;
54use core::option::Option::{self, Some, None};
55fn foo() {
56 let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
57}
58//- /core/lib.rs crate:core
59pub mod option {
60 pub enum Option<T> { Some(T), None }
61}
62pub mod iter {
63 pub trait Iterator {
64 type Item;
65 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
66 fn next(&mut self) -> Option<Self::Item>;
67 }
68 pub struct FilterMap {}
69 impl Iterator for FilterMap {
70 type Item = i32;
71 fn next(&mut self) -> i32 { 7 }
72 }
73}
74"#,
75 r#"
76use core::iter::Iterator;
77use core::option::Option::{self, Some, None};
78fn foo() {
79 let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None });
80}
81"#,
82 )
83 }
84}
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
deleted file mode 100644
index 51fe0f360..000000000
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ /dev/null
@@ -1,183 +0,0 @@
1//! Diagnostic emitted for files that aren't part of any crate.
2
3use hir::{
4 db::DefDatabase,
5 diagnostics::{Diagnostic, DiagnosticCode},
6 InFile,
7};
8use ide_assists::AssistResolveStrategy;
9use ide_db::{
10 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
11 source_change::SourceChange,
12 RootDatabase,
13};
14use syntax::{
15 ast::{self, ModuleItemOwner, NameOwner},
16 AstNode, SyntaxNodePtr,
17};
18use text_edit::TextEdit;
19
20use crate::{
21 diagnostics::{fix, fixes::DiagnosticWithFixes},
22 Assist,
23};
24
25// Diagnostic: unlinked-file
26//
27// 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.
29#[derive(Debug)]
30pub(crate) struct UnlinkedFile {
31 pub(crate) file_id: FileId,
32 pub(crate) node: SyntaxNodePtr,
33}
34
35impl Diagnostic for UnlinkedFile {
36 fn code(&self) -> DiagnosticCode {
37 DiagnosticCode("unlinked-file")
38 }
39
40 fn message(&self) -> String {
41 "file not included in module tree".to_string()
42 }
43
44 fn display_source(&self) -> InFile<SyntaxNodePtr> {
45 InFile::new(self.file_id.into(), self.node.clone())
46 }
47
48 fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) {
49 self
50 }
51}
52
53impl DiagnosticWithFixes for UnlinkedFile {
54 fn fixes(
55 &self,
56 sema: &hir::Semantics<RootDatabase>,
57 _resolve: &AssistResolveStrategy,
58 ) -> Option<Vec<Assist>> {
59 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
60 // suggest that as a fix.
61
62 let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
63 let our_path = source_root.path_for_file(&self.file_id)?;
64 let module_name = our_path.name_and_extension()?.0;
65
66 // Candidates to look for:
67 // - `mod.rs` in the same folder
68 // - we also check `main.rs` and `lib.rs`
69 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
70 let parent = our_path.parent()?;
71 let mut paths =
72 vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
73
74 // `submod/bla.rs` -> `submod.rs`
75 if let Some(newmod) = (|| {
76 let name = parent.name_and_extension()?.0;
77 parent.parent()?.join(&format!("{}.rs", name))
78 })() {
79 paths.push(newmod);
80 }
81
82 for path in &paths {
83 if let Some(parent_id) = source_root.file_for_path(path) {
84 for krate in sema.db.relevant_crates(*parent_id).iter() {
85 let crate_def_map = sema.db.crate_def_map(*krate);
86 for (_, module) in crate_def_map.modules() {
87 if module.origin.is_inline() {
88 // We don't handle inline `mod parent {}`s, they use different paths.
89 continue;
90 }
91
92 if module.origin.file_id() == Some(*parent_id) {
93 return make_fixes(sema.db, *parent_id, module_name, self.file_id);
94 }
95 }
96 }
97 }
98 }
99
100 None
101 }
102}
103
104fn make_fixes(
105 db: &RootDatabase,
106 parent_file_id: FileId,
107 new_mod_name: &str,
108 added_file_id: FileId,
109) -> Option<Vec<Assist>> {
110 fn is_outline_mod(item: &ast::Item) -> bool {
111 matches!(item, ast::Item::Module(m) if m.item_list().is_none())
112 }
113
114 let mod_decl = format!("mod {};", new_mod_name);
115 let pub_mod_decl = format!("pub mod {};", new_mod_name);
116
117 let ast: ast::SourceFile = db.parse(parent_file_id).tree();
118
119 let mut mod_decl_builder = TextEdit::builder();
120 let mut pub_mod_decl_builder = TextEdit::builder();
121
122 // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
123 // probably `#[cfg]`d out).
124 for item in ast.items() {
125 if let ast::Item::Module(m) = item {
126 if let Some(name) = m.name() {
127 if m.item_list().is_none() && name.to_string() == new_mod_name {
128 cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists);
129 return None;
130 }
131 }
132 }
133 }
134
135 // If there are existing `mod m;` items, append after them (after the first group of them, rather).
136 match ast
137 .items()
138 .skip_while(|item| !is_outline_mod(item))
139 .take_while(|item| is_outline_mod(item))
140 .last()
141 {
142 Some(last) => {
143 cov_mark::hit!(unlinked_file_append_to_existing_mods);
144 let offset = last.syntax().text_range().end();
145 mod_decl_builder.insert(offset, format!("\n{}", mod_decl));
146 pub_mod_decl_builder.insert(offset, format!("\n{}", pub_mod_decl));
147 }
148 None => {
149 // Prepend before the first item in the file.
150 match ast.items().next() {
151 Some(item) => {
152 cov_mark::hit!(unlinked_file_prepend_before_first_item);
153 let offset = item.syntax().text_range().start();
154 mod_decl_builder.insert(offset, format!("{}\n\n", mod_decl));
155 pub_mod_decl_builder.insert(offset, format!("{}\n\n", pub_mod_decl));
156 }
157 None => {
158 // No items in the file, so just append at the end.
159 cov_mark::hit!(unlinked_file_empty_file);
160 let offset = ast.syntax().text_range().end();
161 mod_decl_builder.insert(offset, format!("{}\n", mod_decl));
162 pub_mod_decl_builder.insert(offset, format!("{}\n", pub_mod_decl));
163 }
164 }
165 }
166 }
167
168 let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
169 Some(vec![
170 fix(
171 "add_mod_declaration",
172 &format!("Insert `{}`", mod_decl),
173 SourceChange::from_text_edit(parent_file_id, mod_decl_builder.finish()),
174 trigger_range,
175 ),
176 fix(
177 "add_pub_mod_declaration",
178 &format!("Insert `{}`", pub_mod_decl),
179 SourceChange::from_text_edit(parent_file_id, pub_mod_decl_builder.finish()),
180 trigger_range,
181 ),
182 ])
183}
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 ec3828ab2..7ac0118fe 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -151,18 +151,18 @@ pub(crate) fn resolve_doc_path_for_def(
151) -> Option<hir::ModuleDef> { 151) -> Option<hir::ModuleDef> {
152 match def { 152 match def {
153 Definition::ModuleDef(def) => match def { 153 Definition::ModuleDef(def) => match def {
154 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), 154 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns),
155 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), 155 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns),
156 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), 156 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns),
157 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), 157 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns),
158 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), 158 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns),
159 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), 159 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns),
160 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), 160 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns),
161 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), 161 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
162 hir::ModuleDef::BuiltinType(_) => None, 162 hir::ModuleDef::BuiltinType(_) => None,
163 }, 163 },
164 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), 164 Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
165 Definition::Field(it) => it.resolve_doc_path(db, &link, ns), 165 Definition::Field(it) => it.resolve_doc_path(db, link, ns),
166 Definition::SelfType(_) 166 Definition::SelfType(_)
167 | Definition::Local(_) 167 | Definition::Local(_)
168 | Definition::GenericParam(_) 168 | Definition::GenericParam(_)
@@ -192,7 +192,7 @@ pub(crate) fn doc_attributes(
192 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), 192 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
193 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), 193 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
194 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), 194 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
195 _ => return None 195 _ => None
196 } 196 }
197 } 197 }
198} 198}
@@ -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/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 7032889ac..c7ec87edf 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -328,7 +328,7 @@ mod tests {
328 use super::*; 328 use super::*;
329 329
330 fn do_check(before: &str, afters: &[&str]) { 330 fn do_check(before: &str, afters: &[&str]) {
331 let (analysis, position) = fixture::position(&before); 331 let (analysis, position) = fixture::position(before);
332 let before = analysis.file_text(position.file_id).unwrap(); 332 let before = analysis.file_text(position.file_id).unwrap();
333 let range = TextRange::empty(position.offset); 333 let range = TextRange::empty(position.offset);
334 let mut frange = FileRange { file_id: position.file_id, range }; 334 let mut frange = FileRange { file_id: position.file_id, range };
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index 6780af617..cf679edd3 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -1,6 +1,5 @@
1//! Utilities for creating `Analysis` instances for tests. 1//! Utilities for creating `Analysis` instances for tests.
2use ide_db::base_db::fixture::ChangeFixture; 2use ide_db::base_db::fixture::ChangeFixture;
3use syntax::{TextRange, TextSize};
4use test_utils::extract_annotations; 3use test_utils::extract_annotations;
5 4
6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; 5use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
@@ -13,14 +12,6 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
13 (host.analysis(), change_fixture.files[0]) 12 (host.analysis(), change_fixture.files[0])
14} 13}
15 14
16/// Creates analysis for many files.
17pub(crate) fn files(ra_fixture: &str) -> (Analysis, Vec<FileId>) {
18 let mut host = AnalysisHost::default();
19 let change_fixture = ChangeFixture::parse(ra_fixture);
20 host.db.apply_change(change_fixture.change);
21 (host.analysis(), change_fixture.files)
22}
23
24/// 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.
25pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { 16pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
26 let mut host = AnalysisHost::default(); 17 let mut host = AnalysisHost::default();
@@ -63,15 +54,8 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
63 54
64pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { 55pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) {
65 let (analysis, position, mut annotations) = annotations(ra_fixture); 56 let (analysis, position, mut annotations) = annotations(ra_fixture);
66 let (mut expected, data) = annotations.pop().unwrap(); 57 let (expected, data) = annotations.pop().unwrap();
67 assert!(annotations.is_empty()); 58 assert!(annotations.is_empty());
68 match data.as_str() { 59 assert_eq!(data, "");
69 "" => (),
70 "file" => {
71 expected.range =
72 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
73 }
74 data => panic!("bad data: {}", data),
75 }
76 (analysis, position, expected) 60 (analysis, position, expected)
77} 61}
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 2d36c34e9..d8e0dc4d5 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -43,7 +43,7 @@ pub(crate) fn goto_definition(
43 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; 43 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
44 let (_, link, ns) = 44 let (_, link, ns) =
45 extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { 45 extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| {
46 doc_mapping.map(range.clone()).map_or(false, |InFile { file_id, value: range }| { 46 doc_mapping.map(*range).map_or(false, |InFile { file_id, value: range }| {
47 file_id == position.file_id.into() && range.contains(position.offset) 47 file_id == position.file_id.into() && range.contains(position.offset)
48 }) 48 })
49 })?; 49 })?;
@@ -57,7 +57,7 @@ pub(crate) fn goto_definition(
57 }, 57 },
58 ast::Name(name) => { 58 ast::Name(name) => {
59 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); 59 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
60 try_find_trait_item_definition(&sema.db, &def) 60 try_find_trait_item_definition(sema.db, &def)
61 .or_else(|| def.try_to_nav(sema.db)) 61 .or_else(|| def.try_to_nav(sema.db))
62 }, 62 },
63 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) { 63 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
@@ -185,7 +185,7 @@ mod tests {
185extern crate std$0; 185extern crate std$0;
186//- /std/lib.rs crate:std 186//- /std/lib.rs crate:std
187// empty 187// empty
188//^ file 188//^file
189"#, 189"#,
190 ) 190 )
191 } 191 }
@@ -198,7 +198,7 @@ extern crate std$0;
198extern crate std as abc$0; 198extern crate std as abc$0;
199//- /std/lib.rs crate:std 199//- /std/lib.rs crate:std
200// empty 200// empty
201//^ file 201//^file
202"#, 202"#,
203 ) 203 )
204 } 204 }
@@ -253,7 +253,7 @@ mod $0foo;
253 253
254//- /foo.rs 254//- /foo.rs
255// empty 255// empty
256//^ file 256//^file
257"#, 257"#,
258 ); 258 );
259 259
@@ -264,7 +264,7 @@ mod $0foo;
264 264
265//- /foo/mod.rs 265//- /foo/mod.rs
266// empty 266// empty
267//^ file 267//^file
268"#, 268"#,
269 ); 269 );
270 } 270 }
@@ -395,7 +395,7 @@ use foo as bar$0;
395 395
396//- /foo/lib.rs crate:foo 396//- /foo/lib.rs crate:foo
397// empty 397// empty
398//^ file 398//^file
399"#, 399"#,
400 ); 400 );
401 } 401 }
@@ -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 //^^
@@ -1287,7 +1287,7 @@ fn main() {
1287} 1287}
1288//- /foo.txt 1288//- /foo.txt
1289// empty 1289// empty
1290//^ file 1290//^file
1291"#, 1291"#,
1292 ); 1292 );
1293 } 1293 }
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 95fd39850..0013820b4 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -87,7 +87,7 @@ fn impls_for_trait_item(
87 .filter_map(|imp| { 87 .filter_map(|imp| {
88 let item = imp.items(sema.db).iter().find_map(|itm| { 88 let item = imp.items(sema.db).iter().find_map(|itm| {
89 let itm_name = itm.name(sema.db)?; 89 let itm_name = itm.name(sema.db)?;
90 (itm_name == fun_name).then(|| itm.clone()) 90 (itm_name == fun_name).then(|| *itm)
91 })?; 91 })?;
92 item.try_to_nav(sema.db) 92 item.try_to_nav(sema.db)
93 }) 93 })
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 1c6d36939..529cf5f33 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -131,7 +131,7 @@ pub(crate) fn hover(
131 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; 131 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
132 let (idl_range, link, ns) = 132 let (idl_range, link, ns) =
133 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { 133 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| {
134 let InFile { file_id, value: range } = doc_mapping.map(range.clone())?; 134 let InFile { file_id, value: range } = doc_mapping.map(range)?;
135 if file_id == position.file_id.into() && range.contains(position.offset) { 135 if file_id == position.file_id.into() && range.contains(position.offset) {
136 Some((range, link, ns)) 136 Some((range, link, ns))
137 } else { 137 } else {
@@ -288,7 +288,7 @@ fn runnable_action(
288) -> Option<HoverAction> { 288) -> Option<HoverAction> {
289 match def { 289 match def {
290 Definition::ModuleDef(it) => match it { 290 Definition::ModuleDef(it) => match it {
291 ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)), 291 ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
292 ModuleDef::Function(func) => { 292 ModuleDef::Function(func) => {
293 let src = func.source(sema.db)?; 293 let src = func.source(sema.db)?;
294 if src.file_id != file_id.into() { 294 if src.file_id != file_id.into() {
@@ -297,7 +297,7 @@ fn runnable_action(
297 return None; 297 return None;
298 } 298 }
299 299
300 runnable_fn(&sema, func).map(HoverAction::Runnable) 300 runnable_fn(sema, func).map(HoverAction::Runnable)
301 } 301 }
302 _ => None, 302 _ => None,
303 }, 303 },
@@ -432,7 +432,7 @@ fn hover_for_definition(
432 return match def { 432 return match def {
433 Definition::Macro(it) => match &it.source(db)?.value { 433 Definition::Macro(it) => match &it.source(db)?.value {
434 Either::Left(mac) => { 434 Either::Left(mac) => {
435 let label = macro_label(&mac); 435 let label = macro_label(mac);
436 from_def_source_labeled(db, it, Some(label), mod_path) 436 from_def_source_labeled(db, it, Some(label), mod_path)
437 } 437 }
438 Either::Right(_) => { 438 Either::Right(_) => {
@@ -516,7 +516,7 @@ fn hover_for_keyword(
516 if !token.kind().is_keyword() { 516 if !token.kind().is_keyword() {
517 return None; 517 return None;
518 } 518 }
519 let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()?).krate()); 519 let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
520 // std exposes {}_keyword modules with docstrings on the root to document keywords 520 // std exposes {}_keyword modules with docstrings on the root to document keywords
521 let keyword_mod = format!("{}_keyword", token.text()); 521 let keyword_mod = format!("{}_keyword", token.text());
522 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; 522 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
@@ -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: 245..427,
3024 focus_range: 140..146, 3020 focus_range: 284..290,
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/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 821c61403..9cd33d0e4 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -96,7 +96,7 @@ fn get_chaining_hints(
96 } 96 }
97 97
98 let krate = sema.scope(expr.syntax()).module().map(|it| it.krate()); 98 let krate = sema.scope(expr.syntax()).module().map(|it| it.krate());
99 let famous_defs = FamousDefs(&sema, krate); 99 let famous_defs = FamousDefs(sema, krate);
100 100
101 let mut tokens = expr 101 let mut tokens = expr
102 .syntax() 102 .syntax()
@@ -165,7 +165,7 @@ fn get_param_name_hints(
165 }; 165 };
166 Some((param_name, arg)) 166 Some((param_name, arg))
167 }) 167 })
168 .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, &arg)) 168 .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, arg))
169 .map(|(param_name, arg)| InlayHint { 169 .map(|(param_name, arg)| InlayHint {
170 range: arg.syntax().text_range(), 170 range: arg.syntax().text_range(),
171 kind: InlayKind::ParameterHint, 171 kind: InlayKind::ParameterHint,
@@ -187,7 +187,7 @@ fn get_bind_pat_hints(
187 } 187 }
188 188
189 let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); 189 let krate = sema.scope(pat.syntax()).module().map(|it| it.krate());
190 let famous_defs = FamousDefs(&sema, krate); 190 let famous_defs = FamousDefs(sema, krate);
191 191
192 let ty = sema.type_of_pat(&pat.clone().into())?; 192 let ty = sema.type_of_pat(&pat.clone().into())?;
193 193
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index c67ccd1a9..93d3760bf 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -60,7 +60,7 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR
60 let pos: TextSize = (pos as u32).into(); 60 let pos: TextSize = (pos as u32).into();
61 let offset = token.text_range().start() + range.start() + pos; 61 let offset = token.text_range().start() + range.start() + pos;
62 if !edit.invalidates_offset(offset) { 62 if !edit.invalidates_offset(offset) {
63 remove_newline(edit, &token, offset); 63 remove_newline(edit, token, offset);
64 } 64 }
65 } 65 }
66} 66}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 97c9e5d2b..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};
@@ -282,20 +283,20 @@ impl Analysis {
282 file_id: FileId, 283 file_id: FileId,
283 text_range: Option<TextRange>, 284 text_range: Option<TextRange>,
284 ) -> Cancellable<String> { 285 ) -> Cancellable<String> {
285 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) 286 self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range))
286 } 287 }
287 288
288 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { 289 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
289 self.with_db(|db| view_hir::view_hir(&db, position)) 290 self.with_db(|db| view_hir::view_hir(db, position))
290 } 291 }
291 292
292 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { 293 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
293 self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) 294 self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
294 } 295 }
295 296
296 /// Renders the crate graph to GraphViz "dot" syntax. 297 /// Renders the crate graph to GraphViz "dot" syntax.
297 pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { 298 pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> {
298 self.with_db(|db| view_crate_graph::view_crate_graph(&db)) 299 self.with_db(|db| view_crate_graph::view_crate_graph(db))
299 } 300 }
300 301
301 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { 302 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
@@ -315,7 +316,7 @@ impl Analysis {
315 /// up minor stuff like continuing the comment. 316 /// up minor stuff like continuing the comment.
316 /// The edit will be a snippet (with `$0`). 317 /// The edit will be a snippet (with `$0`).
317 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { 318 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
318 self.with_db(|db| typing::on_enter(&db, position)) 319 self.with_db(|db| typing::on_enter(db, position))
319 } 320 }
320 321
321 /// Returns an edit which should be applied after a character was typed. 322 /// Returns an edit which should be applied after a character was typed.
@@ -331,7 +332,7 @@ impl Analysis {
331 if !typing::TRIGGER_CHARS.contains(char_typed) { 332 if !typing::TRIGGER_CHARS.contains(char_typed) {
332 return Ok(None); 333 return Ok(None);
333 } 334 }
334 self.with_db(|db| typing::on_char_typed(&db, position, char_typed)) 335 self.with_db(|db| typing::on_char_typed(db, position, char_typed))
335 } 336 }
336 337
337 /// Returns a tree representation of symbols in the file. Useful to draw a 338 /// Returns a tree representation of symbols in the file. Useful to draw a
@@ -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 f8b64a669..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,
@@ -62,7 +60,7 @@ pub(crate) fn find_all_refs(
62 if let Some(name) = get_name_of_item_declaration(&syntax, position) { 60 if let Some(name) = get_name_of_item_declaration(&syntax, position) {
63 (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) 61 (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true)
64 } else { 62 } else {
65 (find_def(&sema, &syntax, position)?, false) 63 (find_def(sema, &syntax, position)?, false)
66 }; 64 };
67 65
68 let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); 66 let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all();
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/rename.rs
index 7dfc5043e..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, InFile, Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::{ 7use ide_db::{
10 base_db::{AnchoredPathBuf, FileId}, 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::{display::TryToNav, 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,26 +32,10 @@ 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 match def { 35 let frange = def
56 Definition::SelfType(_) => bail!("Cannot rename `Self`"), 36 .range_for_rename(&sema)
57 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"),
58 Definition::ModuleDef(ModuleDef::Module(_)) => (),
59 _ => {
60 let nav = def
61 .try_to_nav(sema.db)
62 .ok_or_else(|| format_err!("No references found at position"))?;
63 nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
64 }
65 };
66 let name_like = sema
67 .find_node_at_offset_with_descend(&syntax, position.offset)
68 .ok_or_else(|| format_err!("No references found at position"))?; 37 .ok_or_else(|| format_err!("No references found at position"))?;
69 let node = match &name_like { 38 Ok(RangeInfo::new(frange.range, ()))
70 ast::NameLike::Name(it) => it.syntax(),
71 ast::NameLike::NameRef(it) => it.syntax(),
72 ast::NameLike::Lifetime(it) => it.syntax(),
73 };
74 Ok(RangeInfo::new(sema.original_range(node).range, ()))
75} 39}
76 40
77// Feature: Rename 41// Feature: Rename
@@ -103,12 +67,19 @@ pub(crate) fn rename_with_semantics(
103 let syntax = source_file.syntax(); 67 let syntax = source_file.syntax();
104 68
105 let def = find_definition(sema, syntax, position)?; 69 let def = find_definition(sema, syntax, position)?;
106 match def { 70
107 Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name), 71 if let Definition::Local(local) = def {
108 Definition::SelfType(_) => bail!("Cannot rename `Self`"), 72 if let Some(self_param) = local.as_self_param(sema.db) {
109 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), 73 cov_mark::hit!(rename_self_to_param);
110 def => rename_reference(sema, def, new_name), 74 return rename_self_to_param(sema, local, self_param, new_name);
75 }
76 if new_name == "self" {
77 cov_mark::hit!(rename_to_self);
78 return rename_to_self(sema, local);
79 }
111 } 80 }
81
82 def.rename(sema, new_name)
112} 83}
113 84
114/// 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.
@@ -119,38 +90,12 @@ pub(crate) fn will_rename_file(
119) -> Option<SourceChange> { 90) -> Option<SourceChange> {
120 let sema = Semantics::new(db); 91 let sema = Semantics::new(db);
121 let module = sema.to_module_def(file_id)?; 92 let module = sema.to_module_def(file_id)?;
122 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()?;
123 change.file_system_edits.clear(); 95 change.file_system_edits.clear();
124 Some(change) 96 Some(change)
125} 97}
126 98
127#[derive(Copy, Clone, Debug, PartialEq)]
128enum IdentifierKind {
129 Ident,
130 Lifetime,
131 ToSelf,
132 Underscore,
133}
134
135fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
136 match lex_single_syntax_kind(new_name) {
137 Some(res) => match res {
138 (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident),
139 (T![_], _) => Ok(IdentifierKind::Underscore),
140 (T![self], _) => Ok(IdentifierKind::ToSelf),
141 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
142 Ok(IdentifierKind::Lifetime)
143 }
144 (SyntaxKind::LIFETIME_IDENT, _) => {
145 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
146 }
147 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
148 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
149 },
150 None => bail!("Invalid name `{}`: not an identifier", new_name),
151 }
152}
153
154fn find_definition( 99fn find_definition(
155 sema: &Semantics<RootDatabase>, 100 sema: &Semantics<RootDatabase>,
156 syntax: &SyntaxNode, 101 syntax: &SyntaxNode,
@@ -192,145 +137,6 @@ fn find_definition(
192 .ok_or_else(|| format_err!("No references found at position")) 137 .ok_or_else(|| format_err!("No references found at position"))
193} 138}
194 139
195fn rename_mod(
196 sema: &Semantics<RootDatabase>,
197 module: Module,
198 new_name: &str,
199) -> RenameResult<SourceChange> {
200 if IdentifierKind::Ident != check_identifier(new_name)? {
201 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
202 }
203
204 let mut source_change = SourceChange::default();
205
206 let InFile { file_id, value: def_source } = module.definition_source(sema.db);
207 let file_id = file_id.original_file(sema.db);
208 if let ModuleSource::SourceFile(..) = def_source {
209 // mod is defined in path/to/dir/mod.rs
210 let path = if module.is_mod_rs(sema.db) {
211 format!("../{}/mod.rs", new_name)
212 } else {
213 format!("{}.rs", new_name)
214 };
215 let dst = AnchoredPathBuf { anchor: file_id, path };
216 let move_file = FileSystemEdit::MoveFile { src: file_id, dst };
217 source_change.push_file_system_edit(move_file);
218 }
219
220 if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) {
221 let file_id = file_id.original_file(sema.db);
222 match decl_source.name() {
223 Some(name) => source_change.insert_source_edit(
224 file_id,
225 TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
226 ),
227 _ => never!("Module source node is missing a name"),
228 }
229 }
230 let def = Definition::ModuleDef(ModuleDef::Module(module));
231 let usages = def.usages(sema).all();
232 let ref_edits = usages.iter().map(|(&file_id, references)| {
233 (file_id, source_edit_from_references(references, def, new_name))
234 });
235 source_change.extend(ref_edits);
236
237 Ok(source_change)
238}
239
240fn rename_reference(
241 sema: &Semantics<RootDatabase>,
242 mut def: Definition,
243 new_name: &str,
244) -> RenameResult<SourceChange> {
245 let ident_kind = check_identifier(new_name)?;
246
247 if matches!(
248 def, // is target a lifetime?
249 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
250 ) {
251 match ident_kind {
252 IdentifierKind::Ident | IdentifierKind::ToSelf | IdentifierKind::Underscore => {
253 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
254 bail!("Invalid name `{}`: not a lifetime identifier", new_name);
255 }
256 IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime),
257 }
258 } else {
259 match (ident_kind, def) {
260 (IdentifierKind::Lifetime, _) => {
261 cov_mark::hit!(rename_not_an_ident_ref);
262 bail!("Invalid name `{}`: not an identifier", new_name);
263 }
264 (IdentifierKind::ToSelf, Definition::Local(local)) => {
265 if local.is_self(sema.db) {
266 // no-op
267 cov_mark::hit!(rename_self_to_self);
268 return Ok(SourceChange::default());
269 } else {
270 cov_mark::hit!(rename_to_self);
271 return rename_to_self(sema, local);
272 }
273 }
274 (ident_kind, Definition::Local(local)) => {
275 if let Some(self_param) = local.as_self_param(sema.db) {
276 cov_mark::hit!(rename_self_to_param);
277 return rename_self_to_param(sema, local, self_param, new_name, ident_kind);
278 } else {
279 cov_mark::hit!(rename_local);
280 }
281 }
282 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
283 (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local),
284 (IdentifierKind::Underscore, _) => (),
285 }
286 }
287
288 def = match def {
289 // HACK: resolve trait impl items to the item def of the trait definition
290 // so that we properly resolve all trait item references
291 Definition::ModuleDef(mod_def) => mod_def
292 .as_assoc_item(sema.db)
293 .and_then(|it| it.containing_trait_impl(sema.db))
294 .and_then(|it| {
295 it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) {
296 (hir::AssocItem::Function(trait_func), ModuleDef::Function(func))
297 if trait_func.name(sema.db) == func.name(sema.db) =>
298 {
299 Some(Definition::ModuleDef(ModuleDef::Function(trait_func)))
300 }
301 (hir::AssocItem::Const(trait_konst), ModuleDef::Const(konst))
302 if trait_konst.name(sema.db) == konst.name(sema.db) =>
303 {
304 Some(Definition::ModuleDef(ModuleDef::Const(trait_konst)))
305 }
306 (
307 hir::AssocItem::TypeAlias(trait_type_alias),
308 ModuleDef::TypeAlias(type_alias),
309 ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => {
310 Some(Definition::ModuleDef(ModuleDef::TypeAlias(trait_type_alias)))
311 }
312 _ => None,
313 })
314 })
315 .unwrap_or(def),
316 _ => def,
317 };
318 let usages = def.usages(sema).all();
319
320 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
321 cov_mark::hit!(rename_underscore_multiple);
322 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
323 }
324 let mut source_change = SourceChange::default();
325 source_change.extend(usages.iter().map(|(&file_id, references)| {
326 (file_id, source_edit_from_references(&references, def, new_name))
327 }));
328
329 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
330 source_change.insert_source_edit(file_id, edit);
331 Ok(source_change)
332}
333
334fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { 140fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> {
335 if never!(local.is_self(sema.db)) { 141 if never!(local.is_self(sema.db)) {
336 bail!("rename_to_self invoked on self"); 142 bail!("rename_to_self invoked on self");
@@ -398,8 +204,15 @@ fn rename_self_to_param(
398 local: hir::Local, 204 local: hir::Local,
399 self_param: hir::SelfParam, 205 self_param: hir::SelfParam,
400 new_name: &str, 206 new_name: &str,
401 identifier_kind: IdentifierKind,
402) -> RenameResult<SourceChange> { 207) -> RenameResult<SourceChange> {
208 if new_name == "self" {
209 // Let's do nothing rather than complain.
210 cov_mark::hit!(rename_self_to_self);
211 return Ok(SourceChange::default());
212 }
213
214 let identifier_kind = IdentifierKind::classify(new_name)?;
215
403 let InFile { file_id, value: self_param } = 216 let InFile { file_id, value: self_param } =
404 self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?; 217 self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?;
405 218
@@ -413,7 +226,7 @@ fn rename_self_to_param(
413 let mut source_change = SourceChange::default(); 226 let mut source_change = SourceChange::default();
414 source_change.insert_source_edit(file_id.original_file(sema.db), edit); 227 source_change.insert_source_edit(file_id.original_file(sema.db), edit);
415 source_change.extend(usages.iter().map(|(&file_id, references)| { 228 source_change.extend(usages.iter().map(|(&file_id, references)| {
416 (file_id, source_edit_from_references(&references, def, new_name)) 229 (file_id, source_edit_from_references(references, def, new_name))
417 })); 230 }));
418 Ok(source_change) 231 Ok(source_change)
419} 232}
@@ -426,7 +239,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
426 None 239 None
427 } 240 }
428 241
429 let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; 242 let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?;
430 let type_name = target_type_name(&impl_def)?; 243 let type_name = target_type_name(&impl_def)?;
431 244
432 let mut replacement_text = String::from(new_name); 245 let mut replacement_text = String::from(new_name);
@@ -441,150 +254,6 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
441 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) 254 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
442} 255}
443 256
444fn source_edit_from_references(
445 references: &[FileReference],
446 def: Definition,
447 new_name: &str,
448) -> TextEdit {
449 let mut edit = TextEdit::builder();
450 for reference in references {
451 let (range, replacement) = match &reference.name {
452 // if the ranges differ then the node is inside a macro call, we can't really attempt
453 // to make special rewrites like shorthand syntax and such, so just rename the node in
454 // the macro input
455 ast::NameLike::NameRef(name_ref)
456 if name_ref.syntax().text_range() == reference.range =>
457 {
458 source_edit_from_name_ref(name_ref, new_name, def)
459 }
460 ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => {
461 source_edit_from_name(name, new_name)
462 }
463 _ => None,
464 }
465 .unwrap_or_else(|| (reference.range, new_name.to_string()));
466 edit.replace(range, replacement);
467 }
468 edit.finish()
469}
470
471fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
472 if let Some(_) = ast::RecordPatField::for_field_name(name) {
473 if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
474 return Some((
475 TextRange::empty(ident_pat.syntax().text_range().start()),
476 [new_name, ": "].concat(),
477 ));
478 }
479 }
480 None
481}
482
483fn source_edit_from_name_ref(
484 name_ref: &ast::NameRef,
485 new_name: &str,
486 def: Definition,
487) -> Option<(TextRange, String)> {
488 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
489 let rcf_name_ref = record_field.name_ref();
490 let rcf_expr = record_field.expr();
491 match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
492 // field: init-expr, check if we can use a field init shorthand
493 (Some(field_name), Some(init)) => {
494 if field_name == *name_ref {
495 if init.text() == new_name {
496 cov_mark::hit!(test_rename_field_put_init_shorthand);
497 // same names, we can use a shorthand here instead.
498 // we do not want to erase attributes hence this range start
499 let s = field_name.syntax().text_range().start();
500 let e = record_field.syntax().text_range().end();
501 return Some((TextRange::new(s, e), new_name.to_owned()));
502 }
503 } else if init == *name_ref {
504 if field_name.text() == new_name {
505 cov_mark::hit!(test_rename_local_put_init_shorthand);
506 // same names, we can use a shorthand here instead.
507 // we do not want to erase attributes hence this range start
508 let s = field_name.syntax().text_range().start();
509 let e = record_field.syntax().text_range().end();
510 return Some((TextRange::new(s, e), new_name.to_owned()));
511 }
512 }
513 None
514 }
515 // init shorthand
516 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
517 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
518 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
519 cov_mark::hit!(test_rename_field_in_field_shorthand);
520 let s = name_ref.syntax().text_range().start();
521 Some((TextRange::empty(s), format!("{}: ", new_name)))
522 }
523 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
524 cov_mark::hit!(test_rename_local_in_field_shorthand);
525 let s = name_ref.syntax().text_range().end();
526 Some((TextRange::empty(s), format!(": {}", new_name)))
527 }
528 _ => None,
529 }
530 } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
531 let rcf_name_ref = record_field.name_ref();
532 let rcf_pat = record_field.pat();
533 match (rcf_name_ref, rcf_pat) {
534 // field: rename
535 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
536 // field name is being renamed
537 if pat.name().map_or(false, |it| it.text() == new_name) {
538 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
539 // same names, we can use a shorthand here instead/
540 // we do not want to erase attributes hence this range start
541 let s = field_name.syntax().text_range().start();
542 let e = record_field.syntax().text_range().end();
543 Some((TextRange::new(s, e), pat.to_string()))
544 } else {
545 None
546 }
547 }
548 _ => None,
549 }
550 } else {
551 None
552 }
553}
554
555fn source_edit_from_def(
556 sema: &Semantics<RootDatabase>,
557 def: Definition,
558 new_name: &str,
559) -> RenameResult<(FileId, TextEdit)> {
560 let nav =
561 def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
562
563 let mut replacement_text = String::new();
564 let mut repl_range =
565 nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
566 if let Definition::Local(local) = def {
567 if let Either::Left(pat) = local.source(sema.db).value {
568 if matches!(
569 pat.syntax().parent().and_then(ast::RecordPatField::cast),
570 Some(pat_field) if pat_field.name_ref().is_none()
571 ) {
572 replacement_text.push_str(": ");
573 replacement_text.push_str(new_name);
574 repl_range = TextRange::new(
575 pat.syntax().text_range().end(),
576 pat.syntax().text_range().end(),
577 );
578 }
579 }
580 }
581 if replacement_text.is_empty() {
582 replacement_text.push_str(new_name);
583 }
584 let edit = TextEdit::replace(repl_range, replacement_text);
585 Ok((nav.file_id, edit))
586}
587
588#[cfg(test)] 257#[cfg(test)]
589mod tests { 258mod tests {
590 use expect_test::{expect, Expect}; 259 use expect_test::{expect, Expect};
@@ -659,7 +328,7 @@ mod tests {
659 fn test_prepare_rename_namelikes() { 328 fn test_prepare_rename_namelikes() {
660 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); 329 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
661 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); 330 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
662 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]); 331 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"3..7: name"#]]);
663 } 332 }
664 333
665 #[test] 334 #[test]
@@ -691,7 +360,7 @@ fn baz() {
691 x.0$0 = 5; 360 x.0$0 = 5;
692} 361}
693"#, 362"#,
694 expect![[r#"No identifier available to rename"#]], 363 expect![[r#"No references found at position"#]],
695 ); 364 );
696 } 365 }
697 366
@@ -703,7 +372,7 @@ fn foo() {
703 let x: i32$0 = 0; 372 let x: i32$0 = 0;
704} 373}
705"#, 374"#,
706 expect![[r#"Cannot rename builtin type"#]], 375 expect![[r#"No references found at position"#]],
707 ); 376 );
708 } 377 }
709 378
@@ -719,7 +388,7 @@ impl Foo {
719 } 388 }
720} 389}
721"#, 390"#,
722 expect![[r#"Cannot rename `Self`"#]], 391 expect![[r#"No references found at position"#]],
723 ); 392 );
724 } 393 }
725 394
@@ -801,7 +470,6 @@ impl Foo {
801 470
802 #[test] 471 #[test]
803 fn test_rename_for_local() { 472 fn test_rename_for_local() {
804 cov_mark::check!(rename_local);
805 check( 473 check(
806 "k", 474 "k",
807 r#" 475 r#"
@@ -2101,4 +1769,22 @@ fn f() { <()>::BAR$0; }"#,
2101 res, 1769 res,
2102 ); 1770 );
2103 } 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 }
2104} 1790}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 552054951..03faabadc 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -158,7 +158,7 @@ fn find_related_tests(
158 search_scope: Option<SearchScope>, 158 search_scope: Option<SearchScope>,
159 tests: &mut FxHashSet<Runnable>, 159 tests: &mut FxHashSet<Runnable>,
160) { 160) {
161 if let Some(refs) = references::find_all_refs(&sema, position, search_scope) { 161 if let Some(refs) = references::find_all_refs(sema, position, search_scope) {
162 for (file_id, refs) in refs.references { 162 for (file_id, refs) in refs.references {
163 let file = sema.parse(file_id); 163 let file = sema.parse(file_id);
164 let file = file.syntax(); 164 let file = file.syntax();
@@ -169,10 +169,10 @@ fn find_related_tests(
169 }); 169 });
170 170
171 for fn_def in functions { 171 for fn_def in functions {
172 if let Some(runnable) = as_test_runnable(&sema, &fn_def) { 172 if let Some(runnable) = as_test_runnable(sema, &fn_def) {
173 // direct test 173 // direct test
174 tests.insert(runnable); 174 tests.insert(runnable);
175 } else if let Some(module) = parent_test_module(&sema, &fn_def) { 175 } else if let Some(module) = parent_test_module(sema, &fn_def) {
176 // indirect test 176 // indirect test
177 find_related_tests_in_module(sema, &fn_def, &module, tests); 177 find_related_tests_in_module(sema, &fn_def, &module, tests);
178 } 178 }
@@ -203,7 +203,7 @@ fn find_related_tests_in_module(
203} 203}
204 204
205fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> { 205fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
206 if test_related_attribute(&fn_def).is_some() { 206 if test_related_attribute(fn_def).is_some() {
207 let function = sema.to_def(fn_def)?; 207 let function = sema.to_def(fn_def)?;
208 runnable_fn(sema, function) 208 runnable_fn(sema, function)
209 } else { 209 } else {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index b03f1c71f..e186b82b7 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -323,7 +323,7 @@ fn traverse(
323 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { 323 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
324 if token.is_raw() { 324 if token.is_raw() {
325 let expanded = element_to_highlight.as_token().unwrap().clone(); 325 let expanded = element_to_highlight.as_token().unwrap().clone();
326 if inject::ra_fixture(hl, &sema, token, expanded).is_some() { 326 if inject::ra_fixture(hl, sema, token, expanded).is_some() {
327 continue; 327 continue;
328 } 328 }
329 } 329 }
@@ -334,7 +334,7 @@ fn traverse(
334 } 334 }
335 335
336 if let Some((mut highlight, binding_hash)) = highlight::element( 336 if let Some((mut highlight, binding_hash)) = highlight::element(
337 &sema, 337 sema,
338 krate, 338 krate,
339 &mut bindings_shadow_count, 339 &mut bindings_shadow_count,
340 syntactic_name_ref_highlighting, 340 syntactic_name_ref_highlighting,
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 9503c936d..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
@@ -131,6 +151,9 @@ pub(super) fn element(
131 } 151 }
132 STRING | BYTE_STRING => HlTag::StringLiteral.into(), 152 STRING | BYTE_STRING => HlTag::StringLiteral.into(),
133 ATTR => HlTag::Attribute.into(), 153 ATTR => HlTag::Attribute.into(),
154 INT_NUMBER if element.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => {
155 SymbolKind::Field.into()
156 }
134 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), 157 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
135 BYTE => HlTag::ByteLiteral.into(), 158 BYTE => HlTag::ByteLiteral.into(),
136 CHAR => HlTag::CharLiteral.into(), 159 CHAR => HlTag::CharLiteral.into(),
@@ -351,15 +374,7 @@ fn highlight_def(db: &RootDatabase, krate: Option<hir::Crate>, def: Definition)
351 374
352 h 375 h
353 } 376 }
354 hir::ModuleDef::Trait(trait_) => { 377 hir::ModuleDef::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)),
355 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Trait));
356
357 if trait_.is_unsafe(db) {
358 h |= HlMod::Unsafe;
359 }
360
361 h
362 }
363 hir::ModuleDef::TypeAlias(type_) => { 378 hir::ModuleDef::TypeAlias(type_) => {
364 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); 379 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
365 380
@@ -446,12 +461,12 @@ fn highlight_method_call(
446 krate: Option<hir::Crate>, 461 krate: Option<hir::Crate>,
447 method_call: &ast::MethodCallExpr, 462 method_call: &ast::MethodCallExpr,
448) -> Option<Highlight> { 463) -> Option<Highlight> {
449 let func = sema.resolve_method_call(&method_call)?; 464 let func = sema.resolve_method_call(method_call)?;
450 465
451 let mut h = SymbolKind::Function.into(); 466 let mut h = SymbolKind::Function.into();
452 h |= HlMod::Associated; 467 h |= HlMod::Associated;
453 468
454 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 469 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(method_call) {
455 h |= HlMod::Unsafe; 470 h |= HlMod::Unsafe;
456 } 471 }
457 if func.is_async(sema.db) { 472 if func.is_async(sema.db) {
@@ -523,11 +538,9 @@ fn highlight_name_ref_by_syntax(
523 }; 538 };
524 539
525 match parent.kind() { 540 match parent.kind() {
526 METHOD_CALL_EXPR => { 541 METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent)
527 return ast::MethodCallExpr::cast(parent) 542 .and_then(|it| highlight_method_call(sema, krate, &it))
528 .and_then(|it| highlight_method_call(sema, krate, &it)) 543 .unwrap_or_else(|| SymbolKind::Function.into()),
529 .unwrap_or_else(|| SymbolKind::Function.into());
530 }
531 FIELD_EXPR => { 544 FIELD_EXPR => {
532 let h = HlTag::Symbol(SymbolKind::Field); 545 let h = HlTag::Symbol(SymbolKind::Field);
533 let is_union = ast::FieldExpr::cast(parent) 546 let is_union = ast::FieldExpr::cast(parent)
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 5327af845..21376a7ae 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -23,7 +23,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
23 let hl_ranges = highlight(db, file_id, None, false); 23 let hl_ranges = highlight(db, file_id, None, false);
24 let text = parse.tree().syntax().to_string(); 24 let text = parse.tree().syntax().to_string();
25 let mut buf = String::new(); 25 let mut buf = String::new();
26 buf.push_str(&STYLE); 26 buf.push_str(STYLE);
27 buf.push_str("<pre><code>"); 27 buf.push_str("<pre><code>");
28 for r in &hl_ranges { 28 for r in &hl_ranges {
29 let chunk = html_escape(&text[r.range]); 29 let chunk = html_escape(&text[r.range]);
@@ -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/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 4269d339e..ec43c8579 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -23,7 +23,7 @@ pub(super) fn ra_fixture(
23 literal: ast::String, 23 literal: ast::String,
24 expanded: SyntaxToken, 24 expanded: SyntaxToken,
25) -> Option<()> { 25) -> Option<()> {
26 let active_parameter = ActiveParameter::at_token(&sema, expanded)?; 26 let active_parameter = ActiveParameter::at_token(sema, expanded)?;
27 if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { 27 if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) {
28 return None; 28 return None;
29 } 29 }
@@ -124,7 +124,7 @@ pub(super) fn doc_comment(
124 } 124 }
125 125
126 for attr in attributes.by_key("doc").attrs() { 126 for attr in attributes.by_key("doc").attrs() {
127 let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); 127 let InFile { file_id, value: src } = attrs_source_map.source_of(attr);
128 if file_id != node.file_id { 128 if file_id != node.file_id {
129 continue; 129 continue;
130 } 130 }
@@ -232,7 +232,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
232 string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) 232 string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text)
233 }) 233 })
234 } 234 }
235 _ => return None, 235 _ => None,
236 } 236 }
237} 237}
238 238
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 a7b5c3b89..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; }
@@ -215,8 +216,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
215 <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> 216 <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
216 <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> 217 <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span>
217 218
218 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> 219 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="comma">,</span><span class="parenthesis">)</span><span class="semicolon">;</span>
219 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> 220 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="operator">.</span><span class="field">0</span><span class="semicolon">;</span>
220 221
221 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> 222 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
222 223
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 6ad2a362a..4f0b1ce85 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -189,8 +189,8 @@ fn main() {
189 let a = |x| x; 189 let a = |x| x;
190 let bar = Foo::baz; 190 let bar = Foo::baz;
191 191
192 let baz = -42; 192 let baz = (-42,);
193 let baz = -baz; 193 let baz = -baz.0;
194 194
195 let _ = !true; 195 let _ = !true;
196 196
@@ -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/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 81c4d95b1..5cba9d11d 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -88,12 +88,12 @@ fn on_enter_in_comment(
88 if comment.text().ends_with(' ') { 88 if comment.text().ends_with(' ') {
89 cov_mark::hit!(continues_end_of_line_comment_with_space); 89 cov_mark::hit!(continues_end_of_line_comment_with_space);
90 remove_trailing_whitespace = true; 90 remove_trailing_whitespace = true;
91 } else if !followed_by_comment(&comment) { 91 } else if !followed_by_comment(comment) {
92 return None; 92 return None;
93 } 93 }
94 } 94 }
95 95
96 let indent = node_indent(&file, comment.syntax())?; 96 let indent = node_indent(file, comment.syntax())?;
97 let inserted = format!("\n{}{} $0", indent, prefix); 97 let inserted = format!("\n{}{} $0", indent, prefix);
98 let delete = if remove_trailing_whitespace { 98 let delete = if remove_trailing_whitespace {
99 let trimmed_len = comment.text().trim_end().len() as u32; 99 let trimmed_len = comment.text().trim_end().len() as u32;
@@ -188,7 +188,7 @@ mod tests {
188 use crate::fixture; 188 use crate::fixture;
189 189
190 fn apply_on_enter(before: &str) -> Option<String> { 190 fn apply_on_enter(before: &str) -> Option<String> {
191 let (analysis, position) = fixture::position(&before); 191 let (analysis, position) = fixture::position(before);
192 let result = analysis.on_enter(position).unwrap()?; 192 let result = analysis.on_enter(position).unwrap()?;
193 193
194 let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); 194 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml
index a83acb191..0d0d1605e 100644
--- a/crates/ide_assists/Cargo.toml
+++ b/crates/ide_assists/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15itertools = "0.10.0" 15itertools = "0.10.0"
16either = "1.6.1" 16either = "1.6.1"
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 8fc40f9bd..36a2bf89a 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -291,8 +291,7 @@ impl AssistBuilder {
291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
292 } 292 }
293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { 293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
294 let file_system_edit = 294 let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() };
295 FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() };
296 self.source_change.push_file_system_edit(file_system_edit); 295 self.source_change.push_file_system_edit(file_system_edit);
297 } 296 }
298 297
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs
index 5c936a510..c93959e66 100644
--- a/crates/ide_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide_assists/src/handlers/apply_demorgan.rs
@@ -78,12 +78,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
78 terms.sort_by_key(|t| t.syntax().text_range().start()); 78 terms.sort_by_key(|t| t.syntax().text_range().start());
79 let mut terms = VecDeque::from(terms); 79 let mut terms = VecDeque::from(terms);
80 80
81 let paren_expr = expr.syntax().parent().and_then(|parent| ast::ParenExpr::cast(parent)); 81 let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
82 82
83 let neg_expr = paren_expr 83 let neg_expr = paren_expr
84 .clone() 84 .clone()
85 .and_then(|paren_expr| paren_expr.syntax().parent()) 85 .and_then(|paren_expr| paren_expr.syntax().parent())
86 .and_then(|parent| ast::PrefixExpr::cast(parent)) 86 .and_then(ast::PrefixExpr::cast)
87 .and_then(|prefix_expr| { 87 .and_then(|prefix_expr| {
88 if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not { 88 if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not {
89 Some(prefix_expr) 89 Some(prefix_expr)
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs
index d202a85f9..749e8685b 100644
--- a/crates/ide_assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide_assists/src/handlers/convert_comment_block.rs
@@ -88,7 +88,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
88 // We pick a single indentation level for the whole block comment based on the 88 // We pick a single indentation level for the whole block comment based on the
89 // comment where the assist was invoked. This will be prepended to the 89 // comment where the assist was invoked. This will be prepended to the
90 // contents of each line comment when they're put into the block comment. 90 // contents of each line comment when they're put into the block comment.
91 let indentation = IndentLevel::from_token(&comment.syntax()); 91 let indentation = IndentLevel::from_token(comment.syntax());
92 92
93 let block_comment_body = 93 let block_comment_body =
94 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); 94 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");
@@ -167,7 +167,7 @@ fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
167 if contents.is_empty() { 167 if contents.is_empty() {
168 contents.to_owned() 168 contents.to_owned()
169 } else { 169 } else {
170 indentation.to_string() + &contents 170 indentation.to_string() + contents
171 } 171 }
172} 172}
173 173
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs
index 5eb6a57f0..ef4a7cb50 100644
--- a/crates/ide_assists/src/handlers/early_return.rs
+++ b/crates/ide_assists/src/handlers/early_return.rs
@@ -108,7 +108,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
108 "Convert to guarded return", 108 "Convert to guarded return",
109 target, 109 target,
110 |edit| { 110 |edit| {
111 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 111 let if_indent_level = IndentLevel::from_node(if_expr.syntax());
112 let new_block = match if_let_pat { 112 let new_block = match if_let_pat {
113 None => { 113 None => {
114 // If. 114 // If.
@@ -174,7 +174,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
174 .take_while(|i| *i != end_of_then), 174 .take_while(|i| *i != end_of_then),
175 ); 175 );
176 replace_children( 176 replace_children(
177 &parent_block.syntax(), 177 parent_block.syntax(),
178 RangeInclusive::new( 178 RangeInclusive::new(
179 if_expr.clone().syntax().clone().into(), 179 if_expr.clone().syntax().clone().into(),
180 if_expr.syntax().clone().into(), 180 if_expr.syntax().clone().into(),
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index a2dba915c..f2be091f4 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -76,7 +76,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
76 let module = ctx.sema.scope(&insert_after).module()?; 76 let module = ctx.sema.scope(&insert_after).module()?;
77 77
78 let vars_defined_in_body_and_outlive = 78 let vars_defined_in_body_and_outlive =
79 vars_defined_in_body_and_outlive(ctx, &body, &node.parent().as_ref().unwrap_or(&node)); 79 vars_defined_in_body_and_outlive(ctx, &body, node.parent().as_ref().unwrap_or(&node));
80 let ret_ty = body_return_ty(ctx, &body)?; 80 let ret_ty = body_return_ty(ctx, &body)?;
81 81
82 // FIXME: we compute variables that outlive here just to check `never!` condition 82 // FIXME: we compute variables that outlive here just to check `never!` condition
@@ -782,7 +782,7 @@ fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Optio
782 Some(false) 782 Some(false)
783} 783}
784 784
785/// Container of local varaible usages 785/// Container of local variable usages
786/// 786///
787/// Semanticall same as `UsageSearchResult`, but provides more convenient interface 787/// Semanticall same as `UsageSearchResult`, but provides more convenient interface
788struct LocalUsages(ide_db::search::UsageSearchResult); 788struct LocalUsages(ide_db::search::UsageSearchResult);
@@ -808,7 +808,7 @@ trait HasTokenAtOffset {
808 808
809impl HasTokenAtOffset for SyntaxNode { 809impl HasTokenAtOffset for SyntaxNode {
810 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { 810 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
811 SyntaxNode::token_at_offset(&self, offset) 811 SyntaxNode::token_at_offset(self, offset)
812 } 812 }
813} 813}
814 814
@@ -854,7 +854,7 @@ fn vars_defined_in_body_and_outlive(
854 body: &FunctionBody, 854 body: &FunctionBody,
855 parent: &SyntaxNode, 855 parent: &SyntaxNode,
856) -> Vec<OutlivedLocal> { 856) -> Vec<OutlivedLocal> {
857 let vars_defined_in_body = vars_defined_in_body(&body, ctx); 857 let vars_defined_in_body = vars_defined_in_body(body, ctx);
858 vars_defined_in_body 858 vars_defined_in_body
859 .into_iter() 859 .into_iter()
860 .filter_map(|var| var_outlives_body(ctx, body, var, parent)) 860 .filter_map(|var| var_outlives_body(ctx, body, var, parent))
@@ -868,7 +868,7 @@ fn is_defined_before(
868 src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>, 868 src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>,
869) -> bool { 869) -> bool {
870 src.file_id.original_file(ctx.db()) == ctx.frange.file_id 870 src.file_id.original_file(ctx.db()) == ctx.frange.file_id
871 && !body.contains_node(&either_syntax(&src.value)) 871 && !body.contains_node(either_syntax(&src.value))
872} 872}
873 873
874fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { 874fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 3d2cd739a..5a43bdd6f 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -202,7 +202,7 @@ impl ExtendedEnum {
202 fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> { 202 fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
203 match self { 203 match self {
204 ExtendedEnum::Enum(e) => { 204 ExtendedEnum::Enum(e) => {
205 e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() 205 e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
206 } 206 }
207 ExtendedEnum::Bool => { 207 ExtendedEnum::Bool => {
208 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) 208 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
@@ -212,7 +212,7 @@ impl ExtendedEnum {
212} 212}
213 213
214fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { 214fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
215 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 215 sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
216 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), 216 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
217 _ => { 217 _ => {
218 if ty.is_bool() { 218 if ty.is_bool() {
@@ -228,7 +228,7 @@ fn resolve_tuple_of_enum_def(
228 sema: &Semantics<RootDatabase>, 228 sema: &Semantics<RootDatabase>,
229 expr: &ast::Expr, 229 expr: &ast::Expr,
230) -> Option<Vec<ExtendedEnum>> { 230) -> Option<Vec<ExtendedEnum>> {
231 sema.type_of_expr(&expr)? 231 sema.type_of_expr(expr)?
232 .tuple_fields(sema.db) 232 .tuple_fields(sema.db)
233 .iter() 233 .iter()
234 .map(|ty| { 234 .map(|ty| {
diff --git a/crates/ide_assists/src/handlers/fix_visibility.rs b/crates/ide_assists/src/handlers/fix_visibility.rs
index 89f7b2c2c..f834bf16a 100644
--- a/crates/ide_assists/src/handlers/fix_visibility.rs
+++ b/crates/ide_assists/src/handlers/fix_visibility.rs
@@ -43,7 +43,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
43 _ => return None, 43 _ => return None,
44 }; 44 };
45 45
46 let current_module = ctx.sema.scope(&path.syntax()).module()?; 46 let current_module = ctx.sema.scope(path.syntax()).module()?;
47 let target_module = def.module(ctx.db())?; 47 let target_module = def.module(ctx.db())?;
48 48
49 let vis = target_module.visibility_of(ctx.db(), &def)?; 49 let vis = target_module.visibility_of(ctx.db(), &def)?;
@@ -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_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
index a9f71a703..24939f262 100644
--- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
@@ -47,7 +47,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); 47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text()));
48 48
49 // Return early if we've found an existing new fn 49 // Return early if we've found an existing new fn
50 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 50 let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
51 51
52 let target = variant.syntax().text_range(); 52 let target = variant.syntax().text_range();
53 acc.add( 53 acc.add(
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
index e2f572ba3..986fb2315 100644
--- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
@@ -136,7 +136,7 @@ fn generate_enum_projection_method(
136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text())); 136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
137 137
138 // Return early if we've found an existing new fn 138 // Return early if we've found an existing new fn
139 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 139 let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
140 140
141 let target = variant.syntax().text_range(); 141 let target = variant.syntax().text_range();
142 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { 142 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| {
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index bc9fc524b..6a658d4cf 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -59,7 +59,7 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
59 None => None, 59 None => None,
60 }; 60 };
61 61
62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(ctx, &call, &path, target_module)?;
63 63
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add( 65 acc.add(
@@ -128,12 +128,12 @@ impl FunctionBuilder {
128 file = in_file; 128 file = in_file;
129 target 129 target
130 } 130 }
131 None => next_space_for_fn_after_call_site(&call)?, 131 None => next_space_for_fn_after_call_site(call)?,
132 }; 132 };
133 let needs_pub = target_module.is_some(); 133 let needs_pub = target_module.is_some();
134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; 134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
135 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(path)?;
136 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, call)?;
137 137
138 // should_render_snippet intends to express a rough level of confidence about 138 // should_render_snippet intends to express a rough level of confidence about
139 // the correctness of the return type. 139 // the correctness of the return type.
@@ -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/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index 09971226e..cc020c92c 100644
--- a/crates/ide_assists/src/handlers/generate_getter.rs
+++ b/crates/ide_assists/src/handlers/generate_getter.rs
@@ -75,7 +75,7 @@ pub(crate) fn generate_getter_impl(
75 if mutable { 75 if mutable {
76 format_to!(fn_name, "_mut"); 76 format_to!(fn_name, "_mut");
77 } 77 }
78 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; 78 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
79 79
80 let (id, label) = if mutable { 80 let (id, label) = if mutable {
81 ("generate_getter_mut", "Generate a mut getter method") 81 ("generate_getter_mut", "Generate a mut getter method")
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs
index 959a1f86c..b65e8387b 100644
--- a/crates/ide_assists/src/handlers/generate_new.rs
+++ b/crates/ide_assists/src/handlers/generate_new.rs
@@ -36,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
36 }; 36 };
37 37
38 // Return early if we've found an existing new fn 38 // Return early if we've found an existing new fn
39 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; 39 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
40 40
41 let target = strukt.syntax().text_range(); 41 let target = strukt.syntax().text_range();
42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { 42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
diff --git a/crates/ide_assists/src/handlers/generate_setter.rs b/crates/ide_assists/src/handlers/generate_setter.rs
index 288cf745d..5bdf6b3f4 100644
--- a/crates/ide_assists/src/handlers/generate_setter.rs
+++ b/crates/ide_assists/src/handlers/generate_setter.rs
@@ -39,7 +39,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<
39 // Return early if we've found an existing fn 39 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string()); 40 let fn_name = to_lower_snake_case(&field_name.to_string());
41 let impl_def = find_struct_impl( 41 let impl_def = find_struct_impl(
42 &ctx, 42 ctx,
43 &ast::Adt::Struct(strukt.clone()), 43 &ast::Adt::Struct(strukt.clone()),
44 format!("set_{}", fn_name).as_str(), 44 format!("set_{}", fn_name).as_str(),
45 )?; 45 )?;
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/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs
index c8226550f..b20fe992d 100644
--- a/crates/ide_assists/src/handlers/remove_dbg.rs
+++ b/crates/ide_assists/src/handlers/remove_dbg.rs
@@ -85,7 +85,7 @@ fn whitespace_start(it: SyntaxElement) -> Option<TextSize> {
85} 85}
86 86
87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { 87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
88 let contents = get_valid_macrocall_contents(&macro_call, "dbg")?; 88 let contents = get_valid_macrocall_contents(macro_call, "dbg")?;
89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); 89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
90 let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new( 90 let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
91 TextSize::of('('), 91 TextSize::of('('),
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 933acead1..f6a926042 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -28,7 +28,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<(
28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?; 28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?;
29 29
30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?; 30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
31 let ranks = compute_fields_ranks(&path, &ctx)?; 31 let ranks = compute_fields_ranks(&path, ctx)?;
32 let get_rank_of_field = 32 let get_rank_of_field =
33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); 33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
34 34
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 10d9cec31..f9474c9f5 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -112,7 +112,7 @@ fn add_assist(
112 let insert_pos = adt.syntax().text_range().end(); 112 let insert_pos = adt.syntax().text_range().end();
113 let impl_def_with_items = 113 let impl_def_with_items =
114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); 114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path);
115 update_attribute(builder, &input, &trait_name, &attr); 115 update_attribute(builder, input, &trait_name, attr);
116 let trait_path = format!("{}", trait_path); 116 let trait_path = format!("{}", trait_path);
117 match (ctx.config.snippet_cap, impl_def_with_items) { 117 match (ctx.config.snippet_cap, impl_def_with_items) {
118 (None, _) => { 118 (None, _) => {
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
index aee880625..9404aa26d 100644
--- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
@@ -169,7 +169,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
169} 169}
170 170
171fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { 171fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
172 sema.type_of_pat(&pat) 172 sema.type_of_pat(pat)
173 .and_then(|ty| TryEnum::from_ty(sema, &ty)) 173 .and_then(|ty| TryEnum::from_ty(sema, &ty))
174 .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text()) 174 .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text())
175 .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_))) 175 .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_)))
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/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 8d271e056..14e862cd0 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -73,7 +73,11 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
73 for path in paths { 73 for path in paths {
74 final_path = ast::make::path_concat(path, final_path) 74 final_path = ast::make::path_concat(path, final_path)
75 } 75 }
76 Some(final_path) 76 if final_path.segment().map_or(false, |it| it.self_token().is_some()) {
77 final_path.qualifier()
78 } else {
79 Some(final_path)
80 }
77} 81}
78 82
79#[cfg(test)] 83#[cfg(test)]
@@ -223,4 +227,14 @@ pub use std::fmt::Display;
223", 227",
224 ); 228 );
225 } 229 }
230
231 #[test]
232 fn unmerge_use_item_on_self() {
233 check_assist(
234 unmerge_use,
235 r"use std::process::{Command, self$0};",
236 r"use std::process::{Command};
237use std::process;",
238 );
239 }
226} 240}
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
index 2f1da82c7..140e27356 100644
--- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
@@ -123,7 +123,7 @@ impl TailReturnCollector {
123 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { 123 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
124 match expr { 124 match expr {
125 Expr::BlockExpr(block_expr) => { 125 Expr::BlockExpr(block_expr) => {
126 self.collect_jump_exprs(&block_expr, collect_break); 126 self.collect_jump_exprs(block_expr, collect_break);
127 } 127 }
128 Expr::ReturnExpr(ret_expr) => { 128 Expr::ReturnExpr(ret_expr) => {
129 if let Some(ret_expr_arg) = &ret_expr.expr() { 129 if let Some(ret_expr_arg) = &ret_expr.expr() {
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 16af72927..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 => return true,
51 AssistKind::Refactor => match other {
52 AssistKind::RefactorExtract
53 | AssistKind::RefactorInline
54 | AssistKind::RefactorRewrite => return true,
55 _ => return false,
56 },
57 _ => return 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 2b7c2d581..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 {
@@ -74,18 +74,18 @@ pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) {
74#[track_caller] 74#[track_caller]
75fn check_doc_test(assist_id: &str, before: &str, after: &str) { 75fn check_doc_test(assist_id: &str, before: &str, after: &str) {
76 let after = trim_indent(after); 76 let after = trim_indent(after);
77 let (db, file_id, selection) = RootDatabase::with_range_or_offset(&before); 77 let (db, file_id, selection) = RootDatabase::with_range_or_offset(before);
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_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 30128a24a..068df005b 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -492,7 +492,7 @@ pub(crate) fn add_method_to_adt(
492 let start_offset = impl_def 492 let start_offset = impl_def
493 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) 493 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
494 .unwrap_or_else(|| { 494 .unwrap_or_else(|| {
495 buf = generate_impl_text(&adt, &buf); 495 buf = generate_impl_text(adt, &buf);
496 adt.syntax().text_range().end() 496 adt.syntax().text_range().end()
497 }); 497 });
498 498
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index b3aabeab3..cb8bc8b2f 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -187,7 +187,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> {
187 } 187 }
188 } 188 }
189 189
190 normalize(&name) 190 normalize(name)
191} 191}
192 192
193fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { 193fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index ba81c9e04..3c45fe1cb 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14itertools = "0.10.0" 14itertools = "0.10.0"
15log = "0.4.8" 15log = "0.4.8"
16rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index fbd499900..783305005 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -29,7 +29,7 @@ use crate::{
29 macro_::render_macro, 29 macro_::render_macro,
30 pattern::{render_struct_pat, render_variant_pat}, 30 pattern::{render_struct_pat, render_variant_pat},
31 render_field, render_resolution, render_tuple_field, 31 render_field, render_resolution, render_tuple_field,
32 type_alias::render_type_alias, 32 type_alias::{render_type_alias, render_type_alias_with_eq},
33 RenderContext, 33 RenderContext,
34 }, 34 },
35 CompletionContext, CompletionItem, CompletionItemKind, 35 CompletionContext, CompletionItem, CompletionItemKind,
@@ -109,9 +109,6 @@ impl Completions {
109 local_name: hir::Name, 109 local_name: hir::Name,
110 resolution: &hir::ScopeDef, 110 resolution: &hir::ScopeDef,
111 ) { 111 ) {
112 if ctx.expects_type() && resolution.is_value_def() {
113 return;
114 }
115 self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution)); 112 self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution));
116 } 113 }
117 114
@@ -134,9 +131,6 @@ impl Completions {
134 func: hir::Function, 131 func: hir::Function,
135 local_name: Option<hir::Name>, 132 local_name: Option<hir::Name>,
136 ) { 133 ) {
137 if ctx.expects_type() {
138 return;
139 }
140 self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func)); 134 self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func));
141 } 135 }
142 136
@@ -178,9 +172,6 @@ impl Completions {
178 } 172 }
179 173
180 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { 174 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
181 if ctx.expects_type() {
182 return;
183 }
184 self.add_opt(render_const(RenderContext::new(ctx), constant)); 175 self.add_opt(render_const(RenderContext::new(ctx), constant));
185 } 176 }
186 177
@@ -188,6 +179,14 @@ impl Completions {
188 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); 179 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias));
189 } 180 }
190 181
182 pub(crate) fn add_type_alias_with_eq(
183 &mut self,
184 ctx: &CompletionContext,
185 type_alias: hir::TypeAlias,
186 ) {
187 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
188 }
189
191 pub(crate) fn add_qualified_enum_variant( 190 pub(crate) fn add_qualified_enum_variant(
192 &mut self, 191 &mut self,
193 ctx: &CompletionContext, 192 ctx: &CompletionContext,
@@ -204,32 +203,30 @@ impl Completions {
204 variant: hir::Variant, 203 variant: hir::Variant,
205 local_name: Option<hir::Name>, 204 local_name: Option<hir::Name>,
206 ) { 205 ) {
207 if ctx.expects_type() {
208 return;
209 }
210 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); 206 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
211 self.add(item); 207 self.add(item);
212 } 208 }
213} 209}
214 210
211/// Calls the callback for each variant of the provided enum with the path to the variant.
215fn complete_enum_variants( 212fn complete_enum_variants(
216 acc: &mut Completions, 213 acc: &mut Completions,
217 ctx: &CompletionContext, 214 ctx: &CompletionContext,
218 enum_data: hir::Enum, 215 enum_: hir::Enum,
219 cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), 216 cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath),
220) { 217) {
221 let variants = enum_data.variants(ctx.db); 218 let variants = enum_.variants(ctx.db);
222 219
223 let module = if let Some(module) = ctx.scope.module() { 220 let module = if let Some(module) = ctx.scope.module() {
224 // Compute path from the completion site if available. 221 // Compute path from the completion site if available.
225 module 222 module
226 } else { 223 } else {
227 // Otherwise fall back to the enum's definition site. 224 // Otherwise fall back to the enum's definition site.
228 enum_data.module(ctx.db) 225 enum_.module(ctx.db)
229 }; 226 };
230 227
231 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { 228 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
232 if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_data)) { 229 if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) {
233 for &variant in &variants { 230 for &variant in &variants {
234 let self_path = hir::ModPath::from_segments( 231 let self_path = hir::ModPath::from_segments(
235 hir::PathKind::Plain, 232 hir::PathKind::Plain,
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 7f76e357e..6df569c2a 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -219,7 +219,7 @@ static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
219}); 219});
220const EXPR_ATTRIBUTES: &[&str] = attrs!(); 220const EXPR_ATTRIBUTES: &[&str] = attrs!();
221 221
222/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 222/// <https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index>
223// Keep these sorted for the binary search! 223// Keep these sorted for the binary search!
224const ATTRIBUTES: &[AttrCompletion] = &[ 224const ATTRIBUTES: &[AttrCompletion] = &[
225 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 225 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
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/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 8ad57a069..9552875c1 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -13,7 +13,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
13 _ => return complete_undotted_self(acc, ctx), 13 _ => return complete_undotted_self(acc, ctx),
14 }; 14 };
15 15
16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
17 Some(ty) => ty, 17 Some(ty) => ty,
18 _ => return, 18 _ => return,
19 }; 19 };
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index c010cbbca..30b8d44bd 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -1,10 +1,10 @@
1//! Feature: completion with imports-on-the-fly 1//! Feature: completion with imports-on-the-fly
2//! 2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates, 3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input. 4//! if they can be qualified in the scope, and their name contains all symbols from the completion input.
5//! 5//!
6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent. 6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively. 7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively.
8//! 8//!
9//! ``` 9//! ```
10//! fn main() { 10//! fn main() {
@@ -23,8 +23,8 @@
23//! ``` 23//! ```
24//! 24//!
25//! Also completes associated items, that require trait imports. 25//! Also completes associated items, that require trait imports.
26//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account. 26//! If any unresolved and/or partially-qualified path precedes the input, it will be taken into account.
27//! Currently, only the imports with their import path ending with the whole qialifier will be proposed 27//! Currently, only the imports with their import path ending with the whole qualifier will be proposed
28//! (no fuzzy matching for qualifier). 28//! (no fuzzy matching for qualifier).
29//! 29//!
30//! ``` 30//! ```
@@ -61,14 +61,14 @@
61//! } 61//! }
62//! ``` 62//! ```
63//! 63//!
64//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path, 64//! NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
65//! no imports will be proposed. 65//! no imports will be proposed.
66//! 66//!
67//! .Fuzzy search details 67//! .Fuzzy search details
68//! 68//!
69//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only 69//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
70//! (i.e. in `HashMap` in the `std::collections::HashMap` path). 70//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
71//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols 71//! For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
72//! (but shows all associated items for any input length). 72//! (but shows all associated items for any input length).
73//! 73//!
74//! .Import configuration 74//! .Import configuration
@@ -79,15 +79,15 @@
79//! .LSP and performance implications 79//! .LSP and performance implications
80//! 80//!
81//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` 81//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
82//! (case sensitive) resolve client capability in its client capabilities. 82//! (case-sensitive) resolve client capability in its client capabilities.
83//! This way the server is able to defer the costly computations, doing them for a selected completion item only. 83//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
84//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, 84//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
85//! which might be slow ergo the feature is automatically disabled. 85//! which might be slow ergo the feature is automatically disabled.
86//! 86//!
87//! .Feature toggle 87//! .Feature toggle
88//! 88//!
89//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag. 89//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
90//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 90//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
91//! capability enabled. 91//! capability enabled.
92 92
93use ide_db::helpers::{ 93use ide_db::helpers::{
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/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 86eb21714..9f98b21be 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -34,7 +34,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
34 34
35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal); 35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
36 36
37 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 37 let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
38 Some(it) => it, 38 Some(it) => it,
39 None => return, 39 None => return,
40 }; 40 };
@@ -50,7 +50,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
50 postfix_snippet( 50 postfix_snippet(
51 ctx, 51 ctx,
52 cap, 52 cap,
53 &dot_receiver, 53 dot_receiver,
54 "ifl", 54 "ifl",
55 "if let Ok {}", 55 "if let Ok {}",
56 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text), 56 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
@@ -60,7 +60,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
60 postfix_snippet( 60 postfix_snippet(
61 ctx, 61 ctx,
62 cap, 62 cap,
63 &dot_receiver, 63 dot_receiver,
64 "while", 64 "while",
65 "while let Ok {}", 65 "while let Ok {}",
66 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text), 66 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
@@ -71,7 +71,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
71 postfix_snippet( 71 postfix_snippet(
72 ctx, 72 ctx,
73 cap, 73 cap,
74 &dot_receiver, 74 dot_receiver,
75 "ifl", 75 "ifl",
76 "if let Some {}", 76 "if let Some {}",
77 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text), 77 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
@@ -81,7 +81,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
81 postfix_snippet( 81 postfix_snippet(
82 ctx, 82 ctx,
83 cap, 83 cap,
84 &dot_receiver, 84 dot_receiver,
85 "while", 85 "while",
86 "while let Some {}", 86 "while let Some {}",
87 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text), 87 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
@@ -93,7 +93,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
93 postfix_snippet( 93 postfix_snippet(
94 ctx, 94 ctx,
95 cap, 95 cap,
96 &dot_receiver, 96 dot_receiver,
97 "if", 97 "if",
98 "if expr {}", 98 "if expr {}",
99 &format!("if {} {{\n $0\n}}", receiver_text), 99 &format!("if {} {{\n $0\n}}", receiver_text),
@@ -102,22 +102,22 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
102 postfix_snippet( 102 postfix_snippet(
103 ctx, 103 ctx,
104 cap, 104 cap,
105 &dot_receiver, 105 dot_receiver,
106 "while", 106 "while",
107 "while expr {}", 107 "while expr {}",
108 &format!("while {} {{\n $0\n}}", receiver_text), 108 &format!("while {} {{\n $0\n}}", receiver_text),
109 ) 109 )
110 .add_to(acc); 110 .add_to(acc);
111 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) 111 postfix_snippet(ctx, cap, dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
112 .add_to(acc); 112 .add_to(acc);
113 } 113 }
114 114
115 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) 115 postfix_snippet(ctx, cap, dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
116 .add_to(acc); 116 .add_to(acc);
117 postfix_snippet( 117 postfix_snippet(
118 ctx, 118 ctx,
119 cap, 119 cap,
120 &dot_receiver, 120 dot_receiver,
121 "refm", 121 "refm",
122 "&mut expr", 122 "&mut expr",
123 &format!("&mut {}", receiver_text), 123 &format!("&mut {}", receiver_text),
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 9ebe1dcc0..2dc13c293 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -53,7 +53,7 @@ pub(crate) fn add_format_like_completions(
53 for (label, macro_name) in KINDS { 53 for (label, macro_name) in KINDS {
54 let snippet = parser.into_suggestion(macro_name); 54 let snippet = parser.into_suggestion(macro_name);
55 55
56 postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); 56 postfix_snippet(ctx, cap, dot_receiver, label, macro_name, &snippet).add_to(acc);
57 } 57 }
58 } 58 }
59} 59}
@@ -91,7 +91,7 @@ enum State {
91impl FormatStrParser { 91impl FormatStrParser {
92 pub(crate) fn new(input: String) -> Self { 92 pub(crate) fn new(input: String) -> Self {
93 Self { 93 Self {
94 input: input, 94 input,
95 output: String::new(), 95 output: String::new(),
96 extracted_expressions: Vec::new(), 96 extracted_expressions: Vec::new(),
97 state: State::NotExpr, 97 state: State::NotExpr,
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 58d4dd9ee..1643eeed4 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -15,10 +15,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
15 None => return, 15 None => return,
16 }; 16 };
17 17
18 let resolution = match ctx.sema.resolve_path(&path) { 18 let resolution = match ctx.sema.resolve_path(path) {
19 Some(res) => res, 19 Some(res) => res,
20 None => return, 20 None => return,
21 }; 21 };
22
22 let context_module = ctx.scope.module(); 23 let context_module = ctx.scope.module();
23 24
24 if ctx.expects_item() || ctx.expects_assoc_item() { 25 if ctx.expects_item() || ctx.expects_assoc_item() {
@@ -60,21 +61,31 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
60 } 61 }
61 } 62 }
62 63
63 if let hir::ScopeDef::MacroDef(macro_def) = def { 64 let add_resolution = match def {
64 if !macro_def.is_fn_like() { 65 // Don't suggest attribute macros and derives.
65 // Don't suggest attribute macros and derives. 66 hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
66 continue; 67 // no values in type places
68 hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(_))
69 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Variant(_))
70 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Static(_))
71 | hir::ScopeDef::Local(_) => !ctx.expects_type(),
72 // unless its a constant in a generic arg list position
73 hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
74 !ctx.expects_type() || ctx.expects_generic_arg()
67 } 75 }
68 } 76 _ => true,
77 };
69 78
70 acc.add_resolution(ctx, name, &def); 79 if add_resolution {
80 acc.add_resolution(ctx, name, &def);
81 }
71 } 82 }
72 } 83 }
73 hir::PathResolution::Def(def @ hir::ModuleDef::Adt(_)) 84 hir::PathResolution::Def(def @ hir::ModuleDef::Adt(_))
74 | hir::PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) 85 | hir::PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_))
75 | hir::PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => { 86 | hir::PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => {
76 if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { 87 if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
77 add_enum_variants(ctx, acc, e); 88 add_enum_variants(acc, ctx, e);
78 } 89 }
79 let ty = match def { 90 let ty = match def {
80 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), 91 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
@@ -82,7 +93,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
82 let ty = a.ty(ctx.db); 93 let ty = a.ty(ctx.db);
83 if let Some(hir::Adt::Enum(e)) = ty.as_adt() { 94 if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
84 cov_mark::hit!(completes_variant_through_alias); 95 cov_mark::hit!(completes_variant_through_alias);
85 add_enum_variants(ctx, acc, e); 96 add_enum_variants(acc, ctx, e);
86 } 97 }
87 ty 98 ty
88 } 99 }
@@ -107,11 +118,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
107 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 118 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
108 return None; 119 return None;
109 } 120 }
110 match item { 121 add_assoc_item(acc, ctx, item);
111 hir::AssocItem::Function(func) => acc.add_function(ctx, func, None),
112 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
113 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
114 }
115 None::<()> 122 None::<()>
116 }); 123 });
117 124
@@ -133,11 +140,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
133 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 140 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
134 continue; 141 continue;
135 } 142 }
136 match item { 143 add_assoc_item(acc, ctx, item);
137 hir::AssocItem::Function(func) => acc.add_function(ctx, func, None),
138 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
139 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
140 }
141 } 144 }
142 } 145 }
143 hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { 146 hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
@@ -149,7 +152,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
149 }; 152 };
150 153
151 if let Some(hir::Adt::Enum(e)) = ty.as_adt() { 154 if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
152 add_enum_variants(ctx, acc, e); 155 add_enum_variants(acc, ctx, e);
153 } 156 }
154 157
155 let traits_in_scope = ctx.scope.traits_in_scope(); 158 let traits_in_scope = ctx.scope.traits_in_scope();
@@ -162,11 +165,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
162 // We might iterate candidates of a trait multiple times here, so deduplicate 165 // We might iterate candidates of a trait multiple times here, so deduplicate
163 // them. 166 // them.
164 if seen.insert(item) { 167 if seen.insert(item) {
165 match item { 168 add_assoc_item(acc, ctx, item);
166 hir::AssocItem::Function(func) => acc.add_function(ctx, func, None),
167 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
168 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
169 }
170 } 169 }
171 None::<()> 170 None::<()>
172 }); 171 });
@@ -176,10 +175,22 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
176 } 175 }
177} 176}
178 177
179fn add_enum_variants(ctx: &CompletionContext, acc: &mut Completions, e: hir::Enum) { 178fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
180 for variant in e.variants(ctx.db) { 179 match item {
181 acc.add_enum_variant(ctx, variant, None); 180 hir::AssocItem::Function(func) if !ctx.expects_type() => acc.add_function(ctx, func, None),
181 hir::AssocItem::Const(ct) if !ctx.expects_type() || ctx.expects_generic_arg() => {
182 acc.add_const(ctx, ct)
183 }
184 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
185 _ => (),
186 }
187}
188
189fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) {
190 if ctx.expects_type() {
191 return;
182 } 192 }
193 e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None));
183} 194}
184 195
185#[cfg(test)] 196#[cfg(test)]
@@ -927,4 +938,24 @@ fn main() {
927 "#]], 938 "#]],
928 ); 939 );
929 } 940 }
941
942 #[test]
943 fn completes_types_and_const_in_arg_list() {
944 check(
945 r#"
946mod foo {
947 pub const CONST: () = ();
948 pub type Type = ();
949}
950
951struct Foo<T>(t);
952
953fn foo(_: Foo<foo::$0>) {}
954"#,
955 expect![[r#"
956 ta Type
957 ct CONST
958 "#]],
959 );
960 }
930} 961}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index b1e6b2b77..b5af1c810 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,8 +1,9 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::{ast, AstNode};
4 5
5use crate::{CompletionContext, Completions}; 6use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
6 7
7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 8pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
8 if ctx.is_path_disallowed() || !ctx.is_trivial_path() { 9 if ctx.is_path_disallowed() || !ctx.is_trivial_path() {
@@ -35,21 +36,50 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
35 return; 36 return;
36 } 37 }
37 38
38 if let Some(hir::Adt::Enum(e)) = 39 if !ctx.expects_type() {
39 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 40 if let Some(hir::Adt::Enum(e)) =
40 { 41 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
41 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { 42 {
42 acc.add_qualified_enum_variant(ctx, variant, path) 43 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
43 }); 44 acc.add_qualified_enum_variant(ctx, variant, path)
45 });
46 }
47 }
48
49 if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
50 if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) {
51 if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
52 ctx.sema.resolve_path(&path_seg.parent_path())
53 {
54 trait_.items(ctx.sema.db).into_iter().for_each(|it| {
55 if let hir::AssocItem::TypeAlias(alias) = it {
56 acc.add_type_alias_with_eq(ctx, alias)
57 }
58 });
59 }
60 }
44 } 61 }
45 62
46 ctx.scope.process_all_names(&mut |name, res| { 63 ctx.scope.process_all_names(&mut |name, res| {
47 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 64 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) =
65 res
66 {
48 cov_mark::hit!(skip_lifetime_completion); 67 cov_mark::hit!(skip_lifetime_completion);
49 return; 68 return;
50 } 69 }
51 let add_resolution = match res { 70 let add_resolution = match res {
71 // Don't suggest attribute macros and derives.
52 ScopeDef::MacroDef(mac) => mac.is_fn_like(), 72 ScopeDef::MacroDef(mac) => mac.is_fn_like(),
73 // no values in type places
74 ScopeDef::ModuleDef(hir::ModuleDef::Function(_))
75 | ScopeDef::ModuleDef(hir::ModuleDef::Variant(_))
76 | ScopeDef::ModuleDef(hir::ModuleDef::Static(_))
77 | ScopeDef::Local(_) => !ctx.expects_type(),
78 // unless its a constant in a generic arg list position
79 ScopeDef::ModuleDef(hir::ModuleDef::Const(_))
80 | ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => {
81 !ctx.expects_type() || ctx.expects_generic_arg()
82 }
53 _ => true, 83 _ => true,
54 }; 84 };
55 if add_resolution { 85 if add_resolution {
@@ -777,4 +807,30 @@ $0
777 "#]], 807 "#]],
778 ) 808 )
779 } 809 }
810
811 #[test]
812 fn completes_types_and_const_in_arg_list() {
813 check(
814 r#"
815enum Bar {
816 Baz
817}
818trait Foo {
819 type Bar;
820}
821
822const CONST: () = ();
823
824fn foo<T: Foo<$0>, const CONST_PARAM: usize>(_: T) {}
825"#,
826 expect![[r#"
827 ta Bar = type Bar;
828 tp T
829 cp CONST_PARAM
830 tt Foo
831 en Bar
832 ct CONST
833 "#]],
834 );
835 }
780} 836}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 2c2a4aa6b..a8437d81c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -276,6 +276,10 @@ impl<'a> CompletionContext<'a> {
276 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 276 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
277 } 277 }
278 278
279 pub(crate) fn expects_generic_arg(&self) -> bool {
280 matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
281 }
282
279 pub(crate) fn has_block_expr_parent(&self) -> bool { 283 pub(crate) fn has_block_expr_parent(&self) -> bool {
280 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 284 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
281 } 285 }
@@ -380,7 +384,7 @@ impl<'a> CompletionContext<'a> {
380 (|| { 384 (|| {
381 let expr_field = self.token.prev_sibling_or_token()? 385 let expr_field = self.token.prev_sibling_or_token()?
382 .into_node() 386 .into_node()
383 .and_then(|node| ast::RecordExprField::cast(node))?; 387 .and_then(ast::RecordExprField::cast)?;
384 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; 388 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
385 Some(( 389 Some((
386 Some(ty), 390 Some(ty),
@@ -467,7 +471,7 @@ impl<'a> CompletionContext<'a> {
467 self.expected_type = expected_type; 471 self.expected_type = expected_type;
468 self.expected_name = expected_name; 472 self.expected_name = expected_name;
469 473
470 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { 474 let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
471 Some(it) => it, 475 Some(it) => it,
472 None => return, 476 None => return,
473 }; 477 };
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index ee87bf461..72e67e3c4 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -47,6 +47,9 @@ pub(crate) enum ImmediateLocation {
47 receiver_is_ambiguous_float_literal: bool, 47 receiver_is_ambiguous_float_literal: bool,
48 }, 48 },
49 // Original file ast node 49 // Original file ast node
50 // Only set from a type arg
51 GenericArgList(ast::GenericArgList),
52 // Original file ast node
50 /// The record expr of the field name we are completing 53 /// The record expr of the field name we are completing
51 RecordExpr(ast::RecordExpr), 54 RecordExpr(ast::RecordExpr),
52 // Original file ast node 55 // Original file ast node
@@ -112,12 +115,12 @@ pub(crate) fn determine_location(
112) -> Option<ImmediateLocation> { 115) -> Option<ImmediateLocation> {
113 let node = match name_like { 116 let node = match name_like {
114 ast::NameLike::NameRef(name_ref) => { 117 ast::NameLike::NameRef(name_ref) => {
115 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 118 if ast::RecordExprField::for_field_name(name_ref).is_some() {
116 return sema 119 return sema
117 .find_node_at_offset_with_macros(original_file, offset) 120 .find_node_at_offset_with_macros(original_file, offset)
118 .map(ImmediateLocation::RecordExpr); 121 .map(ImmediateLocation::RecordExpr);
119 } 122 }
120 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { 123 if ast::RecordPatField::for_field_name_ref(name_ref).is_some() {
121 return sema 124 return sema
122 .find_node_at_offset_with_macros(original_file, offset) 125 .find_node_at_offset_with_macros(original_file, offset)
123 .map(ImmediateLocation::RecordPat); 126 .map(ImmediateLocation::RecordPat);
@@ -125,7 +128,7 @@ pub(crate) fn determine_location(
125 maximize_name_ref(name_ref) 128 maximize_name_ref(name_ref)
126 } 129 }
127 ast::NameLike::Name(name) => { 130 ast::NameLike::Name(name) => {
128 if ast::RecordPatField::for_field_name(&name).is_some() { 131 if ast::RecordPatField::for_field_name(name).is_some() {
129 return sema 132 return sema
130 .find_node_at_offset_with_macros(original_file, offset) 133 .find_node_at_offset_with_macros(original_file, offset)
131 .map(ImmediateLocation::RecordPat); 134 .map(ImmediateLocation::RecordPat);
@@ -159,7 +162,6 @@ pub(crate) fn determine_location(
159 } 162 }
160 } 163 }
161 }; 164 };
162
163 let res = match_ast! { 165 let res = match_ast! {
164 match parent { 166 match parent {
165 ast::IdentPat(_it) => ImmediateLocation::IdentPat, 167 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
@@ -174,6 +176,9 @@ pub(crate) fn determine_location(
174 Some(TRAIT) => ImmediateLocation::Trait, 176 Some(TRAIT) => ImmediateLocation::Trait,
175 _ => return None, 177 _ => return None,
176 }, 178 },
179 ast::GenericArgList(_it) => sema
180 .find_node_at_offset_with_macros(original_file, offset)
181 .map(ImmediateLocation::GenericArgList)?,
177 ast::Module(it) => { 182 ast::Module(it) => {
178 if it.item_list().is_none() { 183 if it.item_list().is_none() {
179 ImmediateLocation::ModDeclaration(it) 184 ImmediateLocation::ModDeclaration(it)
@@ -254,7 +259,7 @@ fn test_inside_impl_trait_block() {
254} 259}
255 260
256pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { 261pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
257 element.into_token().and_then(|it| previous_non_trivia_token(it)) 262 element.into_token().and_then(previous_non_trivia_token)
258} 263}
259 264
260/// Check if the token previous to the previous one is `for`. 265/// Check if the token previous to the previous one is `for`.
@@ -262,8 +267,8 @@ pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
262pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { 267pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
263 element 268 element
264 .into_token() 269 .into_token()
265 .and_then(|it| previous_non_trivia_token(it)) 270 .and_then(previous_non_trivia_token)
266 .and_then(|it| previous_non_trivia_token(it)) 271 .and_then(previous_non_trivia_token)
267 .filter(|it| it.kind() == T![for]) 272 .filter(|it| it.kind() == T![for])
268 .is_some() 273 .is_some()
269} 274}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index d3db55c35..2bd2c44d0 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -23,53 +23,6 @@ use crate::{
23 render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, 23 render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
25}; 25};
26
27pub(crate) fn render_field(
28 ctx: RenderContext<'_>,
29 receiver: Option<hir::Name>,
30 field: hir::Field,
31 ty: &hir::Type,
32) -> CompletionItem {
33 render_field_(ctx, receiver, field, ty)
34}
35
36pub(crate) fn render_tuple_field(
37 ctx: RenderContext<'_>,
38 receiver: Option<hir::Name>,
39 field: usize,
40 ty: &hir::Type,
41) -> CompletionItem {
42 render_tuple_field_(ctx, receiver, field, ty)
43}
44
45pub(crate) fn render_resolution(
46 ctx: RenderContext<'_>,
47 local_name: hir::Name,
48 resolution: &hir::ScopeDef,
49) -> Option<CompletionItem> {
50 render_resolution_(ctx, local_name, None, resolution)
51}
52
53pub(crate) fn render_resolution_with_import(
54 ctx: RenderContext<'_>,
55 import_edit: ImportEdit,
56) -> Option<CompletionItem> {
57 let resolution = hir::ScopeDef::from(import_edit.import.original_item);
58 if ctx.completion.expects_type() && resolution.is_value_def() {
59 return None;
60 }
61 let local_name = match resolution {
62 hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
63 hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
64 hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
65 _ => item_name(ctx.db(), import_edit.import.original_item)?,
66 };
67 render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| {
68 item.completion_kind = CompletionKind::Magic;
69 item
70 })
71}
72
73/// Interface for data and methods required for items rendering. 26/// Interface for data and methods required for items rendering.
74#[derive(Debug)] 27#[derive(Debug)]
75pub(crate) struct RenderContext<'a> { 28pub(crate) struct RenderContext<'a> {
@@ -86,7 +39,7 @@ impl<'a> RenderContext<'a> {
86 } 39 }
87 40
88 fn db(&self) -> &'a RootDatabase { 41 fn db(&self) -> &'a RootDatabase {
89 &self.completion.db 42 self.completion.db
90 } 43 }
91 44
92 fn source_range(&self) -> TextRange { 45 fn source_range(&self) -> TextRange {
@@ -122,7 +75,7 @@ impl<'a> RenderContext<'a> {
122 } 75 }
123} 76}
124 77
125fn render_field_( 78pub(crate) fn render_field(
126 ctx: RenderContext<'_>, 79 ctx: RenderContext<'_>,
127 receiver: Option<hir::Name>, 80 receiver: Option<hir::Name>,
128 field: hir::Field, 81 field: hir::Field,
@@ -135,7 +88,6 @@ fn render_field_(
135 ctx.source_range(), 88 ctx.source_range(),
136 receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), 89 receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
137 ); 90 );
138
139 item.set_relevance(CompletionRelevance { 91 item.set_relevance(CompletionRelevance {
140 type_match: compute_type_match(ctx.completion, ty), 92 type_match: compute_type_match(ctx.completion, ty),
141 exact_name_match: compute_exact_name_match(ctx.completion, &name), 93 exact_name_match: compute_exact_name_match(ctx.completion, &name),
@@ -146,17 +98,15 @@ fn render_field_(
146 .set_documentation(field.docs(ctx.db())) 98 .set_documentation(field.docs(ctx.db()))
147 .set_deprecated(is_deprecated) 99 .set_deprecated(is_deprecated)
148 .lookup_by(name); 100 .lookup_by(name);
149
150 if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) { 101 if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) {
151 // FIXME 102 // FIXME
152 // For now we don't properly calculate the edits for ref match 103 // For now we don't properly calculate the edits for ref match
153 // completions on struct fields, so we've disabled them. See #8058. 104 // completions on struct fields, so we've disabled them. See #8058.
154 } 105 }
155
156 item.build() 106 item.build()
157} 107}
158 108
159fn render_tuple_field_( 109pub(crate) fn render_tuple_field(
160 ctx: RenderContext<'_>, 110 ctx: RenderContext<'_>,
161 receiver: Option<hir::Name>, 111 receiver: Option<hir::Name>,
162 field: usize, 112 field: usize,
@@ -167,14 +117,37 @@ fn render_tuple_field_(
167 ctx.source_range(), 117 ctx.source_range(),
168 receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), 118 receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
169 ); 119 );
170
171 item.kind(SymbolKind::Field) 120 item.kind(SymbolKind::Field)
172 .detail(ty.display(ctx.db()).to_string()) 121 .detail(ty.display(ctx.db()).to_string())
173 .lookup_by(field.to_string()); 122 .lookup_by(field.to_string());
174
175 item.build() 123 item.build()
176} 124}
177 125
126pub(crate) fn render_resolution(
127 ctx: RenderContext<'_>,
128 local_name: hir::Name,
129 resolution: &hir::ScopeDef,
130) -> Option<CompletionItem> {
131 render_resolution_(ctx, local_name, None, resolution)
132}
133
134pub(crate) fn render_resolution_with_import(
135 ctx: RenderContext<'_>,
136 import_edit: ImportEdit,
137) -> Option<CompletionItem> {
138 let resolution = hir::ScopeDef::from(import_edit.import.original_item);
139 let local_name = match resolution {
140 hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
141 hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
142 hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
143 _ => item_name(ctx.db(), import_edit.import.original_item)?,
144 };
145 render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| {
146 item.completion_kind = CompletionKind::Magic;
147 item
148 })
149}
150
178fn render_resolution_( 151fn render_resolution_(
179 ctx: RenderContext<'_>, 152 ctx: RenderContext<'_>,
180 local_name: hir::Name, 153 local_name: hir::Name,
@@ -1007,6 +980,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
1007 980
1008 #[test] 981 #[test]
1009 fn too_many_arguments() { 982 fn too_many_arguments() {
983 cov_mark::check!(too_many_arguments);
1010 check_relevance( 984 check_relevance(
1011 r#" 985 r#"
1012struct Foo; 986struct Foo;
@@ -1151,16 +1125,11 @@ fn main() {
1151 fn suggest_deref() { 1125 fn suggest_deref() {
1152 check_relevance( 1126 check_relevance(
1153 r#" 1127 r#"
1154#[lang = "deref"] 1128//- minicore: deref
1155trait Deref {
1156 type Target;
1157 fn deref(&self) -> &Self::Target;
1158}
1159
1160struct S; 1129struct S;
1161struct T(S); 1130struct T(S);
1162 1131
1163impl Deref for T { 1132impl core::ops::Deref for T {
1164 type Target = S; 1133 type Target = S;
1165 1134
1166 fn deref(&self) -> &Self::Target { 1135 fn deref(&self) -> &Self::Target {
@@ -1184,8 +1153,9 @@ fn main() {
1184 st T [] 1153 st T []
1185 st S [] 1154 st S []
1186 fn main() [] 1155 fn main() []
1187 tt Deref []
1188 fn foo(…) [] 1156 fn foo(…) []
1157 md core []
1158 tt Sized []
1189 "#]], 1159 "#]],
1190 ) 1160 )
1191 } 1161 }
@@ -1194,21 +1164,11 @@ fn main() {
1194 fn suggest_deref_mut() { 1164 fn suggest_deref_mut() {
1195 check_relevance( 1165 check_relevance(
1196 r#" 1166 r#"
1197#[lang = "deref"] 1167//- minicore: deref_mut
1198trait Deref {
1199 type Target;
1200 fn deref(&self) -> &Self::Target;
1201}
1202
1203#[lang = "deref_mut"]
1204pub trait DerefMut: Deref {
1205 fn deref_mut(&mut self) -> &mut Self::Target;
1206}
1207
1208struct S; 1168struct S;
1209struct T(S); 1169struct T(S);
1210 1170
1211impl Deref for T { 1171impl core::ops::Deref for T {
1212 type Target = S; 1172 type Target = S;
1213 1173
1214 fn deref(&self) -> &Self::Target { 1174 fn deref(&self) -> &Self::Target {
@@ -1216,7 +1176,7 @@ impl Deref for T {
1216 } 1176 }
1217} 1177}
1218 1178
1219impl DerefMut for T { 1179impl core::ops::DerefMut for T {
1220 fn deref_mut(&mut self) -> &mut Self::Target { 1180 fn deref_mut(&mut self) -> &mut Self::Target {
1221 &mut self.0 1181 &mut self.0
1222 } 1182 }
@@ -1235,12 +1195,12 @@ fn main() {
1235 lc m [local] 1195 lc m [local]
1236 lc t [local] 1196 lc t [local]
1237 lc &mut t [type+local] 1197 lc &mut t [type+local]
1238 tt DerefMut []
1239 tt Deref []
1240 fn foo(…) []
1241 st T [] 1198 st T []
1242 st S [] 1199 st S []
1243 fn main() [] 1200 fn main() []
1201 fn foo(…) []
1202 md core []
1203 tt Sized []
1244 "#]], 1204 "#]],
1245 ) 1205 )
1246 } 1206 }
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 429d937c8..3a7238bb8 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -180,7 +180,7 @@ fn main() { frobnicate!(); }
180/// ``` 180/// ```
181macro_rules! vec { () => {} } 181macro_rules! vec { () => {} }
182 182
183fn fn main() { v$0 } 183fn main() { v$0 }
184"#, 184"#,
185 r#" 185 r#"
186/// Creates a [`Vec`] containing the arguments. 186/// Creates a [`Vec`] containing the arguments.
@@ -193,7 +193,7 @@ fn fn main() { v$0 }
193/// ``` 193/// ```
194macro_rules! vec { () => {} } 194macro_rules! vec { () => {} }
195 195
196fn fn main() { vec![$0] } 196fn main() { vec![$0] }
197"#, 197"#,
198 ); 198 );
199 199
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index b4e80f424..3717a0409 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -75,10 +75,10 @@ fn render_pat(
75) -> Option<String> { 75) -> Option<String> {
76 let mut pat = match kind { 76 let mut pat = match kind {
77 StructKind::Tuple if ctx.snippet_cap().is_some() => { 77 StructKind::Tuple if ctx.snippet_cap().is_some() => {
78 render_tuple_as_pat(&fields, &name, fields_omitted) 78 render_tuple_as_pat(fields, name, fields_omitted)
79 } 79 }
80 StructKind::Record => { 80 StructKind::Record => {
81 render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) 81 render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
82 } 82 }
83 _ => return None, 83 _ => return None,
84 }; 84 };
@@ -86,7 +86,7 @@ fn render_pat(
86 if ctx.completion.is_param { 86 if ctx.completion.is_param {
87 pat.push(':'); 87 pat.push(':');
88 pat.push(' '); 88 pat.push(' ');
89 pat.push_str(&name); 89 pat.push_str(name);
90 } 90 }
91 if ctx.snippet_cap().is_some() { 91 if ctx.snippet_cap().is_some() {
92 pat.push_str("$0"); 92 pat.push_str("$0");
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index e47b4c745..e0234171a 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -16,7 +16,14 @@ pub(crate) fn render_type_alias<'a>(
16 ctx: RenderContext<'a>, 16 ctx: RenderContext<'a>,
17 type_alias: hir::TypeAlias, 17 type_alias: hir::TypeAlias,
18) -> Option<CompletionItem> { 18) -> Option<CompletionItem> {
19 TypeAliasRender::new(ctx, type_alias)?.render() 19 TypeAliasRender::new(ctx, type_alias)?.render(false)
20}
21
22pub(crate) fn render_type_alias_with_eq<'a>(
23 ctx: RenderContext<'a>,
24 type_alias: hir::TypeAlias,
25) -> Option<CompletionItem> {
26 TypeAliasRender::new(ctx, type_alias)?.render(true)
20} 27}
21 28
22#[derive(Debug)] 29#[derive(Debug)]
@@ -32,8 +39,14 @@ impl<'a> TypeAliasRender<'a> {
32 Some(TypeAliasRender { ctx, type_alias, ast_node }) 39 Some(TypeAliasRender { ctx, type_alias, ast_node })
33 } 40 }
34 41
35 fn render(self) -> Option<CompletionItem> { 42 fn render(self, with_eq: bool) -> Option<CompletionItem> {
36 let name = self.name()?; 43 let name = self.ast_node.name().map(|name| {
44 if with_eq {
45 format!("{} = ", name.text())
46 } else {
47 name.text().to_string()
48 }
49 })?;
37 let detail = self.detail(); 50 let detail = self.detail();
38 51
39 let mut item = 52 let mut item =
@@ -49,10 +62,6 @@ impl<'a> TypeAliasRender<'a> {
49 Some(item.build()) 62 Some(item.build())
50 } 63 }
51 64
52 fn name(&self) -> Option<String> {
53 self.ast_node.name().map(|name| name.text().to_string())
54 }
55
56 fn detail(&self) -> String { 65 fn detail(&self) -> String {
57 type_label(&self.ast_node) 66 type_label(&self.ast_node)
58 } 67 }
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 6229996ec..e219c577a 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14log = "0.4.8" 14log = "0.4.8"
15rayon = "1.5.0" 15rayon = "1.5.0"
16fst = { version = "0.4", default-features = false } 16fst = { version = "0.4", default-features = false }
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/call_info.rs b/crates/ide_db/src/call_info.rs
index 933bcad55..4795e2565 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -162,7 +162,7 @@ impl ActiveParameter {
162 } 162 }
163 163
164 pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { 164 pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
165 let (signature, active_parameter) = call_info_impl(&sema, token)?; 165 let (signature, active_parameter) = call_info_impl(sema, token)?;
166 166
167 let idx = active_parameter?; 167 let idx = active_parameter?;
168 let mut params = signature.params(sema.db); 168 let mut params = signature.params(sema.db);
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index ae52dd8bb..9634d872e 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -323,7 +323,7 @@ fn import_for_item(
323 } 323 }
324 324
325 let segment_import = 325 let segment_import =
326 find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; 326 find_import_for_segment(db, original_item_candidate, unresolved_first_segment)?;
327 let trait_item_to_import = item_as_assoc(db, original_item) 327 let trait_item_to_import = item_as_assoc(db, original_item)
328 .and_then(|assoc| assoc.containing_trait(db)) 328 .and_then(|assoc| assoc.containing_trait(db))
329 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); 329 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
@@ -383,7 +383,7 @@ fn find_import_for_segment(
383 original_item 383 original_item
384 } else { 384 } else {
385 let matching_module = 385 let matching_module =
386 module_with_segment_name(db, &unresolved_first_segment, original_item)?; 386 module_with_segment_name(db, unresolved_first_segment, original_item)?;
387 ItemInNs::from(ModuleDef::from(matching_module)) 387 ItemInNs::from(ModuleDef::from(matching_module))
388 }) 388 })
389} 389}
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/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 0dbabb44f..ec29476a4 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -124,7 +124,7 @@ fn recursive_merge(
124 .map(|tree_list| tree_list.use_trees().any(tree_is_self)) 124 .map(|tree_list| tree_list.use_trees().any(tree_is_self))
125 .unwrap_or(false) 125 .unwrap_or(false)
126 }; 126 };
127 match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { 127 match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) {
128 (true, false) => continue, 128 (true, false) => continue,
129 (false, true) => { 129 (false, true) => {
130 *lhs_t = rhs_t; 130 *lhs_t = rhs_t;
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_db/src/search.rs b/crates/ide_db/src/search.rs
index 8152630f5..a840e06a6 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -409,7 +409,7 @@ impl<'a> FindUsages<'a> {
409 if let Some(ast::NameLike::NameRef(name_ref)) = 409 if let Some(ast::NameLike::NameRef(name_ref)) =
410 sema.find_node_at_offset_with_descend(&tree, offset) 410 sema.find_node_at_offset_with_descend(&tree, offset)
411 { 411 {
412 if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) { 412 if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
413 return; 413 return;
414 } 414 }
415 } 415 }
@@ -424,7 +424,7 @@ impl<'a> FindUsages<'a> {
424 name_ref: &ast::NameRef, 424 name_ref: &ast::NameRef,
425 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 425 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
426 ) -> bool { 426 ) -> bool {
427 match NameRefClass::classify(self.sema, &name_ref) { 427 match NameRefClass::classify(self.sema, name_ref) {
428 Some(NameRefClass::Definition(Definition::SelfType(impl_))) 428 Some(NameRefClass::Definition(Definition::SelfType(impl_)))
429 if impl_.self_ty(self.sema.db) == *self_ty => 429 if impl_.self_ty(self.sema.db) == *self_ty =>
430 { 430 {
@@ -464,13 +464,13 @@ impl<'a> FindUsages<'a> {
464 name_ref: &ast::NameRef, 464 name_ref: &ast::NameRef,
465 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 465 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
466 ) -> bool { 466 ) -> bool {
467 match NameRefClass::classify(self.sema, &name_ref) { 467 match NameRefClass::classify(self.sema, name_ref) {
468 Some(NameRefClass::Definition(def)) if def == self.def => { 468 Some(NameRefClass::Definition(def)) if def == self.def => {
469 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 469 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
470 let reference = FileReference { 470 let reference = FileReference {
471 range, 471 range,
472 name: ast::NameLike::NameRef(name_ref.clone()), 472 name: ast::NameLike::NameRef(name_ref.clone()),
473 access: reference_access(&def, &name_ref), 473 access: reference_access(&def, name_ref),
474 }; 474 };
475 sink(file_id, reference) 475 sink(file_id, reference)
476 } 476 }
@@ -480,7 +480,7 @@ impl<'a> FindUsages<'a> {
480 let reference = FileReference { 480 let reference = FileReference {
481 range, 481 range,
482 name: ast::NameLike::NameRef(name_ref.clone()), 482 name: ast::NameLike::NameRef(name_ref.clone()),
483 access: reference_access(&def, &name_ref), 483 access: reference_access(&def, name_ref),
484 }; 484 };
485 sink(file_id, reference) 485 sink(file_id, reference)
486 } else { 486 } else {
@@ -490,11 +490,9 @@ impl<'a> FindUsages<'a> {
490 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 490 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
491 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 491 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
492 let access = match self.def { 492 let access = match self.def {
493 Definition::Field(_) if field == self.def => { 493 Definition::Field(_) if field == self.def => reference_access(&field, name_ref),
494 reference_access(&field, &name_ref)
495 }
496 Definition::Local(l) if local == l => { 494 Definition::Local(l) if local == l => {
497 reference_access(&Definition::Local(local), &name_ref) 495 reference_access(&Definition::Local(local), name_ref)
498 } 496 }
499 _ => return false, 497 _ => return false,
500 }; 498 };
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_diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs
new file mode 100644
index 000000000..d12594a4c
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs
@@ -0,0 +1,30 @@
1use crate::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: break-outside-of-loop
4//
5// This diagnostic is triggered if the `break` keyword is used outside of a loop.
6pub(crate) fn break_outside_of_loop(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::BreakOutsideOfLoop,
9) -> Diagnostic {
10 Diagnostic::new(
11 "break-outside-of-loop",
12 "break outside of loop",
13 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
14 )
15}
16
17#[cfg(test)]
18mod tests {
19 use crate::tests::check_diagnostics;
20
21 #[test]
22 fn break_outside_of_loop() {
23 check_diagnostics(
24 r#"
25fn foo() { break; }
26 //^^^^^ error: break outside of loop
27"#,
28 );
29 }
30}
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide_diagnostics/src/handlers/field_shorthand.rs
index 01bd2dba6..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}; 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),
@@ -46,7 +46,8 @@ fn check_expr_field_shorthand(
46 46
47 let field_range = record_field.syntax().text_range(); 47 let field_range = record_field.syntax().text_range();
48 acc.push( 48 acc.push(
49 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()) 49 Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range)
50 .severity(Severity::WeakWarning)
50 .with_fixes(Some(vec![fix( 51 .with_fixes(Some(vec![fix(
51 "use_expr_field_shorthand", 52 "use_expr_field_shorthand",
52 "Use struct shorthand initialization", 53 "Use struct shorthand initialization",
@@ -85,30 +86,32 @@ fn check_pat_field_shorthand(
85 let edit = edit_builder.finish(); 86 let edit = edit_builder.finish();
86 87
87 let field_range = record_pat_field.syntax().text_range(); 88 let field_range = record_pat_field.syntax().text_range();
88 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fixes( 89 acc.push(
89 Some(vec![fix( 90 Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range)
90 "use_pat_field_shorthand", 91 .severity(Severity::WeakWarning)
91 "Use struct field shorthand", 92 .with_fixes(Some(vec![fix(
92 SourceChange::from_text_edit(file_id, edit), 93 "use_pat_field_shorthand",
93 field_range, 94 "Use struct field shorthand",
94 )]), 95 SourceChange::from_text_edit(file_id, edit),
95 )); 96 field_range,
97 )])),
98 );
96 } 99 }
97} 100}
98 101
99#[cfg(test)] 102#[cfg(test)]
100mod tests { 103mod tests {
101 use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; 104 use crate::tests::{check_diagnostics, check_fix};
102 105
103 #[test] 106 #[test]
104 fn test_check_expr_field_shorthand() { 107 fn test_check_expr_field_shorthand() {
105 check_no_diagnostics( 108 check_diagnostics(
106 r#" 109 r#"
107struct A { a: &'static str } 110struct A { a: &'static str }
108fn main() { A { a: "hello" } } 111fn main() { A { a: "hello" } }
109"#, 112"#,
110 ); 113 );
111 check_no_diagnostics( 114 check_diagnostics(
112 r#" 115 r#"
113struct A(usize); 116struct A(usize);
114fn main() { A { 0: 0 } } 117fn main() { A { 0: 0 } }
@@ -154,13 +157,13 @@ fn main() {
154 157
155 #[test] 158 #[test]
156 fn test_check_pat_field_shorthand() { 159 fn test_check_pat_field_shorthand() {
157 check_no_diagnostics( 160 check_diagnostics(
158 r#" 161 r#"
159struct A { a: &'static str } 162struct A { a: &'static str }
160fn f(a: A) { let A { a: hello } = a; } 163fn f(a: A) { let A { a: hello } = a; }
161"#, 164"#,
162 ); 165 );
163 check_no_diagnostics( 166 check_diagnostics(
164 r#" 167 r#"
165struct A(usize); 168struct A(usize);
166fn f(a: A) { let A { 0: 0 } = a; } 169fn f(a: A) { let A { 0: 0 } = a; }
diff --git a/crates/ide_diagnostics/src/handlers/inactive_code.rs b/crates/ide_diagnostics/src/handlers/inactive_code.rs
new file mode 100644
index 000000000..dfd0e3351
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/inactive_code.rs
@@ -0,0 +1,116 @@
1use cfg::DnfExpr;
2use stdx::format_to;
3
4use crate::{Diagnostic, DiagnosticsContext, Severity};
5
6// Diagnostic: inactive-code
7//
8// This diagnostic is shown for code with inactive `#[cfg]` attributes.
9pub(crate) fn inactive_code(
10 ctx: &DiagnosticsContext<'_>,
11 d: &hir::InactiveCode,
12) -> Option<Diagnostic> {
13 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
14 if d.node.file_id.expansion_info(ctx.sema.db).is_some() {
15 return None;
16 }
17
18 let inactive = DnfExpr::new(d.cfg.clone()).why_inactive(&d.opts);
19 let mut message = "code is inactive due to #[cfg] directives".to_string();
20
21 if let Some(inactive) = inactive {
22 format_to!(message, ": {}", inactive);
23 }
24
25 let res = Diagnostic::new(
26 "inactive-code",
27 message,
28 ctx.sema.diagnostics_display_range(d.node.clone()).range,
29 )
30 .severity(Severity::WeakWarning)
31 .with_unused(true);
32 Some(res)
33}
34
35#[cfg(test)]
36mod tests {
37 use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig};
38
39 pub(crate) fn check(ra_fixture: &str) {
40 let config = DiagnosticsConfig::default();
41 check_diagnostics_with_config(config, ra_fixture)
42 }
43
44 #[test]
45 fn cfg_diagnostics() {
46 check(
47 r#"
48fn f() {
49 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
50
51 #[cfg(a)] fn f() {} // Item statement
52 //^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
53 #[cfg(a)] {} // Expression statement
54 //^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
55 #[cfg(a)] let x = 0; // let statement
56 //^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
57
58 abc(#[cfg(a)] 0);
59 //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
60 let x = Struct {
61 #[cfg(a)] f: 0,
62 //^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
63 };
64 match () {
65 () => (),
66 #[cfg(a)] () => (),
67 //^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
68 }
69
70 #[cfg(a)] 0 // Trailing expression of block
71 //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
72}
73 "#,
74 );
75 }
76
77 #[test]
78 fn inactive_item() {
79 // Additional tests in `cfg` crate. This only tests disabled cfgs.
80
81 check(
82 r#"
83 #[cfg(no)] pub fn f() {}
84 //^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
85
86 #[cfg(no)] #[cfg(no2)] mod m;
87 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no and no2 are disabled
88
89 #[cfg(all(not(a), b))] enum E {}
90 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: b is disabled
91
92 #[cfg(feature = "std")] use std;
93 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: feature = "std" is disabled
94"#,
95 );
96 }
97
98 /// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
99 #[test]
100 fn inactive_via_cfg_attr() {
101 cov_mark::check!(cfg_attr_active);
102 check(
103 r#"
104 #[cfg_attr(not(never), cfg(no))] fn f() {}
105 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
106
107 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
108
109 #[cfg_attr(never, cfg(no))] fn g() {}
110
111 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
113"#,
114 );
115 }
116}
diff --git a/crates/ide_diagnostics/src/handlers/incorrect_case.rs b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
new file mode 100644
index 000000000..68f25f284
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
@@ -0,0 +1,459 @@
1use hir::{db::AstDatabase, InFile};
2use ide_db::{assists::Assist, defs::NameClass};
3use syntax::AstNode;
4
5use crate::{
6 // references::rename::rename_with_semantics,
7 unresolved_fix,
8 Diagnostic,
9 DiagnosticsContext,
10 Severity,
11};
12
13// Diagnostic: incorrect-ident-case
14//
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].
16pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
17 Diagnostic::new(
18 "incorrect-ident-case",
19 format!(
20 "{} `{}` should have {} name, e.g. `{}`",
21 d.ident_type, d.ident_text, d.expected_case, d.suggested_text
22 ),
23 ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range,
24 )
25 .severity(Severity::WeakWarning)
26 .with_fixes(fixes(ctx, d))
27}
28
29fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
30 let root = ctx.sema.db.parse_or_expand(d.file)?;
31 let name_node = d.ident.to_node(&root);
32 let def = NameClass::classify(&ctx.sema, &name_node)?.defined(ctx.sema.db)?;
33
34 let name_node = InFile::new(d.file, name_node.syntax());
35 let frange = name_node.original_file_range(ctx.sema.db);
36
37 let label = format!("Rename to {}", d.suggested_text);
38 let mut res = unresolved_fix("change_case", &label, frange.range);
39 if ctx.resolve.should_resolve(&res.id) {
40 let source_change = def.rename(&ctx.sema, &d.suggested_text);
41 res.source_change = Some(source_change.ok().unwrap_or_default());
42 }
43
44 Some(vec![res])
45}
46
47#[cfg(test)]
48mod change_case {
49 use crate::tests::{check_diagnostics, check_fix};
50
51 #[test]
52 fn test_rename_incorrect_case() {
53 check_fix(
54 r#"
55pub struct test_struct$0 { one: i32 }
56
57pub fn some_fn(val: test_struct) -> test_struct {
58 test_struct { one: val.one + 1 }
59}
60"#,
61 r#"
62pub struct TestStruct { one: i32 }
63
64pub fn some_fn(val: TestStruct) -> TestStruct {
65 TestStruct { one: val.one + 1 }
66}
67"#,
68 );
69
70 check_fix(
71 r#"
72pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
73 NonSnakeCase
74}
75"#,
76 r#"
77pub fn some_fn(non_snake_case: u8) -> u8 {
78 non_snake_case
79}
80"#,
81 );
82
83 check_fix(
84 r#"
85pub fn SomeFn$0(val: u8) -> u8 {
86 if val != 0 { SomeFn(val - 1) } else { val }
87}
88"#,
89 r#"
90pub fn some_fn(val: u8) -> u8 {
91 if val != 0 { some_fn(val - 1) } else { val }
92}
93"#,
94 );
95
96 check_fix(
97 r#"
98fn some_fn() {
99 let whatAWeird_Formatting$0 = 10;
100 another_func(whatAWeird_Formatting);
101}
102"#,
103 r#"
104fn some_fn() {
105 let what_a_weird_formatting = 10;
106 another_func(what_a_weird_formatting);
107}
108"#,
109 );
110 }
111
112 #[test]
113 fn test_uppercase_const_no_diagnostics() {
114 check_diagnostics(
115 r#"
116fn foo() {
117 const ANOTHER_ITEM: &str = "some_item";
118}
119"#,
120 );
121 }
122
123 #[test]
124 fn test_rename_incorrect_case_struct_method() {
125 check_fix(
126 r#"
127pub struct TestStruct;
128
129impl TestStruct {
130 pub fn SomeFn$0() -> TestStruct {
131 TestStruct
132 }
133}
134"#,
135 r#"
136pub struct TestStruct;
137
138impl TestStruct {
139 pub fn some_fn() -> TestStruct {
140 TestStruct
141 }
142}
143"#,
144 );
145 }
146
147 #[test]
148 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
149 check_diagnostics(
150 r#"
151fn FOO() {}
152// ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo`
153"#,
154 );
155 check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#);
156 }
157
158 #[test]
159 fn incorrect_function_name() {
160 check_diagnostics(
161 r#"
162fn NonSnakeCaseName() {}
163// ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
164"#,
165 );
166 }
167
168 #[test]
169 fn incorrect_function_params() {
170 check_diagnostics(
171 r#"
172fn foo(SomeParam: u8) {}
173 // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param`
174
175fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
176 // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
177"#,
178 );
179 }
180
181 #[test]
182 fn incorrect_variable_names() {
183 check_diagnostics(
184 r#"
185fn foo() {
186 let SOME_VALUE = 10;
187 // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
188 let AnotherValue = 20;
189 // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value`
190}
191"#,
192 );
193 }
194
195 #[test]
196 fn incorrect_struct_names() {
197 check_diagnostics(
198 r#"
199struct non_camel_case_name {}
200 // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
201
202struct SCREAMING_CASE {}
203 // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
204"#,
205 );
206 }
207
208 #[test]
209 fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
210 check_diagnostics(
211 r#"
212struct AABB {}
213"#,
214 );
215 }
216
217 #[test]
218 fn incorrect_struct_field() {
219 check_diagnostics(
220 r#"
221struct SomeStruct { SomeField: u8 }
222 // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field`
223"#,
224 );
225 }
226
227 #[test]
228 fn incorrect_enum_names() {
229 check_diagnostics(
230 r#"
231enum some_enum { Val(u8) }
232 // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
233
234enum SOME_ENUM {}
235 // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
236"#,
237 );
238 }
239
240 #[test]
241 fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
242 check_diagnostics(
243 r#"
244enum AABB {}
245"#,
246 );
247 }
248
249 #[test]
250 fn incorrect_enum_variant_name() {
251 check_diagnostics(
252 r#"
253enum SomeEnum { SOME_VARIANT(u8) }
254 // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
255"#,
256 );
257 }
258
259 #[test]
260 fn incorrect_const_name() {
261 check_diagnostics(
262 r#"
263const some_weird_const: u8 = 10;
264 // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
265"#,
266 );
267 }
268
269 #[test]
270 fn incorrect_static_name() {
271 check_diagnostics(
272 r#"
273static some_weird_const: u8 = 10;
274 // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
275"#,
276 );
277 }
278
279 #[test]
280 fn fn_inside_impl_struct() {
281 check_diagnostics(
282 r#"
283struct someStruct;
284 // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
285
286impl someStruct {
287 fn SomeFunc(&self) {
288 // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func`
289 let WHY_VAR_IS_CAPS = 10;
290 // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
291 }
292}
293"#,
294 );
295 }
296
297 #[test]
298 fn no_diagnostic_for_enum_varinats() {
299 check_diagnostics(
300 r#"
301enum Option { Some, None }
302
303fn main() {
304 match Option::None {
305 None => (),
306 Some => (),
307 }
308}
309"#,
310 );
311 }
312
313 #[test]
314 fn non_let_bind() {
315 check_diagnostics(
316 r#"
317enum Option { Some, None }
318
319fn main() {
320 match Option::None {
321 SOME_VAR @ None => (),
322 // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
323 Some => (),
324 }
325}
326"#,
327 );
328 }
329
330 #[test]
331 fn allow_attributes_crate_attr() {
332 check_diagnostics(
333 r#"
334#![allow(non_snake_case)]
335
336mod F {
337 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
338}
339 "#,
340 );
341 }
342
343 #[test]
344 fn complex_ignore() {
345 // FIXME: this should trigger errors for the second case.
346 check_diagnostics(
347 r#"
348trait T { fn a(); }
349struct U {}
350impl T for U {
351 fn a() {
352 #[allow(non_snake_case)]
353 trait __BitFlagsOk {
354 const HiImAlsoBad: u8 = 2;
355 fn Dirty(&self) -> bool { false }
356 }
357
358 trait __BitFlagsBad {
359 const HiImAlsoBad: u8 = 2;
360 fn Dirty(&self) -> bool { false }
361 }
362 }
363}
364"#,
365 );
366 }
367
368 #[test]
369 fn infinite_loop_inner_items() {
370 check_diagnostics(
371 r#"
372fn qualify() {
373 mod foo {
374 use super::*;
375 }
376}
377 "#,
378 )
379 }
380
381 #[test] // Issue #8809.
382 fn parenthesized_parameter() {
383 check_diagnostics(r#"fn f((O): _) {}"#)
384 }
385
386 #[test]
387 fn ignores_extern_items() {
388 cov_mark::check!(extern_func_incorrect_case_ignored);
389 cov_mark::check!(extern_static_incorrect_case_ignored);
390 check_diagnostics(
391 r#"
392extern {
393 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
394 pub static SomeStatic: u8 = 10;
395}
396 "#,
397 );
398 }
399
400 #[test]
401 fn bug_traits_arent_checked() {
402 // FIXME: Traits and functions in traits aren't currently checked by
403 // r-a, even though rustc will complain about them.
404 check_diagnostics(
405 r#"
406trait BAD_TRAIT {
407 fn BAD_FUNCTION();
408 fn BadFunction();
409}
410 "#,
411 );
412 }
413
414 #[test]
415 fn allow_attributes() {
416 check_diagnostics(
417 r#"
418#[allow(non_snake_case)]
419fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
420 // cov_flags generated output from elsewhere in this file
421 extern "C" {
422 #[no_mangle]
423 static lower_case: u8;
424 }
425
426 let OtherVar = SOME_VAR + 1;
427 OtherVar
428}
429
430#[allow(nonstandard_style)]
431mod CheckNonstandardStyle {
432 fn HiImABadFnName() {}
433}
434
435#[allow(bad_style)]
436mod CheckBadStyle {
437 fn HiImABadFnName() {}
438}
439
440mod F {
441 #![allow(non_snake_case)]
442 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
443}
444
445#[allow(non_snake_case, non_camel_case_types)]
446pub struct some_type {
447 SOME_FIELD: u8,
448 SomeField: u16,
449}
450
451#[allow(non_upper_case_globals)]
452pub const some_const: u8 = 10;
453
454#[allow(non_upper_case_globals)]
455pub static SomeStatic: u8 = 10;
456 "#,
457 );
458 }
459}
diff --git a/crates/ide_diagnostics/src/handlers/macro_error.rs b/crates/ide_diagnostics/src/handlers/macro_error.rs
new file mode 100644
index 000000000..356f089b2
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/macro_error.rs
@@ -0,0 +1,173 @@
1use crate::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: macro-error
4//
5// This diagnostic is shown for macro expansion errors.
6pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
7 Diagnostic::new(
8 "macro-error",
9 d.message.clone(),
10 ctx.sema.diagnostics_display_range(d.node.clone()).range,
11 )
12 .experimental()
13}
14
15#[cfg(test)]
16mod tests {
17 use crate::{
18 tests::{check_diagnostics, check_diagnostics_with_config},
19 DiagnosticsConfig,
20 };
21
22 #[test]
23 fn builtin_macro_fails_expansion() {
24 check_diagnostics(
25 r#"
26#[rustc_builtin_macro]
27macro_rules! include { () => {} }
28
29 include!("doesntexist");
30//^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `doesntexist`
31 "#,
32 );
33 }
34
35 #[test]
36 fn include_macro_should_allow_empty_content() {
37 let mut config = DiagnosticsConfig::default();
38
39 // FIXME: This is a false-positive, the file is actually linked in via
40 // `include!` macro
41 config.disabled.insert("unlinked-file".to_string());
42
43 check_diagnostics_with_config(
44 config,
45 r#"
46//- /lib.rs
47#[rustc_builtin_macro]
48macro_rules! include { () => {} }
49
50include!("foo/bar.rs");
51//- /foo/bar.rs
52// empty
53"#,
54 );
55 }
56
57 #[test]
58 fn good_out_dir_diagnostic() {
59 check_diagnostics(
60 r#"
61#[rustc_builtin_macro]
62macro_rules! include { () => {} }
63#[rustc_builtin_macro]
64macro_rules! env { () => {} }
65#[rustc_builtin_macro]
66macro_rules! concat { () => {} }
67
68 include!(concat!(env!("OUT_DIR"), "/out.rs"));
69//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, enable "run build scripts" to fix
70"#,
71 );
72 }
73
74 #[test]
75 fn register_attr_and_tool() {
76 cov_mark::check!(register_attr);
77 cov_mark::check!(register_tool);
78 check_diagnostics(
79 r#"
80#![register_tool(tool)]
81#![register_attr(attr)]
82
83#[tool::path]
84#[attr]
85struct S;
86"#,
87 );
88 // NB: we don't currently emit diagnostics here
89 }
90
91 #[test]
92 fn macro_diag_builtin() {
93 check_diagnostics(
94 r#"
95#[rustc_builtin_macro]
96macro_rules! env {}
97
98#[rustc_builtin_macro]
99macro_rules! include {}
100
101#[rustc_builtin_macro]
102macro_rules! compile_error {}
103
104#[rustc_builtin_macro]
105macro_rules! format_args { () => {} }
106
107fn main() {
108 // Test a handful of built-in (eager) macros:
109
110 include!(invalid);
111 //^^^^^^^^^^^^^^^^^ error: could not convert tokens
112 include!("does not exist");
113 //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
114
115 env!(invalid);
116 //^^^^^^^^^^^^^ error: could not convert tokens
117
118 env!("OUT_DIR");
119 //^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, enable "run build scripts" to fix
120
121 compile_error!("compile_error works");
122 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: compile_error works
123
124 // Lazy:
125
126 format_args!();
127 //^^^^^^^^^^^^^^ error: no rule matches input tokens
128}
129"#,
130 );
131 }
132
133 #[test]
134 fn macro_rules_diag() {
135 check_diagnostics(
136 r#"
137macro_rules! m {
138 () => {};
139}
140fn f() {
141 m!();
142
143 m!(hi);
144 //^^^^^^ error: leftover tokens
145}
146 "#,
147 );
148 }
149 #[test]
150 fn dollar_crate_in_builtin_macro() {
151 check_diagnostics(
152 r#"
153#[macro_export]
154#[rustc_builtin_macro]
155macro_rules! format_args {}
156
157#[macro_export]
158macro_rules! arg { () => {} }
159
160#[macro_export]
161macro_rules! outer {
162 () => {
163 $crate::format_args!( "", $crate::arg!(1) )
164 };
165}
166
167fn f() {
168 outer!();
169} //^^^^^^^^ error: leftover tokens
170"#,
171 )
172 }
173}
diff --git a/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs
new file mode 100644
index 000000000..a9b6d3870
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs
@@ -0,0 +1,272 @@
1use crate::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: mismatched-arg-count
4//
5// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
6pub(crate) fn mismatched_arg_count(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::MismatchedArgCount,
9) -> Diagnostic {
10 let s = if d.expected == 1 { "" } else { "s" };
11 let message = format!("expected {} argument{}, found {}", d.expected, s, d.found);
12 Diagnostic::new(
13 "mismatched-arg-count",
14 message,
15 ctx.sema.diagnostics_display_range(d.call_expr.clone().map(|it| it.into())).range,
16 )
17}
18
19#[cfg(test)]
20mod tests {
21 use crate::tests::check_diagnostics;
22
23 #[test]
24 fn simple_free_fn_zero() {
25 check_diagnostics(
26 r#"
27fn zero() {}
28fn f() { zero(1); }
29 //^^^^^^^ error: expected 0 arguments, found 1
30"#,
31 );
32
33 check_diagnostics(
34 r#"
35fn zero() {}
36fn f() { zero(); }
37"#,
38 );
39 }
40
41 #[test]
42 fn simple_free_fn_one() {
43 check_diagnostics(
44 r#"
45fn one(arg: u8) {}
46fn f() { one(); }
47 //^^^^^ error: expected 1 argument, found 0
48"#,
49 );
50
51 check_diagnostics(
52 r#"
53fn one(arg: u8) {}
54fn f() { one(1); }
55"#,
56 );
57 }
58
59 #[test]
60 fn method_as_fn() {
61 check_diagnostics(
62 r#"
63struct S;
64impl S { fn method(&self) {} }
65
66fn f() {
67 S::method();
68} //^^^^^^^^^^^ error: expected 1 argument, found 0
69"#,
70 );
71
72 check_diagnostics(
73 r#"
74struct S;
75impl S { fn method(&self) {} }
76
77fn f() {
78 S::method(&S);
79 S.method();
80}
81"#,
82 );
83 }
84
85 #[test]
86 fn method_with_arg() {
87 check_diagnostics(
88 r#"
89struct S;
90impl S { fn method(&self, arg: u8) {} }
91
92 fn f() {
93 S.method();
94 } //^^^^^^^^^^ error: expected 1 argument, found 0
95 "#,
96 );
97
98 check_diagnostics(
99 r#"
100struct S;
101impl S { fn method(&self, arg: u8) {} }
102
103fn f() {
104 S::method(&S, 0);
105 S.method(1);
106}
107"#,
108 );
109 }
110
111 #[test]
112 fn method_unknown_receiver() {
113 // note: this is incorrect code, so there might be errors on this in the
114 // future, but we shouldn't emit an argument count diagnostic here
115 check_diagnostics(
116 r#"
117trait Foo { fn method(&self, arg: usize) {} }
118
119fn f() {
120 let x;
121 x.method();
122}
123"#,
124 );
125 }
126
127 #[test]
128 fn tuple_struct() {
129 check_diagnostics(
130 r#"
131struct Tup(u8, u16);
132fn f() {
133 Tup(0);
134} //^^^^^^ error: expected 2 arguments, found 1
135"#,
136 )
137 }
138
139 #[test]
140 fn enum_variant() {
141 check_diagnostics(
142 r#"
143enum En { Variant(u8, u16), }
144fn f() {
145 En::Variant(0);
146} //^^^^^^^^^^^^^^ error: expected 2 arguments, found 1
147"#,
148 )
149 }
150
151 #[test]
152 fn enum_variant_type_macro() {
153 check_diagnostics(
154 r#"
155macro_rules! Type {
156 () => { u32 };
157}
158enum Foo {
159 Bar(Type![])
160}
161impl Foo {
162 fn new() {
163 Foo::Bar(0);
164 Foo::Bar(0, 1);
165 //^^^^^^^^^^^^^^ error: expected 1 argument, found 2
166 Foo::Bar();
167 //^^^^^^^^^^ error: expected 1 argument, found 0
168 }
169}
170 "#,
171 );
172 }
173
174 #[test]
175 fn varargs() {
176 check_diagnostics(
177 r#"
178extern "C" {
179 fn fixed(fixed: u8);
180 fn varargs(fixed: u8, ...);
181 fn varargs2(...);
182}
183
184fn f() {
185 unsafe {
186 fixed(0);
187 fixed(0, 1);
188 //^^^^^^^^^^^ error: expected 1 argument, found 2
189 varargs(0);
190 varargs(0, 1);
191 varargs2();
192 varargs2(0);
193 varargs2(0, 1);
194 }
195}
196 "#,
197 )
198 }
199
200 #[test]
201 fn arg_count_lambda() {
202 check_diagnostics(
203 r#"
204fn main() {
205 let f = |()| ();
206 f();
207 //^^^ error: expected 1 argument, found 0
208 f(());
209 f((), ());
210 //^^^^^^^^^ error: expected 1 argument, found 2
211}
212"#,
213 )
214 }
215
216 #[test]
217 fn cfgd_out_call_arguments() {
218 check_diagnostics(
219 r#"
220struct C(#[cfg(FALSE)] ());
221impl C {
222 fn new() -> Self {
223 Self(
224 #[cfg(FALSE)]
225 (),
226 )
227 }
228
229 fn method(&self) {}
230}
231
232fn main() {
233 C::new().method(#[cfg(FALSE)] 0);
234}
235 "#,
236 );
237 }
238
239 #[test]
240 fn cfgd_out_fn_params() {
241 check_diagnostics(
242 r#"
243fn foo(#[cfg(NEVER)] x: ()) {}
244
245struct S;
246
247impl S {
248 fn method(#[cfg(NEVER)] self) {}
249 fn method2(#[cfg(NEVER)] self, arg: u8) {}
250 fn method3(self, #[cfg(NEVER)] arg: u8) {}
251}
252
253extern "C" {
254 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
255 fn varargs(#[cfg(not(NEVER))] ...);
256}
257
258fn main() {
259 foo();
260 S::method();
261 S::method2(0);
262 S::method3(S);
263 S.method3();
264 unsafe {
265 fixed(0);
266 varargs(1, 2, 3);
267 }
268}
269 "#,
270 )
271 }
272}
diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs
new file mode 100644
index 000000000..bc56e0342
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs
@@ -0,0 +1,355 @@
1use either::Either;
2use hir::{db::AstDatabase, InFile};
3use ide_db::{assists::Assist, source_change::SourceChange};
4use stdx::format_to;
5use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
6use text_edit::TextEdit;
7
8use crate::{fix, Diagnostic, DiagnosticsContext};
9
10// Diagnostic: missing-fields
11//
12// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
13//
14// Example:
15//
16// ```rust
17// struct A { a: u8, b: u8 }
18//
19// let a = A { a: 10 };
20// ```
21pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
22 let mut message = String::from("missing structure fields:\n");
23 for field in &d.missed_fields {
24 format_to!(message, "- {}\n", field);
25 }
26
27 let ptr = InFile::new(
28 d.file,
29 d.field_list_parent_path
30 .clone()
31 .map(SyntaxNodePtr::from)
32 .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
33 );
34
35 Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range)
36 .with_fixes(fixes(ctx, d))
37}
38
39fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
40 // Note that although we could add a diagnostics to
41 // fill the missing tuple field, e.g :
42 // `struct A(usize);`
43 // `let a = A { 0: () }`
44 // but it is uncommon usage and it should not be encouraged.
45 if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
46 return None;
47 }
48
49 let root = ctx.sema.db.parse_or_expand(d.file)?;
50 let field_list_parent = match &d.field_list_parent {
51 Either::Left(record_expr) => record_expr.to_node(&root),
52 // FIXE: patterns should be fixable as well.
53 Either::Right(_) => return None,
54 };
55 let old_field_list = field_list_parent.record_expr_field_list()?;
56 let new_field_list = old_field_list.clone_for_update();
57 for f in d.missed_fields.iter() {
58 let field =
59 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
60 .clone_for_update();
61 new_field_list.add_field(field);
62 }
63
64 let edit = {
65 let mut builder = TextEdit::builder();
66 algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder);
67 builder.finish()
68 };
69 Some(vec![fix(
70 "fill_missing_fields",
71 "Fill struct fields",
72 SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit),
73 ctx.sema.original_range(field_list_parent.syntax()).range,
74 )])
75}
76
77#[cfg(test)]
78mod tests {
79 use crate::tests::{check_diagnostics, check_fix};
80
81 #[test]
82 fn missing_record_pat_field_diagnostic() {
83 check_diagnostics(
84 r#"
85struct S { foo: i32, bar: () }
86fn baz(s: S) {
87 let S { foo: _ } = s;
88 //^ error: missing structure fields:
89 //| - bar
90}
91"#,
92 );
93 }
94
95 #[test]
96 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
97 check_diagnostics(
98 r"
99struct S { foo: i32, bar: () }
100fn baz(s: S) -> i32 {
101 match s {
102 S { foo, .. } => foo,
103 }
104}
105",
106 )
107 }
108
109 #[test]
110 fn missing_record_pat_field_box() {
111 check_diagnostics(
112 r"
113struct S { s: Box<u32> }
114fn x(a: S) {
115 let S { box s } = a;
116}
117",
118 )
119 }
120
121 #[test]
122 fn missing_record_pat_field_ref() {
123 check_diagnostics(
124 r"
125struct S { s: u32 }
126fn x(a: S) {
127 let S { ref s } = a;
128}
129",
130 )
131 }
132
133 #[test]
134 fn range_mapping_out_of_macros() {
135 // FIXME: this is very wrong, but somewhat tricky to fix.
136 check_fix(
137 r#"
138fn some() {}
139fn items() {}
140fn here() {}
141
142macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
143
144fn main() {
145 let _x = id![Foo { a: $042 }];
146}
147
148pub struct Foo { pub a: i32, pub b: i32 }
149"#,
150 r#"
151fn some(, b: () ) {}
152fn items() {}
153fn here() {}
154
155macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
156
157fn main() {
158 let _x = id![Foo { a: 42 }];
159}
160
161pub struct Foo { pub a: i32, pub b: i32 }
162"#,
163 );
164 }
165
166 #[test]
167 fn test_fill_struct_fields_empty() {
168 check_fix(
169 r#"
170struct TestStruct { one: i32, two: i64 }
171
172fn test_fn() {
173 let s = TestStruct {$0};
174}
175"#,
176 r#"
177struct TestStruct { one: i32, two: i64 }
178
179fn test_fn() {
180 let s = TestStruct { one: (), two: () };
181}
182"#,
183 );
184 }
185
186 #[test]
187 fn test_fill_struct_fields_self() {
188 check_fix(
189 r#"
190struct TestStruct { one: i32 }
191
192impl TestStruct {
193 fn test_fn() { let s = Self {$0}; }
194}
195"#,
196 r#"
197struct TestStruct { one: i32 }
198
199impl TestStruct {
200 fn test_fn() { let s = Self { one: () }; }
201}
202"#,
203 );
204 }
205
206 #[test]
207 fn test_fill_struct_fields_enum() {
208 check_fix(
209 r#"
210enum Expr {
211 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
212}
213
214impl Expr {
215 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
216 Expr::Bin {$0 }
217 }
218}
219"#,
220 r#"
221enum Expr {
222 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
223}
224
225impl Expr {
226 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
227 Expr::Bin { lhs: (), rhs: () }
228 }
229}
230"#,
231 );
232 }
233
234 #[test]
235 fn test_fill_struct_fields_partial() {
236 check_fix(
237 r#"
238struct TestStruct { one: i32, two: i64 }
239
240fn test_fn() {
241 let s = TestStruct{ two: 2$0 };
242}
243"#,
244 r"
245struct TestStruct { one: i32, two: i64 }
246
247fn test_fn() {
248 let s = TestStruct{ two: 2, one: () };
249}
250",
251 );
252 }
253
254 #[test]
255 fn test_fill_struct_fields_raw_ident() {
256 check_fix(
257 r#"
258struct TestStruct { r#type: u8 }
259
260fn test_fn() {
261 TestStruct { $0 };
262}
263"#,
264 r"
265struct TestStruct { r#type: u8 }
266
267fn test_fn() {
268 TestStruct { r#type: () };
269}
270",
271 );
272 }
273
274 #[test]
275 fn test_fill_struct_fields_no_diagnostic() {
276 check_diagnostics(
277 r#"
278struct TestStruct { one: i32, two: i64 }
279
280fn test_fn() {
281 let one = 1;
282 let s = TestStruct{ one, two: 2 };
283}
284 "#,
285 );
286 }
287
288 #[test]
289 fn test_fill_struct_fields_no_diagnostic_on_spread() {
290 check_diagnostics(
291 r#"
292struct TestStruct { one: i32, two: i64 }
293
294fn test_fn() {
295 let one = 1;
296 let s = TestStruct{ ..a };
297}
298"#,
299 );
300 }
301
302 #[test]
303 fn test_fill_struct_fields_blank_line() {
304 check_fix(
305 r#"
306struct S { a: (), b: () }
307
308fn f() {
309 S {
310 $0
311 };
312}
313"#,
314 r#"
315struct S { a: (), b: () }
316
317fn f() {
318 S {
319 a: (),
320 b: (),
321 };
322}
323"#,
324 );
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 }
355}
diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
new file mode 100644
index 000000000..947b0f2e2
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
@@ -0,0 +1,929 @@
1use hir::InFile;
2
3use crate::{Diagnostic, DiagnosticsContext};
4
5// Diagnostic: missing-match-arm
6//
7// This diagnostic is triggered if `match` block is missing one or more match arms.
8pub(crate) fn missing_match_arms(
9 ctx: &DiagnosticsContext<'_>,
10 d: &hir::MissingMatchArms,
11) -> Diagnostic {
12 Diagnostic::new(
13 "missing-match-arm",
14 "missing match arm",
15 ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range,
16 )
17}
18
19#[cfg(test)]
20mod tests {
21 use crate::tests::check_diagnostics;
22
23 fn check_diagnostics_no_bails(ra_fixture: &str) {
24 cov_mark::check_count!(validate_match_bailed_out, 0);
25 crate::tests::check_diagnostics(ra_fixture)
26 }
27
28 #[test]
29 fn empty_tuple() {
30 check_diagnostics_no_bails(
31 r#"
32fn main() {
33 match () { }
34 //^^ error: missing match arm
35 match (()) { }
36 //^^^^ error: missing match arm
37
38 match () { _ => (), }
39 match () { () => (), }
40 match (()) { (()) => (), }
41}
42"#,
43 );
44 }
45
46 #[test]
47 fn tuple_of_two_empty_tuple() {
48 check_diagnostics_no_bails(
49 r#"
50fn main() {
51 match ((), ()) { }
52 //^^^^^^^^ error: missing match arm
53
54 match ((), ()) { ((), ()) => (), }
55}
56"#,
57 );
58 }
59
60 #[test]
61 fn boolean() {
62 check_diagnostics_no_bails(
63 r#"
64fn test_main() {
65 match false { }
66 //^^^^^ error: missing match arm
67 match false { true => (), }
68 //^^^^^ error: missing match arm
69 match (false, true) {}
70 //^^^^^^^^^^^^^ error: missing match arm
71 match (false, true) { (true, true) => (), }
72 //^^^^^^^^^^^^^ error: missing match arm
73 match (false, true) {
74 //^^^^^^^^^^^^^ error: missing match arm
75 (false, true) => (),
76 (false, false) => (),
77 (true, false) => (),
78 }
79 match (false, true) { (true, _x) => (), }
80 //^^^^^^^^^^^^^ error: missing match arm
81
82 match false { true => (), false => (), }
83 match (false, true) {
84 (false, _) => (),
85 (true, false) => (),
86 (_, true) => (),
87 }
88 match (false, true) {
89 (true, true) => (),
90 (true, false) => (),
91 (false, true) => (),
92 (false, false) => (),
93 }
94 match (false, true) {
95 (true, _x) => (),
96 (false, true) => (),
97 (false, false) => (),
98 }
99 match (false, true, false) {
100 (false, ..) => (),
101 (true, ..) => (),
102 }
103 match (false, true, false) {
104 (.., false) => (),
105 (.., true) => (),
106 }
107 match (false, true, false) { (..) => (), }
108}
109"#,
110 );
111 }
112
113 #[test]
114 fn tuple_of_tuple_and_bools() {
115 check_diagnostics_no_bails(
116 r#"
117fn main() {
118 match (false, ((), false)) {}
119 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
120 match (false, ((), false)) { (true, ((), true)) => (), }
121 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
122 match (false, ((), false)) { (true, _) => (), }
123 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
124
125 match (false, ((), false)) {
126 (true, ((), true)) => (),
127 (true, ((), false)) => (),
128 (false, ((), true)) => (),
129 (false, ((), false)) => (),
130 }
131 match (false, ((), false)) {
132 (true, ((), true)) => (),
133 (true, ((), false)) => (),
134 (false, _) => (),
135 }
136}
137"#,
138 );
139 }
140
141 #[test]
142 fn enums() {
143 check_diagnostics_no_bails(
144 r#"
145enum Either { A, B, }
146
147fn main() {
148 match Either::A { }
149 //^^^^^^^^^ error: missing match arm
150 match Either::B { Either::A => (), }
151 //^^^^^^^^^ error: missing match arm
152
153 match &Either::B {
154 //^^^^^^^^^^ error: missing match arm
155 Either::A => (),
156 }
157
158 match Either::B {
159 Either::A => (), Either::B => (),
160 }
161 match &Either::B {
162 Either::A => (), Either::B => (),
163 }
164}
165"#,
166 );
167 }
168
169 #[test]
170 fn enum_containing_bool() {
171 check_diagnostics_no_bails(
172 r#"
173enum Either { A(bool), B }
174
175fn main() {
176 match Either::B { }
177 //^^^^^^^^^ error: missing match arm
178 match Either::B {
179 //^^^^^^^^^ error: missing match arm
180 Either::A(true) => (), Either::B => ()
181 }
182
183 match Either::B {
184 Either::A(true) => (),
185 Either::A(false) => (),
186 Either::B => (),
187 }
188 match Either::B {
189 Either::B => (),
190 _ => (),
191 }
192 match Either::B {
193 Either::A(_) => (),
194 Either::B => (),
195 }
196
197}
198 "#,
199 );
200 }
201
202 #[test]
203 fn enum_different_sizes() {
204 check_diagnostics_no_bails(
205 r#"
206enum Either { A(bool), B(bool, bool) }
207
208fn main() {
209 match Either::A(false) {
210 //^^^^^^^^^^^^^^^^ error: missing match arm
211 Either::A(_) => (),
212 Either::B(false, _) => (),
213 }
214
215 match Either::A(false) {
216 Either::A(_) => (),
217 Either::B(true, _) => (),
218 Either::B(false, _) => (),
219 }
220 match Either::A(false) {
221 Either::A(true) | Either::A(false) => (),
222 Either::B(true, _) => (),
223 Either::B(false, _) => (),
224 }
225}
226"#,
227 );
228 }
229
230 #[test]
231 fn tuple_of_enum_no_diagnostic() {
232 check_diagnostics_no_bails(
233 r#"
234enum Either { A(bool), B(bool, bool) }
235enum Either2 { C, D }
236
237fn main() {
238 match (Either::A(false), Either2::C) {
239 (Either::A(true), _) | (Either::A(false), _) => (),
240 (Either::B(true, _), Either2::C) => (),
241 (Either::B(false, _), Either2::C) => (),
242 (Either::B(_, _), Either2::D) => (),
243 }
244}
245"#,
246 );
247 }
248
249 #[test]
250 fn or_pattern_no_diagnostic() {
251 check_diagnostics_no_bails(
252 r#"
253enum Either {A, B}
254
255fn main() {
256 match (Either::A, Either::B) {
257 (Either::A | Either::B, _) => (),
258 }
259}"#,
260 )
261 }
262
263 #[test]
264 fn mismatched_types() {
265 cov_mark::check_count!(validate_match_bailed_out, 4);
266 // Match statements with arms that don't match the
267 // expression pattern do not fire this diagnostic.
268 check_diagnostics(
269 r#"
270enum Either { A, B }
271enum Either2 { C, D }
272
273fn main() {
274 match Either::A {
275 Either2::C => (),
276 Either2::D => (),
277 }
278 match (true, false) {
279 (true, false, true) => (),
280 (true) => (),
281 }
282 match (true, false) { (true,) => {} }
283 match (0) { () => () }
284 match Unresolved::Bar { Unresolved::Baz => () }
285}
286 "#,
287 );
288 }
289
290 #[test]
291 fn mismatched_types_in_or_patterns() {
292 cov_mark::check_count!(validate_match_bailed_out, 2);
293 check_diagnostics(
294 r#"
295fn main() {
296 match false { true | () => {} }
297 match (false,) { (true | (),) => {} }
298}
299"#,
300 );
301 }
302
303 #[test]
304 fn malformed_match_arm_tuple_enum_missing_pattern() {
305 // We are testing to be sure we don't panic here when the match
306 // arm `Either::B` is missing its pattern.
307 check_diagnostics_no_bails(
308 r#"
309enum Either { A, B(u32) }
310
311fn main() {
312 match Either::A {
313 Either::A => (),
314 Either::B() => (),
315 }
316}
317"#,
318 );
319 }
320
321 #[test]
322 fn malformed_match_arm_extra_fields() {
323 cov_mark::check_count!(validate_match_bailed_out, 2);
324 check_diagnostics(
325 r#"
326enum A { B(isize, isize), C }
327fn main() {
328 match A::B(1, 2) {
329 A::B(_, _, _) => (),
330 }
331 match A::B(1, 2) {
332 A::C(_) => (),
333 }
334}
335"#,
336 );
337 }
338
339 #[test]
340 fn expr_diverges() {
341 cov_mark::check_count!(validate_match_bailed_out, 2);
342 check_diagnostics(
343 r#"
344enum Either { A, B }
345
346fn main() {
347 match loop {} {
348 Either::A => (),
349 Either::B => (),
350 }
351 match loop {} {
352 Either::A => (),
353 }
354 match loop { break Foo::A } {
355 //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm
356 Either::A => (),
357 }
358 match loop { break Foo::A } {
359 Either::A => (),
360 Either::B => (),
361 }
362}
363"#,
364 );
365 }
366
367 #[test]
368 fn expr_partially_diverges() {
369 check_diagnostics_no_bails(
370 r#"
371enum Either<T> { A(T), B }
372
373fn foo() -> Either<!> { Either::B }
374fn main() -> u32 {
375 match foo() {
376 Either::A(val) => val,
377 Either::B => 0,
378 }
379}
380"#,
381 );
382 }
383
384 #[test]
385 fn enum_record() {
386 check_diagnostics_no_bails(
387 r#"
388enum Either { A { foo: bool }, B }
389
390fn main() {
391 let a = Either::A { foo: true };
392 match a { }
393 //^ error: missing match arm
394 match a { Either::A { foo: true } => () }
395 //^ error: missing match arm
396 match a {
397 Either::A { } => (),
398 //^^^^^^^^^ error: missing structure fields:
399 // | - foo
400 Either::B => (),
401 }
402 match a {
403 //^ error: missing match arm
404 Either::A { } => (),
405 } //^^^^^^^^^ error: missing structure fields:
406 // | - foo
407
408 match a {
409 Either::A { foo: true } => (),
410 Either::A { foo: false } => (),
411 Either::B => (),
412 }
413 match a {
414 Either::A { foo: _ } => (),
415 Either::B => (),
416 }
417}
418"#,
419 );
420 }
421
422 #[test]
423 fn enum_record_fields_out_of_order() {
424 check_diagnostics_no_bails(
425 r#"
426enum Either {
427 A { foo: bool, bar: () },
428 B,
429}
430
431fn main() {
432 let a = Either::A { foo: true, bar: () };
433 match a {
434 //^ error: missing match arm
435 Either::A { bar: (), foo: false } => (),
436 Either::A { foo: true, bar: () } => (),
437 }
438
439 match a {
440 Either::A { bar: (), foo: false } => (),
441 Either::A { foo: true, bar: () } => (),
442 Either::B => (),
443 }
444}
445"#,
446 );
447 }
448
449 #[test]
450 fn enum_record_ellipsis() {
451 check_diagnostics_no_bails(
452 r#"
453enum Either {
454 A { foo: bool, bar: bool },
455 B,
456}
457
458fn main() {
459 let a = Either::B;
460 match a {
461 //^ error: missing match arm
462 Either::A { foo: true, .. } => (),
463 Either::B => (),
464 }
465 match a {
466 //^ error: missing match arm
467 Either::A { .. } => (),
468 }
469
470 match a {
471 Either::A { foo: true, .. } => (),
472 Either::A { foo: false, .. } => (),
473 Either::B => (),
474 }
475
476 match a {
477 Either::A { .. } => (),
478 Either::B => (),
479 }
480}
481"#,
482 );
483 }
484
485 #[test]
486 fn enum_tuple_partial_ellipsis() {
487 check_diagnostics_no_bails(
488 r#"
489enum Either {
490 A(bool, bool, bool, bool),
491 B,
492}
493
494fn main() {
495 match Either::B {
496 //^^^^^^^^^ error: missing match arm
497 Either::A(true, .., true) => (),
498 Either::A(true, .., false) => (),
499 Either::A(false, .., false) => (),
500 Either::B => (),
501 }
502 match Either::B {
503 //^^^^^^^^^ error: missing match arm
504 Either::A(true, .., true) => (),
505 Either::A(true, .., false) => (),
506 Either::A(.., true) => (),
507 Either::B => (),
508 }
509
510 match Either::B {
511 Either::A(true, .., true) => (),
512 Either::A(true, .., false) => (),
513 Either::A(false, .., true) => (),
514 Either::A(false, .., false) => (),
515 Either::B => (),
516 }
517 match Either::B {
518 Either::A(true, .., true) => (),
519 Either::A(true, .., false) => (),
520 Either::A(.., true) => (),
521 Either::A(.., false) => (),
522 Either::B => (),
523 }
524}
525"#,
526 );
527 }
528
529 #[test]
530 fn never() {
531 check_diagnostics_no_bails(
532 r#"
533enum Never {}
534
535fn enum_(never: Never) {
536 match never {}
537}
538fn enum_ref(never: &Never) {
539 match never {}
540 //^^^^^ error: missing match arm
541}
542fn bang(never: !) {
543 match never {}
544}
545"#,
546 );
547 }
548
549 #[test]
550 fn unknown_type() {
551 cov_mark::check_count!(validate_match_bailed_out, 1);
552
553 check_diagnostics(
554 r#"
555enum Option<T> { Some(T), None }
556
557fn main() {
558 // `Never` is deliberately not defined so that it's an uninferred type.
559 match Option::<Never>::None {
560 None => (),
561 Some(never) => match never {},
562 }
563 match Option::<Never>::None {
564 //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm
565 Option::Some(_never) => {},
566 }
567}
568"#,
569 );
570 }
571
572 #[test]
573 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
574 check_diagnostics_no_bails(
575 r#"
576fn main() {
577 match (false, true, false) {
578 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
579 (false, ..) => (),
580 }
581}"#,
582 );
583 }
584
585 #[test]
586 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
587 check_diagnostics_no_bails(
588 r#"
589fn main() {
590 match (false, true, false) {
591 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
592 (.., false) => (),
593 }
594}"#,
595 );
596 }
597
598 #[test]
599 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
600 check_diagnostics_no_bails(
601 r#"
602fn main() {
603 match (false, true, false) {
604 //^^^^^^^^^^^^^^^^^^^^ error: missing match arm
605 (true, .., false) => (),
606 }
607}"#,
608 );
609 }
610
611 #[test]
612 fn record_struct() {
613 check_diagnostics_no_bails(
614 r#"struct Foo { a: bool }
615fn main(f: Foo) {
616 match f {}
617 //^ error: missing match arm
618 match f { Foo { a: true } => () }
619 //^ error: missing match arm
620 match &f { Foo { a: true } => () }
621 //^^ error: missing match arm
622 match f { Foo { a: _ } => () }
623 match f {
624 Foo { a: true } => (),
625 Foo { a: false } => (),
626 }
627 match &f {
628 Foo { a: true } => (),
629 Foo { a: false } => (),
630 }
631}
632"#,
633 );
634 }
635
636 #[test]
637 fn tuple_struct() {
638 check_diagnostics_no_bails(
639 r#"struct Foo(bool);
640fn main(f: Foo) {
641 match f {}
642 //^ error: missing match arm
643 match f { Foo(true) => () }
644 //^ error: missing match arm
645 match f {
646 Foo(true) => (),
647 Foo(false) => (),
648 }
649}
650"#,
651 );
652 }
653
654 #[test]
655 fn unit_struct() {
656 check_diagnostics_no_bails(
657 r#"struct Foo;
658fn main(f: Foo) {
659 match f {}
660 //^ error: missing match arm
661 match f { Foo => () }
662}
663"#,
664 );
665 }
666
667 #[test]
668 fn record_struct_ellipsis() {
669 check_diagnostics_no_bails(
670 r#"struct Foo { foo: bool, bar: bool }
671fn main(f: Foo) {
672 match f { Foo { foo: true, .. } => () }
673 //^ error: missing match arm
674 match f {
675 //^ error: missing match arm
676 Foo { foo: true, .. } => (),
677 Foo { bar: false, .. } => ()
678 }
679 match f { Foo { .. } => () }
680 match f {
681 Foo { foo: true, .. } => (),
682 Foo { foo: false, .. } => ()
683 }
684}
685"#,
686 );
687 }
688
689 #[test]
690 fn internal_or() {
691 check_diagnostics_no_bails(
692 r#"
693fn main() {
694 enum Either { A(bool), B }
695 match Either::B {
696 //^^^^^^^^^ error: missing match arm
697 Either::A(true | false) => (),
698 }
699}
700"#,
701 );
702 }
703
704 #[test]
705 fn no_panic_at_unimplemented_subpattern_type() {
706 cov_mark::check_count!(validate_match_bailed_out, 1);
707
708 check_diagnostics(
709 r#"
710struct S { a: char}
711fn main(v: S) {
712 match v { S{ a } => {} }
713 match v { S{ a: _x } => {} }
714 match v { S{ a: 'a' } => {} }
715 match v { S{..} => {} }
716 match v { _ => {} }
717 match v { }
718 //^ error: missing match arm
719}
720"#,
721 );
722 }
723
724 #[test]
725 fn binding() {
726 check_diagnostics_no_bails(
727 r#"
728fn main() {
729 match true {
730 _x @ true => {}
731 false => {}
732 }
733 match true { _x @ true => {} }
734 //^^^^ error: missing match arm
735}
736"#,
737 );
738 }
739
740 #[test]
741 fn binding_ref_has_correct_type() {
742 cov_mark::check_count!(validate_match_bailed_out, 1);
743
744 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
745 // If that's not true match checking will panic with "incompatible constructors"
746 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
747 check_diagnostics(
748 r#"
749enum Foo { A }
750fn main() {
751 // FIXME: this should not bail out but current behavior is such as the old algorithm.
752 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
753 match Foo::A {
754 ref _x => {}
755 Foo::A => {}
756 }
757 match (true,) {
758 (ref _x,) => {}
759 (true,) => {}
760 }
761}
762"#,
763 );
764 }
765
766 #[test]
767 fn enum_non_exhaustive() {
768 check_diagnostics_no_bails(
769 r#"
770//- /lib.rs crate:lib
771#[non_exhaustive]
772pub enum E { A, B }
773fn _local() {
774 match E::A { _ => {} }
775 match E::A {
776 E::A => {}
777 E::B => {}
778 }
779 match E::A {
780 E::A | E::B => {}
781 }
782}
783
784//- /main.rs crate:main deps:lib
785use lib::E;
786fn main() {
787 match E::A { _ => {} }
788 match E::A {
789 //^^^^ error: missing match arm
790 E::A => {}
791 E::B => {}
792 }
793 match E::A {
794 //^^^^ error: missing match arm
795 E::A | E::B => {}
796 }
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn match_guard() {
804 check_diagnostics_no_bails(
805 r#"
806fn main() {
807 match true {
808 true if false => {}
809 true => {}
810 false => {}
811 }
812 match true {
813 //^^^^ error: missing match arm
814 true if false => {}
815 false => {}
816 }
817}
818"#,
819 );
820 }
821
822 #[test]
823 fn pattern_type_is_of_substitution() {
824 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
825 check_diagnostics_no_bails(
826 r#"
827struct Foo<T>(T);
828struct Bar;
829fn main() {
830 match Foo(Bar) {
831 _ | Foo(Bar) => {}
832 }
833}
834"#,
835 );
836 }
837
838 #[test]
839 fn record_struct_no_such_field() {
840 cov_mark::check_count!(validate_match_bailed_out, 1);
841
842 check_diagnostics(
843 r#"
844struct Foo { }
845fn main(f: Foo) {
846 match f { Foo { bar } => () }
847}
848"#,
849 );
850 }
851
852 #[test]
853 fn match_ergonomics_issue_9095() {
854 check_diagnostics_no_bails(
855 r#"
856enum Foo<T> { A(T) }
857fn main() {
858 match &Foo::A(true) {
859 _ => {}
860 Foo::A(_) => {}
861 }
862}
863"#,
864 );
865 }
866
867 mod false_negatives {
868 //! The implementation of match checking here is a work in progress. As we roll this out, we
869 //! prefer false negatives to false positives (ideally there would be no false positives). This
870 //! test module should document known false negatives. Eventually we will have a complete
871 //! implementation of match checking and this module will be empty.
872 //!
873 //! The reasons for documenting known false negatives:
874 //!
875 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
876 //! 2. It ensures the code doesn't panic when handling these cases.
877 use super::*;
878
879 #[test]
880 fn integers() {
881 cov_mark::check_count!(validate_match_bailed_out, 1);
882
883 // We don't currently check integer exhaustiveness.
884 check_diagnostics(
885 r#"
886fn main() {
887 match 5 {
888 10 => (),
889 11..20 => (),
890 }
891}
892"#,
893 );
894 }
895
896 #[test]
897 fn reference_patterns_at_top_level() {
898 cov_mark::check_count!(validate_match_bailed_out, 1);
899
900 check_diagnostics(
901 r#"
902fn main() {
903 match &false {
904 &true => {}
905 }
906}
907 "#,
908 );
909 }
910
911 #[test]
912 fn reference_patterns_in_fields() {
913 cov_mark::check_count!(validate_match_bailed_out, 2);
914
915 check_diagnostics(
916 r#"
917fn main() {
918 match (&false,) {
919 (true,) => {}
920 }
921 match (&false,) {
922 (&true,) => {}
923 }
924}
925 "#,
926 );
927 }
928 }
929}
diff --git a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
index 715a403b9..63de54570 100644
--- a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs
@@ -1,31 +1,49 @@
1use hir::{db::AstDatabase, diagnostics::MissingOkOrSomeInTailExpr, Semantics}; 1use hir::db::AstDatabase;
2use ide_assists::{Assist, AssistResolveStrategy}; 2use ide_db::{assists::Assist, source_change::SourceChange};
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::AstNode; 3use syntax::AstNode;
5use text_edit::TextEdit; 4use text_edit::TextEdit;
6 5
7use crate::diagnostics::{fix, DiagnosticWithFixes}; 6use crate::{fix, Diagnostic, DiagnosticsContext};
8 7
9impl DiagnosticWithFixes for MissingOkOrSomeInTailExpr { 8// Diagnostic: missing-ok-or-some-in-tail-expr
10 fn fixes( 9//
11 &self, 10// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
12 sema: &Semantics<RootDatabase>, 11// or if a block that should return `Option` returns a value not wrapped in `Some`.
13 _resolve: &AssistResolveStrategy, 12//
14 ) -> Option<Vec<Assist>> { 13// Example:
15 let root = sema.db.parse_or_expand(self.file)?; 14//
16 let tail_expr = self.expr.to_node(&root); 15// ```rust
17 let tail_expr_range = tail_expr.syntax().text_range(); 16// fn foo() -> Result<u8, ()> {
18 let replacement = format!("{}({})", self.required, tail_expr.syntax()); 17// 10
19 let edit = TextEdit::replace(tail_expr_range, replacement); 18// }
20 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); 19// ```
21 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; 20pub(crate) fn missing_ok_or_some_in_tail_expr(
22 Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)]) 21 ctx: &DiagnosticsContext<'_>,
23 } 22 d: &hir::MissingOkOrSomeInTailExpr,
23) -> Diagnostic {
24 Diagnostic::new(
25 "missing-ok-or-some-in-tail-expr",
26 format!("wrap return expression in {}", d.required),
27 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
28 )
29 .with_fixes(fixes(ctx, d))
30}
31
32fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Option<Vec<Assist>> {
33 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
34 let tail_expr = d.expr.value.to_node(&root);
35 let tail_expr_range = tail_expr.syntax().text_range();
36 let replacement = format!("{}({})", d.required, tail_expr.syntax());
37 let edit = TextEdit::replace(tail_expr_range, replacement);
38 let source_change =
39 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
40 let name = if d.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
41 Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)])
24} 42}
25 43
26#[cfg(test)] 44#[cfg(test)]
27mod tests { 45mod tests {
28 use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; 46 use crate::tests::{check_diagnostics, check_fix};
29 47
30 #[test] 48 #[test]
31 fn test_wrap_return_type_option() { 49 fn test_wrap_return_type_option() {
@@ -169,7 +187,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
169 187
170 #[test] 188 #[test]
171 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 189 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
172 check_no_diagnostics( 190 check_diagnostics(
173 r#" 191 r#"
174//- /main.rs crate:main deps:core 192//- /main.rs crate:main deps:core
175use core::result::Result::{self, Ok, Err}; 193use core::result::Result::{self, Ok, Err};
@@ -189,7 +207,7 @@ pub mod option {
189 207
190 #[test] 208 #[test]
191 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { 209 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
192 check_no_diagnostics( 210 check_diagnostics(
193 r#" 211 r#"
194//- /main.rs crate:main deps:core 212//- /main.rs crate:main deps:core
195use core::result::Result::{self, Ok, Err}; 213use core::result::Result::{self, Ok, Err};
diff --git a/crates/ide_diagnostics/src/handlers/missing_unsafe.rs b/crates/ide_diagnostics/src/handlers/missing_unsafe.rs
new file mode 100644
index 000000000..7acd9228a
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/missing_unsafe.rs
@@ -0,0 +1,101 @@
1use crate::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: missing-unsafe
4//
5// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
6pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
7 Diagnostic::new(
8 "missing-unsafe",
9 "this operation is unsafe and requires an unsafe function or block",
10 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
11 )
12}
13
14#[cfg(test)]
15mod tests {
16 use crate::tests::check_diagnostics;
17
18 #[test]
19 fn missing_unsafe_diagnostic_with_raw_ptr() {
20 check_diagnostics(
21 r#"
22fn main() {
23 let x = &5 as *const usize;
24 unsafe { let y = *x; }
25 let z = *x;
26} //^^ error: this operation is unsafe and requires an unsafe function or block
27"#,
28 )
29 }
30
31 #[test]
32 fn missing_unsafe_diagnostic_with_unsafe_call() {
33 check_diagnostics(
34 r#"
35struct HasUnsafe;
36
37impl HasUnsafe {
38 unsafe fn unsafe_fn(&self) {
39 let x = &5 as *const usize;
40 let y = *x;
41 }
42}
43
44unsafe fn unsafe_fn() {
45 let x = &5 as *const usize;
46 let y = *x;
47}
48
49fn main() {
50 unsafe_fn();
51 //^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
52 HasUnsafe.unsafe_fn();
53 //^^^^^^^^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
54 unsafe {
55 unsafe_fn();
56 HasUnsafe.unsafe_fn();
57 }
58}
59"#,
60 );
61 }
62
63 #[test]
64 fn missing_unsafe_diagnostic_with_static_mut() {
65 check_diagnostics(
66 r#"
67struct Ty {
68 a: u8,
69}
70
71static mut STATIC_MUT: Ty = Ty { a: 0 };
72
73fn main() {
74 let x = STATIC_MUT.a;
75 //^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
76 unsafe {
77 let x = STATIC_MUT.a;
78 }
79}
80"#,
81 );
82 }
83
84 #[test]
85 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
86 check_diagnostics(
87 r#"
88extern "rust-intrinsic" {
89 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
90 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
91}
92
93fn main() {
94 let _ = bitreverse(12);
95 let _ = floorf32(12.0);
96 //^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
97}
98"#,
99 );
100 }
101}
diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide_diagnostics/src/handlers/no_such_field.rs
index a5f457dce..92e8867f4 100644
--- a/crates/ide/src/diagnostics/fixes/create_field.rs
+++ b/crates/ide_diagnostics/src/handlers/no_such_field.rs
@@ -1,4 +1,4 @@
1use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics}; 1use hir::{db::AstDatabase, HasSource, HirDisplay, Semantics};
2use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; 2use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
3use syntax::{ 3use syntax::{
4 ast::{self, edit::IndentLevel, make}, 4 ast::{self, edit::IndentLevel, make},
@@ -6,23 +6,27 @@ 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, DiagnosticWithFixes}, 10
11 Assist, AssistResolveStrategy, 11// Diagnostic: no-such-field
12}; 12//
13impl DiagnosticWithFixes for NoSuchField { 13// This diagnostic is triggered if created structure does not have field provided in record.
14 fn fixes( 14pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
15 &self, 15 Diagnostic::new(
16 sema: &Semantics<RootDatabase>, 16 "no-such-field",
17 _resolve: &AssistResolveStrategy, 17 "no such field",
18 ) -> Option<Vec<Assist>> { 18 ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range,
19 let root = sema.db.parse_or_expand(self.file)?; 19 )
20 missing_record_expr_field_fixes( 20 .with_fixes(fixes(ctx, d))
21 &sema, 21}
22 self.file.original_file(sema.db), 22
23 &self.field.to_node(&root), 23fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
24 ) 24 let root = ctx.sema.db.parse_or_expand(d.field.file_id)?;
25 } 25 missing_record_expr_field_fixes(
26 &ctx.sema,
27 d.field.file_id.original_file(ctx.sema.db),
28 &d.field.value.to_node(&root),
29 )
26} 30}
27 31
28fn missing_record_expr_field_fixes( 32fn missing_record_expr_field_fixes(
@@ -105,7 +109,130 @@ fn missing_record_expr_field_fixes(
105 109
106#[cfg(test)] 110#[cfg(test)]
107mod tests { 111mod tests {
108 use crate::diagnostics::tests::check_fix; 112 use crate::tests::{check_diagnostics, check_fix};
113
114 #[test]
115 fn no_such_field_diagnostics() {
116 check_diagnostics(
117 r#"
118struct S { foo: i32, bar: () }
119impl S {
120 fn new() -> S {
121 S {
122 //^ 💡 error: missing structure fields:
123 //| - bar
124 foo: 92,
125 baz: 62,
126 //^^^^^^^ 💡 error: no such field
127 }
128 }
129}
130"#,
131 );
132 }
133 #[test]
134 fn no_such_field_with_feature_flag_diagnostics() {
135 check_diagnostics(
136 r#"
137//- /lib.rs crate:foo cfg:feature=foo
138struct MyStruct {
139 my_val: usize,
140 #[cfg(feature = "foo")]
141 bar: bool,
142}
143
144impl MyStruct {
145 #[cfg(feature = "foo")]
146 pub(crate) fn new(my_val: usize, bar: bool) -> Self {
147 Self { my_val, bar }
148 }
149 #[cfg(not(feature = "foo"))]
150 pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
151 Self { my_val }
152 }
153}
154"#,
155 );
156 }
157
158 #[test]
159 fn no_such_field_enum_with_feature_flag_diagnostics() {
160 check_diagnostics(
161 r#"
162//- /lib.rs crate:foo cfg:feature=foo
163enum Foo {
164 #[cfg(not(feature = "foo"))]
165 Buz,
166 #[cfg(feature = "foo")]
167 Bar,
168 Baz
169}
170
171fn test_fn(f: Foo) {
172 match f {
173 Foo::Bar => {},
174 Foo::Baz => {},
175 }
176}
177"#,
178 );
179 }
180
181 #[test]
182 fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
183 check_diagnostics(
184 r#"
185//- /lib.rs crate:foo cfg:feature=foo
186struct S {
187 #[cfg(feature = "foo")]
188 foo: u32,
189 #[cfg(not(feature = "foo"))]
190 bar: u32,
191}
192
193impl S {
194 #[cfg(feature = "foo")]
195 fn new(foo: u32) -> Self {
196 Self { foo }
197 }
198 #[cfg(not(feature = "foo"))]
199 fn new(bar: u32) -> Self {
200 Self { bar }
201 }
202 fn new2(bar: u32) -> Self {
203 #[cfg(feature = "foo")]
204 { Self { foo: bar } }
205 #[cfg(not(feature = "foo"))]
206 { Self { bar } }
207 }
208 fn new2(val: u32) -> Self {
209 Self {
210 #[cfg(feature = "foo")]
211 foo: val,
212 #[cfg(not(feature = "foo"))]
213 bar: val,
214 }
215 }
216}
217"#,
218 );
219 }
220
221 #[test]
222 fn no_such_field_with_type_macro() {
223 check_diagnostics(
224 r#"
225macro_rules! Type { () => { u32 }; }
226struct Foo { bar: Type![] }
227
228impl Foo {
229 fn new() -> Self {
230 Foo { bar: 0 }
231 }
232}
233"#,
234 );
235 }
109 236
110 #[test] 237 #[test]
111 fn test_add_field_from_usage() { 238 fn test_add_field_from_usage() {
diff --git a/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs
new file mode 100644
index 000000000..4e639f214
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs
@@ -0,0 +1,61 @@
1use hir::db::AstDatabase;
2use ide_db::source_change::SourceChange;
3use syntax::{ast, AstNode};
4use text_edit::TextEdit;
5
6use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
7
8// Diagnostic: remove-this-semicolon
9//
10// This diagnostic is triggered when there's an erroneous `;` at the end of the block.
11pub(crate) fn remove_this_semicolon(
12 ctx: &DiagnosticsContext<'_>,
13 d: &hir::RemoveThisSemicolon,
14) -> Diagnostic {
15 Diagnostic::new(
16 "remove-this-semicolon",
17 "remove this semicolon",
18 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
19 )
20 .with_fixes(fixes(ctx, d))
21}
22
23fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>> {
24 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
25
26 let semicolon = d
27 .expr
28 .value
29 .to_node(&root)
30 .syntax()
31 .parent()
32 .and_then(ast::ExprStmt::cast)
33 .and_then(|expr| expr.semicolon_token())?
34 .text_range();
35
36 let edit = TextEdit::delete(semicolon);
37 let source_change =
38 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
39
40 Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)])
41}
42
43#[cfg(test)]
44mod tests {
45 use crate::tests::{check_diagnostics, check_fix};
46
47 #[test]
48 fn missing_semicolon() {
49 check_diagnostics(
50 r#"
51fn test() -> i32 { 123; }
52 //^^^ 💡 error: remove this semicolon
53"#,
54 );
55 }
56
57 #[test]
58 fn remove_semicolon() {
59 check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
60 }
61}
diff --git a/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
new file mode 100644
index 000000000..cd87a10bb
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -0,0 +1,179 @@
1use hir::{db::AstDatabase, InFile};
2use ide_db::source_change::SourceChange;
3use syntax::{
4 ast::{self, ArgListOwner},
5 AstNode, TextRange,
6};
7use text_edit::TextEdit;
8
9use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity};
10
11// Diagnostic: replace-filter-map-next-with-find-map
12//
13// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
14pub(crate) fn replace_filter_map_next_with_find_map(
15 ctx: &DiagnosticsContext<'_>,
16 d: &hir::ReplaceFilterMapNextWithFindMap,
17) -> Diagnostic {
18 Diagnostic::new(
19 "replace-filter-map-next-with-find-map",
20 "replace filter_map(..).next() with find_map(..)",
21 ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range,
22 )
23 .severity(Severity::WeakWarning)
24 .with_fixes(fixes(ctx, d))
25}
26
27fn fixes(
28 ctx: &DiagnosticsContext<'_>,
29 d: &hir::ReplaceFilterMapNextWithFindMap,
30) -> Option<Vec<Assist>> {
31 let root = ctx.sema.db.parse_or_expand(d.file)?;
32 let next_expr = d.next_expr.to_node(&root);
33 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
34
35 let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
36 let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
37 let filter_map_args = filter_map_call.arg_list()?;
38
39 let range_to_replace =
40 TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
41 let replacement = format!("find_map{}", filter_map_args.syntax().text());
42 let trigger_range = next_expr.syntax().text_range();
43
44 let edit = TextEdit::replace(range_to_replace, replacement);
45
46 let source_change = SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit);
47
48 Some(vec![fix(
49 "replace_with_find_map",
50 "Replace filter_map(..).next() with find_map()",
51 source_change,
52 trigger_range,
53 )])
54}
55
56#[cfg(test)]
57mod tests {
58 use crate::tests::check_fix;
59
60 // Register the required standard library types to make the tests work
61 #[track_caller]
62 fn check_diagnostics(ra_fixture: &str) {
63 let prefix = r#"
64//- /main.rs crate:main deps:core
65use core::iter::Iterator;
66use core::option::Option::{self, Some, None};
67"#;
68 let suffix = r#"
69//- /core/lib.rs crate:core
70pub mod option {
71 pub enum Option<T> { Some(T), None }
72}
73pub mod iter {
74 pub trait Iterator {
75 type Item;
76 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
77 fn next(&mut self) -> Option<Self::Item>;
78 }
79 pub struct FilterMap {}
80 impl Iterator for FilterMap {
81 type Item = i32;
82 fn next(&mut self) -> i32 { 7 }
83 }
84}
85"#;
86 crate::tests::check_diagnostics(&format!("{}{}{}", prefix, ra_fixture, suffix))
87 }
88
89 #[test]
90 fn replace_filter_map_next_with_find_map2() {
91 check_diagnostics(
92 r#"
93 fn foo() {
94 let m = [1, 2, 3].iter().filter_map(|x| Some(92)).next();
95 } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..)
96"#,
97 );
98 }
99
100 #[test]
101 fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
102 check_diagnostics(
103 r#"
104fn foo() {
105 let m = [1, 2, 3]
106 .iter()
107 .filter_map(|x| Some(92))
108 .len();
109}
110"#,
111 );
112 }
113
114 #[test]
115 fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
116 check_diagnostics(
117 r#"
118fn foo() {
119 let m = [1, 2, 3]
120 .iter()
121 .filter_map(|x| Some(92))
122 .map(|x| x + 2)
123 .len();
124}
125"#,
126 );
127 }
128
129 #[test]
130 fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
131 check_diagnostics(
132 r#"
133fn foo() {
134 let m = [1, 2, 3]
135 .iter()
136 .filter_map(|x| Some(92));
137 let n = m.next();
138}
139"#,
140 );
141 }
142
143 #[test]
144 fn replace_with_wind_map() {
145 check_fix(
146 r#"
147//- /main.rs crate:main deps:core
148use core::iter::Iterator;
149use core::option::Option::{self, Some, None};
150fn foo() {
151 let m = [1, 2, 3].iter().$0filter_map(|x| Some(92)).next();
152}
153//- /core/lib.rs crate:core
154pub mod option {
155 pub enum Option<T> { Some(T), None }
156}
157pub mod iter {
158 pub trait Iterator {
159 type Item;
160 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
161 fn next(&mut self) -> Option<Self::Item>;
162 }
163 pub struct FilterMap {}
164 impl Iterator for FilterMap {
165 type Item = i32;
166 fn next(&mut self) -> i32 { 7 }
167 }
168}
169"#,
170 r#"
171use core::iter::Iterator;
172use core::option::Option::{self, Some, None};
173fn foo() {
174 let m = [1, 2, 3].iter().find_map(|x| Some(92));
175}
176"#,
177 )
178 }
179}
diff --git a/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs b/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs
new file mode 100644
index 000000000..e879de75c
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs
@@ -0,0 +1,16 @@
1use crate::{Diagnostic, DiagnosticsContext, Severity};
2
3// Diagnostic: unimplemented-builtin-macro
4//
5// This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer
6pub(crate) fn unimplemented_builtin_macro(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::UnimplementedBuiltinMacro,
9) -> Diagnostic {
10 Diagnostic::new(
11 "unimplemented-builtin-macro",
12 "unimplemented built-in macro".to_string(),
13 ctx.sema.diagnostics_display_range(d.node.clone()).range,
14 )
15 .severity(Severity::WeakWarning)
16}
diff --git a/crates/ide_diagnostics/src/handlers/unlinked_file.rs b/crates/ide_diagnostics/src/handlers/unlinked_file.rs
new file mode 100644
index 000000000..8e601fa48
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/unlinked_file.rs
@@ -0,0 +1,298 @@
1//! Diagnostic emitted for files that aren't part of any crate.
2
3use hir::db::DefDatabase;
4use ide_db::{
5 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
6 source_change::SourceChange,
7 RootDatabase,
8};
9use syntax::{
10 ast::{self, ModuleItemOwner, NameOwner},
11 AstNode, TextRange, TextSize,
12};
13use text_edit::TextEdit;
14
15use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
16
17// Diagnostic: unlinked-file
18//
19// This diagnostic is shown for files that are not included in any crate, or files that are part of
20// crates rust-analyzer failed to discover. The file will not have IDE features available.
21pub(crate) fn unlinked_file(ctx: &DiagnosticsContext, acc: &mut Vec<Diagnostic>, file_id: FileId) {
22 // Limit diagnostic to the first few characters in the file. This matches how VS Code
23 // renders it with the full span, but on other editors, and is less invasive.
24 let range = ctx.sema.db.parse(file_id).syntax_node().text_range();
25 // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
26 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
27
28 acc.push(
29 Diagnostic::new("unlinked-file", "file not included in module tree", range)
30 .with_fixes(fixes(ctx, file_id)),
31 );
32}
33
34fn fixes(ctx: &DiagnosticsContext, file_id: FileId) -> Option<Vec<Assist>> {
35 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
36 // suggest that as a fix.
37
38 let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(file_id));
39 let our_path = source_root.path_for_file(&file_id)?;
40 let module_name = our_path.name_and_extension()?.0;
41
42 // Candidates to look for:
43 // - `mod.rs` in the same folder
44 // - we also check `main.rs` and `lib.rs`
45 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
46 let parent = our_path.parent()?;
47 let mut paths = vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
48
49 // `submod/bla.rs` -> `submod.rs`
50 if let Some(newmod) = (|| {
51 let name = parent.name_and_extension()?.0;
52 parent.parent()?.join(&format!("{}.rs", name))
53 })() {
54 paths.push(newmod);
55 }
56
57 for path in &paths {
58 if let Some(parent_id) = source_root.file_for_path(path) {
59 for krate in ctx.sema.db.relevant_crates(*parent_id).iter() {
60 let crate_def_map = ctx.sema.db.crate_def_map(*krate);
61 for (_, module) in crate_def_map.modules() {
62 if module.origin.is_inline() {
63 // We don't handle inline `mod parent {}`s, they use different paths.
64 continue;
65 }
66
67 if module.origin.file_id() == Some(*parent_id) {
68 return make_fixes(ctx.sema.db, *parent_id, module_name, file_id);
69 }
70 }
71 }
72 }
73 }
74
75 None
76}
77
78fn make_fixes(
79 db: &RootDatabase,
80 parent_file_id: FileId,
81 new_mod_name: &str,
82 added_file_id: FileId,
83) -> Option<Vec<Assist>> {
84 fn is_outline_mod(item: &ast::Item) -> bool {
85 matches!(item, ast::Item::Module(m) if m.item_list().is_none())
86 }
87
88 let mod_decl = format!("mod {};", new_mod_name);
89 let pub_mod_decl = format!("pub mod {};", new_mod_name);
90
91 let ast: ast::SourceFile = db.parse(parent_file_id).tree();
92
93 let mut mod_decl_builder = TextEdit::builder();
94 let mut pub_mod_decl_builder = TextEdit::builder();
95
96 // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
97 // probably `#[cfg]`d out).
98 for item in ast.items() {
99 if let ast::Item::Module(m) = item {
100 if let Some(name) = m.name() {
101 if m.item_list().is_none() && name.to_string() == new_mod_name {
102 cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists);
103 return None;
104 }
105 }
106 }
107 }
108
109 // If there are existing `mod m;` items, append after them (after the first group of them, rather).
110 match ast
111 .items()
112 .skip_while(|item| !is_outline_mod(item))
113 .take_while(|item| is_outline_mod(item))
114 .last()
115 {
116 Some(last) => {
117 cov_mark::hit!(unlinked_file_append_to_existing_mods);
118 let offset = last.syntax().text_range().end();
119 mod_decl_builder.insert(offset, format!("\n{}", mod_decl));
120 pub_mod_decl_builder.insert(offset, format!("\n{}", pub_mod_decl));
121 }
122 None => {
123 // Prepend before the first item in the file.
124 match ast.items().next() {
125 Some(item) => {
126 cov_mark::hit!(unlinked_file_prepend_before_first_item);
127 let offset = item.syntax().text_range().start();
128 mod_decl_builder.insert(offset, format!("{}\n\n", mod_decl));
129 pub_mod_decl_builder.insert(offset, format!("{}\n\n", pub_mod_decl));
130 }
131 None => {
132 // No items in the file, so just append at the end.
133 cov_mark::hit!(unlinked_file_empty_file);
134 let offset = ast.syntax().text_range().end();
135 mod_decl_builder.insert(offset, format!("{}\n", mod_decl));
136 pub_mod_decl_builder.insert(offset, format!("{}\n", pub_mod_decl));
137 }
138 }
139 }
140 }
141
142 let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
143 Some(vec![
144 fix(
145 "add_mod_declaration",
146 &format!("Insert `{}`", mod_decl),
147 SourceChange::from_text_edit(parent_file_id, mod_decl_builder.finish()),
148 trigger_range,
149 ),
150 fix(
151 "add_pub_mod_declaration",
152 &format!("Insert `{}`", pub_mod_decl),
153 SourceChange::from_text_edit(parent_file_id, pub_mod_decl_builder.finish()),
154 trigger_range,
155 ),
156 ])
157}
158
159#[cfg(test)]
160mod tests {
161 use crate::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix};
162
163 #[test]
164 fn unlinked_file_prepend_first_item() {
165 cov_mark::check!(unlinked_file_prepend_before_first_item);
166 // Only tests the first one for `pub mod` since the rest are the same
167 check_fixes(
168 r#"
169//- /main.rs
170fn f() {}
171//- /foo.rs
172$0
173"#,
174 vec![
175 r#"
176mod foo;
177
178fn f() {}
179"#,
180 r#"
181pub mod foo;
182
183fn f() {}
184"#,
185 ],
186 );
187 }
188
189 #[test]
190 fn unlinked_file_append_mod() {
191 cov_mark::check!(unlinked_file_append_to_existing_mods);
192 check_fix(
193 r#"
194//- /main.rs
195//! Comment on top
196
197mod preexisting;
198
199mod preexisting2;
200
201struct S;
202
203mod preexisting_bottom;)
204//- /foo.rs
205$0
206"#,
207 r#"
208//! Comment on top
209
210mod preexisting;
211
212mod preexisting2;
213mod foo;
214
215struct S;
216
217mod preexisting_bottom;)
218"#,
219 );
220 }
221
222 #[test]
223 fn unlinked_file_insert_in_empty_file() {
224 cov_mark::check!(unlinked_file_empty_file);
225 check_fix(
226 r#"
227//- /main.rs
228//- /foo.rs
229$0
230"#,
231 r#"
232mod foo;
233"#,
234 );
235 }
236
237 #[test]
238 fn unlinked_file_old_style_modrs() {
239 check_fix(
240 r#"
241//- /main.rs
242mod submod;
243//- /submod/mod.rs
244// in mod.rs
245//- /submod/foo.rs
246$0
247"#,
248 r#"
249// in mod.rs
250mod foo;
251"#,
252 );
253 }
254
255 #[test]
256 fn unlinked_file_new_style_mod() {
257 check_fix(
258 r#"
259//- /main.rs
260mod submod;
261//- /submod.rs
262//- /submod/foo.rs
263$0
264"#,
265 r#"
266mod foo;
267"#,
268 );
269 }
270
271 #[test]
272 fn unlinked_file_with_cfg_off() {
273 cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists);
274 check_no_fix(
275 r#"
276//- /main.rs
277#[cfg(never)]
278mod foo;
279
280//- /foo.rs
281$0
282"#,
283 );
284 }
285
286 #[test]
287 fn unlinked_file_with_cfg_on() {
288 check_diagnostics(
289 r#"
290//- /main.rs
291#[cfg(not(never))]
292mod foo;
293
294//- /foo.rs
295"#,
296 );
297 }
298}
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs b/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs
new file mode 100644
index 000000000..74e4a69c6
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs
@@ -0,0 +1,49 @@
1use crate::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: unresolved-extern-crate
4//
5// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
6pub(crate) fn unresolved_extern_crate(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::UnresolvedExternCrate,
9) -> Diagnostic {
10 Diagnostic::new(
11 "unresolved-extern-crate",
12 "unresolved extern crate",
13 ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
14 )
15}
16
17#[cfg(test)]
18mod tests {
19 use crate::tests::check_diagnostics;
20
21 #[test]
22 fn unresolved_extern_crate() {
23 check_diagnostics(
24 r#"
25//- /main.rs crate:main deps:core
26extern crate core;
27 extern crate doesnotexist;
28//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
29//- /lib.rs crate:core
30"#,
31 );
32 }
33
34 #[test]
35 fn extern_crate_self_as() {
36 cov_mark::check!(extern_crate_self_as);
37 check_diagnostics(
38 r#"
39//- /lib.rs
40 extern crate doesnotexist;
41//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
42// Should not error.
43extern crate self as foo;
44struct Foo;
45use foo::Foo as Bar;
46"#,
47 );
48 }
49}
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_import.rs b/crates/ide_diagnostics/src/handlers/unresolved_import.rs
new file mode 100644
index 000000000..e52a88459
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/unresolved_import.rs
@@ -0,0 +1,90 @@
1use crate::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: unresolved-import
4//
5// This diagnostic is triggered if rust-analyzer is unable to resolve a path in
6// a `use` declaration.
7pub(crate) fn unresolved_import(
8 ctx: &DiagnosticsContext<'_>,
9 d: &hir::UnresolvedImport,
10) -> Diagnostic {
11 Diagnostic::new(
12 "unresolved-import",
13 "unresolved import",
14 ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
15 )
16 // This currently results in false positives in the following cases:
17 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
18 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
19 // - proc macros and/or proc macro generated code
20 .experimental()
21}
22
23#[cfg(test)]
24mod tests {
25 use crate::tests::check_diagnostics;
26
27 #[test]
28 fn unresolved_import() {
29 check_diagnostics(
30 r#"
31use does_exist;
32use does_not_exist;
33 //^^^^^^^^^^^^^^ error: unresolved import
34
35mod does_exist {}
36"#,
37 );
38 }
39
40 #[test]
41 fn unresolved_import_in_use_tree() {
42 // Only the relevant part of a nested `use` item should be highlighted.
43 check_diagnostics(
44 r#"
45use does_exist::{Exists, DoesntExist};
46 //^^^^^^^^^^^ error: unresolved import
47
48use {does_not_exist::*, does_exist};
49 //^^^^^^^^^^^^^^^^^ error: unresolved import
50
51use does_not_exist::{
52 a,
53 //^ error: unresolved import
54 b,
55 //^ error: unresolved import
56 c,
57 //^ error: unresolved import
58};
59
60mod does_exist {
61 pub struct Exists;
62}
63"#,
64 );
65 }
66
67 #[test]
68 fn dedup_unresolved_import_from_unresolved_crate() {
69 check_diagnostics(
70 r#"
71//- /main.rs crate:main
72mod a {
73 extern crate doesnotexist;
74 //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unresolved extern crate
75
76 // Should not error, since we already errored for the missing crate.
77 use doesnotexist::{self, bla, *};
78
79 use crate::doesnotexist;
80 //^^^^^^^^^^^^^^^^^^^ error: unresolved import
81}
82
83mod m {
84 use super::doesnotexist;
85 //^^^^^^^^^^^^^^^^^^^ error: unresolved import
86}
87"#,
88 );
89 }
90}
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs
new file mode 100644
index 000000000..f0f7725db
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs
@@ -0,0 +1,84 @@
1use hir::{db::AstDatabase, InFile};
2use syntax::{AstNode, SyntaxNodePtr};
3
4use crate::{Diagnostic, DiagnosticsContext};
5
6// Diagnostic: unresolved-macro-call
7//
8// This diagnostic is triggered if rust-analyzer is unable to resolve the path
9// to a macro in a macro invocation.
10pub(crate) fn unresolved_macro_call(
11 ctx: &DiagnosticsContext<'_>,
12 d: &hir::UnresolvedMacroCall,
13) -> Diagnostic {
14 let last_path_segment = ctx.sema.db.parse_or_expand(d.macro_call.file_id).and_then(|root| {
15 d.macro_call
16 .value
17 .to_node(&root)
18 .path()
19 .and_then(|it| it.segment())
20 .and_then(|it| it.name_ref())
21 .map(|it| InFile::new(d.macro_call.file_id, SyntaxNodePtr::new(it.syntax())))
22 });
23 let diagnostics = last_path_segment.unwrap_or_else(|| d.macro_call.clone().map(|it| it.into()));
24
25 Diagnostic::new(
26 "unresolved-macro-call",
27 format!("unresolved macro `{}!`", d.path),
28 ctx.sema.diagnostics_display_range(diagnostics).range,
29 )
30 .experimental()
31}
32
33#[cfg(test)]
34mod tests {
35 use crate::tests::check_diagnostics;
36
37 #[test]
38 fn unresolved_macro_diag() {
39 check_diagnostics(
40 r#"
41fn f() {
42 m!();
43} //^ error: unresolved macro `m!`
44
45"#,
46 );
47 }
48
49 #[test]
50 fn test_unresolved_macro_range() {
51 check_diagnostics(
52 r#"
53foo::bar!(92);
54 //^^^ error: unresolved macro `foo::bar!`
55"#,
56 );
57 }
58
59 #[test]
60 fn unresolved_legacy_scope_macro() {
61 check_diagnostics(
62 r#"
63macro_rules! m { () => {} }
64
65m!(); m2!();
66 //^^ error: unresolved macro `self::m2!`
67"#,
68 );
69 }
70
71 #[test]
72 fn unresolved_module_scope_macro() {
73 check_diagnostics(
74 r#"
75mod mac {
76#[macro_export]
77macro_rules! m { () => {} } }
78
79self::m!(); self::m2!();
80 //^^ error: unresolved macro `self::m2!`
81"#,
82 );
83 }
84}
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide_diagnostics/src/handlers/unresolved_module.rs
index b3d0283bb..61fc43604 100644
--- a/crates/ide/src/diagnostics/fixes/unresolved_module.rs
+++ b/crates/ide_diagnostics/src/handlers/unresolved_module.rs
@@ -1,39 +1,61 @@
1use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; 1use hir::db::AstDatabase;
2use ide_assists::{Assist, AssistResolveStrategy}; 2use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
3use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase};
4use syntax::AstNode; 3use syntax::AstNode;
5 4
6use crate::diagnostics::{fix, DiagnosticWithFixes}; 5use crate::{fix, Diagnostic, DiagnosticsContext};
7 6
8impl DiagnosticWithFixes for UnresolvedModule { 7// Diagnostic: unresolved-module
9 fn fixes( 8//
10 &self, 9// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
11 sema: &Semantics<RootDatabase>, 10pub(crate) fn unresolved_module(
12 _resolve: &AssistResolveStrategy, 11 ctx: &DiagnosticsContext<'_>,
13 ) -> Option<Vec<Assist>> { 12 d: &hir::UnresolvedModule,
14 let root = sema.db.parse_or_expand(self.file)?; 13) -> Diagnostic {
15 let unresolved_module = self.decl.to_node(&root); 14 Diagnostic::new(
16 Some(vec![fix( 15 "unresolved-module",
17 "create_module", 16 "unresolved module",
18 "Create module", 17 ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
19 FileSystemEdit::CreateFile { 18 )
20 dst: AnchoredPathBuf { 19 .with_fixes(fixes(ctx, d))
21 anchor: self.file.original_file(sema.db), 20}
22 path: self.candidate.clone(), 21
23 }, 22fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
24 initial_contents: "".to_string(), 23 let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
25 } 24 let unresolved_module = d.decl.value.to_node(&root);
26 .into(), 25 Some(vec![fix(
27 unresolved_module.syntax().text_range(), 26 "create_module",
28 )]) 27 "Create module",
29 } 28 FileSystemEdit::CreateFile {
29 dst: AnchoredPathBuf {
30 anchor: d.decl.file_id.original_file(ctx.sema.db),
31 path: d.candidate.clone(),
32 },
33 initial_contents: "".to_string(),
34 }
35 .into(),
36 unresolved_module.syntax().text_range(),
37 )])
30} 38}
31 39
32#[cfg(test)] 40#[cfg(test)]
33mod tests { 41mod tests {
34 use expect_test::expect; 42 use expect_test::expect;
35 43
36 use crate::diagnostics::tests::check_expect; 44 use crate::tests::{check_diagnostics, check_expect};
45
46 #[test]
47 fn unresolved_module() {
48 check_diagnostics(
49 r#"
50//- /lib.rs
51mod foo;
52 mod bar;
53//^^^^^^^^ 💡 error: unresolved module
54mod baz {}
55//- /foo.rs
56"#,
57 );
58 }
37 59
38 #[test] 60 #[test]
39 fn test_unresolved_module_diagnostic() { 61 fn test_unresolved_module_diagnostic() {
@@ -42,9 +64,14 @@ mod tests {
42 expect![[r#" 64 expect![[r#"
43 [ 65 [
44 Diagnostic { 66 Diagnostic {
67 code: DiagnosticCode(
68 "unresolved-module",
69 ),
45 message: "unresolved module", 70 message: "unresolved module",
46 range: 0..8, 71 range: 0..8,
47 severity: Error, 72 severity: Error,
73 unused: false,
74 experimental: false,
48 fixes: Some( 75 fixes: Some(
49 [ 76 [
50 Assist { 77 Assist {
@@ -75,12 +102,6 @@ mod tests {
75 }, 102 },
76 ], 103 ],
77 ), 104 ),
78 unused: false,
79 code: Some(
80 DiagnosticCode(
81 "unresolved-module",
82 ),
83 ),
84 }, 105 },
85 ] 106 ]
86 "#]], 107 "#]],
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs
new file mode 100644
index 000000000..fde1d1323
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -0,0 +1,27 @@
1use crate::{Diagnostic, DiagnosticsContext, Severity};
2
3// Diagnostic: unresolved-proc-macro
4//
5// This diagnostic is shown when a procedural macro can not be found. This usually means that
6// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
7// but can also indicate project setup problems.
8//
9// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
10// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
11// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
12pub(crate) fn unresolved_proc_macro(
13 ctx: &DiagnosticsContext<'_>,
14 d: &hir::UnresolvedProcMacro,
15) -> Diagnostic {
16 // Use more accurate position if available.
17 let display_range = d
18 .precise_location
19 .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range);
20 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
21 let message = match &d.macro_name {
22 Some(name) => format!("proc macro `{}` not expanded", name),
23 None => "proc macro not expanded".to_string(),
24 };
25
26 Diagnostic::new("unresolved-proc-macro", message, display_range).severity(Severity::WeakWarning)
27}
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/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml
index 5d2221ebc..727d17bac 100644
--- a/crates/ide_ssr/Cargo.toml
+++ b/crates/ide_ssr/Cargo.toml
@@ -11,7 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = "2.0.0-pre.1"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16itertools = "0.10.0" 16itertools = "0.10.0"
17 17
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs
index b3072fb9f..fb92a0ccc 100644
--- a/crates/ide_ssr/src/matching.rs
+++ b/crates/ide_ssr/src/matching.rs
@@ -382,7 +382,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
382 code: Option<T>, 382 code: Option<T>,
383 ) -> Result<(), MatchFailed> { 383 ) -> Result<(), MatchFailed> {
384 match (pattern, code) { 384 match (pattern, code) {
385 (Some(p), Some(c)) => self.attempt_match_node(phase, &p.syntax(), &c.syntax()), 385 (Some(p), Some(c)) => self.attempt_match_node(phase, p.syntax(), c.syntax()),
386 (None, None) => Ok(()), 386 (None, None) => Ok(()),
387 (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()), 387 (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()),
388 (None, Some(c)) => { 388 (None, Some(c)) => {
@@ -478,7 +478,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
478 if Some(first_token.text()) == next_pattern_token.as_deref() { 478 if Some(first_token.text()) == next_pattern_token.as_deref() {
479 if let Some(SyntaxElement::Node(p)) = pattern.next() { 479 if let Some(SyntaxElement::Node(p)) = pattern.next() {
480 // We have a subtree that starts with the next token in our pattern. 480 // We have a subtree that starts with the next token in our pattern.
481 self.attempt_match_token_tree(phase, &p, &n)?; 481 self.attempt_match_token_tree(phase, &p, n)?;
482 break; 482 break;
483 } 483 }
484 } 484 }
@@ -609,7 +609,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
609 expr: &ast::Expr, 609 expr: &ast::Expr,
610 ) -> Result<usize, MatchFailed> { 610 ) -> Result<usize, MatchFailed> {
611 use hir::HirDisplay; 611 use hir::HirDisplay;
612 let code_type = self.sema.type_of_expr(&expr).ok_or_else(|| { 612 let code_type = self.sema.type_of_expr(expr).ok_or_else(|| {
613 match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) 613 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
614 })?; 614 })?;
615 // Temporary needed to make the borrow checker happy. 615 // Temporary needed to make the borrow checker happy.
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs
index c9ccc1961..9265af7c1 100644
--- a/crates/ide_ssr/src/replacing.rs
+++ b/crates/ide_ssr/src/replacing.rs
@@ -84,16 +84,16 @@ impl ReplacementRenderer<'_> {
84 fn render_node_or_token(&mut self, node_or_token: &SyntaxElement) { 84 fn render_node_or_token(&mut self, node_or_token: &SyntaxElement) {
85 match node_or_token { 85 match node_or_token {
86 SyntaxElement::Token(token) => { 86 SyntaxElement::Token(token) => {
87 self.render_token(&token); 87 self.render_token(token);
88 } 88 }
89 SyntaxElement::Node(child_node) => { 89 SyntaxElement::Node(child_node) => {
90 self.render_node(&child_node); 90 self.render_node(child_node);
91 } 91 }
92 } 92 }
93 } 93 }
94 94
95 fn render_node(&mut self, node: &SyntaxNode) { 95 fn render_node(&mut self, node: &SyntaxNode) {
96 if let Some(mod_path) = self.match_info.rendered_template_paths.get(&node) { 96 if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) {
97 self.out.push_str(&mod_path.to_string()); 97 self.out.push_str(&mod_path.to_string());
98 // Emit everything except for the segment's name-ref, since we already effectively 98 // Emit everything except for the segment's name-ref, since we already effectively
99 // emitted that as part of `mod_path`. 99 // emitted that as part of `mod_path`.
@@ -107,12 +107,12 @@ impl ReplacementRenderer<'_> {
107 } 107 }
108 } 108 }
109 } else { 109 } else {
110 self.render_node_children(&node); 110 self.render_node_children(node);
111 } 111 }
112 } 112 }
113 113
114 fn render_token(&mut self, token: &SyntaxToken) { 114 fn render_token(&mut self, token: &SyntaxToken) {
115 if let Some(placeholder) = self.rule.get_placeholder(&token) { 115 if let Some(placeholder) = self.rule.get_placeholder(token) {
116 if let Some(placeholder_value) = 116 if let Some(placeholder_value) =
117 self.match_info.placeholder_values.get(&placeholder.ident) 117 self.match_info.placeholder_values.get(&placeholder.ident)
118 { 118 {
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 541da4122..a66a7a4a8 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -211,7 +211,7 @@ impl<'db> ResolutionScope<'db> {
211 // First try resolving the whole path. This will work for things like 211 // First try resolving the whole path. This will work for things like
212 // `std::collections::HashMap`, but will fail for things like 212 // `std::collections::HashMap`, but will fail for things like
213 // `std::collections::HashMap::new`. 213 // `std::collections::HashMap::new`.
214 if let Some(resolution) = self.scope.speculative_resolve(&path) { 214 if let Some(resolution) = self.scope.speculative_resolve(path) {
215 return Some(resolution); 215 return Some(resolution);
216 } 216 }
217 // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if 217 // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs
index 28cef742c..f2056919e 100644
--- a/crates/ide_ssr/src/search.rs
+++ b/crates/ide_ssr/src/search.rs
@@ -173,7 +173,7 @@ impl<'db> MatchFinder<'db> {
173 if !is_search_permitted(code) { 173 if !is_search_permitted(code) {
174 return; 174 return;
175 } 175 }
176 self.try_add_match(rule, &code, restrict_range, matches_out); 176 self.try_add_match(rule, code, restrict_range, matches_out);
177 // If we've got a macro call, we already tried matching it pre-expansion, which is the only 177 // If we've got a macro call, we already tried matching it pre-expansion, which is the only
178 // way to match the whole macro, now try expanding it and matching the expansion. 178 // way to match the whole macro, now try expanding it and matching the expansion.
179 if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { 179 if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs
index 1d8565dc0..444c6b0af 100644
--- a/crates/ide_ssr/src/tests.rs
+++ b/crates/ide_ssr/src/tests.rs
@@ -75,7 +75,7 @@ pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Ve
75 match range_or_offset { 75 match range_or_offset {
76 RangeOrOffset::Range(range) => { 76 RangeOrOffset::Range(range) => {
77 position = FilePosition { file_id, offset: range.start() }; 77 position = FilePosition { file_id, offset: range.start() };
78 selections = vec![FileRange { file_id, range: range }]; 78 selections = vec![FileRange { file_id, range }];
79 } 79 }
80 RangeOrOffset::Offset(offset) => { 80 RangeOrOffset::Offset(offset) => {
81 position = FilePosition { file_id, offset }; 81 position = FilePosition { file_id, offset };
@@ -129,7 +129,7 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
129 let matched_strings: Vec<String> = 129 let matched_strings: Vec<String> =
130 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); 130 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect();
131 if matched_strings != expected && !expected.is_empty() { 131 if matched_strings != expected && !expected.is_empty() {
132 print_match_debug_info(&match_finder, position.file_id, &expected[0]); 132 print_match_debug_info(&match_finder, position.file_id, expected[0]);
133 } 133 }
134 assert_eq!(matched_strings, expected); 134 assert_eq!(matched_strings, expected);
135} 135}
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index 8856787c0..f3092d9aa 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15smallvec = "1.2.0" 15smallvec = "1.2.0"
16log = "0.4.8" 16log = "0.4.8"
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index c982eb58f..c2a9a38c9 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -1,6 +1,6 @@
1//! An NFA-based parser, which is porting from rustc mbe parsing code 1//! An NFA-based parser, which is porting from rustc mbe parsing code
2//! 2//!
3//! See https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs 3//! See <https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs>
4//! Here is a quick intro to how the parser works, copied from rustc: 4//! Here is a quick intro to how the parser works, copied from rustc:
5//! 5//!
6//! A 'position' is a dot in the middle of a matcher, usually represented as a 6//! A 'position' is a dot in the middle of a matcher, usually represented as a
@@ -121,7 +121,7 @@ impl Match {
121 121
122/// Matching errors are added to the `Match`. 122/// Matching errors are added to the `Match`.
123pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { 123pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
124 let mut res = match_loop(pattern, &input); 124 let mut res = match_loop(pattern, input);
125 res.bound_count = count(res.bindings.bindings()); 125 res.bound_count = count(res.bindings.bindings());
126 return res; 126 return res;
127 127
@@ -202,7 +202,7 @@ impl BindingsBuilder {
202 } 202 }
203 203
204 fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) { 204 fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) {
205 let BindingsIdx(idx, nidx) = self.copy(&child); 205 let BindingsIdx(idx, nidx) = self.copy(child);
206 self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx)))); 206 self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx))));
207 } 207 }
208 208
@@ -221,7 +221,7 @@ impl BindingsBuilder {
221 221
222 fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) { 222 fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) {
223 let mut nodes = Vec::new(); 223 let mut nodes = Vec::new();
224 self.collect_nodes(&link_nodes, &mut nodes); 224 self.collect_nodes(link_nodes, &mut nodes);
225 225
226 for cmd in nodes { 226 for cmd in nodes {
227 match &**cmd { 227 match &**cmd {
@@ -282,7 +282,7 @@ impl BindingsBuilder {
282 282
283 nested_refs.into_iter().for_each(|iter| { 283 nested_refs.into_iter().for_each(|iter| {
284 let mut child_bindings = Bindings::default(); 284 let mut child_bindings = Bindings::default();
285 self.build_inner(&mut child_bindings, &iter); 285 self.build_inner(&mut child_bindings, iter);
286 nested.push(child_bindings) 286 nested.push(child_bindings)
287 }) 287 })
288 } 288 }
@@ -417,7 +417,7 @@ fn match_loop_inner<'t>(
417 let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count); 417 let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count);
418 if item.sep.is_some() && sep_idx != sep_len { 418 if item.sep.is_some() && sep_idx != sep_len {
419 let sep = item.sep.as_ref().unwrap(); 419 let sep = item.sep.as_ref().unwrap();
420 if src.clone().expect_separator(&sep, sep_idx) { 420 if src.clone().expect_separator(sep, sep_idx) {
421 item.dot.next(); 421 item.dot.next();
422 item.sep_parsed = Some(sep_idx + 1); 422 item.sep_parsed = Some(sep_idx + 1);
423 try_push!(next_items, item); 423 try_push!(next_items, item);
@@ -487,7 +487,7 @@ fn match_loop_inner<'t>(
487 item.meta_result = Some((fork, match_res)); 487 item.meta_result = Some((fork, match_res));
488 try_push!(bb_items, item); 488 try_push!(bb_items, item);
489 } else { 489 } else {
490 bindings_builder.push_optional(&mut item.bindings, &name); 490 bindings_builder.push_optional(&mut item.bindings, name);
491 item.dot.next(); 491 item.dot.next();
492 cur_items.push(item); 492 cur_items.push(item);
493 } 493 }
@@ -495,7 +495,7 @@ fn match_loop_inner<'t>(
495 Some(err) => { 495 Some(err) => {
496 res.add_err(err); 496 res.add_err(err);
497 if let Some(fragment) = match_res.value { 497 if let Some(fragment) = match_res.value {
498 bindings_builder.push_fragment(&mut item.bindings, &name, fragment); 498 bindings_builder.push_fragment(&mut item.bindings, name, fragment);
499 } 499 }
500 item.is_error = true; 500 item.is_error = true;
501 error_items.push(item); 501 error_items.push(item);
@@ -504,7 +504,7 @@ fn match_loop_inner<'t>(
504 } 504 }
505 } 505 }
506 OpDelimited::Op(Op::Leaf(leaf)) => { 506 OpDelimited::Op(Op::Leaf(leaf)) => {
507 if let Err(err) = match_leaf(&leaf, &mut src.clone()) { 507 if let Err(err) = match_leaf(leaf, &mut src.clone()) {
508 res.add_err(err); 508 res.add_err(err);
509 item.is_error = true; 509 item.is_error = true;
510 } else { 510 } else {
@@ -640,10 +640,10 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
640 let (iter, match_res) = item.meta_result.take().unwrap(); 640 let (iter, match_res) = item.meta_result.take().unwrap();
641 match match_res.value { 641 match match_res.value {
642 Some(fragment) => { 642 Some(fragment) => {
643 bindings_builder.push_fragment(&mut item.bindings, &name, fragment); 643 bindings_builder.push_fragment(&mut item.bindings, name, fragment);
644 } 644 }
645 None if match_res.err.is_none() => { 645 None if match_res.err.is_none() => {
646 bindings_builder.push_optional(&mut item.bindings, &name); 646 bindings_builder.push_optional(&mut item.bindings, name);
647 } 647 }
648 _ => {} 648 _ => {}
649 } 649 }
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index dd7fa97d7..49a137577 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -55,7 +55,7 @@ pub(super) fn transcribe(
55 template: &MetaTemplate, 55 template: &MetaTemplate,
56 bindings: &Bindings, 56 bindings: &Bindings,
57) -> ExpandResult<tt::Subtree> { 57) -> ExpandResult<tt::Subtree> {
58 let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; 58 let mut ctx = ExpandCtx { bindings, nesting: Vec::new() };
59 let mut arena: Vec<tt::TokenTree> = Vec::new(); 59 let mut arena: Vec<tt::TokenTree> = Vec::new();
60 expand_subtree(&mut ctx, template, None, &mut arena) 60 expand_subtree(&mut ctx, template, None, &mut arena)
61} 61}
@@ -91,12 +91,12 @@ fn expand_subtree(
91 Op::Leaf(tt) => arena.push(tt.clone().into()), 91 Op::Leaf(tt) => arena.push(tt.clone().into()),
92 Op::Subtree { tokens, delimiter } => { 92 Op::Subtree { tokens, delimiter } => {
93 let ExpandResult { value: tt, err: e } = 93 let ExpandResult { value: tt, err: e } =
94 expand_subtree(ctx, &tokens, *delimiter, arena); 94 expand_subtree(ctx, tokens, *delimiter, arena);
95 err = err.or(e); 95 err = err.or(e);
96 arena.push(tt.into()); 96 arena.push(tt.into());
97 } 97 }
98 Op::Var { name, id, .. } => { 98 Op::Var { name, id, .. } => {
99 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); 99 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id);
100 err = err.or(e); 100 err = err.or(e);
101 push_fragment(arena, fragment); 101 push_fragment(arena, fragment);
102 } 102 }
@@ -141,7 +141,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult
141 .into(); 141 .into();
142 ExpandResult::ok(Fragment::Tokens(tt)) 142 ExpandResult::ok(Fragment::Tokens(tt))
143 } else { 143 } else {
144 ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( 144 ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
145 |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) }, 145 |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
146 |b| ExpandResult::ok(b.clone()), 146 |b| ExpandResult::ok(b.clone()),
147 ) 147 )
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 380a50744..8c8528aaf 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -280,8 +280,8 @@ impl Rule {
280 .expect_subtree() 280 .expect_subtree()
281 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; 281 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
282 282
283 let lhs = MetaTemplate(parse_pattern(&lhs)?); 283 let lhs = MetaTemplate(parse_pattern(lhs)?);
284 let rhs = MetaTemplate(parse_template(&rhs)?); 284 let rhs = MetaTemplate(parse_template(rhs)?);
285 285
286 Ok(crate::Rule { lhs, rhs }) 286 Ok(crate::Rule { lhs, rhs })
287 } 287 }
@@ -290,7 +290,7 @@ impl Rule {
290fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { 290fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
291 for op in pattern.iter() { 291 for op in pattern.iter() {
292 match op { 292 match op {
293 Op::Subtree { tokens, .. } => validate(&tokens)?, 293 Op::Subtree { tokens, .. } => validate(tokens)?,
294 Op::Repeat { tokens: subtree, separator, .. } => { 294 Op::Repeat { tokens: subtree, separator, .. } => {
295 // Checks that no repetition which could match an empty token 295 // Checks that no repetition which could match an empty token
296 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 296 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 04c0d3e75..deed884d2 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -42,7 +42,7 @@ impl<'a> OpDelimitedIter<'a> {
42 } 42 }
43 43
44 pub(crate) fn reset(&self) -> Self { 44 pub(crate) fn reset(&self) -> Self {
45 Self { inner: &self.inner, idx: 0, delimited: self.delimited } 45 Self { inner: self.inner, idx: 0, delimited: self.delimited }
46 } 46 }
47} 47}
48 48
@@ -126,11 +126,11 @@ impl Separator {
126} 126}
127 127
128pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> { 128pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
129 parse_inner(&template, Mode::Template).into_iter().collect() 129 parse_inner(template, Mode::Template).into_iter().collect()
130} 130}
131 131
132pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> { 132pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
133 parse_inner(&pattern, Mode::Pattern).into_iter().collect() 133 parse_inner(pattern, Mode::Pattern).into_iter().collect()
134} 134}
135 135
136#[derive(Clone, Copy)] 136#[derive(Clone, Copy)]
@@ -140,7 +140,7 @@ enum Mode {
140} 140}
141 141
142fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> { 142fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> {
143 let mut src = TtIter::new(&tt); 143 let mut src = TtIter::new(tt);
144 std::iter::from_fn(move || { 144 std::iter::from_fn(move || {
145 let first = src.next()?; 145 let first = src.next()?;
146 Some(next_op(first, &mut src, mode)) 146 Some(next_op(first, &mut src, mode))
@@ -171,7 +171,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
171 match second { 171 match second {
172 tt::TokenTree::Subtree(subtree) => { 172 tt::TokenTree::Subtree(subtree) => {
173 let (separator, kind) = parse_repeat(src)?; 173 let (separator, kind) = parse_repeat(src)?;
174 let tokens = parse_inner(&subtree, mode) 174 let tokens = parse_inner(subtree, mode)
175 .into_iter() 175 .into_iter()
176 .collect::<Result<Vec<Op>, ParseError>>()?; 176 .collect::<Result<Vec<Op>, ParseError>>()?;
177 Op::Repeat { tokens: MetaTemplate(tokens), separator, kind } 177 Op::Repeat { tokens: MetaTemplate(tokens), separator, kind }
@@ -191,7 +191,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
191 Op::Var { name, kind, id } 191 Op::Var { name, kind, id }
192 } 192 }
193 tt::Leaf::Literal(lit) => { 193 tt::Leaf::Literal(lit) => {
194 if is_boolean_literal(&lit) { 194 if is_boolean_literal(lit) {
195 let name = lit.text.clone(); 195 let name = lit.text.clone();
196 let kind = eat_fragment_kind(src, mode)?; 196 let kind = eat_fragment_kind(src, mode)?;
197 let id = lit.id; 197 let id = lit.id;
@@ -206,7 +206,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
206 tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), 206 tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()),
207 tt::TokenTree::Subtree(subtree) => { 207 tt::TokenTree::Subtree(subtree) => {
208 let tokens = 208 let tokens =
209 parse_inner(&subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?; 209 parse_inner(subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?;
210 Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter } 210 Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter }
211 } 211 }
212 }; 212 };
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index bde370fdb..ee80807ad 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -22,7 +22,7 @@ impl<'a> SubtreeTokenSource {
22 #[cfg(test)] 22 #[cfg(test)]
23 pub(crate) fn text(&self) -> SmolStr { 23 pub(crate) fn text(&self) -> SmolStr {
24 match self.cached.get(self.curr.1) { 24 match self.cached.get(self.curr.1) {
25 Some(ref tt) => tt.text.clone(), 25 Some(tt) => tt.text.clone(),
26 _ => SmolStr::new(""), 26 _ => SmolStr::new(""),
27 } 27 }
28 } 28 }
@@ -59,7 +59,7 @@ impl<'a> SubtreeTokenSource {
59 59
60 current = match tt { 60 current = match tt {
61 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { 61 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
62 cached.push(convert_leaf(&leaf)); 62 cached.push(convert_leaf(leaf));
63 cursor.bump() 63 cursor.bump()
64 } 64 }
65 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { 65 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
@@ -114,7 +114,7 @@ impl<'a> TokenSource for SubtreeTokenSource {
114 /// Is the current token a specified keyword? 114 /// Is the current token a specified keyword?
115 fn is_keyword(&self, kw: &str) -> bool { 115 fn is_keyword(&self, kw: &str) -> bool {
116 match self.cached.get(self.curr.1) { 116 match self.cached.get(self.curr.1) {
117 Some(ref t) => t.text == *kw, 117 Some(t) => t.text == *kw,
118 _ => false, 118 _ => false,
119 } 119 }
120 } 120 }
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 978c75747..cdc22425d 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -633,7 +633,7 @@ impl<'a> TreeSink for TtTreeSink<'a> {
633 } 633 }
634 } 634 }
635 }; 635 };
636 self.buf += &text; 636 self.buf += text;
637 self.text_pos += TextSize::of(text); 637 self.text_pos += TextSize::of(text);
638 } 638 }
639 639
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index 75c88687c..c788e427e 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -490,7 +490,7 @@ [email protected]
490 490
491fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { 491fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree {
492 if let tt::TokenTree::Subtree(subtree) = tt { 492 if let tt::TokenTree::Subtree(subtree) = tt {
493 return &subtree; 493 return subtree;
494 } 494 }
495 unreachable!("It is not a subtree"); 495 unreachable!("It is not a subtree");
496} 496}
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index bd54f2442..5a4eca7bf 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -115,7 +115,7 @@ impl<'a> TtIter<'a> {
115 } 115 }
116 } 116 }
117 117
118 let buffer = TokenBuffer::from_tokens(&self.inner.as_slice()); 118 let buffer = TokenBuffer::from_tokens(self.inner.as_slice());
119 let mut src = SubtreeTokenSource::new(&buffer); 119 let mut src = SubtreeTokenSource::new(&buffer);
120 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; 120 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
121 121
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 9d22e1950..686a64345 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -208,7 +208,7 @@ struct Restrictions {
208 208
209/// Binding powers of operators for a Pratt parser. 209/// Binding powers of operators for a Pratt parser.
210/// 210///
211/// See https://www.oilshell.org/blog/2016/11/03.html 211/// See <https://www.oilshell.org/blog/2016/11/03.html>
212#[rustfmt::skip] 212#[rustfmt::skip]
213fn current_op(p: &Parser) -> (u8, SyntaxKind) { 213fn current_op(p: &Parser) -> (u8, SyntaxKind) {
214 const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); 214 const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index f09ad37e3..48dac14c4 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -244,7 +244,7 @@ impl RelPath {
244 } 244 }
245} 245}
246 246
247/// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 247/// Taken from <https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85>
248fn normalize_path(path: &Path) -> PathBuf { 248fn normalize_path(path: &Path) -> PathBuf {
249 let mut components = path.components().peekable(); 249 let mut components = path.components().peekable();
250 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { 250 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index 14eed4289..899895578 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -59,7 +59,7 @@ pub trait Message: Serialize + DeserializeOwned {
59 Ok(match read_json(inp, buf)? { 59 Ok(match read_json(inp, buf)? {
60 None => None, 60 None => None,
61 Some(text) => { 61 Some(text) => {
62 let mut deserializer = serde_json::Deserializer::from_str(&text); 62 let mut deserializer = serde_json::Deserializer::from_str(text);
63 // Note that some proc-macro generate very deep syntax tree 63 // Note that some proc-macro generate very deep syntax tree
64 // We have to disable the current limit of serde here 64 // We have to disable the current limit of serde here
65 deserializer.disable_recursion_limit(); 65 deserializer.disable_recursion_limit();
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index 99d05aef3..38eac6c17 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -76,9 +76,7 @@ impl ProcMacroProcessSrv {
76 .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?; 76 .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?;
77 77
78 match res { 78 match res {
79 Some(Response::Error(err)) => { 79 Some(Response::Error(err)) => Err(tt::ExpansionError::ExpansionError(err.message)),
80 return Err(tt::ExpansionError::ExpansionError(err.message));
81 }
82 Some(res) => Ok(res.try_into().map_err(|err| { 80 Some(res) => Ok(res.try_into().map_err(|err| {
83 tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err)) 81 tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err))
84 })?), 82 })?),
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
index 6dbac50b4..28a4ac086 100644
--- a/crates/proc_macro_api/src/version.rs
+++ b/crates/proc_macro_api/src/version.rs
@@ -95,7 +95,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
95/// * [version string bytes encoded in utf8] <- GET THIS BOI 95/// * [version string bytes encoded in utf8] <- GET THIS BOI
96/// * [some more bytes that we don really care but still there] :-) 96/// * [some more bytes that we don really care but still there] :-)
97/// Check this issue for more about the bytes layout: 97/// Check this issue for more about the bytes layout:
98/// https://github.com/rust-analyzer/rust-analyzer/issues/6174 98/// <https://github.com/rust-analyzer/rust-analyzer/issues/6174>
99fn read_version(dylib_path: &Path) -> io::Result<String> { 99fn read_version(dylib_path: &Path) -> io::Result<String> {
100 let dylib_file = File::open(dylib_path)?; 100 let dylib_file = File::open(dylib_path)?;
101 let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?; 101 let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;
diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs
index d4f04ee06..f54cbcd61 100644
--- a/crates/proc_macro_srv/src/lib.rs
+++ b/crates/proc_macro_srv/src/lib.rs
@@ -1,7 +1,7 @@
1//! RA Proc Macro Server 1//! RA Proc Macro Server
2//! 2//!
3//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. 3//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
4//! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander. 4//! The general idea here is based on <https://github.com/fedochet/rust-proc-macro-expander>.
5//! 5//!
6//! But we adapt it to better fit RA needs: 6//! But we adapt it to better fit RA needs:
7//! 7//!
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs b/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs
index dae6ff1d1..3b2afe01f 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Buffer management for same-process client<->server communication. 1//! lib-proc-macro Buffer management for same-process client<->server communication.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use std::io::{self, Write}; 6use std::io::{self, Write};
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
index b036d4e20..c135cf7a2 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Client-side types. 1//! lib-proc-macro Client-side types.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use super::*; 6use super::*;
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs b/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs
index 273a97715..f5b6d897e 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. 1//! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs# 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6#[repr(C)] 6#[repr(C)]
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs b/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs
index a2f77b5ac..d2a65d249 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Server-side handles and storage for per-handle data. 1//! lib-proc-macro Server-side handles and storage for per-handle data.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use std::collections::{BTreeMap, HashMap}; 6use std::collections::{BTreeMap, HashMap};
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
index e67902682..375396d1b 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Internal interface for communicating between a `proc_macro` client 1//! lib-proc-macro Internal interface for communicating between a `proc_macro` client
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/mod.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/mod.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5//! 5//!
6//! Internal interface for communicating between a `proc_macro` client 6//! Internal interface for communicating between a `proc_macro` client
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
index bd1e7c2fc..69928ec84 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Serialization for client-server communication. 1//! lib-proc-macro Serialization for client-server communication.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5//! 5//!
6//! Serialization for client-server communication. 6//! Serialization for client-server communication.
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs b/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs
index 6ef7ea43c..0436bc418 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro `Cell` variant for (scoped) existential lifetimes. 1//! lib-proc-macro `Cell` variant for (scoped) existential lifetimes.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use std::cell::Cell; 6use std::cell::Cell;
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
index 88fbdc078..cc9afd84b 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro server-side traits 1//! lib-proc-macro server-side traits
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use super::*; 6use super::*;
diff --git a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs b/crates/proc_macro_srv/src/proc_macro/diagnostic.rs
index 55d93917c..3c5b7bc01 100644
--- a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs
+++ b/crates/proc_macro_srv/src/proc_macro/diagnostic.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro diagnostic 1//! lib-proc-macro diagnostic
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use crate::proc_macro::Span; 6use crate::proc_macro::Span;
@@ -91,7 +91,7 @@ impl<'a> Iterator for Children<'a> {
91impl Diagnostic { 91impl Diagnostic {
92 /// Creates a new diagnostic with the given `level` and `message`. 92 /// Creates a new diagnostic with the given `level` and `message`.
93 pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { 93 pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
94 Diagnostic { level: level, message: message.into(), spans: vec![], children: vec![] } 94 Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
95 } 95 }
96 96
97 /// Creates a new diagnostic with the given `level` and `message` pointing to 97 /// Creates a new diagnostic with the given `level` and `message` pointing to
@@ -101,12 +101,7 @@ impl Diagnostic {
101 S: MultiSpan, 101 S: MultiSpan,
102 T: Into<String>, 102 T: Into<String>,
103 { 103 {
104 Diagnostic { 104 Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] }
105 level: level,
106 message: message.into(),
107 spans: spans.into_spans(),
108 children: vec![],
109 }
110 } 105 }
111 106
112 diagnostic_child_methods!(span_error, error, Level::Error); 107 diagnostic_child_methods!(span_error, error, Level::Error);
diff --git a/crates/proc_macro_srv/src/proc_macro/mod.rs b/crates/proc_macro_srv/src/proc_macro/mod.rs
index fc6e7344f..b7c1d04b5 100644
--- a/crates/proc_macro_srv/src/proc_macro/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/mod.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro main module 1//! lib-proc-macro main module
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/lib.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/lib.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6// NOTE(@edwin0cheng): 6// NOTE(@edwin0cheng):
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index 5d765f6e2..65ca3eb6c 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -1,6 +1,6 @@
1//! Rustc proc-macro server implementation with tt 1//! Rustc proc-macro server implementation with tt
2//! 2//!
3//! Based on idea from https://github.com/fedochet/rust-proc-macro-expander 3//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that 4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
5//! we could provide any TokenStream implementation. 5//! we could provide any TokenStream implementation.
6//! The original idea from fedochet is using proc-macro2 as backend, 6//! The original idea from fedochet is using proc-macro2 as backend,
@@ -539,7 +539,7 @@ impl server::Literal for Rustc {
539 } else { 539 } else {
540 n.parse::<u128>().unwrap().to_string() 540 n.parse::<u128>().unwrap().to_string()
541 }; 541 };
542 return Literal { text: n.into(), id: tt::TokenId::unspecified() }; 542 Literal { text: n.into(), id: tt::TokenId::unspecified() }
543 } 543 }
544 544
545 fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { 545 fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs
index 7cd01a7df..5ea5039db 100644
--- a/crates/profile/src/lib.rs
+++ b/crates/profile/src/lib.rs
@@ -50,10 +50,10 @@ impl Drop for Scope {
50/// A wrapper around google_cpu_profiler. 50/// A wrapper around google_cpu_profiler.
51/// 51///
52/// Usage: 52/// Usage:
53/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. 53/// 1. Install gpref_tools (<https://github.com/gperftools/gperftools>), probably packaged with your Linux distro.
54/// 2. Build with `cpu_profiler` feature. 54/// 2. Build with `cpu_profiler` feature.
55/// 3. Run the code, the *raw* output would be in the `./out.profile` file. 55/// 3. Run the code, the *raw* output would be in the `./out.profile` file.
56/// 4. Install pprof for visualization (https://github.com/google/pprof). 56/// 4. Install pprof for visualization (<https://github.com/google/pprof>).
57/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` 57/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
58/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. 58/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
59/// 59///
@@ -75,7 +75,7 @@ impl Drop for Scope {
75/// 75///
76/// See this diff for how to profile completions: 76/// See this diff for how to profile completions:
77/// 77///
78/// https://github.com/rust-analyzer/rust-analyzer/pull/5306 78/// <https://github.com/rust-analyzer/rust-analyzer/pull/5306>
79#[derive(Debug)] 79#[derive(Debug)]
80pub struct CpuSpan { 80pub struct CpuSpan {
81 _private: (), 81 _private: (),
diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs
index 6ef58c9c1..fbcb9e3c2 100644
--- a/crates/profile/src/memory_usage.rs
+++ b/crates/profile/src/memory_usage.rs
@@ -32,9 +32,7 @@ impl MemoryUsage {
32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), 32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
33 } 33 }
34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { 34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
35 // Note: This is incredibly slow. 35 memusage_linux()
36 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
37 MemoryUsage { allocated: Bytes(alloc) }
38 } else if #[cfg(windows)] { 36 } else if #[cfg(windows)] {
39 // There doesn't seem to be an API for determining heap usage, so we try to 37 // There doesn't seem to be an API for determining heap usage, so we try to
40 // approximate that by using the Commit Charge value. 38 // approximate that by using the Commit Charge value.
@@ -58,6 +56,37 @@ impl MemoryUsage {
58 } 56 }
59} 57}
60 58
59#[cfg(all(target_os = "linux", target_env = "gnu", not(feature = "jemalloc")))]
60fn memusage_linux() -> MemoryUsage {
61 // Linux/glibc has 2 APIs for allocator introspection that we can use: mallinfo and mallinfo2.
62 // mallinfo uses `int` fields and cannot handle memory usage exceeding 2 GB.
63 // mallinfo2 is very recent, so its presence needs to be detected at runtime.
64 // Both are abysmally slow.
65
66 use std::ffi::CStr;
67 use std::sync::atomic::{AtomicUsize, Ordering};
68
69 static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
70
71 let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
72 if mallinfo2 == 1 {
73 let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap();
74 mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize;
75 // NB: races don't matter here, since they'll always store the same value
76 MALLINFO2.store(mallinfo2, Ordering::Relaxed);
77 }
78
79 if mallinfo2 == 0 {
80 // mallinfo2 does not exist, use mallinfo.
81 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
82 MemoryUsage { allocated: Bytes(alloc) }
83 } else {
84 let mallinfo2: fn() -> libc::mallinfo2 = unsafe { std::mem::transmute(mallinfo2) };
85 let alloc = mallinfo2().uordblks as isize;
86 MemoryUsage { allocated: Bytes(alloc) }
87 }
88}
89
61#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] 90#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
62pub struct Bytes(isize); 91pub struct Bytes(isize);
63 92
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 33a4f8168..53cb4bae7 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -184,7 +184,7 @@ impl WorkspaceBuildData {
184 184
185 // Copy-pasted from existing cargo_metadata. It seems like we 185 // Copy-pasted from existing cargo_metadata. It seems like we
186 // should be using sered_stacker here? 186 // should be using sered_stacker here?
187 let mut deserializer = serde_json::Deserializer::from_str(&line); 187 let mut deserializer = serde_json::Deserializer::from_str(line);
188 deserializer.disable_recursion_limit(); 188 deserializer.disable_recursion_limit();
189 let message = Message::deserialize(&mut deserializer) 189 let message = Message::deserialize(&mut deserializer)
190 .unwrap_or(Message::TextLine(line.to_string())); 190 .unwrap_or(Message::TextLine(line.to_string()));
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index b8ad08364..ac079f83e 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -278,7 +278,7 @@ impl CargoWorkspace {
278 id, edition, name, manifest_path, version, metadata, .. 278 id, edition, name, manifest_path, version, metadata, ..
279 } = meta_pkg; 279 } = meta_pkg;
280 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default(); 280 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
281 let is_member = ws_members.contains(&id); 281 let is_member = ws_members.contains(id);
282 let edition = edition 282 let edition = edition
283 .parse::<Edition>() 283 .parse::<Edition>()
284 .with_context(|| format!("Failed to parse edition {}", edition))?; 284 .with_context(|| format!("Failed to parse edition {}", edition))?;
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index 4e39d6dd3..a22f79c15 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -142,12 +142,12 @@ fn discover_sysroot_src_dir(
142 log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); 142 log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
143 } 143 }
144 144
145 get_rust_src(&sysroot_path) 145 get_rust_src(sysroot_path)
146 .or_else(|| { 146 .or_else(|| {
147 let mut rustup = Command::new(toolchain::rustup()); 147 let mut rustup = Command::new(toolchain::rustup());
148 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); 148 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
149 utf8_stdout(rustup).ok()?; 149 utf8_stdout(rustup).ok()?;
150 get_rust_src(&sysroot_path) 150 get_rust_src(sysroot_path)
151 }) 151 })
152 .ok_or_else(|| { 152 .ok_or_else(|| {
153 format_err!( 153 format_err!(
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 84990075f..ef0f3c9e4 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -185,7 +185,7 @@ impl ProjectWorkspace {
185 185
186 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> { 186 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
187 let sysroot = Sysroot::discover( 187 let sysroot = Sysroot::discover(
188 &detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?, 188 detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
189 )?; 189 )?;
190 let rustc_cfg = rustc_cfg::get(None, None); 190 let rustc_cfg = rustc_cfg::get(None, None);
191 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) 191 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
@@ -324,7 +324,7 @@ impl ProjectWorkspace {
324 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { 324 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
325 match self { 325 match self {
326 ProjectWorkspace::Cargo { cargo, .. } => { 326 ProjectWorkspace::Cargo { cargo, .. } => {
327 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); 327 collector.add_config(cargo.workspace_root(), cargo.build_data_config().clone());
328 } 328 }
329 _ => {} 329 _ => {}
330 } 330 }
@@ -348,7 +348,7 @@ fn project_json_to_crate_graph(
348 .crates() 348 .crates()
349 .filter_map(|(crate_id, krate)| { 349 .filter_map(|(crate_id, krate)| {
350 let file_path = &krate.root_module; 350 let file_path = &krate.root_module;
351 let file_id = load(&file_path)?; 351 let file_id = load(file_path)?;
352 Some((crate_id, krate, file_id)) 352 Some((crate_id, krate, file_id))
353 }) 353 })
354 .map(|(crate_id, krate, file_id)| { 354 .map(|(crate_id, krate, file_id)| {
@@ -534,7 +534,7 @@ fn detached_files_to_crate_graph(
534 cfg_options.extend(rustc_cfg); 534 cfg_options.extend(rustc_cfg);
535 535
536 for detached_file in detached_files { 536 for detached_file in detached_files {
537 let file_id = match load(&detached_file) { 537 let file_id = match load(detached_file) {
538 Some(file_id) => file_id, 538 Some(file_id) => file_id,
539 None => { 539 None => {
540 log::error!("Failed to load detached file {:?}", detached_file); 540 log::error!("Failed to load detached file {:?}", detached_file);
@@ -602,7 +602,7 @@ fn handle_rustc_crates(
602 crate_graph, 602 crate_graph,
603 &rustc_workspace[pkg], 603 &rustc_workspace[pkg],
604 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), 604 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
605 &cfg_options, 605 cfg_options,
606 proc_macro_loader, 606 proc_macro_loader,
607 file_id, 607 file_id,
608 &rustc_workspace[tgt].name, 608 &rustc_workspace[tgt].name,
@@ -685,7 +685,7 @@ fn add_target_crate_root(
685 let proc_macro = build_data 685 let proc_macro = build_data
686 .as_ref() 686 .as_ref()
687 .and_then(|it| it.proc_macro_dylib_path.as_ref()) 687 .and_then(|it| it.proc_macro_dylib_path.as_ref())
688 .map(|it| proc_macro_loader(&it)) 688 .map(|it| proc_macro_loader(it))
689 .unwrap_or_default(); 689 .unwrap_or_default();
690 690
691 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); 691 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index f4cd43448..5d8547152 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -123,7 +123,7 @@ impl CargoTargetSpec {
123 let res = CargoTargetSpec { 123 let res = CargoTargetSpec {
124 workspace_root: cargo_ws.workspace_root().to_path_buf(), 124 workspace_root: cargo_ws.workspace_root().to_path_buf(),
125 cargo_toml: package_data.manifest.clone(), 125 cargo_toml: package_data.manifest.clone(),
126 package: cargo_ws.package_flag(&package_data), 126 package: cargo_ws.package_flag(package_data),
127 target: target_data.name.clone(), 127 target: target_data.name.clone(),
128 target_kind: target_data.kind, 128 target_kind: target_data.kind,
129 }; 129 };
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 19cb1c046..b5f5519b4 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -126,7 +126,7 @@ fn load_crate_graph(
126 } 126 }
127 } 127 }
128 } 128 }
129 let source_roots = source_root_config.partition(&vfs); 129 let source_roots = source_root_config.partition(vfs);
130 analysis_change.set_roots(source_roots); 130 analysis_change.set_roots(source_roots);
131 131
132 analysis_change.set_crate_graph(crate_graph); 132 analysis_change.set_crate_graph(crate_graph);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 91a9b55a8..c4757a1cb 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1064,8 +1064,8 @@ mod tests {
1064 let package_json_path = project_root().join("editors/code/package.json"); 1064 let package_json_path = project_root().join("editors/code/package.json");
1065 let mut package_json = fs::read_to_string(&package_json_path).unwrap(); 1065 let mut package_json = fs::read_to_string(&package_json_path).unwrap();
1066 1066
1067 let start_marker = " \"$generated-start\": false,\n"; 1067 let start_marker = " \"$generated-start\": {},\n";
1068 let end_marker = " \"$generated-end\": false\n"; 1068 let end_marker = " \"$generated-end\": {}\n";
1069 1069
1070 let start = package_json.find(start_marker).unwrap() + start_marker.len(); 1070 let start = package_json.find(start_marker).unwrap() + start_marker.len();
1071 let end = package_json.find(end_marker).unwrap(); 1071 let end = package_json.find(end_marker).unwrap();
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index d4b9db362..2f63c26ce 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -47,7 +47,7 @@ impl DiagnosticCollection {
47 ) { 47 ) {
48 let diagnostics = self.check.entry(file_id).or_default(); 48 let diagnostics = self.check.entry(file_id).or_default();
49 for existing_diagnostic in diagnostics.iter() { 49 for existing_diagnostic in diagnostics.iter() {
50 if are_diagnostics_equal(&existing_diagnostic, &diagnostic) { 50 if are_diagnostics_equal(existing_diagnostic, &diagnostic) {
51 return; 51 return;
52 } 52 }
53 } 53 }
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 82dd0da9a..8594d923c 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -224,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
224 224
225 let mut message = rd.message.clone(); 225 let mut message = rd.message.clone();
226 for child in &rd.children { 226 for child in &rd.children {
227 let child = map_rust_child_diagnostic(config, workspace_root, &child); 227 let child = map_rust_child_diagnostic(config, workspace_root, child);
228 match child { 228 match child {
229 MappedRustChildDiagnostic::SubDiagnostic(sub) => { 229 MappedRustChildDiagnostic::SubDiagnostic(sub) => {
230 subdiagnostics.push(sub); 230 subdiagnostics.push(sub);
@@ -268,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
268 primary_spans 268 primary_spans
269 .iter() 269 .iter()
270 .flat_map(|primary_span| { 270 .flat_map(|primary_span| {
271 let primary_location = primary_location(config, workspace_root, &primary_span); 271 let primary_location = primary_location(config, workspace_root, primary_span);
272 272
273 let mut message = message.clone(); 273 let mut message = message.clone();
274 if needs_primary_span_label { 274 if needs_primary_span_label {
@@ -298,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
298 // generated that code. 298 // generated that code.
299 let is_in_macro_call = i != 0; 299 let is_in_macro_call = i != 0;
300 300
301 let secondary_location = location(config, workspace_root, &span); 301 let secondary_location = location(config, workspace_root, span);
302 if secondary_location == primary_location { 302 if secondary_location == primary_location {
303 continue; 303 continue;
304 } 304 }
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index 2011a4132..5991e72b5 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -104,7 +104,7 @@ impl<'a> RequestDispatcher<'a> {
104 104
105 let res = crate::from_json(R::METHOD, req.params); 105 let res = crate::from_json(R::METHOD, req.params);
106 match res { 106 match res {
107 Ok(params) => return Some((req.id, params)), 107 Ok(params) => Some((req.id, params)),
108 Err(err) => { 108 Err(err) => {
109 let response = lsp_server::Response::new_err( 109 let response = lsp_server::Response::new_err(
110 req.id, 110 req.id,
@@ -112,7 +112,7 @@ impl<'a> RequestDispatcher<'a> {
112 err.to_string(), 112 err.to_string(),
113 ); 113 );
114 self.global_state.respond(response); 114 self.global_state.respond(response);
115 return None; 115 None
116 } 116 }
117 } 117 }
118 } 118 }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 582a89667..583900cfe 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -194,7 +194,7 @@ impl GlobalState {
194 change.change_file(file.file_id, text); 194 change.change_file(file.file_id, text);
195 } 195 }
196 if has_fs_changes { 196 if has_fs_changes {
197 let roots = self.source_root_config.partition(&vfs); 197 let roots = self.source_root_config.partition(vfs);
198 change.set_roots(roots); 198 change.set_roots(roots);
199 } 199 }
200 change 200 change
@@ -291,7 +291,7 @@ impl GlobalStateSnapshot {
291 } 291 }
292 292
293 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { 293 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
294 let path = from_proto::vfs_path(&url).ok()?; 294 let path = from_proto::vfs_path(url).ok()?;
295 Some(self.mem_docs.get(&path)?.version) 295 Some(self.mem_docs.get(&path)?.version)
296 } 296 }
297 297
@@ -300,7 +300,7 @@ impl GlobalStateSnapshot {
300 base.pop(); 300 base.pop();
301 let path = base.join(&path.path).unwrap(); 301 let path = base.join(&path.path).unwrap();
302 let path = path.as_path().unwrap(); 302 let path = path.as_path().unwrap();
303 url_from_abs_path(&path) 303 url_from_abs_path(path)
304 } 304 }
305 305
306 pub(crate) fn cargo_target_for_crate_root( 306 pub(crate) fn cargo_target_for_crate_root(
@@ -312,7 +312,7 @@ impl GlobalStateSnapshot {
312 let path = path.as_path()?; 312 let path = path.as_path()?;
313 self.workspaces.iter().find_map(|ws| match ws { 313 self.workspaces.iter().find_map(|ws| match ws {
314 ProjectWorkspace::Cargo { cargo, .. } => { 314 ProjectWorkspace::Cargo { cargo, .. } => {
315 cargo.target_by_root(&path).map(|it| (cargo, it)) 315 cargo.target_by_root(path).map(|it| (cargo, it))
316 } 316 }
317 ProjectWorkspace::Json { .. } => None, 317 ProjectWorkspace::Json { .. } => None,
318 ProjectWorkspace::DetachedFiles { .. } => None, 318 ProjectWorkspace::DetachedFiles { .. } => None,
@@ -323,7 +323,7 @@ impl GlobalStateSnapshot {
323pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { 323pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
324 let path = vfs.file_path(id); 324 let path = vfs.file_path(id);
325 let path = path.as_path().unwrap(); 325 let path = path.as_path().unwrap();
326 url_from_abs_path(&path) 326 url_from_abs_path(path)
327} 327}
328 328
329pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> { 329pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 40dd0da3e..ccf66294f 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -781,7 +781,7 @@ pub(crate) fn handle_completion_resolve(
781 let resolve_data = match original_completion 781 let resolve_data = match original_completion
782 .data 782 .data
783 .take() 783 .take()
784 .map(|data| serde_json::from_value::<CompletionResolveData>(data)) 784 .map(serde_json::from_value::<CompletionResolveData>)
785 .transpose()? 785 .transpose()?
786 { 786 {
787 Some(data) => data, 787 Some(data) => data,
@@ -1229,14 +1229,13 @@ pub(crate) fn publish_diagnostics(
1229 .map(|d| Diagnostic { 1229 .map(|d| Diagnostic {
1230 range: to_proto::range(&line_index, d.range), 1230 range: to_proto::range(&line_index, d.range),
1231 severity: Some(to_proto::diagnostic_severity(d.severity)), 1231 severity: Some(to_proto::diagnostic_severity(d.severity)),
1232 code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String), 1232 code: Some(NumberOrString::String(d.code.as_str().to_string())),
1233 code_description: d.code.and_then(|code| { 1233 code_description: Some(lsp_types::CodeDescription {
1234 lsp_types::Url::parse(&format!( 1234 href: lsp_types::Url::parse(&format!(
1235 "https://rust-analyzer.github.io/manual.html#{}", 1235 "https://rust-analyzer.github.io/manual.html#{}",
1236 code.as_str() 1236 d.code.as_str()
1237 )) 1237 ))
1238 .ok() 1238 .unwrap(),
1239 .map(|href| lsp_types::CodeDescription { href })
1240 }), 1239 }),
1241 source: Some("rust-analyzer".to_string()), 1240 source: Some("rust-analyzer".to_string()),
1242 message: d.message, 1241 message: d.message,
@@ -1396,7 +1395,7 @@ pub(crate) fn handle_semantic_tokens_full_delta(
1396 1395
1397 if let Some(prev_id) = &cached_tokens.result_id { 1396 if let Some(prev_id) = &cached_tokens.result_id {
1398 if *prev_id == params.previous_result_id { 1397 if *prev_id == params.previous_result_id {
1399 let delta = to_proto::semantic_token_delta(&cached_tokens, &semantic_tokens); 1398 let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
1400 *cached_tokens = semantic_tokens; 1399 *cached_tokens = semantic_tokens;
1401 return Ok(Some(delta.into())); 1400 return Ok(Some(delta.into()));
1402 } 1401 }
@@ -1540,7 +1539,7 @@ fn runnable_action_links(
1540 snap: &GlobalStateSnapshot, 1539 snap: &GlobalStateSnapshot,
1541 runnable: Runnable, 1540 runnable: Runnable,
1542) -> Option<lsp_ext::CommandLinkGroup> { 1541) -> Option<lsp_ext::CommandLinkGroup> {
1543 let cargo_spec = CargoTargetSpec::for_file(&snap, runnable.nav.file_id).ok()?; 1542 let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
1544 let hover_config = snap.config.hover(); 1543 let hover_config = snap.config.hover();
1545 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { 1544 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1546 return None; 1545 return None;
@@ -1624,7 +1623,7 @@ fn run_rustfmt(
1624 text_document: TextDocumentIdentifier, 1623 text_document: TextDocumentIdentifier,
1625 range: Option<lsp_types::Range>, 1624 range: Option<lsp_types::Range>,
1626) -> Result<Option<Vec<lsp_types::TextEdit>>> { 1625) -> Result<Option<Vec<lsp_types::TextEdit>>> {
1627 let file_id = from_proto::file_id(&snap, &text_document.uri)?; 1626 let file_id = from_proto::file_id(snap, &text_document.uri)?;
1628 let file = snap.analysis.file_text(file_id)?; 1627 let file = snap.analysis.file_text(file_id)?;
1629 let crate_ids = snap.analysis.crate_for(file_id)?; 1628 let crate_ids = snap.analysis.crate_for(file_id)?;
1630 1629
@@ -1671,7 +1670,7 @@ fn run_rustfmt(
1671 .into()); 1670 .into());
1672 } 1671 }
1673 1672
1674 let frange = from_proto::file_range(&snap, text_document, range)?; 1673 let frange = from_proto::file_range(snap, text_document, range)?;
1675 let start_line = line_index.index.line_col(frange.range.start()).line; 1674 let start_line = line_index.index.line_col(frange.range.start()).line;
1676 let end_line = line_index.index.line_col(frange.range.end()).line; 1675 let end_line = line_index.index.line_col(frange.range.end()).line;
1677 1676
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 8000b5490..087c26a71 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -124,7 +124,7 @@ pub(crate) fn apply_document_changes(
124 match change.range { 124 match change.range {
125 Some(range) => { 125 Some(range) => {
126 if !index_valid.covers(range.end.line) { 126 if !index_valid.covers(range.end.line) {
127 line_index.index = Arc::new(ide::LineIndex::new(&old_text)); 127 line_index.index = Arc::new(ide::LineIndex::new(old_text));
128 } 128 }
129 index_valid = IndexValid::UpToLineExclusive(range.start.line); 129 index_valid = IndexValid::UpToLineExclusive(range.start.line);
130 let range = from_proto::text_range(&line_index, range); 130 let range = from_proto::text_range(&line_index, range);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 31d8ea9e7..1417d8379 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -701,7 +701,7 @@ impl GlobalState {
701 }, 701 },
702 ); 702 );
703 703
704 return Ok(()); 704 Ok(())
705 })? 705 })?
706 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| { 706 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
707 for change in params.changes { 707 for change in params.changes {
@@ -740,7 +740,7 @@ impl GlobalState {
740 let subscriptions = self 740 let subscriptions = self
741 .mem_docs 741 .mem_docs
742 .keys() 742 .keys()
743 .map(|path| self.vfs.read().0.file_id(&path).unwrap()) 743 .map(|path| self.vfs.read().0.file_id(path).unwrap())
744 .collect::<Vec<_>>(); 744 .collect::<Vec<_>>();
745 745
746 log::trace!("updating notifications for {:?}", subscriptions); 746 log::trace!("updating notifications for {:?}", subscriptions);
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index db216d951..c20642231 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -112,7 +112,7 @@ impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
112 112
113/// Tokens are encoded relative to each other. 113/// Tokens are encoded relative to each other.
114/// 114///
115/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 115/// This is a direct port of <https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45>
116pub(crate) struct SemanticTokensBuilder { 116pub(crate) struct SemanticTokensBuilder {
117 id: String, 117 id: String,
118 prev_line: u32, 118 prev_line: u32,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 7428a3043..e53cd3c7b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -405,7 +405,7 @@ pub(crate) fn semantic_tokens(
405 text_range = 405 text_range =
406 TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); 406 TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n'));
407 } 407 }
408 let range = range(&line_index, text_range); 408 let range = range(line_index, text_range);
409 builder.push(range, token_index, modifier_bitset); 409 builder.push(range, token_index, modifier_bitset);
410 } 410 }
411 } 411 }
@@ -781,7 +781,7 @@ pub(crate) fn snippet_workspace_edit(
781 document_changes.extend_from_slice(&ops); 781 document_changes.extend_from_slice(&ops);
782 } 782 }
783 for (file_id, edit) in source_change.source_file_edits { 783 for (file_id, edit) in source_change.source_file_edits {
784 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; 784 let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?;
785 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 785 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
786 } 786 }
787 let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { 787 let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
@@ -957,7 +957,7 @@ pub(crate) fn code_lens(
957 let annotation_range = range(&line_index, annotation.range); 957 let annotation_range = range(&line_index, annotation.range);
958 958
959 let action = run.action(); 959 let action = run.action();
960 let r = runnable(&snap, run)?; 960 let r = runnable(snap, run)?;
961 961
962 let command = if debug { 962 let command = if debug {
963 command::debug_single(&r) 963 command::debug_single(&r)
@@ -1236,12 +1236,12 @@ fn main() {
1236 assert_eq!(folds.len(), 4); 1236 assert_eq!(folds.len(), 4);
1237 1237
1238 let line_index = LineIndex { 1238 let line_index = LineIndex {
1239 index: Arc::new(ide::LineIndex::new(&text)), 1239 index: Arc::new(ide::LineIndex::new(text)),
1240 endings: LineEndings::Unix, 1240 endings: LineEndings::Unix,
1241 encoding: OffsetEncoding::Utf16, 1241 encoding: OffsetEncoding::Utf16,
1242 }; 1242 };
1243 let converted: Vec<lsp_types::FoldingRange> = 1243 let converted: Vec<lsp_types::FoldingRange> =
1244 folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); 1244 folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
1245 1245
1246 let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; 1246 let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
1247 assert_eq!(converted.len(), expected_lines.len()); 1247 assert_eq!(converted.len(), expected_lines.len());
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 9e89209ea..3585132d4 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -493,7 +493,7 @@ fn preserves_dos_line_endings() {
493 } 493 }
494 494
495 let server = Project::with_fixture( 495 let server = Project::with_fixture(
496 &" 496 "
497//- /Cargo.toml 497//- /Cargo.toml
498[package] 498[package]
499name = \"foo\" 499name = \"foo\"
@@ -758,7 +758,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
758 ```rust 758 ```rust
759 fn bar() 759 fn bar()
760 ```"#]] 760 ```"#]]
761 .assert_eq(&value); 761 .assert_eq(value);
762} 762}
763 763
764#[test] 764#[test]
@@ -795,7 +795,7 @@ fn main() {}
795 795
796"#; 796"#;
797 let server = 797 let server =
798 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); 798 Project::with_fixture(code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
799 799
800 //rename same level file 800 //rename same level file
801 server.request::<WillRenameFiles>( 801 server.request::<WillRenameFiles>(
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 75e677762..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();
@@ -323,7 +325,7 @@ fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Valu
323 325
324 if !l.is_empty() { 326 if !l.is_empty() {
325 assert!(!r.is_empty()); 327 assert!(!r.is_empty());
326 Some((&l[0], &r[0])) 328 Some((l[0], r[0]))
327 } else { 329 } else {
328 assert_eq!(r.len(), 0); 330 assert_eq!(r.len(), 0);
329 None 331 None
diff --git a/crates/stdx/src/panic_context.rs b/crates/stdx/src/panic_context.rs
index 8d51e20d3..26debf3c4 100644
--- a/crates/stdx/src/panic_context.rs
+++ b/crates/stdx/src/panic_context.rs
@@ -1,6 +1,6 @@
1//! A micro-crate to enhance panic messages with context info. 1//! A micro-crate to enhance panic messages with context info.
2//! 2//!
3//! FIXME: upstream to https://github.com/kriomant/panic-context ? 3//! FIXME: upstream to <https://github.com/kriomant/panic-context> ?
4 4
5use std::{cell::RefCell, panic, sync::Once}; 5use std::{cell::RefCell, panic, sync::Once};
6 6
diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs
index b0fa12f76..692a2ab3d 100644
--- a/crates/stdx/src/process.rs
+++ b/crates/stdx/src/process.rs
@@ -1,7 +1,7 @@
1//! Read both stdout and stderr of child without deadlocks. 1//! Read both stdout and stderr of child without deadlocks.
2//! 2//!
3//! https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs 3//! <https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs>
4//! https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231 4//! <https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231>
5 5
6use std::{ 6use std::{
7 io, 7 io,
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 2106732cd..f1525a649 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -11,7 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = "2.0.0-pre.1"
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "=0.13.0-pre.6" 16rowan = "=0.13.0-pre.6"
17rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" }
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 19107ee38..2663c0759 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -30,7 +30,7 @@ impl ast::UseTree {
30 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { 30 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() {
31 make::path_unqualified(make::path_segment_self()) 31 make::path_unqualified(make::path_segment_self())
32 } else { 32 } else {
33 match split_path_prefix(&prefix) { 33 match split_path_prefix(prefix) {
34 Some(it) => it, 34 Some(it) => it,
35 None => return self.clone(), 35 None => return self.clone(),
36 } 36 }
@@ -95,7 +95,7 @@ impl fmt::Display for IndentLevel {
95 let indent = if len <= spaces.len() { 95 let indent = if len <= spaces.len() {
96 &spaces[..len] 96 &spaces[..len]
97 } else { 97 } else {
98 buf = iter::repeat(' ').take(len).collect::<String>(); 98 buf = " ".repeat(len);
99 &buf 99 &buf
100 }; 100 };
101 fmt::Display::fmt(indent, f) 101 fmt::Display::fmt(indent, f)
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/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 4b1e1ccee..ad52d9f54 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -242,7 +242,7 @@ impl ast::ByteString {
242 (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), 242 (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
243 (Ok(c), true) => { 243 (Ok(c), true) => {
244 buf.reserve_exact(text.len()); 244 buf.reserve_exact(text.len());
245 buf.extend_from_slice(&text[..char_range.start].as_bytes()); 245 buf.extend_from_slice(text[..char_range.start].as_bytes());
246 buf.push(c as u8); 246 buf.push(c as u8);
247 } 247 }
248 (Err(_), _) => has_error = true, 248 (Err(_), _) => has_error = true,
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 431ed0699..001921343 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -15,7 +15,7 @@ use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
15pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse}; 15pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse};
16 16
17pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { 17pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
18 let (tokens, lexer_errors) = tokenize(&text); 18 let (tokens, lexer_errors) = tokenize(text);
19 19
20 let mut token_source = TextTokenSource::new(text, &tokens); 20 let mut token_source = TextTokenSource::new(text, &tokens);
21 let mut tree_sink = TextTreeSink::new(text, &tokens); 21 let mut tree_sink = TextTreeSink::new(text, &tokens);
@@ -33,7 +33,7 @@ pub(crate) fn parse_text_fragment<T: AstNode>(
33 text: &str, 33 text: &str,
34 fragment_kind: parser::FragmentKind, 34 fragment_kind: parser::FragmentKind,
35) -> Result<T, ()> { 35) -> Result<T, ()> {
36 let (tokens, lexer_errors) = tokenize(&text); 36 let (tokens, lexer_errors) = tokenize(text);
37 if !lexer_errors.is_empty() { 37 if !lexer_errors.is_empty() {
38 return Err(()); 38 return Err(());
39 } 39 }
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index 7c8d0a4c4..ae4844e48 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -144,7 +144,7 @@ fn rustc_token_kind_to_syntax_kind(
144 } 144 }
145 145
146 rustc_lexer::TokenKind::RawIdent => IDENT, 146 rustc_lexer::TokenKind::RawIdent => IDENT,
147 rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(&kind), 147 rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(kind),
148 148
149 rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT, 149 rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT,
150 rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => { 150 rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => {
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 304f47b3d..186cc9e74 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -26,11 +26,11 @@ pub(crate) fn incremental_reparse(
26 edit: &Indel, 26 edit: &Indel,
27 errors: Vec<SyntaxError>, 27 errors: Vec<SyntaxError>,
28) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 28) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
29 if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { 29 if let Some((green, new_errors, old_range)) = reparse_token(node, edit) {
30 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); 30 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
31 } 31 }
32 32
33 if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) { 33 if let Some((green, new_errors, old_range)) = reparse_block(node, edit) {
34 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); 34 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
35 } 35 }
36 None 36 None
@@ -52,7 +52,7 @@ fn reparse_token(
52 } 52 }
53 } 53 }
54 54
55 let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit); 55 let mut new_text = get_text_after_edit(prev_token.clone().into(), edit);
56 let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?; 56 let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?;
57 57
58 if new_token_kind != prev_token_kind 58 if new_token_kind != prev_token_kind
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index 9f2426171..4961ca08d 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -69,13 +69,13 @@ fn parser_tests() {
69 dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], "rast", |text, path| { 69 dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], "rast", |text, path| {
70 let parse = SourceFile::parse(text); 70 let parse = SourceFile::parse(text);
71 let errors = parse.errors(); 71 let errors = parse.errors();
72 assert_errors_are_absent(&errors, path); 72 assert_errors_are_absent(errors, path);
73 parse.debug_dump() 73 parse.debug_dump()
74 }); 74 });
75 dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], "rast", |text, path| { 75 dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], "rast", |text, path| {
76 let parse = SourceFile::parse(text); 76 let parse = SourceFile::parse(text);
77 let errors = parse.errors(); 77 let errors = parse.errors();
78 assert_errors_are_present(&errors, path); 78 assert_errors_are_present(errors, path);
79 parse.debug_dump() 79 parse.debug_dump()
80 }); 80 });
81} 81}
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..005a5c092 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("//-") {
@@ -108,12 +129,22 @@ impl Fixture {
108 if line.starts_with("//-") { 129 if line.starts_with("//-") {
109 let meta = Fixture::parse_meta_line(line); 130 let meta = Fixture::parse_meta_line(line);
110 res.push(meta) 131 res.push(meta)
111 } else if let Some(entry) = res.last_mut() { 132 } else {
112 entry.text.push_str(line); 133 if line.starts_with("// ")
134 && line.contains(":")
135 && !line.contains("::")
136 && line.chars().all(|it| !it.is_uppercase())
137 {
138 panic!("looks like invalid metadata line: {:?}", line)
139 }
140
141 if let Some(entry) = res.last_mut() {
142 entry.text.push_str(line);
143 }
113 } 144 }
114 } 145 }
115 146
116 res 147 (mini_core, res)
117 } 148 }
118 149
119 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo 150 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -133,7 +164,9 @@ impl Fixture {
133 let mut env = FxHashMap::default(); 164 let mut env = FxHashMap::default();
134 let mut introduce_new_source_root = false; 165 let mut introduce_new_source_root = false;
135 for component in components[1..].iter() { 166 for component in components[1..].iter() {
136 let (key, value) = component.split_once(':').unwrap(); 167 let (key, value) = component
168 .split_once(':')
169 .unwrap_or_else(|| panic!("invalid meta line: {:?}", meta));
137 match key { 170 match key {
138 "crate" => krate = Some(value.to_string()), 171 "crate" => krate = Some(value.to_string()),
139 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), 172 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
@@ -172,6 +205,139 @@ impl Fixture {
172 } 205 }
173} 206}
174 207
208impl MiniCore {
209 fn has_flag(&self, flag: &str) -> bool {
210 self.activated_flags.iter().any(|it| it == flag)
211 }
212
213 #[track_caller]
214 fn assert_valid_flag(&self, flag: &str) {
215 if !self.valid_flags.iter().any(|it| it == flag) {
216 panic!("invalid flag: {:?}, valid flags: {:?}", flag, self.valid_flags);
217 }
218 }
219
220 fn parse(line: &str) -> MiniCore {
221 let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() };
222
223 let line = line.strip_prefix("//- minicore:").unwrap().trim();
224 for entry in line.split(", ") {
225 if res.has_flag(entry) {
226 panic!("duplicate minicore flag: {:?}", entry)
227 }
228 res.activated_flags.push(entry.to_string())
229 }
230
231 res
232 }
233
234 /// Strips parts of minicore.rs which are flagged by inactive flags.
235 ///
236 /// This is probably over-engineered to support flags dependencies.
237 pub fn source_code(mut self) -> String {
238 let mut buf = String::new();
239 let raw_mini_core = include_str!("./minicore.rs");
240 let mut lines = raw_mini_core.split_inclusive('\n');
241
242 let mut parsing_flags = false;
243 let mut implications = Vec::new();
244
245 // Parse `//!` preamble and extract flags and dependencies.
246 for line in lines.by_ref() {
247 let line = match line.strip_prefix("//!") {
248 Some(it) => it,
249 None => {
250 assert!(line.trim().is_empty());
251 break;
252 }
253 };
254
255 if parsing_flags {
256 let (flag, deps) = line.split_once(':').unwrap();
257 let flag = flag.trim();
258 self.valid_flags.push(flag.to_string());
259 for dep in deps.split(", ") {
260 let dep = dep.trim();
261 if !dep.is_empty() {
262 self.assert_valid_flag(dep);
263 implications.push((flag, dep));
264 }
265 }
266 }
267
268 if line.contains("Available flags:") {
269 parsing_flags = true;
270 }
271 }
272
273 for flag in &self.activated_flags {
274 self.assert_valid_flag(flag);
275 }
276
277 // Fixed point loop to compute transitive closure of flags.
278 loop {
279 let mut changed = false;
280 for &(u, v) in implications.iter() {
281 if self.has_flag(u) && !self.has_flag(v) {
282 self.activated_flags.push(v.to_string());
283 changed = true;
284 }
285 }
286 if !changed {
287 break;
288 }
289 }
290
291 let mut active_regions = Vec::new();
292 let mut seen_regions = Vec::new();
293 for line in lines {
294 let trimmed = line.trim();
295 if let Some(region) = trimmed.strip_prefix("// region:") {
296 active_regions.push(region);
297 continue;
298 }
299 if let Some(region) = trimmed.strip_prefix("// endregion:") {
300 let prev = active_regions.pop().unwrap();
301 assert_eq!(prev, region);
302 continue;
303 }
304
305 let mut line_region = false;
306 if let Some(idx) = trimmed.find("// :") {
307 line_region = true;
308 active_regions.push(&trimmed[idx + "// :".len()..]);
309 }
310
311 let mut keep = true;
312 for &region in &active_regions {
313 assert!(
314 !region.starts_with(' '),
315 "region marker starts with a space: {:?}",
316 region
317 );
318 self.assert_valid_flag(region);
319 seen_regions.push(region);
320 keep &= self.has_flag(region);
321 }
322
323 if keep {
324 buf.push_str(line)
325 }
326 if line_region {
327 active_regions.pop().unwrap();
328 }
329 }
330
331 for flag in &self.valid_flags {
332 if !seen_regions.iter().any(|it| it == flag) {
333 panic!("unused minicore flag: {:?}", flag);
334 }
335 }
336 format!("{}", buf);
337 buf
338 }
339}
340
175#[test] 341#[test]
176#[should_panic] 342#[should_panic]
177fn parse_fixture_checks_further_indented_metadata() { 343fn parse_fixture_checks_further_indented_metadata() {
@@ -189,12 +355,14 @@ fn parse_fixture_checks_further_indented_metadata() {
189 355
190#[test] 356#[test]
191fn parse_fixture_gets_full_meta() { 357fn parse_fixture_gets_full_meta() {
192 let parsed = Fixture::parse( 358 let (mini_core, parsed) = Fixture::parse(
193 r" 359 r#"
194 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo 360//- minicore: coerce_unsized
195 mod m; 361//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
196 ", 362mod m;
363"#,
197 ); 364 );
365 assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
198 assert_eq!(1, parsed.len()); 366 assert_eq!(1, parsed.len());
199 367
200 let meta = &parsed[0]; 368 let meta = &parsed[0];
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index ac5a9509d..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";
@@ -190,10 +193,21 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
190 res 193 res
191} 194}
192 195
193/// Extracts `//^ some text` annotations 196/// Extracts `//^^^ some text` annotations.
197///
198/// A run of `^^^` can be arbitrary long and points to the corresponding range
199/// in the line above.
200///
201/// The `// ^file text` syntax can be used to attach `text` to the entirety of
202/// the file.
203///
204/// Multiline string values are supported:
205///
206/// // ^^^ first line
207/// // | second line
194pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { 208pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
195 let mut res = Vec::new(); 209 let mut res = Vec::new();
196 let mut prev_line_start: Option<TextSize> = None; 210 let mut prev_line_start: Option<TextSize> = Some(0.into());
197 let mut line_start: TextSize = 0.into(); 211 let mut line_start: TextSize = 0.into();
198 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); 212 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
199 for line in text.split_inclusive('\n') { 213 for line in text.split_inclusive('\n') {
@@ -202,10 +216,15 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
202 let annotation_offset = TextSize::of(&line[..idx + "//".len()]); 216 let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
203 for annotation in extract_line_annotations(&line[idx + "//".len()..]) { 217 for annotation in extract_line_annotations(&line[idx + "//".len()..]) {
204 match annotation { 218 match annotation {
205 LineAnnotation::Annotation { mut range, content } => { 219 LineAnnotation::Annotation { mut range, content, file } => {
206 range += annotation_offset; 220 range += annotation_offset;
207 this_line_annotations.push((range.end(), res.len())); 221 this_line_annotations.push((range.end(), res.len()));
208 res.push((range + prev_line_start.unwrap(), content)) 222 let range = if file {
223 TextRange::up_to(TextSize::of(text))
224 } else {
225 range + prev_line_start.unwrap()
226 };
227 res.push((range, content))
209 } 228 }
210 LineAnnotation::Continuation { mut offset, content } => { 229 LineAnnotation::Continuation { mut offset, content } => {
211 offset += annotation_offset; 230 offset += annotation_offset;
@@ -226,11 +245,12 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
226 245
227 prev_line_annotations = this_line_annotations; 246 prev_line_annotations = this_line_annotations;
228 } 247 }
248
229 res 249 res
230} 250}
231 251
232enum LineAnnotation { 252enum LineAnnotation {
233 Annotation { range: TextRange, content: String }, 253 Annotation { range: TextRange, content: String, file: bool },
234 Continuation { offset: TextSize, content: String }, 254 Continuation { offset: TextSize, content: String },
235} 255}
236 256
@@ -251,12 +271,20 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
251 } 271 }
252 let range = TextRange::at(offset, len.try_into().unwrap()); 272 let range = TextRange::at(offset, len.try_into().unwrap());
253 let next = line[len..].find(marker).map_or(line.len(), |it| it + len); 273 let next = line[len..].find(marker).map_or(line.len(), |it| it + len);
254 let content = line[len..][..next - len].trim().to_string(); 274 let mut content = &line[len..][..next - len];
275
276 let mut file = false;
277 if !continuation && content.starts_with("file") {
278 file = true;
279 content = &content["file".len()..]
280 }
281
282 let content = content.trim().to_string();
255 283
256 let annotation = if continuation { 284 let annotation = if continuation {
257 LineAnnotation::Continuation { offset: range.end(), content } 285 LineAnnotation::Continuation { offset: range.end(), content }
258 } else { 286 } else {
259 LineAnnotation::Annotation { range, content } 287 LineAnnotation::Annotation { range, content, file }
260 }; 288 };
261 res.push(annotation); 289 res.push(annotation);
262 290
@@ -277,16 +305,20 @@ fn main() {
277 zoo + 1 305 zoo + 1
278} //^^^ type: 306} //^^^ type:
279 // | i32 307 // | i32
308
309// ^file
280 "#, 310 "#,
281 ); 311 );
282 let res = extract_annotations(&text) 312 let res = extract_annotations(&text)
283 .into_iter() 313 .into_iter()
284 .map(|(range, ann)| (&text[range], ann)) 314 .map(|(range, ann)| (&text[range], ann))
285 .collect::<Vec<_>>(); 315 .collect::<Vec<_>>();
316
286 assert_eq!( 317 assert_eq!(
287 res, 318 res[..3],
288 vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),] 319 [("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into())]
289 ); 320 );
321 assert_eq!(res[3].0.len(), 115);
290} 322}
291 323
292/// Returns `false` if slow tests should not run, otherwise returns `true` and 324/// Returns `false` if slow tests should not run, otherwise returns `true` and
diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs
new file mode 100644
index 000000000..e04ca58d2
--- /dev/null
+++ b/crates/test_utils/src/minicore.rs
@@ -0,0 +1,234 @@
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//! unsize: sized
13//! coerce_unsized: unsize
14//! slice:
15//! range:
16//! deref: sized
17//! deref_mut: deref
18//! fn:
19//! pin:
20//! future: pin
21//! option:
22//! result:
23
24pub mod marker {
25 // region:sized
26 #[lang = "sized"]
27 #[fundamental]
28 #[rustc_specialization_trait]
29 pub trait Sized {}
30 // endregion:sized
31
32 // region:unsize
33 #[lang = "unsize"]
34 pub trait Unsize<T: ?Sized> {}
35 // endregion:unsize
36}
37
38pub mod ops {
39 // region:coerce_unsized
40 mod unsize {
41 use crate::marker::Unsize;
42
43 #[lang = "coerce_unsized"]
44 pub trait CoerceUnsized<T: ?Sized> {}
45
46 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
47 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
48 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
49 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
50
51 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
52 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
53
54 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
55 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
56 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
57 }
58 pub use self::unsize::CoerceUnsized;
59 // endregion:coerce_unsized
60
61 // region:deref
62 mod deref {
63 #[lang = "deref"]
64 pub trait Deref {
65 #[lang = "deref_target"]
66 type Target: ?Sized;
67 fn deref(&self) -> &Self::Target;
68 }
69 // region:deref_mut
70 #[lang = "deref_mut"]
71 pub trait DerefMut: Deref {
72 fn deref_mut(&mut self) -> &mut Self::Target;
73 }
74 // endregion:deref_mut
75 }
76 pub use self::deref::Deref;
77 pub use self::deref::DerefMut; //:deref_mut
78 // endregion:deref
79
80 // region:range
81 mod range {
82 #[lang = "RangeFull"]
83 pub struct RangeFull;
84
85 #[lang = "Range"]
86 pub struct Range<Idx> {
87 pub start: Idx,
88 pub end: Idx,
89 }
90
91 #[lang = "RangeFrom"]
92 pub struct RangeFrom<Idx> {
93 pub start: Idx,
94 }
95
96 #[lang = "RangeTo"]
97 pub struct RangeTo<Idx> {
98 pub end: Idx,
99 }
100
101 #[lang = "RangeInclusive"]
102 pub struct RangeInclusive<Idx> {
103 pub(crate) start: Idx,
104 pub(crate) end: Idx,
105 pub(crate) exhausted: bool,
106 }
107
108 #[lang = "RangeToInclusive"]
109 pub struct RangeToInclusive<Idx> {
110 pub end: Idx,
111 }
112 }
113 pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
114 pub use self::range::{RangeInclusive, RangeToInclusive};
115 // endregion:range
116
117 // region:fn
118 mod function {
119 #[lang = "fn"]
120 #[fundamental]
121 pub trait Fn<Args>: FnMut<Args> {}
122
123 #[lang = "fn_mut"]
124 #[fundamental]
125 pub trait FnMut<Args>: FnOnce<Args> {}
126
127 #[lang = "fn_once"]
128 #[fundamental]
129 pub trait FnOnce<Args> {
130 #[lang = "fn_once_output"]
131 type Output;
132 }
133 }
134 pub use self::function::{Fn, FnMut, FnOnce};
135 // endregion:fn
136}
137
138// region:slice
139pub mod slice {
140 #[lang = "slice"]
141 impl<T> [T] {
142 pub fn len(&self) -> usize {
143 loop {}
144 }
145 }
146}
147// endregion:slice
148
149// region:option
150pub mod option {
151 pub enum Option<T> {
152 #[lang = "None"]
153 None,
154 #[lang = "Some"]
155 Some(T),
156 }
157}
158// endregion:option
159
160// region:result
161pub mod result {
162 pub enum Result<T, E> {
163 #[lang = "Ok"]
164 Ok(T),
165 #[lang = "Err"]
166 Err(E),
167 }
168}
169// endregion:result
170
171// region:pin
172pub mod pin {
173 #[lang = "pin"]
174 #[fundamental]
175 pub struct Pin<P> {
176 pointer: P,
177 }
178}
179// endregion:pin
180
181// region:future
182pub mod future {
183 use crate::{
184 pin::Pin,
185 task::{Context, Poll},
186 };
187
188 #[lang = "future_trait"]
189 pub trait Future {
190 type Output;
191 #[lang = "poll"]
192 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
193 }
194}
195pub mod task {
196 pub enum Poll<T> {
197 #[lang = "Ready"]
198 Ready(T),
199 #[lang = "Pending"]
200 Pending,
201 }
202
203 pub struct Context<'a> {
204 waker: &'a (),
205 }
206}
207// endregion:future
208
209pub mod prelude {
210 pub mod v1 {
211 pub use crate::{
212 marker::Sized, // :sized
213 ops::{Fn, FnMut, FnOnce}, // :fn
214 option::Option::{self, None, Some}, // :option
215 result::Result::{self, Err, Ok}, // :result
216 };
217 }
218
219 pub mod rust_2015 {
220 pub use super::v1::*;
221 }
222
223 pub mod rust_2018 {
224 pub use super::v1::*;
225 }
226
227 pub mod rust_2021 {
228 pub use super::v1::*;
229 }
230}
231
232#[prelude_import]
233#[allow(unused)]
234use prelude::v1::*;
diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs
index e0021039a..dc577700f 100644
--- a/crates/tt/src/buffer.rs
+++ b/crates/tt/src/buffer.rs
@@ -133,7 +133,7 @@ impl<'a> TokenTreeRef<'a> {
133 } 133 }
134} 134}
135 135
136/// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 136/// A safe version of `Cursor` from `syn` crate <https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125>
137#[derive(Copy, Clone, Debug)] 137#[derive(Copy, Clone, Debug)]
138pub struct Cursor<'a> { 138pub struct Cursor<'a> {
139 buffer: &'a TokenBuffer<'a>, 139 buffer: &'a TokenBuffer<'a>,
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index bed44d600..b51ad5d0f 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -88,7 +88,7 @@ pub struct Ident {
88} 88}
89 89
90fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result { 90fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result {
91 let align = std::iter::repeat(" ").take(level).collect::<String>(); 91 let align = " ".repeat(level);
92 92
93 let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) { 93 let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) {
94 None => "$".to_string(), 94 None => "$".to_string(),
@@ -113,7 +113,7 @@ fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usi
113} 113}
114 114
115fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result { 115fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result {
116 let align = std::iter::repeat(" ").take(level).collect::<String>(); 116 let align = " ".repeat(level);
117 117
118 match tkn { 118 match tkn {
119 TokenTree::Leaf(leaf) => match leaf { 119 TokenTree::Leaf(leaf) => match leaf {
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 0a4590c8d..0011f73c9 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -111,7 +111,7 @@ impl FileSetConfig {
111 let mut scratch_space = Vec::new(); 111 let mut scratch_space = Vec::new();
112 let mut res = vec![FileSet::default(); self.len()]; 112 let mut res = vec![FileSet::default(); self.len()];
113 for (file_id, path) in vfs.iter() { 113 for (file_id, path) in vfs.iter() {
114 let root = self.classify(&path, &mut scratch_space); 114 let root = self.classify(path, &mut scratch_space);
115 res[root].insert(file_id, path.clone()) 115 res[root].insert(file_id, path.clone())
116 } 116 }
117 res 117 res