mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-30 15:03:42 +00:00
Merge branch 'master' into feature/themes
This commit is contained in:
commit
dad9bc6caa
61 changed files with 1807 additions and 1309 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1 +1,2 @@
|
||||||
crates/ra_syntax/tests/data/** -text
|
crates/ra_syntax/test_data/** -text eof=LF
|
||||||
|
crates/ra_ide_api/src/snapshots/** -text eof=LF
|
||||||
|
|
59
Cargo.lock
generated
59
Cargo.lock
generated
|
@ -126,7 +126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-derive"
|
name = "chalk-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -136,9 +136,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-engine"
|
name = "chalk-engine"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -146,18 +146,18 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-ir"
|
name = "chalk-ir"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-macros"
|
name = "chalk-macros"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -165,23 +165,23 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-rust-ir"
|
name = "chalk-rust-ir"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-solve"
|
name = "chalk-solve"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -985,6 +985,7 @@ dependencies = [
|
||||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"salsa 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"salsa 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"test_utils 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1000,9 +1001,9 @@ name = "ra_hir"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
"chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)",
|
||||||
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1018,7 +1019,6 @@ dependencies = [
|
||||||
"ra_prof 0.1.0",
|
"ra_prof 0.1.0",
|
||||||
"ra_syntax 0.1.0",
|
"ra_syntax 0.1.0",
|
||||||
"ra_tt 0.1.0",
|
"ra_tt 0.1.0",
|
||||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"test_utils 0.1.0",
|
"test_utils 0.1.0",
|
||||||
]
|
]
|
||||||
|
@ -1027,6 +1027,7 @@ dependencies = [
|
||||||
name = "ra_hir_def"
|
name = "ra_hir_def"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ra_arena 0.1.0",
|
"ra_arena 0.1.0",
|
||||||
|
@ -1037,7 +1038,6 @@ dependencies = [
|
||||||
"ra_prof 0.1.0",
|
"ra_prof 0.1.0",
|
||||||
"ra_syntax 0.1.0",
|
"ra_syntax 0.1.0",
|
||||||
"ra_tt 0.1.0",
|
"ra_tt 0.1.0",
|
||||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"test_utils 0.1.0",
|
"test_utils 0.1.0",
|
||||||
]
|
]
|
||||||
|
@ -1077,7 +1077,6 @@ dependencies = [
|
||||||
"ra_text_edit 0.1.0",
|
"ra_text_edit 0.1.0",
|
||||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"test_utils 0.1.0",
|
"test_utils 0.1.0",
|
||||||
|
@ -1849,12 +1848,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d"
|
"checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d"
|
||||||
"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c"
|
"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c"
|
||||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
"checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
"checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
|
||||||
"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
|
||||||
"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
|
||||||
"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
|
||||||
"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
|
||||||
"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "<none>"
|
||||||
"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
|
"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
|
||||||
"checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
|
"checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
|
||||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
|
|
|
@ -3,9 +3,10 @@ use std::ops::RangeInclusive;
|
||||||
use hir::db::HirDatabase;
|
use hir::db::HirDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::replace_children,
|
algo::replace_children,
|
||||||
ast::{self, edit::IndentLevel, make},
|
ast::{self, edit::IndentLevel, make, Block, Pat::TupleStructPat},
|
||||||
AstNode,
|
AstNode,
|
||||||
SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
|
SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
|
||||||
|
SyntaxNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -37,7 +38,23 @@ use crate::{
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
||||||
let expr = if_expr.condition()?.expr()?;
|
let cond = if_expr.condition()?;
|
||||||
|
let mut if_let_ident: Option<String> = None;
|
||||||
|
|
||||||
|
// Check if there is an IfLet that we can handle.
|
||||||
|
match cond.pat() {
|
||||||
|
None => {} // No IfLet, supported.
|
||||||
|
Some(TupleStructPat(ref pat)) if pat.args().count() == 1usize => match &pat.path() {
|
||||||
|
Some(p) => match p.qualifier() {
|
||||||
|
None => if_let_ident = Some(p.syntax().text().to_string()),
|
||||||
|
_ => return None,
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
},
|
||||||
|
_ => return None, // Unsupported IfLet.
|
||||||
|
};
|
||||||
|
|
||||||
|
let expr = cond.expr()?;
|
||||||
let then_block = if_expr.then_branch()?.block()?;
|
let then_block = if_expr.then_branch()?.block()?;
|
||||||
if if_expr.else_branch().is_some() {
|
if if_expr.else_branch().is_some() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -63,8 +80,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||||
let parent_container = parent_block.syntax().parent()?.parent()?;
|
let parent_container = parent_block.syntax().parent()?.parent()?;
|
||||||
|
|
||||||
let early_expression = match parent_container.kind() {
|
let early_expression = match parent_container.kind() {
|
||||||
WHILE_EXPR | LOOP_EXPR => Some("continue;"),
|
WHILE_EXPR | LOOP_EXPR => Some("continue"),
|
||||||
FN_DEF => Some("return;"),
|
FN_DEF => Some("return"),
|
||||||
_ => None,
|
_ => None,
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
@ -77,8 +94,34 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||||
|
|
||||||
ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
|
ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
|
||||||
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
||||||
let new_if_expr =
|
let new_block = match if_let_ident {
|
||||||
|
None => {
|
||||||
|
// If.
|
||||||
|
let early_expression = &(early_expression.to_owned() + ";");
|
||||||
|
let new_expr =
|
||||||
if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
|
if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
|
||||||
|
replace(new_expr, &then_block, &parent_block, &if_expr)
|
||||||
|
}
|
||||||
|
Some(if_let_ident) => {
|
||||||
|
// If-let.
|
||||||
|
let new_expr = if_indent_level.increase_indent(make::let_match_early(
|
||||||
|
expr,
|
||||||
|
&if_let_ident,
|
||||||
|
early_expression,
|
||||||
|
));
|
||||||
|
replace(new_expr, &then_block, &parent_block, &if_expr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
edit.target(if_expr.syntax().text_range());
|
||||||
|
edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
|
||||||
|
edit.set_cursor(cursor_position);
|
||||||
|
|
||||||
|
fn replace(
|
||||||
|
new_expr: impl AstNode,
|
||||||
|
then_block: &Block,
|
||||||
|
parent_block: &Block,
|
||||||
|
if_expr: &ast::IfExpr,
|
||||||
|
) -> SyntaxNode {
|
||||||
let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
|
let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
|
||||||
let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
|
let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
|
||||||
let end_of_then =
|
let end_of_then =
|
||||||
|
@ -87,24 +130,22 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||||
} else {
|
} else {
|
||||||
end_of_then
|
end_of_then
|
||||||
};
|
};
|
||||||
let mut new_if_and_then_statements = new_if_expr.syntax().children_with_tokens().chain(
|
let mut then_statements = new_expr.syntax().children_with_tokens().chain(
|
||||||
then_block_items
|
then_block_items
|
||||||
.syntax()
|
.syntax()
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.take_while(|i| *i != end_of_then),
|
.take_while(|i| *i != end_of_then),
|
||||||
);
|
);
|
||||||
let new_block = replace_children(
|
replace_children(
|
||||||
&parent_block.syntax(),
|
&parent_block.syntax(),
|
||||||
RangeInclusive::new(
|
RangeInclusive::new(
|
||||||
if_expr.clone().syntax().clone().into(),
|
if_expr.clone().syntax().clone().into(),
|
||||||
if_expr.syntax().clone().into(),
|
if_expr.syntax().clone().into(),
|
||||||
),
|
),
|
||||||
&mut new_if_and_then_statements,
|
&mut then_statements,
|
||||||
);
|
)
|
||||||
edit.target(if_expr.syntax().text_range());
|
}
|
||||||
edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
|
|
||||||
edit.set_cursor(cursor_position);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +184,68 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_let_inside_fn() {
|
||||||
|
check_assist(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
fn main(n: Option<String>) {
|
||||||
|
bar();
|
||||||
|
if<|> let Some(n) = n {
|
||||||
|
foo(n);
|
||||||
|
|
||||||
|
//comment
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main(n: Option<String>) {
|
||||||
|
bar();
|
||||||
|
le<|>t n = match n {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
foo(n);
|
||||||
|
|
||||||
|
//comment
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_let_ok_inside_fn() {
|
||||||
|
check_assist(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
fn main(n: Option<String>) {
|
||||||
|
bar();
|
||||||
|
if<|> let Ok(n) = n {
|
||||||
|
foo(n);
|
||||||
|
|
||||||
|
//comment
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main(n: Option<String>) {
|
||||||
|
bar();
|
||||||
|
le<|>t n = match n {
|
||||||
|
Ok(it) => it,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
foo(n);
|
||||||
|
|
||||||
|
//comment
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert_inside_while() {
|
fn convert_inside_while() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -171,6 +274,35 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_let_inside_while() {
|
||||||
|
check_assist(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
while true {
|
||||||
|
if<|> let Some(n) = n {
|
||||||
|
foo(n);
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
while true {
|
||||||
|
le<|>t n = match n {
|
||||||
|
Some(it) => it,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
foo(n);
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert_inside_loop() {
|
fn convert_inside_loop() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -199,6 +331,35 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_let_inside_loop() {
|
||||||
|
check_assist(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
if<|> let Some(n) = n {
|
||||||
|
foo(n);
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
le<|>t n = match n {
|
||||||
|
Some(it) => it,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
foo(n);
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignore_already_converted_if() {
|
fn ignore_already_converted_if() {
|
||||||
check_assist_not_applicable(
|
check_assist_not_applicable(
|
||||||
|
|
|
@ -12,3 +12,4 @@ rustc-hash = "1.0"
|
||||||
ra_syntax = { path = "../ra_syntax" }
|
ra_syntax = { path = "../ra_syntax" }
|
||||||
ra_cfg = { path = "../ra_cfg" }
|
ra_cfg = { path = "../ra_cfg" }
|
||||||
ra_prof = { path = "../ra_prof" }
|
ra_prof = { path = "../ra_prof" }
|
||||||
|
test_utils = { path = "../test_utils" }
|
||||||
|
|
186
crates/ra_db/src/fixture.rs
Normal file
186
crates/ra_db/src/fixture.rs
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ra_cfg::CfgOptions;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CrateGraph, Edition, FileId, FilePosition, RelativePathBuf, SourceDatabaseExt, SourceRoot,
|
||||||
|
SourceRootId,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const WORKSPACE: SourceRootId = SourceRootId(0);
|
||||||
|
|
||||||
|
pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
||||||
|
fn with_single_file(text: &str) -> (Self, FileId) {
|
||||||
|
let mut db = Self::default();
|
||||||
|
let file_id = with_single_file(&mut db, text);
|
||||||
|
(db, file_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_files(fixture: &str) -> Self {
|
||||||
|
let mut db = Self::default();
|
||||||
|
let pos = with_files(&mut db, fixture);
|
||||||
|
assert!(pos.is_none());
|
||||||
|
db
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_position(fixture: &str) -> (Self, FilePosition) {
|
||||||
|
let mut db = Self::default();
|
||||||
|
let pos = with_files(&mut db, fixture);
|
||||||
|
(db, pos.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}
|
||||||
|
|
||||||
|
fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId {
|
||||||
|
let file_id = FileId(0);
|
||||||
|
let rel_path: RelativePathBuf = "/main.rs".into();
|
||||||
|
|
||||||
|
let mut source_root = SourceRoot::default();
|
||||||
|
source_root.insert_file(rel_path.clone(), file_id);
|
||||||
|
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default());
|
||||||
|
|
||||||
|
db.set_file_text(file_id, Arc::new(text.to_string()));
|
||||||
|
db.set_file_relative_path(file_id, rel_path);
|
||||||
|
db.set_file_source_root(file_id, WORKSPACE);
|
||||||
|
db.set_source_root(WORKSPACE, Arc::new(source_root));
|
||||||
|
db.set_crate_graph(Arc::new(crate_graph));
|
||||||
|
|
||||||
|
file_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosition> {
|
||||||
|
let fixture = parse_fixture(fixture);
|
||||||
|
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
let mut crates = FxHashMap::default();
|
||||||
|
let mut crate_deps = Vec::new();
|
||||||
|
let mut default_crate_root: Option<FileId> = None;
|
||||||
|
|
||||||
|
let mut source_root = SourceRoot::default();
|
||||||
|
let mut source_root_id = WORKSPACE;
|
||||||
|
let mut source_root_prefix: RelativePathBuf = "/".into();
|
||||||
|
let mut file_id = FileId(0);
|
||||||
|
|
||||||
|
let mut file_position = None;
|
||||||
|
|
||||||
|
for entry in fixture.iter() {
|
||||||
|
let meta = match parse_meta(&entry.meta) {
|
||||||
|
ParsedMeta::Root { path } => {
|
||||||
|
let source_root = std::mem::replace(&mut source_root, SourceRoot::default());
|
||||||
|
db.set_source_root(source_root_id, Arc::new(source_root));
|
||||||
|
source_root_id.0 += 1;
|
||||||
|
source_root_prefix = path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ParsedMeta::File(it) => it,
|
||||||
|
};
|
||||||
|
assert!(meta.path.starts_with(&source_root_prefix));
|
||||||
|
|
||||||
|
if let Some(krate) = meta.krate {
|
||||||
|
let crate_id = crate_graph.add_crate_root(file_id, meta.edition, meta.cfg);
|
||||||
|
let prev = crates.insert(krate.clone(), crate_id);
|
||||||
|
assert!(prev.is_none());
|
||||||
|
for dep in meta.deps {
|
||||||
|
crate_deps.push((krate.clone(), dep))
|
||||||
|
}
|
||||||
|
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
|
||||||
|
assert!(default_crate_root.is_none());
|
||||||
|
default_crate_root = Some(file_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = if entry.text.contains(CURSOR_MARKER) {
|
||||||
|
let (offset, text) = extract_offset(&entry.text);
|
||||||
|
assert!(file_position.is_none());
|
||||||
|
file_position = Some(FilePosition { file_id, offset });
|
||||||
|
text.to_string()
|
||||||
|
} else {
|
||||||
|
entry.text.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
db.set_file_text(file_id, Arc::new(text));
|
||||||
|
db.set_file_relative_path(file_id, meta.path.clone());
|
||||||
|
db.set_file_source_root(file_id, source_root_id);
|
||||||
|
source_root.insert_file(meta.path, file_id);
|
||||||
|
|
||||||
|
file_id.0 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if crates.is_empty() {
|
||||||
|
let crate_root = default_crate_root.unwrap();
|
||||||
|
crate_graph.add_crate_root(crate_root, Edition::Edition2018, CfgOptions::default());
|
||||||
|
} else {
|
||||||
|
for (from, to) in crate_deps {
|
||||||
|
let from_id = crates[&from];
|
||||||
|
let to_id = crates[&to];
|
||||||
|
crate_graph.add_dep(from_id, to.into(), to_id).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.set_source_root(source_root_id, Arc::new(source_root));
|
||||||
|
db.set_crate_graph(Arc::new(crate_graph));
|
||||||
|
|
||||||
|
file_position
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ParsedMeta {
|
||||||
|
Root { path: RelativePathBuf },
|
||||||
|
File(FileMeta),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileMeta {
|
||||||
|
path: RelativePathBuf,
|
||||||
|
krate: Option<String>,
|
||||||
|
deps: Vec<String>,
|
||||||
|
cfg: CfgOptions,
|
||||||
|
edition: Edition,
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /lib.rs crate:foo deps:bar,baz
|
||||||
|
fn parse_meta(meta: &str) -> ParsedMeta {
|
||||||
|
let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if components[0] == "root" {
|
||||||
|
let path: RelativePathBuf = components[1].into();
|
||||||
|
assert!(path.starts_with("/") && path.ends_with("/"));
|
||||||
|
return ParsedMeta::Root { path };
|
||||||
|
}
|
||||||
|
|
||||||
|
let path: RelativePathBuf = components[0].into();
|
||||||
|
assert!(path.starts_with("/"));
|
||||||
|
|
||||||
|
let mut krate = None;
|
||||||
|
let mut deps = Vec::new();
|
||||||
|
let mut edition = Edition::Edition2018;
|
||||||
|
let mut cfg = CfgOptions::default();
|
||||||
|
for component in components[1..].iter() {
|
||||||
|
let (key, value) = split1(component, ':').unwrap();
|
||||||
|
match key {
|
||||||
|
"crate" => krate = Some(value.to_string()),
|
||||||
|
"deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
|
||||||
|
"edition" => edition = Edition::from_string(&value),
|
||||||
|
"cfg" => {
|
||||||
|
for key in value.split(',') {
|
||||||
|
match split1(key, '=') {
|
||||||
|
None => cfg.insert_atom(key.into()),
|
||||||
|
Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("bad component: {:?}", component),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
|
||||||
|
let idx = haystack.find(delim)?;
|
||||||
|
Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
|
||||||
|
}
|
|
@ -6,13 +6,14 @@
|
||||||
//! actual IO. See `vfs` and `project_model` in the `ra_lsp_server` crate for how
|
//! actual IO. See `vfs` and `project_model` in the `ra_lsp_server` crate for how
|
||||||
//! actual IO is done and lowered to input.
|
//! actual IO is done and lowered to input.
|
||||||
|
|
||||||
use relative_path::{RelativePath, RelativePathBuf};
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
|
use crate::{RelativePath, RelativePathBuf};
|
||||||
|
|
||||||
/// `FileId` is an integer which uniquely identifies a file. File paths are
|
/// `FileId` is an integer which uniquely identifies a file. File paths are
|
||||||
/// messy and system-dependent, so most of the code should work directly with
|
/// messy and system-dependent, so most of the code should work directly with
|
||||||
/// `FileId`, without inspecting the path. The mapping between `FileId` and path
|
/// `FileId`, without inspecting the path. The mapping between `FileId` and path
|
||||||
|
@ -97,6 +98,7 @@ pub enum Edition {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edition {
|
impl Edition {
|
||||||
|
//FIXME: replace with FromStr with proper error handling
|
||||||
pub fn from_string(s: &str) -> Edition {
|
pub fn from_string(s: &str) -> Edition {
|
||||||
match s {
|
match s {
|
||||||
"2015" => Edition::Edition2015,
|
"2015" => Edition::Edition2015,
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
//! ra_db defines basic database traits. The concrete DB is defined by ra_ide_api.
|
//! ra_db defines basic database traits. The concrete DB is defined by ra_ide_api.
|
||||||
mod cancellation;
|
mod cancellation;
|
||||||
mod input;
|
mod input;
|
||||||
|
pub mod fixture;
|
||||||
|
|
||||||
use std::{panic, sync::Arc};
|
use std::{panic, sync::Arc};
|
||||||
|
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit};
|
use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit};
|
||||||
use relative_path::{RelativePath, RelativePathBuf};
|
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
cancellation::Canceled,
|
cancellation::Canceled,
|
||||||
input::{CrateGraph, CrateId, Dependency, Edition, FileId, SourceRoot, SourceRootId},
|
input::{CrateGraph, CrateId, Dependency, Edition, FileId, SourceRoot, SourceRootId},
|
||||||
};
|
};
|
||||||
|
pub use relative_path::{RelativePath, RelativePathBuf};
|
||||||
pub use salsa;
|
pub use salsa;
|
||||||
|
|
||||||
pub trait CheckCanceled {
|
pub trait CheckCanceled {
|
||||||
|
|
|
@ -7,7 +7,6 @@ authors = ["rust-analyzer developers"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arrayvec = "0.5.1"
|
arrayvec = "0.5.1"
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
relative-path = "1.0.0"
|
|
||||||
rustc-hash = "1.0"
|
rustc-hash = "1.0"
|
||||||
parking_lot = "0.9.0"
|
parking_lot = "0.9.0"
|
||||||
ena = "0.13"
|
ena = "0.13"
|
||||||
|
@ -24,9 +23,9 @@ hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
|
||||||
test_utils = { path = "../test_utils" }
|
test_utils = { path = "../test_utils" }
|
||||||
ra_prof = { path = "../ra_prof" }
|
ra_prof = { path = "../ra_prof" }
|
||||||
|
|
||||||
chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" }
|
chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "50f9f636123bd88d0cc1b958749981d6702e4d05" }
|
||||||
chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" }
|
chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "50f9f636123bd88d0cc1b958749981d6702e4d05" }
|
||||||
chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" }
|
chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "50f9f636123bd88d0cc1b958749981d6702e4d05" }
|
||||||
lalrpop-intern = "0.15.1"
|
lalrpop-intern = "0.15.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -9,16 +9,18 @@ use hir_def::{
|
||||||
adt::VariantData,
|
adt::VariantData,
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
type_ref::{Mutability, TypeRef},
|
type_ref::{Mutability, TypeRef},
|
||||||
CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId,
|
CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId, UnionId,
|
||||||
|
};
|
||||||
|
use hir_expand::{
|
||||||
|
diagnostics::DiagnosticSink,
|
||||||
|
name::{self, AsName},
|
||||||
};
|
};
|
||||||
use hir_expand::name::{self, AsName};
|
|
||||||
use ra_db::{CrateId, Edition};
|
use ra_db::{CrateId, Edition};
|
||||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||||
diagnostics::DiagnosticSink,
|
|
||||||
expr::{validation::ExprValidator, Body, BodySourceMap},
|
expr::{validation::ExprValidator, Body, BodySourceMap},
|
||||||
generics::HasGenericParams,
|
generics::HasGenericParams,
|
||||||
ids::{
|
ids::{
|
||||||
|
@ -26,11 +28,10 @@ use crate::{
|
||||||
TypeAliasId,
|
TypeAliasId,
|
||||||
},
|
},
|
||||||
impl_block::ImplBlock,
|
impl_block::ImplBlock,
|
||||||
nameres::{ImportId, ModuleScope, Namespace},
|
|
||||||
resolve::{Resolver, Scope, TypeNs},
|
resolve::{Resolver, Scope, TypeNs},
|
||||||
traits::TraitData,
|
traits::TraitData,
|
||||||
ty::{InferenceResult, TraitRef},
|
ty::{InferenceResult, TraitRef},
|
||||||
Either, HasSource, Name, Ty,
|
Either, HasSource, Name, ScopeDef, Ty, {ImportId, Namespace},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// hir::Crate describes a single crate. It's the main interface with which
|
/// hir::Crate describes a single crate. It's the main interface with which
|
||||||
|
@ -64,7 +65,7 @@ impl Crate {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_module(self, db: &impl DefDatabase) -> Option<Module> {
|
pub fn root_module(self, db: &impl DefDatabase) -> Option<Module> {
|
||||||
let module_id = db.crate_def_map(self).root();
|
let module_id = db.crate_def_map(self.crate_id).root();
|
||||||
Some(Module::new(self, module_id))
|
Some(Module::new(self, module_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +119,7 @@ impl Module {
|
||||||
|
|
||||||
/// Name of this module.
|
/// Name of this module.
|
||||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
let parent = def_map[self.id.module_id].parent?;
|
let parent = def_map[self.id.module_id].parent?;
|
||||||
def_map[parent].children.iter().find_map(|(name, module_id)| {
|
def_map[parent].children.iter().find_map(|(name, module_id)| {
|
||||||
if *module_id == self.id.module_id {
|
if *module_id == self.id.module_id {
|
||||||
|
@ -149,20 +150,20 @@ impl Module {
|
||||||
/// might be missing `krate`. This can happen if a module's file is not included
|
/// might be missing `krate`. This can happen if a module's file is not included
|
||||||
/// in the module tree of any target in `Cargo.toml`.
|
/// in the module tree of any target in `Cargo.toml`.
|
||||||
pub fn crate_root(self, db: &impl DefDatabase) -> Module {
|
pub fn crate_root(self, db: &impl DefDatabase) -> Module {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
self.with_module_id(def_map.root())
|
self.with_module_id(def_map.root())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds a child module with the specified name.
|
/// Finds a child module with the specified name.
|
||||||
pub fn child(self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
|
pub fn child(self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
let child_id = def_map[self.id.module_id].children.get(name)?;
|
let child_id = def_map[self.id.module_id].children.get(name)?;
|
||||||
Some(self.with_module_id(*child_id))
|
Some(self.with_module_id(*child_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates over all child modules.
|
/// Iterates over all child modules.
|
||||||
pub fn children(self, db: &impl DefDatabase) -> impl Iterator<Item = Module> {
|
pub fn children(self, db: &impl DefDatabase) -> impl Iterator<Item = Module> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
let children = def_map[self.id.module_id]
|
let children = def_map[self.id.module_id]
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -173,7 +174,7 @@ impl Module {
|
||||||
|
|
||||||
/// Finds a parent module.
|
/// Finds a parent module.
|
||||||
pub fn parent(self, db: &impl DefDatabase) -> Option<Module> {
|
pub fn parent(self, db: &impl DefDatabase) -> Option<Module> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
let parent_id = def_map[self.id.module_id].parent?;
|
let parent_id = def_map[self.id.module_id].parent?;
|
||||||
Some(self.with_module_id(parent_id))
|
Some(self.with_module_id(parent_id))
|
||||||
}
|
}
|
||||||
|
@ -189,12 +190,16 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `ModuleScope`: a set of items, visible in this module.
|
/// Returns a `ModuleScope`: a set of items, visible in this module.
|
||||||
pub fn scope(self, db: &impl HirDatabase) -> ModuleScope {
|
pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef, Option<ImportId>)> {
|
||||||
db.crate_def_map(self.krate())[self.id.module_id].scope.clone()
|
db.crate_def_map(self.id.krate)[self.id.module_id]
|
||||||
|
.scope
|
||||||
|
.entries()
|
||||||
|
.map(|(name, res)| (name.clone(), res.def.into(), res.import))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
|
pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
|
||||||
db.crate_def_map(self.krate()).add_diagnostics(db, self.id.module_id, sink);
|
db.crate_def_map(self.id.krate).add_diagnostics(db, self.id.module_id, sink);
|
||||||
for decl in self.declarations(db) {
|
for decl in self.declarations(db) {
|
||||||
match decl {
|
match decl {
|
||||||
crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
|
crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
|
||||||
|
@ -218,12 +223,12 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
pub(crate) fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
Resolver::default().push_module_scope(def_map, self.id.module_id)
|
Resolver::default().push_module_scope(def_map, self.id.module_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declarations(self, db: &impl DefDatabase) -> Vec<ModuleDef> {
|
pub fn declarations(self, db: &impl DefDatabase) -> Vec<ModuleDef> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
def_map[self.id.module_id]
|
def_map[self.id.module_id]
|
||||||
.scope
|
.scope
|
||||||
.entries()
|
.entries()
|
||||||
|
@ -231,6 +236,7 @@ impl Module {
|
||||||
.flat_map(|per_ns| {
|
.flat_map(|per_ns| {
|
||||||
per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
|
per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
|
||||||
})
|
})
|
||||||
|
.map(ModuleDef::from)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,12 +340,12 @@ impl Struct {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Union {
|
pub struct Union {
|
||||||
pub(crate) id: StructId,
|
pub(crate) id: UnionId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Union {
|
impl Union {
|
||||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||||
db.struct_data(self.id).name.clone()
|
db.union_data(self.id).name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(self, db: &impl HirDatabase) -> Module {
|
pub fn module(self, db: &impl HirDatabase) -> Module {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use hir_def::Source;
|
pub use hir_expand::Source;
|
||||||
|
|
||||||
pub trait HasSource {
|
pub trait HasSource {
|
||||||
type Ast;
|
type Ast;
|
||||||
|
@ -22,7 +22,7 @@ pub trait HasSource {
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
|
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
|
||||||
pub fn definition_source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ModuleSource> {
|
pub fn definition_source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ModuleSource> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
let decl_id = def_map[self.id.module_id].declaration;
|
let decl_id = def_map[self.id.module_id].declaration;
|
||||||
let file_id = def_map[self.id.module_id].definition;
|
let file_id = def_map[self.id.module_id].definition;
|
||||||
let ast = ModuleSource::new(db, file_id, decl_id);
|
let ast = ModuleSource::new(db, file_id, decl_id);
|
||||||
|
@ -36,7 +36,7 @@ impl Module {
|
||||||
self,
|
self,
|
||||||
db: &(impl DefDatabase + AstDatabase),
|
db: &(impl DefDatabase + AstDatabase),
|
||||||
) -> Option<Source<ast::Module>> {
|
) -> Option<Source<ast::Module>> {
|
||||||
let def_map = db.crate_def_map(self.krate());
|
let def_map = db.crate_def_map(self.id.krate);
|
||||||
let decl = def_map[self.id.module_id].declaration?;
|
let decl = def_map[self.id.module_id].declaration?;
|
||||||
let ast = decl.to_node(db);
|
let ast = decl.to_node(db);
|
||||||
Some(Source { file_id: decl.file_id(), ast })
|
Some(Source { file_id: decl.file_id(), ast })
|
||||||
|
|
|
@ -11,20 +11,19 @@ use crate::{
|
||||||
ids,
|
ids,
|
||||||
impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks},
|
impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks},
|
||||||
lang_item::{LangItemTarget, LangItems},
|
lang_item::{LangItemTarget, LangItems},
|
||||||
nameres::{CrateDefMap, Namespace},
|
|
||||||
traits::TraitData,
|
traits::TraitData,
|
||||||
ty::{
|
ty::{
|
||||||
method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
|
method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
|
||||||
InferenceResult, Substs, Ty, TypableDef, TypeCtor,
|
InferenceResult, Substs, Ty, TypableDef, TypeCtor,
|
||||||
},
|
},
|
||||||
type_alias::TypeAliasData,
|
type_alias::TypeAliasData,
|
||||||
Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Static,
|
Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Namespace, Static,
|
||||||
StructField, Trait, TypeAlias,
|
StructField, Trait, TypeAlias,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use hir_def::db::{
|
pub use hir_def::db::{
|
||||||
DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase, InternDatabaseStorage,
|
CrateDefMapQuery, DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase,
|
||||||
RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery,
|
InternDatabaseStorage, RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery,
|
||||||
};
|
};
|
||||||
pub use hir_expand::db::{
|
pub use hir_expand::db::{
|
||||||
AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery,
|
AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery,
|
||||||
|
@ -41,9 +40,6 @@ pub trait DefDatabase: HirDebugDatabase + DefDatabase2 {
|
||||||
#[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)]
|
#[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)]
|
||||||
fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex;
|
fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex;
|
||||||
|
|
||||||
#[salsa::invoke(CrateDefMap::crate_def_map_query)]
|
|
||||||
fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>;
|
|
||||||
|
|
||||||
#[salsa::invoke(ModuleImplBlocks::impls_in_module_with_source_map_query)]
|
#[salsa::invoke(ModuleImplBlocks::impls_in_module_with_source_map_query)]
|
||||||
fn impls_in_module_with_source_map(
|
fn impls_in_module_with_source_map(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -1,82 +1,13 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use std::{any::Any, fmt};
|
use std::any::Any;
|
||||||
|
|
||||||
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange};
|
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
|
||||||
use relative_path::RelativePathBuf;
|
|
||||||
|
|
||||||
use crate::{db::HirDatabase, HirFileId, Name, Source};
|
use crate::{db::AstDatabase, HirFileId, Name, Source};
|
||||||
|
|
||||||
/// Diagnostic defines hir API for errors and warnings.
|
pub use hir_def::diagnostics::UnresolvedModule;
|
||||||
///
|
pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
|
||||||
/// It is used as a `dyn` object, which you can downcast to a concrete
|
|
||||||
/// diagnostic. DiagnosticSink are structured, meaning that they include rich
|
|
||||||
/// information which can be used by IDE to create fixes. DiagnosticSink are
|
|
||||||
/// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea
|
|
||||||
/// to diagnostic in a salsa value).
|
|
||||||
///
|
|
||||||
/// Internally, various subsystems of hir produce diagnostics specific to a
|
|
||||||
/// subsystem (typically, an `enum`), which are safe to store in salsa but do not
|
|
||||||
/// include source locations. Such internal diagnostic are transformed into an
|
|
||||||
/// instance of `Diagnostic` on demand.
|
|
||||||
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
|
||||||
fn message(&self) -> String;
|
|
||||||
fn source(&self) -> Source<SyntaxNodePtr>;
|
|
||||||
fn highlight_range(&self) -> TextRange {
|
|
||||||
self.source().ast.range()
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AstDiagnostic {
|
|
||||||
type AST;
|
|
||||||
fn ast(&self, db: &impl HirDatabase) -> Self::AST;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl dyn Diagnostic {
|
|
||||||
pub fn syntax_node(&self, db: &impl HirDatabase) -> SyntaxNode {
|
|
||||||
let node = db.parse_or_expand(self.source().file_id).unwrap();
|
|
||||||
self.source().ast.to_node(&node)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
|
|
||||||
self.as_any().downcast_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DiagnosticSink<'a> {
|
|
||||||
callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
|
|
||||||
default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DiagnosticSink<'a> {
|
|
||||||
pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> {
|
|
||||||
DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> {
|
|
||||||
let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
|
|
||||||
Some(d) => {
|
|
||||||
cb(d);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => Err(()),
|
|
||||||
};
|
|
||||||
self.callbacks.push(Box::new(cb));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn push(&mut self, d: impl Diagnostic) {
|
|
||||||
let d: &dyn Diagnostic = &d;
|
|
||||||
for cb in self.callbacks.iter_mut() {
|
|
||||||
match cb(d) {
|
|
||||||
Ok(()) => return,
|
|
||||||
Err(()) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(self.default_callback)(d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NoSuchField {
|
pub struct NoSuchField {
|
||||||
|
@ -98,25 +29,6 @@ impl Diagnostic for NoSuchField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct UnresolvedModule {
|
|
||||||
pub file: HirFileId,
|
|
||||||
pub decl: AstPtr<ast::Module>,
|
|
||||||
pub candidate: RelativePathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostic for UnresolvedModule {
|
|
||||||
fn message(&self) -> String {
|
|
||||||
"unresolved module".to_string()
|
|
||||||
}
|
|
||||||
fn source(&self) -> Source<SyntaxNodePtr> {
|
|
||||||
Source { file_id: self.file, ast: self.decl.into() }
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MissingFields {
|
pub struct MissingFields {
|
||||||
pub file: HirFileId,
|
pub file: HirFileId,
|
||||||
|
@ -139,7 +51,7 @@ impl Diagnostic for MissingFields {
|
||||||
impl AstDiagnostic for MissingFields {
|
impl AstDiagnostic for MissingFields {
|
||||||
type AST = ast::RecordFieldList;
|
type AST = ast::RecordFieldList;
|
||||||
|
|
||||||
fn ast(&self, db: &impl HirDatabase) -> Self::AST {
|
fn ast(&self, db: &impl AstDatabase) -> Self::AST {
|
||||||
let root = db.parse_or_expand(self.source().file_id).unwrap();
|
let root = db.parse_or_expand(self.source().file_id).unwrap();
|
||||||
let node = self.source().ast.to_node(&root);
|
let node = self.source().ast.to_node(&root);
|
||||||
ast::RecordFieldList::cast(node).unwrap()
|
ast::RecordFieldList::cast(node).unwrap()
|
||||||
|
@ -167,7 +79,7 @@ impl Diagnostic for MissingOkInTailExpr {
|
||||||
impl AstDiagnostic for MissingOkInTailExpr {
|
impl AstDiagnostic for MissingOkInTailExpr {
|
||||||
type AST = ast::Expr;
|
type AST = ast::Expr;
|
||||||
|
|
||||||
fn ast(&self, db: &impl HirDatabase) -> Self::AST {
|
fn ast(&self, db: &impl AstDatabase) -> Self::AST {
|
||||||
let root = db.parse_or_expand(self.file).unwrap();
|
let root = db.parse_or_expand(self.file).unwrap();
|
||||||
let node = self.source().ast.to_node(&root);
|
let node = self.source().ast.to_node(&root);
|
||||||
ast::Expr::cast(node).unwrap()
|
ast::Expr::cast(node).unwrap()
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_def::path::known;
|
use hir_def::path::known;
|
||||||
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
use ra_syntax::ast;
|
use ra_syntax::ast;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
|
diagnostics::{MissingFields, MissingOkInTailExpr},
|
||||||
expr::AstPtr,
|
expr::AstPtr,
|
||||||
ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
|
ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
|
||||||
Adt, Function, Name, Path,
|
Adt, Function, Name, Path,
|
||||||
|
|
63
crates/ra_hir/src/from_id.rs
Normal file
63
crates/ra_hir/src/from_id.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
//! Utility module for converting between hir_def ids and code_model wrappers.
|
||||||
|
//!
|
||||||
|
//! It's unclear if we need this long-term, but it's definitelly useful while we
|
||||||
|
//! are splitting the hir.
|
||||||
|
|
||||||
|
use hir_def::{AdtId, EnumVariantId, ModuleDefId};
|
||||||
|
|
||||||
|
use crate::{Adt, EnumVariant, ModuleDef};
|
||||||
|
|
||||||
|
macro_rules! from_id {
|
||||||
|
($(($id:path, $ty:path)),*) => {$(
|
||||||
|
impl From<$id> for $ty {
|
||||||
|
fn from(id: $id) -> $ty {
|
||||||
|
$ty { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_id![
|
||||||
|
(hir_def::ModuleId, crate::Module),
|
||||||
|
(hir_def::StructId, crate::Struct),
|
||||||
|
(hir_def::UnionId, crate::Union),
|
||||||
|
(hir_def::EnumId, crate::Enum),
|
||||||
|
(hir_def::TypeAliasId, crate::TypeAlias),
|
||||||
|
(hir_def::TraitId, crate::Trait),
|
||||||
|
(hir_def::StaticId, crate::Static),
|
||||||
|
(hir_def::ConstId, crate::Const),
|
||||||
|
(hir_def::FunctionId, crate::Function),
|
||||||
|
(hir_expand::MacroDefId, crate::MacroDef)
|
||||||
|
];
|
||||||
|
|
||||||
|
impl From<AdtId> for Adt {
|
||||||
|
fn from(id: AdtId) -> Self {
|
||||||
|
match id {
|
||||||
|
AdtId::StructId(it) => Adt::Struct(it.into()),
|
||||||
|
AdtId::UnionId(it) => Adt::Union(it.into()),
|
||||||
|
AdtId::EnumId(it) => Adt::Enum(it.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EnumVariantId> for EnumVariant {
|
||||||
|
fn from(id: EnumVariantId) -> Self {
|
||||||
|
EnumVariant { parent: id.parent.into(), id: id.local_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ModuleDefId> for ModuleDef {
|
||||||
|
fn from(id: ModuleDefId) -> Self {
|
||||||
|
match id {
|
||||||
|
ModuleDefId::ModuleId(it) => ModuleDef::Module(it.into()),
|
||||||
|
ModuleDefId::FunctionId(it) => ModuleDef::Function(it.into()),
|
||||||
|
ModuleDefId::AdtId(it) => ModuleDef::Adt(it.into()),
|
||||||
|
ModuleDefId::EnumVariantId(it) => ModuleDef::EnumVariant(it.into()),
|
||||||
|
ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()),
|
||||||
|
ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()),
|
||||||
|
ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()),
|
||||||
|
ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
|
||||||
|
ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -149,13 +149,19 @@ impl Module {
|
||||||
ModuleSource::SourceFile(_) => None,
|
ModuleSource::SourceFile(_) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
db.relevant_crates(src.file_id.original_file(db))
|
db.relevant_crates(src.file_id.original_file(db)).iter().find_map(|&crate_id| {
|
||||||
.iter()
|
let def_map = db.crate_def_map(crate_id);
|
||||||
.map(|&crate_id| Crate { crate_id })
|
|
||||||
.find_map(|krate| {
|
let (module_id, _module_data) =
|
||||||
let def_map = db.crate_def_map(krate);
|
def_map.modules.iter().find(|(_module_id, module_data)| {
|
||||||
let module_id = def_map.find_module_by_source(src.file_id, decl_id)?;
|
if decl_id.is_some() {
|
||||||
Some(Module::new(krate, module_id))
|
module_data.declaration == decl_id
|
||||||
|
} else {
|
||||||
|
module_data.definition.map(|it| it.into()) == Some(src.file_id)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Some(Module::new(Crate { crate_id }, module_id))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ pub mod mock;
|
||||||
pub mod source_binder;
|
pub mod source_binder;
|
||||||
|
|
||||||
mod ids;
|
mod ids;
|
||||||
mod nameres;
|
|
||||||
mod adt;
|
mod adt;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod type_alias;
|
mod type_alias;
|
||||||
|
@ -47,6 +46,7 @@ mod resolve;
|
||||||
pub mod diagnostics;
|
pub mod diagnostics;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
mod from_id;
|
||||||
mod code_model;
|
mod code_model;
|
||||||
|
|
||||||
pub mod from_source;
|
pub mod from_source;
|
||||||
|
@ -62,7 +62,7 @@ pub use crate::{
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
code_model::{
|
code_model::{
|
||||||
docs::{DocDef, Docs, Documentation},
|
docs::{DocDef, Docs, Documentation},
|
||||||
src::{HasBodySource, HasSource, Source},
|
src::{HasBodySource, HasSource},
|
||||||
Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
|
Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
|
||||||
EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef,
|
EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef,
|
||||||
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||||
|
@ -72,7 +72,6 @@ pub use crate::{
|
||||||
generics::{GenericDef, GenericParam, GenericParams, HasGenericParams},
|
generics::{GenericDef, GenericParam, GenericParams, HasGenericParams},
|
||||||
ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
|
ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
|
||||||
impl_block::ImplBlock,
|
impl_block::ImplBlock,
|
||||||
nameres::{ImportId, Namespace, PerNs},
|
|
||||||
resolve::ScopeDef,
|
resolve::ScopeDef,
|
||||||
source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
|
source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
|
||||||
ty::{
|
ty::{
|
||||||
|
@ -82,7 +81,11 @@ pub use crate::{
|
||||||
|
|
||||||
pub use hir_def::{
|
pub use hir_def::{
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
|
nameres::{
|
||||||
|
per_ns::{Namespace, PerNs},
|
||||||
|
raw::ImportId,
|
||||||
|
},
|
||||||
path::{Path, PathKind},
|
path::{Path, PathKind},
|
||||||
type_ref::Mutability,
|
type_ref::Mutability,
|
||||||
};
|
};
|
||||||
pub use hir_expand::{either::Either, name::Name};
|
pub use hir_expand::{either::Either, name::Name, Source};
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
//! See test_utils/src/marks.rs
|
//! See test_utils/src/marks.rs
|
||||||
|
|
||||||
test_utils::marks!(
|
test_utils::marks!(
|
||||||
bogus_paths
|
|
||||||
// FIXME: restore this mark once hir is split
|
|
||||||
name_res_works_for_broken_modules
|
|
||||||
can_import_enum_variant
|
|
||||||
type_var_cycles_resolve_completely
|
type_var_cycles_resolve_completely
|
||||||
type_var_cycles_resolve_as_possible
|
type_var_cycles_resolve_as_possible
|
||||||
type_var_resolves_to_int_var
|
type_var_resolves_to_int_var
|
||||||
glob_enum
|
|
||||||
glob_across_crates
|
|
||||||
std_prelude
|
|
||||||
match_ergonomics_ref
|
match_ergonomics_ref
|
||||||
infer_while_let
|
infer_while_let
|
||||||
macro_rules_from_other_crates_are_visible_with_macro_use
|
|
||||||
prelude_is_macro_use
|
|
||||||
coerce_merge_fail_fallback
|
coerce_merge_fail_fallback
|
||||||
macro_dollar_crate_self
|
|
||||||
macro_dollar_crate_other
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
|
|
||||||
use std::{panic, sync::Arc};
|
use std::{panic, sync::Arc};
|
||||||
|
|
||||||
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use ra_db::{
|
use ra_db::{
|
||||||
salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition,
|
salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition,
|
||||||
SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
|
RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
|
||||||
};
|
};
|
||||||
use relative_path::{RelativePath, RelativePathBuf};
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
|
use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
|
||||||
|
|
||||||
use crate::{db, debug::HirDebugHelper, diagnostics::DiagnosticSink};
|
use crate::{db, debug::HirDebugHelper};
|
||||||
|
|
||||||
pub const WORKSPACE: SourceRootId = SourceRootId(0);
|
pub const WORKSPACE: SourceRootId = SourceRootId(0);
|
||||||
|
|
||||||
|
@ -77,12 +77,6 @@ impl MockDatabase {
|
||||||
(db, source_root, file_id)
|
(db, source_root, file_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_position(fixture: &str) -> (MockDatabase, FilePosition) {
|
|
||||||
let (db, position) = MockDatabase::from_fixture(fixture);
|
|
||||||
let position = position.expect("expected a marker ( <|> )");
|
|
||||||
(db, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_id_of(&self, path: &str) -> FileId {
|
pub fn file_id_of(&self, path: &str) -> FileId {
|
||||||
match self.files.get(path) {
|
match self.files.get(path) {
|
||||||
Some(it) => *it,
|
Some(it) => *it,
|
||||||
|
@ -90,25 +84,6 @@ impl MockDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) {
|
|
||||||
let mut ids = FxHashMap::default();
|
|
||||||
let mut crate_graph = CrateGraph::default();
|
|
||||||
for (crate_name, (crate_root, edition, cfg_options, _)) in graph.0.iter() {
|
|
||||||
let crate_root = self.file_id_of(&crate_root);
|
|
||||||
let crate_id = crate_graph.add_crate_root(crate_root, *edition, cfg_options.clone());
|
|
||||||
Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone());
|
|
||||||
ids.insert(crate_name, crate_id);
|
|
||||||
}
|
|
||||||
for (crate_name, (_, _, _, deps)) in graph.0.iter() {
|
|
||||||
let from = ids[crate_name];
|
|
||||||
for dep in deps {
|
|
||||||
let to = ids[dep];
|
|
||||||
crate_graph.add_dep(from, dep.as_str().into(), to).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.set_crate_graph(Arc::new(crate_graph))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diagnostics(&self) -> String {
|
pub fn diagnostics(&self) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let mut files: Vec<FileId> = self.files.values().copied().collect();
|
let mut files: Vec<FileId> = self.files.values().copied().collect();
|
||||||
|
@ -285,46 +260,3 @@ impl MockDatabase {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec<String>))>);
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! crate_graph {
|
|
||||||
($(
|
|
||||||
$crate_name:literal: (
|
|
||||||
$crate_path:literal,
|
|
||||||
$($edition:literal,)?
|
|
||||||
[$($dep:literal),*]
|
|
||||||
$(, cfg = {
|
|
||||||
$($key:literal $(= $value:literal)?),*
|
|
||||||
$(,)?
|
|
||||||
})?
|
|
||||||
),
|
|
||||||
)*) => {{
|
|
||||||
let mut res = $crate::mock::CrateGraphFixture::default();
|
|
||||||
$(
|
|
||||||
#[allow(unused_mut, unused_assignments)]
|
|
||||||
let mut edition = ra_db::Edition::Edition2018;
|
|
||||||
$(edition = ra_db::Edition::from_string($edition);)?
|
|
||||||
let cfg_options = {
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut cfg = ::ra_cfg::CfgOptions::default();
|
|
||||||
$(
|
|
||||||
$(
|
|
||||||
if 0 == 0 $(+ { drop($value); 1})? {
|
|
||||||
cfg.insert_atom($key.into());
|
|
||||||
}
|
|
||||||
$(cfg.insert_key_value($key.into(), $value.into());)?
|
|
||||||
)*
|
|
||||||
)?
|
|
||||||
cfg
|
|
||||||
};
|
|
||||||
res.0.push((
|
|
||||||
$crate_name.to_string(),
|
|
||||||
($crate_path.to_string(), edition, cfg_options, vec![$($dep.to_string()),*])
|
|
||||||
));
|
|
||||||
)*
|
|
||||||
res
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,557 +0,0 @@
|
||||||
//! This module implements import-resolution/macro expansion algorithm.
|
|
||||||
//!
|
|
||||||
//! The result of this module is `CrateDefMap`: a data structure which contains:
|
|
||||||
//!
|
|
||||||
//! * a tree of modules for the crate
|
|
||||||
//! * for each module, a set of items visible in the module (directly declared
|
|
||||||
//! or imported)
|
|
||||||
//!
|
|
||||||
//! Note that `CrateDefMap` contains fully macro expanded code.
|
|
||||||
//!
|
|
||||||
//! Computing `CrateDefMap` can be partitioned into several logically
|
|
||||||
//! independent "phases". The phases are mutually recursive though, there's no
|
|
||||||
//! strict ordering.
|
|
||||||
//!
|
|
||||||
//! ## Collecting RawItems
|
|
||||||
//!
|
|
||||||
//! This happens in the `raw` module, which parses a single source file into a
|
|
||||||
//! set of top-level items. Nested imports are desugared to flat imports in
|
|
||||||
//! this phase. Macro calls are represented as a triple of (Path, Option<Name>,
|
|
||||||
//! TokenTree).
|
|
||||||
//!
|
|
||||||
//! ## Collecting Modules
|
|
||||||
//!
|
|
||||||
//! This happens in the `collector` module. In this phase, we recursively walk
|
|
||||||
//! tree of modules, collect raw items from submodules, populate module scopes
|
|
||||||
//! with defined items (so, we assign item ids in this phase) and record the set
|
|
||||||
//! of unresolved imports and macros.
|
|
||||||
//!
|
|
||||||
//! While we walk tree of modules, we also record macro_rules definitions and
|
|
||||||
//! expand calls to macro_rules defined macros.
|
|
||||||
//!
|
|
||||||
//! ## Resolving Imports
|
|
||||||
//!
|
|
||||||
//! We maintain a list of currently unresolved imports. On every iteration, we
|
|
||||||
//! try to resolve some imports from this list. If the import is resolved, we
|
|
||||||
//! record it, by adding an item to current module scope and, if necessary, by
|
|
||||||
//! recursively populating glob imports.
|
|
||||||
//!
|
|
||||||
//! ## Resolving Macros
|
|
||||||
//!
|
|
||||||
//! macro_rules from the same crate use a global mutable namespace. We expand
|
|
||||||
//! them immediately, when we collect modules.
|
|
||||||
//!
|
|
||||||
//! Macros from other crates (including proc-macros) can be used with
|
|
||||||
//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
|
|
||||||
//! unexpanded macros. On every iteration, we try to resolve each macro call
|
|
||||||
//! path and, upon success, we run macro expansion and "collect module" phase
|
|
||||||
//! on the result
|
|
||||||
|
|
||||||
mod per_ns;
|
|
||||||
mod collector;
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use hir_def::{builtin_type::BuiltinType, CrateModuleId};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use ra_arena::Arena;
|
|
||||||
use ra_db::{Edition, FileId};
|
|
||||||
use ra_prof::profile;
|
|
||||||
use ra_syntax::ast;
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
|
||||||
use test_utils::tested_by;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
db::{AstDatabase, DefDatabase},
|
|
||||||
diagnostics::DiagnosticSink,
|
|
||||||
ids::MacroDefId,
|
|
||||||
nameres::diagnostics::DefDiagnostic,
|
|
||||||
Adt, AstId, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind, Trait,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use self::per_ns::{Namespace, PerNs};
|
|
||||||
|
|
||||||
pub use hir_def::nameres::raw::ImportId;
|
|
||||||
|
|
||||||
/// Contains all top-level defs from a macro-expanded crate
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct CrateDefMap {
|
|
||||||
krate: Crate,
|
|
||||||
edition: Edition,
|
|
||||||
/// The prelude module for this crate. This either comes from an import
|
|
||||||
/// marked with the `prelude_import` attribute, or (in the normal case) from
|
|
||||||
/// a dependency (`std` or `core`).
|
|
||||||
prelude: Option<Module>,
|
|
||||||
extern_prelude: FxHashMap<Name, ModuleDef>,
|
|
||||||
root: CrateModuleId,
|
|
||||||
modules: Arena<CrateModuleId, ModuleData>,
|
|
||||||
|
|
||||||
/// Some macros are not well-behavior, which leads to infinite loop
|
|
||||||
/// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
|
|
||||||
/// We mark it down and skip it in collector
|
|
||||||
///
|
|
||||||
/// FIXME:
|
|
||||||
/// Right now it only handle a poison macro in a single crate,
|
|
||||||
/// such that if other crate try to call that macro,
|
|
||||||
/// the whole process will do again until it became poisoned in that crate.
|
|
||||||
/// We should handle this macro set globally
|
|
||||||
/// However, do we want to put it as a global variable?
|
|
||||||
poison_macros: FxHashSet<MacroDefId>,
|
|
||||||
|
|
||||||
diagnostics: Vec<DefDiagnostic>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Index<CrateModuleId> for CrateDefMap {
|
|
||||||
type Output = ModuleData;
|
|
||||||
fn index(&self, id: CrateModuleId) -> &ModuleData {
|
|
||||||
&self.modules[id]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
|
||||||
pub struct ModuleData {
|
|
||||||
pub(crate) parent: Option<CrateModuleId>,
|
|
||||||
pub(crate) children: FxHashMap<Name, CrateModuleId>,
|
|
||||||
pub(crate) scope: ModuleScope,
|
|
||||||
/// None for root
|
|
||||||
pub(crate) declaration: Option<AstId<ast::Module>>,
|
|
||||||
/// None for inline modules.
|
|
||||||
///
|
|
||||||
/// Note that non-inline modules, by definition, live inside non-macro file.
|
|
||||||
pub(crate) definition: Option<FileId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
|
||||||
pub struct ModuleScope {
|
|
||||||
items: FxHashMap<Name, Resolution>,
|
|
||||||
/// Macros visable in current module in legacy textual scope
|
|
||||||
///
|
|
||||||
/// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first.
|
|
||||||
/// If it yields no result, then it turns to module scoped `macros`.
|
|
||||||
/// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
|
|
||||||
/// and only normal scoped `macros` will be searched in.
|
|
||||||
///
|
|
||||||
/// Note that this automatically inherit macros defined textually before the definition of module itself.
|
|
||||||
///
|
|
||||||
/// Module scoped macros will be inserted into `items` instead of here.
|
|
||||||
// FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
|
|
||||||
// be all resolved to the last one defined if shadowing happens.
|
|
||||||
legacy_macros: FxHashMap<Name, MacroDef>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
|
|
||||||
BuiltinType::ALL
|
|
||||||
.iter()
|
|
||||||
.map(|(name, ty)| {
|
|
||||||
(name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
|
|
||||||
/// Other methods will only resolve values, types and module scoped macros only.
|
|
||||||
impl ModuleScope {
|
|
||||||
pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
|
|
||||||
//FIXME: shadowing
|
|
||||||
self.items.iter().chain(BUILTIN_SCOPE.iter())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over all module scoped macros
|
|
||||||
pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDef)> + 'a {
|
|
||||||
self.items
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over all legacy textual scoped macros visable at the end of the module
|
|
||||||
pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDef)> + 'a {
|
|
||||||
self.legacy_macros.iter().map(|(name, def)| (name, *def))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a name from current module scope, legacy macros are not included
|
|
||||||
pub fn get(&self, name: &Name) -> Option<&Resolution> {
|
|
||||||
self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn traits<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
|
|
||||||
self.items.values().filter_map(|r| match r.def.take_types() {
|
|
||||||
Some(ModuleDef::Trait(t)) => Some(t),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_legacy_macro(&self, name: &Name) -> Option<MacroDef> {
|
|
||||||
self.legacy_macros.get(name).copied()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
||||||
pub struct Resolution {
|
|
||||||
/// None for unresolved
|
|
||||||
pub def: PerNs,
|
|
||||||
/// ident by which this is imported into local scope.
|
|
||||||
pub import: Option<ImportId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolution {
|
|
||||||
pub(crate) fn from_macro(macro_: MacroDef) -> Self {
|
|
||||||
Resolution { def: PerNs::macros(macro_), import: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct ResolvePathResult {
|
|
||||||
resolved_def: PerNs,
|
|
||||||
segment_index: Option<usize>,
|
|
||||||
reached_fixedpoint: ReachedFixedPoint,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResolvePathResult {
|
|
||||||
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
|
||||||
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with(
|
|
||||||
resolved_def: PerNs,
|
|
||||||
reached_fixedpoint: ReachedFixedPoint,
|
|
||||||
segment_index: Option<usize>,
|
|
||||||
) -> ResolvePathResult {
|
|
||||||
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum ResolveMode {
|
|
||||||
Import,
|
|
||||||
Other,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum ReachedFixedPoint {
|
|
||||||
Yes,
|
|
||||||
No,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CrateDefMap {
|
|
||||||
pub(crate) fn crate_def_map_query(
|
|
||||||
// Note that this doesn't have `+ AstDatabase`!
|
|
||||||
// This gurantess that `CrateDefMap` is stable across reparses.
|
|
||||||
db: &impl DefDatabase,
|
|
||||||
krate: Crate,
|
|
||||||
) -> Arc<CrateDefMap> {
|
|
||||||
let _p = profile("crate_def_map_query");
|
|
||||||
let def_map = {
|
|
||||||
let edition = krate.edition(db);
|
|
||||||
let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
|
|
||||||
let root = modules.alloc(ModuleData::default());
|
|
||||||
CrateDefMap {
|
|
||||||
krate,
|
|
||||||
edition,
|
|
||||||
extern_prelude: FxHashMap::default(),
|
|
||||||
prelude: None,
|
|
||||||
root,
|
|
||||||
modules,
|
|
||||||
poison_macros: FxHashSet::default(),
|
|
||||||
diagnostics: Vec::new(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let def_map = collector::collect_defs(db, def_map);
|
|
||||||
Arc::new(def_map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn krate(&self) -> Crate {
|
|
||||||
self.krate
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn root(&self) -> CrateModuleId {
|
|
||||||
self.root
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn prelude(&self) -> Option<Module> {
|
|
||||||
self.prelude
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> {
|
|
||||||
&self.extern_prelude
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_diagnostics(
|
|
||||||
&self,
|
|
||||||
db: &(impl DefDatabase + AstDatabase),
|
|
||||||
module: CrateModuleId,
|
|
||||||
sink: &mut DiagnosticSink,
|
|
||||||
) {
|
|
||||||
self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn find_module_by_source(
|
|
||||||
&self,
|
|
||||||
file_id: HirFileId,
|
|
||||||
decl_id: Option<AstId<ast::Module>>,
|
|
||||||
) -> Option<CrateModuleId> {
|
|
||||||
let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| {
|
|
||||||
if decl_id.is_some() {
|
|
||||||
module_data.declaration == decl_id
|
|
||||||
} else {
|
|
||||||
module_data.definition.map(|it| it.into()) == Some(file_id)
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
Some(module_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn resolve_path(
|
|
||||||
&self,
|
|
||||||
db: &impl DefDatabase,
|
|
||||||
original_module: CrateModuleId,
|
|
||||||
path: &Path,
|
|
||||||
) -> (PerNs, Option<usize>) {
|
|
||||||
let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
|
|
||||||
(res.resolved_def, res.segment_index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
|
||||||
// the result.
|
|
||||||
fn resolve_path_fp_with_macro(
|
|
||||||
&self,
|
|
||||||
db: &impl DefDatabase,
|
|
||||||
mode: ResolveMode,
|
|
||||||
original_module: CrateModuleId,
|
|
||||||
path: &Path,
|
|
||||||
) -> ResolvePathResult {
|
|
||||||
let mut segments = path.segments.iter().enumerate();
|
|
||||||
let mut curr_per_ns: PerNs = match path.kind {
|
|
||||||
PathKind::DollarCrate(crate_id) => {
|
|
||||||
let krate = Crate { crate_id };
|
|
||||||
if krate == self.krate {
|
|
||||||
tested_by!(macro_dollar_crate_self);
|
|
||||||
PerNs::types(Module::new(self.krate, self.root).into())
|
|
||||||
} else {
|
|
||||||
match krate.root_module(db) {
|
|
||||||
Some(module) => {
|
|
||||||
tested_by!(macro_dollar_crate_other);
|
|
||||||
PerNs::types(module.into())
|
|
||||||
}
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PathKind::Crate => PerNs::types(Module::new(self.krate, self.root).into()),
|
|
||||||
PathKind::Self_ => PerNs::types(Module::new(self.krate, original_module).into()),
|
|
||||||
// plain import or absolute path in 2015: crate-relative with
|
|
||||||
// fallback to extern prelude (with the simplification in
|
|
||||||
// rust-lang/rust#57745)
|
|
||||||
// FIXME there must be a nicer way to write this condition
|
|
||||||
PathKind::Plain | PathKind::Abs
|
|
||||||
if self.edition == Edition::Edition2015
|
|
||||||
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
|
|
||||||
{
|
|
||||||
let segment = match segments.next() {
|
|
||||||
Some((_, segment)) => segment,
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
};
|
|
||||||
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
|
||||||
self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
|
|
||||||
}
|
|
||||||
PathKind::Plain => {
|
|
||||||
let segment = match segments.next() {
|
|
||||||
Some((_, segment)) => segment,
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
};
|
|
||||||
log::debug!("resolving {:?} in module", segment);
|
|
||||||
self.resolve_name_in_module(db, original_module, &segment.name)
|
|
||||||
}
|
|
||||||
PathKind::Super => {
|
|
||||||
if let Some(p) = self.modules[original_module].parent {
|
|
||||||
PerNs::types(Module::new(self.krate, p).into())
|
|
||||||
} else {
|
|
||||||
log::debug!("super path in root module");
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PathKind::Abs => {
|
|
||||||
// 2018-style absolute path -- only extern prelude
|
|
||||||
let segment = match segments.next() {
|
|
||||||
Some((_, segment)) => segment,
|
|
||||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
|
||||||
};
|
|
||||||
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
|
||||||
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
|
||||||
PerNs::types(*def)
|
|
||||||
} else {
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PathKind::Type(_) => {
|
|
||||||
// This is handled in `infer::infer_path_expr`
|
|
||||||
// The result returned here does not matter
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i, segment) in segments {
|
|
||||||
let curr = match curr_per_ns.take_types() {
|
|
||||||
Some(r) => r,
|
|
||||||
None => {
|
|
||||||
// we still have path segments left, but the path so far
|
|
||||||
// didn't resolve in the types namespace => no resolution
|
|
||||||
// (don't break here because `curr_per_ns` might contain
|
|
||||||
// something in the value namespace, and it would be wrong
|
|
||||||
// to return that)
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// resolve segment in curr
|
|
||||||
|
|
||||||
curr_per_ns = match curr {
|
|
||||||
ModuleDef::Module(module) => {
|
|
||||||
if module.krate() != self.krate {
|
|
||||||
let path =
|
|
||||||
Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
|
|
||||||
log::debug!("resolving {:?} in other crate", path);
|
|
||||||
let defp_map = db.crate_def_map(module.krate());
|
|
||||||
let (def, s) = defp_map.resolve_path(db, module.id.module_id, &path);
|
|
||||||
return ResolvePathResult::with(
|
|
||||||
def,
|
|
||||||
ReachedFixedPoint::Yes,
|
|
||||||
s.map(|s| s + i),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since it is a qualified path here, it should not contains legacy macros
|
|
||||||
match self[module.id.module_id].scope.get(&segment.name) {
|
|
||||||
Some(res) => res.def,
|
|
||||||
_ => {
|
|
||||||
log::debug!("path segment {:?} not found", segment.name);
|
|
||||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ModuleDef::Adt(Adt::Enum(e)) => {
|
|
||||||
// enum variant
|
|
||||||
tested_by!(can_import_enum_variant);
|
|
||||||
match e.variant(db, &segment.name) {
|
|
||||||
Some(variant) => PerNs::both(variant.into(), variant.into()),
|
|
||||||
None => {
|
|
||||||
return ResolvePathResult::with(
|
|
||||||
PerNs::types(e.into()),
|
|
||||||
ReachedFixedPoint::Yes,
|
|
||||||
Some(i),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s => {
|
|
||||||
// could be an inherent method call in UFCS form
|
|
||||||
// (`Struct::method`), or some other kind of associated item
|
|
||||||
log::debug!(
|
|
||||||
"path segment {:?} resolved to non-module {:?}, but is not last",
|
|
||||||
segment.name,
|
|
||||||
curr,
|
|
||||||
);
|
|
||||||
|
|
||||||
return ResolvePathResult::with(
|
|
||||||
PerNs::types(s),
|
|
||||||
ReachedFixedPoint::Yes,
|
|
||||||
Some(i),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
|
|
||||||
let from_crate_root =
|
|
||||||
self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
|
|
||||||
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
|
|
||||||
|
|
||||||
from_crate_root.or(from_extern_prelude)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn resolve_name_in_module(
|
|
||||||
&self,
|
|
||||||
db: &impl DefDatabase,
|
|
||||||
module: CrateModuleId,
|
|
||||||
name: &Name,
|
|
||||||
) -> PerNs {
|
|
||||||
// Resolve in:
|
|
||||||
// - legacy scope of macro
|
|
||||||
// - current module / scope
|
|
||||||
// - extern prelude
|
|
||||||
// - std prelude
|
|
||||||
let from_legacy_macro =
|
|
||||||
self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
|
|
||||||
let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
|
|
||||||
let from_extern_prelude =
|
|
||||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
|
|
||||||
let from_prelude = self.resolve_in_prelude(db, name);
|
|
||||||
|
|
||||||
from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
|
|
||||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs {
|
|
||||||
if let Some(prelude) = self.prelude {
|
|
||||||
let keep;
|
|
||||||
let def_map = if prelude.krate() == self.krate {
|
|
||||||
self
|
|
||||||
} else {
|
|
||||||
// Extend lifetime
|
|
||||||
keep = db.crate_def_map(prelude.krate());
|
|
||||||
&keep
|
|
||||||
};
|
|
||||||
def_map[prelude.id.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
|
|
||||||
} else {
|
|
||||||
PerNs::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod diagnostics {
|
|
||||||
use ra_syntax::{ast, AstPtr};
|
|
||||||
use relative_path::RelativePathBuf;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
db::{AstDatabase, DefDatabase},
|
|
||||||
diagnostics::{DiagnosticSink, UnresolvedModule},
|
|
||||||
nameres::CrateModuleId,
|
|
||||||
AstId,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub(super) enum DefDiagnostic {
|
|
||||||
UnresolvedModule {
|
|
||||||
module: CrateModuleId,
|
|
||||||
declaration: AstId<ast::Module>,
|
|
||||||
candidate: RelativePathBuf,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DefDiagnostic {
|
|
||||||
pub(super) fn add_to(
|
|
||||||
&self,
|
|
||||||
db: &(impl DefDatabase + AstDatabase),
|
|
||||||
target_module: CrateModuleId,
|
|
||||||
sink: &mut DiagnosticSink,
|
|
||||||
) {
|
|
||||||
match self {
|
|
||||||
DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
|
|
||||||
if *module != target_module {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let decl = declaration.to_node(db);
|
|
||||||
sink.push(UnresolvedModule {
|
|
||||||
file: declaration.file_id(),
|
|
||||||
decl: AstPtr::new(&decl),
|
|
||||||
candidate: candidate.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,8 +3,9 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
|
nameres::CrateDefMap,
|
||||||
path::{Path, PathKind},
|
path::{Path, PathKind},
|
||||||
CrateModuleId,
|
AdtId, CrateModuleId, ModuleDefId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::{self, Name};
|
use hir_expand::name::{self, Name};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
@ -18,8 +19,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
impl_block::ImplBlock,
|
impl_block::ImplBlock,
|
||||||
nameres::{CrateDefMap, PerNs},
|
Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, PerNs, Static, Struct, Trait,
|
||||||
Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct, Trait, TypeAlias,
|
TypeAlias,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
@ -90,7 +91,7 @@ impl Resolver {
|
||||||
pub(crate) fn resolve_known_trait(&self, db: &impl HirDatabase, path: &Path) -> Option<Trait> {
|
pub(crate) fn resolve_known_trait(&self, db: &impl HirDatabase, path: &Path) -> Option<Trait> {
|
||||||
let res = self.resolve_module_path(db, path).take_types()?;
|
let res = self.resolve_module_path(db, path).take_types()?;
|
||||||
match res {
|
match res {
|
||||||
ModuleDef::Trait(it) => Some(it),
|
ModuleDefId::TraitId(it) => Some(it.into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +104,7 @@ impl Resolver {
|
||||||
) -> Option<Struct> {
|
) -> Option<Struct> {
|
||||||
let res = self.resolve_module_path(db, path).take_types()?;
|
let res = self.resolve_module_path(db, path).take_types()?;
|
||||||
match res {
|
match res {
|
||||||
ModuleDef::Adt(Adt::Struct(it)) => Some(it),
|
ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it.into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,7 @@ impl Resolver {
|
||||||
pub(crate) fn resolve_known_enum(&self, db: &impl HirDatabase, path: &Path) -> Option<Enum> {
|
pub(crate) fn resolve_known_enum(&self, db: &impl HirDatabase, path: &Path) -> Option<Enum> {
|
||||||
let res = self.resolve_module_path(db, path).take_types()?;
|
let res = self.resolve_module_path(db, path).take_types()?;
|
||||||
match res {
|
match res {
|
||||||
ModuleDef::Adt(Adt::Enum(it)) => Some(it),
|
ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it.into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,18 +167,18 @@ impl Resolver {
|
||||||
Scope::ModuleScope(m) => {
|
Scope::ModuleScope(m) => {
|
||||||
let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path);
|
let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path);
|
||||||
let res = match module_def.take_types()? {
|
let res = match module_def.take_types()? {
|
||||||
ModuleDef::Adt(it) => TypeNs::Adt(it),
|
ModuleDefId::AdtId(it) => TypeNs::Adt(it.into()),
|
||||||
ModuleDef::EnumVariant(it) => TypeNs::EnumVariant(it),
|
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariant(it.into()),
|
||||||
|
|
||||||
ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it),
|
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAlias(it.into()),
|
||||||
ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it),
|
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||||
|
|
||||||
ModuleDef::Trait(it) => TypeNs::Trait(it),
|
ModuleDefId::TraitId(it) => TypeNs::Trait(it.into()),
|
||||||
|
|
||||||
ModuleDef::Function(_)
|
ModuleDefId::FunctionId(_)
|
||||||
| ModuleDef::Const(_)
|
| ModuleDefId::ConstId(_)
|
||||||
| ModuleDef::Static(_)
|
| ModuleDefId::StaticId(_)
|
||||||
| ModuleDef::Module(_) => return None,
|
| ModuleDefId::ModuleId(_) => return None,
|
||||||
};
|
};
|
||||||
return Some((res, idx));
|
return Some((res, idx));
|
||||||
}
|
}
|
||||||
|
@ -261,33 +262,35 @@ impl Resolver {
|
||||||
return match idx {
|
return match idx {
|
||||||
None => {
|
None => {
|
||||||
let value = match module_def.take_values()? {
|
let value = match module_def.take_values()? {
|
||||||
ModuleDef::Function(it) => ValueNs::Function(it),
|
ModuleDefId::FunctionId(it) => ValueNs::Function(it.into()),
|
||||||
ModuleDef::Adt(Adt::Struct(it)) => ValueNs::Struct(it),
|
ModuleDefId::AdtId(AdtId::StructId(it)) => {
|
||||||
ModuleDef::EnumVariant(it) => ValueNs::EnumVariant(it),
|
ValueNs::Struct(it.into())
|
||||||
ModuleDef::Const(it) => ValueNs::Const(it),
|
}
|
||||||
ModuleDef::Static(it) => ValueNs::Static(it),
|
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariant(it.into()),
|
||||||
|
ModuleDefId::ConstId(it) => ValueNs::Const(it.into()),
|
||||||
|
ModuleDefId::StaticId(it) => ValueNs::Static(it.into()),
|
||||||
|
|
||||||
ModuleDef::Adt(Adt::Enum(_))
|
ModuleDefId::AdtId(AdtId::EnumId(_))
|
||||||
| ModuleDef::Adt(Adt::Union(_))
|
| ModuleDefId::AdtId(AdtId::UnionId(_))
|
||||||
| ModuleDef::Trait(_)
|
| ModuleDefId::TraitId(_)
|
||||||
| ModuleDef::TypeAlias(_)
|
| ModuleDefId::TypeAliasId(_)
|
||||||
| ModuleDef::BuiltinType(_)
|
| ModuleDefId::BuiltinType(_)
|
||||||
| ModuleDef::Module(_) => return None,
|
| ModuleDefId::ModuleId(_) => return None,
|
||||||
};
|
};
|
||||||
Some(ResolveValueResult::ValueNs(value))
|
Some(ResolveValueResult::ValueNs(value))
|
||||||
}
|
}
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
let ty = match module_def.take_types()? {
|
let ty = match module_def.take_types()? {
|
||||||
ModuleDef::Adt(it) => TypeNs::Adt(it),
|
ModuleDefId::AdtId(it) => TypeNs::Adt(it.into()),
|
||||||
ModuleDef::Trait(it) => TypeNs::Trait(it),
|
ModuleDefId::TraitId(it) => TypeNs::Trait(it.into()),
|
||||||
ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it),
|
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAlias(it.into()),
|
||||||
ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it),
|
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||||
|
|
||||||
ModuleDef::Module(_)
|
ModuleDefId::ModuleId(_)
|
||||||
| ModuleDef::Function(_)
|
| ModuleDefId::FunctionId(_)
|
||||||
| ModuleDef::EnumVariant(_)
|
| ModuleDefId::EnumVariantId(_)
|
||||||
| ModuleDef::Const(_)
|
| ModuleDefId::ConstId(_)
|
||||||
| ModuleDef::Static(_) => return None,
|
| ModuleDefId::StaticId(_) => return None,
|
||||||
};
|
};
|
||||||
Some(ResolveValueResult::Partial(ty, idx))
|
Some(ResolveValueResult::Partial(ty, idx))
|
||||||
}
|
}
|
||||||
|
@ -315,7 +318,7 @@ impl Resolver {
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Option<MacroDef> {
|
) -> Option<MacroDef> {
|
||||||
let (item_map, module) = self.module()?;
|
let (item_map, module) = self.module()?;
|
||||||
item_map.resolve_path(db, module, path).0.get_macros()
|
item_map.resolve_path(db, module, path).0.get_macros().map(MacroDef::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn process_all_names(
|
pub(crate) fn process_all_names(
|
||||||
|
@ -333,10 +336,11 @@ impl Resolver {
|
||||||
for scope in &self.scopes {
|
for scope in &self.scopes {
|
||||||
if let Scope::ModuleScope(m) = scope {
|
if let Scope::ModuleScope(m) = scope {
|
||||||
if let Some(prelude) = m.crate_def_map.prelude() {
|
if let Some(prelude) = m.crate_def_map.prelude() {
|
||||||
let prelude_def_map = db.crate_def_map(prelude.krate());
|
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||||
traits.extend(prelude_def_map[prelude.id.module_id].scope.traits());
|
traits
|
||||||
|
.extend(prelude_def_map[prelude.module_id].scope.traits().map(Trait::from));
|
||||||
}
|
}
|
||||||
traits.extend(m.crate_def_map[m.module_id].scope.traits());
|
traits.extend(m.crate_def_map[m.module_id].scope.traits().map(Trait::from));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
traits
|
traits
|
||||||
|
@ -351,7 +355,7 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn krate(&self) -> Option<Crate> {
|
pub(crate) fn krate(&self) -> Option<Crate> {
|
||||||
self.module().map(|t| t.0.krate())
|
self.module().map(|t| Crate { crate_id: t.0.krate() })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn where_predicates_in_scope<'a>(
|
pub(crate) fn where_predicates_in_scope<'a>(
|
||||||
|
@ -420,8 +424,10 @@ impl From<PerNs> for ScopeDef {
|
||||||
fn from(def: PerNs) -> Self {
|
fn from(def: PerNs) -> Self {
|
||||||
def.take_types()
|
def.take_types()
|
||||||
.or_else(|| def.take_values())
|
.or_else(|| def.take_values())
|
||||||
.map(ScopeDef::ModuleDef)
|
.map(|module_def_id| ScopeDef::ModuleDef(module_def_id.into()))
|
||||||
.or_else(|| def.get_macros().map(ScopeDef::MacroDef))
|
.or_else(|| {
|
||||||
|
def.get_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into()))
|
||||||
|
})
|
||||||
.unwrap_or(ScopeDef::Unknown)
|
.unwrap_or(ScopeDef::Unknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,18 +447,16 @@ impl Scope {
|
||||||
f(name.clone(), res.def.into());
|
f(name.clone(), res.def.into());
|
||||||
});
|
});
|
||||||
m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
|
m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
|
||||||
f(name.clone(), ScopeDef::MacroDef(macro_));
|
f(name.clone(), ScopeDef::MacroDef(macro_.into()));
|
||||||
});
|
});
|
||||||
m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| {
|
m.crate_def_map.extern_prelude().iter().for_each(|(name, &def)| {
|
||||||
f(name.clone(), ScopeDef::ModuleDef(*def));
|
f(name.clone(), ScopeDef::ModuleDef(def.into()));
|
||||||
});
|
});
|
||||||
if let Some(prelude) = m.crate_def_map.prelude() {
|
if let Some(prelude) = m.crate_def_map.prelude() {
|
||||||
let prelude_def_map = db.crate_def_map(prelude.krate());
|
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||||
prelude_def_map[prelude.id.module_id].scope.entries().for_each(
|
prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| {
|
||||||
|(name, res)| {
|
|
||||||
f(name.clone(), res.def.into());
|
f(name.clone(), res.def.into());
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scope::GenericParams(gp) => {
|
Scope::GenericParams(gp) => {
|
||||||
|
|
|
@ -253,8 +253,11 @@ impl SourceAnalyzer {
|
||||||
Some(res)
|
Some(res)
|
||||||
});
|
});
|
||||||
|
|
||||||
let items =
|
let items = self
|
||||||
self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def);
|
.resolver
|
||||||
|
.resolve_module_path(db, &path)
|
||||||
|
.take_types()
|
||||||
|
.map(|it| PathResolution::Def(it.into()));
|
||||||
types.or(values).or(items).or_else(|| {
|
types.or(values).or(items).or_else(|| {
|
||||||
self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def))
|
self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def))
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,7 +25,7 @@ use hir_def::{
|
||||||
path::known,
|
path::known,
|
||||||
type_ref::{Mutability, TypeRef},
|
type_ref::{Mutability, TypeRef},
|
||||||
};
|
};
|
||||||
use hir_expand::name;
|
use hir_expand::{diagnostics::DiagnosticSink, name};
|
||||||
use ra_arena::map::ArenaMap;
|
use ra_arena::map::ArenaMap;
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
@ -40,7 +40,6 @@ use crate::{
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
code_model::TypeAlias,
|
code_model::TypeAlias,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
diagnostics::DiagnosticSink,
|
|
||||||
expr::{BindingAnnotation, Body, ExprId, PatId},
|
expr::{BindingAnnotation, Body, ExprId, PatId},
|
||||||
resolve::{Resolver, TypeNs},
|
resolve::{Resolver, TypeNs},
|
||||||
ty::infer::diagnostics::InferenceDiagnostic,
|
ty::infer::diagnostics::InferenceDiagnostic,
|
||||||
|
@ -719,12 +718,9 @@ impl Expectation {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod diagnostics {
|
mod diagnostics {
|
||||||
use crate::{
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
db::HirDatabase,
|
|
||||||
diagnostics::{DiagnosticSink, NoSuchField},
|
use crate::{db::HirDatabase, diagnostics::NoSuchField, expr::ExprId, Function, HasSource};
|
||||||
expr::ExprId,
|
|
||||||
Function, HasSource,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub(super) enum InferenceDiagnostic {
|
pub(super) enum InferenceDiagnostic {
|
||||||
|
|
|
@ -11,12 +11,11 @@ use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
|
expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
|
||||||
generics::{GenericParams, HasGenericParams},
|
generics::{GenericParams, HasGenericParams},
|
||||||
nameres::Namespace,
|
|
||||||
ty::{
|
ty::{
|
||||||
autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation,
|
autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation,
|
||||||
ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||||
},
|
},
|
||||||
Adt, Name,
|
Adt, Name, Namespace,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
|
|
|
@ -23,15 +23,14 @@ use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
generics::HasGenericParams,
|
generics::HasGenericParams,
|
||||||
generics::{GenericDef, WherePredicate},
|
generics::{GenericDef, WherePredicate},
|
||||||
nameres::Namespace,
|
|
||||||
resolve::{Resolver, TypeNs},
|
resolve::{Resolver, TypeNs},
|
||||||
ty::{
|
ty::{
|
||||||
primitive::{FloatTy, IntTy},
|
primitive::{FloatTy, IntTy},
|
||||||
Adt,
|
Adt,
|
||||||
},
|
},
|
||||||
util::make_mut_slice,
|
util::make_mut_slice,
|
||||||
Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, Trait,
|
Const, Enum, EnumVariant, Function, ModuleDef, Namespace, Path, Static, Struct, StructField,
|
||||||
TypeAlias, Union,
|
Trait, TypeAlias, Union,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Ty {
|
impl Ty {
|
||||||
|
|
|
@ -2,8 +2,7 @@ use std::fmt::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
|
use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
|
||||||
use ra_db::{salsa::Database, FilePosition, SourceDatabase};
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo,
|
algo,
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
|
@ -25,9 +24,9 @@ mod coercion;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cfg_impl_block() {
|
fn cfg_impl_block() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo cfg:test
|
||||||
use foo::S as T;
|
use foo::S as T;
|
||||||
struct S;
|
struct S;
|
||||||
|
|
||||||
|
@ -46,7 +45,7 @@ fn test() {
|
||||||
t<|>;
|
t<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /foo.rs
|
//- /foo.rs crate:foo
|
||||||
struct S;
|
struct S;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
@ -60,18 +59,14 @@ impl S {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"], cfg = { "test" }),
|
|
||||||
"foo": ("/foo.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos));
|
assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_await() {
|
fn infer_await() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
|
|
||||||
struct IntFuture;
|
struct IntFuture;
|
||||||
|
|
||||||
|
@ -85,7 +80,7 @@ fn test() {
|
||||||
v<|>;
|
v<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /std.rs
|
//- /std.rs crate:std
|
||||||
#[prelude_import] use future::*;
|
#[prelude_import] use future::*;
|
||||||
mod future {
|
mod future {
|
||||||
trait Future {
|
trait Future {
|
||||||
|
@ -95,18 +90,14 @@ mod future {
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/std.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("u64", type_at_pos(&db, pos));
|
assert_eq!("u64", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_box() {
|
fn infer_box() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
let x = box 1;
|
let x = box 1;
|
||||||
|
@ -114,7 +105,7 @@ fn test() {
|
||||||
t<|>;
|
t<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /std.rs
|
//- /std.rs crate:std
|
||||||
#[prelude_import] use prelude::*;
|
#[prelude_import] use prelude::*;
|
||||||
mod prelude {}
|
mod prelude {}
|
||||||
|
|
||||||
|
@ -126,10 +117,6 @@ mod boxed {
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/std.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("(Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32;_]>)", type_at_pos(&db, pos));
|
assert_eq!("(Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32;_]>)", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,9 +141,9 @@ fn test() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_try() {
|
fn infer_try() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
let r: Result<i32, u64> = Result::Ok(1);
|
let r: Result<i32, u64> = Result::Ok(1);
|
||||||
|
@ -164,7 +151,7 @@ fn test() {
|
||||||
v<|>;
|
v<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /std.rs
|
//- /std.rs crate:std
|
||||||
|
|
||||||
#[prelude_import] use ops::*;
|
#[prelude_import] use ops::*;
|
||||||
mod ops {
|
mod ops {
|
||||||
|
@ -189,18 +176,14 @@ mod result {
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/std.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("i32", type_at_pos(&db, pos));
|
assert_eq!("i32", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_for_loop() {
|
fn infer_for_loop() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
|
|
||||||
use std::collections::Vec;
|
use std::collections::Vec;
|
||||||
|
|
||||||
|
@ -212,7 +195,7 @@ fn test() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /std.rs
|
//- /std.rs crate:std
|
||||||
|
|
||||||
#[prelude_import] use iter::*;
|
#[prelude_import] use iter::*;
|
||||||
mod iter {
|
mod iter {
|
||||||
|
@ -234,10 +217,6 @@ mod collections {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/std.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("&str", type_at_pos(&db, pos));
|
assert_eq!("&str", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2505,15 +2484,15 @@ pub fn main_loop() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cross_crate_associated_method_call() {
|
fn cross_crate_associated_method_call() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:other_crate
|
||||||
fn test() {
|
fn test() {
|
||||||
let x = other_crate::foo::S::thing();
|
let x = other_crate::foo::S::thing();
|
||||||
x<|>;
|
x<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:other_crate
|
||||||
mod foo {
|
mod foo {
|
||||||
struct S;
|
struct S;
|
||||||
impl S {
|
impl S {
|
||||||
|
@ -2522,10 +2501,6 @@ mod foo {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["other_crate"]),
|
|
||||||
"other_crate": ("/lib.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("i128", type_at_pos(&db, pos));
|
assert_eq!("i128", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3403,16 +3378,15 @@ fn test() { S.foo()<|>; }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_macro_with_dollar_crate_is_correct_in_expr() {
|
fn infer_macro_with_dollar_crate_is_correct_in_expr() {
|
||||||
covers!(macro_dollar_crate_other);
|
let (db, pos) = MockDatabase::with_position(
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
fn test() {
|
fn test() {
|
||||||
let x = (foo::foo!(1), foo::foo!(2));
|
let x = (foo::foo!(1), foo::foo!(2));
|
||||||
x<|>;
|
x<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
(1) => { $crate::bar!() };
|
(1) => { $crate::bar!() };
|
||||||
|
@ -3427,10 +3401,6 @@ macro_rules! bar {
|
||||||
pub fn baz() -> usize { 31usize }
|
pub fn baz() -> usize { 31usize }
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3512,9 +3482,9 @@ fn test() { (&S).foo()<|>; }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn method_resolution_trait_from_prelude() {
|
fn method_resolution_trait_from_prelude() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:other_crate
|
||||||
struct S;
|
struct S;
|
||||||
impl Clone for S {}
|
impl Clone for S {}
|
||||||
|
|
||||||
|
@ -3522,7 +3492,7 @@ fn test() {
|
||||||
S.clone()<|>;
|
S.clone()<|>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:other_crate
|
||||||
#[prelude_import] use foo::*;
|
#[prelude_import] use foo::*;
|
||||||
|
|
||||||
mod foo {
|
mod foo {
|
||||||
|
@ -3532,10 +3502,6 @@ mod foo {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
|
||||||
"main": ("/main.rs", ["other_crate"]),
|
|
||||||
"other_crate": ("/lib.rs", []),
|
|
||||||
});
|
|
||||||
assert_eq!("S", type_at_pos(&db, pos));
|
assert_eq!("S", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ authors = ["rust-analyzer developers"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
once_cell = "1.0.1"
|
once_cell = "1.0.1"
|
||||||
relative-path = "1.0.0"
|
|
||||||
rustc-hash = "1.0"
|
rustc-hash = "1.0"
|
||||||
|
|
||||||
ra_arena = { path = "../ra_arena" }
|
ra_arena = { path = "../ra_arena" }
|
||||||
|
@ -19,3 +18,6 @@ test_utils = { path = "../test_utils" }
|
||||||
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||||
ra_cfg = { path = "../ra_cfg" }
|
ra_cfg = { path = "../ra_cfg" }
|
||||||
tt = { path = "../ra_tt", package = "ra_tt" }
|
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
insta = "0.12.0"
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId,
|
db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId,
|
||||||
LocalStructFieldId, StructId,
|
LocalStructFieldId, StructId, UnionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Note that we use `StructData` for unions as well!
|
/// Note that we use `StructData` for unions as well!
|
||||||
|
@ -56,6 +56,13 @@ impl StructData {
|
||||||
let variant_data = Arc::new(variant_data);
|
let variant_data = Arc::new(variant_data);
|
||||||
Arc::new(StructData { name, variant_data })
|
Arc::new(StructData { name, variant_data })
|
||||||
}
|
}
|
||||||
|
pub(crate) fn union_data_query(db: &impl DefDatabase2, struct_: UnionId) -> Arc<StructData> {
|
||||||
|
let src = struct_.source(db);
|
||||||
|
let name = src.ast.name().map(|n| n.as_name());
|
||||||
|
let variant_data = VariantData::new(src.ast.kind());
|
||||||
|
let variant_data = Arc::new(variant_data);
|
||||||
|
Arc::new(StructData { name, variant_data })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumData {
|
impl EnumData {
|
||||||
|
@ -74,6 +81,11 @@ impl EnumData {
|
||||||
.collect();
|
.collect();
|
||||||
Arc::new(EnumData { name, variants })
|
Arc::new(EnumData { name, variants })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
|
||||||
|
let (id, _) = self.variants.iter().find(|(_id, data)| data.name.as_ref() == Some(name))?;
|
||||||
|
Some(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariantData {
|
impl VariantData {
|
||||||
|
|
|
@ -2,13 +2,16 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_expand::{db::AstDatabase, HirFileId};
|
use hir_expand::{db::AstDatabase, HirFileId};
|
||||||
use ra_db::{salsa, SourceDatabase};
|
use ra_db::{salsa, CrateId, SourceDatabase};
|
||||||
use ra_syntax::ast;
|
use ra_syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::{EnumData, StructData},
|
adt::{EnumData, StructData},
|
||||||
nameres::raw::{ImportSourceMap, RawItems},
|
nameres::{
|
||||||
EnumId, StructId,
|
raw::{ImportSourceMap, RawItems},
|
||||||
|
CrateDefMap,
|
||||||
|
},
|
||||||
|
EnumId, StructId, UnionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[salsa::query_group(InternDatabaseStorage)]
|
#[salsa::query_group(InternDatabaseStorage)]
|
||||||
|
@ -42,9 +45,15 @@ pub trait DefDatabase2: InternDatabase + AstDatabase {
|
||||||
#[salsa::invoke(RawItems::raw_items_query)]
|
#[salsa::invoke(RawItems::raw_items_query)]
|
||||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
||||||
|
|
||||||
|
#[salsa::invoke(CrateDefMap::crate_def_map_query)]
|
||||||
|
fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>;
|
||||||
|
|
||||||
#[salsa::invoke(StructData::struct_data_query)]
|
#[salsa::invoke(StructData::struct_data_query)]
|
||||||
fn struct_data(&self, s: StructId) -> Arc<StructData>;
|
fn struct_data(&self, s: StructId) -> Arc<StructData>;
|
||||||
|
|
||||||
|
#[salsa::invoke(StructData::union_data_query)]
|
||||||
|
fn union_data(&self, s: UnionId) -> Arc<StructData>;
|
||||||
|
|
||||||
#[salsa::invoke(EnumData::enum_data_query)]
|
#[salsa::invoke(EnumData::enum_data_query)]
|
||||||
fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
|
fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
|
||||||
}
|
}
|
||||||
|
|
28
crates/ra_hir_def/src/diagnostics.rs
Normal file
28
crates/ra_hir_def/src/diagnostics.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//! Diagnostics produced by `hir_def`.
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
use hir_expand::diagnostics::Diagnostic;
|
||||||
|
use ra_db::RelativePathBuf;
|
||||||
|
use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||||
|
|
||||||
|
use hir_expand::{HirFileId, Source};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnresolvedModule {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub decl: AstPtr<ast::Module>,
|
||||||
|
pub candidate: RelativePathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for UnresolvedModule {
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"unresolved module".to_string()
|
||||||
|
}
|
||||||
|
fn source(&self) -> Source<SyntaxNodePtr> {
|
||||||
|
Source { file_id: self.file, ast: self.decl.into() }
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,25 +13,25 @@ pub mod path;
|
||||||
pub mod type_ref;
|
pub mod type_ref;
|
||||||
pub mod builtin_type;
|
pub mod builtin_type;
|
||||||
pub mod adt;
|
pub mod adt;
|
||||||
|
pub mod diagnostics;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_db;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod marks;
|
||||||
|
|
||||||
// FIXME: this should be private
|
// FIXME: this should be private
|
||||||
pub mod nameres;
|
pub mod nameres;
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId};
|
use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, Source};
|
||||||
use ra_arena::{impl_arena_id, RawId};
|
use ra_arena::{impl_arena_id, RawId};
|
||||||
use ra_db::{salsa, CrateId, FileId};
|
use ra_db::{salsa, CrateId, FileId};
|
||||||
use ra_syntax::{ast, AstNode, SyntaxNode};
|
use ra_syntax::{ast, AstNode, SyntaxNode};
|
||||||
|
|
||||||
use crate::{builtin_type::BuiltinType, db::InternDatabase};
|
use crate::{builtin_type::BuiltinType, db::InternDatabase};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub struct Source<T> {
|
|
||||||
pub file_id: HirFileId,
|
|
||||||
pub ast: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ModuleSource {
|
pub enum ModuleSource {
|
||||||
SourceFile(ast::SourceFile),
|
SourceFile(ast::SourceFile),
|
||||||
Module(ast::Module),
|
Module(ast::Module),
|
||||||
|
@ -94,15 +94,6 @@ impl ModuleSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Source<T> {
|
|
||||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
|
||||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
|
||||||
}
|
|
||||||
pub fn file_syntax(&self, db: &impl AstDatabase) -> SyntaxNode {
|
|
||||||
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct ModuleId {
|
pub struct ModuleId {
|
||||||
pub krate: CrateId,
|
pub krate: CrateId,
|
||||||
|
@ -252,8 +243,8 @@ impl AstItemDef<ast::EnumDef> for EnumId {
|
||||||
// FIXME: rename to `VariantId`, only enums can ave variants
|
// FIXME: rename to `VariantId`, only enums can ave variants
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct EnumVariantId {
|
pub struct EnumVariantId {
|
||||||
parent: EnumId,
|
pub parent: EnumId,
|
||||||
local_id: LocalEnumVariantId,
|
pub local_id: LocalEnumVariantId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
14
crates/ra_hir_def/src/marks.rs
Normal file
14
crates/ra_hir_def/src/marks.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//! See test_utils/src/marks.rs
|
||||||
|
|
||||||
|
test_utils::marks!(
|
||||||
|
bogus_paths
|
||||||
|
name_res_works_for_broken_modules
|
||||||
|
can_import_enum_variant
|
||||||
|
glob_enum
|
||||||
|
glob_across_crates
|
||||||
|
std_prelude
|
||||||
|
macro_rules_from_other_crates_are_visible_with_macro_use
|
||||||
|
prelude_is_macro_use
|
||||||
|
macro_dollar_crate_self
|
||||||
|
macro_dollar_crate_other
|
||||||
|
);
|
|
@ -1,5 +1,543 @@
|
||||||
//! FIXME: write short doc here
|
//! This module implements import-resolution/macro expansion algorithm.
|
||||||
|
//!
|
||||||
|
//! The result of this module is `CrateDefMap`: a data structure which contains:
|
||||||
|
//!
|
||||||
|
//! * a tree of modules for the crate
|
||||||
|
//! * for each module, a set of items visible in the module (directly declared
|
||||||
|
//! or imported)
|
||||||
|
//!
|
||||||
|
//! Note that `CrateDefMap` contains fully macro expanded code.
|
||||||
|
//!
|
||||||
|
//! Computing `CrateDefMap` can be partitioned into several logically
|
||||||
|
//! independent "phases". The phases are mutually recursive though, there's no
|
||||||
|
//! strict ordering.
|
||||||
|
//!
|
||||||
|
//! ## Collecting RawItems
|
||||||
|
//!
|
||||||
|
//! This happens in the `raw` module, which parses a single source file into a
|
||||||
|
//! set of top-level items. Nested imports are desugared to flat imports in
|
||||||
|
//! this phase. Macro calls are represented as a triple of (Path, Option<Name>,
|
||||||
|
//! TokenTree).
|
||||||
|
//!
|
||||||
|
//! ## Collecting Modules
|
||||||
|
//!
|
||||||
|
//! This happens in the `collector` module. In this phase, we recursively walk
|
||||||
|
//! tree of modules, collect raw items from submodules, populate module scopes
|
||||||
|
//! with defined items (so, we assign item ids in this phase) and record the set
|
||||||
|
//! of unresolved imports and macros.
|
||||||
|
//!
|
||||||
|
//! While we walk tree of modules, we also record macro_rules definitions and
|
||||||
|
//! expand calls to macro_rules defined macros.
|
||||||
|
//!
|
||||||
|
//! ## Resolving Imports
|
||||||
|
//!
|
||||||
|
//! We maintain a list of currently unresolved imports. On every iteration, we
|
||||||
|
//! try to resolve some imports from this list. If the import is resolved, we
|
||||||
|
//! record it, by adding an item to current module scope and, if necessary, by
|
||||||
|
//! recursively populating glob imports.
|
||||||
|
//!
|
||||||
|
//! ## Resolving Macros
|
||||||
|
//!
|
||||||
|
//! macro_rules from the same crate use a global mutable namespace. We expand
|
||||||
|
//! them immediately, when we collect modules.
|
||||||
|
//!
|
||||||
|
//! Macros from other crates (including proc-macros) can be used with
|
||||||
|
//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
|
||||||
|
//! unexpanded macros. On every iteration, we try to resolve each macro call
|
||||||
|
//! path and, upon success, we run macro expansion and "collect module" phase
|
||||||
|
//! on the result
|
||||||
|
|
||||||
// FIXME: review privacy of submodules
|
// FIXME: review privacy of submodules
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
|
pub mod per_ns;
|
||||||
|
pub mod collector;
|
||||||
pub mod mod_resolution;
|
pub mod mod_resolution;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use ra_arena::Arena;
|
||||||
|
use ra_db::{CrateId, Edition, FileId};
|
||||||
|
use ra_prof::profile;
|
||||||
|
use ra_syntax::ast;
|
||||||
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use test_utils::tested_by;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
builtin_type::BuiltinType,
|
||||||
|
db::DefDatabase2,
|
||||||
|
nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId},
|
||||||
|
path::{Path, PathKind},
|
||||||
|
AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Contains all top-level defs from a macro-expanded crate
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct CrateDefMap {
|
||||||
|
krate: CrateId,
|
||||||
|
edition: Edition,
|
||||||
|
/// The prelude module for this crate. This either comes from an import
|
||||||
|
/// marked with the `prelude_import` attribute, or (in the normal case) from
|
||||||
|
/// a dependency (`std` or `core`).
|
||||||
|
prelude: Option<ModuleId>,
|
||||||
|
extern_prelude: FxHashMap<Name, ModuleDefId>,
|
||||||
|
root: CrateModuleId,
|
||||||
|
pub modules: Arena<CrateModuleId, ModuleData>,
|
||||||
|
|
||||||
|
/// Some macros are not well-behavior, which leads to infinite loop
|
||||||
|
/// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
|
||||||
|
/// We mark it down and skip it in collector
|
||||||
|
///
|
||||||
|
/// FIXME:
|
||||||
|
/// Right now it only handle a poison macro in a single crate,
|
||||||
|
/// such that if other crate try to call that macro,
|
||||||
|
/// the whole process will do again until it became poisoned in that crate.
|
||||||
|
/// We should handle this macro set globally
|
||||||
|
/// However, do we want to put it as a global variable?
|
||||||
|
poison_macros: FxHashSet<MacroDefId>,
|
||||||
|
|
||||||
|
diagnostics: Vec<DefDiagnostic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<CrateModuleId> for CrateDefMap {
|
||||||
|
type Output = ModuleData;
|
||||||
|
fn index(&self, id: CrateModuleId) -> &ModuleData {
|
||||||
|
&self.modules[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ModuleData {
|
||||||
|
pub parent: Option<CrateModuleId>,
|
||||||
|
pub children: FxHashMap<Name, CrateModuleId>,
|
||||||
|
pub scope: ModuleScope,
|
||||||
|
/// None for root
|
||||||
|
pub declaration: Option<AstId<ast::Module>>,
|
||||||
|
/// None for inline modules.
|
||||||
|
///
|
||||||
|
/// Note that non-inline modules, by definition, live inside non-macro file.
|
||||||
|
pub definition: Option<FileId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
|
pub struct ModuleScope {
|
||||||
|
pub items: FxHashMap<Name, Resolution>,
|
||||||
|
/// Macros visable in current module in legacy textual scope
|
||||||
|
///
|
||||||
|
/// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first.
|
||||||
|
/// If it yields no result, then it turns to module scoped `macros`.
|
||||||
|
/// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
|
||||||
|
/// and only normal scoped `macros` will be searched in.
|
||||||
|
///
|
||||||
|
/// Note that this automatically inherit macros defined textually before the definition of module itself.
|
||||||
|
///
|
||||||
|
/// Module scoped macros will be inserted into `items` instead of here.
|
||||||
|
// FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
|
||||||
|
// be all resolved to the last one defined if shadowing happens.
|
||||||
|
legacy_macros: FxHashMap<Name, MacroDefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
|
||||||
|
BuiltinType::ALL
|
||||||
|
.iter()
|
||||||
|
.map(|(name, ty)| {
|
||||||
|
(name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
|
||||||
|
/// Other methods will only resolve values, types and module scoped macros only.
|
||||||
|
impl ModuleScope {
|
||||||
|
pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
|
||||||
|
//FIXME: shadowing
|
||||||
|
self.items.iter().chain(BUILTIN_SCOPE.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over all module scoped macros
|
||||||
|
pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
|
||||||
|
self.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over all legacy textual scoped macros visable at the end of the module
|
||||||
|
pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
|
||||||
|
self.legacy_macros.iter().map(|(name, def)| (name, *def))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a name from current module scope, legacy macros are not included
|
||||||
|
pub fn get(&self, name: &Name) -> Option<&Resolution> {
|
||||||
|
self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
|
||||||
|
self.items.values().filter_map(|r| match r.def.take_types() {
|
||||||
|
Some(ModuleDefId::TraitId(t)) => Some(t),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
|
||||||
|
self.legacy_macros.get(name).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub struct Resolution {
|
||||||
|
/// None for unresolved
|
||||||
|
pub def: PerNs,
|
||||||
|
/// ident by which this is imported into local scope.
|
||||||
|
pub import: Option<ImportId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolution {
|
||||||
|
pub(crate) fn from_macro(macro_: MacroDefId) -> Self {
|
||||||
|
Resolution { def: PerNs::macros(macro_), import: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ResolvePathResult {
|
||||||
|
resolved_def: PerNs,
|
||||||
|
segment_index: Option<usize>,
|
||||||
|
reached_fixedpoint: ReachedFixedPoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolvePathResult {
|
||||||
|
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
||||||
|
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with(
|
||||||
|
resolved_def: PerNs,
|
||||||
|
reached_fixedpoint: ReachedFixedPoint,
|
||||||
|
segment_index: Option<usize>,
|
||||||
|
) -> ResolvePathResult {
|
||||||
|
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum ResolveMode {
|
||||||
|
Import,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum ReachedFixedPoint {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrateDefMap {
|
||||||
|
pub(crate) fn crate_def_map_query(
|
||||||
|
// Note that this doesn't have `+ AstDatabase`!
|
||||||
|
// This gurantess that `CrateDefMap` is stable across reparses.
|
||||||
|
db: &impl DefDatabase2,
|
||||||
|
krate: CrateId,
|
||||||
|
) -> Arc<CrateDefMap> {
|
||||||
|
let _p = profile("crate_def_map_query");
|
||||||
|
let def_map = {
|
||||||
|
let crate_graph = db.crate_graph();
|
||||||
|
let edition = crate_graph.edition(krate);
|
||||||
|
let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
|
||||||
|
let root = modules.alloc(ModuleData::default());
|
||||||
|
CrateDefMap {
|
||||||
|
krate,
|
||||||
|
edition,
|
||||||
|
extern_prelude: FxHashMap::default(),
|
||||||
|
prelude: None,
|
||||||
|
root,
|
||||||
|
modules,
|
||||||
|
poison_macros: FxHashSet::default(),
|
||||||
|
diagnostics: Vec::new(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let def_map = collector::collect_defs(db, def_map);
|
||||||
|
Arc::new(def_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn krate(&self) -> CrateId {
|
||||||
|
self.krate
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn root(&self) -> CrateModuleId {
|
||||||
|
self.root
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prelude(&self) -> Option<ModuleId> {
|
||||||
|
self.prelude
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDefId> {
|
||||||
|
&self.extern_prelude
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_diagnostics(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase2,
|
||||||
|
module: CrateModuleId,
|
||||||
|
sink: &mut DiagnosticSink,
|
||||||
|
) {
|
||||||
|
self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_path(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase2,
|
||||||
|
original_module: CrateModuleId,
|
||||||
|
path: &Path,
|
||||||
|
) -> (PerNs, Option<usize>) {
|
||||||
|
let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
|
||||||
|
(res.resolved_def, res.segment_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
|
||||||
|
// the result.
|
||||||
|
fn resolve_path_fp_with_macro(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase2,
|
||||||
|
mode: ResolveMode,
|
||||||
|
original_module: CrateModuleId,
|
||||||
|
path: &Path,
|
||||||
|
) -> ResolvePathResult {
|
||||||
|
let mut segments = path.segments.iter().enumerate();
|
||||||
|
let mut curr_per_ns: PerNs = match path.kind {
|
||||||
|
PathKind::DollarCrate(krate) => {
|
||||||
|
if krate == self.krate {
|
||||||
|
tested_by!(macro_dollar_crate_self);
|
||||||
|
PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
|
||||||
|
} else {
|
||||||
|
let def_map = db.crate_def_map(krate);
|
||||||
|
let module = ModuleId { krate, module_id: def_map.root };
|
||||||
|
tested_by!(macro_dollar_crate_other);
|
||||||
|
PerNs::types(module.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathKind::Crate => {
|
||||||
|
PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
|
||||||
|
}
|
||||||
|
PathKind::Self_ => {
|
||||||
|
PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
|
||||||
|
}
|
||||||
|
// plain import or absolute path in 2015: crate-relative with
|
||||||
|
// fallback to extern prelude (with the simplification in
|
||||||
|
// rust-lang/rust#57745)
|
||||||
|
// FIXME there must be a nicer way to write this condition
|
||||||
|
PathKind::Plain | PathKind::Abs
|
||||||
|
if self.edition == Edition::Edition2015
|
||||||
|
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
|
||||||
|
{
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
||||||
|
self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
|
||||||
|
}
|
||||||
|
PathKind::Plain => {
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
log::debug!("resolving {:?} in module", segment);
|
||||||
|
self.resolve_name_in_module(db, original_module, &segment.name)
|
||||||
|
}
|
||||||
|
PathKind::Super => {
|
||||||
|
if let Some(p) = self.modules[original_module].parent {
|
||||||
|
PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
|
||||||
|
} else {
|
||||||
|
log::debug!("super path in root module");
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathKind::Abs => {
|
||||||
|
// 2018-style absolute path -- only extern prelude
|
||||||
|
let segment = match segments.next() {
|
||||||
|
Some((_, segment)) => segment,
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
};
|
||||||
|
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
||||||
|
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||||
|
PerNs::types(*def)
|
||||||
|
} else {
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathKind::Type(_) => {
|
||||||
|
// This is handled in `infer::infer_path_expr`
|
||||||
|
// The result returned here does not matter
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, segment) in segments {
|
||||||
|
let curr = match curr_per_ns.take_types() {
|
||||||
|
Some(r) => r,
|
||||||
|
None => {
|
||||||
|
// we still have path segments left, but the path so far
|
||||||
|
// didn't resolve in the types namespace => no resolution
|
||||||
|
// (don't break here because `curr_per_ns` might contain
|
||||||
|
// something in the value namespace, and it would be wrong
|
||||||
|
// to return that)
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// resolve segment in curr
|
||||||
|
|
||||||
|
curr_per_ns = match curr {
|
||||||
|
ModuleDefId::ModuleId(module) => {
|
||||||
|
if module.krate != self.krate {
|
||||||
|
let path =
|
||||||
|
Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
|
||||||
|
log::debug!("resolving {:?} in other crate", path);
|
||||||
|
let defp_map = db.crate_def_map(module.krate);
|
||||||
|
let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
|
||||||
|
return ResolvePathResult::with(
|
||||||
|
def,
|
||||||
|
ReachedFixedPoint::Yes,
|
||||||
|
s.map(|s| s + i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since it is a qualified path here, it should not contains legacy macros
|
||||||
|
match self[module.module_id].scope.get(&segment.name) {
|
||||||
|
Some(res) => res.def,
|
||||||
|
_ => {
|
||||||
|
log::debug!("path segment {:?} not found", segment.name);
|
||||||
|
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
|
||||||
|
// enum variant
|
||||||
|
tested_by!(can_import_enum_variant);
|
||||||
|
let enum_data = db.enum_data(e);
|
||||||
|
match enum_data.variant(&segment.name) {
|
||||||
|
Some(local_id) => {
|
||||||
|
let variant = EnumVariantId { parent: e, local_id };
|
||||||
|
PerNs::both(variant.into(), variant.into())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return ResolvePathResult::with(
|
||||||
|
PerNs::types(e.into()),
|
||||||
|
ReachedFixedPoint::Yes,
|
||||||
|
Some(i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s => {
|
||||||
|
// could be an inherent method call in UFCS form
|
||||||
|
// (`Struct::method`), or some other kind of associated item
|
||||||
|
log::debug!(
|
||||||
|
"path segment {:?} resolved to non-module {:?}, but is not last",
|
||||||
|
segment.name,
|
||||||
|
curr,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ResolvePathResult::with(
|
||||||
|
PerNs::types(s),
|
||||||
|
ReachedFixedPoint::Yes,
|
||||||
|
Some(i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
|
||||||
|
let from_crate_root =
|
||||||
|
self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
|
||||||
|
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
|
||||||
|
|
||||||
|
from_crate_root.or(from_extern_prelude)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_name_in_module(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase2,
|
||||||
|
module: CrateModuleId,
|
||||||
|
name: &Name,
|
||||||
|
) -> PerNs {
|
||||||
|
// Resolve in:
|
||||||
|
// - legacy scope of macro
|
||||||
|
// - current module / scope
|
||||||
|
// - extern prelude
|
||||||
|
// - std prelude
|
||||||
|
let from_legacy_macro =
|
||||||
|
self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
|
||||||
|
let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
|
||||||
|
let from_extern_prelude =
|
||||||
|
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
|
||||||
|
let from_prelude = self.resolve_in_prelude(db, name);
|
||||||
|
|
||||||
|
from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
|
||||||
|
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
|
||||||
|
if let Some(prelude) = self.prelude {
|
||||||
|
let keep;
|
||||||
|
let def_map = if prelude.krate == self.krate {
|
||||||
|
self
|
||||||
|
} else {
|
||||||
|
// Extend lifetime
|
||||||
|
keep = db.crate_def_map(prelude.krate);
|
||||||
|
&keep
|
||||||
|
};
|
||||||
|
def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
|
||||||
|
} else {
|
||||||
|
PerNs::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod diagnostics {
|
||||||
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
|
use ra_db::RelativePathBuf;
|
||||||
|
use ra_syntax::{ast, AstPtr};
|
||||||
|
|
||||||
|
use crate::{db::DefDatabase2, diagnostics::UnresolvedModule, nameres::CrateModuleId, AstId};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(super) enum DefDiagnostic {
|
||||||
|
UnresolvedModule {
|
||||||
|
module: CrateModuleId,
|
||||||
|
declaration: AstId<ast::Module>,
|
||||||
|
candidate: RelativePathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefDiagnostic {
|
||||||
|
pub(super) fn add_to(
|
||||||
|
&self,
|
||||||
|
db: &impl DefDatabase2,
|
||||||
|
target_module: CrateModuleId,
|
||||||
|
sink: &mut DiagnosticSink,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
|
||||||
|
if *module != target_module {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let decl = declaration.to_node(db);
|
||||||
|
sink.push(UnresolvedModule {
|
||||||
|
file: declaration.file_id(),
|
||||||
|
decl: AstPtr::new(&decl),
|
||||||
|
candidate: candidate.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,45 +1,49 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use hir_def::{
|
use hir_expand::{
|
||||||
attr::Attr,
|
name::{self, AsName, Name},
|
||||||
nameres::{mod_resolution::ModDir, raw},
|
HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind,
|
||||||
};
|
};
|
||||||
use hir_expand::name;
|
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use ra_db::FileId;
|
use ra_db::{CrateId, FileId};
|
||||||
use ra_syntax::{ast, SmolStr};
|
use ra_syntax::{ast, SmolStr};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
attr::Attr,
|
||||||
ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind},
|
db::DefDatabase2,
|
||||||
nameres::{
|
nameres::{
|
||||||
diagnostics::DefDiagnostic, Crate, CrateDefMap, CrateModuleId, ModuleData, ModuleDef,
|
diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap,
|
||||||
PerNs, ReachedFixedPoint, Resolution, ResolveMode,
|
ModuleData, ReachedFixedPoint, Resolution, ResolveMode,
|
||||||
},
|
},
|
||||||
Adt, AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, PathKind, Static,
|
path::{Path, PathKind},
|
||||||
Struct, Trait, TypeAlias, Union,
|
AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
|
||||||
|
LocationCtx, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap {
|
pub(super) fn collect_defs(db: &impl DefDatabase2, mut def_map: CrateDefMap) -> CrateDefMap {
|
||||||
|
let crate_graph = db.crate_graph();
|
||||||
|
|
||||||
// populate external prelude
|
// populate external prelude
|
||||||
for dep in def_map.krate.dependencies(db) {
|
for dep in crate_graph.dependencies(def_map.krate) {
|
||||||
log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
|
let dep_def_map = db.crate_def_map(dep.crate_id);
|
||||||
if let Some(module) = dep.krate.root_module(db) {
|
log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
|
||||||
def_map.extern_prelude.insert(dep.name.clone(), module.into());
|
def_map.extern_prelude.insert(
|
||||||
}
|
dep.as_name(),
|
||||||
|
ModuleId { krate: dep.crate_id, module_id: dep_def_map.root }.into(),
|
||||||
|
);
|
||||||
|
|
||||||
// look for the prelude
|
// look for the prelude
|
||||||
if def_map.prelude.is_none() {
|
if def_map.prelude.is_none() {
|
||||||
let map = db.crate_def_map(dep.krate);
|
let map = db.crate_def_map(dep.crate_id);
|
||||||
if map.prelude.is_some() {
|
if map.prelude.is_some() {
|
||||||
def_map.prelude = map.prelude;
|
def_map.prelude = map.prelude;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let crate_graph = db.crate_graph();
|
let cfg_options = crate_graph.cfg_options(def_map.krate);
|
||||||
let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id());
|
|
||||||
|
|
||||||
let mut collector = DefCollector {
|
let mut collector = DefCollector {
|
||||||
db,
|
db,
|
||||||
|
@ -101,11 +105,11 @@ struct DefCollector<'a, DB> {
|
||||||
|
|
||||||
impl<DB> DefCollector<'_, DB>
|
impl<DB> DefCollector<'_, DB>
|
||||||
where
|
where
|
||||||
DB: DefDatabase,
|
DB: DefDatabase2,
|
||||||
{
|
{
|
||||||
fn collect(&mut self) {
|
fn collect(&mut self) {
|
||||||
let crate_graph = self.db.crate_graph();
|
let crate_graph = self.db.crate_graph();
|
||||||
let file_id = crate_graph.crate_root(self.def_map.krate.crate_id());
|
let file_id = crate_graph.crate_root(self.def_map.krate);
|
||||||
let raw_items = self.db.raw_items(file_id.into());
|
let raw_items = self.db.raw_items(file_id.into());
|
||||||
let module_id = self.def_map.root;
|
let module_id = self.def_map.root;
|
||||||
self.def_map.modules[module_id].definition = Some(file_id);
|
self.def_map.modules[module_id].definition = Some(file_id);
|
||||||
|
@ -168,7 +172,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
module_id: CrateModuleId,
|
module_id: CrateModuleId,
|
||||||
name: Name,
|
name: Name,
|
||||||
macro_: MacroDef,
|
macro_: MacroDefId,
|
||||||
export: bool,
|
export: bool,
|
||||||
) {
|
) {
|
||||||
// Textual scoping
|
// Textual scoping
|
||||||
|
@ -189,7 +193,7 @@ where
|
||||||
/// the definition of current module.
|
/// the definition of current module.
|
||||||
/// And also, `macro_use` on a module will import all legacy macros visable inside to
|
/// And also, `macro_use` on a module will import all legacy macros visable inside to
|
||||||
/// current legacy scope, with possible shadowing.
|
/// current legacy scope, with possible shadowing.
|
||||||
fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDef) {
|
fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDefId) {
|
||||||
// Always shadowing
|
// Always shadowing
|
||||||
self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_);
|
self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_);
|
||||||
}
|
}
|
||||||
|
@ -213,9 +217,9 @@ where
|
||||||
.expect("extern crate should have been desugared to one-element path"),
|
.expect("extern crate should have been desugared to one-element path"),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ModuleDef::Module(m)) = res.take_types() {
|
if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
|
||||||
tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||||
self.import_all_macros_exported(current_module_id, m.krate());
|
self.import_all_macros_exported(current_module_id, m.krate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +228,7 @@ where
|
||||||
/// Exported macros are just all macros in the root module scope.
|
/// Exported macros are just all macros in the root module scope.
|
||||||
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
|
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
|
||||||
/// created by `use` in the root module, ignoring the visibility of `use`.
|
/// created by `use` in the root module, ignoring the visibility of `use`.
|
||||||
fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: Crate) {
|
fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: CrateId) {
|
||||||
let def_map = self.db.crate_def_map(krate);
|
let def_map = self.db.crate_def_map(krate);
|
||||||
for (name, def) in def_map[def_map.root].scope.macros() {
|
for (name, def) in def_map[def_map.root].scope.macros() {
|
||||||
// `macro_use` only bring things into legacy scope.
|
// `macro_use` only bring things into legacy scope.
|
||||||
|
@ -288,15 +292,15 @@ where
|
||||||
if import.is_glob {
|
if import.is_glob {
|
||||||
log::debug!("glob import: {:?}", import);
|
log::debug!("glob import: {:?}", import);
|
||||||
match def.take_types() {
|
match def.take_types() {
|
||||||
Some(ModuleDef::Module(m)) => {
|
Some(ModuleDefId::ModuleId(m)) => {
|
||||||
if import.is_prelude {
|
if import.is_prelude {
|
||||||
tested_by!(std_prelude);
|
tested_by!(std_prelude);
|
||||||
self.def_map.prelude = Some(m);
|
self.def_map.prelude = Some(m);
|
||||||
} else if m.krate() != self.def_map.krate {
|
} else if m.krate != self.def_map.krate {
|
||||||
tested_by!(glob_across_crates);
|
tested_by!(glob_across_crates);
|
||||||
// glob import from other crate => we can just import everything once
|
// glob import from other crate => we can just import everything once
|
||||||
let item_map = self.db.crate_def_map(m.krate());
|
let item_map = self.db.crate_def_map(m.krate);
|
||||||
let scope = &item_map[m.id.module_id].scope;
|
let scope = &item_map[m.module_id].scope;
|
||||||
|
|
||||||
// Module scoped macros is included
|
// Module scoped macros is included
|
||||||
let items = scope
|
let items = scope
|
||||||
|
@ -310,7 +314,7 @@ where
|
||||||
// glob import from same crate => we do an initial
|
// glob import from same crate => we do an initial
|
||||||
// import, and then need to propagate any further
|
// import, and then need to propagate any further
|
||||||
// additions
|
// additions
|
||||||
let scope = &self.def_map[m.id.module_id].scope;
|
let scope = &self.def_map[m.module_id].scope;
|
||||||
|
|
||||||
// Module scoped macros is included
|
// Module scoped macros is included
|
||||||
let items = scope
|
let items = scope
|
||||||
|
@ -322,23 +326,25 @@ where
|
||||||
self.update(module_id, Some(import_id), &items);
|
self.update(module_id, Some(import_id), &items);
|
||||||
// record the glob import in case we add further items
|
// record the glob import in case we add further items
|
||||||
self.glob_imports
|
self.glob_imports
|
||||||
.entry(m.id.module_id)
|
.entry(m.module_id)
|
||||||
.or_default()
|
.or_default()
|
||||||
.push((module_id, import_id));
|
.push((module_id, import_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(ModuleDef::Adt(Adt::Enum(e))) => {
|
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
|
||||||
tested_by!(glob_enum);
|
tested_by!(glob_enum);
|
||||||
// glob import from enum => just import all the variants
|
// glob import from enum => just import all the variants
|
||||||
let variants = e.variants(self.db);
|
let enum_data = self.db.enum_data(e);
|
||||||
let resolutions = variants
|
let resolutions = enum_data
|
||||||
.into_iter()
|
.variants
|
||||||
.filter_map(|variant| {
|
.iter()
|
||||||
|
.filter_map(|(local_id, variant_data)| {
|
||||||
|
let name = variant_data.name.clone()?;
|
||||||
|
let variant = EnumVariantId { parent: e, local_id };
|
||||||
let res = Resolution {
|
let res = Resolution {
|
||||||
def: PerNs::both(variant.into(), variant.into()),
|
def: PerNs::both(variant.into(), variant.into()),
|
||||||
import: Some(import_id),
|
import: Some(import_id),
|
||||||
};
|
};
|
||||||
let name = variant.name(self.db)?;
|
|
||||||
Some((name, res))
|
Some((name, res))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -451,8 +457,8 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(def) = resolved_res.resolved_def.get_macros() {
|
if let Some(def) = resolved_res.resolved_def.get_macros() {
|
||||||
let call_id = self.db.intern_macro(MacroCallLoc { def: def.id, ast_id: *ast_id });
|
let call_id = self.db.intern_macro(MacroCallLoc { def, ast_id: *ast_id });
|
||||||
resolved.push((*module_id, call_id, def.id));
|
resolved.push((*module_id, call_id, def));
|
||||||
res = ReachedFixedPoint::No;
|
res = ReachedFixedPoint::No;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -517,7 +523,7 @@ struct ModCollector<'a, D> {
|
||||||
|
|
||||||
impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
|
impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
|
||||||
where
|
where
|
||||||
DB: DefDatabase,
|
DB: DefDatabase2,
|
||||||
{
|
{
|
||||||
fn collect(&mut self, items: &[raw::RawItem]) {
|
fn collect(&mut self, items: &[raw::RawItem]) {
|
||||||
// Note: don't assert that inserted value is fresh: it's simply not true
|
// Note: don't assert that inserted value is fresh: it's simply not true
|
||||||
|
@ -526,10 +532,9 @@ where
|
||||||
|
|
||||||
// Prelude module is always considered to be `#[macro_use]`.
|
// Prelude module is always considered to be `#[macro_use]`.
|
||||||
if let Some(prelude_module) = self.def_collector.def_map.prelude {
|
if let Some(prelude_module) = self.def_collector.def_map.prelude {
|
||||||
if prelude_module.krate() != self.def_collector.def_map.krate {
|
if prelude_module.krate != self.def_collector.def_map.krate {
|
||||||
tested_by!(prelude_is_macro_use);
|
tested_by!(prelude_is_macro_use);
|
||||||
self.def_collector
|
self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
|
||||||
.import_all_macros_exported(self.module_id, prelude_module.krate());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +640,9 @@ where
|
||||||
modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
|
modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
|
||||||
modules[self.module_id].children.insert(name.clone(), res);
|
modules[self.module_id].children.insert(name.clone(), res);
|
||||||
let resolution = Resolution {
|
let resolution = Resolution {
|
||||||
def: PerNs::types(Module::new(self.def_collector.def_map.krate, res).into()),
|
def: PerNs::types(
|
||||||
|
ModuleId { krate: self.def_collector.def_map.krate, module_id: res }.into(),
|
||||||
|
),
|
||||||
import: None,
|
import: None,
|
||||||
};
|
};
|
||||||
self.def_collector.update(self.module_id, None, &[(name, resolution)]);
|
self.def_collector.update(self.module_id, None, &[(name, resolution)]);
|
||||||
|
@ -643,30 +650,32 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_def(&mut self, def: &raw::DefData) {
|
fn define_def(&mut self, def: &raw::DefData) {
|
||||||
let module = Module::new(self.def_collector.def_map.krate, self.module_id);
|
let module =
|
||||||
let ctx = LocationCtx::new(self.def_collector.db, module.id, self.file_id);
|
ModuleId { krate: self.def_collector.def_map.krate, module_id: self.module_id };
|
||||||
|
let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
|
||||||
|
|
||||||
macro_rules! def {
|
|
||||||
($kind:ident, $ast_id:ident) => {
|
|
||||||
$kind { id: AstItemDef::from_ast_id(ctx, $ast_id) }.into()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let name = def.name.clone();
|
let name = def.name.clone();
|
||||||
let def: PerNs = match def.kind {
|
let def: PerNs = match def.kind {
|
||||||
raw::DefKind::Function(ast_id) => PerNs::values(def!(Function, ast_id)),
|
raw::DefKind::Function(ast_id) => {
|
||||||
|
PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into())
|
||||||
|
}
|
||||||
raw::DefKind::Struct(ast_id) => {
|
raw::DefKind::Struct(ast_id) => {
|
||||||
let s = def!(Struct, ast_id);
|
let s = StructId::from_ast_id(ctx, ast_id).into();
|
||||||
PerNs::both(s, s)
|
PerNs::both(s, s)
|
||||||
}
|
}
|
||||||
raw::DefKind::Union(ast_id) => {
|
raw::DefKind::Union(ast_id) => {
|
||||||
let s = def!(Union, ast_id);
|
let s = UnionId::from_ast_id(ctx, ast_id).into();
|
||||||
PerNs::both(s, s)
|
PerNs::both(s, s)
|
||||||
}
|
}
|
||||||
raw::DefKind::Enum(ast_id) => PerNs::types(def!(Enum, ast_id)),
|
raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()),
|
||||||
raw::DefKind::Const(ast_id) => PerNs::values(def!(Const, ast_id)),
|
raw::DefKind::Const(ast_id) => PerNs::values(ConstId::from_ast_id(ctx, ast_id).into()),
|
||||||
raw::DefKind::Static(ast_id) => PerNs::values(def!(Static, ast_id)),
|
raw::DefKind::Static(ast_id) => {
|
||||||
raw::DefKind::Trait(ast_id) => PerNs::types(def!(Trait, ast_id)),
|
PerNs::values(StaticId::from_ast_id(ctx, ast_id).into())
|
||||||
raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)),
|
}
|
||||||
|
raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()),
|
||||||
|
raw::DefKind::TypeAlias(ast_id) => {
|
||||||
|
PerNs::types(TypeAliasId::from_ast_id(ctx, ast_id).into())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let resolution = Resolution { def, import: None };
|
let resolution = Resolution { def, import: None };
|
||||||
self.def_collector.update(self.module_id, None, &[(name, resolution)])
|
self.def_collector.update(self.module_id, None, &[(name, resolution)])
|
||||||
|
@ -678,10 +687,8 @@ where
|
||||||
// Case 1: macro rules, define a macro in crate-global mutable scope
|
// Case 1: macro rules, define a macro in crate-global mutable scope
|
||||||
if is_macro_rules(&mac.path) {
|
if is_macro_rules(&mac.path) {
|
||||||
if let Some(name) = &mac.name {
|
if let Some(name) = &mac.name {
|
||||||
let macro_id =
|
let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
|
||||||
MacroDefId { ast_id, krate: self.def_collector.def_map.krate.crate_id };
|
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
||||||
let macro_ = MacroDef { id: macro_id };
|
|
||||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -691,10 +698,10 @@ where
|
||||||
if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
|
if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
|
||||||
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
|
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
|
||||||
}) {
|
}) {
|
||||||
let def = macro_def.id;
|
let macro_call_id =
|
||||||
let macro_call_id = self.def_collector.db.intern_macro(MacroCallLoc { def, ast_id });
|
self.def_collector.db.intern_macro(MacroCallLoc { def: macro_def, ast_id });
|
||||||
|
|
||||||
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def);
|
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,15 +740,16 @@ fn is_macro_rules(path: &Path) -> bool {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ra_db::SourceDatabase;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{db::DefDatabase, mock::MockDatabase, Crate};
|
|
||||||
use ra_arena::Arena;
|
use ra_arena::Arena;
|
||||||
|
use ra_db::{fixture::WithFixture, SourceDatabase};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
|
use crate::{db::DefDatabase2, test_db::TestDB};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
fn do_collect_defs(
|
fn do_collect_defs(
|
||||||
db: &impl DefDatabase,
|
db: &impl DefDatabase2,
|
||||||
def_map: CrateDefMap,
|
def_map: CrateDefMap,
|
||||||
monitor: MacroStackMonitor,
|
monitor: MacroStackMonitor,
|
||||||
) -> CrateDefMap {
|
) -> CrateDefMap {
|
||||||
|
@ -760,12 +768,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap {
|
fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap {
|
||||||
let (db, _source_root, _) = MockDatabase::with_single_file(&code);
|
let (db, _file_id) = TestDB::with_single_file(&code);
|
||||||
let crate_id = db.crate_graph().iter().next().unwrap();
|
let krate = db.crate_graph().iter().next().unwrap();
|
||||||
let krate = Crate { crate_id };
|
|
||||||
|
|
||||||
let def_map = {
|
let def_map = {
|
||||||
let edition = krate.edition(&db);
|
let edition = db.crate_graph().edition(krate);
|
||||||
let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
|
let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
|
||||||
let root = modules.alloc(ModuleData::default());
|
let root = modules.alloc(ModuleData::default());
|
||||||
CrateDefMap {
|
CrateDefMap {
|
|
@ -1,8 +1,7 @@
|
||||||
//! This module resolves `mod foo;` declaration to file.
|
//! This module resolves `mod foo;` declaration to file.
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use ra_db::FileId;
|
use ra_db::{FileId, RelativePathBuf};
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
use relative_path::RelativePathBuf;
|
|
||||||
|
|
||||||
use crate::{db::DefDatabase2, HirFileId};
|
use crate::{db::DefDatabase2, HirFileId};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use crate::{MacroDef, ModuleDef};
|
use hir_expand::MacroDefId;
|
||||||
|
|
||||||
|
use crate::ModuleDefId;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Namespace {
|
pub enum Namespace {
|
||||||
|
@ -12,11 +14,11 @@ pub enum Namespace {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct PerNs {
|
pub struct PerNs {
|
||||||
pub types: Option<ModuleDef>,
|
pub types: Option<ModuleDefId>,
|
||||||
pub values: Option<ModuleDef>,
|
pub values: Option<ModuleDefId>,
|
||||||
/// Since macros has different type, many methods simply ignore it.
|
/// Since macros has different type, many methods simply ignore it.
|
||||||
/// We can only use special method like `get_macros` to access it.
|
/// We can only use special method like `get_macros` to access it.
|
||||||
pub macros: Option<MacroDef>,
|
pub macros: Option<MacroDefId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PerNs {
|
impl Default for PerNs {
|
||||||
|
@ -30,19 +32,19 @@ impl PerNs {
|
||||||
PerNs { types: None, values: None, macros: None }
|
PerNs { types: None, values: None, macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn values(t: ModuleDef) -> PerNs {
|
pub fn values(t: ModuleDefId) -> PerNs {
|
||||||
PerNs { types: None, values: Some(t), macros: None }
|
PerNs { types: None, values: Some(t), macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn types(t: ModuleDef) -> PerNs {
|
pub fn types(t: ModuleDefId) -> PerNs {
|
||||||
PerNs { types: Some(t), values: None, macros: None }
|
PerNs { types: Some(t), values: None, macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn both(types: ModuleDef, values: ModuleDef) -> PerNs {
|
pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs {
|
||||||
PerNs { types: Some(types), values: Some(values), macros: None }
|
PerNs { types: Some(types), values: Some(values), macros: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn macros(macro_: MacroDef) -> PerNs {
|
pub fn macros(macro_: MacroDefId) -> PerNs {
|
||||||
PerNs { types: None, values: None, macros: Some(macro_) }
|
PerNs { types: None, values: None, macros: Some(macro_) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,15 +56,15 @@ impl PerNs {
|
||||||
self.types.is_some() && self.values.is_some() && self.macros.is_some()
|
self.types.is_some() && self.values.is_some() && self.macros.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_types(self) -> Option<ModuleDef> {
|
pub fn take_types(self) -> Option<ModuleDefId> {
|
||||||
self.types
|
self.types
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_values(self) -> Option<ModuleDef> {
|
pub fn take_values(self) -> Option<ModuleDefId> {
|
||||||
self.values
|
self.values
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_macros(&self) -> Option<MacroDef> {
|
pub fn get_macros(&self) -> Option<MacroDefId> {
|
||||||
self.macros
|
self.macros
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use ra_syntax::{
|
||||||
ast::{self, AttrsOwner, NameOwner},
|
ast::{self, AttrsOwner, NameOwner},
|
||||||
AstNode, AstPtr, SourceFile,
|
AstNode, AstPtr, SourceFile,
|
||||||
};
|
};
|
||||||
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source};
|
use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source};
|
||||||
|
|
||||||
|
@ -297,8 +298,7 @@ impl RawItemsCollector {
|
||||||
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// FIXME: restore this mark once we complete hir splitting
|
tested_by!(name_res_works_for_broken_modules);
|
||||||
// tested_by!(name_res_works_for_broken_modules);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
||||||
|
|
|
@ -1,35 +1,31 @@
|
||||||
mod macros;
|
|
||||||
mod globs;
|
mod globs;
|
||||||
mod incremental;
|
mod incremental;
|
||||||
mod primitives;
|
mod macros;
|
||||||
mod mod_resolution;
|
mod mod_resolution;
|
||||||
|
mod primitives;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use ra_db::SourceDatabase;
|
use ra_db::{fixture::WithFixture, SourceDatabase};
|
||||||
use test_utils::covers;
|
use test_utils::covers;
|
||||||
|
|
||||||
use crate::{
|
use crate::{db::DefDatabase2, nameres::*, test_db::TestDB, CrateModuleId};
|
||||||
mock::{CrateGraphFixture, MockDatabase},
|
|
||||||
Crate,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
fn def_map(fixtute: &str) -> String {
|
||||||
|
let dm = compute_crate_def_map(fixtute);
|
||||||
fn compute_crate_def_map(fixture: &str, graph: Option<CrateGraphFixture>) -> Arc<CrateDefMap> {
|
render_crate_def_map(&dm)
|
||||||
let mut db = MockDatabase::with_files(fixture);
|
|
||||||
if let Some(graph) = graph {
|
|
||||||
db.set_crate_graph_from_fixture(graph);
|
|
||||||
}
|
}
|
||||||
let crate_id = db.crate_graph().iter().next().unwrap();
|
|
||||||
let krate = Crate { crate_id };
|
fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
|
||||||
|
let db = TestDB::with_files(fixture);
|
||||||
|
let krate = db.crate_graph().iter().next().unwrap();
|
||||||
db.crate_def_map(krate)
|
db.crate_def_map(krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_crate_def_map(map: &CrateDefMap) -> String {
|
fn render_crate_def_map(map: &CrateDefMap) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
go(&mut buf, map, "\ncrate", map.root);
|
go(&mut buf, map, "\ncrate", map.root());
|
||||||
return buf.trim().to_string();
|
return buf.trim().to_string();
|
||||||
|
|
||||||
fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) {
|
fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) {
|
||||||
|
@ -70,16 +66,6 @@ fn render_crate_def_map(map: &CrateDefMap) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn def_map(fixtute: &str) -> String {
|
|
||||||
let dm = compute_crate_def_map(fixtute, None);
|
|
||||||
render_crate_def_map(&dm)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn def_map_with_crate_graph(fixture: &str, graph: CrateGraphFixture) -> String {
|
|
||||||
let dm = compute_crate_def_map(fixture, Some(graph));
|
|
||||||
render_crate_def_map(&dm)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn crate_def_map_smoke_test() {
|
fn crate_def_map_smoke_test() {
|
||||||
let map = def_map(
|
let map = def_map(
|
||||||
|
@ -234,12 +220,12 @@ fn re_exports() {
|
||||||
#[test]
|
#[test]
|
||||||
fn std_prelude() {
|
fn std_prelude() {
|
||||||
covers!(std_prelude);
|
covers!(std_prelude);
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:test_crate
|
||||||
use Foo::*;
|
use Foo::*;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:test_crate
|
||||||
mod prelude;
|
mod prelude;
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
@ -247,10 +233,6 @@ fn std_prelude() {
|
||||||
//- /prelude.rs
|
//- /prelude.rs
|
||||||
pub enum Foo { Bar, Baz };
|
pub enum Foo { Bar, Baz };
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["test_crate"]),
|
|
||||||
"test_crate": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -279,9 +261,9 @@ fn can_import_enum_variant() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn edition_2015_imports() {
|
fn edition_2015_imports() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:other_crate edition:2015
|
||||||
mod foo;
|
mod foo;
|
||||||
mod bar;
|
mod bar;
|
||||||
|
|
||||||
|
@ -292,13 +274,9 @@ fn edition_2015_imports() {
|
||||||
use bar::Bar;
|
use bar::Bar;
|
||||||
use other_crate::FromLib;
|
use other_crate::FromLib;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:other_crate edition:2018
|
||||||
struct FromLib;
|
struct FromLib;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", "2015", ["other_crate"]),
|
|
||||||
"other_crate": ("/lib.rs", "2018", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -343,18 +321,14 @@ fn item_map_using_self() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn item_map_across_crates() {
|
fn item_map_across_crates() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:test_crate
|
||||||
use test_crate::Baz;
|
use test_crate::Baz;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:test_crate
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["test_crate"]),
|
|
||||||
"test_crate": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -365,9 +339,9 @@ fn item_map_across_crates() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extern_crate_rename() {
|
fn extern_crate_rename() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:alloc
|
||||||
extern crate alloc as alloc_crate;
|
extern crate alloc as alloc_crate;
|
||||||
|
|
||||||
mod alloc;
|
mod alloc;
|
||||||
|
@ -376,13 +350,9 @@ fn extern_crate_rename() {
|
||||||
//- /sync.rs
|
//- /sync.rs
|
||||||
use alloc_crate::Arc;
|
use alloc_crate::Arc;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:alloc
|
||||||
struct Arc;
|
struct Arc;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["alloc"]),
|
|
||||||
"alloc": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -397,9 +367,9 @@ fn extern_crate_rename() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extern_crate_rename_2015_edition() {
|
fn extern_crate_rename_2015_edition() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:alloc edition:2015
|
||||||
extern crate alloc as alloc_crate;
|
extern crate alloc as alloc_crate;
|
||||||
|
|
||||||
mod alloc;
|
mod alloc;
|
||||||
|
@ -408,13 +378,9 @@ fn extern_crate_rename_2015_edition() {
|
||||||
//- /sync.rs
|
//- /sync.rs
|
||||||
use alloc_crate::Arc;
|
use alloc_crate::Arc;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:alloc
|
||||||
struct Arc;
|
struct Arc;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", "2015", ["alloc"]),
|
|
||||||
"alloc": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map,
|
assert_snapshot!(map,
|
||||||
|
@ -431,24 +397,21 @@ fn extern_crate_rename_2015_edition() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_across_source_roots() {
|
fn import_across_source_roots() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /lib.rs
|
//- /main.rs crate:main deps:test_crate
|
||||||
|
use test_crate::a::b::C;
|
||||||
|
|
||||||
|
//- root /test_crate/
|
||||||
|
|
||||||
|
//- /test_crate/lib.rs crate:test_crate
|
||||||
pub mod a {
|
pub mod a {
|
||||||
pub mod b {
|
pub mod b {
|
||||||
pub struct C;
|
pub struct C;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- root /main/
|
|
||||||
|
|
||||||
//- /main/main.rs
|
|
||||||
use test_crate::a::b::C;
|
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main/main.rs", ["test_crate"]),
|
|
||||||
"test_crate": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -459,12 +422,12 @@ fn import_across_source_roots() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reexport_across_crates() {
|
fn reexport_across_crates() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:test_crate
|
||||||
use test_crate::Baz;
|
use test_crate::Baz;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:test_crate
|
||||||
pub use foo::Baz;
|
pub use foo::Baz;
|
||||||
|
|
||||||
mod foo;
|
mod foo;
|
||||||
|
@ -472,10 +435,6 @@ fn reexport_across_crates() {
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["test_crate"]),
|
|
||||||
"test_crate": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -486,19 +445,15 @@ fn reexport_across_crates() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn values_dont_shadow_extern_crates() {
|
fn values_dont_shadow_extern_crates() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
fn foo() {}
|
fn foo() {}
|
||||||
use foo::Bar;
|
use foo::Bar;
|
||||||
|
|
||||||
//- /foo/lib.rs
|
//- /foo/lib.rs crate:foo
|
||||||
pub struct Bar;
|
pub struct Bar;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/foo/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -510,11 +465,12 @@ fn values_dont_shadow_extern_crates() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cfg_not_test() {
|
fn cfg_not_test() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
use {Foo, Bar, Baz};
|
use {Foo, Bar, Baz};
|
||||||
//- /lib.rs
|
|
||||||
|
//- /lib.rs crate:std
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
pub use self::prelude::*;
|
pub use self::prelude::*;
|
||||||
mod prelude {
|
mod prelude {
|
||||||
|
@ -526,10 +482,6 @@ fn cfg_not_test() {
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -542,11 +494,12 @@ fn cfg_not_test() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cfg_test() {
|
fn cfg_test() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
use {Foo, Bar, Baz};
|
use {Foo, Bar, Baz};
|
||||||
//- /lib.rs
|
|
||||||
|
//- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
pub use self::prelude::*;
|
pub use self::prelude::*;
|
||||||
mod prelude {
|
mod prelude {
|
||||||
|
@ -558,15 +511,6 @@ fn cfg_test() {
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/lib.rs", [], cfg = {
|
|
||||||
"test",
|
|
||||||
"feature" = "foo",
|
|
||||||
"feature" = "bar",
|
|
||||||
"opt" = "42",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
|
@ -76,18 +76,14 @@ fn glob_2() {
|
||||||
#[test]
|
#[test]
|
||||||
fn glob_across_crates() {
|
fn glob_across_crates() {
|
||||||
covers!(glob_across_crates);
|
covers!(glob_across_crates);
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:test_crate
|
||||||
use test_crate::*;
|
use test_crate::*;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:test_crate
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["test_crate"]),
|
|
||||||
"test_crate": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
|
@ -1,13 +1,12 @@
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_db::{SourceDatabase, SourceDatabaseExt};
|
use ra_db::{SourceDatabase, SourceDatabaseExt};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) {
|
fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) {
|
||||||
let (mut db, pos) = MockDatabase::with_position(initial);
|
let (mut db, pos) = TestDB::with_position(initial);
|
||||||
let crate_id = db.crate_graph().iter().next().unwrap();
|
let krate = db.crate_graph().iter().next().unwrap();
|
||||||
let krate = Crate { crate_id };
|
|
||||||
{
|
{
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
db.crate_def_map(krate);
|
db.crate_def_map(krate);
|
||||||
|
@ -92,7 +91,7 @@ fn adding_inner_items_should_not_invalidate_def_map() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (mut db, pos) = TestDB::with_position(
|
||||||
"
|
"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
|
@ -112,15 +111,12 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
m!(X);
|
m!(X);
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
let krate = db.crate_graph().iter().next().unwrap();
|
||||||
{
|
{
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
let src = crate::Source {
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
file_id: pos.file_id.into(),
|
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||||
ast: crate::ModuleSource::new(&db, Some(pos.file_id), None),
|
assert_eq!(module_data.scope.items.len(), 1);
|
||||||
};
|
|
||||||
let module = crate::Module::from_definition(&db, src).unwrap();
|
|
||||||
let decls = module.declarations(&db);
|
|
||||||
assert_eq!(decls.len(), 18);
|
|
||||||
});
|
});
|
||||||
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||||
}
|
}
|
||||||
|
@ -128,13 +124,9 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
|
|
||||||
{
|
{
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
let src = crate::Source {
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
file_id: pos.file_id.into(),
|
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||||
ast: crate::ModuleSource::new(&db, Some(pos.file_id), None),
|
assert_eq!(module_data.scope.items.len(), 1);
|
||||||
};
|
|
||||||
let module = crate::Module::from_definition(&db, src).unwrap();
|
|
||||||
let decls = module.declarations(&db);
|
|
||||||
assert_eq!(decls.len(), 18);
|
|
||||||
});
|
});
|
||||||
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||||
}
|
}
|
|
@ -71,16 +71,16 @@ fn macro_rules_can_define_modules() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_rules_from_other_crates_are_visible() {
|
fn macro_rules_from_other_crates_are_visible() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
foo::structs!(Foo, Bar)
|
foo::structs!(Foo, Bar)
|
||||||
mod bar;
|
mod bar;
|
||||||
|
|
||||||
//- /bar.rs
|
//- /bar.rs
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! structs {
|
macro_rules! structs {
|
||||||
($($i:ident),*) => {
|
($($i:ident),*) => {
|
||||||
|
@ -88,10 +88,6 @@ fn macro_rules_from_other_crates_are_visible() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -108,16 +104,16 @@ fn macro_rules_from_other_crates_are_visible() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_rules_export_with_local_inner_macros_are_visible() {
|
fn macro_rules_export_with_local_inner_macros_are_visible() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
foo::structs!(Foo, Bar)
|
foo::structs!(Foo, Bar)
|
||||||
mod bar;
|
mod bar;
|
||||||
|
|
||||||
//- /bar.rs
|
//- /bar.rs
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
#[macro_export(local_inner_macros)]
|
#[macro_export(local_inner_macros)]
|
||||||
macro_rules! structs {
|
macro_rules! structs {
|
||||||
($($i:ident),*) => {
|
($($i:ident),*) => {
|
||||||
|
@ -125,10 +121,6 @@ fn macro_rules_export_with_local_inner_macros_are_visible() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -145,9 +137,9 @@ fn macro_rules_export_with_local_inner_macros_are_visible() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
macro_rules! baz {
|
macro_rules! baz {
|
||||||
() => {
|
() => {
|
||||||
use foo::bar;
|
use foo::bar;
|
||||||
|
@ -158,7 +150,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
bar!();
|
bar!();
|
||||||
baz!();
|
baz!();
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
() => {
|
() => {
|
||||||
|
@ -172,10 +164,6 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -188,9 +176,9 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
||||||
covers!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
covers!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
structs!(Foo);
|
structs!(Foo);
|
||||||
structs_priv!(Bar);
|
structs_priv!(Bar);
|
||||||
structs_not_exported!(MacroNotResolved1);
|
structs_not_exported!(MacroNotResolved1);
|
||||||
|
@ -205,7 +193,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
||||||
structs!(Baz);
|
structs!(Baz);
|
||||||
crate::structs!(MacroNotResolved3);
|
crate::structs!(MacroNotResolved3);
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! structs {
|
macro_rules! structs {
|
||||||
($i:ident) => { struct $i; }
|
($i:ident) => { struct $i; }
|
||||||
|
@ -222,10 +210,6 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -242,9 +226,9 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
||||||
#[test]
|
#[test]
|
||||||
fn prelude_is_macro_use() {
|
fn prelude_is_macro_use() {
|
||||||
covers!(prelude_is_macro_use);
|
covers!(prelude_is_macro_use);
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
structs!(Foo);
|
structs!(Foo);
|
||||||
structs_priv!(Bar);
|
structs_priv!(Bar);
|
||||||
structs_outside!(Out);
|
structs_outside!(Out);
|
||||||
|
@ -256,7 +240,7 @@ fn prelude_is_macro_use() {
|
||||||
structs!(Baz);
|
structs!(Baz);
|
||||||
crate::structs!(MacroNotResolved3);
|
crate::structs!(MacroNotResolved3);
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
use self::prelude::*;
|
use self::prelude::*;
|
||||||
|
|
||||||
|
@ -279,10 +263,6 @@ fn prelude_is_macro_use() {
|
||||||
($i:ident) => { struct $i; }
|
($i:ident) => { struct $i; }
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -447,16 +427,16 @@ fn type_value_macro_live_in_different_scopes() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn macro_use_can_be_aliased() {
|
fn macro_use_can_be_aliased() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate foo;
|
extern crate foo;
|
||||||
|
|
||||||
foo!(Direct);
|
foo!(Direct);
|
||||||
bar!(Alias);
|
bar!(Alias);
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
use crate::foo as bar;
|
use crate::foo as bar;
|
||||||
|
|
||||||
mod m {
|
mod m {
|
||||||
|
@ -466,10 +446,6 @@ fn macro_use_can_be_aliased() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -533,9 +509,9 @@ fn path_qualified_macros() {
|
||||||
fn macro_dollar_crate_is_correct_in_item() {
|
fn macro_dollar_crate_is_correct_in_item() {
|
||||||
covers!(macro_dollar_crate_self);
|
covers!(macro_dollar_crate_self);
|
||||||
covers!(macro_dollar_crate_other);
|
covers!(macro_dollar_crate_other);
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:foo
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate foo;
|
extern crate foo;
|
||||||
|
|
||||||
|
@ -554,7 +530,7 @@ fn macro_dollar_crate_is_correct_in_item() {
|
||||||
not_current1!();
|
not_current1!();
|
||||||
foo::not_current2!();
|
foo::not_current2!();
|
||||||
|
|
||||||
//- /lib.rs
|
//- /lib.rs crate:foo
|
||||||
mod m {
|
mod m {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! not_current1 {
|
macro_rules! not_current1 {
|
||||||
|
@ -574,10 +550,6 @@ fn macro_dollar_crate_is_correct_in_item() {
|
||||||
struct Bar;
|
struct Bar;
|
||||||
struct Baz;
|
struct Baz;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["foo"]),
|
|
||||||
"foo": ("/lib.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
||||||
|
@ -596,12 +568,12 @@ fn macro_dollar_crate_is_correct_in_item() {
|
||||||
fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
||||||
covers!(macro_dollar_crate_other);
|
covers!(macro_dollar_crate_other);
|
||||||
// From std
|
// From std
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs crate:main deps:std
|
||||||
foo!();
|
foo!();
|
||||||
|
|
||||||
//- /std.rs
|
//- /std.rs crate:std deps:core
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
use self::prelude::*;
|
use self::prelude::*;
|
||||||
|
|
||||||
|
@ -612,7 +584,7 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod std_macros;
|
mod std_macros;
|
||||||
|
|
||||||
//- /core.rs
|
//- /core.rs crate:core
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
() => {
|
() => {
|
||||||
|
@ -622,11 +594,6 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
||||||
|
|
||||||
pub struct bar;
|
pub struct bar;
|
||||||
"#,
|
"#,
|
||||||
crate_graph! {
|
|
||||||
"main": ("/main.rs", ["std"]),
|
|
||||||
"std": ("/std.rs", ["core"]),
|
|
||||||
"core": ("/core.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
⋮crate
|
|
@ -2,7 +2,7 @@ use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn name_res_works_for_broken_modules() {
|
fn name_res_works_for_broken_modules() {
|
||||||
// covers!(name_res_works_for_broken_modules);
|
covers!(name_res_works_for_broken_modules);
|
||||||
let map = def_map(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
|
@ -54,18 +54,15 @@ fn nested_module_resolution() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn module_resolution_works_for_non_standard_filenames() {
|
fn module_resolution_works_for_non_standard_filenames() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map(
|
||||||
"
|
"
|
||||||
//- /my_library.rs
|
//- /my_library.rs crate:my_library
|
||||||
mod foo;
|
mod foo;
|
||||||
use self::foo::Bar;
|
use self::foo::Bar;
|
||||||
|
|
||||||
//- /foo/mod.rs
|
//- /foo/mod.rs
|
||||||
pub struct Bar;
|
pub struct Bar;
|
||||||
",
|
",
|
||||||
crate_graph! {
|
|
||||||
"my_library": ("/my_library.rs", []),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
|
@ -650,7 +647,7 @@ fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unresolved_module_diagnostics() {
|
fn unresolved_module_diagnostics() {
|
||||||
let diagnostics = MockDatabase::with_files(
|
let db = TestDB::with_files(
|
||||||
r"
|
r"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
mod foo;
|
mod foo;
|
||||||
|
@ -658,11 +655,37 @@ fn unresolved_module_diagnostics() {
|
||||||
mod baz {}
|
mod baz {}
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
",
|
",
|
||||||
)
|
);
|
||||||
.diagnostics();
|
let krate = db.crate_graph().iter().next().unwrap();
|
||||||
|
|
||||||
assert_snapshot!(diagnostics, @r###"
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
"mod bar;": unresolved module
|
|
||||||
|
insta::assert_debug_snapshot!(
|
||||||
|
crate_def_map.diagnostics,
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
UnresolvedModule {
|
||||||
|
module: CrateModuleId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
declaration: AstId {
|
||||||
|
file_id: HirFileId(
|
||||||
|
FileId(
|
||||||
|
FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
file_ast_id: FileAstId {
|
||||||
|
raw: ErasedFileAstId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
_ty: PhantomData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
candidate: "bar.rs",
|
||||||
|
},
|
||||||
|
]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
75
crates/ra_hir_def/src/test_db.rs
Normal file
75
crates/ra_hir_def/src/test_db.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
//! Database used for testing `hir_def`.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
panic,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
|
||||||
|
|
||||||
|
#[salsa::database(
|
||||||
|
ra_db::SourceDatabaseExtStorage,
|
||||||
|
ra_db::SourceDatabaseStorage,
|
||||||
|
hir_expand::db::AstDatabaseStorage,
|
||||||
|
crate::db::InternDatabaseStorage,
|
||||||
|
crate::db::DefDatabase2Storage
|
||||||
|
)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TestDB {
|
||||||
|
runtime: salsa::Runtime<TestDB>,
|
||||||
|
events: Mutex<Option<Vec<salsa::Event<TestDB>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl salsa::Database for TestDB {
|
||||||
|
fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
|
||||||
|
&self.runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
|
||||||
|
let mut events = self.events.lock().unwrap();
|
||||||
|
if let Some(events) = &mut *events {
|
||||||
|
events.push(event());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl panic::RefUnwindSafe for TestDB {}
|
||||||
|
|
||||||
|
impl FileLoader for TestDB {
|
||||||
|
fn file_text(&self, file_id: FileId) -> Arc<String> {
|
||||||
|
FileLoaderDelegate(self).file_text(file_id)
|
||||||
|
}
|
||||||
|
fn resolve_relative_path(
|
||||||
|
&self,
|
||||||
|
anchor: FileId,
|
||||||
|
relative_path: &RelativePath,
|
||||||
|
) -> Option<FileId> {
|
||||||
|
FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
|
||||||
|
}
|
||||||
|
fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
|
||||||
|
FileLoaderDelegate(self).relevant_crates(file_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestDB {
|
||||||
|
pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> {
|
||||||
|
*self.events.lock().unwrap() = Some(Vec::new());
|
||||||
|
f();
|
||||||
|
self.events.lock().unwrap().take().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
|
||||||
|
let events = self.log(f);
|
||||||
|
events
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|e| match e.kind {
|
||||||
|
// This pretty horrible, but `Debug` is the only way to inspect
|
||||||
|
// QueryDescriptor at the moment.
|
||||||
|
salsa::EventKind::WillExecute { database_key } => {
|
||||||
|
Some(format!("{:?}", database_key))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
85
crates/ra_hir_expand/src/diagnostics.rs
Normal file
85
crates/ra_hir_expand/src/diagnostics.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
//! Semantic errors and warnings.
|
||||||
|
//!
|
||||||
|
//! The `Diagnostic` trait defines a trait object which can represent any
|
||||||
|
//! diagnostic.
|
||||||
|
//!
|
||||||
|
//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
|
||||||
|
//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
|
||||||
|
//! Diagnostic` or to any concrete diagnostic (downcasting is sued internally).
|
||||||
|
//!
|
||||||
|
//! Because diagnostics store file offsets, it's a bad idea to store them
|
||||||
|
//! directly in salsa. For this reason, every hir subsytem defines it's own
|
||||||
|
//! strongly-typed closed set of diagnostics which use hir ids internally, are
|
||||||
|
//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a
|
||||||
|
//! subsystem provides a separate, non-query-based API which can walk all stored
|
||||||
|
//! values and transform them into instances of `Diagnostic`.
|
||||||
|
|
||||||
|
use std::{any::Any, fmt};
|
||||||
|
|
||||||
|
use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange};
|
||||||
|
|
||||||
|
use crate::{db::AstDatabase, Source};
|
||||||
|
|
||||||
|
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
||||||
|
fn message(&self) -> String;
|
||||||
|
fn source(&self) -> Source<SyntaxNodePtr>;
|
||||||
|
fn highlight_range(&self) -> TextRange {
|
||||||
|
self.source().ast.range()
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AstDiagnostic {
|
||||||
|
type AST;
|
||||||
|
fn ast(&self, db: &impl AstDatabase) -> Self::AST;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl dyn Diagnostic {
|
||||||
|
pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
|
||||||
|
let node = db.parse_or_expand(self.source().file_id).unwrap();
|
||||||
|
self.source().ast.to_node(&node)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
|
||||||
|
self.as_any().downcast_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiagnosticSink<'a> {
|
||||||
|
callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
|
||||||
|
default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DiagnosticSink<'a> {
|
||||||
|
/// FIXME: split `new` and `on` into a separate builder type
|
||||||
|
pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> {
|
||||||
|
DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> {
|
||||||
|
let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
|
||||||
|
Some(d) => {
|
||||||
|
cb(d);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
None => Err(()),
|
||||||
|
};
|
||||||
|
self.callbacks.push(Box::new(cb));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, d: impl Diagnostic) {
|
||||||
|
let d: &dyn Diagnostic = &d;
|
||||||
|
self._push(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _push(&mut self, d: &dyn Diagnostic) {
|
||||||
|
for cb in self.callbacks.iter_mut() {
|
||||||
|
match cb(d) {
|
||||||
|
Ok(()) => return,
|
||||||
|
Err(()) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(self.default_callback)(d)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,15 @@ pub mod ast_id_map;
|
||||||
pub mod either;
|
pub mod either;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
pub mod hygiene;
|
pub mod hygiene;
|
||||||
|
pub mod diagnostics;
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use ra_db::{salsa, CrateId, FileId};
|
use ra_db::{salsa, CrateId, FileId};
|
||||||
use ra_syntax::ast::{self, AstNode};
|
use ra_syntax::{
|
||||||
|
ast::{self, AstNode},
|
||||||
|
SyntaxNode,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::ast_id_map::FileAstId;
|
use crate::ast_id_map::FileAstId;
|
||||||
|
|
||||||
|
@ -151,3 +155,18 @@ impl<N: AstNode> AstId<N> {
|
||||||
db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root)
|
db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct Source<T> {
|
||||||
|
pub file_id: HirFileId,
|
||||||
|
pub ast: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Source<T> {
|
||||||
|
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||||
|
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||||
|
}
|
||||||
|
pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode {
|
||||||
|
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ format-buf = "1.0.0"
|
||||||
itertools = "0.8.0"
|
itertools = "0.8.0"
|
||||||
join_to_string = "0.1.3"
|
join_to_string = "0.1.3"
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
relative-path = "1.0.0"
|
|
||||||
rayon = "1.0.2"
|
rayon = "1.0.2"
|
||||||
fst = { version = "0.3.1", default-features = false }
|
fst = { version = "0.3.1", default-features = false }
|
||||||
rustc-hash = "1.0"
|
rustc-hash = "1.0"
|
||||||
|
|
|
@ -4,13 +4,13 @@ use std::{fmt, sync::Arc, time};
|
||||||
|
|
||||||
use ra_db::{
|
use ra_db::{
|
||||||
salsa::{Database, Durability, SweepStrategy},
|
salsa::{Database, Durability, SweepStrategy},
|
||||||
CrateGraph, CrateId, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
|
CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot,
|
||||||
|
SourceRootId,
|
||||||
};
|
};
|
||||||
use ra_prof::{memory_usage, profile, Bytes};
|
use ra_prof::{memory_usage, profile, Bytes};
|
||||||
use ra_syntax::SourceFile;
|
use ra_syntax::SourceFile;
|
||||||
#[cfg(not(feature = "wasm"))]
|
#[cfg(not(feature = "wasm"))]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use relative_path::RelativePathBuf;
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -18,15 +18,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
match def {
|
match def {
|
||||||
hir::ModuleDef::Module(module) => {
|
hir::ModuleDef::Module(module) => {
|
||||||
let module_scope = module.scope(ctx.db);
|
let module_scope = module.scope(ctx.db);
|
||||||
for (name, res) in module_scope.entries() {
|
for (name, def, import) in module_scope {
|
||||||
if let Some(hir::ModuleDef::BuiltinType(..)) = res.def.take_types() {
|
if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def {
|
||||||
if ctx.use_item_syntax.is_some() {
|
if ctx.use_item_syntax.is_some() {
|
||||||
tested_by!(dont_complete_primitive_in_use);
|
tested_by!(dont_complete_primitive_in_use);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if Some(module) == ctx.module {
|
if Some(module) == ctx.module {
|
||||||
if let Some(import) = res.import {
|
if let Some(import) = import {
|
||||||
if let Either::A(use_tree) = module.import_source(ctx.db, import) {
|
if let Either::A(use_tree) = module.import_source(ctx.db, import) {
|
||||||
if use_tree.syntax().text_range().contains_inclusive(ctx.offset) {
|
if use_tree.syntax().text_range().contains_inclusive(ctx.offset) {
|
||||||
// for `use self::foo<|>`, don't suggest `foo` as a completion
|
// for `use self::foo<|>`, don't suggest `foo` as a completion
|
||||||
|
@ -36,7 +36,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acc.add_resolution(ctx, name.to_string(), &res.def.into());
|
acc.add_resolution(ctx, name.to_string(), &def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
|
hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
|
||||||
|
|
|
@ -31,6 +31,34 @@ mod tests {
|
||||||
do_completion(code, CompletionKind::Reference)
|
do_completion(code, CompletionKind::Reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_record_literal_deprecated_field() {
|
||||||
|
let completions = complete(
|
||||||
|
r"
|
||||||
|
struct A {
|
||||||
|
#[deprecated]
|
||||||
|
the_field: u32,
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
A { the<|> }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_debug_snapshot!(completions, @r###"
|
||||||
|
⋮[
|
||||||
|
⋮ CompletionItem {
|
||||||
|
⋮ label: "the_field",
|
||||||
|
⋮ source_range: [142; 145),
|
||||||
|
⋮ delete: [142; 145),
|
||||||
|
⋮ insert: "the_field",
|
||||||
|
⋮ kind: Field,
|
||||||
|
⋮ detail: "u32",
|
||||||
|
⋮ deprecated: true,
|
||||||
|
⋮ },
|
||||||
|
⋮]
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_record_literal_field() {
|
fn test_record_literal_field() {
|
||||||
let completions = complete(
|
let completions = complete(
|
||||||
|
|
|
@ -44,6 +44,9 @@ pub struct CompletionItem {
|
||||||
/// Additional info to show in the UI pop up.
|
/// Additional info to show in the UI pop up.
|
||||||
detail: Option<String>,
|
detail: Option<String>,
|
||||||
documentation: Option<Documentation>,
|
documentation: Option<Documentation>,
|
||||||
|
|
||||||
|
/// Whether this item is marked as deprecated
|
||||||
|
deprecated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use custom debug for CompletionItem to make `insta`'s diffs more readable.
|
// We use custom debug for CompletionItem to make `insta`'s diffs more readable.
|
||||||
|
@ -70,6 +73,9 @@ impl fmt::Debug for CompletionItem {
|
||||||
if let Some(documentation) = self.documentation() {
|
if let Some(documentation) = self.documentation() {
|
||||||
s.field("documentation", &documentation);
|
s.field("documentation", &documentation);
|
||||||
}
|
}
|
||||||
|
if self.deprecated {
|
||||||
|
s.field("deprecated", &true);
|
||||||
|
}
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,6 +138,7 @@ impl CompletionItem {
|
||||||
lookup: None,
|
lookup: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
text_edit: None,
|
text_edit: None,
|
||||||
|
deprecated: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// What user sees in pop-up in the UI.
|
/// What user sees in pop-up in the UI.
|
||||||
|
@ -166,6 +173,10 @@ impl CompletionItem {
|
||||||
pub fn kind(&self) -> Option<CompletionItemKind> {
|
pub fn kind(&self) -> Option<CompletionItemKind> {
|
||||||
self.kind
|
self.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deprecated(&self) -> bool {
|
||||||
|
self.deprecated
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper to make `CompletionItem`s.
|
/// A helper to make `CompletionItem`s.
|
||||||
|
@ -181,6 +192,7 @@ pub(crate) struct Builder {
|
||||||
lookup: Option<String>,
|
lookup: Option<String>,
|
||||||
kind: Option<CompletionItemKind>,
|
kind: Option<CompletionItemKind>,
|
||||||
text_edit: Option<TextEdit>,
|
text_edit: Option<TextEdit>,
|
||||||
|
deprecated: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
|
@ -208,6 +220,7 @@ impl Builder {
|
||||||
lookup: self.lookup,
|
lookup: self.lookup,
|
||||||
kind: self.kind,
|
kind: self.kind,
|
||||||
completion_kind: self.completion_kind,
|
completion_kind: self.completion_kind,
|
||||||
|
deprecated: self.deprecated.unwrap_or(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
|
pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
|
||||||
|
@ -254,6 +267,10 @@ impl Builder {
|
||||||
self.documentation = docs.map(Into::into);
|
self.documentation = docs.map(Into::into);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
|
||||||
|
self.deprecated = Some(deprecated);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<CompletionItem> for Builder {
|
impl<'a> Into<CompletionItem> for Builder {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
|
use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
|
||||||
use join_to_string::join;
|
use join_to_string::join;
|
||||||
use ra_syntax::ast::NameOwner;
|
use ra_syntax::ast::{AttrsOwner, NameOwner};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::completion::{
|
use crate::completion::{
|
||||||
|
@ -18,6 +18,11 @@ impl Completions {
|
||||||
field: hir::StructField,
|
field: hir::StructField,
|
||||||
substs: &hir::Substs,
|
substs: &hir::Substs,
|
||||||
) {
|
) {
|
||||||
|
let ast_node = field.source(ctx.db).ast;
|
||||||
|
let is_deprecated = match ast_node {
|
||||||
|
hir::FieldSource::Named(m) => is_deprecated(m),
|
||||||
|
hir::FieldSource::Pos(m) => is_deprecated(m),
|
||||||
|
};
|
||||||
CompletionItem::new(
|
CompletionItem::new(
|
||||||
CompletionKind::Reference,
|
CompletionKind::Reference,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
|
@ -26,6 +31,7 @@ impl Completions {
|
||||||
.kind(CompletionItemKind::Field)
|
.kind(CompletionItemKind::Field)
|
||||||
.detail(field.ty(ctx.db).subst(substs).display(ctx.db).to_string())
|
.detail(field.ty(ctx.db).subst(substs).display(ctx.db).to_string())
|
||||||
.set_documentation(field.docs(ctx.db))
|
.set_documentation(field.docs(ctx.db))
|
||||||
|
.set_deprecated(is_deprecated)
|
||||||
.add_to(self);
|
.add_to(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +185,7 @@ impl Completions {
|
||||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), ¯o_declaration)
|
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), ¯o_declaration)
|
||||||
.kind(CompletionItemKind::Macro)
|
.kind(CompletionItemKind::Macro)
|
||||||
.set_documentation(docs.clone())
|
.set_documentation(docs.clone())
|
||||||
|
.set_deprecated(is_deprecated(ast_node))
|
||||||
.detail(detail);
|
.detail(detail);
|
||||||
|
|
||||||
builder = if ctx.use_item_syntax.is_some() {
|
builder = if ctx.use_item_syntax.is_some() {
|
||||||
|
@ -211,6 +218,7 @@ impl Completions {
|
||||||
CompletionItemKind::Function
|
CompletionItemKind::Function
|
||||||
})
|
})
|
||||||
.set_documentation(func.docs(ctx.db))
|
.set_documentation(func.docs(ctx.db))
|
||||||
|
.set_deprecated(is_deprecated(ast_node))
|
||||||
.detail(detail);
|
.detail(detail);
|
||||||
|
|
||||||
// Add `<>` for generic types
|
// Add `<>` for generic types
|
||||||
|
@ -242,6 +250,7 @@ impl Completions {
|
||||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
|
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
|
||||||
.kind(CompletionItemKind::Const)
|
.kind(CompletionItemKind::Const)
|
||||||
.set_documentation(constant.docs(ctx.db))
|
.set_documentation(constant.docs(ctx.db))
|
||||||
|
.set_deprecated(is_deprecated(ast_node))
|
||||||
.detail(detail)
|
.detail(detail)
|
||||||
.add_to(self);
|
.add_to(self);
|
||||||
}
|
}
|
||||||
|
@ -257,11 +266,13 @@ impl Completions {
|
||||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
|
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
|
||||||
.kind(CompletionItemKind::TypeAlias)
|
.kind(CompletionItemKind::TypeAlias)
|
||||||
.set_documentation(type_alias.docs(ctx.db))
|
.set_documentation(type_alias.docs(ctx.db))
|
||||||
|
.set_deprecated(is_deprecated(type_def))
|
||||||
.detail(detail)
|
.detail(detail)
|
||||||
.add_to(self);
|
.add_to(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
|
pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
|
||||||
|
let is_deprecated = is_deprecated(variant.source(ctx.db).ast);
|
||||||
let name = match variant.name(ctx.db) {
|
let name = match variant.name(ctx.db) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return,
|
None => return,
|
||||||
|
@ -274,11 +285,16 @@ impl Completions {
|
||||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
|
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
|
||||||
.kind(CompletionItemKind::EnumVariant)
|
.kind(CompletionItemKind::EnumVariant)
|
||||||
.set_documentation(variant.docs(ctx.db))
|
.set_documentation(variant.docs(ctx.db))
|
||||||
|
.set_deprecated(is_deprecated)
|
||||||
.detail(detail)
|
.detail(detail)
|
||||||
.add_to(self);
|
.add_to(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_deprecated(node: impl AttrsOwner) -> bool {
|
||||||
|
node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated")
|
||||||
|
}
|
||||||
|
|
||||||
fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
|
fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
|
||||||
let subst = db.generic_defaults(def);
|
let subst = db.generic_defaults(def);
|
||||||
subst.iter().any(|ty| ty == &Ty::Unknown)
|
subst.iter().any(|ty| ty == &Ty::Unknown)
|
||||||
|
@ -295,6 +311,56 @@ mod tests {
|
||||||
do_completion(code, CompletionKind::Reference)
|
do_completion(code, CompletionKind::Reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sets_deprecated_flag_in_completion_items() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
r#"
|
||||||
|
#[deprecated]
|
||||||
|
fn something_deprecated() {}
|
||||||
|
|
||||||
|
#[deprecated(since = "1.0.0")]
|
||||||
|
fn something_else_deprecated() {}
|
||||||
|
|
||||||
|
fn main() { som<|> }
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "main()",
|
||||||
|
source_range: [203; 206),
|
||||||
|
delete: [203; 206),
|
||||||
|
insert: "main()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "main",
|
||||||
|
detail: "fn main()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "something_deprecated()",
|
||||||
|
source_range: [203; 206),
|
||||||
|
delete: [203; 206),
|
||||||
|
insert: "something_deprecated()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "something_deprecated",
|
||||||
|
detail: "fn something_deprecated()",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "something_else_deprecated()",
|
||||||
|
source_range: [203; 206),
|
||||||
|
delete: [203; 206),
|
||||||
|
insert: "something_else_deprecated()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "something_else_deprecated",
|
||||||
|
detail: "fn something_else_deprecated()",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inserts_parens_for_function_calls() {
|
fn inserts_parens_for_function_calls() {
|
||||||
covers!(inserts_parens_for_function_calls);
|
covers!(inserts_parens_for_function_calls);
|
||||||
|
|
|
@ -4,10 +4,9 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use ra_db::{
|
use ra_db::{
|
||||||
salsa::{self, Database, Durability},
|
salsa::{self, Database, Durability},
|
||||||
Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
|
Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath,
|
||||||
SourceDatabaseExt, SourceRootId,
|
SourceDatabase, SourceDatabaseExt, SourceRootId,
|
||||||
};
|
};
|
||||||
use relative_path::RelativePath;
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::cell::RefCell;
|
||||||
|
|
||||||
use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink};
|
use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ra_db::{SourceDatabase, SourceDatabaseExt};
|
use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo,
|
algo,
|
||||||
|
@ -12,7 +12,6 @@ use ra_syntax::{
|
||||||
Location, SyntaxNode, TextRange, T,
|
Location, SyntaxNode, TextRange, T,
|
||||||
};
|
};
|
||||||
use ra_text_edit::{TextEdit, TextEditBuilder};
|
use ra_text_edit::{TextEdit, TextEditBuilder};
|
||||||
use relative_path::RelativePath;
|
|
||||||
|
|
||||||
use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit};
|
use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use relative_path::RelativePathBuf;
|
use ra_db::RelativePathBuf;
|
||||||
use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
|
use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use hir::ModuleSource;
|
use hir::ModuleSource;
|
||||||
use ra_db::{SourceDatabase, SourceDatabaseExt};
|
use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt};
|
||||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode};
|
use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode};
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use relative_path::{RelativePath, RelativePathBuf};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange,
|
db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange,
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
//!
|
//!
|
||||||
//! It can be viewed as a dual for `AnalysisChange`.
|
//! It can be viewed as a dual for `AnalysisChange`.
|
||||||
|
|
||||||
|
use ra_db::RelativePathBuf;
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use relative_path::RelativePathBuf;
|
|
||||||
|
|
||||||
use crate::{FileId, FilePosition, SourceRootId, TextUnit};
|
use crate::{FileId, FilePosition, SourceRootId, TextUnit};
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
|
||||||
text_edit: Some(text_edit),
|
text_edit: Some(text_edit),
|
||||||
additional_text_edits: Some(additional_text_edits),
|
additional_text_edits: Some(additional_text_edits),
|
||||||
documentation: self.documentation().map(|it| it.conv()),
|
documentation: self.documentation().map(|it| it.conv()),
|
||||||
|
deprecated: Some(self.deprecated()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
res.insert_text_format = Some(match self.insert_text_format() {
|
res.insert_text_format = Some(match self.insert_text_format() {
|
||||||
|
|
|
@ -110,6 +110,23 @@ pub fn match_arm_list(arms: impl Iterator<Item = ast::MatchArm>) -> ast::MatchAr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn let_match_early(expr: ast::Expr, path: &str, early_expression: &str) -> ast::LetStmt {
|
||||||
|
return from_text(&format!(
|
||||||
|
r#"let {} = match {} {{
|
||||||
|
{}(it) => it,
|
||||||
|
None => {},
|
||||||
|
}};"#,
|
||||||
|
expr.syntax().text(),
|
||||||
|
expr.syntax().text(),
|
||||||
|
path,
|
||||||
|
early_expression
|
||||||
|
));
|
||||||
|
|
||||||
|
fn from_text(text: &str) -> ast::LetStmt {
|
||||||
|
ast_from_text(&format!("fn f() {{ {} }}", text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn where_pred(path: ast::Path, bounds: impl Iterator<Item = ast::TypeBound>) -> ast::WherePred {
|
pub fn where_pred(path: ast::Path, bounds: impl Iterator<Item = ast::TypeBound>) -> ast::WherePred {
|
||||||
let bounds = bounds.map(|b| b.syntax().to_string()).join(" + ");
|
let bounds = bounds.map(|b| b.syntax().to_string()).join(" + ");
|
||||||
return from_text(&format!("{}: {}", path.syntax(), bounds));
|
return from_text(&format!("{}: {}", path.syntax(), bounds));
|
||||||
|
|
|
@ -111,7 +111,7 @@ impl<'a> Cursor<'a> {
|
||||||
|
|
||||||
/// If the cursor is pointing at the end of a subtree, returns
|
/// If the cursor is pointing at the end of a subtree, returns
|
||||||
/// the parent subtree
|
/// the parent subtree
|
||||||
pub fn end(self) -> Option<(&'a Subtree)> {
|
pub fn end(self) -> Option<&'a Subtree> {
|
||||||
match self.entry() {
|
match self.entry() {
|
||||||
Some(Entry::End(Some(ptr))) => {
|
Some(Entry::End(Some(ptr))) => {
|
||||||
let idx = ptr.1;
|
let idx = ptr.1;
|
||||||
|
@ -127,7 +127,7 @@ impl<'a> Cursor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry(self) -> Option<(&'a Entry<'a>)> {
|
fn entry(self) -> Option<&'a Entry<'a>> {
|
||||||
self.buffer.entry(&self.ptr)
|
self.buffer.entry(&self.ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
reorder_modules = false
|
reorder_modules = false
|
||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
newline_style = "Unix"
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ fn is_hidden(entry: &DirEntry) -> bool {
|
||||||
fn no_docs_comments() {
|
fn no_docs_comments() {
|
||||||
let crates = project_root().join("crates");
|
let crates = project_root().join("crates");
|
||||||
let iter = WalkDir::new(crates);
|
let iter = WalkDir::new(crates);
|
||||||
|
let mut missing_docs = Vec::new();
|
||||||
for f in iter.into_iter().filter_entry(|e| !is_hidden(e)) {
|
for f in iter.into_iter().filter_entry(|e| !is_hidden(e)) {
|
||||||
let f = f.unwrap();
|
let f = f.unwrap();
|
||||||
if f.file_type().is_dir() {
|
if f.file_type().is_dir() {
|
||||||
|
@ -54,12 +55,14 @@ fn no_docs_comments() {
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
reader.read_line(&mut line).unwrap();
|
reader.read_line(&mut line).unwrap();
|
||||||
if !line.starts_with("//!") {
|
if !line.starts_with("//!") {
|
||||||
|
missing_docs.push(f.path().display().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !missing_docs.is_empty() {
|
||||||
panic!(
|
panic!(
|
||||||
"\nMissing docs strings\n\
|
"\nMissing docs strings\n\n\
|
||||||
module: {}\n\
|
modules:\n{}\n\n",
|
||||||
Need add doc for module\n",
|
missing_docs.join("\n")
|
||||||
f.path().display()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue