aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock50
-rw-r--r--crates/assists/Cargo.toml2
-rw-r--r--crates/assists/src/assist_config.rs3
-rw-r--r--crates/assists/src/ast_transform.rs3
-rw-r--r--crates/assists/src/handlers/auto_import.rs5
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs16
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs9
-rw-r--r--crates/assists/src/handlers/generate_from_impl_for_enum.rs3
-rw-r--r--crates/assists/src/handlers/infer_function_return_type.rs24
-rw-r--r--crates/assists/src/handlers/merge_imports.rs6
-rw-r--r--crates/assists/src/handlers/qualify_path.rs2
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs4
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs8
-rw-r--r--crates/assists/src/handlers/toggle_ignore.rs (renamed from crates/assists/src/handlers/ignore_test.rs)23
-rw-r--r--crates/assists/src/lib.rs4
-rw-r--r--crates/assists/src/tests/generated.rs40
-rw-r--r--crates/assists/src/utils.rs199
-rw-r--r--crates/completion/Cargo.toml1
-rw-r--r--crates/completion/src/completions/postfix.rs86
-rw-r--r--crates/completion/src/completions/qualified_path.rs40
-rw-r--r--crates/completion/src/completions/record.rs4
-rw-r--r--crates/completion/src/completions/trait_impl.rs2
-rw-r--r--crates/completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/completion/src/config.rs2
-rw-r--r--crates/completion/src/item.rs38
-rw-r--r--crates/completion/src/lib.rs2
-rw-r--r--crates/completion/src/render.rs25
-rw-r--r--crates/completion/src/render/builder_ext.rs5
-rw-r--r--crates/completion/src/render/enum_variant.rs15
-rw-r--r--crates/completion/src/render/function.rs86
-rw-r--r--crates/completion/src/render/macro_.rs17
-rw-r--r--crates/hir/src/code_model.rs16
-rw-r--r--crates/hir/src/diagnostics.rs2
-rw-r--r--crates/hir_def/src/body.rs73
-rw-r--r--crates/hir_def/src/body/diagnostics.rs10
-rw-r--r--crates/hir_def/src/body/lower.rs31
-rw-r--r--crates/hir_def/src/data.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs67
-rw-r--r--crates/hir_def/src/nameres.rs87
-rw-r--r--crates/hir_def/src/nameres/collector.rs27
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs21
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs129
-rw-r--r--crates/hir_expand/src/db.rs17
-rw-r--r--crates/hir_expand/src/diagnostics.rs9
-rw-r--r--crates/hir_expand/src/eager.rs2
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs2
-rw-r--r--crates/ide/src/diagnostics.rs9
-rw-r--r--crates/ide/src/goto_definition.rs64
-rw-r--r--crates/ide/src/hover.rs165
-rw-r--r--crates/ide/src/inlay_hints.rs4
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/references.rs103
-rw-r--r--crates/ide/src/references/rename.rs166
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html10
-rw-r--r--crates/ide_db/Cargo.toml3
-rw-r--r--crates/ide_db/src/helpers.rs203
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs (renamed from crates/assists/src/utils/insert_use.rs)20
-rw-r--r--crates/ide_db/src/lib.rs1
-rw-r--r--crates/ide_db/src/search.rs1
-rw-r--r--crates/mbe/src/lib.rs2
-rw-r--r--crates/rust-analyzer/Cargo.toml4
-rw-r--r--crates/rust-analyzer/src/config.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs104
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs2
-rw-r--r--crates/syntax/Cargo.toml3
-rw-r--r--crates/syntax/src/algo.rs13
-rw-r--r--crates/syntax/src/ast/token_ext.rs27
-rw-r--r--crates/vfs/src/file_set.rs51
-rw-r--r--crates/vfs/src/file_set/tests.rs42
-rw-r--r--crates/vfs/src/vfs_path.rs33
-rw-r--r--crates/vfs/src/vfs_path/tests.rs30
-rw-r--r--docs/user/generated_diagnostic.adoc14
78 files changed, 1606 insertions, 730 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 051d9e734..d8838cf2b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -253,7 +253,6 @@ dependencies = [
253name = "completion" 253name = "completion"
254version = "0.0.0" 254version = "0.0.0"
255dependencies = [ 255dependencies = [
256 "assists",
257 "base_db", 256 "base_db",
258 "either", 257 "either",
259 "expect-test", 258 "expect-test",
@@ -301,7 +300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
301checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" 300checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
302dependencies = [ 301dependencies = [
303 "cfg-if 1.0.0", 302 "cfg-if 1.0.0",
304 "crossbeam-utils 0.8.0", 303 "crossbeam-utils 0.8.1",
305] 304]
306 305
307[[package]] 306[[package]]
@@ -312,18 +311,18 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
312dependencies = [ 311dependencies = [
313 "cfg-if 1.0.0", 312 "cfg-if 1.0.0",
314 "crossbeam-epoch", 313 "crossbeam-epoch",
315 "crossbeam-utils 0.8.0", 314 "crossbeam-utils 0.8.1",
316] 315]
317 316
318[[package]] 317[[package]]
319name = "crossbeam-epoch" 318name = "crossbeam-epoch"
320version = "0.9.0" 319version = "0.9.1"
321source = "registry+https://github.com/rust-lang/crates.io-index" 320source = "registry+https://github.com/rust-lang/crates.io-index"
322checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f" 321checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
323dependencies = [ 322dependencies = [
324 "cfg-if 1.0.0", 323 "cfg-if 1.0.0",
325 "const_fn", 324 "const_fn",
326 "crossbeam-utils 0.8.0", 325 "crossbeam-utils 0.8.1",
327 "lazy_static", 326 "lazy_static",
328 "memoffset", 327 "memoffset",
329 "scopeguard", 328 "scopeguard",
@@ -342,13 +341,12 @@ dependencies = [
342 341
343[[package]] 342[[package]]
344name = "crossbeam-utils" 343name = "crossbeam-utils"
345version = "0.8.0" 344version = "0.8.1"
346source = "registry+https://github.com/rust-lang/crates.io-index" 345source = "registry+https://github.com/rust-lang/crates.io-index"
347checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5" 346checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
348dependencies = [ 347dependencies = [
349 "autocfg", 348 "autocfg",
350 "cfg-if 1.0.0", 349 "cfg-if 1.0.0",
351 "const_fn",
352 "lazy_static", 350 "lazy_static",
353] 351]
354 352
@@ -682,6 +680,7 @@ dependencies = [
682 "expect-test", 680 "expect-test",
683 "fst", 681 "fst",
684 "hir", 682 "hir",
683 "itertools",
685 "log", 684 "log",
686 "once_cell", 685 "once_cell",
687 "profile", 686 "profile",
@@ -865,9 +864,9 @@ dependencies = [
865 864
866[[package]] 865[[package]]
867name = "lsp-types" 866name = "lsp-types"
868version = "0.84.0" 867version = "0.85.0"
869source = "registry+https://github.com/rust-lang/crates.io-index" 868source = "registry+https://github.com/rust-lang/crates.io-index"
870checksum = "3b95be71fe205e44de754185bcf86447b65813ce1ceb298f8d3793ade5fff08d" 869checksum = "857650f3e83fb62f89d15410414e0ed7d0735445020da398d37f65d20a5423b9"
871dependencies = [ 870dependencies = [
872 "base64", 871 "base64",
873 "bitflags", 872 "bitflags",
@@ -929,9 +928,9 @@ dependencies = [
929 928
930[[package]] 929[[package]]
931name = "memoffset" 930name = "memoffset"
932version = "0.5.6" 931version = "0.6.1"
933source = "registry+https://github.com/rust-lang/crates.io-index" 932source = "registry+https://github.com/rust-lang/crates.io-index"
934checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" 933checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
935dependencies = [ 934dependencies = [
936 "autocfg", 935 "autocfg",
937] 936]
@@ -988,9 +987,9 @@ dependencies = [
988 987
989[[package]] 988[[package]]
990name = "miow" 989name = "miow"
991version = "0.2.1" 990version = "0.2.2"
992source = "registry+https://github.com/rust-lang/crates.io-index" 991source = "registry+https://github.com/rust-lang/crates.io-index"
993checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 992checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
994dependencies = [ 993dependencies = [
995 "kernel32-sys", 994 "kernel32-sys",
996 "net2", 995 "net2",
@@ -1000,9 +999,9 @@ dependencies = [
1000 999
1001[[package]] 1000[[package]]
1002name = "net2" 1001name = "net2"
1003version = "0.2.35" 1002version = "0.2.36"
1004source = "registry+https://github.com/rust-lang/crates.io-index" 1003source = "registry+https://github.com/rust-lang/crates.io-index"
1005checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" 1004checksum = "d7cf75f38f16cb05ea017784dc6dbfd354f76c223dba37701734c4f5a9337d02"
1006dependencies = [ 1005dependencies = [
1007 "cfg-if 0.1.10", 1006 "cfg-if 0.1.10",
1008 "libc", 1007 "libc",
@@ -1298,7 +1297,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
1298dependencies = [ 1297dependencies = [
1299 "crossbeam-channel 0.5.0", 1298 "crossbeam-channel 0.5.0",
1300 "crossbeam-deque", 1299 "crossbeam-deque",
1301 "crossbeam-utils 0.8.0", 1300 "crossbeam-utils 0.8.1",
1302 "lazy_static", 1301 "lazy_static",
1303 "num_cpus", 1302 "num_cpus",
1304] 1303]
@@ -1396,9 +1395,9 @@ dependencies = [
1396 1395
1397[[package]] 1396[[package]]
1398name = "rustc-ap-rustc_lexer" 1397name = "rustc-ap-rustc_lexer"
1399version = "688.0.0" 1398version = "691.0.0"
1400source = "registry+https://github.com/rust-lang/crates.io-index" 1399source = "registry+https://github.com/rust-lang/crates.io-index"
1401checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db" 1400checksum = "44bc89d9ca7a78fb82e103b389362c55f03800745f8ba14e068b805cfaf783ec"
1402dependencies = [ 1401dependencies = [
1403 "unicode-xid", 1402 "unicode-xid",
1404] 1403]
@@ -1436,7 +1435,7 @@ version = "0.16.0"
1436source = "registry+https://github.com/rust-lang/crates.io-index" 1435source = "registry+https://github.com/rust-lang/crates.io-index"
1437checksum = "d8fadca2ab5de17acf66d744f4888049ca8f1bb9b8a1ab8afd9d032cc959c5dc" 1436checksum = "d8fadca2ab5de17acf66d744f4888049ca8f1bb9b8a1ab8afd9d032cc959c5dc"
1438dependencies = [ 1437dependencies = [
1439 "crossbeam-utils 0.8.0", 1438 "crossbeam-utils 0.8.1",
1440 "indexmap", 1439 "indexmap",
1441 "lock_api", 1440 "lock_api",
1442 "log", 1441 "log",
@@ -1627,9 +1626,9 @@ version = "0.0.0"
1627 1626
1628[[package]] 1627[[package]]
1629name = "syn" 1628name = "syn"
1630version = "1.0.51" 1629version = "1.0.53"
1631source = "registry+https://github.com/rust-lang/crates.io-index" 1630source = "registry+https://github.com/rust-lang/crates.io-index"
1632checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" 1631checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68"
1633dependencies = [ 1632dependencies = [
1634 "proc-macro2", 1633 "proc-macro2",
1635 "quote", 1634 "quote",
@@ -1658,6 +1657,7 @@ dependencies = [
1658 "itertools", 1657 "itertools",
1659 "once_cell", 1658 "once_cell",
1660 "parser", 1659 "parser",
1660 "profile",
1661 "rayon", 1661 "rayon",
1662 "rowan", 1662 "rowan",
1663 "rustc-ap-rustc_lexer", 1663 "rustc-ap-rustc_lexer",
@@ -1898,9 +1898,9 @@ dependencies = [
1898 1898
1899[[package]] 1899[[package]]
1900name = "unicode-segmentation" 1900name = "unicode-segmentation"
1901version = "1.7.0" 1901version = "1.7.1"
1902source = "registry+https://github.com/rust-lang/crates.io-index" 1902source = "registry+https://github.com/rust-lang/crates.io-index"
1903checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae" 1903checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
1904 1904
1905[[package]] 1905[[package]]
1906name = "unicode-xid" 1906name = "unicode-xid"
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml
index 108f656e9..3fd8327d6 100644
--- a/crates/assists/Cargo.toml
+++ b/crates/assists/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.9.0" 14itertools = "0.9.0"
15either = "1.5.3" 15either = "1.6.1"
16 16
17stdx = { path = "../stdx", version = "0.0.0" } 17stdx = { path = "../stdx", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index b24527ec4..786224cfa 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -5,8 +5,9 @@
5//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use hir::PrefixKind; 7use hir::PrefixKind;
8use ide_db::helpers::insert_use::MergeBehaviour;
8 9
9use crate::{utils::MergeBehaviour, AssistKind}; 10use crate::AssistKind;
10 11
11#[derive(Clone, Debug, PartialEq, Eq)] 12#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct AssistConfig { 13pub struct AssistConfig {
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index ac72f3f02..66e4634b1 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -1,5 +1,6 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use hir::{HirDisplay, PathResolution, SemanticsScope}; 2use hir::{HirDisplay, PathResolution, SemanticsScope};
3use ide_db::helpers::mod_path_to_ast;
3use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
4use syntax::{ 5use syntax::{
5 algo::SyntaxRewriter, 6 algo::SyntaxRewriter,
@@ -7,8 +8,6 @@ use syntax::{
7 SyntaxNode, 8 SyntaxNode,
8}; 9};
9 10
10use crate::utils::mod_path_to_ast;
11
12pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
13 SyntaxRewriter::from_fn(|element| match element { 12 SyntaxRewriter::from_fn(|element| match element {
14 syntax::SyntaxElement::Node(n) => { 13 syntax::SyntaxElement::Node(n) => {
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index d665837a2..bd5bba646 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,8 +1,11 @@
1use ide_db::helpers::{
2 insert_use::{insert_use, ImportScope},
3 mod_path_to_ast,
4};
1use syntax::ast; 5use syntax::ast;
2 6
3use crate::{ 7use crate::{
4 utils::import_assets::{ImportAssets, ImportCandidate}, 8 utils::import_assets::{ImportAssets, ImportCandidate},
5 utils::{insert_use, mod_path_to_ast, ImportScope},
6 AssistContext, AssistId, AssistKind, Assists, GroupLabel, 9 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
7}; 10};
8 11
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index cac77c49b..d85767b4e 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -2,6 +2,10 @@ use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; 4use hir::{AsName, EnumVariant, Module, ModuleDef, Name};
5use ide_db::helpers::{
6 insert_use::{insert_use, ImportScope},
7 mod_path_to_ast,
8};
5use ide_db::{defs::Definition, search::Reference, RootDatabase}; 9use ide_db::{defs::Definition, search::Reference, RootDatabase};
6use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
7use syntax::{ 11use syntax::{
@@ -10,10 +14,7 @@ use syntax::{
10 SourceFile, SyntaxElement, SyntaxNode, T, 14 SourceFile, SyntaxElement, SyntaxNode, T,
11}; 15};
12 16
13use crate::{ 17use crate::{AssistContext, AssistId, AssistKind, Assists};
14 utils::{insert_use, mod_path_to_ast, ImportScope},
15 AssistContext, AssistId, AssistKind, Assists,
16};
17 18
18// Assist: extract_struct_from_enum_variant 19// Assist: extract_struct_from_enum_variant
19// 20//
@@ -236,10 +237,9 @@ fn update_reference(
236 237
237#[cfg(test)] 238#[cfg(test)]
238mod tests { 239mod tests {
239 use crate::{ 240 use ide_db::helpers::FamousDefs;
240 tests::{check_assist, check_assist_not_applicable}, 241
241 utils::FamousDefs, 242 use crate::tests::{check_assist, check_assist_not_applicable};
242 };
243 243
244 use super::*; 244 use super::*;
245 245
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index eda45f5b3..ef12ef0cf 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -1,13 +1,14 @@
1use std::iter; 1use std::iter;
2 2
3use hir::{Adt, HasSource, ModuleDef, Semantics}; 3use hir::{Adt, HasSource, ModuleDef, Semantics};
4use ide_db::helpers::{mod_path_to_ast, FamousDefs};
4use ide_db::RootDatabase; 5use ide_db::RootDatabase;
5use itertools::Itertools; 6use itertools::Itertools;
6use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
7use test_utils::mark; 8use test_utils::mark;
8 9
9use crate::{ 10use crate::{
10 utils::{mod_path_to_ast, render_snippet, Cursor, FamousDefs}, 11 utils::{render_snippet, Cursor},
11 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
12}; 13};
13 14
@@ -212,12 +213,10 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
212 213
213#[cfg(test)] 214#[cfg(test)]
214mod tests { 215mod tests {
216 use ide_db::helpers::FamousDefs;
215 use test_utils::mark; 217 use test_utils::mark;
216 218
217 use crate::{ 219 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
218 tests::{check_assist, check_assist_not_applicable, check_assist_target},
219 utils::FamousDefs,
220 };
221 220
222 use super::fill_match_arms; 221 use super::fill_match_arms;
223 222
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
index 674e5a175..3c374e5d9 100644
--- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,8 +1,9 @@
1use ide_db::helpers::FamousDefs;
1use ide_db::RootDatabase; 2use ide_db::RootDatabase;
2use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
3use test_utils::mark; 4use test_utils::mark;
4 5
5use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
6 7
7// Assist: generate_from_impl_for_enum 8// Assist: generate_from_impl_for_enum
8// 9//
diff --git a/crates/assists/src/handlers/infer_function_return_type.rs b/crates/assists/src/handlers/infer_function_return_type.rs
index 520d07ae0..aa584eb03 100644
--- a/crates/assists/src/handlers/infer_function_return_type.rs
+++ b/crates/assists/src/handlers/infer_function_return_type.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
17// fn foo() -> i32 { 42i32 } 17// fn foo() -> i32 { 42i32 }
18// ``` 18// ```
19pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 19pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
20 let (tail_expr, builder_edit_pos, wrap_expr) = extract_tail(ctx)?; 20 let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
21 let module = ctx.sema.scope(tail_expr.syntax()).module()?; 21 let module = ctx.sema.scope(tail_expr.syntax()).module()?;
22 let ty = ctx.sema.type_of_expr(&tail_expr)?; 22 let ty = ctx.sema.type_of_expr(&tail_expr)?;
23 if ty.is_unit() { 23 if ty.is_unit() {
@@ -27,7 +27,10 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
27 27
28 acc.add( 28 acc.add(
29 AssistId("infer_function_return_type", AssistKind::RefactorRewrite), 29 AssistId("infer_function_return_type", AssistKind::RefactorRewrite),
30 "Add this function's return type", 30 match fn_type {
31 FnType::Function => "Add this function's return type",
32 FnType::Closure { .. } => "Add this closure's return type",
33 },
31 tail_expr.syntax().text_range(), 34 tail_expr.syntax().text_range(),
32 |builder| { 35 |builder| {
33 match builder_edit_pos { 36 match builder_edit_pos {
@@ -38,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
38 builder.replace(text_range, &format!("-> {}", ty)) 41 builder.replace(text_range, &format!("-> {}", ty))
39 } 42 }
40 } 43 }
41 if wrap_expr { 44 if let FnType::Closure { wrap_expr: true } = fn_type {
42 mark::hit!(wrap_closure_non_block_expr); 45 mark::hit!(wrap_closure_non_block_expr);
43 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block 46 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
44 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); 47 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
@@ -72,8 +75,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio
72 } 75 }
73} 76}
74 77
75fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool)> { 78enum FnType {
76 let (tail_expr, return_type_range, action, wrap_expr) = 79 Function,
80 Closure { wrap_expr: bool },
81}
82
83fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
84 let (fn_type, tail_expr, return_type_range, action) =
77 if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() { 85 if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
78 let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end(); 86 let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end();
79 let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?; 87 let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?;
@@ -86,7 +94,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool
86 }; 94 };
87 95
88 let ret_range = TextRange::new(rpipe_pos, body_start); 96 let ret_range = TextRange::new(rpipe_pos, body_start);
89 (tail_expr, ret_range, action, wrap_expr) 97 (FnType::Closure { wrap_expr }, tail_expr, ret_range, action)
90 } else { 98 } else {
91 let func = ctx.find_node_at_offset::<ast::Fn>()?; 99 let func = ctx.find_node_at_offset::<ast::Fn>()?;
92 let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end(); 100 let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end();
@@ -97,7 +105,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool
97 105
98 let ret_range_end = body.l_curly_token()?.text_range().start(); 106 let ret_range_end = body.l_curly_token()?.text_range().start();
99 let ret_range = TextRange::new(rparen_pos, ret_range_end); 107 let ret_range = TextRange::new(rparen_pos, ret_range_end);
100 (tail_expr, ret_range, action, false) 108 (FnType::Function, tail_expr, ret_range, action)
101 }; 109 };
102 let frange = ctx.frange.range; 110 let frange = ctx.frange.range;
103 if return_type_range.contains_range(frange) { 111 if return_type_range.contains_range(frange) {
@@ -109,7 +117,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool
109 } else { 117 } else {
110 return None; 118 return None;
111 } 119 }
112 Some((tail_expr, action, wrap_expr)) 120 Some((fn_type, tail_expr, action))
113} 121}
114 122
115#[cfg(test)] 123#[cfg(test)]
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs
index fd9c9e03c..b7e853994 100644
--- a/crates/assists/src/handlers/merge_imports.rs
+++ b/crates/assists/src/handlers/merge_imports.rs
@@ -1,3 +1,4 @@
1use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehaviour};
1use syntax::{ 2use syntax::{
2 algo::{neighbor, SyntaxRewriter}, 3 algo::{neighbor, SyntaxRewriter},
3 ast, AstNode, 4 ast, AstNode,
@@ -5,10 +6,7 @@ use syntax::{
5 6
6use crate::{ 7use crate::{
7 assist_context::{AssistContext, Assists}, 8 assist_context::{AssistContext, Assists},
8 utils::{ 9 utils::next_prev,
9 insert_use::{try_merge_imports, try_merge_trees},
10 next_prev, MergeBehaviour,
11 },
12 AssistId, AssistKind, 10 AssistId, AssistKind,
13}; 11};
14 12
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index d5bc4e574..6f9810fe8 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,6 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::AsName;
4use ide_db::helpers::mod_path_to_ast;
4use ide_db::RootDatabase; 5use ide_db::RootDatabase;
5use syntax::{ 6use syntax::{
6 ast, 7 ast,
@@ -12,7 +13,6 @@ use test_utils::mark;
12use crate::{ 13use crate::{
13 assist_context::{AssistContext, Assists}, 14 assist_context::{AssistContext, Assists},
14 utils::import_assets::{ImportAssets, ImportCandidate}, 15 utils::import_assets::{ImportAssets, ImportCandidate},
15 utils::mod_path_to_ast,
16 AssistId, AssistKind, GroupLabel, 16 AssistId, AssistKind, GroupLabel,
17}; 17};
18 18
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 453a6cebf..4d6a1956b 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,3 +1,4 @@
1use ide_db::helpers::mod_path_to_ast;
1use ide_db::imports_locator; 2use ide_db::imports_locator;
2use itertools::Itertools; 3use itertools::Itertools;
3use syntax::{ 4use syntax::{
@@ -10,8 +11,7 @@ use syntax::{
10use crate::{ 11use crate::{
11 assist_context::{AssistBuilder, AssistContext, Assists}, 12 assist_context::{AssistBuilder, AssistContext, Assists},
12 utils::{ 13 utils::{
13 add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, 14 add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods,
14 DefaultMethods,
15 }, 15 },
16 AssistId, AssistKind, 16 AssistId, AssistKind,
17}; 17};
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index a66db9ae3..8193e45a8 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,10 +1,8 @@
1use ide_db::helpers::insert_use::{insert_use, ImportScope};
1use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; 2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode};
2use test_utils::mark; 3use test_utils::mark;
3 4
4use crate::{ 5use crate::{AssistContext, AssistId, AssistKind, Assists};
5 utils::{insert_use, ImportScope},
6 AssistContext, AssistId, AssistKind, Assists,
7};
8 6
9// Assist: replace_qualified_name_with_use 7// Assist: replace_qualified_name_with_use
10// 8//
@@ -409,7 +407,7 @@ impl std::fmt::Display<|> for Foo {
409} 407}
410", 408",
411 r" 409 r"
412use std::fmt::{nested::Debug, Display}; 410use std::fmt::{Display, nested::Debug};
413 411
414impl Display for Foo { 412impl Display for Foo {
415} 413}
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/toggle_ignore.rs
index 5096a0005..14b420421 100644
--- a/crates/assists/src/handlers/ignore_test.rs
+++ b/crates/assists/src/handlers/toggle_ignore.rs
@@ -5,7 +5,7 @@ use syntax::{
5 5
6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; 6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
7 7
8// Assist: ignore_test 8// Assist: toggle_ignore
9// 9//
10// Adds `#[ignore]` attribute to the test. 10// Adds `#[ignore]` attribute to the test.
11// 11//
@@ -23,20 +23,20 @@ use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind,
23// assert_eq!(2 + 2, 5); 23// assert_eq!(2 + 2, 5);
24// } 24// }
25// ``` 25// ```
26pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 26pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let attr: ast::Attr = ctx.find_node_at_offset()?; 27 let attr: ast::Attr = ctx.find_node_at_offset()?;
28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?; 28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
29 let attr = test_related_attribute(&func)?; 29 let attr = test_related_attribute(&func)?;
30 30
31 match has_ignore_attribute(&func) { 31 match has_ignore_attribute(&func) {
32 None => acc.add( 32 None => acc.add(
33 AssistId("ignore_test", AssistKind::None), 33 AssistId("toggle_ignore", AssistKind::None),
34 "Ignore this test", 34 "Ignore this test",
35 attr.syntax().text_range(), 35 attr.syntax().text_range(),
36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), 36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
37 ), 37 ),
38 Some(ignore_attr) => acc.add( 38 Some(ignore_attr) => acc.add(
39 AssistId("unignore_test", AssistKind::None), 39 AssistId("toggle_ignore", AssistKind::None),
40 "Re-enable this test", 40 "Re-enable this test",
41 ignore_attr.syntax().text_range(), 41 ignore_attr.syntax().text_range(),
42 |builder| { 42 |builder| {
@@ -55,24 +55,19 @@ pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
55} 55}
56 56
57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { 57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
58 fn_def.attrs().find_map(|attr| { 58 fn_def.attrs().find(|attr| attr.path().map(|it| it.syntax().text() == "ignore") == Some(true))
59 if attr.path()?.syntax().text() == "ignore" {
60 Some(attr)
61 } else {
62 None
63 }
64 })
65} 59}
66 60
67#[cfg(test)] 61#[cfg(test)]
68mod tests { 62mod tests {
69 use super::ignore_test;
70 use crate::tests::check_assist; 63 use crate::tests::check_assist;
71 64
65 use super::*;
66
72 #[test] 67 #[test]
73 fn test_base_case() { 68 fn test_base_case() {
74 check_assist( 69 check_assist(
75 ignore_test, 70 toggle_ignore,
76 r#" 71 r#"
77 #[test<|>] 72 #[test<|>]
78 fn test() {} 73 fn test() {}
@@ -88,7 +83,7 @@ mod tests {
88 #[test] 83 #[test]
89 fn test_unignore() { 84 fn test_unignore() {
90 check_assist( 85 check_assist(
91 ignore_test, 86 toggle_ignore,
92 r#" 87 r#"
93 #[test<|>] 88 #[test<|>]
94 #[ignore] 89 #[ignore]
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 17e9312db..dfe6c2729 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -141,7 +141,6 @@ mod handlers {
141 mod generate_function; 141 mod generate_function;
142 mod generate_impl; 142 mod generate_impl;
143 mod generate_new; 143 mod generate_new;
144 mod ignore_test;
145 mod infer_function_return_type; 144 mod infer_function_return_type;
146 mod inline_local_variable; 145 mod inline_local_variable;
147 mod introduce_named_lifetime; 146 mod introduce_named_lifetime;
@@ -164,6 +163,7 @@ mod handlers {
164 mod replace_string_with_char; 163 mod replace_string_with_char;
165 mod replace_unwrap_with_match; 164 mod replace_unwrap_with_match;
166 mod split_import; 165 mod split_import;
166 mod toggle_ignore;
167 mod unwrap_block; 167 mod unwrap_block;
168 mod wrap_return_type_in_result; 168 mod wrap_return_type_in_result;
169 169
@@ -190,7 +190,6 @@ mod handlers {
190 generate_function::generate_function, 190 generate_function::generate_function,
191 generate_impl::generate_impl, 191 generate_impl::generate_impl,
192 generate_new::generate_new, 192 generate_new::generate_new,
193 ignore_test::ignore_test,
194 infer_function_return_type::infer_function_return_type, 193 infer_function_return_type::infer_function_return_type,
195 inline_local_variable::inline_local_variable, 194 inline_local_variable::inline_local_variable,
196 introduce_named_lifetime::introduce_named_lifetime, 195 introduce_named_lifetime::introduce_named_lifetime,
@@ -215,6 +214,7 @@ mod handlers {
215 replace_qualified_name_with_use::replace_qualified_name_with_use, 214 replace_qualified_name_with_use::replace_qualified_name_with_use,
216 replace_unwrap_with_match::replace_unwrap_with_match, 215 replace_unwrap_with_match::replace_unwrap_with_match,
217 split_import::split_import, 216 split_import::split_import,
217 toggle_ignore::toggle_ignore,
218 unwrap_block::unwrap_block, 218 unwrap_block::unwrap_block,
219 wrap_return_type_in_result::wrap_return_type_in_result, 219 wrap_return_type_in_result::wrap_return_type_in_result,
220 // These are manually sorted for better priorities 220 // These are manually sorted for better priorities
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 5a9d1a01b..8d50c8791 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -474,26 +474,6 @@ impl<T: Clone> Ctx<T> {
474} 474}
475 475
476#[test] 476#[test]
477fn doctest_ignore_test() {
478 check_doc_test(
479 "ignore_test",
480 r#####"
481<|>#[test]
482fn arithmetics {
483 assert_eq!(2 + 2, 5);
484}
485"#####,
486 r#####"
487#[test]
488#[ignore]
489fn arithmetics {
490 assert_eq!(2 + 2, 5);
491}
492"#####,
493 )
494}
495
496#[test]
497fn doctest_infer_function_return_type() { 477fn doctest_infer_function_return_type() {
498 check_doc_test( 478 check_doc_test(
499 "infer_function_return_type", 479 "infer_function_return_type",
@@ -979,6 +959,26 @@ use std::{collections::HashMap};
979} 959}
980 960
981#[test] 961#[test]
962fn doctest_toggle_ignore() {
963 check_doc_test(
964 "toggle_ignore",
965 r#####"
966<|>#[test]
967fn arithmetics {
968 assert_eq!(2 + 2, 5);
969}
970"#####,
971 r#####"
972#[test]
973#[ignore]
974fn arithmetics {
975 assert_eq!(2 + 2, 5);
976}
977"#####,
978 )
979}
980
981#[test]
982fn doctest_unwrap_block() { 982fn doctest_unwrap_block() {
983 check_doc_test( 983 check_doc_test(
984 "unwrap_block", 984 "unwrap_block",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 66c0cdd5f..01f5c291f 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -1,10 +1,9 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2pub(crate) mod insert_use;
3pub(crate) mod import_assets; 2pub(crate) mod import_assets;
4 3
5use std::ops; 4use std::ops;
6 5
7use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; 6use hir::HasSource;
8use ide_db::RootDatabase; 7use ide_db::RootDatabase;
9use itertools::Itertools; 8use itertools::Itertools;
10use syntax::{ 9use syntax::{
@@ -22,29 +21,6 @@ use crate::{
22 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 21 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
23}; 22};
24 23
25pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
26
27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
28 let mut segments = Vec::new();
29 let mut is_abs = false;
30 match path.kind {
31 hir::PathKind::Plain => {}
32 hir::PathKind::Super(0) => segments.push(make::path_segment_self()),
33 hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
34 hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
35 segments.push(make::path_segment_crate())
36 }
37 hir::PathKind::Abs => is_abs = true,
38 }
39
40 segments.extend(
41 path.segments
42 .iter()
43 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))),
44 );
45 make::path_from_segments(segments, is_abs)
46}
47
48pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { 24pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
49 extract_trivial_expression(&block) 25 extract_trivial_expression(&block)
50 .filter(|expr| !expr.syntax().text().contains_char('\n')) 26 .filter(|expr| !expr.syntax().text().contains_char('\n'))
@@ -259,179 +235,6 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
259 } 235 }
260} 236}
261 237
262/// Helps with finding well-know things inside the standard library. This is
263/// somewhat similar to the known paths infra inside hir, but it different; We
264/// want to make sure that IDE specific paths don't become interesting inside
265/// the compiler itself as well.
266pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>);
267
268#[allow(non_snake_case)]
269impl FamousDefs<'_, '_> {
270 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
271pub mod convert {
272 pub trait From<T> {
273 fn from(t: T) -> Self;
274 }
275}
276
277pub mod default {
278 pub trait Default {
279 fn default() -> Self;
280 }
281}
282
283pub mod iter {
284 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
285 mod traits {
286 pub(crate) mod iterator {
287 use crate::option::Option;
288 pub trait Iterator {
289 type Item;
290 fn next(&mut self) -> Option<Self::Item>;
291 fn by_ref(&mut self) -> &mut Self {
292 self
293 }
294 fn take(self, n: usize) -> crate::iter::Take<Self> {
295 crate::iter::Take { inner: self }
296 }
297 }
298
299 impl<I: Iterator> Iterator for &mut I {
300 type Item = I::Item;
301 fn next(&mut self) -> Option<I::Item> {
302 (**self).next()
303 }
304 }
305 }
306 pub(crate) mod collect {
307 pub trait IntoIterator {
308 type Item;
309 }
310 }
311 }
312
313 pub use self::sources::*;
314 pub(crate) mod sources {
315 use super::Iterator;
316 use crate::option::Option::{self, *};
317 pub struct Repeat<A> {
318 element: A,
319 }
320
321 pub fn repeat<T>(elt: T) -> Repeat<T> {
322 Repeat { element: elt }
323 }
324
325 impl<A> Iterator for Repeat<A> {
326 type Item = A;
327
328 fn next(&mut self) -> Option<A> {
329 None
330 }
331 }
332 }
333
334 pub use self::adapters::*;
335 pub(crate) mod adapters {
336 use super::Iterator;
337 use crate::option::Option::{self, *};
338 pub struct Take<I> { pub(crate) inner: I }
339 impl<I> Iterator for Take<I> where I: Iterator {
340 type Item = <I as Iterator>::Item;
341 fn next(&mut self) -> Option<<I as Iterator>::Item> {
342 None
343 }
344 }
345 }
346}
347
348pub mod option {
349 pub enum Option<T> { None, Some(T)}
350}
351
352pub mod prelude {
353 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
354}
355#[prelude_import]
356pub use prelude::*;
357"#;
358
359 pub fn core(&self) -> Option<Crate> {
360 self.find_crate("core")
361 }
362
363 pub(crate) fn core_convert_From(&self) -> Option<Trait> {
364 self.find_trait("core:convert:From")
365 }
366
367 pub(crate) fn core_option_Option(&self) -> Option<Enum> {
368 self.find_enum("core:option:Option")
369 }
370
371 pub fn core_default_Default(&self) -> Option<Trait> {
372 self.find_trait("core:default:Default")
373 }
374
375 pub fn core_iter_Iterator(&self) -> Option<Trait> {
376 self.find_trait("core:iter:traits:iterator:Iterator")
377 }
378
379 pub fn core_iter(&self) -> Option<Module> {
380 self.find_module("core:iter")
381 }
382
383 fn find_trait(&self, path: &str) -> Option<Trait> {
384 match self.find_def(path)? {
385 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
386 _ => None,
387 }
388 }
389
390 fn find_enum(&self, path: &str) -> Option<Enum> {
391 match self.find_def(path)? {
392 hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
393 _ => None,
394 }
395 }
396
397 fn find_module(&self, path: &str) -> Option<Module> {
398 match self.find_def(path)? {
399 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
400 _ => None,
401 }
402 }
403
404 fn find_crate(&self, name: &str) -> Option<Crate> {
405 let krate = self.1?;
406 let db = self.0.db;
407 let res =
408 krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate;
409 Some(res)
410 }
411
412 fn find_def(&self, path: &str) -> Option<ScopeDef> {
413 let db = self.0.db;
414 let mut path = path.split(':');
415 let trait_ = path.next_back()?;
416 let std_crate = path.next()?;
417 let std_crate = self.find_crate(std_crate)?;
418 let mut module = std_crate.root_module(db);
419 for segment in path {
420 module = module.children(db).find_map(|child| {
421 let name = child.name(db)?;
422 if name.to_string() == segment {
423 Some(child)
424 } else {
425 None
426 }
427 })?;
428 }
429 let def =
430 module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
431 Some(def)
432 }
433}
434
435pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { 238pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
436 [Direction::Next, Direction::Prev].iter().copied() 239 [Direction::Next, Direction::Prev].iter().copied()
437} 240}
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index e7df9d955..35e169a28 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -15,7 +15,6 @@ log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16either = "1.6.1" 16either = "1.6.1"
17 17
18assists = { path = "../assists", version = "0.0.0" }
19stdx = { path = "../stdx", version = "0.0.0" } 18stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
21text_edit = { path = "../text_edit", version = "0.0.0" } 20text_edit = { path = "../text_edit", version = "0.0.0" }
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 7fbda7a6b..c8ba63cd3 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -5,6 +5,7 @@ mod format_like;
5use ide_db::ty_filter::TryEnum; 5use ide_db::ty_filter::TryEnum;
6use syntax::{ 6use syntax::{
7 ast::{self, AstNode, AstToken}, 7 ast::{self, AstNode, AstToken},
8 SyntaxKind::{BLOCK_EXPR, EXPR_STMT},
8 TextRange, TextSize, 9 TextRange, TextSize,
9}; 10};
10use text_edit::TextEdit; 11use text_edit::TextEdit;
@@ -220,6 +221,29 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
220 ) 221 )
221 .add_to(acc); 222 .add_to(acc);
222 223
224 if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
225 if matches!(parent.kind(), BLOCK_EXPR | EXPR_STMT) {
226 postfix_snippet(
227 ctx,
228 cap,
229 &dot_receiver,
230 "let",
231 "let",
232 &format!("let $0 = {};", receiver_text),
233 )
234 .add_to(acc);
235 postfix_snippet(
236 ctx,
237 cap,
238 &dot_receiver,
239 "letm",
240 "let mut",
241 &format!("let mut $0 = {};", receiver_text),
242 )
243 .add_to(acc);
244 }
245 }
246
223 if let ast::Expr::Literal(literal) = dot_receiver.clone() { 247 if let ast::Expr::Literal(literal) = dot_receiver.clone() {
224 if let Some(literal_text) = ast::String::cast(literal.token()) { 248 if let Some(literal_text) = ast::String::cast(literal.token()) {
225 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text); 249 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
@@ -296,6 +320,38 @@ fn main() {
296 sn dbg dbg!(expr) 320 sn dbg dbg!(expr)
297 sn dbgr dbg!(&expr) 321 sn dbgr dbg!(&expr)
298 sn if if expr {} 322 sn if if expr {}
323 sn let let
324 sn letm let mut
325 sn match match expr {}
326 sn not !expr
327 sn ok Ok(expr)
328 sn ref &expr
329 sn refm &mut expr
330 sn some Some(expr)
331 sn while while expr {}
332 "#]],
333 );
334 }
335
336 #[test]
337 fn postfix_completion_works_for_function_calln() {
338 check(
339 r#"
340fn foo(elt: bool) -> bool {
341 !elt
342}
343
344fn main() {
345 let bar = true;
346 foo(bar.<|>)
347}
348"#,
349 expect![[r#"
350 sn box Box::new(expr)
351 sn call function(expr)
352 sn dbg dbg!(expr)
353 sn dbgr dbg!(&expr)
354 sn if if expr {}
299 sn match match expr {} 355 sn match match expr {}
300 sn not !expr 356 sn not !expr
301 sn ok Ok(expr) 357 sn ok Ok(expr)
@@ -321,6 +377,8 @@ fn main() {
321 sn call function(expr) 377 sn call function(expr)
322 sn dbg dbg!(expr) 378 sn dbg dbg!(expr)
323 sn dbgr dbg!(&expr) 379 sn dbgr dbg!(&expr)
380 sn let let
381 sn letm let mut
324 sn match match expr {} 382 sn match match expr {}
325 sn ok Ok(expr) 383 sn ok Ok(expr)
326 sn ref &expr 384 sn ref &expr
@@ -331,6 +389,34 @@ fn main() {
331 } 389 }
332 390
333 #[test] 391 #[test]
392 fn let_middle_block() {
393 check(
394 r#"
395fn main() {
396 baz.l<|>
397 res
398}
399"#,
400 expect![[r#"
401 sn box Box::new(expr)
402 sn call function(expr)
403 sn dbg dbg!(expr)
404 sn dbgr dbg!(&expr)
405 sn if if expr {}
406 sn let let
407 sn letm let mut
408 sn match match expr {}
409 sn not !expr
410 sn ok Ok(expr)
411 sn ref &expr
412 sn refm &mut expr
413 sn some Some(expr)
414 sn while while expr {}
415 "#]],
416 );
417 }
418
419 #[test]
334 fn option_iflet() { 420 fn option_iflet() {
335 check_edit( 421 check_edit(
336 "ifl", 422 "ifl",
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs
index d9387054d..bc23bea3f 100644
--- a/crates/completion/src/completions/qualified_path.rs
+++ b/crates/completion/src/completions/qualified_path.rs
@@ -353,10 +353,10 @@ impl S {
353fn foo() { let _ = S::<|> } 353fn foo() { let _ = S::<|> }
354"#, 354"#,
355 expect![[r#" 355 expect![[r#"
356 ct C const C: i32 = 42; 356 ct C const C: i32 = 42;
357 ta T type T = i32; 357 ta T type T = i32;
358 fn a() fn a() 358 fn a() fn a()
359 me b() fn b(&self) 359 me b(…) fn b(&self)
360 "#]], 360 "#]],
361 ); 361 );
362 } 362 }
@@ -503,14 +503,14 @@ trait Sub: Super {
503fn foo<T: Sub>() { T::<|> } 503fn foo<T: Sub>() { T::<|> }
504"#, 504"#,
505 expect![[r#" 505 expect![[r#"
506 ct C2 const C2: (); 506 ct C2 const C2: ();
507 ct CONST const CONST: u8; 507 ct CONST const CONST: u8;
508 ta SubTy type SubTy; 508 ta SubTy type SubTy;
509 ta Ty type Ty; 509 ta Ty type Ty;
510 fn func() fn func() 510 fn func() fn func()
511 me method() fn method(&self) 511 me method(…) fn method(&self)
512 fn subfunc() fn subfunc() 512 fn subfunc() fn subfunc()
513 me submethod() fn submethod(&self) 513 me submethod(…) fn submethod(&self)
514 "#]], 514 "#]],
515 ); 515 );
516 } 516 }
@@ -543,14 +543,14 @@ impl<T> Sub for Wrap<T> {
543} 543}
544"#, 544"#,
545 expect![[r#" 545 expect![[r#"
546 ct C2 const C2: () = (); 546 ct C2 const C2: () = ();
547 ct CONST const CONST: u8 = 0; 547 ct CONST const CONST: u8 = 0;
548 ta SubTy type SubTy; 548 ta SubTy type SubTy;
549 ta Ty type Ty; 549 ta Ty type Ty;
550 fn func() fn func() 550 fn func() fn func()
551 me method() fn method(&self) 551 me method(…) fn method(&self)
552 fn subfunc() fn subfunc() 552 fn subfunc() fn subfunc()
553 me submethod() fn submethod(&self) 553 me submethod(…) fn submethod(&self)
554 "#]], 554 "#]],
555 ); 555 );
556 } 556 }
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index 2049b9d09..eaa44c97d 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -1,5 +1,5 @@
1//! Complete fields in record literals and patterns. 1//! Complete fields in record literals and patterns.
2use assists::utils::FamousDefs; 2use ide_db::helpers::FamousDefs;
3use syntax::ast::Expr; 3use syntax::ast::Expr;
4 4
5use crate::{ 5use crate::{
@@ -45,8 +45,8 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
45 45
46#[cfg(test)] 46#[cfg(test)]
47mod tests { 47mod tests {
48 use assists::utils::FamousDefs;
49 use expect_test::{expect, Expect}; 48 use expect_test::{expect, Expect};
49 use ide_db::helpers::FamousDefs;
50 50
51 use crate::{test_utils::completion_list, CompletionKind}; 51 use crate::{test_utils::completion_list, CompletionKind};
52 52
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index a14be9c73..e2fe44aff 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -139,7 +139,7 @@ fn add_function_impl(
139) { 139) {
140 let fn_name = func.name(ctx.db).to_string(); 140 let fn_name = func.name(ctx.db).to_string();
141 141
142 let label = if func.params(ctx.db).is_empty() { 142 let label = if func.assoc_fn_params(ctx.db).is_empty() {
143 format!("fn {}()", fn_name) 143 format!("fn {}()", fn_name)
144 } else { 144 } else {
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 3bd776905..81691cd7f 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -1,8 +1,8 @@
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 assists::utils::ImportScope;
4use either::Either; 3use either::Either;
5use hir::{Adt, ModuleDef, ScopeDef, Type}; 4use hir::{Adt, ModuleDef, ScopeDef, Type};
5use ide_db::helpers::insert_use::ImportScope;
6use ide_db::imports_locator; 6use ide_db::imports_locator;
7use syntax::AstNode; 7use syntax::AstNode;
8use test_utils::mark; 8use test_utils::mark;
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index f50735372..654a76f7b 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,7 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use assists::utils::MergeBehaviour; 7use ide_db::helpers::insert_use::MergeBehaviour;
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index b13c3f376..e85549fef 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -2,8 +2,11 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
6use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, ModPath, Mutability};
6use ide_db::helpers::{
7 insert_use::{self, ImportScope, MergeBehaviour},
8 mod_path_to_ast,
9};
7use syntax::{algo, TextRange}; 10use syntax::{algo, TextRange};
8use text_edit::TextEdit; 11use text_edit::TextEdit;
9 12
@@ -201,7 +204,7 @@ impl CompletionItem {
201 trigger_call_info: None, 204 trigger_call_info: None,
202 score: None, 205 score: None,
203 ref_match: None, 206 ref_match: None,
204 import_data: None, 207 import_to_add: None,
205 } 208 }
206 } 209 }
207 210
@@ -255,13 +258,21 @@ impl CompletionItem {
255 } 258 }
256} 259}
257 260
261/// An extra import to add after the completion is applied.
262#[derive(Clone)]
263pub(crate) struct ImportToAdd {
264 pub(crate) import_path: ModPath,
265 pub(crate) import_scope: ImportScope,
266 pub(crate) merge_behaviour: Option<MergeBehaviour>,
267}
268
258/// A helper to make `CompletionItem`s. 269/// A helper to make `CompletionItem`s.
259#[must_use] 270#[must_use]
260#[derive(Clone)] 271#[derive(Clone)]
261pub(crate) struct Builder { 272pub(crate) struct Builder {
262 source_range: TextRange, 273 source_range: TextRange,
263 completion_kind: CompletionKind, 274 completion_kind: CompletionKind,
264 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 275 import_to_add: Option<ImportToAdd>,
265 label: String, 276 label: String,
266 insert_text: Option<String>, 277 insert_text: Option<String>,
267 insert_text_format: InsertTextFormat, 278 insert_text_format: InsertTextFormat,
@@ -278,14 +289,16 @@ pub(crate) struct Builder {
278 289
279impl Builder { 290impl Builder {
280 pub(crate) fn build(self) -> CompletionItem { 291 pub(crate) fn build(self) -> CompletionItem {
292 let _p = profile::span("item::Builder::build");
293
281 let mut label = self.label; 294 let mut label = self.label;
282 let mut lookup = self.lookup; 295 let mut lookup = self.lookup;
283 let mut insert_text = self.insert_text; 296 let mut insert_text = self.insert_text;
284 let mut text_edits = TextEdit::builder(); 297 let mut text_edits = TextEdit::builder();
285 298
286 if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { 299 if let Some(import_data) = self.import_to_add {
287 let import = mod_path_to_ast(&import_path); 300 let import = mod_path_to_ast(&import_data.import_path);
288 let mut import_path_without_last_segment = import_path; 301 let mut import_path_without_last_segment = import_data.import_path;
289 let _ = import_path_without_last_segment.segments.pop(); 302 let _ = import_path_without_last_segment.segments.pop();
290 303
291 if !import_path_without_last_segment.segments.is_empty() { 304 if !import_path_without_last_segment.segments.is_empty() {
@@ -298,7 +311,11 @@ impl Builder {
298 label = format!("{}::{}", import_path_without_last_segment, label); 311 label = format!("{}::{}", import_path_without_last_segment, label);
299 } 312 }
300 313
301 let rewriter = insert_use(&import_scope, import, merge_behaviour); 314 let rewriter = insert_use::insert_use(
315 &import_data.import_scope,
316 import,
317 import_data.merge_behaviour,
318 );
302 if let Some(old_ast) = rewriter.rewrite_root() { 319 if let Some(old_ast) = rewriter.rewrite_root() {
303 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); 320 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits);
304 } 321 }
@@ -390,11 +407,8 @@ impl Builder {
390 self.trigger_call_info = Some(true); 407 self.trigger_call_info = Some(true);
391 self 408 self
392 } 409 }
393 pub(crate) fn import_data( 410 pub(crate) fn add_import(mut self, import_to_add: Option<ImportToAdd>) -> Builder {
394 mut self, 411 self.import_to_add = import_to_add;
395 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
396 ) -> Builder {
397 self.import_data = import_data;
398 self 412 self
399 } 413 }
400 pub(crate) fn set_ref_match( 414 pub(crate) fn set_ref_match(
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index aecc1378b..1ec2e9be7 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -44,6 +44,8 @@ pub use crate::{
44// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` 44// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
45// - `expr.ref` -> `&expr` 45// - `expr.ref` -> `&expr`
46// - `expr.refm` -> `&mut expr` 46// - `expr.refm` -> `&mut expr`
47// - `expr.let` -> `let <|> = expr;`
48// - `expr.letm` -> `let mut <|> = expr;`
47// - `expr.not` -> `!expr` 49// - `expr.not` -> `!expr`
48// - `expr.dbg` -> `dbg!(expr)` 50// - `expr.dbg` -> `dbg!(expr)`
49// - `expr.dbgr` -> `dbg!(&expr)` 51// - `expr.dbgr` -> `dbg!(&expr)`
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index bce02f577..504757a6a 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -9,15 +9,15 @@ pub(crate) mod type_alias;
9 9
10mod builder_ext; 10mod builder_ext;
11 11
12use assists::utils::{ImportScope, MergeBehaviour};
13use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; 12use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
13use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour};
14use ide_db::RootDatabase; 14use ide_db::RootDatabase;
15use syntax::TextRange; 15use syntax::TextRange;
16use test_utils::mark; 16use test_utils::mark;
17 17
18use crate::{ 18use crate::{
19 config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 19 config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind,
20 CompletionScore, 20 CompletionKind, CompletionScore,
21}; 21};
22 22
23use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; 23use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro};
@@ -48,15 +48,15 @@ pub(crate) fn render_resolution<'a>(
48 48
49pub(crate) fn render_resolution_with_import<'a>( 49pub(crate) fn render_resolution_with_import<'a>(
50 ctx: RenderContext<'a>, 50 ctx: RenderContext<'a>,
51 import: ModPath, 51 import_path: ModPath,
52 import_scope: ImportScope, 52 import_scope: ImportScope,
53 merge_behaviour: Option<MergeBehaviour>, 53 merge_behaviour: Option<MergeBehaviour>,
54 resolution: &ScopeDef, 54 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 55) -> Option<CompletionItem> {
56 let local_name = import.segments.last()?.to_string(); 56 let local_name = import_path.segments.last()?.to_string();
57 Render::new(ctx).render_resolution( 57 Render::new(ctx).render_resolution(
58 local_name, 58 local_name,
59 Some((import, import_scope, merge_behaviour)), 59 Some(ImportToAdd { import_path, import_scope, merge_behaviour }),
60 resolution, 60 resolution,
61 ) 61 )
62} 62}
@@ -147,7 +147,7 @@ impl<'a> Render<'a> {
147 fn render_resolution( 147 fn render_resolution(
148 self, 148 self,
149 local_name: String, 149 local_name: String,
150 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 150 import_to_add: Option<ImportToAdd>,
151 resolution: &ScopeDef, 151 resolution: &ScopeDef,
152 ) -> Option<CompletionItem> { 152 ) -> Option<CompletionItem> {
153 let _p = profile::span("render_resolution"); 153 let _p = profile::span("render_resolution");
@@ -160,15 +160,16 @@ impl<'a> Render<'a> {
160 160
161 let kind = match resolution { 161 let kind = match resolution {
162 ScopeDef::ModuleDef(Function(func)) => { 162 ScopeDef::ModuleDef(Function(func)) => {
163 let item = render_fn(self.ctx, import_data, Some(local_name), *func); 163 let item = render_fn(self.ctx, import_to_add, Some(local_name), *func);
164 return Some(item); 164 return Some(item);
165 } 165 }
166 ScopeDef::ModuleDef(EnumVariant(var)) => { 166 ScopeDef::ModuleDef(EnumVariant(var)) => {
167 let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); 167 let item =
168 render_enum_variant(self.ctx, import_to_add, Some(local_name), *var, None);
168 return Some(item); 169 return Some(item);
169 } 170 }
170 ScopeDef::MacroDef(mac) => { 171 ScopeDef::MacroDef(mac) => {
171 let item = render_macro(self.ctx, import_data, local_name, *mac); 172 let item = render_macro(self.ctx, import_to_add, local_name, *mac);
172 return item; 173 return item;
173 } 174 }
174 175
@@ -193,7 +194,7 @@ impl<'a> Render<'a> {
193 local_name, 194 local_name,
194 ) 195 )
195 .kind(CompletionItemKind::UnresolvedReference) 196 .kind(CompletionItemKind::UnresolvedReference)
196 .import_data(import_data) 197 .add_import(import_to_add)
197 .build(); 198 .build();
198 return Some(item); 199 return Some(item);
199 } 200 }
@@ -248,7 +249,7 @@ impl<'a> Render<'a> {
248 249
249 let item = item 250 let item = item
250 .kind(kind) 251 .kind(kind)
251 .import_data(import_data) 252 .add_import(import_to_add)
252 .set_documentation(docs) 253 .set_documentation(docs)
253 .set_ref_match(ref_match) 254 .set_ref_match(ref_match)
254 .build(); 255 .build();
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs
index 37b0d0459..ce8718bd5 100644
--- a/crates/completion/src/render/builder_ext.rs
+++ b/crates/completion/src/render/builder_ext.rs
@@ -5,6 +5,7 @@ use test_utils::mark;
5 5
6use crate::{item::Builder, CompletionContext}; 6use crate::{item::Builder, CompletionContext};
7 7
8#[derive(Debug)]
8pub(super) enum Params { 9pub(super) enum Params {
9 Named(Vec<String>), 10 Named(Vec<String>),
10 Anonymous(usize), 11 Anonymous(usize),
@@ -24,7 +25,7 @@ impl Params {
24} 25}
25 26
26impl Builder { 27impl Builder {
27 pub(super) fn should_add_parems(&self, ctx: &CompletionContext) -> bool { 28 fn should_add_parens(&self, ctx: &CompletionContext) -> bool {
28 if !ctx.config.add_call_parenthesis { 29 if !ctx.config.add_call_parenthesis {
29 return false; 30 return false;
30 } 31 }
@@ -58,7 +59,7 @@ impl Builder {
58 name: String, 59 name: String,
59 params: Params, 60 params: Params,
60 ) -> Builder { 61 ) -> Builder {
61 if !self.should_add_parems(ctx) { 62 if !self.should_add_parens(ctx) {
62 return self; 63 return self;
63 } 64 }
64 65
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 6070e9b1d..f4bd02f25 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -1,23 +1,23 @@
1//! Renderer for `enum` variants. 1//! Renderer for `enum` variants.
2 2
3use assists::utils::{ImportScope, MergeBehaviour};
4use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
5use itertools::Itertools; 4use itertools::Itertools;
6use test_utils::mark; 5use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
10 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
11}; 10};
12 11
13pub(crate) fn render_enum_variant<'a>( 12pub(crate) fn render_enum_variant<'a>(
14 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
15 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 14 import_to_add: Option<ImportToAdd>,
16 local_name: Option<String>, 15 local_name: Option<String>,
17 variant: hir::EnumVariant, 16 variant: hir::EnumVariant,
18 path: Option<ModPath>, 17 path: Option<ModPath>,
19) -> CompletionItem { 18) -> CompletionItem {
20 EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) 19 let _p = profile::span("render_enum_variant");
20 EnumVariantRender::new(ctx, local_name, variant, path).render(import_to_add)
21} 21}
22 22
23#[derive(Debug)] 23#[derive(Debug)]
@@ -62,10 +62,7 @@ impl<'a> EnumVariantRender<'a> {
62 } 62 }
63 } 63 }
64 64
65 fn render( 65 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem {
66 self,
67 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
68 ) -> CompletionItem {
69 let mut builder = CompletionItem::new( 66 let mut builder = CompletionItem::new(
70 CompletionKind::Reference, 67 CompletionKind::Reference,
71 self.ctx.source_range(), 68 self.ctx.source_range(),
@@ -74,7 +71,7 @@ impl<'a> EnumVariantRender<'a> {
74 .kind(CompletionItemKind::EnumVariant) 71 .kind(CompletionItemKind::EnumVariant)
75 .set_documentation(self.variant.docs(self.ctx.db())) 72 .set_documentation(self.variant.docs(self.ctx.db()))
76 .set_deprecated(self.ctx.is_deprecated(self.variant)) 73 .set_deprecated(self.ctx.is_deprecated(self.variant))
77 .import_data(import_data) 74 .add_import(import_to_add)
78 .detail(self.detail()); 75 .detail(self.detail());
79 76
80 if self.variant_kind == StructKind::Tuple { 77 if self.variant_kind == StructKind::Tuple {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 9dd5cd18c..00e3eb203 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -1,28 +1,29 @@
1//! Renderer for function calls. 1//! Renderer for function calls.
2 2
3use assists::utils::{ImportScope, MergeBehaviour}; 3use hir::{HasSource, Type};
4use hir::{HasSource, ModPath, Type};
5use syntax::{ast::Fn, display::function_declaration}; 4use syntax::{ast::Fn, display::function_declaration};
5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
10}; 10};
11 11
12pub(crate) fn render_fn<'a>( 12pub(crate) fn render_fn<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 14 import_to_add: Option<ImportToAdd>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 fn_: hir::Function, 16 fn_: hir::Function,
17) -> CompletionItem { 17) -> CompletionItem {
18 FunctionRender::new(ctx, local_name, fn_).render(import_data) 18 let _p = profile::span("render_fn");
19 FunctionRender::new(ctx, local_name, fn_).render(import_to_add)
19} 20}
20 21
21#[derive(Debug)] 22#[derive(Debug)]
22struct FunctionRender<'a> { 23struct FunctionRender<'a> {
23 ctx: RenderContext<'a>, 24 ctx: RenderContext<'a>,
24 name: String, 25 name: String,
25 fn_: hir::Function, 26 func: hir::Function,
26 ast_node: Fn, 27 ast_node: Fn,
27} 28}
28 29
@@ -35,21 +36,18 @@ impl<'a> FunctionRender<'a> {
35 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); 36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
36 let ast_node = fn_.source(ctx.db()).value; 37 let ast_node = fn_.source(ctx.db()).value;
37 38
38 FunctionRender { ctx, name, fn_, ast_node } 39 FunctionRender { ctx, name, func: fn_, ast_node }
39 } 40 }
40 41
41 fn render( 42 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem {
42 self,
43 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
44 ) -> CompletionItem {
45 let params = self.params(); 43 let params = self.params();
46 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
47 .kind(self.kind()) 45 .kind(self.kind())
48 .set_documentation(self.ctx.docs(self.fn_)) 46 .set_documentation(self.ctx.docs(self.func))
49 .set_deprecated(self.ctx.is_deprecated(self.fn_)) 47 .set_deprecated(self.ctx.is_deprecated(self.func))
50 .detail(self.detail()) 48 .detail(self.detail())
51 .add_call_parens(self.ctx.completion, self.name, params) 49 .add_call_parens(self.ctx.completion, self.name, params)
52 .import_data(import_data) 50 .add_import(import_to_add)
53 .build() 51 .build()
54 } 52 }
55 53
@@ -70,27 +68,39 @@ impl<'a> FunctionRender<'a> {
70 } 68 }
71 69
72 fn params(&self) -> Params { 70 fn params(&self) -> Params {
73 let params_ty = self.fn_.params(self.ctx.db()); 71 let ast_params = match self.ast_node.param_list() {
74 let params = self 72 Some(it) => it,
75 .ast_node 73 None => return Params::Named(Vec::new()),
76 .param_list() 74 };
75
76 let mut params_pats = Vec::new();
77 let params_ty = if self.ctx.completion.dot_receiver.is_some() {
78 self.func.method_params(self.ctx.db()).unwrap_or_default()
79 } else {
80 if let Some(s) = ast_params.self_param() {
81 mark::hit!(parens_for_method_call_as_assoc_fn);
82 params_pats.push(Some(s.to_string()));
83 }
84 self.func.assoc_fn_params(self.ctx.db())
85 };
86 params_pats
87 .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string())));
88
89 let params = params_pats
77 .into_iter() 90 .into_iter()
78 .flat_map(|it| it.params())
79 .zip(params_ty) 91 .zip(params_ty)
80 .flat_map(|(it, param_ty)| { 92 .flat_map(|(pat, param_ty)| {
81 if let Some(pat) = it.pat() { 93 let pat = pat?;
82 let name = pat.to_string(); 94 let name = pat.to_string();
83 let arg = name.trim_start_matches("mut ").trim_start_matches('_'); 95 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
84 return Some(self.add_arg(arg, param_ty.ty())); 96 Some(self.add_arg(arg, param_ty.ty()))
85 }
86 None
87 }) 97 })
88 .collect(); 98 .collect();
89 Params::Named(params) 99 Params::Named(params)
90 } 100 }
91 101
92 fn kind(&self) -> CompletionItemKind { 102 fn kind(&self) -> CompletionItemKind {
93 if self.fn_.self_param(self.ctx.db()).is_some() { 103 if self.func.self_param(self.ctx.db()).is_some() {
94 CompletionItemKind::Method 104 CompletionItemKind::Method
95 } else { 105 } else {
96 CompletionItemKind::Function 106 CompletionItemKind::Function
@@ -176,6 +186,28 @@ fn bar(s: &S) {
176 } 186 }
177 187
178 #[test] 188 #[test]
189 fn parens_for_method_call_as_assoc_fn() {
190 mark::check!(parens_for_method_call_as_assoc_fn);
191 check_edit(
192 "foo",
193 r#"
194struct S;
195impl S {
196 fn foo(&self) {}
197}
198fn main() { S::f<|> }
199"#,
200 r#"
201struct S;
202impl S {
203 fn foo(&self) {}
204}
205fn main() { S::foo(${1:&self})$0 }
206"#,
207 );
208 }
209
210 #[test]
179 fn suppress_arg_snippets() { 211 fn suppress_arg_snippets() {
180 mark::check!(suppress_arg_snippets); 212 mark::check!(suppress_arg_snippets);
181 check_edit_with_config( 213 check_edit_with_config(
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index fead59e41..b4ab32c6e 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -1,22 +1,22 @@
1//! Renderer for macro invocations. 1//! Renderer for macro invocations.
2 2
3use assists::utils::{ImportScope, MergeBehaviour}; 3use hir::{Documentation, HasSource};
4use hir::{Documentation, HasSource, ModPath};
5use syntax::display::macro_label; 4use syntax::display::macro_label;
6use test_utils::mark; 5use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
10 render::RenderContext, 9 render::RenderContext,
11}; 10};
12 11
13pub(crate) fn render_macro<'a>( 12pub(crate) fn render_macro<'a>(
14 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
15 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 14 import_to_add: Option<ImportToAdd>,
16 name: String, 15 name: String,
17 macro_: hir::MacroDef, 16 macro_: hir::MacroDef,
18) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
19 MacroRender::new(ctx, name, macro_).render(import_data) 18 let _p = profile::span("render_macro");
19 MacroRender::new(ctx, name, macro_).render(import_to_add)
20} 20}
21 21
22#[derive(Debug)] 22#[derive(Debug)]
@@ -38,10 +38,7 @@ impl<'a> MacroRender<'a> {
38 MacroRender { ctx, name, macro_, docs, bra, ket } 38 MacroRender { ctx, name, macro_, docs, bra, ket }
39 } 39 }
40 40
41 fn render( 41 fn render(&self, import_to_add: Option<ImportToAdd>) -> Option<CompletionItem> {
42 &self,
43 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
44 ) -> Option<CompletionItem> {
45 // FIXME: Currently proc-macro do not have ast-node, 42 // FIXME: Currently proc-macro do not have ast-node,
46 // such that it does not have source 43 // such that it does not have source
47 if self.macro_.is_proc_macro() { 44 if self.macro_.is_proc_macro() {
@@ -53,7 +50,7 @@ impl<'a> MacroRender<'a> {
53 .kind(CompletionItemKind::Macro) 50 .kind(CompletionItemKind::Macro)
54 .set_documentation(self.docs.clone()) 51 .set_documentation(self.docs.clone())
55 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 52 .set_deprecated(self.ctx.is_deprecated(self.macro_))
56 .import_data(import_data) 53 .add_import(import_to_add)
57 .detail(self.detail()); 54 .detail(self.detail());
58 55
59 let needs_bang = self.needs_bang(); 56 let needs_bang = self.needs_bang();
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 37ed092ad..4500050f1 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -744,14 +744,13 @@ impl Function {
744 Some(SelfParam { func: self.id }) 744 Some(SelfParam { func: self.id })
745 } 745 }
746 746
747 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { 747 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
748 let resolver = self.id.resolver(db.upcast()); 748 let resolver = self.id.resolver(db.upcast());
749 let ctx = hir_ty::TyLoweringContext::new(db, &resolver); 749 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
750 let environment = TraitEnvironment::lower(db, &resolver); 750 let environment = TraitEnvironment::lower(db, &resolver);
751 db.function_data(self.id) 751 db.function_data(self.id)
752 .params 752 .params
753 .iter() 753 .iter()
754 .skip(if self.self_param(db).is_some() { 1 } else { 0 })
755 .map(|type_ref| { 754 .map(|type_ref| {
756 let ty = Type { 755 let ty = Type {
757 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, 756 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
@@ -764,6 +763,14 @@ impl Function {
764 }) 763 })
765 .collect() 764 .collect()
766 } 765 }
766 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
767 if self.self_param(db).is_none() {
768 return None;
769 }
770 let mut res = self.assoc_fn_params(db);
771 res.remove(0);
772 Some(res)
773 }
767 774
768 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { 775 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
769 db.function_data(self.id).is_unsafe 776 db.function_data(self.id).is_unsafe
@@ -799,6 +806,7 @@ impl From<Mutability> for Access {
799 } 806 }
800} 807}
801 808
809#[derive(Debug)]
802pub struct Param { 810pub struct Param {
803 ty: Type, 811 ty: Type,
804} 812}
@@ -1420,11 +1428,11 @@ impl Type {
1420 pub fn normalize_trait_assoc_type( 1428 pub fn normalize_trait_assoc_type(
1421 &self, 1429 &self,
1422 db: &dyn HirDatabase, 1430 db: &dyn HirDatabase,
1423 r#trait: Trait, 1431 trait_: Trait,
1424 args: &[Type], 1432 args: &[Type],
1425 alias: TypeAlias, 1433 alias: TypeAlias,
1426 ) -> Option<Type> { 1434 ) -> Option<Type> {
1427 let subst = Substs::build_for_def(db, r#trait.id) 1435 let subst = Substs::build_for_def(db, trait_.id)
1428 .push(self.ty.value.clone()) 1436 .push(self.ty.value.clone())
1429 .fill(args.iter().map(|t| t.ty.value.clone())) 1437 .fill(args.iter().map(|t| t.ty.value.clone()))
1430 .build(); 1438 .build();
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index d9ad8db6f..eaf1a14ec 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,5 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; 2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro};
3pub use hir_expand::diagnostics::{ 3pub use hir_expand::diagnostics::{
4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, 4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
5}; 5};
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index d10b1af01..33eb5e78c 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -14,8 +14,8 @@ use cfg::CfgOptions;
14use drop_bomb::DropBomb; 14use drop_bomb::DropBomb;
15use either::Either; 15use either::Either;
16use hir_expand::{ 16use hir_expand::{
17 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, HirFileId, InFile, 17 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult,
18 MacroDefId, 18 HirFileId, InFile, MacroDefId,
19}; 19};
20use rustc_hash::FxHashMap; 20use rustc_hash::FxHashMap;
21use syntax::{ast, AstNode, AstPtr}; 21use syntax::{ast, AstNode, AstPtr};
@@ -102,11 +102,11 @@ impl Expander {
102 db: &dyn DefDatabase, 102 db: &dyn DefDatabase,
103 local_scope: Option<&ItemScope>, 103 local_scope: Option<&ItemScope>,
104 macro_call: ast::MacroCall, 104 macro_call: ast::MacroCall,
105 ) -> Option<(Mark, T)> { 105 ) -> ExpandResult<Option<(Mark, T)>> {
106 self.recursion_limit += 1; 106 self.recursion_limit += 1;
107 if self.recursion_limit > EXPANSION_RECURSION_LIMIT { 107 if self.recursion_limit > EXPANSION_RECURSION_LIMIT {
108 mark::hit!(your_stack_belongs_to_me); 108 mark::hit!(your_stack_belongs_to_me);
109 return None; 109 return ExpandResult::str_err("reached recursion limit during macro expansion".into());
110 } 110 }
111 111
112 let macro_call = InFile::new(self.current_file_id, &macro_call); 112 let macro_call = InFile::new(self.current_file_id, &macro_call);
@@ -120,28 +120,55 @@ impl Expander {
120 self.resolve_path_as_macro(db, &path) 120 self.resolve_path_as_macro(db, &path)
121 }; 121 };
122 122
123 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { 123 let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) {
124 let file_id = call_id.as_file(); 124 Some(it) => it,
125 if let Some(node) = db.parse_or_expand(file_id) { 125 None => {
126 if let Some(expr) = T::cast(node) { 126 // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough
127 log::debug!("macro expansion {:#?}", expr.syntax()); 127 // info.
128 128 return ExpandResult::only_err(mbe::ExpandError::Other(
129 let mark = Mark { 129 "failed to parse or resolve macro invocation".into(),
130 file_id: self.current_file_id, 130 ));
131 ast_id_map: mem::take(&mut self.ast_id_map), 131 }
132 bomb: DropBomb::new("expansion mark dropped"), 132 };
133 }; 133
134 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); 134 let err = db.macro_expand_error(call_id);
135 self.current_file_id = file_id; 135
136 self.ast_id_map = db.ast_id_map(file_id); 136 let file_id = call_id.as_file();
137 return Some((mark, expr)); 137
138 let raw_node = match db.parse_or_expand(file_id) {
139 Some(it) => it,
140 None => {
141 // Only `None` if the macro expansion produced no usable AST.
142 if err.is_none() {
143 log::warn!("no error despite `parse_or_expand` failing");
138 } 144 }
145
146 return ExpandResult::only_err(err.unwrap_or_else(|| {
147 mbe::ExpandError::Other("failed to parse macro invocation".into())
148 }));
139 } 149 }
140 } 150 };
151
152 let node = match T::cast(raw_node) {
153 Some(it) => it,
154 None => {
155 // This can happen without being an error, so only forward previous errors.
156 return ExpandResult { value: None, err };
157 }
158 };
159
160 log::debug!("macro expansion {:#?}", node.syntax());
161
162 let mark = Mark {
163 file_id: self.current_file_id,
164 ast_id_map: mem::take(&mut self.ast_id_map),
165 bomb: DropBomb::new("expansion mark dropped"),
166 };
167 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
168 self.current_file_id = file_id;
169 self.ast_id_map = db.ast_id_map(file_id);
141 170
142 // FIXME: Instead of just dropping the error from expansion 171 ExpandResult { value: Some((mark, node)), err }
143 // report it
144 None
145 } 172 }
146 173
147 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { 174 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs
index e57bdc133..1de7d30e2 100644
--- a/crates/hir_def/src/body/diagnostics.rs
+++ b/crates/hir_def/src/body/diagnostics.rs
@@ -2,11 +2,13 @@
2 2
3use hir_expand::diagnostics::DiagnosticSink; 3use hir_expand::diagnostics::DiagnosticSink;
4 4
5use crate::diagnostics::InactiveCode; 5use crate::diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro};
6 6
7#[derive(Debug, Eq, PartialEq)] 7#[derive(Debug, Eq, PartialEq)]
8pub(crate) enum BodyDiagnostic { 8pub(crate) enum BodyDiagnostic {
9 InactiveCode(InactiveCode), 9 InactiveCode(InactiveCode),
10 MacroError(MacroError),
11 UnresolvedProcMacro(UnresolvedProcMacro),
10} 12}
11 13
12impl BodyDiagnostic { 14impl BodyDiagnostic {
@@ -15,6 +17,12 @@ impl BodyDiagnostic {
15 BodyDiagnostic::InactiveCode(diag) => { 17 BodyDiagnostic::InactiveCode(diag) => {
16 sink.push(diag.clone()); 18 sink.push(diag.clone());
17 } 19 }
20 BodyDiagnostic::MacroError(diag) => {
21 sink.push(diag.clone());
22 }
23 BodyDiagnostic::UnresolvedProcMacro(diag) => {
24 sink.push(diag.clone());
25 }
18 } 26 }
19 } 27 }
20} 28}
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index cd7958746..2c41c0005 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -8,7 +8,7 @@ use either::Either;
8use hir_expand::{ 8use hir_expand::{
9 hygiene::Hygiene, 9 hygiene::Hygiene,
10 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
11 HirFileId, MacroDefId, MacroDefKind, 11 ExpandError, HirFileId, MacroDefId, MacroDefKind,
12}; 12};
13use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
14use syntax::{ 14use syntax::{
@@ -25,7 +25,7 @@ use crate::{
25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, 25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
26 builtin_type::{BuiltinFloat, BuiltinInt}, 26 builtin_type::{BuiltinFloat, BuiltinInt},
27 db::DefDatabase, 27 db::DefDatabase,
28 diagnostics::InactiveCode, 28 diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro},
29 expr::{ 29 expr::{
30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, 30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal,
31 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, 31 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
@@ -561,7 +561,32 @@ impl ExprCollector<'_> {
561 self.alloc_expr(Expr::Missing, syntax_ptr) 561 self.alloc_expr(Expr::Missing, syntax_ptr)
562 } else { 562 } else {
563 let macro_call = self.expander.to_source(AstPtr::new(&e)); 563 let macro_call = self.expander.to_source(AstPtr::new(&e));
564 match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) { 564 let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e);
565
566 match res.err {
567 Some(ExpandError::UnresolvedProcMacro) => {
568 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
569 UnresolvedProcMacro {
570 file: self.expander.current_file_id,
571 node: syntax_ptr.clone().into(),
572 precise_location: None,
573 macro_name: None,
574 },
575 ));
576 }
577 Some(err) => {
578 self.source_map.diagnostics.push(BodyDiagnostic::MacroError(
579 MacroError {
580 file: self.expander.current_file_id,
581 node: syntax_ptr.clone().into(),
582 message: err.to_string(),
583 },
584 ));
585 }
586 None => {}
587 }
588
589 match res.value {
565 Some((mark, expansion)) => { 590 Some((mark, expansion)) => {
566 self.source_map 591 self.source_map
567 .expansions 592 .expansions
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index ff1ef0df6..146045938 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -257,7 +257,7 @@ fn collect_items(
257 let root = db.parse_or_expand(file_id).unwrap(); 257 let root = db.parse_or_expand(file_id).unwrap();
258 let call = ast_id_map.get(call.ast_id).to_node(&root); 258 let call = ast_id_map.get(call.ast_id).to_node(&root);
259 259
260 if let Some((mark, mac)) = expander.enter_expand(db, None, call) { 260 if let Some((mark, mac)) = expander.enter_expand(db, None, call).value {
261 let src: InFile<ast::MacroItems> = expander.to_source(mac); 261 let src: InFile<ast::MacroItems> = expander.to_source(mac);
262 let item_tree = db.item_tree(src.file_id); 262 let item_tree = db.item_tree(src.file_id);
263 let iter = 263 let iter =
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index b221b290c..c71266dc0 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -6,7 +6,7 @@ use stdx::format_to;
6use cfg::{CfgExpr, CfgOptions, DnfExpr}; 6use cfg::{CfgExpr, CfgOptions, DnfExpr};
7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; 7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
9use syntax::{ast, AstPtr, SyntaxNodePtr}; 9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10 10
11use crate::{db::DefDatabase, DefWithBodyId}; 11use crate::{db::DefDatabase, DefWithBodyId};
12 12
@@ -127,3 +127,68 @@ impl Diagnostic for InactiveCode {
127 self 127 self
128 } 128 }
129} 129}
130
131// Diagnostic: unresolved-proc-macro
132//
133// This diagnostic is shown when a procedural macro can not be found. This usually means that
134// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
135// but can also indicate project setup problems.
136#[derive(Debug, Clone, Eq, PartialEq)]
137pub struct UnresolvedProcMacro {
138 pub file: HirFileId,
139 pub node: SyntaxNodePtr,
140 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
141 /// to use instead.
142 pub precise_location: Option<TextRange>,
143 pub macro_name: Option<String>,
144}
145
146impl Diagnostic for UnresolvedProcMacro {
147 fn code(&self) -> DiagnosticCode {
148 DiagnosticCode("unresolved-proc-macro")
149 }
150
151 fn message(&self) -> String {
152 match &self.macro_name {
153 Some(name) => format!("proc macro `{}` not expanded", name),
154 None => "proc macro not expanded".to_string(),
155 }
156 }
157
158 fn display_source(&self) -> InFile<SyntaxNodePtr> {
159 InFile::new(self.file, self.node.clone())
160 }
161
162 fn as_any(&self) -> &(dyn Any + Send + 'static) {
163 self
164 }
165}
166
167// Diagnostic: macro-error
168//
169// This diagnostic is shown for macro expansion errors.
170#[derive(Debug, Clone, Eq, PartialEq)]
171pub struct MacroError {
172 pub file: HirFileId,
173 pub node: SyntaxNodePtr,
174 pub message: String,
175}
176
177impl Diagnostic for MacroError {
178 fn code(&self) -> DiagnosticCode {
179 DiagnosticCode("macro-error")
180 }
181 fn message(&self) -> String {
182 self.message.clone()
183 }
184 fn display_source(&self) -> InFile<SyntaxNodePtr> {
185 InFile::new(self.file, self.node.clone())
186 }
187 fn as_any(&self) -> &(dyn Any + Send + 'static) {
188 self
189 }
190 fn is_experimental(&self) -> bool {
191 // Newly added and not very well-tested, might contain false positives.
192 true
193 }
194}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 202a7dcb6..ffd0381d4 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -286,8 +286,9 @@ mod diagnostics {
286 use cfg::{CfgExpr, CfgOptions}; 286 use cfg::{CfgExpr, CfgOptions};
287 use hir_expand::diagnostics::DiagnosticSink; 287 use hir_expand::diagnostics::DiagnosticSink;
288 use hir_expand::hygiene::Hygiene; 288 use hir_expand::hygiene::Hygiene;
289 use hir_expand::InFile; 289 use hir_expand::{InFile, MacroCallKind};
290 use syntax::{ast, AstPtr}; 290 use syntax::ast::AttrsOwner;
291 use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr};
291 292
292 use crate::path::ModPath; 293 use crate::path::ModPath;
293 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; 294 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
@@ -301,6 +302,10 @@ mod diagnostics {
301 UnresolvedImport { ast: AstId<ast::Use>, index: usize }, 302 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
302 303
303 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, 304 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
305
306 UnresolvedProcMacro { ast: MacroCallKind },
307
308 MacroError { ast: MacroCallKind, message: String },
304 } 309 }
305 310
306 #[derive(Debug, PartialEq, Eq)] 311 #[derive(Debug, PartialEq, Eq)]
@@ -348,6 +353,18 @@ mod diagnostics {
348 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } 353 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
349 } 354 }
350 355
356 pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
357 Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } }
358 }
359
360 pub(super) fn macro_error(
361 container: LocalModuleId,
362 ast: MacroCallKind,
363 message: String,
364 ) -> Self {
365 Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } }
366 }
367
351 pub(super) fn add_to( 368 pub(super) fn add_to(
352 &self, 369 &self,
353 db: &dyn DefDatabase, 370 db: &dyn DefDatabase,
@@ -407,6 +424,72 @@ mod diagnostics {
407 opts: opts.clone(), 424 opts: opts.clone(),
408 }); 425 });
409 } 426 }
427
428 DiagnosticKind::UnresolvedProcMacro { ast } => {
429 let mut precise_location = None;
430 let (file, ast, name) = match ast {
431 MacroCallKind::FnLike(ast) => {
432 let node = ast.to_node(db.upcast());
433 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
434 }
435 MacroCallKind::Attr(ast, name) => {
436 let node = ast.to_node(db.upcast());
437
438 // Compute the precise location of the macro name's token in the derive
439 // list.
440 // FIXME: This does not handle paths to the macro, but neither does the
441 // rest of r-a.
442 let derive_attrs =
443 node.attrs().filter_map(|attr| match attr.as_simple_call() {
444 Some((name, args)) if name == "derive" => Some(args),
445 _ => None,
446 });
447 'outer: for attr in derive_attrs {
448 let tokens =
449 attr.syntax().children_with_tokens().filter_map(|elem| {
450 match elem {
451 syntax::NodeOrToken::Node(_) => None,
452 syntax::NodeOrToken::Token(tok) => Some(tok),
453 }
454 });
455 for token in tokens {
456 if token.kind() == SyntaxKind::IDENT
457 && token.to_string() == *name
458 {
459 precise_location = Some(token.text_range());
460 break 'outer;
461 }
462 }
463 }
464
465 (
466 ast.file_id,
467 SyntaxNodePtr::from(AstPtr::new(&node)),
468 Some(name.clone()),
469 )
470 }
471 };
472 sink.push(UnresolvedProcMacro {
473 file,
474 node: ast,
475 precise_location,
476 macro_name: name,
477 });
478 }
479
480 DiagnosticKind::MacroError { ast, message } => {
481 let (file, ast) = match ast {
482 MacroCallKind::FnLike(ast) => {
483 let node = ast.to_node(db.upcast());
484 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
485 }
486 MacroCallKind::Attr(ast, _) => {
487 let node = ast.to_node(db.upcast());
488 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
489 }
490 };
491 sink.push(MacroError { file, node: ast, message: message.clone() });
492 }
410 } 493 }
411 } 494 }
412 } 495 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 5ed9073e0..19cd713ba 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -7,7 +7,6 @@ use std::iter;
7 7
8use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
9use cfg::{CfgExpr, CfgOptions}; 9use cfg::{CfgExpr, CfgOptions};
10use hir_expand::InFile;
11use hir_expand::{ 10use hir_expand::{
12 ast_id_map::FileAstId, 11 ast_id_map::FileAstId,
13 builtin_derive::find_builtin_derive, 12 builtin_derive::find_builtin_derive,
@@ -16,6 +15,7 @@ use hir_expand::{
16 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
17 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 16 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
18}; 17};
18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21use test_utils::mark; 21use test_utils::mark;
@@ -812,7 +812,30 @@ impl DefCollector<'_> {
812 log::warn!("macro expansion is too deep"); 812 log::warn!("macro expansion is too deep");
813 return; 813 return;
814 } 814 }
815 let file_id: HirFileId = macro_call_id.as_file(); 815 let file_id = macro_call_id.as_file();
816
817 // First, fetch the raw expansion result for purposes of error reporting. This goes through
818 // `macro_expand_error` to avoid depending on the full expansion result (to improve
819 // incrementality).
820 let err = self.db.macro_expand_error(macro_call_id);
821 if let Some(err) = err {
822 if let MacroCallId::LazyMacro(id) = macro_call_id {
823 let loc: MacroCallLoc = self.db.lookup_intern_macro(id);
824
825 let diag = match err {
826 hir_expand::ExpandError::UnresolvedProcMacro => {
827 // Missing proc macros are non-fatal, so they are handled specially.
828 DefDiagnostic::unresolved_proc_macro(module_id, loc.kind)
829 }
830 _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
831 };
832
833 self.def_map.diagnostics.push(diag);
834 }
835 // FIXME: Handle eager macros.
836 }
837
838 // Then, fetch and process the item tree. This will reuse the expansion result from above.
816 let item_tree = self.db.item_tree(file_id); 839 let item_tree = self.db.item_tree(file_id);
817 let mod_dir = self.mod_dirs[&module_id].clone(); 840 let mod_dir = self.mod_dirs[&module_id].clone();
818 ModCollector { 841 ModCollector {
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index ba295fd9e..ef6f85e15 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -798,3 +798,24 @@ mod foo;
798"#, 798"#,
799 ); 799 );
800} 800}
801
802#[test]
803fn abs_path_ignores_local() {
804 check(
805 r#"
806//- /main.rs crate:main deps:core
807pub use ::core::hash::Hash;
808pub mod core {}
809
810//- /lib.rs crate:core
811pub mod hash { pub trait Hash {} }
812"#,
813 expect![[r#"
814 crate
815 Hash: t
816 core: t
817
818 crate::core
819 "#]],
820 );
821}
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
index 53cecb05f..ba0d1f0e7 100644
--- a/crates/hir_def/src/path/lower/lower_use.rs
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -76,7 +76,7 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) ->
76 Either::Left(name) => { 76 Either::Left(name) => {
77 // no type args in use 77 // no type args in use
78 let mut res = prefix.unwrap_or_else(|| ModPath { 78 let mut res = prefix.unwrap_or_else(|| ModPath {
79 kind: PathKind::Plain, 79 kind: segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
80 segments: Vec::with_capacity(1), 80 segments: Vec::with_capacity(1),
81 }); 81 });
82 res.segments.push(name); 82 res.segments.push(name);
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index aebbfc4df..7f4db106d 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -6,7 +6,7 @@ use crate::{
6 6
7use base_db::FileId; 7use base_db::FileId;
8use either::Either; 8use either::Either;
9use mbe::parse_to_token_tree; 9use mbe::{parse_to_token_tree, ExpandResult};
10use parser::FragmentKind; 10use parser::FragmentKind;
11use syntax::ast::{self, AstToken}; 11use syntax::ast::{self, AstToken};
12 12
@@ -28,7 +28,7 @@ macro_rules! register_builtin {
28 db: &dyn AstDatabase, 28 db: &dyn AstDatabase,
29 id: LazyMacroId, 29 id: LazyMacroId,
30 tt: &tt::Subtree, 30 tt: &tt::Subtree,
31 ) -> Result<tt::Subtree, mbe::ExpandError> { 31 ) -> ExpandResult<tt::Subtree> {
32 let expander = match *self { 32 let expander = match *self {
33 $( BuiltinFnLikeExpander::$kind => $expand, )* 33 $( BuiltinFnLikeExpander::$kind => $expand, )*
34 }; 34 };
@@ -42,7 +42,7 @@ macro_rules! register_builtin {
42 db: &dyn AstDatabase, 42 db: &dyn AstDatabase,
43 arg_id: EagerMacroId, 43 arg_id: EagerMacroId,
44 tt: &tt::Subtree, 44 tt: &tt::Subtree,
45 ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 45 ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
46 let expander = match *self { 46 let expander = match *self {
47 $( EagerExpander::$e_kind => $e_expand, )* 47 $( EagerExpander::$e_kind => $e_expand, )*
48 }; 48 };
@@ -109,25 +109,28 @@ fn line_expand(
109 _db: &dyn AstDatabase, 109 _db: &dyn AstDatabase,
110 _id: LazyMacroId, 110 _id: LazyMacroId,
111 _tt: &tt::Subtree, 111 _tt: &tt::Subtree,
112) -> Result<tt::Subtree, mbe::ExpandError> { 112) -> ExpandResult<tt::Subtree> {
113 // dummy implementation for type-checking purposes 113 // dummy implementation for type-checking purposes
114 let line_num = 0; 114 let line_num = 0;
115 let expanded = quote! { 115 let expanded = quote! {
116 #line_num 116 #line_num
117 }; 117 };
118 118
119 Ok(expanded) 119 ExpandResult::ok(expanded)
120} 120}
121 121
122fn stringify_expand( 122fn stringify_expand(
123 db: &dyn AstDatabase, 123 db: &dyn AstDatabase,
124 id: LazyMacroId, 124 id: LazyMacroId,
125 _tt: &tt::Subtree, 125 _tt: &tt::Subtree,
126) -> Result<tt::Subtree, mbe::ExpandError> { 126) -> ExpandResult<tt::Subtree> {
127 let loc = db.lookup_intern_macro(id); 127 let loc = db.lookup_intern_macro(id);
128 128
129 let macro_content = { 129 let macro_content = {
130 let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; 130 let arg = match loc.kind.arg(db) {
131 Some(arg) => arg,
132 None => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
133 };
131 let macro_args = arg; 134 let macro_args = arg;
132 let text = macro_args.text(); 135 let text = macro_args.text();
133 let without_parens = TextSize::of('(')..text.len() - TextSize::of(')'); 136 let without_parens = TextSize::of('(')..text.len() - TextSize::of(')');
@@ -138,28 +141,28 @@ fn stringify_expand(
138 #macro_content 141 #macro_content
139 }; 142 };
140 143
141 Ok(expanded) 144 ExpandResult::ok(expanded)
142} 145}
143 146
144fn column_expand( 147fn column_expand(
145 _db: &dyn AstDatabase, 148 _db: &dyn AstDatabase,
146 _id: LazyMacroId, 149 _id: LazyMacroId,
147 _tt: &tt::Subtree, 150 _tt: &tt::Subtree,
148) -> Result<tt::Subtree, mbe::ExpandError> { 151) -> ExpandResult<tt::Subtree> {
149 // dummy implementation for type-checking purposes 152 // dummy implementation for type-checking purposes
150 let col_num = 0; 153 let col_num = 0;
151 let expanded = quote! { 154 let expanded = quote! {
152 #col_num 155 #col_num
153 }; 156 };
154 157
155 Ok(expanded) 158 ExpandResult::ok(expanded)
156} 159}
157 160
158fn assert_expand( 161fn assert_expand(
159 _db: &dyn AstDatabase, 162 _db: &dyn AstDatabase,
160 _id: LazyMacroId, 163 _id: LazyMacroId,
161 tt: &tt::Subtree, 164 tt: &tt::Subtree,
162) -> Result<tt::Subtree, mbe::ExpandError> { 165) -> ExpandResult<tt::Subtree> {
163 // A hacky implementation for goto def and hover 166 // A hacky implementation for goto def and hover
164 // We expand `assert!(cond, arg1, arg2)` to 167 // We expand `assert!(cond, arg1, arg2)` to
165 // ``` 168 // ```
@@ -191,14 +194,14 @@ fn assert_expand(
191 let expanded = quote! { 194 let expanded = quote! {
192 { { (##arg_tts); } } 195 { { (##arg_tts); } }
193 }; 196 };
194 Ok(expanded) 197 ExpandResult::ok(expanded)
195} 198}
196 199
197fn file_expand( 200fn file_expand(
198 _db: &dyn AstDatabase, 201 _db: &dyn AstDatabase,
199 _id: LazyMacroId, 202 _id: LazyMacroId,
200 _tt: &tt::Subtree, 203 _tt: &tt::Subtree,
201) -> Result<tt::Subtree, mbe::ExpandError> { 204) -> ExpandResult<tt::Subtree> {
202 // FIXME: RA purposefully lacks knowledge of absolute file names 205 // FIXME: RA purposefully lacks knowledge of absolute file names
203 // so just return "". 206 // so just return "".
204 let file_name = ""; 207 let file_name = "";
@@ -207,31 +210,33 @@ fn file_expand(
207 #file_name 210 #file_name
208 }; 211 };
209 212
210 Ok(expanded) 213 ExpandResult::ok(expanded)
211} 214}
212 215
213fn compile_error_expand( 216fn compile_error_expand(
214 _db: &dyn AstDatabase, 217 _db: &dyn AstDatabase,
215 _id: LazyMacroId, 218 _id: LazyMacroId,
216 tt: &tt::Subtree, 219 tt: &tt::Subtree,
217) -> Result<tt::Subtree, mbe::ExpandError> { 220) -> ExpandResult<tt::Subtree> {
218 if tt.count() == 1 { 221 if tt.count() == 1 {
219 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] { 222 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
220 let s = it.text.as_str(); 223 let s = it.text.as_str();
221 if s.contains('"') { 224 if s.contains('"') {
222 return Ok(quote! { loop { #it }}); 225 return ExpandResult::ok(quote! { loop { #it }});
223 } 226 }
224 }; 227 };
225 } 228 }
226 229
227 Err(mbe::ExpandError::BindingError("Must be a string".into())) 230 ExpandResult::only_err(mbe::ExpandError::BindingError(
231 "`compile_error!` argument be a string".into(),
232 ))
228} 233}
229 234
230fn format_args_expand( 235fn format_args_expand(
231 _db: &dyn AstDatabase, 236 _db: &dyn AstDatabase,
232 _id: LazyMacroId, 237 _id: LazyMacroId,
233 tt: &tt::Subtree, 238 tt: &tt::Subtree,
234) -> Result<tt::Subtree, mbe::ExpandError> { 239) -> ExpandResult<tt::Subtree> {
235 // We expand `format_args!("", a1, a2)` to 240 // We expand `format_args!("", a1, a2)` to
236 // ``` 241 // ```
237 // std::fmt::Arguments::new_v1(&[], &[ 242 // std::fmt::Arguments::new_v1(&[], &[
@@ -257,7 +262,7 @@ fn format_args_expand(
257 args.push(current); 262 args.push(current);
258 } 263 }
259 if args.is_empty() { 264 if args.is_empty() {
260 return Err(mbe::ExpandError::NoMatchingRule); 265 return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
261 } 266 }
262 let _format_string = args.remove(0); 267 let _format_string = args.remove(0);
263 let arg_tts = args.into_iter().flat_map(|arg| { 268 let arg_tts = args.into_iter().flat_map(|arg| {
@@ -266,7 +271,7 @@ fn format_args_expand(
266 let expanded = quote! { 271 let expanded = quote! {
267 std::fmt::Arguments::new_v1(&[], &[##arg_tts]) 272 std::fmt::Arguments::new_v1(&[], &[##arg_tts])
268 }; 273 };
269 Ok(expanded) 274 ExpandResult::ok(expanded)
270} 275}
271 276
272fn unquote_str(lit: &tt::Literal) -> Option<String> { 277fn unquote_str(lit: &tt::Literal) -> Option<String> {
@@ -279,19 +284,24 @@ fn concat_expand(
279 _db: &dyn AstDatabase, 284 _db: &dyn AstDatabase,
280 _arg_id: EagerMacroId, 285 _arg_id: EagerMacroId,
281 tt: &tt::Subtree, 286 tt: &tt::Subtree,
282) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 287) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
283 let mut text = String::new(); 288 let mut text = String::new();
284 for (i, t) in tt.token_trees.iter().enumerate() { 289 for (i, t) in tt.token_trees.iter().enumerate() {
285 match t { 290 match t {
286 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { 291 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
287 text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?; 292 text += &match unquote_str(&it) {
293 Some(s) => s,
294 None => {
295 return ExpandResult::only_err(mbe::ExpandError::ConversionError);
296 }
297 };
288 } 298 }
289 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), 299 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
290 _ => return Err(mbe::ExpandError::UnexpectedToken), 300 _ => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
291 } 301 }
292 } 302 }
293 303
294 Ok((quote!(#text), FragmentKind::Expr)) 304 ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr)))
295} 305}
296 306
297fn relative_file( 307fn relative_file(
@@ -324,26 +334,35 @@ fn include_expand(
324 db: &dyn AstDatabase, 334 db: &dyn AstDatabase,
325 arg_id: EagerMacroId, 335 arg_id: EagerMacroId,
326 tt: &tt::Subtree, 336 tt: &tt::Subtree,
327) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 337) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
328 let path = parse_string(tt)?; 338 let res = (|| {
329 let file_id = relative_file(db, arg_id.into(), &path, false) 339 let path = parse_string(tt)?;
330 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 340 let file_id = relative_file(db, arg_id.into(), &path, false)
331 341 .ok_or_else(|| mbe::ExpandError::ConversionError)?;
332 // FIXME: 342
333 // Handle include as expression 343 Ok(parse_to_token_tree(&db.file_text(file_id))
334 let res = parse_to_token_tree(&db.file_text(file_id)) 344 .ok_or_else(|| mbe::ExpandError::ConversionError)?
335 .ok_or_else(|| mbe::ExpandError::ConversionError)? 345 .0)
336 .0; 346 })();
337 347
338 Ok((res, FragmentKind::Items)) 348 match res {
349 Ok(res) => {
350 // FIXME:
351 // Handle include as expression
352 ExpandResult::ok(Some((res, FragmentKind::Items)))
353 }
354 Err(e) => ExpandResult::only_err(e),
355 }
339} 356}
340 357
341fn include_bytes_expand( 358fn include_bytes_expand(
342 _db: &dyn AstDatabase, 359 _db: &dyn AstDatabase,
343 _arg_id: EagerMacroId, 360 _arg_id: EagerMacroId,
344 tt: &tt::Subtree, 361 tt: &tt::Subtree,
345) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 362) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
346 let _path = parse_string(tt)?; 363 if let Err(e) = parse_string(tt) {
364 return ExpandResult::only_err(e);
365 }
347 366
348 // FIXME: actually read the file here if the user asked for macro expansion 367 // FIXME: actually read the file here if the user asked for macro expansion
349 let res = tt::Subtree { 368 let res = tt::Subtree {
@@ -353,15 +372,18 @@ fn include_bytes_expand(
353 id: tt::TokenId::unspecified(), 372 id: tt::TokenId::unspecified(),
354 }))], 373 }))],
355 }; 374 };
356 Ok((res, FragmentKind::Expr)) 375 ExpandResult::ok(Some((res, FragmentKind::Expr)))
357} 376}
358 377
359fn include_str_expand( 378fn include_str_expand(
360 db: &dyn AstDatabase, 379 db: &dyn AstDatabase,
361 arg_id: EagerMacroId, 380 arg_id: EagerMacroId,
362 tt: &tt::Subtree, 381 tt: &tt::Subtree,
363) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 382) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
364 let path = parse_string(tt)?; 383 let path = match parse_string(tt) {
384 Ok(it) => it,
385 Err(e) => return ExpandResult::only_err(e),
386 };
365 387
366 // FIXME: we're not able to read excluded files (which is most of them because 388 // FIXME: we're not able to read excluded files (which is most of them because
367 // it's unusual to `include_str!` a Rust file), but we can return an empty string. 389 // it's unusual to `include_str!` a Rust file), but we can return an empty string.
@@ -370,14 +392,14 @@ fn include_str_expand(
370 let file_id = match relative_file(db, arg_id.into(), &path, true) { 392 let file_id = match relative_file(db, arg_id.into(), &path, true) {
371 Some(file_id) => file_id, 393 Some(file_id) => file_id,
372 None => { 394 None => {
373 return Ok((quote!(""), FragmentKind::Expr)); 395 return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr)));
374 } 396 }
375 }; 397 };
376 398
377 let text = db.file_text(file_id); 399 let text = db.file_text(file_id);
378 let text = &*text; 400 let text = &*text;
379 401
380 Ok((quote!(#text), FragmentKind::Expr)) 402 ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr)))
381} 403}
382 404
383fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 405fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
@@ -389,8 +411,11 @@ fn env_expand(
389 db: &dyn AstDatabase, 411 db: &dyn AstDatabase,
390 arg_id: EagerMacroId, 412 arg_id: EagerMacroId,
391 tt: &tt::Subtree, 413 tt: &tt::Subtree,
392) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 414) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
393 let key = parse_string(tt)?; 415 let key = match parse_string(tt) {
416 Ok(it) => it,
417 Err(e) => return ExpandResult::only_err(e),
418 };
394 419
395 // FIXME: 420 // FIXME:
396 // If the environment variable is not defined int rustc, then a compilation error will be emitted. 421 // If the environment variable is not defined int rustc, then a compilation error will be emitted.
@@ -402,21 +427,25 @@ fn env_expand(
402 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); 427 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string());
403 let expanded = quote! { #s }; 428 let expanded = quote! { #s };
404 429
405 Ok((expanded, FragmentKind::Expr)) 430 ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
406} 431}
407 432
408fn option_env_expand( 433fn option_env_expand(
409 db: &dyn AstDatabase, 434 db: &dyn AstDatabase,
410 arg_id: EagerMacroId, 435 arg_id: EagerMacroId,
411 tt: &tt::Subtree, 436 tt: &tt::Subtree,
412) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 437) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
413 let key = parse_string(tt)?; 438 let key = match parse_string(tt) {
439 Ok(it) => it,
440 Err(e) => return ExpandResult::only_err(e),
441 };
442
414 let expanded = match get_env_inner(db, arg_id, &key) { 443 let expanded = match get_env_inner(db, arg_id, &key) {
415 None => quote! { std::option::Option::None::<&str> }, 444 None => quote! { std::option::Option::None::<&str> },
416 Some(s) => quote! { std::option::Some(#s) }, 445 Some(s) => quote! { std::option::Some(#s) },
417 }; 446 };
418 447
419 Ok((expanded, FragmentKind::Expr)) 448 ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
420} 449}
421 450
422#[cfg(test)] 451#[cfg(test)]
@@ -485,7 +514,7 @@ mod tests {
485 } 514 }
486 }); 515 });
487 516
488 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap(); 517 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).value.unwrap();
489 let eager = EagerCallLoc { 518 let eager = EagerCallLoc {
490 def, 519 def,
491 fragment, 520 fragment,
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 46ebdbc74..4fd0ba290 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -3,7 +3,7 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use base_db::{salsa, SourceDatabase}; 5use base_db::{salsa, SourceDatabase};
6use mbe::{ExpandResult, MacroRules}; 6use mbe::{ExpandError, ExpandResult, MacroRules};
7use parser::FragmentKind; 7use parser::FragmentKind;
8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
9 9
@@ -30,8 +30,8 @@ impl TokenExpander {
30 ) -> mbe::ExpandResult<tt::Subtree> { 30 ) -> mbe::ExpandResult<tt::Subtree> {
31 match self { 31 match self {
32 TokenExpander::MacroRules(it) => it.expand(tt), 32 TokenExpander::MacroRules(it) => it.expand(tt),
33 TokenExpander::Builtin(it) => it.expand(db, id, tt),
33 // FIXME switch these to ExpandResult as well 34 // FIXME switch these to ExpandResult as well
34 TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
35 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 35 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
36 TokenExpander::ProcMacro(_) => { 36 TokenExpander::ProcMacro(_) => {
37 // We store the result in salsa db to prevent non-determinisc behavior in 37 // We store the result in salsa db to prevent non-determinisc behavior in
@@ -81,6 +81,9 @@ pub trait AstDatabase: SourceDatabase {
81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; 81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; 82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
83 83
84 /// Firewall query that returns the error from the `macro_expand` query.
85 fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
86
84 #[salsa::interned] 87 #[salsa::interned]
85 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 88 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
86 89
@@ -171,6 +174,10 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
171 macro_expand_with_arg(db, id, None) 174 macro_expand_with_arg(db, id, None)
172} 175}
173 176
177fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
178 db.macro_expand(macro_call).err
179}
180
174fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 181fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
175 let lazy_id = match id { 182 let lazy_id = match id {
176 MacroCallId::LazyMacro(id) => id, 183 MacroCallId::LazyMacro(id) => id,
@@ -189,6 +196,7 @@ fn macro_expand_with_arg(
189 id: MacroCallId, 196 id: MacroCallId,
190 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 197 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
191) -> ExpandResult<Option<Arc<tt::Subtree>>> { 198) -> ExpandResult<Option<Arc<tt::Subtree>>> {
199 let _p = profile::span("macro_expand");
192 let lazy_id = match id { 200 let lazy_id = match id {
193 MacroCallId::LazyMacro(id) => id, 201 MacroCallId::LazyMacro(id) => id,
194 MacroCallId::EagerMacro(id) => { 202 MacroCallId::EagerMacro(id) => {
@@ -276,14 +284,15 @@ fn parse_macro_with_arg(
276 macro_file: MacroFile, 284 macro_file: MacroFile,
277 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 285 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
278) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { 286) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
279 let _p = profile::span("parse_macro_query");
280
281 let macro_call_id = macro_file.macro_call_id; 287 let macro_call_id = macro_file.macro_call_id;
282 let result = if let Some(arg) = arg { 288 let result = if let Some(arg) = arg {
283 macro_expand_with_arg(db, macro_call_id, Some(arg)) 289 macro_expand_with_arg(db, macro_call_id, Some(arg))
284 } else { 290 } else {
285 db.macro_expand(macro_call_id) 291 db.macro_expand(macro_call_id)
286 }; 292 };
293
294 let _p = profile::span("parse_macro_expansion");
295
287 if let Some(err) = &result.err { 296 if let Some(err) = &result.err {
288 // Note: 297 // Note:
289 // The final goal we would like to make all parse_macro success, 298 // The final goal we would like to make all parse_macro success,
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs
index 1043c6aeb..bf0b85ce9 100644
--- a/crates/hir_expand/src/diagnostics.rs
+++ b/crates/hir_expand/src/diagnostics.rs
@@ -5,7 +5,7 @@
5//! 5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating 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 7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is sued internally). 8//! Diagnostic` or to any concrete diagnostic (downcasting is used internally).
9//! 9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them 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 11//! directly in salsa. For this reason, every hir subsytem defines it's own
@@ -32,7 +32,12 @@ impl DiagnosticCode {
32pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 32pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
33 fn code(&self) -> DiagnosticCode; 33 fn code(&self) -> DiagnosticCode;
34 fn message(&self) -> String; 34 fn message(&self) -> String;
35 /// Used in highlighting and related purposes 35 /// Source element that triggered the diagnostics.
36 ///
37 /// Note that this should reflect "semantics", rather than specific span we
38 /// want to highlight. When rendering the diagnostics into an error message,
39 /// the IDE will fetch the `SyntaxNode` and will narrow the span
40 /// appropriately.
36 fn display_source(&self) -> InFile<SyntaxNodePtr>; 41 fn display_source(&self) -> InFile<SyntaxNodePtr>;
37 fn as_any(&self) -> &(dyn Any + Send + 'static); 42 fn as_any(&self) -> &(dyn Any + Send + 'static);
38 fn is_experimental(&self) -> bool { 43 fn is_experimental(&self) -> bool {
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 2f37d7189..ab6b4477c 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -65,7 +65,7 @@ pub fn expand_eager_macro(
65 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
66 66
67 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?;
69 let eager = EagerCallLoc { 69 let eager = EagerCallLoc {
70 def, 70 def,
71 fragment, 71 fragment,
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index d5ba691b7..6dad2507b 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -255,7 +255,7 @@ pub enum MacroDefKind {
255pub struct MacroCallLoc { 255pub struct MacroCallLoc {
256 pub(crate) def: MacroDefId, 256 pub(crate) def: MacroDefId,
257 pub(crate) krate: CrateId, 257 pub(crate) krate: CrateId,
258 pub(crate) kind: MacroCallKind, 258 pub kind: MacroCallKind,
259} 259}
260 260
261#[derive(Debug, Clone, PartialEq, Eq, Hash)] 261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 7505cb061..97edf0fb6 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -50,7 +50,7 @@ impl ProcMacroExpander {
50 50
51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
52 } 52 }
53 None => Err(err!("Unresolved proc macro")), 53 None => Err(mbe::ExpandError::UnresolvedProcMacro),
54 } 54 }
55 } 55 }
56} 56}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3df73ed4f..9d3d88289 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -142,6 +142,15 @@ pub(crate) fn diagnostics(
142 .with_code(Some(d.code())), 142 .with_code(Some(d.code())),
143 ); 143 );
144 }) 144 })
145 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
146 // Use more accurate position if available.
147 let display_range =
148 d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range);
149
150 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
151 res.borrow_mut()
152 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
153 })
145 // Only collect experimental diagnostics when they're enabled. 154 // Only collect experimental diagnostics when they're enabled.
146 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 155 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
147 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 156 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 15792f947..b9810457f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,14 +1,10 @@
1use hir::Semantics; 1use hir::Semantics;
2use ide_db::{ 2use ide_db::{
3 base_db::FileId,
3 defs::{NameClass, NameRefClass}, 4 defs::{NameClass, NameRefClass},
4 symbol_index, RootDatabase, 5 symbol_index, RootDatabase,
5}; 6};
6use syntax::{ 7use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
7 ast::{self},
8 match_ast, AstNode,
9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset, T,
11};
12 8
13use crate::{ 9use crate::{
14 display::{ToNav, TryToNav}, 10 display::{ToNav, TryToNav},
@@ -44,6 +40,19 @@ pub(crate) fn goto_definition(
44 let nav = def.try_to_nav(sema.db)?; 40 let nav = def.try_to_nav(sema.db)?;
45 vec![nav] 41 vec![nav]
46 }, 42 },
43 ast::SelfParam(self_param) => {
44 vec![self_to_nav_target(self_param, position.file_id)?]
45 },
46 ast::PathSegment(segment) => {
47 segment.self_token()?;
48 let path = segment.parent_path();
49 if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
50 return None;
51 }
52 let func = segment.syntax().ancestors().find_map(ast::Fn::cast)?;
53 let self_param = func.param_list()?.self_param()?;
54 vec![self_to_nav_target(self_param, position.file_id)?]
55 },
47 _ => return None, 56 _ => return None,
48 } 57 }
49 }; 58 };
@@ -62,6 +71,20 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
62 } 71 }
63} 72}
64 73
74fn self_to_nav_target(self_param: ast::SelfParam, file_id: FileId) -> Option<NavigationTarget> {
75 let self_token = self_param.self_token()?;
76 Some(NavigationTarget {
77 file_id,
78 full_range: self_param.syntax().text_range(),
79 focus_range: Some(self_token.text_range()),
80 name: self_token.text().clone(),
81 kind: self_token.kind(),
82 container_name: None,
83 description: None,
84 docs: None,
85 })
86}
87
65#[derive(Debug)] 88#[derive(Debug)]
66pub(crate) enum ReferenceResult { 89pub(crate) enum ReferenceResult {
67 Exact(NavigationTarget), 90 Exact(NavigationTarget),
@@ -984,4 +1007,33 @@ fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {}
984"#, 1007"#,
985 ); 1008 );
986 } 1009 }
1010
1011 #[test]
1012 fn goto_self_param_ty_specified() {
1013 check(
1014 r#"
1015struct Foo {}
1016
1017impl Foo {
1018 fn bar(self: &Foo) {
1019 //^^^^
1020 let foo = sel<|>f;
1021 }
1022}"#,
1023 )
1024 }
1025
1026 #[test]
1027 fn goto_self_param_on_decl() {
1028 check(
1029 r#"
1030struct Foo {}
1031
1032impl Foo {
1033 fn bar(&self<|>) {
1034 //^^^^
1035 }
1036}"#,
1037 )
1038 }
987} 1039}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 832192881..462f5c2b8 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -139,14 +139,17 @@ pub(crate) fn hover(
139 } 139 }
140 } 140 }
141 141
142 let node = token 142 let node = token.ancestors().find(|n| {
143 .ancestors() 143 ast::Expr::can_cast(n.kind())
144 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; 144 || ast::Pat::can_cast(n.kind())
145 || ast::SelfParam::can_cast(n.kind())
146 })?;