mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-30 11:37:31 +00:00
Auto merge of #144114 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
This commit is contained in:
commit
f2840cd6c8
110 changed files with 3767 additions and 1221 deletions
82
Cargo.lock
generated
82
Cargo.lock
generated
|
|
@ -153,6 +153,22 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-util-schemas"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde-untagged",
|
||||||
|
"serde-value",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"toml",
|
||||||
|
"unicode-xid",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo_metadata"
|
name = "cargo_metadata"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
|
@ -161,7 +177,22 @@ checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"camino",
|
"camino",
|
||||||
"cargo-platform",
|
"cargo-platform",
|
||||||
"cargo-util-schemas",
|
"cargo-util-schemas 0.2.0",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo_metadata"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
|
||||||
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
"cargo-platform",
|
||||||
|
"cargo-util-schemas 0.8.2",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1190,13 +1221,16 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
name = "lsp-server"
|
name = "lsp-server"
|
||||||
version = "0.7.8"
|
version = "0.7.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"log",
|
"log",
|
||||||
"lsp-types",
|
"lsp-types",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"toolchain",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1471,7 +1505,7 @@ dependencies = [
|
||||||
"edition",
|
"edition",
|
||||||
"expect-test",
|
"expect-test",
|
||||||
"ra-ap-rustc_lexer",
|
"ra-ap-rustc_lexer",
|
||||||
"rustc-literal-escaper 0.0.4",
|
"rustc-literal-escaper",
|
||||||
"stdx",
|
"stdx",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
@ -1599,7 +1633,7 @@ dependencies = [
|
||||||
name = "proc-macro-test"
|
name = "proc-macro-test"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo_metadata",
|
"cargo_metadata 0.20.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1640,7 +1674,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base-db",
|
"base-db",
|
||||||
"cargo_metadata",
|
"cargo_metadata 0.21.0",
|
||||||
"cfg",
|
"cfg",
|
||||||
"expect-test",
|
"expect-test",
|
||||||
"intern",
|
"intern",
|
||||||
|
|
@ -1722,9 +1756,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_abi"
|
name = "ra-ap-rustc_abi"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a967e3a9cd3e38b543f503978e0eccee461e3aea3f7b10e944959bff41dbe612"
|
checksum = "3ee51482d1c9d3e538acda8cce723db8eea1a81540544bf362bf4c3d841b2329"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"ra-ap-rustc_hashes",
|
"ra-ap-rustc_hashes",
|
||||||
|
|
@ -1734,18 +1768,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_hashes"
|
name = "ra-ap-rustc_hashes"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ea4c755ecbbffa5743c251344f484ebe571ec7bc5b36d80b2a8ae775d1a7a40"
|
checksum = "19c8f1e0c28e24e1b4c55dc08058c6c9829df2204497d4034259f491d348c204"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc-stable-hash",
|
"rustc-stable-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index"
|
name = "ra-ap-rustc_index"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aca7ad7cf911538c619caa2162339fe98637e9e46f11bb0484ef96735df4d64a"
|
checksum = "5f33f429cec6b92fa2c7243883279fb29dd233fdc3e94099aff32aa91aa87f50"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index_macros",
|
"ra-ap-rustc_index_macros",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
|
@ -1753,9 +1787,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index_macros"
|
name = "ra-ap-rustc_index_macros"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8767ba551c9355bc3031be072cc4bb0381106e5e7cd275e72b7a8c76051c4070"
|
checksum = "b9b55910dbe1fe7ef34bdc1d1bcb41e99b377eb680ea58a1218d95d6b4152257"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1764,9 +1798,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_lexer"
|
name = "ra-ap-rustc_lexer"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6101374afb267e6c27e4e2eb0b1352e9f3504c1a8f716f619cd39244e2ed92ab"
|
checksum = "22944e31fb91e9b3e75bcbc91e37d958b8c0825a6160927f2856831d2ce83b36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
|
|
@ -1775,19 +1809,19 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_parse_format"
|
name = "ra-ap-rustc_parse_format"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ecd88a19f00da4f43e6727d5013444cbc399804b5046dfa2bbcd28ebed3970ce"
|
checksum = "81057891bc2063ad9e353f29462fbc47a0f5072560af34428ae9313aaa5e9d97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_lexer",
|
"ra-ap-rustc_lexer",
|
||||||
"rustc-literal-escaper 0.0.2",
|
"rustc-literal-escaper",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_pattern_analysis"
|
name = "ra-ap-rustc_pattern_analysis"
|
||||||
version = "0.116.0"
|
version = "0.121.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb332dd32d7850a799862533b1c021e6062558861a4ad57817bf522499fbb892"
|
checksum = "fe21a3542980d56d2435e96c2720773cac1c63fd4db666417e414729da192eb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
|
|
@ -1855,7 +1889,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
"cargo_metadata",
|
"cargo_metadata 0.21.0",
|
||||||
"cfg",
|
"cfg",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
|
@ -1932,12 +1966,6 @@ version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-literal-escaper"
|
|
||||||
version = "0.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-literal-escaper"
|
name = "rustc-literal-escaper"
|
||||||
version = "0.0.4"
|
version = "0.0.4"
|
||||||
|
|
@ -2231,7 +2259,7 @@ dependencies = [
|
||||||
"rayon",
|
"rayon",
|
||||||
"rowan",
|
"rowan",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"rustc-literal-escaper 0.0.4",
|
"rustc-literal-escaper",
|
||||||
"rustc_apfloat",
|
"rustc_apfloat",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
"stdx",
|
"stdx",
|
||||||
|
|
|
||||||
20
Cargo.toml
20
Cargo.toml
|
|
@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
rust-version = "1.86"
|
rust-version = "1.88"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
authors = ["rust-analyzer team"]
|
authors = ["rust-analyzer team"]
|
||||||
|
|
@ -89,11 +89,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||||
edition = { path = "./crates/edition", version = "0.0.0" }
|
edition = { path = "./crates/edition", version = "0.0.0" }
|
||||||
|
|
||||||
ra-ap-rustc_lexer = { version = "0.116", default-features = false }
|
ra-ap-rustc_lexer = { version = "0.121", default-features = false }
|
||||||
ra-ap-rustc_parse_format = { version = "0.116", default-features = false }
|
ra-ap-rustc_parse_format = { version = "0.121", default-features = false }
|
||||||
ra-ap-rustc_index = { version = "0.116", default-features = false }
|
ra-ap-rustc_index = { version = "0.121", default-features = false }
|
||||||
ra-ap-rustc_abi = { version = "0.116", default-features = false }
|
ra-ap-rustc_abi = { version = "0.121", default-features = false }
|
||||||
ra-ap-rustc_pattern_analysis = { version = "0.116", default-features = false }
|
ra-ap-rustc_pattern_analysis = { version = "0.121", default-features = false }
|
||||||
|
|
||||||
# local crates that aren't published to crates.io. These should not have versions.
|
# local crates that aren't published to crates.io. These should not have versions.
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ lsp-server = { version = "0.7.8" }
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
arrayvec = "0.7.6"
|
arrayvec = "0.7.6"
|
||||||
bitflags = "2.9.1"
|
bitflags = "2.9.1"
|
||||||
cargo_metadata = "0.20.0"
|
cargo_metadata = "0.21.0"
|
||||||
camino = "1.1.10"
|
camino = "1.1.10"
|
||||||
chalk-solve = { version = "0.103.0", default-features = false }
|
chalk-solve = { version = "0.103.0", default-features = false }
|
||||||
chalk-ir = "0.103.0"
|
chalk-ir = "0.103.0"
|
||||||
|
|
@ -138,7 +138,11 @@ rayon = "1.10.0"
|
||||||
rowan = "=0.15.15"
|
rowan = "=0.15.15"
|
||||||
# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
|
# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
|
||||||
# on impls without it
|
# on impls without it
|
||||||
salsa = { version = "0.23.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
|
salsa = { version = "0.23.0", default-features = true, features = [
|
||||||
|
"rayon",
|
||||||
|
"salsa_unstable",
|
||||||
|
"macros",
|
||||||
|
] }
|
||||||
salsa-macros = "0.23.0"
|
salsa-macros = "0.23.0"
|
||||||
semver = "1.0.26"
|
semver = "1.0.26"
|
||||||
serde = { version = "1.0.219" }
|
serde = { version = "1.0.219" }
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ use rustc_hash::FxHashMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use span::{Edition, SyntaxContext};
|
use span::{Edition, SyntaxContext};
|
||||||
use syntax::{AstPtr, SyntaxNodePtr, ast};
|
use syntax::{AstPtr, SyntaxNodePtr, ast};
|
||||||
|
use thin_vec::ThinVec;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
use tt::TextRange;
|
use tt::TextRange;
|
||||||
|
|
||||||
|
|
@ -93,17 +94,17 @@ pub type TypeSource = InFile<TypePtr>;
|
||||||
pub type LifetimePtr = AstPtr<ast::Lifetime>;
|
pub type LifetimePtr = AstPtr<ast::Lifetime>;
|
||||||
pub type LifetimeSource = InFile<LifetimePtr>;
|
pub type LifetimeSource = InFile<LifetimePtr>;
|
||||||
|
|
||||||
|
// We split the store into types-only and expressions, because most stores (e.g. generics)
|
||||||
|
// don't store any expressions and this saves memory. Same thing for the source map.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct ExpressionStore {
|
struct ExpressionOnlyStore {
|
||||||
pub exprs: Arena<Expr>,
|
exprs: Arena<Expr>,
|
||||||
pub pats: Arena<Pat>,
|
pats: Arena<Pat>,
|
||||||
pub bindings: Arena<Binding>,
|
bindings: Arena<Binding>,
|
||||||
pub labels: Arena<Label>,
|
labels: Arena<Label>,
|
||||||
pub types: Arena<TypeRef>,
|
|
||||||
pub lifetimes: Arena<LifetimeRef>,
|
|
||||||
/// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
|
/// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
|
||||||
/// top level expression, it will not be listed in here.
|
/// top level expression, it will not be listed in here.
|
||||||
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
binding_owners: FxHashMap<BindingId, ExprId>,
|
||||||
/// Block expressions in this store that may contain inner items.
|
/// Block expressions in this store that may contain inner items.
|
||||||
block_scopes: Box<[BlockId]>,
|
block_scopes: Box<[BlockId]>,
|
||||||
|
|
||||||
|
|
@ -114,8 +115,118 @@ pub struct ExpressionStore {
|
||||||
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct ExpressionStore {
|
||||||
|
expr_only: Option<Box<ExpressionOnlyStore>>,
|
||||||
|
pub types: Arena<TypeRef>,
|
||||||
|
pub lifetimes: Arena<LifetimeRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, Default)]
|
||||||
|
struct ExpressionOnlySourceMap {
|
||||||
|
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
|
||||||
|
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
|
||||||
|
expr_map: FxHashMap<ExprSource, ExprOrPatId>,
|
||||||
|
expr_map_back: ArenaMap<ExprId, ExprOrPatSource>,
|
||||||
|
|
||||||
|
pat_map: FxHashMap<PatSource, ExprOrPatId>,
|
||||||
|
pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
|
||||||
|
|
||||||
|
label_map: FxHashMap<LabelSource, LabelId>,
|
||||||
|
label_map_back: ArenaMap<LabelId, LabelSource>,
|
||||||
|
|
||||||
|
binding_definitions:
|
||||||
|
ArenaMap<BindingId, SmallVec<[PatId; 2 * size_of::<usize>() / size_of::<PatId>()]>>,
|
||||||
|
|
||||||
|
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
|
||||||
|
/// Instead, we use id of expression (`92`) to identify the field.
|
||||||
|
field_map_back: FxHashMap<ExprId, FieldSource>,
|
||||||
|
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
|
||||||
|
|
||||||
|
template_map: Option<Box<FormatTemplate>>,
|
||||||
|
|
||||||
|
expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
|
||||||
|
|
||||||
|
/// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
|
||||||
|
/// the source map (since they're just as volatile).
|
||||||
|
//
|
||||||
|
// We store diagnostics on the `ExpressionOnlySourceMap` because diagnostics are rare (except
|
||||||
|
// maybe for cfgs, and they are also not common in type places).
|
||||||
|
diagnostics: ThinVec<ExpressionStoreDiagnostics>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ExpressionOnlySourceMap {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// we only need to compare one of the two mappings
|
||||||
|
// as the other is a reverse mapping and thus will compare
|
||||||
|
// the same as normal mapping
|
||||||
|
let Self {
|
||||||
|
expr_map: _,
|
||||||
|
expr_map_back,
|
||||||
|
pat_map: _,
|
||||||
|
pat_map_back,
|
||||||
|
label_map: _,
|
||||||
|
label_map_back,
|
||||||
|
// If this changed, our pattern data must have changed
|
||||||
|
binding_definitions: _,
|
||||||
|
// If this changed, our expression data must have changed
|
||||||
|
field_map_back: _,
|
||||||
|
// If this changed, our pattern data must have changed
|
||||||
|
pat_field_map_back: _,
|
||||||
|
template_map,
|
||||||
|
expansions,
|
||||||
|
diagnostics,
|
||||||
|
} = self;
|
||||||
|
*expr_map_back == other.expr_map_back
|
||||||
|
&& *pat_map_back == other.pat_map_back
|
||||||
|
&& *label_map_back == other.label_map_back
|
||||||
|
&& *template_map == other.template_map
|
||||||
|
&& *expansions == other.expansions
|
||||||
|
&& *diagnostics == other.diagnostics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, Default)]
|
#[derive(Debug, Eq, Default)]
|
||||||
pub struct ExpressionStoreSourceMap {
|
pub struct ExpressionStoreSourceMap {
|
||||||
|
expr_only: Option<Box<ExpressionOnlySourceMap>>,
|
||||||
|
|
||||||
|
types_map_back: ArenaMap<TypeRefId, TypeSource>,
|
||||||
|
types_map: FxHashMap<TypeSource, TypeRefId>,
|
||||||
|
|
||||||
|
lifetime_map_back: ArenaMap<LifetimeRefId, LifetimeSource>,
|
||||||
|
#[expect(
|
||||||
|
unused,
|
||||||
|
reason = "this is here for completeness, and maybe we'll need it in the future"
|
||||||
|
)]
|
||||||
|
lifetime_map: FxHashMap<LifetimeSource, LifetimeRefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ExpressionStoreSourceMap {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// we only need to compare one of the two mappings
|
||||||
|
// as the other is a reverse mapping and thus will compare
|
||||||
|
// the same as normal mapping
|
||||||
|
let Self { expr_only, types_map_back, types_map: _, lifetime_map_back, lifetime_map: _ } =
|
||||||
|
self;
|
||||||
|
*expr_only == other.expr_only
|
||||||
|
&& *types_map_back == other.types_map_back
|
||||||
|
&& *lifetime_map_back == other.lifetime_map_back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The body of an item (function, const etc.).
|
||||||
|
#[derive(Debug, Eq, PartialEq, Default)]
|
||||||
|
pub struct ExpressionStoreBuilder {
|
||||||
|
pub exprs: Arena<Expr>,
|
||||||
|
pub pats: Arena<Pat>,
|
||||||
|
pub bindings: Arena<Binding>,
|
||||||
|
pub labels: Arena<Label>,
|
||||||
|
pub lifetimes: Arena<LifetimeRef>,
|
||||||
|
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
||||||
|
pub types: Arena<TypeRef>,
|
||||||
|
block_scopes: Vec<BlockId>,
|
||||||
|
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
||||||
|
|
||||||
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
|
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
|
||||||
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
|
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
|
||||||
expr_map: FxHashMap<ExprSource, ExprOrPatId>,
|
expr_map: FxHashMap<ExprSource, ExprOrPatId>,
|
||||||
|
|
@ -143,62 +254,14 @@ pub struct ExpressionStoreSourceMap {
|
||||||
|
|
||||||
template_map: Option<Box<FormatTemplate>>,
|
template_map: Option<Box<FormatTemplate>>,
|
||||||
|
|
||||||
pub expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
|
expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
|
||||||
|
|
||||||
/// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
|
/// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
|
||||||
/// the source map (since they're just as volatile).
|
/// the source map (since they're just as volatile).
|
||||||
pub diagnostics: Vec<ExpressionStoreDiagnostics>,
|
//
|
||||||
}
|
// We store diagnostics on the `ExpressionOnlySourceMap` because diagnostics are rare (except
|
||||||
|
// maybe for cfgs, and they are also not common in type places).
|
||||||
impl PartialEq for ExpressionStoreSourceMap {
|
pub(crate) diagnostics: Vec<ExpressionStoreDiagnostics>,
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
// we only need to compare one of the two mappings
|
|
||||||
// as the other is a reverse mapping and thus will compare
|
|
||||||
// the same as normal mapping
|
|
||||||
let Self {
|
|
||||||
expr_map: _,
|
|
||||||
expr_map_back,
|
|
||||||
pat_map: _,
|
|
||||||
pat_map_back,
|
|
||||||
label_map: _,
|
|
||||||
label_map_back,
|
|
||||||
types_map_back,
|
|
||||||
types_map: _,
|
|
||||||
lifetime_map_back,
|
|
||||||
lifetime_map: _,
|
|
||||||
// If this changed, our pattern data must have changed
|
|
||||||
binding_definitions: _,
|
|
||||||
// If this changed, our expression data must have changed
|
|
||||||
field_map_back: _,
|
|
||||||
// If this changed, our pattern data must have changed
|
|
||||||
pat_field_map_back: _,
|
|
||||||
template_map,
|
|
||||||
expansions,
|
|
||||||
diagnostics,
|
|
||||||
} = self;
|
|
||||||
*expr_map_back == other.expr_map_back
|
|
||||||
&& *pat_map_back == other.pat_map_back
|
|
||||||
&& *label_map_back == other.label_map_back
|
|
||||||
&& *types_map_back == other.types_map_back
|
|
||||||
&& *lifetime_map_back == other.lifetime_map_back
|
|
||||||
&& *template_map == other.template_map
|
|
||||||
&& *expansions == other.expansions
|
|
||||||
&& *diagnostics == other.diagnostics
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The body of an item (function, const etc.).
|
|
||||||
#[derive(Debug, Eq, PartialEq, Default)]
|
|
||||||
pub struct ExpressionStoreBuilder {
|
|
||||||
pub exprs: Arena<Expr>,
|
|
||||||
pub pats: Arena<Pat>,
|
|
||||||
pub bindings: Arena<Binding>,
|
|
||||||
pub labels: Arena<Label>,
|
|
||||||
pub lifetimes: Arena<LifetimeRef>,
|
|
||||||
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
|
||||||
pub types: Arena<TypeRef>,
|
|
||||||
block_scopes: Vec<BlockId>,
|
|
||||||
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Eq, PartialEq)]
|
#[derive(Default, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -226,7 +289,7 @@ pub enum ExpressionStoreDiagnostics {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpressionStoreBuilder {
|
impl ExpressionStoreBuilder {
|
||||||
pub fn finish(self) -> ExpressionStore {
|
pub fn finish(self) -> (ExpressionStore, ExpressionStoreSourceMap) {
|
||||||
let Self {
|
let Self {
|
||||||
block_scopes,
|
block_scopes,
|
||||||
mut exprs,
|
mut exprs,
|
||||||
|
|
@ -237,6 +300,23 @@ impl ExpressionStoreBuilder {
|
||||||
mut ident_hygiene,
|
mut ident_hygiene,
|
||||||
mut types,
|
mut types,
|
||||||
mut lifetimes,
|
mut lifetimes,
|
||||||
|
|
||||||
|
mut expr_map,
|
||||||
|
mut expr_map_back,
|
||||||
|
mut pat_map,
|
||||||
|
mut pat_map_back,
|
||||||
|
mut label_map,
|
||||||
|
mut label_map_back,
|
||||||
|
mut types_map_back,
|
||||||
|
mut types_map,
|
||||||
|
mut lifetime_map_back,
|
||||||
|
mut lifetime_map,
|
||||||
|
mut binding_definitions,
|
||||||
|
mut field_map_back,
|
||||||
|
mut pat_field_map_back,
|
||||||
|
mut template_map,
|
||||||
|
mut expansions,
|
||||||
|
diagnostics,
|
||||||
} = self;
|
} = self;
|
||||||
exprs.shrink_to_fit();
|
exprs.shrink_to_fit();
|
||||||
labels.shrink_to_fit();
|
labels.shrink_to_fit();
|
||||||
|
|
@ -247,24 +327,90 @@ impl ExpressionStoreBuilder {
|
||||||
types.shrink_to_fit();
|
types.shrink_to_fit();
|
||||||
lifetimes.shrink_to_fit();
|
lifetimes.shrink_to_fit();
|
||||||
|
|
||||||
ExpressionStore {
|
expr_map.shrink_to_fit();
|
||||||
|
expr_map_back.shrink_to_fit();
|
||||||
|
pat_map.shrink_to_fit();
|
||||||
|
pat_map_back.shrink_to_fit();
|
||||||
|
label_map.shrink_to_fit();
|
||||||
|
label_map_back.shrink_to_fit();
|
||||||
|
types_map_back.shrink_to_fit();
|
||||||
|
types_map.shrink_to_fit();
|
||||||
|
lifetime_map_back.shrink_to_fit();
|
||||||
|
lifetime_map.shrink_to_fit();
|
||||||
|
binding_definitions.shrink_to_fit();
|
||||||
|
field_map_back.shrink_to_fit();
|
||||||
|
pat_field_map_back.shrink_to_fit();
|
||||||
|
if let Some(template_map) = &mut template_map {
|
||||||
|
let FormatTemplate {
|
||||||
|
format_args_to_captures,
|
||||||
|
asm_to_captures,
|
||||||
|
implicit_capture_to_source,
|
||||||
|
} = &mut **template_map;
|
||||||
|
format_args_to_captures.shrink_to_fit();
|
||||||
|
asm_to_captures.shrink_to_fit();
|
||||||
|
implicit_capture_to_source.shrink_to_fit();
|
||||||
|
}
|
||||||
|
expansions.shrink_to_fit();
|
||||||
|
|
||||||
|
let has_exprs =
|
||||||
|
!exprs.is_empty() || !labels.is_empty() || !pats.is_empty() || !bindings.is_empty();
|
||||||
|
|
||||||
|
let store = {
|
||||||
|
let expr_only = if has_exprs {
|
||||||
|
Some(Box::new(ExpressionOnlyStore {
|
||||||
exprs,
|
exprs,
|
||||||
pats,
|
pats,
|
||||||
bindings,
|
bindings,
|
||||||
labels,
|
labels,
|
||||||
binding_owners,
|
binding_owners,
|
||||||
types,
|
|
||||||
lifetimes,
|
|
||||||
block_scopes: block_scopes.into_boxed_slice(),
|
block_scopes: block_scopes.into_boxed_slice(),
|
||||||
ident_hygiene,
|
ident_hygiene,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
ExpressionStore { expr_only, types, lifetimes }
|
||||||
|
};
|
||||||
|
|
||||||
|
let source_map = {
|
||||||
|
let expr_only = if has_exprs || !expansions.is_empty() || !diagnostics.is_empty() {
|
||||||
|
Some(Box::new(ExpressionOnlySourceMap {
|
||||||
|
expr_map,
|
||||||
|
expr_map_back,
|
||||||
|
pat_map,
|
||||||
|
pat_map_back,
|
||||||
|
label_map,
|
||||||
|
label_map_back,
|
||||||
|
binding_definitions,
|
||||||
|
field_map_back,
|
||||||
|
pat_field_map_back,
|
||||||
|
template_map,
|
||||||
|
expansions,
|
||||||
|
diagnostics: ThinVec::from_iter(diagnostics),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
ExpressionStoreSourceMap {
|
||||||
|
expr_only,
|
||||||
|
types_map_back,
|
||||||
|
types_map,
|
||||||
|
lifetime_map_back,
|
||||||
|
lifetime_map,
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(store, source_map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpressionStore {
|
impl ExpressionStore {
|
||||||
pub fn empty_singleton() -> Arc<Self> {
|
pub fn empty_singleton() -> (Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>) {
|
||||||
static EMPTY: LazyLock<Arc<ExpressionStore>> =
|
static EMPTY: LazyLock<(Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>)> =
|
||||||
LazyLock::new(|| Arc::new(ExpressionStoreBuilder::default().finish()));
|
LazyLock::new(|| {
|
||||||
|
let (store, source_map) = ExpressionStoreBuilder::default().finish();
|
||||||
|
(Arc::new(store), Arc::new(source_map))
|
||||||
|
});
|
||||||
EMPTY.clone()
|
EMPTY.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,7 +419,12 @@ impl ExpressionStore {
|
||||||
&'a self,
|
&'a self,
|
||||||
db: &'a dyn DefDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
) -> impl Iterator<Item = (BlockId, &'a DefMap)> + 'a {
|
) -> impl Iterator<Item = (BlockId, &'a DefMap)> + 'a {
|
||||||
self.block_scopes.iter().map(move |&block| (block, block_def_map(db, block)))
|
self.expr_only
|
||||||
|
.as_ref()
|
||||||
|
.map(|it| &*it.block_scopes)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter()
|
||||||
|
.map(move |&block| (block, block_def_map(db, block)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
|
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
|
||||||
|
|
@ -320,7 +471,8 @@ impl ExpressionStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
|
pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
|
||||||
match self.binding_owners.get(&binding) {
|
let Some(expr_only) = &self.expr_only else { return false };
|
||||||
|
match expr_only.binding_owners.get(&binding) {
|
||||||
Some(it) => {
|
Some(it) => {
|
||||||
// We assign expression ids in a way that outer closures will receive
|
// We assign expression ids in a way that outer closures will receive
|
||||||
// a lower id
|
// a lower id
|
||||||
|
|
@ -330,6 +482,11 @@ impl ExpressionStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn binding_owner(&self, id: BindingId) -> Option<ExprId> {
|
||||||
|
self.expr_only.as_ref()?.binding_owners.get(&id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
/// Walks the immediate children expressions and calls `f` for each child expression.
|
/// Walks the immediate children expressions and calls `f` for each child expression.
|
||||||
///
|
///
|
||||||
/// Note that this does not walk const blocks.
|
/// Note that this does not walk const blocks.
|
||||||
|
|
@ -601,16 +758,22 @@ impl ExpressionStore {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
fn assert_expr_only(&self) -> &ExpressionOnlyStore {
|
||||||
|
self.expr_only.as_ref().expect("should have `ExpressionStore::expr_only`")
|
||||||
|
}
|
||||||
|
|
||||||
fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
|
fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
|
||||||
self.bindings[binding].hygiene
|
self.assert_expr_only().bindings[binding].hygiene
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
|
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
|
||||||
self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
|
self.assert_expr_only().ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
|
pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
|
||||||
self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
|
self.assert_expr_only().ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
|
pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
|
||||||
|
|
@ -619,43 +782,72 @@ impl ExpressionStore {
|
||||||
ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
|
ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn exprs(&self) -> impl Iterator<Item = (ExprId, &Expr)> {
|
||||||
|
match &self.expr_only {
|
||||||
|
Some(it) => it.exprs.iter(),
|
||||||
|
None => const { &Arena::new() }.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pats(&self) -> impl Iterator<Item = (PatId, &Pat)> {
|
||||||
|
match &self.expr_only {
|
||||||
|
Some(it) => it.pats.iter(),
|
||||||
|
None => const { &Arena::new() }.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn bindings(&self) -> impl Iterator<Item = (BindingId, &Binding)> {
|
||||||
|
match &self.expr_only {
|
||||||
|
Some(it) => it.bindings.iter(),
|
||||||
|
None => const { &Arena::new() }.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<ExprId> for ExpressionStore {
|
impl Index<ExprId> for ExpressionStore {
|
||||||
type Output = Expr;
|
type Output = Expr;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn index(&self, expr: ExprId) -> &Expr {
|
fn index(&self, expr: ExprId) -> &Expr {
|
||||||
&self.exprs[expr]
|
&self.assert_expr_only().exprs[expr]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<PatId> for ExpressionStore {
|
impl Index<PatId> for ExpressionStore {
|
||||||
type Output = Pat;
|
type Output = Pat;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn index(&self, pat: PatId) -> &Pat {
|
fn index(&self, pat: PatId) -> &Pat {
|
||||||
&self.pats[pat]
|
&self.assert_expr_only().pats[pat]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<LabelId> for ExpressionStore {
|
impl Index<LabelId> for ExpressionStore {
|
||||||
type Output = Label;
|
type Output = Label;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn index(&self, label: LabelId) -> &Label {
|
fn index(&self, label: LabelId) -> &Label {
|
||||||
&self.labels[label]
|
&self.assert_expr_only().labels[label]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<BindingId> for ExpressionStore {
|
impl Index<BindingId> for ExpressionStore {
|
||||||
type Output = Binding;
|
type Output = Binding;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn index(&self, b: BindingId) -> &Binding {
|
fn index(&self, b: BindingId) -> &Binding {
|
||||||
&self.bindings[b]
|
&self.assert_expr_only().bindings[b]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<TypeRefId> for ExpressionStore {
|
impl Index<TypeRefId> for ExpressionStore {
|
||||||
type Output = TypeRef;
|
type Output = TypeRef;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn index(&self, b: TypeRefId) -> &TypeRef {
|
fn index(&self, b: TypeRefId) -> &TypeRef {
|
||||||
&self.types[b]
|
&self.types[b]
|
||||||
}
|
}
|
||||||
|
|
@ -664,6 +856,7 @@ impl Index<TypeRefId> for ExpressionStore {
|
||||||
impl Index<LifetimeRefId> for ExpressionStore {
|
impl Index<LifetimeRefId> for ExpressionStore {
|
||||||
type Output = LifetimeRef;
|
type Output = LifetimeRef;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn index(&self, b: LifetimeRefId) -> &LifetimeRef {
|
fn index(&self, b: LifetimeRefId) -> &LifetimeRef {
|
||||||
&self.lifetimes[b]
|
&self.lifetimes[b]
|
||||||
}
|
}
|
||||||
|
|
@ -684,12 +877,6 @@ impl Index<PathId> for ExpressionStore {
|
||||||
// FIXME: Change `node_` prefix to something more reasonable.
|
// FIXME: Change `node_` prefix to something more reasonable.
|
||||||
// Perhaps `expr_syntax` and `expr_id`?
|
// Perhaps `expr_syntax` and `expr_id`?
|
||||||
impl ExpressionStoreSourceMap {
|
impl ExpressionStoreSourceMap {
|
||||||
pub fn empty_singleton() -> Arc<Self> {
|
|
||||||
static EMPTY: LazyLock<Arc<ExpressionStoreSourceMap>> =
|
|
||||||
LazyLock::new(|| Arc::new(ExpressionStoreSourceMap::default()));
|
|
||||||
EMPTY.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
||||||
match id {
|
match id {
|
||||||
ExprOrPatId::ExprId(id) => self.expr_syntax(id),
|
ExprOrPatId::ExprId(id) => self.expr_syntax(id),
|
||||||
|
|
@ -697,30 +884,46 @@ impl ExpressionStoreSourceMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn expr_or_synthetic(&self) -> Result<&ExpressionOnlySourceMap, SyntheticSyntax> {
|
||||||
|
self.expr_only.as_deref().ok_or(SyntheticSyntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn expr_only(&self) -> Option<&ExpressionOnlySourceMap> {
|
||||||
|
self.expr_only.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
fn assert_expr_only(&self) -> &ExpressionOnlySourceMap {
|
||||||
|
self.expr_only.as_ref().expect("should have `ExpressionStoreSourceMap::expr_only`")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
||||||
self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
|
self.expr_or_synthetic()?.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
|
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
|
||||||
let src = node.map(AstPtr::new);
|
let src = node.map(AstPtr::new);
|
||||||
self.expr_map.get(&src).cloned()
|
self.expr_only()?.expr_map.get(&src).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
|
pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
|
||||||
let src = node.map(AstPtr::new);
|
let src = node.map(AstPtr::new);
|
||||||
self.expansions.get(&src).cloned()
|
self.expr_only()?.expansions.get(&src).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroCallId)> + '_ {
|
pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroCallId)> + '_ {
|
||||||
self.expansions.iter().map(|(&a, &b)| (a, b))
|
self.expr_only().into_iter().flat_map(|it| it.expansions.iter().map(|(&a, &b)| (a, b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
||||||
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
|
self.expr_or_synthetic()?.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<ExprOrPatId> {
|
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<ExprOrPatId> {
|
||||||
self.pat_map.get(&node.map(AstPtr::new)).cloned()
|
self.expr_only()?.pat_map.get(&node.map(AstPtr::new)).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
|
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
|
||||||
|
|
@ -732,49 +935,50 @@ impl ExpressionStoreSourceMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label_syntax(&self, label: LabelId) -> LabelSource {
|
pub fn label_syntax(&self, label: LabelId) -> LabelSource {
|
||||||
self.label_map_back[label]
|
self.assert_expr_only().label_map_back[label]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
|
pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
|
||||||
self.binding_definitions.get(binding).map_or(&[], Deref::deref)
|
self.assert_expr_only().binding_definitions.get(binding).map_or(&[], Deref::deref)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
|
pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
|
||||||
let src = node.map(AstPtr::new);
|
let src = node.map(AstPtr::new);
|
||||||
self.label_map.get(&src).cloned()
|
self.expr_only()?.label_map.get(&src).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_syntax(&self, expr: ExprId) -> FieldSource {
|
pub fn field_syntax(&self, expr: ExprId) -> FieldSource {
|
||||||
self.field_map_back[&expr]
|
self.assert_expr_only().field_map_back[&expr]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
|
pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
|
||||||
self.pat_field_map_back[&pat]
|
self.assert_expr_only().pat_field_map_back[&pat]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
|
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
|
||||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
|
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
|
||||||
self.expr_map.get(&src).copied()
|
self.expr_only()?.expr_map.get(&src).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroCallId)> {
|
pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroCallId)> {
|
||||||
self.expansions.iter()
|
self.expr_only().into_iter().flat_map(|it| it.expansions.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
|
pub fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
|
||||||
self.expansions.get(&node.map(AstPtr::new)).copied()
|
self.expr_only()?.expansions.get(&node.map(AstPtr::new)).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn implicit_format_args(
|
pub fn implicit_format_args(
|
||||||
&self,
|
&self,
|
||||||
node: InFile<&ast::FormatArgsExpr>,
|
node: InFile<&ast::FormatArgsExpr>,
|
||||||
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
|
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
|
||||||
|
let expr_only = self.expr_only()?;
|
||||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||||
let (hygiene, names) = self
|
let (hygiene, names) = expr_only
|
||||||
.template_map
|
.template_map
|
||||||
.as_ref()?
|
.as_ref()?
|
||||||
.format_args_to_captures
|
.format_args_to_captures
|
||||||
.get(&self.expr_map.get(&src)?.as_expr()?)?;
|
.get(&expr_only.expr_map.get(&src)?.as_expr()?)?;
|
||||||
Some((*hygiene, &**names))
|
Some((*hygiene, &**names))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -782,67 +986,28 @@ impl ExpressionStoreSourceMap {
|
||||||
&self,
|
&self,
|
||||||
capture_expr: ExprId,
|
capture_expr: ExprId,
|
||||||
) -> Option<InFile<(ExprPtr, TextRange)>> {
|
) -> Option<InFile<(ExprPtr, TextRange)>> {
|
||||||
self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
|
self.expr_only()?
|
||||||
|
.template_map
|
||||||
|
.as_ref()?
|
||||||
|
.implicit_capture_to_source
|
||||||
|
.get(&capture_expr)
|
||||||
|
.copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asm_template_args(
|
pub fn asm_template_args(
|
||||||
&self,
|
&self,
|
||||||
node: InFile<&ast::AsmExpr>,
|
node: InFile<&ast::AsmExpr>,
|
||||||
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
|
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
|
||||||
|
let expr_only = self.expr_only()?;
|
||||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||||
let expr = self.expr_map.get(&src)?.as_expr()?;
|
let expr = expr_only.expr_map.get(&src)?.as_expr()?;
|
||||||
Some(expr)
|
Some(expr).zip(
|
||||||
.zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
|
expr_only.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the source map's diagnostics.
|
/// Get a reference to the source map's diagnostics.
|
||||||
pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
|
pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
|
||||||
&self.diagnostics
|
self.expr_only().map(|it| &*it.diagnostics).unwrap_or_default()
|
||||||
}
|
|
||||||
|
|
||||||
fn shrink_to_fit(&mut self) {
|
|
||||||
let Self {
|
|
||||||
expr_map,
|
|
||||||
expr_map_back,
|
|
||||||
pat_map,
|
|
||||||
pat_map_back,
|
|
||||||
label_map,
|
|
||||||
label_map_back,
|
|
||||||
field_map_back,
|
|
||||||
pat_field_map_back,
|
|
||||||
expansions,
|
|
||||||
template_map,
|
|
||||||
diagnostics,
|
|
||||||
binding_definitions,
|
|
||||||
types_map,
|
|
||||||
types_map_back,
|
|
||||||
lifetime_map_back,
|
|
||||||
lifetime_map,
|
|
||||||
} = self;
|
|
||||||
if let Some(template_map) = template_map {
|
|
||||||
let FormatTemplate {
|
|
||||||
format_args_to_captures,
|
|
||||||
asm_to_captures,
|
|
||||||
implicit_capture_to_source,
|
|
||||||
} = &mut **template_map;
|
|
||||||
format_args_to_captures.shrink_to_fit();
|
|
||||||
asm_to_captures.shrink_to_fit();
|
|
||||||
implicit_capture_to_source.shrink_to_fit();
|
|
||||||
}
|
|
||||||
expr_map.shrink_to_fit();
|
|
||||||
expr_map_back.shrink_to_fit();
|
|
||||||
pat_map.shrink_to_fit();
|
|
||||||
pat_map_back.shrink_to_fit();
|
|
||||||
label_map.shrink_to_fit();
|
|
||||||
label_map_back.shrink_to_fit();
|
|
||||||
field_map_back.shrink_to_fit();
|
|
||||||
pat_field_map_back.shrink_to_fit();
|
|
||||||
expansions.shrink_to_fit();
|
|
||||||
diagnostics.shrink_to_fit();
|
|
||||||
binding_definitions.shrink_to_fit();
|
|
||||||
types_map.shrink_to_fit();
|
|
||||||
types_map_back.shrink_to_fit();
|
|
||||||
lifetime_map.shrink_to_fit();
|
|
||||||
lifetime_map_back.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ pub struct Body {
|
||||||
impl ops::Deref for Body {
|
impl ops::Deref for Body {
|
||||||
type Target = ExpressionStore;
|
type Target = ExpressionStore;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.store
|
&self.store
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +62,7 @@ pub struct BodySourceMap {
|
||||||
impl ops::Deref for BodySourceMap {
|
impl ops::Deref for BodySourceMap {
|
||||||
type Target = ExpressionStoreSourceMap;
|
type Target = ExpressionStoreSourceMap;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.store
|
&self.store
|
||||||
}
|
}
|
||||||
|
|
@ -102,9 +104,7 @@ impl Body {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let module = def.module(db);
|
let module = def.module(db);
|
||||||
let (body, mut source_map) =
|
let (body, source_map) = lower_body(db, def, file_id, module, params, body, is_async_fn);
|
||||||
lower_body(db, def, file_id, module, params, body, is_async_fn);
|
|
||||||
source_map.store.shrink_to_fit();
|
|
||||||
|
|
||||||
(Arc::new(body), Arc::new(source_map))
|
(Arc::new(body), Arc::new(source_map))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,14 +121,10 @@ pub(super) fn lower_body(
|
||||||
params = (0..count).map(|_| collector.missing_pat()).collect();
|
params = (0..count).map(|_| collector.missing_pat()).collect();
|
||||||
};
|
};
|
||||||
let body_expr = collector.missing_expr();
|
let body_expr = collector.missing_expr();
|
||||||
|
let (store, source_map) = collector.store.finish();
|
||||||
return (
|
return (
|
||||||
Body {
|
Body { store, params: params.into_boxed_slice(), self_param, body_expr },
|
||||||
store: collector.store.finish(),
|
BodySourceMap { self_param: source_map_self_param, store: source_map },
|
||||||
params: params.into_boxed_slice(),
|
|
||||||
self_param,
|
|
||||||
body_expr,
|
|
||||||
},
|
|
||||||
BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,14 +167,10 @@ pub(super) fn lower_body(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let (store, source_map) = collector.store.finish();
|
||||||
(
|
(
|
||||||
Body {
|
Body { store, params: params.into_boxed_slice(), self_param, body_expr },
|
||||||
store: collector.store.finish(),
|
BodySourceMap { self_param: source_map_self_param, store: source_map },
|
||||||
params: params.into_boxed_slice(),
|
|
||||||
self_param,
|
|
||||||
body_expr,
|
|
||||||
},
|
|
||||||
BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +182,8 @@ pub(crate) fn lower_type_ref(
|
||||||
let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id);
|
let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id);
|
||||||
let type_ref =
|
let type_ref =
|
||||||
expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator);
|
expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator);
|
||||||
(expr_collector.store.finish(), expr_collector.source_map, type_ref)
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
|
(store, source_map, type_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_generic_params(
|
pub(crate) fn lower_generic_params(
|
||||||
|
|
@ -205,7 +198,8 @@ pub(crate) fn lower_generic_params(
|
||||||
let mut collector = generics::GenericParamsCollector::new(def);
|
let mut collector = generics::GenericParamsCollector::new(def);
|
||||||
collector.lower(&mut expr_collector, param_list, where_clause);
|
collector.lower(&mut expr_collector, param_list, where_clause);
|
||||||
let params = collector.finish();
|
let params = collector.finish();
|
||||||
(Arc::new(expr_collector.store.finish()), params, expr_collector.source_map)
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
|
(Arc::new(store), params, source_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_impl(
|
pub(crate) fn lower_impl(
|
||||||
|
|
@ -232,7 +226,8 @@ pub(crate) fn lower_impl(
|
||||||
impl_syntax.value.where_clause(),
|
impl_syntax.value.where_clause(),
|
||||||
);
|
);
|
||||||
let params = collector.finish();
|
let params = collector.finish();
|
||||||
(expr_collector.store.finish(), expr_collector.source_map, self_ty, trait_, params)
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
|
(store, source_map, self_ty, trait_, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_trait(
|
pub(crate) fn lower_trait(
|
||||||
|
|
@ -253,7 +248,8 @@ pub(crate) fn lower_trait(
|
||||||
trait_syntax.value.where_clause(),
|
trait_syntax.value.where_clause(),
|
||||||
);
|
);
|
||||||
let params = collector.finish();
|
let params = collector.finish();
|
||||||
(expr_collector.store.finish(), expr_collector.source_map, params)
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
|
(store, source_map, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_trait_alias(
|
pub(crate) fn lower_trait_alias(
|
||||||
|
|
@ -274,7 +270,8 @@ pub(crate) fn lower_trait_alias(
|
||||||
trait_syntax.value.where_clause(),
|
trait_syntax.value.where_clause(),
|
||||||
);
|
);
|
||||||
let params = collector.finish();
|
let params = collector.finish();
|
||||||
(expr_collector.store.finish(), expr_collector.source_map, params)
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
|
(store, source_map, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_type_alias(
|
pub(crate) fn lower_type_alias(
|
||||||
|
|
@ -313,7 +310,8 @@ pub(crate) fn lower_type_alias(
|
||||||
.value
|
.value
|
||||||
.ty()
|
.ty()
|
||||||
.map(|ty| expr_collector.lower_type_ref(ty, &mut ExprCollector::impl_trait_allocator));
|
.map(|ty| expr_collector.lower_type_ref(ty, &mut ExprCollector::impl_trait_allocator));
|
||||||
(expr_collector.store.finish(), expr_collector.source_map, params, bounds, type_ref)
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
|
(store, source_map, params, bounds, type_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_function(
|
pub(crate) fn lower_function(
|
||||||
|
|
@ -421,9 +419,10 @@ pub(crate) fn lower_function(
|
||||||
} else {
|
} else {
|
||||||
return_type
|
return_type
|
||||||
};
|
};
|
||||||
|
let (store, source_map) = expr_collector.store.finish();
|
||||||
(
|
(
|
||||||
expr_collector.store.finish(),
|
store,
|
||||||
expr_collector.source_map,
|
source_map,
|
||||||
generics,
|
generics,
|
||||||
params.into_boxed_slice(),
|
params.into_boxed_slice(),
|
||||||
return_type,
|
return_type,
|
||||||
|
|
@ -440,7 +439,6 @@ pub struct ExprCollector<'db> {
|
||||||
local_def_map: &'db LocalDefMap,
|
local_def_map: &'db LocalDefMap,
|
||||||
module: ModuleId,
|
module: ModuleId,
|
||||||
pub store: ExpressionStoreBuilder,
|
pub store: ExpressionStoreBuilder,
|
||||||
pub(crate) source_map: ExpressionStoreSourceMap,
|
|
||||||
|
|
||||||
// state stuff
|
// state stuff
|
||||||
// Prevent nested impl traits like `impl Foo<impl Bar>`.
|
// Prevent nested impl traits like `impl Foo<impl Bar>`.
|
||||||
|
|
@ -551,7 +549,6 @@ impl ExprCollector<'_> {
|
||||||
module,
|
module,
|
||||||
def_map,
|
def_map,
|
||||||
local_def_map,
|
local_def_map,
|
||||||
source_map: ExpressionStoreSourceMap::default(),
|
|
||||||
store: ExpressionStoreBuilder::default(),
|
store: ExpressionStoreBuilder::default(),
|
||||||
expander,
|
expander,
|
||||||
current_try_block_label: None,
|
current_try_block_label: None,
|
||||||
|
|
@ -698,7 +695,7 @@ impl ExprCollector<'_> {
|
||||||
let id = self.collect_macro_call(mcall, macro_ptr, true, |this, expansion| {
|
let id = self.collect_macro_call(mcall, macro_ptr, true, |this, expansion| {
|
||||||
this.lower_type_ref_opt(expansion, impl_trait_lower_fn)
|
this.lower_type_ref_opt(expansion, impl_trait_lower_fn)
|
||||||
});
|
});
|
||||||
self.source_map.types_map.insert(src, id);
|
self.store.types_map.insert(src, id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
None => TypeRef::Error,
|
None => TypeRef::Error,
|
||||||
|
|
@ -732,8 +729,8 @@ impl ExprCollector<'_> {
|
||||||
fn alloc_type_ref(&mut self, type_ref: TypeRef, node: TypePtr) -> TypeRefId {
|
fn alloc_type_ref(&mut self, type_ref: TypeRef, node: TypePtr) -> TypeRefId {
|
||||||
let id = self.store.types.alloc(type_ref);
|
let id = self.store.types.alloc(type_ref);
|
||||||
let ptr = self.expander.in_file(node);
|
let ptr = self.expander.in_file(node);
|
||||||
self.source_map.types_map_back.insert(id, ptr);
|
self.store.types_map_back.insert(id, ptr);
|
||||||
self.source_map.types_map.insert(ptr, id);
|
self.store.types_map.insert(ptr, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -744,8 +741,8 @@ impl ExprCollector<'_> {
|
||||||
) -> LifetimeRefId {
|
) -> LifetimeRefId {
|
||||||
let id = self.store.lifetimes.alloc(lifetime_ref);
|
let id = self.store.lifetimes.alloc(lifetime_ref);
|
||||||
let ptr = self.expander.in_file(node);
|
let ptr = self.expander.in_file(node);
|
||||||
self.source_map.lifetime_map_back.insert(id, ptr);
|
self.store.lifetime_map_back.insert(id, ptr);
|
||||||
self.source_map.lifetime_map.insert(ptr, id);
|
self.store.lifetime_map.insert(ptr, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1190,14 +1187,14 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
ast::Expr::ContinueExpr(e) => {
|
ast::Expr::ContinueExpr(e) => {
|
||||||
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
||||||
self.source_map.diagnostics.push(e);
|
self.store.diagnostics.push(e);
|
||||||
None
|
None
|
||||||
});
|
});
|
||||||
self.alloc_expr(Expr::Continue { label }, syntax_ptr)
|
self.alloc_expr(Expr::Continue { label }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::BreakExpr(e) => {
|
ast::Expr::BreakExpr(e) => {
|
||||||
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
||||||
self.source_map.diagnostics.push(e);
|
self.store.diagnostics.push(e);
|
||||||
None
|
None
|
||||||
});
|
});
|
||||||
let expr = e.expr().map(|e| self.collect_expr(e));
|
let expr = e.expr().map(|e| self.collect_expr(e));
|
||||||
|
|
@ -1207,7 +1204,7 @@ impl ExprCollector<'_> {
|
||||||
let inner = self.collect_expr_opt(e.expr());
|
let inner = self.collect_expr_opt(e.expr());
|
||||||
// make the paren expr point to the inner expression as well for IDE resolution
|
// make the paren expr point to the inner expression as well for IDE resolution
|
||||||
let src = self.expander.in_file(syntax_ptr);
|
let src = self.expander.in_file(syntax_ptr);
|
||||||
self.source_map.expr_map.insert(src, inner.into());
|
self.store.expr_map.insert(src, inner.into());
|
||||||
inner
|
inner
|
||||||
}
|
}
|
||||||
ast::Expr::ReturnExpr(e) => {
|
ast::Expr::ReturnExpr(e) => {
|
||||||
|
|
@ -1248,7 +1245,7 @@ impl ExprCollector<'_> {
|
||||||
None => self.missing_expr(),
|
None => self.missing_expr(),
|
||||||
};
|
};
|
||||||
let src = self.expander.in_file(AstPtr::new(&field));
|
let src = self.expander.in_file(AstPtr::new(&field));
|
||||||
self.source_map.field_map_back.insert(expr, src);
|
self.store.field_map_back.insert(expr, src);
|
||||||
Some(RecordLitField { name, expr })
|
Some(RecordLitField { name, expr })
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1271,12 +1268,10 @@ impl ExprCollector<'_> {
|
||||||
ast::Expr::AwaitExpr(e) => {
|
ast::Expr::AwaitExpr(e) => {
|
||||||
let expr = self.collect_expr_opt(e.expr());
|
let expr = self.collect_expr_opt(e.expr());
|
||||||
if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
|
if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
|
||||||
self.source_map.diagnostics.push(
|
self.store.diagnostics.push(ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
|
||||||
ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
|
|
||||||
node: self.expander.in_file(AstPtr::new(&e)),
|
node: self.expander.in_file(AstPtr::new(&e)),
|
||||||
location: location.to_string(),
|
location: location.to_string(),
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
|
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
|
||||||
}
|
}
|
||||||
|
|
@ -1442,7 +1437,7 @@ impl ExprCollector<'_> {
|
||||||
// Make the macro-call point to its expanded expression so we can query
|
// Make the macro-call point to its expanded expression so we can query
|
||||||
// semantics on syntax pointers to the macro
|
// semantics on syntax pointers to the macro
|
||||||
let src = self.expander.in_file(syntax_ptr);
|
let src = self.expander.in_file(syntax_ptr);
|
||||||
self.source_map.expr_map.insert(src, id.into());
|
self.store.expr_map.insert(src, id.into());
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||||
|
|
@ -1486,7 +1481,7 @@ impl ExprCollector<'_> {
|
||||||
let expr = self.collect_expr(expr);
|
let expr = self.collect_expr(expr);
|
||||||
// Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
|
// Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
|
||||||
let id = self.store.pats.alloc(Pat::Expr(expr));
|
let id = self.store.pats.alloc(Pat::Expr(expr));
|
||||||
self.source_map.pat_map_back.insert(id, src);
|
self.store.pat_map_back.insert(id, src);
|
||||||
id
|
id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1555,7 +1550,7 @@ impl ExprCollector<'_> {
|
||||||
let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
|
let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
|
||||||
this.collect_expr_as_pat_opt(expansion)
|
this.collect_expr_as_pat_opt(expansion)
|
||||||
});
|
});
|
||||||
self.source_map.expr_map.insert(src, id.into());
|
self.store.expr_map.insert(src, id.into());
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
ast::Expr::RecordExpr(e) => {
|
ast::Expr::RecordExpr(e) => {
|
||||||
|
|
@ -1576,7 +1571,7 @@ impl ExprCollector<'_> {
|
||||||
let pat = self.collect_expr_as_pat(field_expr);
|
let pat = self.collect_expr_as_pat(field_expr);
|
||||||
let name = f.field_name()?.as_name();
|
let name = f.field_name()?.as_name();
|
||||||
let src = self.expander.in_file(AstPtr::new(&f).wrap_left());
|
let src = self.expander.in_file(AstPtr::new(&f).wrap_left());
|
||||||
self.source_map.pat_field_map_back.insert(pat, src);
|
self.store.pat_field_map_back.insert(pat, src);
|
||||||
Some(RecordFieldPat { name, pat })
|
Some(RecordFieldPat { name, pat })
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1622,7 +1617,7 @@ impl ExprCollector<'_> {
|
||||||
);
|
);
|
||||||
if let Either::Left(pat) = pat {
|
if let Either::Left(pat) = pat {
|
||||||
let src = this.expander.in_file(AstPtr::new(&expr).wrap_left());
|
let src = this.expander.in_file(AstPtr::new(&expr).wrap_left());
|
||||||
this.source_map.pat_map_back.insert(pat, src);
|
this.store.pat_map_back.insert(pat, src);
|
||||||
}
|
}
|
||||||
pat
|
pat
|
||||||
}
|
}
|
||||||
|
|
@ -1968,7 +1963,7 @@ impl ExprCollector<'_> {
|
||||||
self.module.krate(),
|
self.module.krate(),
|
||||||
resolver,
|
resolver,
|
||||||
&mut |ptr, call| {
|
&mut |ptr, call| {
|
||||||
_ = self.source_map.expansions.insert(ptr.map(|(it, _)| it), call);
|
_ = self.store.expansions.insert(ptr.map(|(it, _)| it), call);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1978,19 +1973,17 @@ impl ExprCollector<'_> {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(UnresolvedMacro { path }) => {
|
Err(UnresolvedMacro { path }) => {
|
||||||
if record_diagnostics {
|
if record_diagnostics {
|
||||||
self.source_map.diagnostics.push(
|
self.store.diagnostics.push(ExpressionStoreDiagnostics::UnresolvedMacroCall {
|
||||||
ExpressionStoreDiagnostics::UnresolvedMacroCall {
|
|
||||||
node: self.expander.in_file(syntax_ptr),
|
node: self.expander.in_file(syntax_ptr),
|
||||||
path,
|
path,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return collector(self, None);
|
return collector(self, None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if record_diagnostics {
|
if record_diagnostics {
|
||||||
if let Some(err) = res.err {
|
if let Some(err) = res.err {
|
||||||
self.source_map
|
self.store
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(ExpressionStoreDiagnostics::MacroError { node: macro_call_ptr, err });
|
.push(ExpressionStoreDiagnostics::MacroError { node: macro_call_ptr, err });
|
||||||
}
|
}
|
||||||
|
|
@ -2001,7 +1994,7 @@ impl ExprCollector<'_> {
|
||||||
// Keep collecting even with expansion errors so we can provide completions and
|
// Keep collecting even with expansion errors so we can provide completions and
|
||||||
// other services in incomplete macro expressions.
|
// other services in incomplete macro expressions.
|
||||||
if let Some(macro_file) = self.expander.current_file_id().macro_file() {
|
if let Some(macro_file) = self.expander.current_file_id().macro_file() {
|
||||||
self.source_map.expansions.insert(macro_call_ptr, macro_file);
|
self.store.expansions.insert(macro_call_ptr, macro_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if record_diagnostics {
|
if record_diagnostics {
|
||||||
|
|
@ -2050,7 +2043,7 @@ impl ExprCollector<'_> {
|
||||||
// Make the macro-call point to its expanded expression so we can query
|
// Make the macro-call point to its expanded expression so we can query
|
||||||
// semantics on syntax pointers to the macro
|
// semantics on syntax pointers to the macro
|
||||||
let src = self.expander.in_file(syntax_ptr);
|
let src = self.expander.in_file(syntax_ptr);
|
||||||
self.source_map.expr_map.insert(src, tail.into());
|
self.store.expr_map.insert(src, tail.into());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2361,7 +2354,7 @@ impl ExprCollector<'_> {
|
||||||
let pat = self.collect_pat(ast_pat, binding_list);
|
let pat = self.collect_pat(ast_pat, binding_list);
|
||||||
let name = f.field_name()?.as_name();
|
let name = f.field_name()?.as_name();
|
||||||
let src = self.expander.in_file(AstPtr::new(&f).wrap_right());
|
let src = self.expander.in_file(AstPtr::new(&f).wrap_right());
|
||||||
self.source_map.pat_field_map_back.insert(pat, src);
|
self.store.pat_field_map_back.insert(pat, src);
|
||||||
Some(RecordFieldPat { name, pat })
|
Some(RecordFieldPat { name, pat })
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -2424,7 +2417,7 @@ impl ExprCollector<'_> {
|
||||||
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
|
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
|
||||||
this.collect_pat_opt(expanded_pat, binding_list)
|
this.collect_pat_opt(expanded_pat, binding_list)
|
||||||
});
|
});
|
||||||
self.source_map.pat_map.insert(src, pat.into());
|
self.store.pat_map.insert(src, pat.into());
|
||||||
return pat;
|
return pat;
|
||||||
}
|
}
|
||||||
None => Pat::Missing,
|
None => Pat::Missing,
|
||||||
|
|
@ -2515,7 +2508,7 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if let Some(pat) = pat.left() {
|
if let Some(pat) = pat.left() {
|
||||||
self.source_map.pat_map.insert(src, pat.into());
|
self.store.pat_map.insert(src, pat.into());
|
||||||
}
|
}
|
||||||
pat
|
pat
|
||||||
}
|
}
|
||||||
|
|
@ -2537,7 +2530,7 @@ impl ExprCollector<'_> {
|
||||||
match enabled {
|
match enabled {
|
||||||
Ok(()) => true,
|
Ok(()) => true,
|
||||||
Err(cfg) => {
|
Err(cfg) => {
|
||||||
self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
|
self.store.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
|
||||||
node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
|
node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
|
||||||
cfg,
|
cfg,
|
||||||
opts: self.cfg_options.clone(),
|
opts: self.cfg_options.clone(),
|
||||||
|
|
@ -2548,7 +2541,7 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
|
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
|
||||||
self.source_map.binding_definitions.entry(binding_id).or_default().push(pat_id);
|
self.store.binding_definitions.entry(binding_id).or_default().push(pat_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// region: labels
|
// region: labels
|
||||||
|
|
@ -2724,7 +2717,7 @@ impl ExprCollector<'_> {
|
||||||
|name, range| {
|
|name, range| {
|
||||||
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
|
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
|
||||||
if let Some(range) = range {
|
if let Some(range) = range {
|
||||||
self.source_map
|
self.store
|
||||||
.template_map
|
.template_map
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.implicit_capture_to_source
|
.implicit_capture_to_source
|
||||||
|
|
@ -2836,7 +2829,7 @@ impl ExprCollector<'_> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.source_map
|
self.store
|
||||||
.template_map
|
.template_map
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.format_args_to_captures
|
.format_args_to_captures
|
||||||
|
|
@ -3386,8 +3379,8 @@ impl ExprCollector<'_> {
|
||||||
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
||||||
let src = self.expander.in_file(ptr);
|
let src = self.expander.in_file(ptr);
|
||||||
let id = self.store.exprs.alloc(expr);
|
let id = self.store.exprs.alloc(expr);
|
||||||
self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
|
self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
|
||||||
self.source_map.expr_map.insert(src, id.into());
|
self.store.expr_map.insert(src, id.into());
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
|
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
|
||||||
|
|
@ -3398,9 +3391,9 @@ impl ExprCollector<'_> {
|
||||||
fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
||||||
let src = self.expander.in_file(ptr);
|
let src = self.expander.in_file(ptr);
|
||||||
let id = self.store.exprs.alloc(expr);
|
let id = self.store.exprs.alloc(expr);
|
||||||
self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
|
self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
|
||||||
// We intentionally don't fill this as it could overwrite a non-desugared entry
|
// We intentionally don't fill this as it could overwrite a non-desugared entry
|
||||||
// self.source_map.expr_map.insert(src, id);
|
// self.store.expr_map.insert(src, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
fn missing_expr(&mut self) -> ExprId {
|
fn missing_expr(&mut self) -> ExprId {
|
||||||
|
|
@ -3423,24 +3416,24 @@ impl ExprCollector<'_> {
|
||||||
fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
|
fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
|
||||||
let src = self.expander.in_file(ptr);
|
let src = self.expander.in_file(ptr);
|
||||||
let id = self.store.pats.alloc(pat);
|
let id = self.store.pats.alloc(pat);
|
||||||
self.source_map.expr_map.insert(src, id.into());
|
self.store.expr_map.insert(src, id.into());
|
||||||
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
|
self.store.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_expr_from_pat(&mut self, expr: Expr, ptr: PatPtr) -> ExprId {
|
fn alloc_expr_from_pat(&mut self, expr: Expr, ptr: PatPtr) -> ExprId {
|
||||||
let src = self.expander.in_file(ptr);
|
let src = self.expander.in_file(ptr);
|
||||||
let id = self.store.exprs.alloc(expr);
|
let id = self.store.exprs.alloc(expr);
|
||||||
self.source_map.pat_map.insert(src, id.into());
|
self.store.pat_map.insert(src, id.into());
|
||||||
self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
|
self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
||||||
let src = self.expander.in_file(ptr);
|
let src = self.expander.in_file(ptr);
|
||||||
let id = self.store.pats.alloc(pat);
|
let id = self.store.pats.alloc(pat);
|
||||||
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
|
self.store.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
|
||||||
self.source_map.pat_map.insert(src, id.into());
|
self.store.pat_map.insert(src, id.into());
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
|
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
|
||||||
|
|
@ -3454,8 +3447,8 @@ impl ExprCollector<'_> {
|
||||||
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
|
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
|
||||||
let src = self.expander.in_file(ptr);
|
let src = self.expander.in_file(ptr);
|
||||||
let id = self.store.labels.alloc(label);
|
let id = self.store.labels.alloc(label);
|
||||||
self.source_map.label_map_back.insert(id, src);
|
self.store.label_map_back.insert(id, src);
|
||||||
self.source_map.label_map.insert(src, id);
|
self.store.label_map.insert(src, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
|
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use tt::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr_store::lower::{ExprCollector, FxIndexSet},
|
expr_store::lower::{ExprCollector, FxIndexSet},
|
||||||
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
|
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmKind, InlineAsmRegOrRegClass},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ExprCollector<'_> {
|
impl ExprCollector<'_> {
|
||||||
|
|
@ -269,11 +269,20 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let kind = if asm.global_asm_token().is_some() {
|
||||||
|
InlineAsmKind::GlobalAsm
|
||||||
|
} else if asm.naked_asm_token().is_some() {
|
||||||
|
InlineAsmKind::NakedAsm
|
||||||
|
} else {
|
||||||
|
InlineAsmKind::Asm
|
||||||
|
};
|
||||||
|
|
||||||
let idx = self.alloc_expr(
|
let idx = self.alloc_expr(
|
||||||
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
|
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options, kind }),
|
||||||
syntax_ptr,
|
syntax_ptr,
|
||||||
);
|
);
|
||||||
self.source_map
|
self.store
|
||||||
.template_map
|
.template_map
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.asm_to_captures
|
.asm_to_captures
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) {
|
||||||
let mut ctx =
|
let mut ctx =
|
||||||
ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into());
|
ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into());
|
||||||
let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
|
let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
|
||||||
let store = ctx.store.finish();
|
let (store, _) = ctx.store.finish();
|
||||||
(db, store, lowered_path)
|
(db, store, lowered_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -902,7 +902,7 @@ impl Printer<'_> {
|
||||||
let mut same_name = false;
|
let mut same_name = false;
|
||||||
if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
|
if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
|
||||||
if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
|
if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
|
||||||
&self.store.bindings[*id]
|
&self.store.assert_expr_only().bindings[*id]
|
||||||
{
|
{
|
||||||
if name.as_str() == field_name {
|
if name.as_str() == field_name {
|
||||||
same_name = true;
|
same_name = true;
|
||||||
|
|
@ -1063,7 +1063,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_binding(&mut self, id: BindingId) {
|
fn print_binding(&mut self, id: BindingId) {
|
||||||
let Binding { name, mode, .. } = &self.store.bindings[id];
|
let Binding { name, mode, .. } = &self.store.assert_expr_only().bindings[id];
|
||||||
let mode = match mode {
|
let mode = match mode {
|
||||||
BindingAnnotation::Unannotated => "",
|
BindingAnnotation::Unannotated => "",
|
||||||
BindingAnnotation::Mutable => "mut ",
|
BindingAnnotation::Mutable => "mut ",
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,9 @@ impl ExprScopes {
|
||||||
let mut scopes = ExprScopes {
|
let mut scopes = ExprScopes {
|
||||||
scopes: Arena::default(),
|
scopes: Arena::default(),
|
||||||
scope_entries: Arena::default(),
|
scope_entries: Arena::default(),
|
||||||
scope_by_expr: ArenaMap::with_capacity(body.exprs.len()),
|
scope_by_expr: ArenaMap::with_capacity(
|
||||||
|
body.expr_only.as_ref().map_or(0, |it| it.exprs.len()),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let mut root = scopes.root_scope();
|
let mut root = scopes.root_scope();
|
||||||
if let Some(self_param) = body.self_param {
|
if let Some(self_param) = body.self_param {
|
||||||
|
|
@ -179,7 +181,7 @@ impl ExprScopes {
|
||||||
binding: BindingId,
|
binding: BindingId,
|
||||||
hygiene: HygieneId,
|
hygiene: HygieneId,
|
||||||
) {
|
) {
|
||||||
let Binding { name, .. } = &store.bindings[binding];
|
let Binding { name, .. } = &store[binding];
|
||||||
let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
|
let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
|
||||||
self.scopes[scope].entries =
|
self.scopes[scope].entries =
|
||||||
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
|
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
|
||||||
|
|
@ -251,7 +253,7 @@ fn compute_expr_scopes(
|
||||||
scope: &mut ScopeId,
|
scope: &mut ScopeId,
|
||||||
) {
|
) {
|
||||||
let make_label =
|
let make_label =
|
||||||
|label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone()));
|
|label: &Option<LabelId>| label.map(|label| (label, store[label].name.clone()));
|
||||||
|
|
||||||
let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
|
let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
|
||||||
compute_expr_scopes(expr, store, scopes, scope)
|
compute_expr_scopes(expr, store, scopes, scope)
|
||||||
|
|
@ -534,9 +536,8 @@ fn foo() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
|
let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
|
||||||
let pat_src = source_map
|
let pat_src =
|
||||||
.pat_syntax(*source_map.binding_definitions[resolved.binding()].first().unwrap())
|
source_map.pat_syntax(source_map.patterns_for_binding(resolved.binding())[0]).unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
|
let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
|
||||||
assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
|
assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
|
||||||
|
|
|
||||||
|
|
@ -508,9 +508,9 @@ fn f() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
|
assert_eq!(body.assert_expr_only().bindings.len(), 1, "should have a binding for `B`");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
|
body[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
|
||||||
"B",
|
"B",
|
||||||
"should have a binding for `B`",
|
"should have a binding for `B`",
|
||||||
);
|
);
|
||||||
|
|
@ -566,6 +566,7 @@ const fn f(x: i32) -> i32 {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mtch_arms = body
|
let mtch_arms = body
|
||||||
|
.assert_expr_only()
|
||||||
.exprs
|
.exprs
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|(_, expr)| {
|
.find_map(|(_, expr)| {
|
||||||
|
|
@ -578,10 +579,10 @@ const fn f(x: i32) -> i32 {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let MatchArm { pat, .. } = mtch_arms[1];
|
let MatchArm { pat, .. } = mtch_arms[1];
|
||||||
match body.pats[pat] {
|
match body[pat] {
|
||||||
Pat::Range { start, end } => {
|
Pat::Range { start, end } => {
|
||||||
let hir_start = &body.exprs[start.unwrap()];
|
let hir_start = &body[start.unwrap()];
|
||||||
let hir_end = &body.exprs[end.unwrap()];
|
let hir_end = &body[end.unwrap()];
|
||||||
|
|
||||||
assert!(matches!(hir_start, Expr::Path { .. }));
|
assert!(matches!(hir_start, Expr::Path { .. }));
|
||||||
assert!(matches!(hir_end, Expr::Path { .. }));
|
assert!(matches!(hir_end, Expr::Path { .. }));
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,17 @@ pub struct OffsetOf {
|
||||||
pub struct InlineAsm {
|
pub struct InlineAsm {
|
||||||
pub operands: Box<[(Option<Name>, AsmOperand)]>,
|
pub operands: Box<[(Option<Name>, AsmOperand)]>,
|
||||||
pub options: AsmOptions,
|
pub options: AsmOptions,
|
||||||
|
pub kind: InlineAsmKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum InlineAsmKind {
|
||||||
|
/// `asm!()`.
|
||||||
|
Asm,
|
||||||
|
/// `global_asm!()`.
|
||||||
|
GlobalAsm,
|
||||||
|
/// `naked_asm!()`.
|
||||||
|
NakedAsm,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,8 @@ impl<'a> Ctx<'a> {
|
||||||
ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
|
ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
|
||||||
ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
|
ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
|
||||||
ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
|
ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
|
||||||
|
// FIXME: Handle `global_asm!()`.
|
||||||
|
ast::Item::AsmExpr(_) => return None,
|
||||||
};
|
};
|
||||||
let attrs = RawAttrs::new(self.db, item, self.span_map());
|
let attrs = RawAttrs::new(self.db, item, self.span_map());
|
||||||
self.add_attrs(mod_item.ast_id(), attrs);
|
self.add_attrs(mod_item.ast_id(), attrs);
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@ use a::{c, d::{e}};
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![doc = " another file comment"]
|
#![doc = " another file comment"]
|
||||||
|
|
||||||
// AstId: ExternCrate[5A82, 0]
|
// AstId: ExternCrate[070B, 0]
|
||||||
pub(self) extern crate self as renamed;
|
pub(self) extern crate self as renamed;
|
||||||
|
|
||||||
// AstId: ExternCrate[7E1C, 0]
|
// AstId: ExternCrate[1EA5, 0]
|
||||||
pub(in super) extern crate bli;
|
pub(in super) extern crate bli;
|
||||||
|
|
||||||
// AstId: Use[0000, 0]
|
// AstId: Use[0000, 0]
|
||||||
|
|
@ -78,15 +78,15 @@ extern "C" {
|
||||||
// AstId: ExternBlock[0000, 0]
|
// AstId: ExternBlock[0000, 0]
|
||||||
extern {
|
extern {
|
||||||
#[on_extern_type]
|
#[on_extern_type]
|
||||||
// AstId: TypeAlias[9FDF, 0]
|
// AstId: TypeAlias[A09C, 0]
|
||||||
pub(self) type ExType;
|
pub(self) type ExType;
|
||||||
|
|
||||||
#[on_extern_static]
|
#[on_extern_static]
|
||||||
// AstId: Static[43C1, 0]
|
// AstId: Static[D85E, 0]
|
||||||
pub(self) static EX_STATIC = _;
|
pub(self) static EX_STATIC = _;
|
||||||
|
|
||||||
#[on_extern_fn]
|
#[on_extern_fn]
|
||||||
// AstId: Fn[452D, 0]
|
// AstId: Fn[B240, 0]
|
||||||
pub(self) fn ex_fn;
|
pub(self) fn ex_fn;
|
||||||
}
|
}
|
||||||
"#]],
|
"#]],
|
||||||
|
|
@ -124,20 +124,20 @@ enum E {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
// AstId: Struct[DFF3, 0]
|
// AstId: Struct[ED35, 0]
|
||||||
pub(self) struct Unit;
|
pub(self) struct Unit;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// AstId: Struct[C7A1, 0]
|
// AstId: Struct[A47C, 0]
|
||||||
pub(self) struct Struct { ... }
|
pub(self) struct Struct { ... }
|
||||||
|
|
||||||
// AstId: Struct[DAC2, 0]
|
// AstId: Struct[C8C9, 0]
|
||||||
pub(self) struct Tuple(...);
|
pub(self) struct Tuple(...);
|
||||||
|
|
||||||
// AstId: Union[2DBB, 0]
|
// AstId: Union[2797, 0]
|
||||||
pub(self) union Ize { ... }
|
pub(self) union Ize { ... }
|
||||||
|
|
||||||
// AstId: Enum[7FF8, 0]
|
// AstId: Enum[7D23, 0]
|
||||||
pub(self) enum E { ... }
|
pub(self) enum E { ... }
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
@ -162,18 +162,18 @@ trait Tr: SuperTrait + 'lifetime {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
// AstId: Static[B393, 0]
|
// AstId: Static[F7C1, 0]
|
||||||
pub static ST = _;
|
pub static ST = _;
|
||||||
|
|
||||||
// AstId: Const[B309, 0]
|
// AstId: Const[84BB, 0]
|
||||||
pub(self) const _ = _;
|
pub(self) const _ = _;
|
||||||
|
|
||||||
#[attr]
|
#[attr]
|
||||||
#[inner_attr_in_fn]
|
#[inner_attr_in_fn]
|
||||||
// AstId: Fn[75E3, 0]
|
// AstId: Fn[BE8F, 0]
|
||||||
pub(self) fn f;
|
pub(self) fn f;
|
||||||
|
|
||||||
// AstId: Trait[2998, 0]
|
// AstId: Trait[9320, 0]
|
||||||
pub(self) trait Tr { ... }
|
pub(self) trait Tr { ... }
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
@ -197,16 +197,16 @@ mod outline;
|
||||||
expect![[r##"
|
expect![[r##"
|
||||||
#[doc = " outer"]
|
#[doc = " outer"]
|
||||||
#[doc = " inner"]
|
#[doc = " inner"]
|
||||||
// AstId: Module[CF93, 0]
|
// AstId: Module[03AE, 0]
|
||||||
pub(self) mod inline {
|
pub(self) mod inline {
|
||||||
// AstId: Use[0000, 0]
|
// AstId: Use[0000, 0]
|
||||||
pub(self) use super::*;
|
pub(self) use super::*;
|
||||||
|
|
||||||
// AstId: Fn[1B26, 0]
|
// AstId: Fn[2A78, 0]
|
||||||
pub(self) fn fn_in_module;
|
pub(self) fn fn_in_module;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AstId: Module[8994, 0]
|
// AstId: Module[C08B, 0]
|
||||||
pub(self) mod outline;
|
pub(self) mod outline;
|
||||||
"##]],
|
"##]],
|
||||||
);
|
);
|
||||||
|
|
@ -225,13 +225,13 @@ pub macro m2() {}
|
||||||
m!();
|
m!();
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
// AstId: MacroRules[88CE, 0]
|
// AstId: MacroRules[7E68, 0]
|
||||||
macro_rules! m { ... }
|
macro_rules! m { ... }
|
||||||
|
|
||||||
// AstId: MacroDef[DC34, 0]
|
// AstId: MacroDef[1C1E, 0]
|
||||||
pub macro m2 { ... }
|
pub macro m2 { ... }
|
||||||
|
|
||||||
// AstId: MacroCall[612F, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
|
// AstId: MacroCall[7E68, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
|
||||||
m!(...);
|
m!(...);
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
@ -244,7 +244,7 @@ fn pub_self() {
|
||||||
pub(self) struct S;
|
pub(self) struct S;
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
// AstId: Struct[42E2, 0]
|
// AstId: Struct[5024, 0]
|
||||||
pub(self) struct S;
|
pub(self) struct S;
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,19 @@ fn test_asm_expand() {
|
||||||
r#"
|
r#"
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
macro_rules! asm {() => {}}
|
macro_rules! asm {() => {}}
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! global_asm {() => {}}
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! naked_asm {() => {}}
|
||||||
|
|
||||||
|
global_asm! {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
extern "C" fn foo() {
|
||||||
|
naked_asm!("");
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let i: u64 = 3;
|
let i: u64 = 3;
|
||||||
|
|
@ -45,6 +58,17 @@ fn main() {
|
||||||
expect![[r##"
|
expect![[r##"
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
macro_rules! asm {() => {}}
|
macro_rules! asm {() => {}}
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! global_asm {() => {}}
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! naked_asm {() => {}}
|
||||||
|
|
||||||
|
builtin #global_asm ("")
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
extern "C" fn foo() {
|
||||||
|
builtin #naked_asm ("");
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let i: u64 = 3;
|
let i: u64 = 3;
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ macro_rules! f {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct#0:MacroRules[8C8E, 0]@58..64#14336# MyTraitMap2#0:MacroCall[D499, 0]@31..42#ROOT2024# {#0:MacroRules[8C8E, 0]@72..73#14336#
|
struct#0:MacroRules[BE8F, 0]@58..64#14336# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#14336#
|
||||||
map#0:MacroRules[8C8E, 0]@86..89#14336#:#0:MacroRules[8C8E, 0]@89..90#14336# #0:MacroRules[8C8E, 0]@89..90#14336#::#0:MacroRules[8C8E, 0]@91..93#14336#std#0:MacroRules[8C8E, 0]@93..96#14336#::#0:MacroRules[8C8E, 0]@96..98#14336#collections#0:MacroRules[8C8E, 0]@98..109#14336#::#0:MacroRules[8C8E, 0]@109..111#14336#HashSet#0:MacroRules[8C8E, 0]@111..118#14336#<#0:MacroRules[8C8E, 0]@118..119#14336#(#0:MacroRules[8C8E, 0]@119..120#14336#)#0:MacroRules[8C8E, 0]@120..121#14336#>#0:MacroRules[8C8E, 0]@121..122#14336#,#0:MacroRules[8C8E, 0]@122..123#14336#
|
map#0:MacroRules[BE8F, 0]@86..89#14336#:#0:MacroRules[BE8F, 0]@89..90#14336# #0:MacroRules[BE8F, 0]@89..90#14336#::#0:MacroRules[BE8F, 0]@91..93#14336#std#0:MacroRules[BE8F, 0]@93..96#14336#::#0:MacroRules[BE8F, 0]@96..98#14336#collections#0:MacroRules[BE8F, 0]@98..109#14336#::#0:MacroRules[BE8F, 0]@109..111#14336#HashSet#0:MacroRules[BE8F, 0]@111..118#14336#<#0:MacroRules[BE8F, 0]@118..119#14336#(#0:MacroRules[BE8F, 0]@119..120#14336#)#0:MacroRules[BE8F, 0]@120..121#14336#>#0:MacroRules[BE8F, 0]@121..122#14336#,#0:MacroRules[BE8F, 0]@122..123#14336#
|
||||||
}#0:MacroRules[8C8E, 0]@132..133#14336#
|
}#0:MacroRules[BE8F, 0]@132..133#14336#
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -75,12 +75,12 @@ macro_rules! f {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn#0:MacroCall[D499, 0]@30..32#ROOT2024# main#0:MacroCall[D499, 0]@33..37#ROOT2024#(#0:MacroCall[D499, 0]@37..38#ROOT2024#)#0:MacroCall[D499, 0]@38..39#ROOT2024# {#0:MacroCall[D499, 0]@40..41#ROOT2024#
|
fn#0:MacroCall[BE8F, 0]@30..32#ROOT2024# main#0:MacroCall[BE8F, 0]@33..37#ROOT2024#(#0:MacroCall[BE8F, 0]@37..38#ROOT2024#)#0:MacroCall[BE8F, 0]@38..39#ROOT2024# {#0:MacroCall[BE8F, 0]@40..41#ROOT2024#
|
||||||
1#0:MacroCall[D499, 0]@50..51#ROOT2024#;#0:MacroCall[D499, 0]@51..52#ROOT2024#
|
1#0:MacroCall[BE8F, 0]@50..51#ROOT2024#;#0:MacroCall[BE8F, 0]@51..52#ROOT2024#
|
||||||
1.0#0:MacroCall[D499, 0]@61..64#ROOT2024#;#0:MacroCall[D499, 0]@64..65#ROOT2024#
|
1.0#0:MacroCall[BE8F, 0]@61..64#ROOT2024#;#0:MacroCall[BE8F, 0]@64..65#ROOT2024#
|
||||||
(#0:MacroCall[D499, 0]@74..75#ROOT2024#(#0:MacroCall[D499, 0]@75..76#ROOT2024#1#0:MacroCall[D499, 0]@76..77#ROOT2024#,#0:MacroCall[D499, 0]@77..78#ROOT2024# )#0:MacroCall[D499, 0]@78..79#ROOT2024#,#0:MacroCall[D499, 0]@79..80#ROOT2024# )#0:MacroCall[D499, 0]@80..81#ROOT2024#.#0:MacroCall[D499, 0]@81..82#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#.#0:MacroCall[D499, 0]@82..85#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#;#0:MacroCall[D499, 0]@85..86#ROOT2024#
|
(#0:MacroCall[BE8F, 0]@74..75#ROOT2024#(#0:MacroCall[BE8F, 0]@75..76#ROOT2024#1#0:MacroCall[BE8F, 0]@76..77#ROOT2024#,#0:MacroCall[BE8F, 0]@77..78#ROOT2024# )#0:MacroCall[BE8F, 0]@78..79#ROOT2024#,#0:MacroCall[BE8F, 0]@79..80#ROOT2024# )#0:MacroCall[BE8F, 0]@80..81#ROOT2024#.#0:MacroCall[BE8F, 0]@81..82#ROOT2024#0#0:MacroCall[BE8F, 0]@82..85#ROOT2024#.#0:MacroCall[BE8F, 0]@82..85#ROOT2024#0#0:MacroCall[BE8F, 0]@82..85#ROOT2024#;#0:MacroCall[BE8F, 0]@85..86#ROOT2024#
|
||||||
let#0:MacroCall[D499, 0]@95..98#ROOT2024# x#0:MacroCall[D499, 0]@99..100#ROOT2024# =#0:MacroCall[D499, 0]@101..102#ROOT2024# 1#0:MacroCall[D499, 0]@103..104#ROOT2024#;#0:MacroCall[D499, 0]@104..105#ROOT2024#
|
let#0:MacroCall[BE8F, 0]@95..98#ROOT2024# x#0:MacroCall[BE8F, 0]@99..100#ROOT2024# =#0:MacroCall[BE8F, 0]@101..102#ROOT2024# 1#0:MacroCall[BE8F, 0]@103..104#ROOT2024#;#0:MacroCall[BE8F, 0]@104..105#ROOT2024#
|
||||||
}#0:MacroCall[D499, 0]@110..111#ROOT2024#
|
}#0:MacroCall[BE8F, 0]@110..111#ROOT2024#
|
||||||
|
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
|
|
@ -171,7 +171,7 @@ fn main(foo: ()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(foo: ()) {
|
fn main(foo: ()) {
|
||||||
/* error: unresolved macro unresolved */"helloworld!"#0:Fn[B9C7, 0]@236..321#ROOT2024#;
|
/* error: unresolved macro unresolved */"helloworld!"#0:Fn[15AE, 0]@236..321#ROOT2024#;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ macro_rules! mk_struct {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod foo;
|
mod foo;
|
||||||
|
|
||||||
struct#1:MacroRules[E572, 0]@59..65#14336# Foo#0:MacroCall[BDD3, 0]@32..35#ROOT2024#(#1:MacroRules[E572, 0]@70..71#14336#u32#0:MacroCall[BDD3, 0]@41..44#ROOT2024#)#1:MacroRules[E572, 0]@74..75#14336#;#1:MacroRules[E572, 0]@75..76#14336#
|
struct#1:MacroRules[DB0C, 0]@59..65#14336# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#14336#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#14336#;#1:MacroRules[DB0C, 0]@75..76#14336#
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,14 @@ use base_db::RootQueryDb;
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
|
AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
|
||||||
|
builtin::quote::quote,
|
||||||
db::ExpandDatabase,
|
db::ExpandDatabase,
|
||||||
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
|
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
|
||||||
span_map::SpanMapRef,
|
span_map::SpanMapRef,
|
||||||
};
|
};
|
||||||
use intern::Symbol;
|
use intern::{Symbol, sym};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use span::{Edition, Span};
|
use span::{Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext};
|
||||||
use stdx::{format_to, format_to_acc};
|
use stdx::{format_to, format_to_acc};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode, AstPtr,
|
AstNode, AstPtr,
|
||||||
|
|
@ -34,7 +35,9 @@ use syntax::{
|
||||||
SyntaxNode, T,
|
SyntaxNode, T,
|
||||||
ast::{self, edit::IndentLevel},
|
ast::{self, edit::IndentLevel},
|
||||||
};
|
};
|
||||||
|
use syntax_bridge::token_tree_to_syntax_node;
|
||||||
use test_fixture::WithFixture;
|
use test_fixture::WithFixture;
|
||||||
|
use tt::{TextRange, TextSize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AdtId, Lookup, ModuleDefId,
|
AdtId, Lookup, ModuleDefId,
|
||||||
|
|
@ -386,3 +389,38 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
|
||||||
other.type_id() == TypeId::of::<Self>()
|
other.type_id() == TypeId::of::<Self>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_20171() {
|
||||||
|
// This really isn't the appropriate place to put this test, but it's convenient with access to `quote!`.
|
||||||
|
let span = Span {
|
||||||
|
range: TextRange::empty(TextSize::new(0)),
|
||||||
|
anchor: SpanAnchor {
|
||||||
|
file_id: span::EditionedFileId::current_edition(span::FileId::from_raw(0)),
|
||||||
|
ast_id: ROOT_ERASED_FILE_AST_ID,
|
||||||
|
},
|
||||||
|
ctx: SyntaxContext::root(Edition::CURRENT),
|
||||||
|
};
|
||||||
|
let close_brace = tt::Punct { char: '}', spacing: tt::Spacing::Alone, span };
|
||||||
|
let dotdot1 = tt::Punct { char: '.', spacing: tt::Spacing::Joint, span };
|
||||||
|
let dotdot2 = tt::Punct { char: '.', spacing: tt::Spacing::Alone, span };
|
||||||
|
let dollar_crate = sym::dollar_crate;
|
||||||
|
let tt = quote! {
|
||||||
|
span => {
|
||||||
|
if !((matches!(
|
||||||
|
drive_parser(&mut parser, data, false),
|
||||||
|
Err(TarParserError::CorruptField {
|
||||||
|
field: CorruptFieldContext::PaxKvLength,
|
||||||
|
error: GeneralParseError::ParseInt(ParseIntError { #dotdot1 #dotdot2 })
|
||||||
|
})
|
||||||
|
#close_brace ))) {
|
||||||
|
#dollar_crate::panic::panic_2021!();
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
token_tree_to_syntax_node(
|
||||||
|
&tt,
|
||||||
|
syntax_bridge::TopEntryPoint::MacroStmts,
|
||||||
|
&mut |_| Edition::CURRENT,
|
||||||
|
Edition::CURRENT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,9 +181,9 @@ fn foo(&self) {
|
||||||
self.0. 1;
|
self.0. 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn#0:Fn[4D85, 0]@45..47#ROOT2024# foo#0:Fn[4D85, 0]@48..51#ROOT2024#(#0:Fn[4D85, 0]@51..52#ROOT2024#�:Fn[4D85, 0]@52..53#ROOT2024#self#0:Fn[4D85, 0]@53..57#ROOT2024# )#0:Fn[4D85, 0]@57..58#ROOT2024# {#0:Fn[4D85, 0]@59..60#ROOT2024#
|
fn#0:Fn[8A31, 0]@45..47#ROOT2024# foo#0:Fn[8A31, 0]@48..51#ROOT2024#(#0:Fn[8A31, 0]@51..52#ROOT2024#�:Fn[8A31, 0]@52..53#ROOT2024#self#0:Fn[8A31, 0]@53..57#ROOT2024# )#0:Fn[8A31, 0]@57..58#ROOT2024# {#0:Fn[8A31, 0]@59..60#ROOT2024#
|
||||||
self#0:Fn[4D85, 0]@65..69#ROOT2024# .#0:Fn[4D85, 0]@69..70#ROOT2024#0#0:Fn[4D85, 0]@70..71#ROOT2024#.#0:Fn[4D85, 0]@71..72#ROOT2024#1#0:Fn[4D85, 0]@73..74#ROOT2024#;#0:Fn[4D85, 0]@74..75#ROOT2024#
|
self#0:Fn[8A31, 0]@65..69#ROOT2024# .#0:Fn[8A31, 0]@69..70#ROOT2024#0#0:Fn[8A31, 0]@70..71#ROOT2024#.#0:Fn[8A31, 0]@71..72#ROOT2024#1#0:Fn[8A31, 0]@73..74#ROOT2024#;#0:Fn[8A31, 0]@74..75#ROOT2024#
|
||||||
}#0:Fn[4D85, 0]@76..77#ROOT2024#"#]],
|
}#0:Fn[8A31, 0]@76..77#ROOT2024#"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -373,19 +373,14 @@ pub fn crate_def_map(db: &dyn DefDatabase, crate_id: Crate) -> &DefMap {
|
||||||
crate_local_def_map(db, crate_id).def_map(db)
|
crate_local_def_map(db, crate_id).def_map(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_lifetimes)]
|
#[salsa_macros::tracked]
|
||||||
mod __ {
|
pub(crate) struct DefMapPair<'db> {
|
||||||
use super::*;
|
|
||||||
#[salsa_macros::tracked]
|
|
||||||
pub(crate) struct DefMapPair<'db> {
|
|
||||||
#[tracked]
|
#[tracked]
|
||||||
#[returns(ref)]
|
#[returns(ref)]
|
||||||
pub(crate) def_map: DefMap,
|
pub(crate) def_map: DefMap,
|
||||||
#[returns(ref)]
|
#[returns(ref)]
|
||||||
pub(crate) local: LocalDefMap,
|
pub(crate) local: LocalDefMap,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub(crate) use __::DefMapPair;
|
|
||||||
|
|
||||||
#[salsa_macros::tracked(returns(ref))]
|
#[salsa_macros::tracked(returns(ref))]
|
||||||
pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> {
|
pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> {
|
||||||
|
|
|
||||||
|
|
@ -1052,17 +1052,6 @@ impl<'db> Scope<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolver_for_expr(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
expr_id: ExprId,
|
|
||||||
) -> Resolver<'_> {
|
|
||||||
let r = owner.resolver(db);
|
|
||||||
let scopes = db.expr_scopes(owner);
|
|
||||||
let scope_id = scopes.scope_for(expr_id);
|
|
||||||
resolver_for_scope_(db, scopes, scope_id, r, owner)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolver_for_scope(
|
pub fn resolver_for_scope(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
owner: DefWithBodyId,
|
owner: DefWithBodyId,
|
||||||
|
|
|
||||||
|
|
@ -779,14 +779,10 @@ impl VariantFields {
|
||||||
Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
|
Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
|
||||||
Arc::new(source_map),
|
Arc::new(source_map),
|
||||||
),
|
),
|
||||||
None => (
|
None => {
|
||||||
Arc::new(VariantFields {
|
let (store, source_map) = ExpressionStore::empty_singleton();
|
||||||
fields: Arena::default(),
|
(Arc::new(VariantFields { fields: Arena::default(), store, shape }), source_map)
|
||||||
store: ExpressionStore::empty_singleton(),
|
}
|
||||||
shape,
|
|
||||||
}),
|
|
||||||
ExpressionStoreSourceMap::empty_singleton(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -878,7 +874,7 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
Err(cfg) => {
|
Err(cfg) => {
|
||||||
col.source_map.diagnostics.push(
|
col.store.diagnostics.push(
|
||||||
crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
|
crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
|
||||||
node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
|
node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
|
||||||
cfg,
|
cfg,
|
||||||
|
|
@ -891,9 +887,9 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
|
||||||
if !has_fields {
|
if !has_fields {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let store = col.store.finish();
|
let (store, source_map) = col.store.finish();
|
||||||
arena.shrink_to_fit();
|
arena.shrink_to_fit();
|
||||||
Some((arena, store, col.source_map))
|
Some((arena, store, source_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
|
@ -980,7 +976,7 @@ impl EnumVariants {
|
||||||
if !matches!(variant.shape, FieldsShape::Unit) {
|
if !matches!(variant.shape, FieldsShape::Unit) {
|
||||||
let body = db.body(v.into());
|
let body = db.body(v.into());
|
||||||
// A variant with explicit discriminant
|
// A variant with explicit discriminant
|
||||||
if body.exprs[body.body_expr] != crate::hir::Expr::Missing {
|
if !matches!(body[body.body_expr], crate::hir::Expr::Missing) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,8 @@ register_builtin! {
|
||||||
(assert, Assert) => assert_expand,
|
(assert, Assert) => assert_expand,
|
||||||
(stringify, Stringify) => stringify_expand,
|
(stringify, Stringify) => stringify_expand,
|
||||||
(asm, Asm) => asm_expand,
|
(asm, Asm) => asm_expand,
|
||||||
(global_asm, GlobalAsm) => asm_expand,
|
(global_asm, GlobalAsm) => global_asm_expand,
|
||||||
(naked_asm, NakedAsm) => asm_expand,
|
(naked_asm, NakedAsm) => naked_asm_expand,
|
||||||
(cfg, Cfg) => cfg_expand,
|
(cfg, Cfg) => cfg_expand,
|
||||||
(core_panic, CorePanic) => panic_expand,
|
(core_panic, CorePanic) => panic_expand,
|
||||||
(std_panic, StdPanic) => panic_expand,
|
(std_panic, StdPanic) => panic_expand,
|
||||||
|
|
@ -325,6 +325,36 @@ fn asm_expand(
|
||||||
ExpandResult::ok(expanded)
|
ExpandResult::ok(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn global_asm_expand(
|
||||||
|
_db: &dyn ExpandDatabase,
|
||||||
|
_id: MacroCallId,
|
||||||
|
tt: &tt::TopSubtree,
|
||||||
|
span: Span,
|
||||||
|
) -> ExpandResult<tt::TopSubtree> {
|
||||||
|
let mut tt = tt.clone();
|
||||||
|
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||||
|
let pound = mk_pound(span);
|
||||||
|
let expanded = quote! {span =>
|
||||||
|
builtin #pound global_asm #tt
|
||||||
|
};
|
||||||
|
ExpandResult::ok(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn naked_asm_expand(
|
||||||
|
_db: &dyn ExpandDatabase,
|
||||||
|
_id: MacroCallId,
|
||||||
|
tt: &tt::TopSubtree,
|
||||||
|
span: Span,
|
||||||
|
) -> ExpandResult<tt::TopSubtree> {
|
||||||
|
let mut tt = tt.clone();
|
||||||
|
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||||
|
let pound = mk_pound(span);
|
||||||
|
let expanded = quote! {span =>
|
||||||
|
builtin #pound naked_asm #tt
|
||||||
|
};
|
||||||
|
ExpandResult::ok(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
fn cfg_expand(
|
fn cfg_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ macro_rules! quote {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(super) use quote;
|
pub use quote;
|
||||||
|
|
||||||
pub trait ToTokenTree {
|
pub trait ToTokenTree {
|
||||||
fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder);
|
fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder);
|
||||||
|
|
|
||||||
|
|
@ -179,10 +179,9 @@ impl Name {
|
||||||
self.symbol.as_str()
|
self.symbol.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn display<'a>(
|
pub fn display<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
db: &dyn salsa::Database,
|
db: &dyn crate::db::ExpandDatabase,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
) -> impl fmt::Display + 'a {
|
) -> impl fmt::Display + 'a {
|
||||||
_ = db;
|
_ = db;
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,7 @@ pub(crate) fn const_eval_discriminant_variant(
|
||||||
let def = variant_id.into();
|
let def = variant_id.into();
|
||||||
let body = db.body(def);
|
let body = db.body(def);
|
||||||
let loc = variant_id.lookup(db);
|
let loc = variant_id.lookup(db);
|
||||||
if body.exprs[body.body_expr] == Expr::Missing {
|
if matches!(body[body.body_expr], Expr::Missing) {
|
||||||
let prev_idx = loc.index.checked_sub(1);
|
let prev_idx = loc.index.checked_sub(1);
|
||||||
let value = match prev_idx {
|
let value = match prev_idx {
|
||||||
Some(prev_idx) => {
|
Some(prev_idx) => {
|
||||||
|
|
@ -334,7 +334,7 @@ pub(crate) fn eval_to_const(
|
||||||
// Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
|
// Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
|
||||||
return unknown_const(infer[expr].clone());
|
return unknown_const(infer[expr].clone());
|
||||||
}
|
}
|
||||||
if let Expr::Path(p) = &ctx.body.exprs[expr] {
|
if let Expr::Path(p) = &ctx.body[expr] {
|
||||||
let resolver = &ctx.resolver;
|
let resolver = &ctx.resolver;
|
||||||
if let Some(c) =
|
if let Some(c) =
|
||||||
path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
|
path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
|
||||||
|
|
|
||||||
|
|
@ -273,8 +273,9 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
|
||||||
|
|
||||||
#[salsa::invoke(crate::variance::variances_of)]
|
#[salsa::invoke(crate::variance::variances_of)]
|
||||||
#[salsa::cycle(
|
#[salsa::cycle(
|
||||||
cycle_fn = crate::variance::variances_of_cycle_fn,
|
// cycle_fn = crate::variance::variances_of_cycle_fn,
|
||||||
cycle_initial = crate::variance::variances_of_cycle_initial,
|
// cycle_initial = crate::variance::variances_of_cycle_initial,
|
||||||
|
cycle_result = crate::variance::variances_of_cycle_initial,
|
||||||
)]
|
)]
|
||||||
fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>;
|
fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,11 +226,10 @@ impl<'a> DeclValidator<'a> {
|
||||||
let body = self.db.body(func.into());
|
let body = self.db.body(func.into());
|
||||||
let edition = self.edition(func);
|
let edition = self.edition(func);
|
||||||
let mut pats_replacements = body
|
let mut pats_replacements = body
|
||||||
.pats
|
.pats()
|
||||||
.iter()
|
|
||||||
.filter_map(|(pat_id, pat)| match pat {
|
.filter_map(|(pat_id, pat)| match pat {
|
||||||
Pat::Bind { id, .. } => {
|
Pat::Bind { id, .. } => {
|
||||||
let bind_name = &body.bindings[*id].name;
|
let bind_name = &body[*id].name;
|
||||||
let mut suggested_text = to_lower_snake_case(bind_name.as_str())?;
|
let mut suggested_text = to_lower_snake_case(bind_name.as_str())?;
|
||||||
if is_raw_identifier(&suggested_text, edition) {
|
if is_raw_identifier(&suggested_text, edition) {
|
||||||
suggested_text.insert_str(0, "r#");
|
suggested_text.insert_str(0, "r#");
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ impl ExprValidator {
|
||||||
self.check_for_trailing_return(body.body_expr, &body);
|
self.check_for_trailing_return(body.body_expr, &body);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, expr) in body.exprs.iter() {
|
for (id, expr) in body.exprs() {
|
||||||
if let Some((variant, missed_fields, true)) =
|
if let Some((variant, missed_fields, true)) =
|
||||||
record_literal_missing_fields(db, &self.infer, id, expr)
|
record_literal_missing_fields(db, &self.infer, id, expr)
|
||||||
{
|
{
|
||||||
|
|
@ -132,7 +132,7 @@ impl ExprValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, pat) in body.pats.iter() {
|
for (id, pat) in body.pats() {
|
||||||
if let Some((variant, missed_fields, true)) =
|
if let Some((variant, missed_fields, true)) =
|
||||||
record_pattern_missing_fields(db, &self.infer, id, pat)
|
record_pattern_missing_fields(db, &self.infer, id, pat)
|
||||||
{
|
{
|
||||||
|
|
@ -389,7 +389,7 @@ impl ExprValidator {
|
||||||
if !self.validate_lints {
|
if !self.validate_lints {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match &body.exprs[body_expr] {
|
match &body[body_expr] {
|
||||||
Expr::Block { statements, tail, .. } => {
|
Expr::Block { statements, tail, .. } => {
|
||||||
let last_stmt = tail.or_else(|| match statements.last()? {
|
let last_stmt = tail.or_else(|| match statements.last()? {
|
||||||
Statement::Expr { expr, .. } => Some(*expr),
|
Statement::Expr { expr, .. } => Some(*expr),
|
||||||
|
|
@ -428,7 +428,7 @@ impl ExprValidator {
|
||||||
if else_branch.is_none() {
|
if else_branch.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Expr::Block { statements, tail, .. } = &self.body.exprs[*then_branch] {
|
if let Expr::Block { statements, tail, .. } = &self.body[*then_branch] {
|
||||||
let last_then_expr = tail.or_else(|| match statements.last()? {
|
let last_then_expr = tail.or_else(|| match statements.last()? {
|
||||||
Statement::Expr { expr, .. } => Some(*expr),
|
Statement::Expr { expr, .. } => Some(*expr),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ impl<'a> PatCtxt<'a> {
|
||||||
hir_def::hir::Pat::Bind { id, subpat, .. } => {
|
hir_def::hir::Pat::Bind { id, subpat, .. } => {
|
||||||
let bm = self.infer.binding_modes[pat];
|
let bm = self.infer.binding_modes[pat];
|
||||||
ty = &self.infer[id];
|
ty = &self.infer[id];
|
||||||
let name = &self.body.bindings[id].name;
|
let name = &self.body[id].name;
|
||||||
match (bm, ty.kind(Interner)) {
|
match (bm, ty.kind(Interner)) {
|
||||||
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
|
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
|
||||||
(BindingMode::Ref(_), _) => {
|
(BindingMode::Ref(_), _) => {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
|
AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
|
||||||
expr_store::{Body, path::Path},
|
expr_store::{Body, path::Path},
|
||||||
hir::{AsmOperand, Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
|
hir::{AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp},
|
||||||
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
||||||
signatures::StaticFlags,
|
signatures::StaticFlags,
|
||||||
type_ref::Rawness,
|
type_ref::Rawness,
|
||||||
|
|
@ -217,7 +217,7 @@ impl<'db> UnsafeVisitor<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_pat(&mut self, current: PatId) {
|
fn walk_pat(&mut self, current: PatId) {
|
||||||
let pat = &self.body.pats[current];
|
let pat = &self.body[current];
|
||||||
|
|
||||||
if self.inside_union_destructure {
|
if self.inside_union_destructure {
|
||||||
match pat {
|
match pat {
|
||||||
|
|
@ -264,7 +264,7 @@ impl<'db> UnsafeVisitor<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_expr(&mut self, current: ExprId) {
|
fn walk_expr(&mut self, current: ExprId) {
|
||||||
let expr = &self.body.exprs[current];
|
let expr = &self.body[current];
|
||||||
let inside_assignment = mem::replace(&mut self.inside_assignment, false);
|
let inside_assignment = mem::replace(&mut self.inside_assignment, false);
|
||||||
match expr {
|
match expr {
|
||||||
&Expr::Call { callee, .. } => {
|
&Expr::Call { callee, .. } => {
|
||||||
|
|
@ -284,7 +284,7 @@ impl<'db> UnsafeVisitor<'db> {
|
||||||
self.resolver.reset_to_guard(guard);
|
self.resolver.reset_to_guard(guard);
|
||||||
}
|
}
|
||||||
Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
|
Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
|
||||||
match self.body.exprs[*expr] {
|
match self.body[*expr] {
|
||||||
// Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
|
// Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
|
||||||
// see https://github.com/rust-lang/rust/pull/125834.
|
// see https://github.com/rust-lang/rust/pull/125834.
|
||||||
Expr::Path(_) => return,
|
Expr::Path(_) => return,
|
||||||
|
|
@ -315,7 +315,12 @@ impl<'db> UnsafeVisitor<'db> {
|
||||||
self.inside_assignment = old_inside_assignment;
|
self.inside_assignment = old_inside_assignment;
|
||||||
}
|
}
|
||||||
Expr::InlineAsm(asm) => {
|
Expr::InlineAsm(asm) => {
|
||||||
|
if asm.kind == InlineAsmKind::Asm {
|
||||||
|
// `naked_asm!()` requires `unsafe` on the attribute (`#[unsafe(naked)]`),
|
||||||
|
// and `global_asm!()` doesn't require it at all.
|
||||||
self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
|
self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
|
||||||
|
}
|
||||||
|
|
||||||
asm.operands.iter().for_each(|(_, op)| match op {
|
asm.operands.iter().for_each(|(_, op)| match op {
|
||||||
AsmOperand::In { expr, .. }
|
AsmOperand::In { expr, .. }
|
||||||
| AsmOperand::Out { expr: Some(expr), .. }
|
| AsmOperand::Out { expr: Some(expr), .. }
|
||||||
|
|
|
||||||
|
|
@ -795,6 +795,14 @@ fn render_const_scalar(
|
||||||
let Some(bytes) = memory_map.get(addr, size_one * count) else {
|
let Some(bytes) = memory_map.get(addr, size_one * count) else {
|
||||||
return f.write_str("<ref-data-not-available>");
|
return f.write_str("<ref-data-not-available>");
|
||||||
};
|
};
|
||||||
|
let expected_len = count * size_one;
|
||||||
|
if bytes.len() < expected_len {
|
||||||
|
never!(
|
||||||
|
"Memory map size is too small. Expected {expected_len}, got {}",
|
||||||
|
bytes.len(),
|
||||||
|
);
|
||||||
|
return f.write_str("<layout-error>");
|
||||||
|
}
|
||||||
f.write_str("&[")?;
|
f.write_str("&[")?;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
|
|
@ -2328,6 +2336,7 @@ impl HirDisplayWithExpressionStore for TypeBound {
|
||||||
store[*path].hir_fmt(f, store)
|
store[*path].hir_fmt(f, store)
|
||||||
}
|
}
|
||||||
TypeBound::Use(args) => {
|
TypeBound::Use(args) => {
|
||||||
|
write!(f, "use<")?;
|
||||||
let edition = f.edition();
|
let edition = f.edition();
|
||||||
let last = args.len().saturating_sub(1);
|
let last = args.len().saturating_sub(1);
|
||||||
for (idx, arg) in args.iter().enumerate() {
|
for (idx, arg) in args.iter().enumerate() {
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@ impl InferenceContext<'_> {
|
||||||
fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
|
fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
|
||||||
let mut r = Mutability::Not;
|
let mut r = Mutability::Not;
|
||||||
self.body.walk_bindings_in_pat(pat, |b| {
|
self.body.walk_bindings_in_pat(pat, |b| {
|
||||||
if self.body.bindings[b].mode == BindingAnnotation::RefMut {
|
if self.body[b].mode == BindingAnnotation::RefMut {
|
||||||
r = Mutability::Mut;
|
r = Mutability::Mut;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,7 @@ impl InferenceContext<'_> {
|
||||||
expected: &Ty,
|
expected: &Ty,
|
||||||
decl: Option<DeclContext>,
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let Binding { mode, .. } = self.body.bindings[binding];
|
let Binding { mode, .. } = self.body[binding];
|
||||||
let mode = if mode == BindingAnnotation::Unannotated {
|
let mode = if mode == BindingAnnotation::Unannotated {
|
||||||
default_bm
|
default_bm
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -639,7 +639,7 @@ impl InferenceContext<'_> {
|
||||||
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
|
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
|
||||||
let mut res = false;
|
let mut res = false;
|
||||||
body.walk_pats(pat_id, &mut |pat| {
|
body.walk_pats(pat_id, &mut |pat| {
|
||||||
res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
|
res |= matches!(body[pat], Pat::Bind { id, .. } if body[id].mode == BindingAnnotation::Ref);
|
||||||
});
|
});
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use base_db::Crate;
|
use base_db::Crate;
|
||||||
use hir_def::layout::TargetDataLayout;
|
use hir_def::layout::TargetDataLayout;
|
||||||
use rustc_abi::{AlignFromBytesError, TargetDataLayoutErrors, AddressSpace};
|
use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::db::HirDatabase;
|
use crate::db::HirDatabase;
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,7 @@ fn eval_expr(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let hir_body = db.body(function_id.into());
|
let hir_body = db.body(function_id.into());
|
||||||
let b = hir_body
|
let b = hir_body
|
||||||
.bindings
|
.bindings()
|
||||||
.iter()
|
|
||||||
.find(|x| x.1.name.display_no_db(file_id.edition(&db)).to_smolstr() == "goal")
|
.find(|x| x.1.name.display_no_db(file_id.edition(&db)).to_smolstr() == "goal")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0;
|
.0;
|
||||||
|
|
|
||||||
|
|
@ -1018,8 +1018,12 @@ fn check_generic_args_len(
|
||||||
}
|
}
|
||||||
|
|
||||||
let lifetime_args_len = def_generics.len_lifetimes_self();
|
let lifetime_args_len = def_generics.len_lifetimes_self();
|
||||||
if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics {
|
if provided_lifetimes_count == 0
|
||||||
// In generic associated types, we never allow inferring the lifetimes.
|
&& lifetime_args_len > 0
|
||||||
|
&& (!lowering_assoc_type_generics || infer_args)
|
||||||
|
{
|
||||||
|
// In generic associated types, we never allow inferring the lifetimes, but only in type context, that is
|
||||||
|
// when `infer_args == false`. In expression/pattern context we always allow inferring them, even for GATs.
|
||||||
match lifetime_elision {
|
match lifetime_elision {
|
||||||
&LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
|
&LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
|
||||||
ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
|
ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
|
||||||
|
|
|
||||||
|
|
@ -1212,10 +1212,9 @@ impl MirSpan {
|
||||||
match *self {
|
match *self {
|
||||||
MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
|
MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
|
||||||
// FIXME: Figure out if this is correct wrt. match ergonomics.
|
// FIXME: Figure out if this is correct wrt. match ergonomics.
|
||||||
MirSpan::BindingId(binding) => matches!(
|
MirSpan::BindingId(binding) => {
|
||||||
body.bindings[binding].mode,
|
matches!(body[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
|
||||||
BindingAnnotation::Ref | BindingAnnotation::RefMut
|
}
|
||||||
),
|
|
||||||
MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
|
MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ use syntax::{SyntaxNodePtr, TextRange};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, Interner,
|
AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId,
|
||||||
MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
||||||
consteval::{ConstEvalError, intern_const_scalar, try_const_usize},
|
consteval::{ConstEvalError, intern_const_scalar, try_const_usize},
|
||||||
db::{HirDatabase, InternedClosure},
|
db::{HirDatabase, InternedClosure},
|
||||||
display::{ClosureStyle, DisplayTarget, HirDisplay},
|
display::{ClosureStyle, DisplayTarget, HirDisplay},
|
||||||
|
|
@ -2195,7 +2195,7 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chalk_ir::TyKind::Array(inner, len) => {
|
TyKind::Array(inner, len) => {
|
||||||
let len = match try_const_usize(this.db, len) {
|
let len = match try_const_usize(this.db, len) {
|
||||||
Some(it) => it as usize,
|
Some(it) => it as usize,
|
||||||
None => not_supported!("non evaluatable array len in patching addresses"),
|
None => not_supported!("non evaluatable array len in patching addresses"),
|
||||||
|
|
@ -2213,7 +2213,7 @@ impl Evaluator<'_> {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chalk_ir::TyKind::Tuple(_, subst) => {
|
TyKind::Tuple(_, subst) => {
|
||||||
let layout = this.layout(ty)?;
|
let layout = this.layout(ty)?;
|
||||||
for (id, ty) in subst.iter(Interner).enumerate() {
|
for (id, ty) in subst.iter(Interner).enumerate() {
|
||||||
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
|
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
|
||||||
|
|
@ -2229,7 +2229,7 @@ impl Evaluator<'_> {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
|
TyKind::Adt(adt, subst) => match adt.0 {
|
||||||
AdtId::StructId(s) => {
|
AdtId::StructId(s) => {
|
||||||
let data = s.fields(this.db);
|
let data = s.fields(this.db);
|
||||||
let layout = this.layout(ty)?;
|
let layout = this.layout(ty)?;
|
||||||
|
|
@ -2280,6 +2280,10 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
AdtId::UnionId(_) => (),
|
AdtId::UnionId(_) => (),
|
||||||
},
|
},
|
||||||
|
TyKind::Alias(AliasTy::Projection(proj)) => {
|
||||||
|
let ty = this.db.normalize_projection(proj.clone(), this.trait_env.clone());
|
||||||
|
rec(this, bytes, &ty, locals, mm, stack_depth_limit - 1)?;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
current: BasicBlockId,
|
current: BasicBlockId,
|
||||||
) -> Result<Option<(Operand, BasicBlockId)>> {
|
) -> Result<Option<(Operand, BasicBlockId)>> {
|
||||||
if !self.has_adjustments(expr_id) {
|
if !self.has_adjustments(expr_id) {
|
||||||
if let Expr::Literal(l) = &self.body.exprs[expr_id] {
|
if let Expr::Literal(l) = &self.body[expr_id] {
|
||||||
let ty = self.expr_ty_without_adjust(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
|
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +411,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
place: Place,
|
place: Place,
|
||||||
mut current: BasicBlockId,
|
mut current: BasicBlockId,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
match &self.body.exprs[expr_id] {
|
match &self.body[expr_id] {
|
||||||
Expr::OffsetOf(_) => {
|
Expr::OffsetOf(_) => {
|
||||||
not_supported!("builtin#offset_of")
|
not_supported!("builtin#offset_of")
|
||||||
}
|
}
|
||||||
|
|
@ -1374,7 +1374,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<Operand> {
|
fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<Operand> {
|
||||||
match &self.body.exprs[*loc] {
|
match &self.body[*loc] {
|
||||||
Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
|
Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
|
||||||
Expr::Path(c) => {
|
Expr::Path(c) => {
|
||||||
let owner = self.owner;
|
let owner = self.owner;
|
||||||
|
|
@ -1850,7 +1850,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
self.drop_scopes.last_mut().unwrap().locals.push(local_id);
|
self.drop_scopes.last_mut().unwrap().locals.push(local_id);
|
||||||
if let Pat::Bind { id, subpat: None } = self.body[it] {
|
if let Pat::Bind { id, subpat: None } = self.body[it] {
|
||||||
if matches!(
|
if matches!(
|
||||||
self.body.bindings[id].mode,
|
self.body[id].mode,
|
||||||
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
|
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
|
||||||
) {
|
) {
|
||||||
self.result.binding_locals.insert(id, local_id);
|
self.result.binding_locals.insert(id, local_id);
|
||||||
|
|
@ -1859,7 +1859,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
local_id
|
local_id
|
||||||
}));
|
}));
|
||||||
// and then rest of bindings
|
// and then rest of bindings
|
||||||
for (id, _) in self.body.bindings.iter() {
|
for (id, _) in self.body.bindings() {
|
||||||
if !pick_binding(id) {
|
if !pick_binding(id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -2126,7 +2126,7 @@ pub fn mir_body_for_closure_query(
|
||||||
.result
|
.result
|
||||||
.binding_locals
|
.binding_locals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr))
|
.filter(|it| ctx.body.binding_owner(it.0) == Some(expr))
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
return Err(MirLowerError::UnresolvedUpvar(err));
|
return Err(MirLowerError::UnresolvedUpvar(err));
|
||||||
|
|
@ -2191,7 +2191,7 @@ pub fn lower_to_mir(
|
||||||
// 0 is return local
|
// 0 is return local
|
||||||
ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
|
ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
|
||||||
let binding_picker = |b: BindingId| {
|
let binding_picker = |b: BindingId| {
|
||||||
let owner = ctx.body.binding_owners.get(&b).copied();
|
let owner = ctx.body.binding_owner(b);
|
||||||
if root_expr == body.body_expr { owner.is_none() } else { owner == Some(root_expr) }
|
if root_expr == body.body_expr { owner.is_none() } else { owner == Some(root_expr) }
|
||||||
};
|
};
|
||||||
// 1 to param_len is for params
|
// 1 to param_len is for params
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ impl MirLowerCtx<'_> {
|
||||||
}
|
}
|
||||||
this.lower_expr_to_some_place_without_adjust(expr_id, current)
|
this.lower_expr_to_some_place_without_adjust(expr_id, current)
|
||||||
};
|
};
|
||||||
match &self.body.exprs[expr_id] {
|
match &self.body[expr_id] {
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let resolver_guard =
|
let resolver_guard =
|
||||||
self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
|
self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ impl MirLowerCtx<'_> {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
Ok(match &self.body.pats[pattern] {
|
Ok(match &self.body[pattern] {
|
||||||
Pat::Missing => return Err(MirLowerError::IncompletePattern),
|
Pat::Missing => return Err(MirLowerError::IncompletePattern),
|
||||||
Pat::Wild => (current, current_else),
|
Pat::Wild => (current, current_else),
|
||||||
Pat::Tuple { args, ellipsis } => {
|
Pat::Tuple { args, ellipsis } => {
|
||||||
|
|
@ -436,7 +436,7 @@ impl MirLowerCtx<'_> {
|
||||||
(next, Some(else_target))
|
(next, Some(else_target))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Pat::Lit(l) => match &self.body.exprs[*l] {
|
Pat::Lit(l) => match &self.body[*l] {
|
||||||
Expr::Literal(l) => {
|
Expr::Literal(l) => {
|
||||||
if mode == MatchingMode::Check {
|
if mode == MatchingMode::Check {
|
||||||
let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
|
let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||||
|
|
||||||
fn local_name(&self, local: LocalId) -> LocalName {
|
fn local_name(&self, local: LocalId) -> LocalName {
|
||||||
match self.local_to_binding.get(local) {
|
match self.local_to_binding.get(local) {
|
||||||
Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local),
|
Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local),
|
||||||
None => LocalName::Unknown(local),
|
None => LocalName::Unknown(local),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ fn check_impl(
|
||||||
let inference_result = db.infer(def);
|
let inference_result = db.infer(def);
|
||||||
|
|
||||||
for (pat, mut ty) in inference_result.type_of_pat.iter() {
|
for (pat, mut ty) in inference_result.type_of_pat.iter() {
|
||||||
if let Pat::Bind { id, .. } = body.pats[pat] {
|
if let Pat::Bind { id, .. } = body[pat] {
|
||||||
ty = &inference_result.type_of_binding[id];
|
ty = &inference_result.type_of_binding[id];
|
||||||
}
|
}
|
||||||
let node = match pat_node(&body_source_map, pat, &db) {
|
let node = match pat_node(&body_source_map, pat, &db) {
|
||||||
|
|
@ -316,7 +316,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pat, mut ty) in inference_result.type_of_pat.iter() {
|
for (pat, mut ty) in inference_result.type_of_pat.iter() {
|
||||||
if let Pat::Bind { id, .. } = body.pats[pat] {
|
if let Pat::Bind { id, .. } = body[pat] {
|
||||||
ty = &inference_result.type_of_binding[id];
|
ty = &inference_result.type_of_binding[id];
|
||||||
}
|
}
|
||||||
let node = match body_source_map.pat_syntax(pat) {
|
let node = match body_source_map.pat_syntax(pat) {
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,14 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Ar
|
||||||
variances.is_empty().not().then(|| Arc::from_iter(variances))
|
variances.is_empty().not().then(|| Arc::from_iter(variances))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn variances_of_cycle_fn(
|
// pub(crate) fn variances_of_cycle_fn(
|
||||||
_db: &dyn HirDatabase,
|
// _db: &dyn HirDatabase,
|
||||||
_result: &Option<Arc<[Variance]>>,
|
// _result: &Option<Arc<[Variance]>>,
|
||||||
_count: u32,
|
// _count: u32,
|
||||||
_def: GenericDefId,
|
// _def: GenericDefId,
|
||||||
) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
|
// ) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
|
||||||
salsa::CycleRecoveryAction::Iterate
|
// salsa::CycleRecoveryAction::Iterate
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub(crate) fn variances_of_cycle_initial(
|
pub(crate) fn variances_of_cycle_initial(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
|
@ -965,7 +965,7 @@ struct S3<T>(S<T, T>);
|
||||||
struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
|
struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
FixedPoint[T: covariant, U: covariant, V: covariant]
|
FixedPoint[T: bivariant, U: bivariant, V: bivariant]
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2036,7 +2036,7 @@ impl DefWithBody {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let mol = &borrowck_result.mutability_of_locals;
|
let mol = &borrowck_result.mutability_of_locals;
|
||||||
for (binding_id, binding_data) in body.bindings.iter() {
|
for (binding_id, binding_data) in body.bindings() {
|
||||||
if binding_data.problems.is_some() {
|
if binding_data.problems.is_some() {
|
||||||
// We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
|
// We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -3222,7 +3222,8 @@ impl Macro {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
|
/// Is this `asm!()`, or a variant of it (e.g. `global_asm!()`)?
|
||||||
|
pub fn is_asm_like(&self, db: &dyn HirDatabase) -> bool {
|
||||||
match self.id {
|
match self.id {
|
||||||
MacroId::Macro2Id(it) => {
|
MacroId::Macro2Id(it) => {
|
||||||
matches!(it.lookup(db).expander, MacroExpander::BuiltIn(m) if m.is_asm())
|
matches!(it.lookup(db).expander, MacroExpander::BuiltIn(m) if m.is_asm())
|
||||||
|
|
|
||||||
|
|
@ -677,8 +677,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
|
pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
|
||||||
let body = self.db.body(to_be_renamed.parent);
|
let body = self.db.body(to_be_renamed.parent);
|
||||||
let resolver = to_be_renamed.parent.resolver(self.db);
|
let resolver = to_be_renamed.parent.resolver(self.db);
|
||||||
let starting_expr =
|
let starting_expr = body.binding_owner(to_be_renamed.binding_id).unwrap_or(body.body_expr);
|
||||||
body.binding_owners.get(&to_be_renamed.binding_id).copied().unwrap_or(body.body_expr);
|
|
||||||
let mut visitor = RenameConflictsVisitor {
|
let mut visitor = RenameConflictsVisitor {
|
||||||
body: &body,
|
body: &body,
|
||||||
conflicts: FxHashSet::default(),
|
conflicts: FxHashSet::default(),
|
||||||
|
|
@ -1776,7 +1775,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
|
|
||||||
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||||
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
|
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
|
||||||
if mac.is_asm_or_global_asm(self.db) {
|
if mac.is_asm_like(self.db) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -242,11 +242,7 @@ impl<'db> SourceAnalyzer<'db> {
|
||||||
|
|
||||||
fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
|
fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
|
||||||
let pat_id = self.pat_id(&pat.clone().into())?;
|
let pat_id = self.pat_id(&pat.clone().into())?;
|
||||||
if let Pat::Bind { id, .. } = self.store()?.pats[pat_id.as_pat()?] {
|
if let Pat::Bind { id, .. } = self.store()?[pat_id.as_pat()?] { Some(id) } else { None }
|
||||||
Some(id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> {
|
pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> {
|
||||||
|
|
@ -995,7 +991,7 @@ impl<'db> SourceAnalyzer<'db> {
|
||||||
let parent_hir_path = path
|
let parent_hir_path = path
|
||||||
.parent_path()
|
.parent_path()
|
||||||
.and_then(|p| collector.lower_path(p, &mut ExprCollector::impl_trait_error_allocator));
|
.and_then(|p| collector.lower_path(p, &mut ExprCollector::impl_trait_error_allocator));
|
||||||
let store = collector.store.finish();
|
let (store, _) = collector.store.finish();
|
||||||
|
|
||||||
// Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
|
// Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
|
||||||
// trying to resolve foo::bar.
|
// trying to resolve foo::bar.
|
||||||
|
|
@ -1204,7 +1200,7 @@ impl<'db> SourceAnalyzer<'db> {
|
||||||
let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
|
let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
|
||||||
let hir_path =
|
let hir_path =
|
||||||
collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
|
collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
|
||||||
let store = collector.store.finish();
|
let (store, _) = collector.store.finish();
|
||||||
Some(resolve_hir_path_(
|
Some(resolve_hir_path_(
|
||||||
db,
|
db,
|
||||||
&self.resolver,
|
&self.resolver,
|
||||||
|
|
@ -1439,9 +1435,11 @@ fn scope_for(
|
||||||
) -> Option<ScopeId> {
|
) -> Option<ScopeId> {
|
||||||
node.ancestors_with_macros(db)
|
node.ancestors_with_macros(db)
|
||||||
.take_while(|it| {
|
.take_while(|it| {
|
||||||
!ast::Item::can_cast(it.kind())
|
let kind = it.kind();
|
||||||
|| ast::MacroCall::can_cast(it.kind())
|
!ast::Item::can_cast(kind)
|
||||||
|| ast::Use::can_cast(it.kind())
|
|| ast::MacroCall::can_cast(kind)
|
||||||
|
|| ast::Use::can_cast(kind)
|
||||||
|
|| ast::AsmExpr::can_cast(kind)
|
||||||
})
|
})
|
||||||
.filter_map(|it| it.map(ast::Expr::cast).transpose())
|
.filter_map(|it| it.map(ast::Expr::cast).transpose())
|
||||||
.filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
|
.filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,13 @@ impl<'a> SymbolCollector<'a> {
|
||||||
}
|
}
|
||||||
ModuleDefId::AdtId(AdtId::EnumId(id)) => {
|
ModuleDefId::AdtId(AdtId::EnumId(id)) => {
|
||||||
this.push_decl(id, name, false, None);
|
this.push_decl(id, name, false, None);
|
||||||
|
let enum_name = this.db.enum_signature(id).name.as_str().to_smolstr();
|
||||||
|
this.with_container_name(Some(enum_name), |this| {
|
||||||
|
let variants = id.enum_variants(this.db);
|
||||||
|
for (variant_id, variant_name, _) in &variants.variants {
|
||||||
|
this.push_decl(*variant_id, variant_name, true, None);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ModuleDefId::AdtId(AdtId::UnionId(id)) => {
|
ModuleDefId::AdtId(AdtId::UnionId(id)) => {
|
||||||
this.push_decl(id, name, false, None);
|
this.push_decl(id, name, false, None);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use ide_db::defs::{Definition, NameRefClass};
|
use ide_db::defs::{Definition, NameRefClass};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode, SyntaxNode,
|
AstNode, SyntaxNode,
|
||||||
ast::{self, HasName, Name},
|
ast::{self, HasName, Name, syntax_factory::SyntaxFactory},
|
||||||
ted,
|
syntax_editor::SyntaxEditor,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -121,34 +121,36 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
|
||||||
|
|
||||||
// Rename `extracted` with `binding` in `pat`.
|
// Rename `extracted` with `binding` in `pat`.
|
||||||
fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
|
fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
|
||||||
let syntax = pat.syntax().clone_for_update();
|
let syntax = pat.syntax().clone_subtree();
|
||||||
|
let mut editor = SyntaxEditor::new(syntax.clone());
|
||||||
|
let make = SyntaxFactory::with_mappings();
|
||||||
let extracted = extracted
|
let extracted = extracted
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| syntax.covering_element(e.syntax().text_range()))
|
.map(|e| e.syntax().text_range() - pat.syntax().text_range().start())
|
||||||
|
.map(|r| syntax.covering_element(r))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for extracted_syntax in extracted {
|
for extracted_syntax in extracted {
|
||||||
// If `extracted` variable is a record field, we should rename it to `binding`,
|
// If `extracted` variable is a record field, we should rename it to `binding`,
|
||||||
// otherwise we just need to replace `extracted` with `binding`.
|
// otherwise we just need to replace `extracted` with `binding`.
|
||||||
|
|
||||||
if let Some(record_pat_field) =
|
if let Some(record_pat_field) =
|
||||||
extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
|
extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
|
||||||
{
|
{
|
||||||
if let Some(name_ref) = record_pat_field.field_name() {
|
if let Some(name_ref) = record_pat_field.field_name() {
|
||||||
ted::replace(
|
editor.replace(
|
||||||
record_pat_field.syntax(),
|
record_pat_field.syntax(),
|
||||||
ast::make::record_pat_field(
|
make.record_pat_field(
|
||||||
ast::make::name_ref(&name_ref.text()),
|
make.name_ref(&name_ref.text()),
|
||||||
binding.clone(),
|
binding.clone_for_update(),
|
||||||
)
|
)
|
||||||
.syntax()
|
.syntax(),
|
||||||
.clone_for_update(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ted::replace(extracted_syntax, binding.clone().syntax().clone_for_update());
|
editor.replace(extracted_syntax, binding.syntax().clone_for_update());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syntax
|
editor.add_mappings(make.finish_with_mappings());
|
||||||
|
editor.finish().new_root().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
SyntaxKind,
|
SyntaxKind,
|
||||||
ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
|
ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
|
||||||
match_ast, ted,
|
match_ast,
|
||||||
|
syntax_editor::{Position, SyntaxEditor},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
|
use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
|
||||||
|
|
@ -97,11 +98,14 @@ fn edit_struct_def(
|
||||||
// Note that we don't need to consider macro files in this function because this is
|
// Note that we don't need to consider macro files in this function because this is
|
||||||
// currently not triggered for struct definitions inside macro calls.
|
// currently not triggered for struct definitions inside macro calls.
|
||||||
let tuple_fields = record_fields.fields().filter_map(|f| {
|
let tuple_fields = record_fields.fields().filter_map(|f| {
|
||||||
let field = ast::make::tuple_field(f.visibility(), f.ty()?).clone_for_update();
|
let field = ast::make::tuple_field(f.visibility(), f.ty()?);
|
||||||
ted::insert_all(
|
let mut editor = SyntaxEditor::new(field.syntax().clone());
|
||||||
ted::Position::first_child_of(field.syntax()),
|
editor.insert_all(
|
||||||
|
Position::first_child_of(field.syntax()),
|
||||||
f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
|
f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
|
||||||
);
|
);
|
||||||
|
let field_syntax = editor.finish().new_root().clone();
|
||||||
|
let field = ast::TupleField::cast(field_syntax)?;
|
||||||
Some(field)
|
Some(field)
|
||||||
});
|
});
|
||||||
let tuple_fields = ast::make::tuple_field_list(tuple_fields);
|
let tuple_fields = ast::make::tuple_field_list(tuple_fields);
|
||||||
|
|
@ -1086,8 +1090,7 @@ pub struct $0Foo {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
pub struct Foo(#[my_custom_attr]
|
pub struct Foo(#[my_custom_attr]u32);
|
||||||
u32);
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use ide_db::famous_defs::FamousDefs;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode,
|
AstNode,
|
||||||
ast::{self, HasGenericParams, HasName, Impl, make},
|
ast::{self, HasGenericParams, HasName, HasTypeBounds, Impl, make},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -88,20 +88,19 @@ fn generate_trait_impl_text_from_impl(
|
||||||
let generic_params = impl_.generic_param_list().map(|generic_params| {
|
let generic_params = impl_.generic_param_list().map(|generic_params| {
|
||||||
let lifetime_params =
|
let lifetime_params =
|
||||||
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
||||||
let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
|
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
||||||
// remove defaults since they can't be specified in impls
|
// remove defaults since they can't be specified in impls
|
||||||
match param {
|
let param = match param {
|
||||||
ast::TypeOrConstParam::Type(param) => {
|
ast::TypeOrConstParam::Type(param) => {
|
||||||
let param = param.clone_for_update();
|
let param = make::type_param(param.name()?, param.type_bound_list());
|
||||||
param.remove_default();
|
|
||||||
ast::GenericParam::TypeParam(param)
|
ast::GenericParam::TypeParam(param)
|
||||||
}
|
}
|
||||||
ast::TypeOrConstParam::Const(param) => {
|
ast::TypeOrConstParam::Const(param) => {
|
||||||
let param = param.clone_for_update();
|
let param = make::const_param(param.name()?, param.ty()?);
|
||||||
param.remove_default();
|
|
||||||
ast::GenericParam::ConstParam(param)
|
ast::GenericParam::ConstParam(param)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Some(param)
|
||||||
});
|
});
|
||||||
|
|
||||||
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI
|
||||||
let self_expr = make::ext::expr_self();
|
let self_expr = make::ext::expr_self();
|
||||||
let lhs = make::expr_field(self_expr, field_name);
|
let lhs = make::expr_field(self_expr, field_name);
|
||||||
let rhs = make::expr_path(make::ext::ident_path(field_name));
|
let rhs = make::expr_path(make::ext::ident_path(field_name));
|
||||||
let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs));
|
let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs).into());
|
||||||
let body = make::block_expr([assign_stmt.into()], None);
|
let body = make::block_expr([assign_stmt.into()], None);
|
||||||
|
|
||||||
// Make the setter fn
|
// Make the setter fn
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasName, edit_in_place::Indent, make},
|
ast::{self, AstNode, HasName, edit_in_place::Indent, make},
|
||||||
ted,
|
syntax_editor::{Position, SyntaxEditor},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, Assists, utils};
|
use crate::{AssistContext, AssistId, Assists, utils};
|
||||||
|
|
||||||
fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) {
|
fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
|
||||||
let indent = nominal.indent_level();
|
let indent = nominal.indent_level();
|
||||||
ted::insert_all_raw(
|
editor.insert_all(
|
||||||
ted::Position::after(nominal.syntax()),
|
Position::after(nominal.syntax()),
|
||||||
vec![
|
vec![
|
||||||
// Add a blank line after the ADT, and indentation for the impl to match the ADT
|
// Add a blank line after the ADT, and indentation for the impl to match the ADT
|
||||||
make::tokens::whitespace(&format!("\n\n{indent}")).into(),
|
make::tokens::whitespace(&format!("\n\n{indent}")).into(),
|
||||||
|
|
@ -51,14 +51,17 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
|
||||||
// Generate the impl
|
// Generate the impl
|
||||||
let impl_ = utils::generate_impl(&nominal);
|
let impl_ = utils::generate_impl(&nominal);
|
||||||
|
|
||||||
|
let mut editor = edit.make_editor(nominal.syntax());
|
||||||
// Add a tabstop after the left curly brace
|
// Add a tabstop after the left curly brace
|
||||||
if let Some(cap) = ctx.config.snippet_cap {
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
|
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
|
||||||
edit.add_tabstop_after_token(cap, l_curly);
|
let tabstop = edit.make_tabstop_after(cap);
|
||||||
|
editor.add_annotation(l_curly, tabstop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_impl(impl_, &edit.make_mut(nominal));
|
insert_impl(&mut editor, &impl_, &nominal);
|
||||||
|
edit.add_file_edits(ctx.vfs_file_id(), editor);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -97,18 +100,22 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
||||||
// Generate the impl
|
// Generate the impl
|
||||||
let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
|
let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
|
||||||
|
|
||||||
|
let mut editor = edit.make_editor(nominal.syntax());
|
||||||
// Make the trait type a placeholder snippet
|
// Make the trait type a placeholder snippet
|
||||||
if let Some(cap) = ctx.config.snippet_cap {
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
if let Some(trait_) = impl_.trait_() {
|
if let Some(trait_) = impl_.trait_() {
|
||||||
edit.add_placeholder_snippet(cap, trait_);
|
let placeholder = edit.make_placeholder_snippet(cap);
|
||||||
|
editor.add_annotation(trait_.syntax(), placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
|
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
|
||||||
edit.add_tabstop_after_token(cap, l_curly);
|
let tabstop = edit.make_tabstop_after(cap);
|
||||||
|
editor.add_annotation(l_curly, tabstop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_impl(impl_, &edit.make_mut(nominal));
|
insert_impl(&mut editor, &impl_, &nominal);
|
||||||
|
edit.add_file_edits(ctx.vfs_file_id(), editor);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ide_db::famous_defs::FamousDefs;
|
use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode,
|
AstNode, T,
|
||||||
ast::{self, edit_in_place::Indent, make},
|
ast::{self, edit_in_place::Indent, make},
|
||||||
ted,
|
ted,
|
||||||
};
|
};
|
||||||
|
|
@ -32,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists};
|
||||||
//
|
//
|
||||||
// $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
|
// $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
|
||||||
// fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
|
// fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
|
||||||
// &self[index as usize]
|
// &mut self[index as usize]
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
|
@ -48,36 +48,34 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
|
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
|
||||||
let indent = impl_def.indent_level();
|
let indent = impl_def.indent_level();
|
||||||
|
|
||||||
let trait_ = impl_def.trait_()?;
|
let ast::Type::PathType(path) = impl_def.trait_()? else {
|
||||||
if let ast::Type::PathType(trait_path) = trait_ {
|
|
||||||
let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?;
|
|
||||||
let scope = ctx.sema.scope(trait_path.syntax())?;
|
|
||||||
if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
};
|
||||||
}
|
let trait_name = path.path()?.segment()?.name_ref()?;
|
||||||
|
|
||||||
|
let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?;
|
||||||
|
let famous = FamousDefs(&ctx.sema, scope.krate());
|
||||||
|
|
||||||
|
let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
|
||||||
|
let trait_new = get_trait_mut(&trait_, famous)?;
|
||||||
|
|
||||||
// Index -> IndexMut
|
// Index -> IndexMut
|
||||||
let index_trait = impl_def
|
ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax());
|
||||||
.syntax()
|
|
||||||
.descendants()
|
|
||||||
.filter_map(ast::NameRef::cast)
|
|
||||||
.find(|it| it.text() == "Index")?;
|
|
||||||
ted::replace(
|
|
||||||
index_trait.syntax(),
|
|
||||||
make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// index -> index_mut
|
// index -> index_mut
|
||||||
let trait_method_name = impl_def
|
let (trait_method_name, new_trait_method_name) = impl_def
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants()
|
.descendants()
|
||||||
.filter_map(ast::Name::cast)
|
.filter_map(ast::Name::cast)
|
||||||
.find(|it| it.text() == "index")?;
|
.find_map(process_method_name)?;
|
||||||
ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax());
|
ted::replace(
|
||||||
|
trait_method_name.syntax(),
|
||||||
|
make::name(new_trait_method_name).clone_for_update().syntax(),
|
||||||
|
);
|
||||||
|
|
||||||
let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?;
|
if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) {
|
||||||
ted::remove(type_alias.syntax());
|
ted::remove(type_alias.syntax());
|
||||||
|
}
|
||||||
|
|
||||||
// &self -> &mut self
|
// &self -> &mut self
|
||||||
let mut_self_param = make::mut_self_param();
|
let mut_self_param = make::mut_self_param();
|
||||||
|
|
@ -87,15 +85,14 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
|
|
||||||
// &Self::Output -> &mut Self::Output
|
// &Self::Output -> &mut Self::Output
|
||||||
let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
|
let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
|
||||||
ted::replace(
|
let new_ret_type = process_ret_type(&ret_type)?;
|
||||||
ret_type.syntax(),
|
ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax());
|
||||||
make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
|
let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
|
||||||
ast::AssocItem::Fn(f) => Some(f),
|
ast::AssocItem::Fn(f) => Some(f),
|
||||||
_ => None,
|
_ => None,
|
||||||
})?;
|
})?;
|
||||||
|
let _ = process_ref_mut(&fn_);
|
||||||
|
|
||||||
let assoc_list = make::assoc_item_list().clone_for_update();
|
let assoc_list = make::assoc_item_list().clone_for_update();
|
||||||
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
|
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
|
||||||
|
|
@ -104,7 +101,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
let target = impl_def.syntax().text_range();
|
let target = impl_def.syntax().text_range();
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId::generate("generate_mut_trait_impl"),
|
AssistId::generate("generate_mut_trait_impl"),
|
||||||
"Generate `IndexMut` impl from this `Index` trait",
|
format!("Generate `{trait_new}` impl from this `{trait_name}` trait"),
|
||||||
target,
|
target,
|
||||||
|edit| {
|
|edit| {
|
||||||
edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}"));
|
edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}"));
|
||||||
|
|
@ -112,6 +109,52 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_ref_mut(fn_: &ast::Fn) -> Option<()> {
|
||||||
|
let expr = fn_.body()?.tail_expr()?;
|
||||||
|
match &expr {
|
||||||
|
ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => {
|
||||||
|
ted::insert_all_raw(
|
||||||
|
ted::Position::after(ref_expr.amp_token()?),
|
||||||
|
vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> {
|
||||||
|
let trait_ = Some(apply_trait);
|
||||||
|
if trait_ == famous.core_convert_Index().as_ref() {
|
||||||
|
return Some("IndexMut");
|
||||||
|
}
|
||||||
|
if trait_ == famous.core_convert_AsRef().as_ref() {
|
||||||
|
return Some("AsMut");
|
||||||
|
}
|
||||||
|
if trait_ == famous.core_borrow_Borrow().as_ref() {
|
||||||
|
return Some("BorrowMut");
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
|
||||||
|
let new_name = match &*name.text() {
|
||||||
|
"index" => "index_mut",
|
||||||
|
"as_ref" => "as_mut",
|
||||||
|
"borrow" => "borrow_mut",
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some((name, new_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_ret_type(ref_ty: &ast::RetType) -> Option<ast::Type> {
|
||||||
|
let ty = ref_ty.ty()?;
|
||||||
|
let ast::Type::RefType(ref_type) = ty else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(make::ty_ref(ref_type.ty()?, true))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||||
|
|
@ -139,7 +182,7 @@ pub enum Axis { X = 0, Y = 1, Z = 2 }
|
||||||
|
|
||||||
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
|
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
|
||||||
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
|
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
|
||||||
&self[index as usize]
|
&mut self[index as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +229,35 @@ impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
|
||||||
var_name
|
var_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
generate_mut_trait_impl,
|
||||||
|
r#"
|
||||||
|
//- minicore: as_ref
|
||||||
|
struct Foo(i32);
|
||||||
|
|
||||||
|
impl core::convert::AsRef$0<i32> for Foo {
|
||||||
|
fn as_ref(&self) -> &i32 {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo(i32);
|
||||||
|
|
||||||
|
$0impl core::convert::AsMut<i32> for Foo {
|
||||||
|
fn as_mut(&mut self) -> &mut i32 {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::AsRef<i32> for Foo {
|
||||||
|
fn as_ref(&self) -> &i32 {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +357,14 @@ mod foo {
|
||||||
pub trait Index<Idx: ?Sized> {}
|
pub trait Index<Idx: ?Sized> {}
|
||||||
|
|
||||||
impl<T> Index$0<i32> for [T; 3] {}
|
impl<T> Index$0<i32> for [T; 3] {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist_not_applicable(
|
||||||
|
generate_mut_trait_impl,
|
||||||
|
r#"
|
||||||
|
pub trait AsRef<T: ?Sized> {}
|
||||||
|
|
||||||
|
impl AsRef$0<i32> for [T; 3] {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
|
imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator,
|
||||||
|
use_trivial_constructor::use_trivial_constructor,
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
|
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
|
||||||
|
|
@ -35,10 +36,30 @@ use crate::{
|
||||||
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||||
|
|
||||||
// We want to only apply this to non-union structs with named fields
|
|
||||||
let field_list = match strukt.kind() {
|
let field_list = match strukt.kind() {
|
||||||
StructKind::Record(named) => named,
|
StructKind::Record(named) => {
|
||||||
_ => return None,
|
named.fields().filter_map(|f| Some((f.name()?, f.ty()?))).collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
StructKind::Tuple(tuple) => {
|
||||||
|
let mut name_generator = NameGenerator::default();
|
||||||
|
tuple
|
||||||
|
.fields()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, f)| {
|
||||||
|
let ty = f.ty()?;
|
||||||
|
let name = match name_generator.for_type(
|
||||||
|
&ctx.sema.resolve_type(&ty)?,
|
||||||
|
ctx.db(),
|
||||||
|
ctx.edition(),
|
||||||
|
) {
|
||||||
|
Some(name) => name,
|
||||||
|
None => name_generator.suggest_name(&format!("_{i}")),
|
||||||
|
};
|
||||||
|
Some((make::name(name.as_str()), f.ty()?))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
StructKind::Unit => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return early if we've found an existing new fn
|
// Return early if we've found an existing new fn
|
||||||
|
|
@ -50,11 +71,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
let target = strukt.syntax().text_range();
|
let target = strukt.syntax().text_range();
|
||||||
acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
|
acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
|
||||||
let trivial_constructors = field_list
|
let trivial_constructors = field_list
|
||||||
.fields()
|
.iter()
|
||||||
.map(|f| {
|
.map(|(name, ty)| {
|
||||||
let name = f.name()?;
|
let ty = ctx.sema.resolve_type(ty)?;
|
||||||
|
|
||||||
let ty = ctx.sema.resolve_type(&f.ty()?)?;
|
|
||||||
|
|
||||||
let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
|
let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
|
||||||
|
|
||||||
|
|
@ -73,34 +92,44 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
edition,
|
edition,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Some(make::record_expr_field(make::name_ref(&name.text()), Some(expr)))
|
Some((make::name_ref(&name.text()), Some(expr)))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let params = field_list.fields().enumerate().filter_map(|(i, f)| {
|
let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| {
|
||||||
if trivial_constructors[i].is_none() {
|
if trivial_constructors[i].is_none() {
|
||||||
let name = f.name()?;
|
Some(make::param(make::ident_pat(false, false, name.clone()).into(), ty.clone()))
|
||||||
let ty = f.ty()?;
|
|
||||||
|
|
||||||
Some(make::param(make::ident_pat(false, false, name).into(), ty))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let params = make::param_list(None, params);
|
let params = make::param_list(None, params);
|
||||||
|
|
||||||
let fields = field_list.fields().enumerate().filter_map(|(i, f)| {
|
let fields = field_list.iter().enumerate().map(|(i, (name, _))| {
|
||||||
let constructor = trivial_constructors[i].clone();
|
if let Some(constructor) = trivial_constructors[i].clone() {
|
||||||
if constructor.is_some() {
|
|
||||||
constructor
|
constructor
|
||||||
} else {
|
} else {
|
||||||
Some(make::record_expr_field(make::name_ref(&f.name()?.text()), None))
|
(make::name_ref(&name.text()), None)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let fields = make::record_expr_field_list(fields);
|
|
||||||
|
|
||||||
let record_expr = make::record_expr(make::ext::ident_path("Self"), fields);
|
let tail_expr: ast::Expr = match strukt.kind() {
|
||||||
let body = make::block_expr(None, Some(record_expr.into()));
|
StructKind::Record(_) => {
|
||||||
|
let fields = fields.map(|(name, expr)| make::record_expr_field(name, expr));
|
||||||
|
let fields = make::record_expr_field_list(fields);
|
||||||
|
make::record_expr(make::ext::ident_path("Self"), fields).into()
|
||||||
|
}
|
||||||
|
StructKind::Tuple(_) => {
|
||||||
|
let args = fields.map(|(arg, expr)| {
|
||||||
|
let arg = || make::expr_path(make::path_unqualified(make::path_segment(arg)));
|
||||||
|
expr.unwrap_or_else(arg)
|
||||||
|
});
|
||||||
|
let arg_list = make::arg_list(args);
|
||||||
|
make::expr_call(make::expr_path(make::ext::ident_path("Self")), arg_list).into()
|
||||||
|
}
|
||||||
|
StructKind::Unit => unreachable!(),
|
||||||
|
};
|
||||||
|
let body = make::block_expr(None, tail_expr.into());
|
||||||
|
|
||||||
let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
|
let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
|
||||||
|
|
||||||
|
|
@ -120,8 +149,35 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
.clone_for_update();
|
.clone_for_update();
|
||||||
fn_.indent(1.into());
|
fn_.indent(1.into());
|
||||||
|
|
||||||
// Add a tabstop before the name
|
|
||||||
if let Some(cap) = ctx.config.snippet_cap {
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
|
match strukt.kind() {
|
||||||
|
StructKind::Tuple(_) => {
|
||||||
|
let struct_args = fn_
|
||||||
|
.body()
|
||||||
|
.unwrap()
|
||||||
|
.syntax()
|
||||||
|
.descendants()
|
||||||
|
.filter(|it| syntax::ast::ArgList::can_cast(it.kind()))
|
||||||
|
.flat_map(|args| args.children())
|
||||||
|
.filter(|it| syntax::ast::PathExpr::can_cast(it.kind()))
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, node)| {
|
||||||
|
if trivial_constructors[i].is_none() { Some(node) } else { None }
|
||||||
|
});
|
||||||
|
if let Some(fn_params) = fn_.param_list() {
|
||||||
|
for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
|
||||||
|
if let Some(fn_pat) = fn_param.pat() {
|
||||||
|
let fn_pat = fn_pat.syntax().clone();
|
||||||
|
builder
|
||||||
|
.add_placeholder_snippet_group(cap, vec![struct_arg, fn_pat]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a tabstop before the name
|
||||||
if let Some(name) = fn_.name() {
|
if let Some(name) = fn_.name() {
|
||||||
builder.add_tabstop_before(cap, name);
|
builder.add_tabstop_before(cap, name);
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +213,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod record_tests {
|
||||||
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -695,3 +751,308 @@ impl<T> Source<T> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tuple_tests {
|
||||||
|
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_new_with_zst_fields() {
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Empty;
|
||||||
|
|
||||||
|
struct Foo(Empty$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Empty;
|
||||||
|
|
||||||
|
struct Foo(Empty);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new() -> Self {
|
||||||
|
Self(Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Empty;
|
||||||
|
|
||||||
|
struct Foo(String, Empty$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Empty;
|
||||||
|
|
||||||
|
struct Foo(String, Empty);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new(${1:_0}: String) -> Self {
|
||||||
|
Self(${1:_0}, Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
enum Empty { Bar }
|
||||||
|
|
||||||
|
struct Foo(Empty$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Empty { Bar }
|
||||||
|
|
||||||
|
struct Foo(Empty);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new() -> Self {
|
||||||
|
Self(Empty::Bar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
// make sure the assist only works on unit variants
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Empty {}
|
||||||
|
|
||||||
|
struct Foo(Empty$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Empty {}
|
||||||
|
|
||||||
|
struct Foo(Empty);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new(${1:empty}: Empty) -> Self {
|
||||||
|
Self(${1:empty})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
enum Empty { Bar {} }
|
||||||
|
|
||||||
|
struct Foo(Empty$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Empty { Bar {} }
|
||||||
|
|
||||||
|
struct Foo(Empty);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new(${1:empty}: Empty) -> Self {
|
||||||
|
Self(${1:empty})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_new() {
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Foo($0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo();
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new() -> Self {
|
||||||
|
Self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Foo<T: Clone>($0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo<T: Clone>();
|
||||||
|
|
||||||
|
impl<T: Clone> Foo<T> {
|
||||||
|
fn $0new() -> Self {
|
||||||
|
Self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Foo<'a, T: Foo<'a>>($0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo<'a, T: Foo<'a>>();
|
||||||
|
|
||||||
|
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
||||||
|
fn $0new() -> Self {
|
||||||
|
Self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Foo(String$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo(String);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new(${1:_0}: String) -> Self {
|
||||||
|
Self(${1:_0})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Vec<T> { };
|
||||||
|
struct Foo(String, Vec<i32>$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Vec<T> { };
|
||||||
|
struct Foo(String, Vec<i32>);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
|
||||||
|
Self(${1:_0}, ${2:items})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_that_visibility_modifiers_dont_get_brought_in() {
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Vec<T> { };
|
||||||
|
struct Foo(pub String, pub Vec<i32>$0);
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Vec<T> { };
|
||||||
|
struct Foo(pub String, pub Vec<i32>);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
|
||||||
|
Self(${1:_0}, ${2:items})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_new_not_applicable_if_fn_exists() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Foo($0);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_not_applicable(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct Foo($0);
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn New() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_new_target() {
|
||||||
|
check_assist_target(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
struct SomeThingIrrelevant;
|
||||||
|
/// Has a lifetime parameter
|
||||||
|
struct Foo<'a, T: Foo<'a>>($0);
|
||||||
|
struct EvenMoreIrrelevant;
|
||||||
|
"#,
|
||||||
|
"/// Has a lifetime parameter
|
||||||
|
struct Foo<'a, T: Foo<'a>>();",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unrelated_new() {
|
||||||
|
check_assist(
|
||||||
|
generate_new,
|
||||||
|
r#"
|
||||||
|
pub struct AstId<N: AstNode> {
|
||||||
|
file_id: HirFileId,
|
||||||
|
file_ast_id: FileAstId<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: AstNode> AstId<N> {
|
||||||
|
pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
|
||||||
|
AstId { file_id, file_ast_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Source<T>(pub HirFileId,$0 pub T);
|
||||||
|
|
||||||
|
impl<T> Source<T> {
|
||||||
|
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||||
|
Source(self.file_id, f(self.ast))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
pub struct AstId<N: AstNode> {
|
||||||
|
file_id: HirFileId,
|
||||||
|
file_ast_id: FileAstId<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: AstNode> AstId<N> {
|
||||||
|
pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
|
||||||
|
AstId { file_id, file_ast_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Source<T>(pub HirFileId, pub T);
|
||||||
|
|
||||||
|
impl<T> Source<T> {
|
||||||
|
pub fn $0new(${1:_0}: HirFileId, ${2:_1}: T) -> Self {
|
||||||
|
Self(${1:_0}, ${2:_1})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||||
|
Source(self.file_id, f(self.ast))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
1000
crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
Normal file
1000
crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,8 @@
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode,
|
AstNode,
|
||||||
ast::{self, make},
|
algo::find_node_at_range,
|
||||||
ted,
|
ast::{self, syntax_factory::SyntaxFactory},
|
||||||
|
syntax_editor::SyntaxEditor,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -66,20 +67,27 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let target = tgt.syntax().text_range();
|
||||||
|
|
||||||
acc.add(
|
let edit_tgt = tgt.syntax().clone_subtree();
|
||||||
AssistId::refactor_extract("pull_assignment_up"),
|
|
||||||
"Pull assignment up",
|
|
||||||
tgt.syntax().text_range(),
|
|
||||||
move |edit| {
|
|
||||||
let assignments: Vec<_> = collector
|
let assignments: Vec<_> = collector
|
||||||
.assignments
|
.assignments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update()))
|
.filter_map(|(stmt, rhs)| {
|
||||||
|
Some((
|
||||||
|
find_node_at_range::<ast::BinExpr>(
|
||||||
|
&edit_tgt,
|
||||||
|
stmt.syntax().text_range() - target.start(),
|
||||||
|
)?,
|
||||||
|
find_node_at_range::<ast::Expr>(
|
||||||
|
&edit_tgt,
|
||||||
|
rhs.syntax().text_range() - target.start(),
|
||||||
|
)?,
|
||||||
|
))
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tgt = edit.make_mut(tgt);
|
let mut editor = SyntaxEditor::new(edit_tgt);
|
||||||
|
|
||||||
for (stmt, rhs) in assignments {
|
for (stmt, rhs) in assignments {
|
||||||
let mut stmt = stmt.syntax().clone();
|
let mut stmt = stmt.syntax().clone();
|
||||||
if let Some(parent) = stmt.parent() {
|
if let Some(parent) = stmt.parent() {
|
||||||
|
|
@ -87,12 +95,23 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
||||||
stmt = parent.clone();
|
stmt = parent.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ted::replace(stmt, rhs.syntax());
|
editor.replace(stmt, rhs.syntax());
|
||||||
}
|
}
|
||||||
let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
|
let new_tgt_root = editor.finish().new_root().clone();
|
||||||
let assign_stmt = make::expr_stmt(assign_expr);
|
let new_tgt = ast::Expr::cast(new_tgt_root)?;
|
||||||
|
acc.add(
|
||||||
|
AssistId::refactor_extract("pull_assignment_up"),
|
||||||
|
"Pull assignment up",
|
||||||
|
target,
|
||||||
|
move |edit| {
|
||||||
|
let make = SyntaxFactory::with_mappings();
|
||||||
|
let mut editor = edit.make_editor(tgt.syntax());
|
||||||
|
let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone());
|
||||||
|
let assign_stmt = make.expr_stmt(assign_expr.into());
|
||||||
|
|
||||||
ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
|
editor.replace(tgt.syntax(), assign_stmt.syntax());
|
||||||
|
editor.add_mappings(make.finish_with_mappings());
|
||||||
|
edit.add_file_edits(ctx.vfs_file_id(), editor);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
Edition, NodeOrToken, SyntaxElement, T, TextRange, TextSize,
|
Edition, NodeOrToken, SyntaxNode, SyntaxToken, T,
|
||||||
ast::{self, AstNode, AstToken, make},
|
ast::{self, AstNode, make},
|
||||||
match_ast, ted,
|
match_ast,
|
||||||
|
syntax_editor::{Position, SyntaxEditor},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, Assists};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
@ -40,21 +41,23 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
|
||||||
|
|
||||||
let replacements =
|
let replacements =
|
||||||
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
|
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
|
||||||
|
let target = replacements
|
||||||
acc.add(
|
.iter()
|
||||||
AssistId::quick_fix("remove_dbg"),
|
.flat_map(|(node_or_token, _)| node_or_token.iter())
|
||||||
"Remove dbg!()",
|
.map(|t| t.text_range())
|
||||||
replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
|
.reduce(|acc, range| acc.cover(range))?;
|
||||||
|builder| {
|
acc.add(AssistId::quick_fix("remove_dbg"), "Remove dbg!()", target, |builder| {
|
||||||
|
let mut editor = builder.make_editor(ctx.source_file().syntax());
|
||||||
for (range, expr) in replacements {
|
for (range, expr) in replacements {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
builder.replace(range, expr.to_string());
|
editor.insert(Position::before(range[0].clone()), expr.syntax().clone_for_update());
|
||||||
} else {
|
}
|
||||||
builder.delete(range);
|
for node_or_token in range {
|
||||||
|
editor.delete(node_or_token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
builder.add_file_edits(ctx.vfs_file_id(), editor);
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `None` when either
|
/// Returns `None` when either
|
||||||
|
|
@ -63,7 +66,9 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
|
||||||
/// - (`macro_expr` has no parent - is that possible?)
|
/// - (`macro_expr` has no parent - is that possible?)
|
||||||
///
|
///
|
||||||
/// Returns `Some(_, None)` when the macro call should just be removed.
|
/// Returns `Some(_, None)` when the macro call should just be removed.
|
||||||
fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option<ast::Expr>)> {
|
fn compute_dbg_replacement(
|
||||||
|
macro_expr: ast::MacroExpr,
|
||||||
|
) -> Option<(Vec<NodeOrToken<SyntaxNode, SyntaxToken>>, Option<ast::Expr>)> {
|
||||||
let macro_call = macro_expr.macro_call()?;
|
let macro_call = macro_expr.macro_call()?;
|
||||||
let tt = macro_call.token_tree()?;
|
let tt = macro_call.token_tree()?;
|
||||||
let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
|
let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
|
||||||
|
|
@ -88,22 +93,22 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::StmtList(_) => {
|
ast::StmtList(_) => {
|
||||||
let range = macro_expr.syntax().text_range();
|
let mut replace = vec![macro_expr.syntax().clone().into()];
|
||||||
let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
|
if let Some(prev_sibling) = macro_expr.syntax().prev_sibling_or_token()
|
||||||
Some(start) => range.cover_offset(start),
|
&& prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
|
||||||
None => range,
|
replace.push(prev_sibling);
|
||||||
};
|
}
|
||||||
(range, None)
|
(replace, None)
|
||||||
},
|
},
|
||||||
ast::ExprStmt(it) => {
|
ast::ExprStmt(it) => {
|
||||||
let range = it.syntax().text_range();
|
let mut replace = vec![it.syntax().clone().into()];
|
||||||
let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
|
if let Some(prev_sibling) = it.syntax().prev_sibling_or_token()
|
||||||
Some(start) => range.cover_offset(start),
|
&& prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
|
||||||
None => range,
|
replace.push(prev_sibling);
|
||||||
};
|
}
|
||||||
(range, None)
|
(replace, None)
|
||||||
},
|
},
|
||||||
_ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())),
|
_ => (vec![macro_call.syntax().clone().into()], Some(make::ext::expr_unit())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,13 +152,13 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
|
||||||
};
|
};
|
||||||
let expr = replace_nested_dbgs(expr.clone());
|
let expr = replace_nested_dbgs(expr.clone());
|
||||||
let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() };
|
let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() };
|
||||||
(macro_call.syntax().text_range(), Some(expr))
|
(vec![macro_call.syntax().clone().into()], Some(expr))
|
||||||
}
|
}
|
||||||
// dbg!(expr0, expr1, ...)
|
// dbg!(expr0, expr1, ...)
|
||||||
exprs => {
|
exprs => {
|
||||||
let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
|
let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
|
||||||
let expr = make::expr_tuple(exprs);
|
let expr = make::expr_tuple(exprs);
|
||||||
(macro_call.syntax().text_range(), Some(expr.into()))
|
(vec![macro_call.syntax().clone().into()], Some(expr.into()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -178,8 +183,8 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
|
||||||
return replaced;
|
return replaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
let expanded = expanded.clone_for_update();
|
let expanded = expanded.clone_subtree();
|
||||||
|
let mut editor = SyntaxEditor::new(expanded.syntax().clone());
|
||||||
// We need to collect to avoid mutation during traversal.
|
// We need to collect to avoid mutation during traversal.
|
||||||
let macro_exprs: Vec<_> =
|
let macro_exprs: Vec<_> =
|
||||||
expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
|
expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
|
||||||
|
|
@ -191,17 +196,13 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(expr) = expr_opt {
|
if let Some(expr) = expr_opt {
|
||||||
ted::replace(mac.syntax(), expr.syntax().clone_for_update());
|
editor.replace(mac.syntax(), expr.syntax().clone_for_update());
|
||||||
} else {
|
} else {
|
||||||
ted::remove(mac.syntax());
|
editor.delete(mac.syntax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let expanded_syntax = editor.finish().new_root().clone();
|
||||||
expanded
|
ast::Expr::cast(expanded_syntax).unwrap()
|
||||||
}
|
|
||||||
|
|
||||||
fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
|
|
||||||
Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,13 @@ pub(crate) fn replace_is_method_with_if_let_method(
|
||||||
let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]);
|
let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]);
|
||||||
let let_expr = make.expr_let(pat.into(), receiver);
|
let let_expr = make.expr_let(pat.into(), receiver);
|
||||||
|
|
||||||
if let Some(cap) = ctx.config.snippet_cap {
|
if let Some(cap) = ctx.config.snippet_cap
|
||||||
if let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() {
|
&& let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat()
|
||||||
if let Some(first_var) = pat.fields().next() {
|
&& let Some(first_var) = pat.fields().next()
|
||||||
|
{
|
||||||
let placeholder = edit.make_placeholder_snippet(cap);
|
let placeholder = edit.make_placeholder_snippet(cap);
|
||||||
editor.add_annotation(first_var.syntax(), placeholder);
|
editor.add_annotation(first_var.syntax(), placeholder);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.replace(call_expr.syntax(), let_expr.syntax());
|
editor.replace(call_expr.syntax(), let_expr.syntax());
|
||||||
editor.add_mappings(make.finish_with_mappings());
|
editor.add_mappings(make.finish_with_mappings());
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,7 @@ mod handlers {
|
||||||
mod generate_is_empty_from_len;
|
mod generate_is_empty_from_len;
|
||||||
mod generate_mut_trait_impl;
|
mod generate_mut_trait_impl;
|
||||||
mod generate_new;
|
mod generate_new;
|
||||||
|
mod generate_single_field_struct_from;
|
||||||
mod generate_trait_from_impl;
|
mod generate_trait_from_impl;
|
||||||
mod inline_call;
|
mod inline_call;
|
||||||
mod inline_const_as_literal;
|
mod inline_const_as_literal;
|
||||||
|
|
@ -305,6 +306,7 @@ mod handlers {
|
||||||
generate_mut_trait_impl::generate_mut_trait_impl,
|
generate_mut_trait_impl::generate_mut_trait_impl,
|
||||||
generate_new::generate_new,
|
generate_new::generate_new,
|
||||||
generate_trait_from_impl::generate_trait_from_impl,
|
generate_trait_from_impl::generate_trait_from_impl,
|
||||||
|
generate_single_field_struct_from::generate_single_field_struct_from,
|
||||||
inline_call::inline_call,
|
inline_call::inline_call,
|
||||||
inline_call::inline_into_callers,
|
inline_call::inline_into_callers,
|
||||||
inline_const_as_literal::inline_const_as_literal,
|
inline_const_as_literal::inline_const_as_literal,
|
||||||
|
|
|
||||||
|
|
@ -1933,7 +1933,7 @@ pub enum Axis { X = 0, Y = 1, Z = 2 }
|
||||||
|
|
||||||
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
|
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
|
||||||
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
|
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
|
||||||
&self[index as usize]
|
&mut self[index as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1994,6 +1994,34 @@ impl Person {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doctest_generate_single_field_struct_from() {
|
||||||
|
check_doc_test(
|
||||||
|
"generate_single_field_struct_from",
|
||||||
|
r#####"
|
||||||
|
//- minicore: from, phantom_data
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
struct $0Foo<T> {
|
||||||
|
id: i32,
|
||||||
|
_phantom_data: PhantomData<T>,
|
||||||
|
}
|
||||||
|
"#####,
|
||||||
|
r#####"
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
struct Foo<T> {
|
||||||
|
id: i32,
|
||||||
|
_phantom_data: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<i32> for Foo<T> {
|
||||||
|
fn from(id: i32) -> Self {
|
||||||
|
Self { id, _phantom_data: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#####,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn doctest_generate_trait_from_impl() {
|
fn doctest_generate_trait_from_impl() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
|
|
|
||||||
|
|
@ -594,12 +594,10 @@ fn generate_impl_text_inner(
|
||||||
let generic_params = adt.generic_param_list().map(|generic_params| {
|
let generic_params = adt.generic_param_list().map(|generic_params| {
|
||||||
let lifetime_params =
|
let lifetime_params =
|
||||||
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
||||||
let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
|
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
||||||
match param {
|
let param = match param {
|
||||||
ast::TypeOrConstParam::Type(param) => {
|
ast::TypeOrConstParam::Type(param) => {
|
||||||
let param = param.clone_for_update();
|
|
||||||
// remove defaults since they can't be specified in impls
|
// remove defaults since they can't be specified in impls
|
||||||
param.remove_default();
|
|
||||||
let mut bounds =
|
let mut bounds =
|
||||||
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
|
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
|
||||||
if let Some(trait_) = trait_text {
|
if let Some(trait_) = trait_text {
|
||||||
|
|
@ -610,17 +608,16 @@ fn generate_impl_text_inner(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// `{ty_param}: {bounds}`
|
// `{ty_param}: {bounds}`
|
||||||
let param =
|
let param = make::type_param(param.name()?, make::type_bound_list(bounds));
|
||||||
make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
|
|
||||||
ast::GenericParam::TypeParam(param)
|
ast::GenericParam::TypeParam(param)
|
||||||
}
|
}
|
||||||
ast::TypeOrConstParam::Const(param) => {
|
ast::TypeOrConstParam::Const(param) => {
|
||||||
let param = param.clone_for_update();
|
|
||||||
// remove defaults since they can't be specified in impls
|
// remove defaults since they can't be specified in impls
|
||||||
param.remove_default();
|
let param = make::const_param(param.name()?, param.ty()?);
|
||||||
ast::GenericParam::ConstParam(param)
|
ast::GenericParam::ConstParam(param)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Some(param)
|
||||||
});
|
});
|
||||||
|
|
||||||
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
||||||
|
|
@ -695,12 +692,10 @@ fn generate_impl_inner(
|
||||||
let generic_params = adt.generic_param_list().map(|generic_params| {
|
let generic_params = adt.generic_param_list().map(|generic_params| {
|
||||||
let lifetime_params =
|
let lifetime_params =
|
||||||
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
||||||
let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
|
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
||||||
match param {
|
let param = match param {
|
||||||
ast::TypeOrConstParam::Type(param) => {
|
ast::TypeOrConstParam::Type(param) => {
|
||||||
let param = param.clone_for_update();
|
|
||||||
// remove defaults since they can't be specified in impls
|
// remove defaults since they can't be specified in impls
|
||||||
param.remove_default();
|
|
||||||
let mut bounds =
|
let mut bounds =
|
||||||
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
|
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
|
||||||
if let Some(trait_) = &trait_ {
|
if let Some(trait_) = &trait_ {
|
||||||
|
|
@ -711,17 +706,16 @@ fn generate_impl_inner(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// `{ty_param}: {bounds}`
|
// `{ty_param}: {bounds}`
|
||||||
let param =
|
let param = make::type_param(param.name()?, make::type_bound_list(bounds));
|
||||||
make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
|
|
||||||
ast::GenericParam::TypeParam(param)
|
ast::GenericParam::TypeParam(param)
|
||||||
}
|
}
|
||||||
ast::TypeOrConstParam::Const(param) => {
|
ast::TypeOrConstParam::Const(param) => {
|
||||||
let param = param.clone_for_update();
|
|
||||||
// remove defaults since they can't be specified in impls
|
// remove defaults since they can't be specified in impls
|
||||||
param.remove_default();
|
let param = make::const_param(param.name()?, param.ty()?);
|
||||||
ast::GenericParam::ConstParam(param)
|
ast::GenericParam::ConstParam(param)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Some(param)
|
||||||
});
|
});
|
||||||
|
|
||||||
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
||||||
|
|
@ -749,16 +743,23 @@ fn generate_impl_inner(
|
||||||
.clone_for_update();
|
.clone_for_update();
|
||||||
|
|
||||||
// Copy any cfg attrs from the original adt
|
// Copy any cfg attrs from the original adt
|
||||||
let cfg_attrs = adt
|
add_cfg_attrs_to(adt, &impl_);
|
||||||
.attrs()
|
|
||||||
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
|
|
||||||
for attr in cfg_attrs {
|
|
||||||
impl_.add_attr(attr.clone_for_update());
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_
|
impl_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_cfg_attrs_to<T, U>(from: &T, to: &U)
|
||||||
|
where
|
||||||
|
T: HasAttrs,
|
||||||
|
U: AttrsOwnerEdit,
|
||||||
|
{
|
||||||
|
let cfg_attrs =
|
||||||
|
from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
|
||||||
|
for attr in cfg_attrs {
|
||||||
|
to.add_attr(attr.clone_for_update());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn add_method_to_adt(
|
pub(crate) fn add_method_to_adt(
|
||||||
builder: &mut SourceChangeBuilder,
|
builder: &mut SourceChangeBuilder,
|
||||||
adt: &ast::Adt,
|
adt: &ast::Adt,
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ use ide_db::{
|
||||||
SymbolKind, documentation::HasDocs, path_transform::PathTransform,
|
SymbolKind, documentation::HasDocs, path_transform::PathTransform,
|
||||||
syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items,
|
syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items,
|
||||||
};
|
};
|
||||||
|
use syntax::ast::HasGenericParams;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr,
|
AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr,
|
||||||
ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
|
ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
|
||||||
|
|
@ -388,6 +389,12 @@ fn add_type_alias_impl(
|
||||||
{
|
{
|
||||||
end
|
end
|
||||||
} else if let Some(end) = transformed_ty.eq_token().map(|tok| tok.text_range().start())
|
} else if let Some(end) = transformed_ty.eq_token().map(|tok| tok.text_range().start())
|
||||||
|
{
|
||||||
|
end
|
||||||
|
} else if let Some(end) = transformed_ty
|
||||||
|
.where_clause()
|
||||||
|
.and_then(|wc| wc.where_token())
|
||||||
|
.map(|tok| tok.text_range().start())
|
||||||
{
|
{
|
||||||
end
|
end
|
||||||
} else if let Some(end) =
|
} else if let Some(end) =
|
||||||
|
|
@ -400,17 +407,29 @@ fn add_type_alias_impl(
|
||||||
|
|
||||||
let len = end - start;
|
let len = end - start;
|
||||||
let mut decl = transformed_ty.syntax().text().slice(..len).to_string();
|
let mut decl = transformed_ty.syntax().text().slice(..len).to_string();
|
||||||
if !decl.ends_with(' ') {
|
decl.truncate(decl.trim_end().len());
|
||||||
decl.push(' ');
|
decl.push_str(" = ");
|
||||||
}
|
|
||||||
decl.push_str("= ");
|
let wc = transformed_ty
|
||||||
|
.where_clause()
|
||||||
|
.map(|wc| {
|
||||||
|
let ws = wc
|
||||||
|
.where_token()
|
||||||
|
.and_then(|it| it.prev_token())
|
||||||
|
.filter(|token| token.kind() == SyntaxKind::WHITESPACE)
|
||||||
|
.map(|token| token.to_string())
|
||||||
|
.unwrap_or_else(|| " ".into());
|
||||||
|
format!("{ws}{wc}")
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
match ctx.config.snippet_cap {
|
match ctx.config.snippet_cap {
|
||||||
Some(cap) => {
|
Some(cap) => {
|
||||||
let snippet = format!("{decl}$0;");
|
let snippet = format!("{decl}$0{wc};");
|
||||||
item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
|
item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
decl.push_str(&wc);
|
||||||
item.text_edit(TextEdit::replace(replacement_range, decl));
|
item.text_edit(TextEdit::replace(replacement_range, decl));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1437,6 +1456,30 @@ trait Tr<'b> {
|
||||||
impl<'b> Tr<'b> for () {
|
impl<'b> Tr<'b> for () {
|
||||||
type Ty<'a: 'b, T: Copy, const C: usize> = $0;
|
type Ty<'a: 'b, T: Copy, const C: usize> = $0;
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn includes_where_clause() {
|
||||||
|
check_edit(
|
||||||
|
"type Ty",
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
type Ty where Self: Copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tr for () {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
type Ty where Self: Copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tr for () {
|
||||||
|
type Ty = $0 where Self: Copy;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,33 @@ type O = $0;
|
||||||
r"
|
r"
|
||||||
struct A;
|
struct A;
|
||||||
trait B {
|
trait B {
|
||||||
|
type O<'a>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
}
|
||||||
|
impl B for A {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r#"
|
||||||
|
struct A;
|
||||||
|
trait B {
|
||||||
|
type O<'a>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
}
|
||||||
|
impl B for A {
|
||||||
|
type O<'a> = $0
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_edit(
|
||||||
|
"type O",
|
||||||
|
r"
|
||||||
|
struct A;
|
||||||
|
trait B {
|
||||||
type O: ?Sized = u32;
|
type O: ?Sized = u32;
|
||||||
}
|
}
|
||||||
impl B for A {
|
impl B for A {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,40 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
FileSymbol {
|
||||||
|
name: "A",
|
||||||
|
def: Variant(
|
||||||
|
Variant {
|
||||||
|
id: EnumVariantId(
|
||||||
|
7800,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
loc: DeclarationLocation {
|
||||||
|
hir_file_id: FileId(
|
||||||
|
EditionedFileId(
|
||||||
|
Id(2000),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ptr: SyntaxNodePtr {
|
||||||
|
kind: VARIANT,
|
||||||
|
range: 201..202,
|
||||||
|
},
|
||||||
|
name_ptr: AstPtr(
|
||||||
|
SyntaxNodePtr {
|
||||||
|
kind: NAME,
|
||||||
|
range: 201..202,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
container_name: Some(
|
||||||
|
"Enum",
|
||||||
|
),
|
||||||
|
is_alias: false,
|
||||||
|
is_assoc: true,
|
||||||
|
is_import: false,
|
||||||
|
do_not_complete: Yes,
|
||||||
|
},
|
||||||
FileSymbol {
|
FileSymbol {
|
||||||
name: "Alias",
|
name: "Alias",
|
||||||
def: TypeAlias(
|
def: TypeAlias(
|
||||||
|
|
@ -42,6 +76,40 @@
|
||||||
is_import: false,
|
is_import: false,
|
||||||
do_not_complete: Yes,
|
do_not_complete: Yes,
|
||||||
},
|
},
|
||||||
|
FileSymbol {
|
||||||
|
name: "B",
|
||||||
|
def: Variant(
|
||||||
|
Variant {
|
||||||
|
id: EnumVariantId(
|
||||||
|
7801,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
loc: DeclarationLocation {
|
||||||
|
hir_file_id: FileId(
|
||||||
|
EditionedFileId(
|
||||||
|
Id(2000),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ptr: SyntaxNodePtr {
|
||||||
|
kind: VARIANT,
|
||||||
|
range: 204..205,
|
||||||
|
},
|
||||||
|
name_ptr: AstPtr(
|
||||||
|
SyntaxNodePtr {
|
||||||
|
kind: NAME,
|
||||||
|
range: 204..205,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
container_name: Some(
|
||||||
|
"Enum",
|
||||||
|
),
|
||||||
|
is_alias: false,
|
||||||
|
is_assoc: true,
|
||||||
|
is_import: false,
|
||||||
|
do_not_complete: Yes,
|
||||||
|
},
|
||||||
FileSymbol {
|
FileSymbol {
|
||||||
name: "CONST",
|
name: "CONST",
|
||||||
def: Const(
|
def: Const(
|
||||||
|
|
|
||||||
|
|
@ -183,4 +183,28 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_assoc_type_infer_lifetime_in_expr_position() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
struct Player;
|
||||||
|
|
||||||
|
struct Foo<'c, C> {
|
||||||
|
_v: &'c C,
|
||||||
|
}
|
||||||
|
trait WithSignals: Sized {
|
||||||
|
type SignalCollection<'c, C>;
|
||||||
|
fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self>;
|
||||||
|
}
|
||||||
|
impl WithSignals for Player {
|
||||||
|
type SignalCollection<'c, C> = Foo<'c, C>;
|
||||||
|
fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self> {
|
||||||
|
Self::SignalCollection { _v: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -979,6 +979,21 @@ fn test() {
|
||||||
let foo = 10;
|
let foo = 10;
|
||||||
let bar = true;
|
let bar = true;
|
||||||
let _x = format_args!("{} {0} {} {last}", foo, bar, last = "!");
|
let _x = format_args!("{} {0} {} {last}", foo, bar, last = "!");
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn naked_asm_is_safe() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! naked_asm { () => {} }
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
extern "C" fn naked() {
|
||||||
|
naked_asm!("");
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -505,7 +505,7 @@ fn map_links<'e>(
|
||||||
Event::End(Tag::Link(link_type, target, _)) => {
|
Event::End(Tag::Link(link_type, target, _)) => {
|
||||||
in_link = false;
|
in_link = false;
|
||||||
Event::End(Tag::Link(
|
Event::End(Tag::Link(
|
||||||
end_link_type.unwrap_or(link_type),
|
end_link_type.take().unwrap_or(link_type),
|
||||||
end_link_target.take().unwrap_or(target),
|
end_link_target.take().unwrap_or(target),
|
||||||
CowStr::Borrowed(""),
|
CowStr::Borrowed(""),
|
||||||
))
|
))
|
||||||
|
|
@ -514,7 +514,7 @@ fn map_links<'e>(
|
||||||
let (link_type, link_target_s, link_name) =
|
let (link_type, link_target_s, link_name) =
|
||||||
callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
|
callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
|
||||||
end_link_target = Some(CowStr::Boxed(link_target_s.into()));
|
end_link_target = Some(CowStr::Boxed(link_target_s.into()));
|
||||||
if !matches!(end_link_type, Some(LinkType::Autolink)) {
|
if !matches!(end_link_type, Some(LinkType::Autolink)) && link_type.is_some() {
|
||||||
end_link_type = link_type;
|
end_link_type = link_type;
|
||||||
}
|
}
|
||||||
Event::Text(CowStr::Boxed(link_name.into()))
|
Event::Text(CowStr::Boxed(link_name.into()))
|
||||||
|
|
@ -523,7 +523,7 @@ fn map_links<'e>(
|
||||||
let (link_type, link_target_s, link_name) =
|
let (link_type, link_target_s, link_name) =
|
||||||
callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
|
callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
|
||||||
end_link_target = Some(CowStr::Boxed(link_target_s.into()));
|
end_link_target = Some(CowStr::Boxed(link_target_s.into()));
|
||||||
if !matches!(end_link_type, Some(LinkType::Autolink)) {
|
if !matches!(end_link_type, Some(LinkType::Autolink)) && link_type.is_some() {
|
||||||
end_link_type = link_type;
|
end_link_type = link_type;
|
||||||
}
|
}
|
||||||
Event::Code(CowStr::Boxed(link_name.into()))
|
Event::Code(CowStr::Boxed(link_name.into()))
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ pub enum FoldKind {
|
||||||
WhereClause,
|
WhereClause,
|
||||||
ReturnType,
|
ReturnType,
|
||||||
MatchArm,
|
MatchArm,
|
||||||
|
Function,
|
||||||
// region: item runs
|
// region: item runs
|
||||||
Modules,
|
Modules,
|
||||||
Consts,
|
Consts,
|
||||||
|
|
@ -47,6 +48,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
let mut visited_comments = FxHashSet::default();
|
let mut visited_comments = FxHashSet::default();
|
||||||
let mut visited_nodes = FxHashSet::default();
|
let mut visited_nodes = FxHashSet::default();
|
||||||
|
let mut merged_fn_bodies = FxHashSet::default();
|
||||||
|
|
||||||
// regions can be nested, here is a LIFO buffer
|
// regions can be nested, here is a LIFO buffer
|
||||||
let mut region_starts: Vec<TextSize> = vec![];
|
let mut region_starts: Vec<TextSize> = vec![];
|
||||||
|
|
@ -59,6 +61,32 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
|
||||||
NodeOrToken::Token(token) => token.text().contains('\n'),
|
NodeOrToken::Token(token) => token.text().contains('\n'),
|
||||||
};
|
};
|
||||||
if is_multiline {
|
if is_multiline {
|
||||||
|
// for the func with multiline param list
|
||||||
|
if matches!(element.kind(), FN) {
|
||||||
|
if let NodeOrToken::Node(node) = &element {
|
||||||
|
if let Some(fn_node) = ast::Fn::cast(node.clone()) {
|
||||||
|
if !fn_node
|
||||||
|
.param_list()
|
||||||
|
.map(|param_list| param_list.syntax().text().contains_char('\n'))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(body) = fn_node.body() {
|
||||||
|
res.push(Fold {
|
||||||
|
range: TextRange::new(
|
||||||
|
node.text_range().start(),
|
||||||
|
node.text_range().end(),
|
||||||
|
),
|
||||||
|
kind: FoldKind::Function,
|
||||||
|
});
|
||||||
|
merged_fn_bodies.insert(body.syntax().text_range());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
res.push(Fold { range: element.text_range(), kind });
|
res.push(Fold { range: element.text_range(), kind });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +180,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
|
||||||
ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList),
|
ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList),
|
||||||
ARRAY_EXPR => Some(FoldKind::Array),
|
ARRAY_EXPR => Some(FoldKind::Array),
|
||||||
RET_TYPE => Some(FoldKind::ReturnType),
|
RET_TYPE => Some(FoldKind::ReturnType),
|
||||||
|
FN => Some(FoldKind::Function),
|
||||||
WHERE_CLAUSE => Some(FoldKind::WhereClause),
|
WHERE_CLAUSE => Some(FoldKind::WhereClause),
|
||||||
ASSOC_ITEM_LIST
|
ASSOC_ITEM_LIST
|
||||||
| RECORD_FIELD_LIST
|
| RECORD_FIELD_LIST
|
||||||
|
|
@ -291,6 +320,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||||
let (ranges, text) = extract_tags(ra_fixture, "fold");
|
let (ranges, text) = extract_tags(ra_fixture, "fold");
|
||||||
|
|
||||||
|
|
@ -322,6 +352,7 @@ mod tests {
|
||||||
FoldKind::WhereClause => "whereclause",
|
FoldKind::WhereClause => "whereclause",
|
||||||
FoldKind::ReturnType => "returntype",
|
FoldKind::ReturnType => "returntype",
|
||||||
FoldKind::MatchArm => "matcharm",
|
FoldKind::MatchArm => "matcharm",
|
||||||
|
FoldKind::Function => "function",
|
||||||
FoldKind::TraitAliases => "traitaliases",
|
FoldKind::TraitAliases => "traitaliases",
|
||||||
FoldKind::ExternCrates => "externcrates",
|
FoldKind::ExternCrates => "externcrates",
|
||||||
};
|
};
|
||||||
|
|
@ -329,6 +360,23 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fold_func_with_multiline_param_list() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
<fold function>fn func<fold arglist>(
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
c: i32,
|
||||||
|
)</fold> <fold block>{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}</fold></fold>
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fold_comments() {
|
fn test_fold_comments() {
|
||||||
check(
|
check(
|
||||||
|
|
@ -541,10 +589,10 @@ const _: S = S <fold block>{
|
||||||
fn fold_multiline_params() {
|
fn fold_multiline_params() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
fn foo<fold arglist>(
|
<fold function>fn foo<fold arglist>(
|
||||||
x: i32,
|
x: i32,
|
||||||
y: String,
|
y: String,
|
||||||
)</fold> {}
|
)</fold> {}</fold>
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10958,3 +10958,68 @@ fn bar$0() -> Foo {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_20190() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
/// [`foo` bar](Foo).
|
||||||
|
fn has_docs$0() {}
|
||||||
|
"#,
|
||||||
|
expect.
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_20225() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized
|
||||||
|
trait Trait {
|
||||||
|
type Type<'a, T: ?Sized + 'a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Borrowed {}
|
||||||
|
|
||||||
|
impl Trait for Borrowed {
|
||||||
|
type Type<'a, T: ?Sized + 'a> = &'a T;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Enum<'a, T: Trait + 'a> {
|
||||||
|
Variant1(T::Type<'a, [Enum<'a, T>]>),
|
||||||
|
Variant2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Enum<'_, Borrowed> {
|
||||||
|
const CONSTANT$0: Self = Self::Variant1(&[Self::Variant2]);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*CONSTANT*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Enum
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const CONSTANT: Self = Variant1(&[Variant2])
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ pub(super) fn hints(
|
||||||
},
|
},
|
||||||
MirSpan::Unknown => continue,
|
MirSpan::Unknown => continue,
|
||||||
};
|
};
|
||||||
let binding = &hir.bindings[binding_idx];
|
let binding = &hir[binding_idx];
|
||||||
let name = binding.name.display_no_db(display_target.edition).to_smolstr();
|
let name = binding.name.display_no_db(display_target.edition).to_smolstr();
|
||||||
if name.starts_with("<ra@") {
|
if name.starts_with("<ra@") {
|
||||||
continue; // Ignore desugared variables
|
continue; // Ignore desugared variables
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,12 @@ pub(super) fn hints(
|
||||||
let parent = path.syntax().parent()?;
|
let parent = path.syntax().parent()?;
|
||||||
let range = match path {
|
let range = match path {
|
||||||
Either::Left(path) => {
|
Either::Left(path) => {
|
||||||
let paren =
|
let paren = parent
|
||||||
parent.ancestors().take_while(|it| ast::ParenType::can_cast(it.kind())).last();
|
.ancestors()
|
||||||
|
.take_while(|it| {
|
||||||
|
ast::ParenType::can_cast(it.kind()) || ast::ForType::can_cast(it.kind())
|
||||||
|
})
|
||||||
|
.last();
|
||||||
let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
|
let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
|
||||||
if ast::TypeBound::can_cast(parent.kind())
|
if ast::TypeBound::can_cast(parent.kind())
|
||||||
|| ast::TypeAnchor::can_cast(parent.kind())
|
|| ast::TypeAnchor::can_cast(parent.kind())
|
||||||
|
|
@ -34,7 +38,7 @@ pub(super) fn hints(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
sema.resolve_trait(&path.path()?)?;
|
sema.resolve_trait(&path.path()?)?;
|
||||||
paren.map_or_else(|| path.syntax().text_range(), |it| it.text_range())
|
path.syntax().text_range()
|
||||||
}
|
}
|
||||||
Either::Right(dyn_) => {
|
Either::Right(dyn_) => {
|
||||||
if dyn_.dyn_token().is_some() {
|
if dyn_.dyn_token().is_some() {
|
||||||
|
|
@ -89,7 +93,7 @@ fn foo(_: &T, _: for<'a> T) {}
|
||||||
impl T {}
|
impl T {}
|
||||||
// ^ dyn
|
// ^ dyn
|
||||||
impl T for (T) {}
|
impl T for (T) {}
|
||||||
// ^^^ dyn
|
// ^ dyn
|
||||||
impl T
|
impl T
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
@ -112,7 +116,7 @@ fn foo(
|
||||||
_: &mut (T + T)
|
_: &mut (T + T)
|
||||||
// ^^^^^ dyn
|
// ^^^^^ dyn
|
||||||
_: *mut (T),
|
_: *mut (T),
|
||||||
// ^^^ dyn
|
// ^ dyn
|
||||||
) {}
|
) {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
@ -136,4 +140,26 @@ fn foo(
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hrtb_bound_does_not_add_dyn() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn test<F>(f: F) where F: for<'a> FnOnce(&'a i32) {}
|
||||||
|
// ^: Sized
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_parentheses() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait T {}
|
||||||
|
fn foo(v: &(T)) {}
|
||||||
|
// ^ dyn
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal};
|
pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal, parse_asm_expr};
|
||||||
pub(crate) use atom::{block_expr, match_arm_list};
|
pub(crate) use atom::{block_expr, match_arm_list};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
|
|
|
||||||
|
|
@ -253,8 +253,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
p.bump_remap(T![builtin]);
|
p.bump_remap(T![builtin]);
|
||||||
p.bump(T![#]);
|
p.bump(T![#]);
|
||||||
if p.at_contextual_kw(T![offset_of]) {
|
if p.eat_contextual_kw(T![offset_of]) {
|
||||||
p.bump_remap(T![offset_of]);
|
|
||||||
p.expect(T!['(']);
|
p.expect(T!['(']);
|
||||||
type_(p);
|
type_(p);
|
||||||
p.expect(T![,]);
|
p.expect(T![,]);
|
||||||
|
|
@ -278,8 +277,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
||||||
p.expect(T![')']);
|
p.expect(T![')']);
|
||||||
}
|
}
|
||||||
Some(m.complete(p, OFFSET_OF_EXPR))
|
Some(m.complete(p, OFFSET_OF_EXPR))
|
||||||
} else if p.at_contextual_kw(T![format_args]) {
|
} else if p.eat_contextual_kw(T![format_args]) {
|
||||||
p.bump_remap(T![format_args]);
|
|
||||||
p.expect(T!['(']);
|
p.expect(T!['(']);
|
||||||
expr(p);
|
expr(p);
|
||||||
if p.eat(T![,]) {
|
if p.eat(T![,]) {
|
||||||
|
|
@ -302,7 +300,16 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
||||||
}
|
}
|
||||||
p.expect(T![')']);
|
p.expect(T![')']);
|
||||||
Some(m.complete(p, FORMAT_ARGS_EXPR))
|
Some(m.complete(p, FORMAT_ARGS_EXPR))
|
||||||
} else if p.at_contextual_kw(T![asm]) {
|
} else if p.eat_contextual_kw(T![asm])
|
||||||
|
|| p.eat_contextual_kw(T![global_asm])
|
||||||
|
|| p.eat_contextual_kw(T![naked_asm])
|
||||||
|
{
|
||||||
|
// test asm_kinds
|
||||||
|
// fn foo() {
|
||||||
|
// builtin#asm("");
|
||||||
|
// builtin#global_asm("");
|
||||||
|
// builtin#naked_asm("");
|
||||||
|
// }
|
||||||
parse_asm_expr(p, m)
|
parse_asm_expr(p, m)
|
||||||
} else {
|
} else {
|
||||||
m.abandon(p);
|
m.abandon(p);
|
||||||
|
|
@ -321,8 +328,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
||||||
// tmp = out(reg) _,
|
// tmp = out(reg) _,
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
pub(crate) fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
p.bump_remap(T![asm]);
|
|
||||||
p.expect(T!['(']);
|
p.expect(T!['(']);
|
||||||
if expr(p).is_none() {
|
if expr(p).is_none() {
|
||||||
p.err_and_bump("expected asm template");
|
p.err_and_bump("expected asm template");
|
||||||
|
|
@ -411,11 +417,10 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
dir_spec.abandon(p);
|
dir_spec.abandon(p);
|
||||||
op.abandon(p);
|
op.abandon(p);
|
||||||
op_n.abandon(p);
|
op_n.abandon(p);
|
||||||
p.err_and_bump("expected asm operand");
|
|
||||||
|
|
||||||
// improves error recovery and handles err_and_bump recovering from `{` which gets
|
// improves error recovery
|
||||||
// the parser stuck here
|
|
||||||
if p.at(T!['{']) {
|
if p.at(T!['{']) {
|
||||||
|
p.error("expected asm operand");
|
||||||
// test_err bad_asm_expr
|
// test_err bad_asm_expr
|
||||||
// fn foo() {
|
// fn foo() {
|
||||||
// builtin#asm(
|
// builtin#asm(
|
||||||
|
|
@ -423,6 +428,8 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
expr(p);
|
expr(p);
|
||||||
|
} else {
|
||||||
|
p.err_and_bump("expected asm operand");
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.at(T!['}']) {
|
if p.at(T!['}']) {
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,19 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
|
||||||
T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
|
T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
|
||||||
T![static] if (la == IDENT || la == T![_] || la == T![mut]) => consts::static_(p, m),
|
T![static] if (la == IDENT || la == T![_] || la == T![mut]) => consts::static_(p, m),
|
||||||
|
|
||||||
|
IDENT
|
||||||
|
if p.at_contextual_kw(T![builtin])
|
||||||
|
&& p.nth_at(1, T![#])
|
||||||
|
&& p.nth_at_contextual_kw(2, T![global_asm]) =>
|
||||||
|
{
|
||||||
|
p.bump_remap(T![builtin]);
|
||||||
|
p.bump(T![#]);
|
||||||
|
p.bump_remap(T![global_asm]);
|
||||||
|
// test global_asm
|
||||||
|
// builtin#global_asm("")
|
||||||
|
expressions::parse_asm_expr(p, m);
|
||||||
|
}
|
||||||
|
|
||||||
_ => return Err(m),
|
_ => return Err(m),
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use rustc_literal_escaper::{
|
use rustc_literal_escaper::{
|
||||||
unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, EscapeError,
|
EscapeError, Mode, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char,
|
||||||
Mode,
|
unescape_str,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ pub(crate) struct Parser<'t> {
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PARSER_STEP_LIMIT: usize = 15_000_000;
|
const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15_000_000 };
|
||||||
|
|
||||||
impl<'t> Parser<'t> {
|
impl<'t> Parser<'t> {
|
||||||
pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
|
pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
|
||||||
|
|
@ -254,7 +254,10 @@ impl<'t> Parser<'t> {
|
||||||
|
|
||||||
/// Create an error node and consume the next token.
|
/// Create an error node and consume the next token.
|
||||||
pub(crate) fn err_and_bump(&mut self, message: &str) {
|
pub(crate) fn err_and_bump(&mut self, message: &str) {
|
||||||
self.err_recover(message, TokenSet::EMPTY);
|
let m = self.start();
|
||||||
|
self.error(message);
|
||||||
|
self.bump_any();
|
||||||
|
m.complete(self, ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an error node and consume the next token unless it is in the recovery set.
|
/// Create an error node and consume the next token unless it is in the recovery set.
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -21,6 +21,8 @@ mod ok {
|
||||||
#[test]
|
#[test]
|
||||||
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
|
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
|
||||||
#[test]
|
#[test]
|
||||||
|
fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); }
|
||||||
|
#[test]
|
||||||
fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
|
fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
|
||||||
#[test]
|
#[test]
|
||||||
fn assoc_const_eq() {
|
fn assoc_const_eq() {
|
||||||
|
|
@ -298,6 +300,8 @@ mod ok {
|
||||||
run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_list.rs");
|
run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_list.rs");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
fn global_asm() { run_and_expect_no_errors("test_data/parser/inline/ok/global_asm.rs"); }
|
||||||
|
#[test]
|
||||||
fn half_open_range_pat() {
|
fn half_open_range_pat() {
|
||||||
run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs");
|
run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
crates/parser/test_data/parser/inline/ok/asm_kinds.rast
Normal file
48
crates/parser/test_data/parser/inline/ok/asm_kinds.rast
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
SOURCE_FILE
|
||||||
|
FN
|
||||||
|
FN_KW "fn"
|
||||||
|
WHITESPACE " "
|
||||||
|
NAME
|
||||||
|
IDENT "foo"
|
||||||
|
PARAM_LIST
|
||||||
|
L_PAREN "("
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE " "
|
||||||
|
BLOCK_EXPR
|
||||||
|
STMT_LIST
|
||||||
|
L_CURLY "{"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
ASM_EXPR
|
||||||
|
BUILTIN_KW "builtin"
|
||||||
|
POUND "#"
|
||||||
|
ASM_KW "asm"
|
||||||
|
L_PAREN "("
|
||||||
|
LITERAL
|
||||||
|
STRING "\"\""
|
||||||
|
R_PAREN ")"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
ASM_EXPR
|
||||||
|
BUILTIN_KW "builtin"
|
||||||
|
POUND "#"
|
||||||
|
GLOBAL_ASM_KW "global_asm"
|
||||||
|
L_PAREN "("
|
||||||
|
LITERAL
|
||||||
|
STRING "\"\""
|
||||||
|
R_PAREN ")"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n "
|
||||||
|
EXPR_STMT
|
||||||
|
ASM_EXPR
|
||||||
|
BUILTIN_KW "builtin"
|
||||||
|
POUND "#"
|
||||||
|
NAKED_ASM_KW "naked_asm"
|
||||||
|
L_PAREN "("
|
||||||
|
LITERAL
|
||||||
|
STRING "\"\""
|
||||||
|
R_PAREN ")"
|
||||||
|
SEMICOLON ";"
|
||||||
|
WHITESPACE "\n"
|
||||||
|
R_CURLY "}"
|
||||||
|
WHITESPACE "\n"
|
||||||
5
crates/parser/test_data/parser/inline/ok/asm_kinds.rs
Normal file
5
crates/parser/test_data/parser/inline/ok/asm_kinds.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn foo() {
|
||||||
|
builtin#asm("");
|
||||||
|
builtin#global_asm("");
|
||||||
|
builtin#naked_asm("");
|
||||||
|
}
|
||||||
10
crates/parser/test_data/parser/inline/ok/global_asm.rast
Normal file
10
crates/parser/test_data/parser/inline/ok/global_asm.rast
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
SOURCE_FILE
|
||||||
|
ASM_EXPR
|
||||||
|
BUILTIN_KW "builtin"
|
||||||
|
POUND "#"
|
||||||
|
GLOBAL_ASM_KW "global_asm"
|
||||||
|
L_PAREN "("
|
||||||
|
LITERAL
|
||||||
|
STRING "\"\""
|
||||||
|
R_PAREN ")"
|
||||||
|
WHITESPACE "\n"
|
||||||
1
crates/parser/test_data/parser/inline/ok/global_asm.rs
Normal file
1
crates/parser/test_data/parser/inline/ok/global_asm.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
builtin#global_asm("")
|
||||||
34
crates/project-model/src/cargo_config_file.rs
Normal file
34
crates/project-model/src/cargo_config_file.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
//! Read `.cargo/config.toml` as a JSON object
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use toolchain::Tool;
|
||||||
|
|
||||||
|
use crate::{ManifestPath, Sysroot, utf8_stdout};
|
||||||
|
|
||||||
|
pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
|
||||||
|
|
||||||
|
pub(crate) fn read(
|
||||||
|
manifest: &ManifestPath,
|
||||||
|
extra_env: &FxHashMap<String, Option<String>>,
|
||||||
|
sysroot: &Sysroot,
|
||||||
|
) -> Option<CargoConfigFile> {
|
||||||
|
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
||||||
|
cargo_config
|
||||||
|
.args(["-Z", "unstable-options", "config", "get", "--format", "json"])
|
||||||
|
.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
if manifest.is_rust_manifest() {
|
||||||
|
cargo_config.arg("-Zscript");
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::debug!("Discovering cargo config by {:?}", cargo_config);
|
||||||
|
let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
|
||||||
|
.inspect(|json| {
|
||||||
|
tracing::debug!("Discovered cargo config: {:?}", json);
|
||||||
|
})
|
||||||
|
.inspect_err(|err| {
|
||||||
|
tracing::debug!("Failed to discover cargo config: {:?}", err);
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.and_then(|stdout| serde_json::from_str(&stdout).ok())?;
|
||||||
|
|
||||||
|
Some(json)
|
||||||
|
}
|
||||||
|
|
@ -300,8 +300,6 @@ pub struct CargoMetadataConfig {
|
||||||
pub extra_args: Vec<String>,
|
pub extra_args: Vec<String>,
|
||||||
/// Extra env vars to set when invoking the cargo command
|
/// Extra env vars to set when invoking the cargo command
|
||||||
pub extra_env: FxHashMap<String, Option<String>>,
|
pub extra_env: FxHashMap<String, Option<String>>,
|
||||||
/// The target dir for this workspace load.
|
|
||||||
pub target_dir: Utf8PathBuf,
|
|
||||||
/// What kind of metadata are we fetching: workspace, rustc, or sysroot.
|
/// What kind of metadata are we fetching: workspace, rustc, or sysroot.
|
||||||
pub kind: &'static str,
|
pub kind: &'static str,
|
||||||
/// The toolchain version, if known.
|
/// The toolchain version, if known.
|
||||||
|
|
@ -317,188 +315,6 @@ struct PackageMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CargoWorkspace {
|
impl CargoWorkspace {
|
||||||
/// Fetches the metadata for the given `cargo_toml` manifest.
|
|
||||||
/// A successful result may contain another metadata error if the initial fetching failed but
|
|
||||||
/// the `--no-deps` retry succeeded.
|
|
||||||
///
|
|
||||||
/// The sysroot is used to set the `RUSTUP_TOOLCHAIN` env var when invoking cargo
|
|
||||||
/// to ensure that the rustup proxy uses the correct toolchain.
|
|
||||||
pub fn fetch_metadata(
|
|
||||||
cargo_toml: &ManifestPath,
|
|
||||||
current_dir: &AbsPath,
|
|
||||||
config: &CargoMetadataConfig,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
no_deps: bool,
|
|
||||||
locked: bool,
|
|
||||||
progress: &dyn Fn(String),
|
|
||||||
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
|
||||||
let res = Self::fetch_metadata_(
|
|
||||||
cargo_toml,
|
|
||||||
current_dir,
|
|
||||||
config,
|
|
||||||
sysroot,
|
|
||||||
no_deps,
|
|
||||||
locked,
|
|
||||||
progress,
|
|
||||||
);
|
|
||||||
if let Ok((_, Some(ref e))) = res {
|
|
||||||
tracing::warn!(
|
|
||||||
%cargo_toml,
|
|
||||||
?e,
|
|
||||||
"`cargo metadata` failed, but retry with `--no-deps` succeeded"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_metadata_(
|
|
||||||
cargo_toml: &ManifestPath,
|
|
||||||
current_dir: &AbsPath,
|
|
||||||
config: &CargoMetadataConfig,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
no_deps: bool,
|
|
||||||
locked: bool,
|
|
||||||
progress: &dyn Fn(String),
|
|
||||||
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
|
||||||
let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
|
|
||||||
let mut meta = MetadataCommand::new();
|
|
||||||
meta.cargo_path(cargo.get_program());
|
|
||||||
cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default()));
|
|
||||||
meta.manifest_path(cargo_toml.to_path_buf());
|
|
||||||
match &config.features {
|
|
||||||
CargoFeatures::All => {
|
|
||||||
meta.features(CargoOpt::AllFeatures);
|
|
||||||
}
|
|
||||||
CargoFeatures::Selected { features, no_default_features } => {
|
|
||||||
if *no_default_features {
|
|
||||||
meta.features(CargoOpt::NoDefaultFeatures);
|
|
||||||
}
|
|
||||||
if !features.is_empty() {
|
|
||||||
meta.features(CargoOpt::SomeFeatures(features.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
meta.current_dir(current_dir);
|
|
||||||
|
|
||||||
let mut other_options = vec![];
|
|
||||||
// cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
|
|
||||||
// the only relevant flags for metadata here are unstable ones, so we pass those along
|
|
||||||
// but nothing else
|
|
||||||
let mut extra_args = config.extra_args.iter();
|
|
||||||
while let Some(arg) = extra_args.next() {
|
|
||||||
if arg == "-Z" {
|
|
||||||
if let Some(arg) = extra_args.next() {
|
|
||||||
other_options.push("-Z".to_owned());
|
|
||||||
other_options.push(arg.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !config.targets.is_empty() {
|
|
||||||
other_options.extend(
|
|
||||||
config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if no_deps {
|
|
||||||
other_options.push("--no-deps".to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut using_lockfile_copy = false;
|
|
||||||
// The manifest is a rust file, so this means its a script manifest
|
|
||||||
if cargo_toml.is_rust_manifest() {
|
|
||||||
other_options.push("-Zscript".to_owned());
|
|
||||||
} else if config
|
|
||||||
.toolchain_version
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
|
|
||||||
{
|
|
||||||
let lockfile = <_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock");
|
|
||||||
let target_lockfile = config
|
|
||||||
.target_dir
|
|
||||||
.join("rust-analyzer")
|
|
||||||
.join("metadata")
|
|
||||||
.join(config.kind)
|
|
||||||
.join("Cargo.lock");
|
|
||||||
match std::fs::copy(&lockfile, &target_lockfile) {
|
|
||||||
Ok(_) => {
|
|
||||||
using_lockfile_copy = true;
|
|
||||||
other_options.push("--lockfile-path".to_owned());
|
|
||||||
other_options.push(target_lockfile.to_string());
|
|
||||||
}
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
|
||||||
// There exists no lockfile yet
|
|
||||||
using_lockfile_copy = true;
|
|
||||||
other_options.push("--lockfile-path".to_owned());
|
|
||||||
other_options.push(target_lockfile.to_string());
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::warn!(
|
|
||||||
"Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if using_lockfile_copy {
|
|
||||||
other_options.push("-Zunstable-options".to_owned());
|
|
||||||
meta.env("RUSTC_BOOTSTRAP", "1");
|
|
||||||
}
|
|
||||||
// No need to lock it if we copied the lockfile, we won't modify the original after all/
|
|
||||||
// This way cargo cannot error out on us if the lockfile requires updating.
|
|
||||||
if !using_lockfile_copy && locked {
|
|
||||||
other_options.push("--locked".to_owned());
|
|
||||||
}
|
|
||||||
meta.other_options(other_options);
|
|
||||||
|
|
||||||
// FIXME: Fetching metadata is a slow process, as it might require
|
|
||||||
// calling crates.io. We should be reporting progress here, but it's
|
|
||||||
// unclear whether cargo itself supports it.
|
|
||||||
progress("cargo metadata: started".to_owned());
|
|
||||||
|
|
||||||
let res = (|| -> anyhow::Result<(_, _)> {
|
|
||||||
let mut errored = false;
|
|
||||||
let output =
|
|
||||||
spawn_with_streaming_output(meta.cargo_command(), &mut |_| (), &mut |line| {
|
|
||||||
errored = errored || line.starts_with("error") || line.starts_with("warning");
|
|
||||||
if errored {
|
|
||||||
progress("cargo metadata: ?".to_owned());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
progress(format!("cargo metadata: {line}"));
|
|
||||||
})?;
|
|
||||||
if !output.status.success() {
|
|
||||||
progress(format!("cargo metadata: failed {}", output.status));
|
|
||||||
let error = cargo_metadata::Error::CargoMetadata {
|
|
||||||
stderr: String::from_utf8(output.stderr)?,
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
if !no_deps {
|
|
||||||
// If we failed to fetch metadata with deps, try again without them.
|
|
||||||
// This makes r-a still work partially when offline.
|
|
||||||
if let Ok((metadata, _)) = Self::fetch_metadata_(
|
|
||||||
cargo_toml,
|
|
||||||
current_dir,
|
|
||||||
config,
|
|
||||||
sysroot,
|
|
||||||
true,
|
|
||||||
locked,
|
|
||||||
progress,
|
|
||||||
) {
|
|
||||||
return Ok((metadata, Some(error)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
let stdout = from_utf8(&output.stdout)?
|
|
||||||
.lines()
|
|
||||||
.find(|line| line.starts_with('{'))
|
|
||||||
.ok_or(cargo_metadata::Error::NoJson)?;
|
|
||||||
Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
|
|
||||||
})()
|
|
||||||
.with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()));
|
|
||||||
progress("cargo metadata: finished".to_owned());
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut meta: cargo_metadata::Metadata,
|
mut meta: cargo_metadata::Metadata,
|
||||||
ws_manifest_path: ManifestPath,
|
ws_manifest_path: ManifestPath,
|
||||||
|
|
@ -733,3 +549,214 @@ impl CargoWorkspace {
|
||||||
self.requires_rustc_private
|
self.requires_rustc_private
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FetchMetadata {
|
||||||
|
command: cargo_metadata::MetadataCommand,
|
||||||
|
lockfile_path: Option<Utf8PathBuf>,
|
||||||
|
kind: &'static str,
|
||||||
|
no_deps: bool,
|
||||||
|
no_deps_result: anyhow::Result<cargo_metadata::Metadata>,
|
||||||
|
other_options: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchMetadata {
|
||||||
|
/// Builds a command to fetch metadata for the given `cargo_toml` manifest.
|
||||||
|
///
|
||||||
|
/// Performs a lightweight pre-fetch using the `--no-deps` option,
|
||||||
|
/// available via [`FetchMetadata::no_deps_metadata`], to gather basic
|
||||||
|
/// information such as the `target-dir`.
|
||||||
|
///
|
||||||
|
/// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN`
|
||||||
|
/// environment variable when invoking Cargo, ensuring that the
|
||||||
|
/// rustup proxy selects the correct toolchain.
|
||||||
|
pub(crate) fn new(
|
||||||
|
cargo_toml: &ManifestPath,
|
||||||
|
current_dir: &AbsPath,
|
||||||
|
config: &CargoMetadataConfig,
|
||||||
|
sysroot: &Sysroot,
|
||||||
|
no_deps: bool,
|
||||||
|
) -> Self {
|
||||||
|
let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
|
||||||
|
let mut command = MetadataCommand::new();
|
||||||
|
command.cargo_path(cargo.get_program());
|
||||||
|
cargo.get_envs().for_each(|(var, val)| _ = command.env(var, val.unwrap_or_default()));
|
||||||
|
command.manifest_path(cargo_toml.to_path_buf());
|
||||||
|
match &config.features {
|
||||||
|
CargoFeatures::All => {
|
||||||
|
command.features(CargoOpt::AllFeatures);
|
||||||
|
}
|
||||||
|
CargoFeatures::Selected { features, no_default_features } => {
|
||||||
|
if *no_default_features {
|
||||||
|
command.features(CargoOpt::NoDefaultFeatures);
|
||||||
|
}
|
||||||
|
if !features.is_empty() {
|
||||||
|
command.features(CargoOpt::SomeFeatures(features.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.current_dir(current_dir);
|
||||||
|
|
||||||
|
let mut needs_nightly = false;
|
||||||
|
let mut other_options = vec![];
|
||||||
|
// cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
|
||||||
|
// the only relevant flags for metadata here are unstable ones, so we pass those along
|
||||||
|
// but nothing else
|
||||||
|
let mut extra_args = config.extra_args.iter();
|
||||||
|
while let Some(arg) = extra_args.next() {
|
||||||
|
if arg == "-Z" {
|
||||||
|
if let Some(arg) = extra_args.next() {
|
||||||
|
needs_nightly = true;
|
||||||
|
other_options.push("-Z".to_owned());
|
||||||
|
other_options.push(arg.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut lockfile_path = None;
|
||||||
|
if cargo_toml.is_rust_manifest() {
|
||||||
|
needs_nightly = true;
|
||||||
|
other_options.push("-Zscript".to_owned());
|
||||||
|
} else if config
|
||||||
|
.toolchain_version
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
|
||||||
|
{
|
||||||
|
lockfile_path = Some(<_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.targets.is_empty() {
|
||||||
|
other_options.extend(
|
||||||
|
config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
command.other_options(other_options.clone());
|
||||||
|
|
||||||
|
if needs_nightly {
|
||||||
|
command.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-fetch basic metadata using `--no-deps`, which:
|
||||||
|
// - avoids fetching registries like crates.io,
|
||||||
|
// - skips dependency resolution and does not modify lockfiles,
|
||||||
|
// - and thus doesn't require progress reporting or copying lockfiles.
|
||||||
|
//
|
||||||
|
// Useful as a fast fallback to extract info like `target-dir`.
|
||||||
|
let cargo_command;
|
||||||
|
let no_deps_result = if no_deps {
|
||||||
|
command.no_deps();
|
||||||
|
cargo_command = command.cargo_command();
|
||||||
|
command.exec()
|
||||||
|
} else {
|
||||||
|
let mut no_deps_command = command.clone();
|
||||||
|
no_deps_command.no_deps();
|
||||||
|
cargo_command = no_deps_command.cargo_command();
|
||||||
|
no_deps_command.exec()
|
||||||
|
}
|
||||||
|
.with_context(|| format!("Failed to run `{cargo_command:?}`"));
|
||||||
|
|
||||||
|
Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> {
|
||||||
|
self.no_deps_result.as_ref().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the metadata-fetching command.
|
||||||
|
///
|
||||||
|
/// A successful result may still contain a metadata error if the full fetch failed,
|
||||||
|
/// but the fallback `--no-deps` pre-fetch succeeded during command construction.
|
||||||
|
pub(crate) fn exec(
|
||||||
|
self,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
|
locked: bool,
|
||||||
|
progress: &dyn Fn(String),
|
||||||
|
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
||||||
|
let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } =
|
||||||
|
self;
|
||||||
|
|
||||||
|
if no_deps {
|
||||||
|
return no_deps_result.map(|m| (m, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut using_lockfile_copy = false;
|
||||||
|
// The manifest is a rust file, so this means its a script manifest
|
||||||
|
if let Some(lockfile) = lockfile_path {
|
||||||
|
let target_lockfile =
|
||||||
|
target_dir.join("rust-analyzer").join("metadata").join(kind).join("Cargo.lock");
|
||||||
|
match std::fs::copy(&lockfile, &target_lockfile) {
|
||||||
|
Ok(_) => {
|
||||||
|
using_lockfile_copy = true;
|
||||||
|
other_options.push("--lockfile-path".to_owned());
|
||||||
|
other_options.push(target_lockfile.to_string());
|
||||||
|
}
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
// There exists no lockfile yet
|
||||||
|
using_lockfile_copy = true;
|
||||||
|
other_options.push("--lockfile-path".to_owned());
|
||||||
|
other_options.push(target_lockfile.to_string());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
"Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if using_lockfile_copy {
|
||||||
|
other_options.push("-Zunstable-options".to_owned());
|
||||||
|
command.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
}
|
||||||
|
// No need to lock it if we copied the lockfile, we won't modify the original after all/
|
||||||
|
// This way cargo cannot error out on us if the lockfile requires updating.
|
||||||
|
if !using_lockfile_copy && locked {
|
||||||
|
other_options.push("--locked".to_owned());
|
||||||
|
}
|
||||||
|
command.other_options(other_options);
|
||||||
|
|
||||||
|
// FIXME: Fetching metadata is a slow process, as it might require
|
||||||
|
// calling crates.io. We should be reporting progress here, but it's
|
||||||
|
// unclear whether cargo itself supports it.
|
||||||
|
progress("cargo metadata: started".to_owned());
|
||||||
|
|
||||||
|
let res = (|| -> anyhow::Result<(_, _)> {
|
||||||
|
let mut errored = false;
|
||||||
|
let output =
|
||||||
|
spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| {
|
||||||
|
errored = errored || line.starts_with("error") || line.starts_with("warning");
|
||||||
|
if errored {
|
||||||
|
progress("cargo metadata: ?".to_owned());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progress(format!("cargo metadata: {line}"));
|
||||||
|
})?;
|
||||||
|
if !output.status.success() {
|
||||||
|
progress(format!("cargo metadata: failed {}", output.status));
|
||||||
|
let error = cargo_metadata::Error::CargoMetadata {
|
||||||
|
stderr: String::from_utf8(output.stderr)?,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
if !no_deps {
|
||||||
|
// If we failed to fetch metadata with deps, return pre-fetched result without them.
|
||||||
|
// This makes r-a still work partially when offline.
|
||||||
|
if let Ok(metadata) = no_deps_result {
|
||||||
|
tracing::warn!(
|
||||||
|
?error,
|
||||||
|
"`cargo metadata` failed and returning succeeded result with `--no-deps`"
|
||||||
|
);
|
||||||
|
return Ok((metadata, Some(error)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
let stdout = from_utf8(&output.stdout)?
|
||||||
|
.lines()
|
||||||
|
.find(|line| line.starts_with('{'))
|
||||||
|
.ok_or(cargo_metadata::Error::NoJson)?;
|
||||||
|
Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
|
||||||
|
})()
|
||||||
|
.with_context(|| format!("Failed to run `{:?}`", command.cargo_command()));
|
||||||
|
progress("cargo metadata: finished".to_owned());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
//! Cargo-like environment variables injection.
|
//! Cargo-like environment variables injection.
|
||||||
use base_db::Env;
|
use base_db::Env;
|
||||||
use paths::{Utf8Path, Utf8PathBuf};
|
use paths::Utf8Path;
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use toolchain::Tool;
|
use toolchain::Tool;
|
||||||
|
|
||||||
use crate::{ManifestPath, PackageData, Sysroot, TargetKind, utf8_stdout};
|
use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
|
||||||
|
|
||||||
/// Recreates the compile-time environment variables that Cargo sets.
|
/// Recreates the compile-time environment variables that Cargo sets.
|
||||||
///
|
///
|
||||||
|
|
@ -61,104 +60,68 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
|
||||||
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
|
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cargo_config_env(
|
pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoConfigFile>) -> Env {
|
||||||
manifest: &ManifestPath,
|
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
) -> Env {
|
|
||||||
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
|
||||||
cargo_config
|
|
||||||
.args(["-Z", "unstable-options", "config", "get", "env"])
|
|
||||||
.env("RUSTC_BOOTSTRAP", "1");
|
|
||||||
if manifest.is_rust_manifest() {
|
|
||||||
cargo_config.arg("-Zscript");
|
|
||||||
}
|
|
||||||
// if successful we receive `env.key.value = "value" per entry
|
|
||||||
tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
|
|
||||||
utf8_stdout(&mut cargo_config)
|
|
||||||
.map(|stdout| parse_output_cargo_config_env(manifest, &stdout))
|
|
||||||
.inspect(|env| {
|
|
||||||
tracing::debug!("Discovered cargo config env: {:?}", env);
|
|
||||||
})
|
|
||||||
.inspect_err(|err| {
|
|
||||||
tracing::debug!("Failed to discover cargo config env: {:?}", err);
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env {
|
|
||||||
let mut env = Env::default();
|
let mut env = Env::default();
|
||||||
let mut relatives = vec![];
|
let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
|
||||||
for (key, val) in
|
else {
|
||||||
stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = "))
|
return env;
|
||||||
{
|
};
|
||||||
let val = val.trim_matches('"').to_owned();
|
|
||||||
if let Some((key, modifier)) = key.split_once('.') {
|
|
||||||
match modifier {
|
|
||||||
"relative" => relatives.push((key, val)),
|
|
||||||
"value" => _ = env.insert(key, val),
|
|
||||||
_ => {
|
|
||||||
tracing::warn!(
|
|
||||||
"Unknown modifier in cargo config env: {}, expected `relative` or `value`",
|
|
||||||
modifier
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
env.insert(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
|
// FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
|
||||||
// But cargo does not provide this information.
|
// But cargo does not provide this information.
|
||||||
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
|
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
|
||||||
for (key, relative) in relatives {
|
|
||||||
if relative != "true" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(suffix) = env.get(key) {
|
|
||||||
env.insert(key, base.join(suffix).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn cargo_config_build_target_dir(
|
for (key, entry) in env_json {
|
||||||
manifest: &ManifestPath,
|
let serde_json::Value::Object(entry) = entry else {
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
continue;
|
||||||
sysroot: &Sysroot,
|
};
|
||||||
) -> Option<Utf8PathBuf> {
|
let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
|
||||||
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
continue;
|
||||||
cargo_config
|
};
|
||||||
.args(["-Z", "unstable-options", "config", "get", "build.target-dir"])
|
|
||||||
.env("RUSTC_BOOTSTRAP", "1");
|
let value = if entry
|
||||||
if manifest.is_rust_manifest() {
|
.get("relative")
|
||||||
cargo_config.arg("-Zscript");
|
.and_then(|v| v.as_bool())
|
||||||
|
.is_some_and(std::convert::identity)
|
||||||
|
{
|
||||||
|
base.join(value).to_string()
|
||||||
|
} else {
|
||||||
|
value.to_owned()
|
||||||
|
};
|
||||||
|
env.insert(key, value);
|
||||||
}
|
}
|
||||||
utf8_stdout(&mut cargo_config)
|
|
||||||
.map(|stdout| {
|
env
|
||||||
Utf8Path::new(stdout.trim_start_matches("build.target-dir = ").trim_matches('"'))
|
|
||||||
.to_owned()
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_output_cargo_config_env_works() {
|
fn parse_output_cargo_config_env_works() {
|
||||||
let stdout = r#"
|
let raw = r#"
|
||||||
env.CARGO_WORKSPACE_DIR.relative = true
|
{
|
||||||
env.CARGO_WORKSPACE_DIR.value = ""
|
"env": {
|
||||||
env.RELATIVE.relative = true
|
"CARGO_WORKSPACE_DIR": {
|
||||||
env.RELATIVE.value = "../relative"
|
"relative": true,
|
||||||
env.INVALID.relative = invalidbool
|
"value": ""
|
||||||
env.INVALID.value = "../relative"
|
},
|
||||||
env.TEST.value = "test"
|
"INVALID": {
|
||||||
"#
|
"relative": "invalidbool",
|
||||||
.trim();
|
"value": "../relative"
|
||||||
|
},
|
||||||
|
"RELATIVE": {
|
||||||
|
"relative": true,
|
||||||
|
"value": "../relative"
|
||||||
|
},
|
||||||
|
"TEST": {
|
||||||
|
"value": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
|
||||||
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
|
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
|
||||||
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
|
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
|
||||||
let manifest = ManifestPath::try_from(manifest).unwrap();
|
let manifest = ManifestPath::try_from(manifest).unwrap();
|
||||||
let env = parse_output_cargo_config_env(&manifest, stdout);
|
let env = cargo_config_env(&manifest, &Some(config));
|
||||||
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
|
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
|
||||||
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
|
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
|
||||||
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
|
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub mod toolchain_info {
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{ManifestPath, Sysroot};
|
use crate::{ManifestPath, Sysroot, cargo_config_file::CargoConfigFile};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum QueryConfig<'a> {
|
pub enum QueryConfig<'a> {
|
||||||
|
|
@ -32,11 +32,12 @@ pub mod toolchain_info {
|
||||||
Rustc(&'a Sysroot, &'a Path),
|
Rustc(&'a Sysroot, &'a Path),
|
||||||
/// Attempt to use cargo to query the desired information, honoring cargo configurations.
|
/// Attempt to use cargo to query the desired information, honoring cargo configurations.
|
||||||
/// If this fails, falls back to invoking `rustc` directly.
|
/// If this fails, falls back to invoking `rustc` directly.
|
||||||
Cargo(&'a Sysroot, &'a ManifestPath),
|
Cargo(&'a Sysroot, &'a ManifestPath, &'a Option<CargoConfigFile>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod build_dependencies;
|
mod build_dependencies;
|
||||||
|
mod cargo_config_file;
|
||||||
mod cargo_workspace;
|
mod cargo_workspace;
|
||||||
mod env;
|
mod env;
|
||||||
mod manifest_path;
|
mod manifest_path;
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ use std::{env, fs, ops::Not, path::Path, process::Command};
|
||||||
|
|
||||||
use anyhow::{Result, format_err};
|
use anyhow::{Result, format_err};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use toolchain::{Tool, probe_for_binary};
|
use toolchain::{Tool, probe_for_binary};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CargoWorkspace, ManifestPath, ProjectJson, RustSourceWorkspaceConfig,
|
CargoWorkspace, ManifestPath, ProjectJson, RustSourceWorkspaceConfig,
|
||||||
cargo_workspace::CargoMetadataConfig, utf8_stdout,
|
cargo_workspace::{CargoMetadataConfig, FetchMetadata},
|
||||||
|
utf8_stdout,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -211,6 +212,7 @@ impl Sysroot {
|
||||||
sysroot_source_config: &RustSourceWorkspaceConfig,
|
sysroot_source_config: &RustSourceWorkspaceConfig,
|
||||||
no_deps: bool,
|
no_deps: bool,
|
||||||
current_dir: &AbsPath,
|
current_dir: &AbsPath,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
) -> Option<RustLibSrcWorkspace> {
|
) -> Option<RustLibSrcWorkspace> {
|
||||||
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
|
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
|
||||||
|
|
@ -224,6 +226,7 @@ impl Sysroot {
|
||||||
match self.load_library_via_cargo(
|
match self.load_library_via_cargo(
|
||||||
&library_manifest,
|
&library_manifest,
|
||||||
current_dir,
|
current_dir,
|
||||||
|
target_dir,
|
||||||
cargo_config,
|
cargo_config,
|
||||||
no_deps,
|
no_deps,
|
||||||
progress,
|
progress,
|
||||||
|
|
@ -319,6 +322,7 @@ impl Sysroot {
|
||||||
&self,
|
&self,
|
||||||
library_manifest: &ManifestPath,
|
library_manifest: &ManifestPath,
|
||||||
current_dir: &AbsPath,
|
current_dir: &AbsPath,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
cargo_config: &CargoMetadataConfig,
|
cargo_config: &CargoMetadataConfig,
|
||||||
no_deps: bool,
|
no_deps: bool,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
|
|
@ -331,16 +335,11 @@ impl Sysroot {
|
||||||
Some("nightly".to_owned()),
|
Some("nightly".to_owned()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (mut res, _) = CargoWorkspace::fetch_metadata(
|
|
||||||
library_manifest,
|
|
||||||
current_dir,
|
|
||||||
&cargo_config,
|
|
||||||
self,
|
|
||||||
no_deps,
|
|
||||||
// Make sure we never attempt to write to the sysroot
|
// Make sure we never attempt to write to the sysroot
|
||||||
true,
|
let locked = true;
|
||||||
progress,
|
let (mut res, _) =
|
||||||
)?;
|
FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps)
|
||||||
|
.exec(target_dir, locked, progress)?;
|
||||||
|
|
||||||
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
||||||
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
|
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
|
||||||
|
|
|
||||||
|
|
@ -239,8 +239,13 @@ fn smoke_test_real_sysroot_cargo() {
|
||||||
);
|
);
|
||||||
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
|
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
|
||||||
std::fs::create_dir_all(&cwd).unwrap();
|
std::fs::create_dir_all(&cwd).unwrap();
|
||||||
let loaded_sysroot =
|
let loaded_sysroot = sysroot.load_workspace(
|
||||||
sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &cwd, &|_| ());
|
&RustSourceWorkspaceConfig::default_cargo(),
|
||||||
|
false,
|
||||||
|
&cwd,
|
||||||
|
&Utf8PathBuf::default(),
|
||||||
|
&|_| (),
|
||||||
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
sysroot.set_workspace(loaded_sysroot);
|
sysroot.set_workspace(loaded_sysroot);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ fn rustc_print_cfg(
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
|
const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
|
||||||
let (sysroot, current_dir) = match config {
|
let (sysroot, current_dir) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
|
||||||
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
||||||
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
|
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
|
||||||
if let Some(target) = target {
|
if let Some(target) = target {
|
||||||
|
|
@ -109,7 +109,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
|
assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub fn get(
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let (sysroot, current_dir) = match config {
|
let (sysroot, current_dir) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
|
||||||
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
||||||
cmd.env("RUSTC_BOOTSTRAP", "1");
|
cmd.env("RUSTC_BOOTSTRAP", "1");
|
||||||
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
|
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
|
||||||
|
|
@ -66,7 +66,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ use anyhow::Context;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use toolchain::Tool;
|
use toolchain::Tool;
|
||||||
|
|
||||||
use crate::{ManifestPath, Sysroot, toolchain_info::QueryConfig, utf8_stdout};
|
use crate::{
|
||||||
|
Sysroot, cargo_config_file::CargoConfigFile, toolchain_info::QueryConfig, utf8_stdout,
|
||||||
|
};
|
||||||
|
|
||||||
/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
|
/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
|
||||||
/// For rustc, runs `rustc --print -vV` to get the host target.
|
/// For rustc, runs `rustc --print -vV` to get the host target.
|
||||||
|
|
@ -20,8 +22,8 @@ pub fn get(
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sysroot, current_dir) = match config {
|
let (sysroot, current_dir) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, config_file) => {
|
||||||
match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
|
match config_file.as_ref().and_then(cargo_config_build_target) {
|
||||||
Some(it) => return Ok(it),
|
Some(it) => return Ok(it),
|
||||||
None => (sysroot, cargo_toml.parent().as_ref()),
|
None => (sysroot, cargo_toml.parent().as_ref()),
|
||||||
}
|
}
|
||||||
|
|
@ -50,30 +52,30 @@ fn rustc_discover_host_tuple(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cargo_config_build_target(
|
fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
|
||||||
cargo_toml: &ManifestPath,
|
match parse_json_cargo_config_build_target(config) {
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
Ok(v) => v,
|
||||||
sysroot: &Sysroot,
|
Err(e) => {
|
||||||
) -> Option<Vec<String>> {
|
tracing::debug!("Failed to discover cargo config build target {e:?}");
|
||||||
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
None
|
||||||
cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
|
}
|
||||||
cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
|
}
|
||||||
// if successful we receive `build.target = "target-tuple"`
|
|
||||||
// or `build.target = ["<target 1>", ..]`
|
|
||||||
// this might be `error: config value `build.target` is not set` in which case we
|
|
||||||
// don't wanna log the error
|
|
||||||
utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
|
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
|
||||||
fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
|
fn parse_json_cargo_config_build_target(
|
||||||
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
|
config: &CargoConfigFile,
|
||||||
|
) -> anyhow::Result<Option<Vec<String>>> {
|
||||||
if !trimmed.starts_with('[') {
|
let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
|
||||||
return Ok([trimmed.to_owned()].to_vec());
|
match target {
|
||||||
|
Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
|
||||||
|
Some(v) => serde_json::from_value(v.clone())
|
||||||
|
.map(Option::Some)
|
||||||
|
.context("Failed to parse `build.target` as an array of target"),
|
||||||
|
// t`error: config value `build.target` is not set`, in which case we
|
||||||
|
// don't wanna log the error
|
||||||
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
|
|
||||||
serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -90,7 +92,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub(crate) fn get(
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
extra_env: &FxHashMap<String, Option<String>>,
|
||||||
) -> Result<Option<Version>, anyhow::Error> {
|
) -> Result<Option<Version>, anyhow::Error> {
|
||||||
let (mut cmd, prefix) = match config {
|
let (mut cmd, prefix) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
|
||||||
(sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
|
(sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
|
||||||
}
|
}
|
||||||
QueryConfig::Rustc(sysroot, current_dir) => {
|
QueryConfig::Rustc(sysroot, current_dir) => {
|
||||||
|
|
@ -44,7 +44,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert!(get(cfg, &FxHashMap::default()).is_ok());
|
assert!(get(cfg, &FxHashMap::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,9 @@ use crate::{
|
||||||
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
|
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
|
||||||
WorkspaceBuildScripts,
|
WorkspaceBuildScripts,
|
||||||
build_dependencies::BuildScriptOutput,
|
build_dependencies::BuildScriptOutput,
|
||||||
cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
|
cargo_config_file,
|
||||||
env::{
|
cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
|
||||||
cargo_config_build_target_dir, cargo_config_env, inject_cargo_env,
|
env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
|
||||||
inject_cargo_package_env, inject_rustc_tool_env,
|
|
||||||
},
|
|
||||||
project_json::{Crate, CrateArrayIdx},
|
project_json::{Crate, CrateArrayIdx},
|
||||||
sysroot::RustLibSrcWorkspace,
|
sysroot::RustLibSrcWorkspace,
|
||||||
toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
|
toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
|
||||||
|
|
@ -270,7 +268,9 @@ impl ProjectWorkspace {
|
||||||
|
|
||||||
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
|
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
|
||||||
progress("querying project metadata".to_owned());
|
progress("querying project metadata".to_owned());
|
||||||
let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
|
let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
|
||||||
|
let config_file_ = config_file.clone();
|
||||||
|
let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
|
||||||
let targets =
|
let targets =
|
||||||
target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
|
target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
|
||||||
let toolchain = version::get(toolchain_config, extra_env)
|
let toolchain = version::get(toolchain_config, extra_env)
|
||||||
|
|
@ -282,10 +282,24 @@ impl ProjectWorkspace {
|
||||||
.ok()
|
.ok()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
|
let fetch_metadata = FetchMetadata::new(
|
||||||
|
cargo_toml,
|
||||||
|
workspace_dir,
|
||||||
|
&CargoMetadataConfig {
|
||||||
|
features: features.clone(),
|
||||||
|
targets: targets.clone(),
|
||||||
|
extra_args: extra_args.clone(),
|
||||||
|
extra_env: extra_env.clone(),
|
||||||
|
toolchain_version: toolchain.clone(),
|
||||||
|
kind: "workspace",
|
||||||
|
},
|
||||||
|
&sysroot,
|
||||||
|
*no_deps,
|
||||||
|
);
|
||||||
let target_dir = config
|
let target_dir = config
|
||||||
.target_dir
|
.target_dir
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| cargo_config_build_target_dir(cargo_toml, extra_env, &sysroot))
|
.or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
|
||||||
.unwrap_or_else(|| workspace_dir.join("target").into());
|
.unwrap_or_else(|| workspace_dir.join("target").into());
|
||||||
|
|
||||||
// We spawn a bunch of processes to query various information about the workspace's
|
// We spawn a bunch of processes to query various information about the workspace's
|
||||||
|
|
@ -319,7 +333,7 @@ impl ProjectWorkspace {
|
||||||
};
|
};
|
||||||
rustc_dir.and_then(|rustc_dir| {
|
rustc_dir.and_then(|rustc_dir| {
|
||||||
info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
|
info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
|
||||||
match CargoWorkspace::fetch_metadata(
|
match FetchMetadata::new(
|
||||||
&rustc_dir,
|
&rustc_dir,
|
||||||
workspace_dir,
|
workspace_dir,
|
||||||
&CargoMetadataConfig {
|
&CargoMetadataConfig {
|
||||||
|
|
@ -327,15 +341,12 @@ impl ProjectWorkspace {
|
||||||
targets: targets.clone(),
|
targets: targets.clone(),
|
||||||
extra_args: extra_args.clone(),
|
extra_args: extra_args.clone(),
|
||||||
extra_env: extra_env.clone(),
|
extra_env: extra_env.clone(),
|
||||||
target_dir: target_dir.clone(),
|
|
||||||
toolchain_version: toolchain.clone(),
|
toolchain_version: toolchain.clone(),
|
||||||
kind: "rustc-dev"
|
kind: "rustc-dev"
|
||||||
},
|
},
|
||||||
&sysroot,
|
&sysroot,
|
||||||
*no_deps,
|
*no_deps,
|
||||||
true,
|
).exec(&target_dir, true, progress) {
|
||||||
progress,
|
|
||||||
) {
|
|
||||||
Ok((meta, _error)) => {
|
Ok((meta, _error)) => {
|
||||||
let workspace = CargoWorkspace::new(
|
let workspace = CargoWorkspace::new(
|
||||||
meta,
|
meta,
|
||||||
|
|
@ -364,40 +375,22 @@ impl ProjectWorkspace {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let cargo_metadata = s.spawn(|| {
|
let cargo_metadata = s.spawn(|| fetch_metadata.exec(&target_dir, false, progress));
|
||||||
CargoWorkspace::fetch_metadata(
|
|
||||||
cargo_toml,
|
|
||||||
workspace_dir,
|
|
||||||
&CargoMetadataConfig {
|
|
||||||
features: features.clone(),
|
|
||||||
targets: targets.clone(),
|
|
||||||
extra_args: extra_args.clone(),
|
|
||||||
extra_env: extra_env.clone(),
|
|
||||||
target_dir: target_dir.clone(),
|
|
||||||
toolchain_version: toolchain.clone(),
|
|
||||||
kind: "workspace",
|
|
||||||
},
|
|
||||||
&sysroot,
|
|
||||||
*no_deps,
|
|
||||||
false,
|
|
||||||
progress,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let loaded_sysroot = s.spawn(|| {
|
let loaded_sysroot = s.spawn(|| {
|
||||||
sysroot.load_workspace(
|
sysroot.load_workspace(
|
||||||
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
||||||
config,
|
config,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
target_dir.clone(),
|
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
workspace_dir,
|
workspace_dir,
|
||||||
|
&target_dir,
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let cargo_config_extra_env =
|
let cargo_config_extra_env =
|
||||||
s.spawn(|| cargo_config_env(cargo_toml, extra_env, &sysroot));
|
s.spawn(move || cargo_config_env(cargo_toml, &config_file));
|
||||||
thread::Result::Ok((
|
thread::Result::Ok((
|
||||||
rustc_cfg.join()?,
|
rustc_cfg.join()?,
|
||||||
data_layout.join()?,
|
data_layout.join()?,
|
||||||
|
|
@ -476,9 +469,7 @@ impl ProjectWorkspace {
|
||||||
let target_dir = config
|
let target_dir = config
|
||||||
.target_dir
|
.target_dir
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| {
|
.or_else(|| cargo_target_dir(project_json.manifest()?, &config.extra_env, &sysroot))
|
||||||
cargo_config_build_target_dir(project_json.manifest()?, &config.extra_env, &sysroot)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| project_root.join("target").into());
|
.unwrap_or_else(|| project_root.join("target").into());
|
||||||
|
|
||||||
// We spawn a bunch of processes to query various information about the workspace's
|
// We spawn a bunch of processes to query various information about the workspace's
|
||||||
|
|
@ -502,6 +493,7 @@ impl ProjectWorkspace {
|
||||||
&RustSourceWorkspaceConfig::Json(*sysroot_project),
|
&RustSourceWorkspaceConfig::Json(*sysroot_project),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
project_root,
|
project_root,
|
||||||
|
&target_dir,
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -510,10 +502,10 @@ impl ProjectWorkspace {
|
||||||
config,
|
config,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
target_dir,
|
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
project_root,
|
project_root,
|
||||||
|
&target_dir,
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -554,7 +546,8 @@ impl ProjectWorkspace {
|
||||||
None => Sysroot::empty(),
|
None => Sysroot::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let query_config = QueryConfig::Cargo(&sysroot, detached_file);
|
let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
|
||||||
|
let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
|
||||||
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
|
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
|
||||||
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
@ -563,7 +556,7 @@ impl ProjectWorkspace {
|
||||||
let target_dir = config
|
let target_dir = config
|
||||||
.target_dir
|
.target_dir
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| cargo_config_build_target_dir(detached_file, &config.extra_env, &sysroot))
|
.or_else(|| cargo_target_dir(detached_file, &config.extra_env, &sysroot))
|
||||||
.unwrap_or_else(|| dir.join("target").into());
|
.unwrap_or_else(|| dir.join("target").into());
|
||||||
|
|
||||||
let loaded_sysroot = sysroot.load_workspace(
|
let loaded_sysroot = sysroot.load_workspace(
|
||||||
|
|
@ -571,17 +564,17 @@ impl ProjectWorkspace {
|
||||||
config,
|
config,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
target_dir.clone(),
|
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
dir,
|
dir,
|
||||||
|
&target_dir,
|
||||||
&|_| (),
|
&|_| (),
|
||||||
);
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
sysroot.set_workspace(loaded_sysroot);
|
sysroot.set_workspace(loaded_sysroot);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cargo_script = CargoWorkspace::fetch_metadata(
|
let fetch_metadata = FetchMetadata::new(
|
||||||
detached_file,
|
detached_file,
|
||||||
dir,
|
dir,
|
||||||
&CargoMetadataConfig {
|
&CargoMetadataConfig {
|
||||||
|
|
@ -589,19 +582,20 @@ impl ProjectWorkspace {
|
||||||
targets,
|
targets,
|
||||||
extra_args: config.extra_args.clone(),
|
extra_args: config.extra_args.clone(),
|
||||||
extra_env: config.extra_env.clone(),
|
extra_env: config.extra_env.clone(),
|
||||||
target_dir,
|
|
||||||
toolchain_version: toolchain.clone(),
|
toolchain_version: toolchain.clone(),
|
||||||
kind: "detached-file",
|
kind: "detached-file",
|
||||||
},
|
},
|
||||||
&sysroot,
|
&sysroot,
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
false,
|
);
|
||||||
&|_| (),
|
let target_dir = config
|
||||||
)
|
.target_dir
|
||||||
.ok()
|
.clone()
|
||||||
.map(|(ws, error)| {
|
.or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
|
||||||
let cargo_config_extra_env =
|
.unwrap_or_else(|| dir.join("target").into());
|
||||||
cargo_config_env(detached_file, &config.extra_env, &sysroot);
|
let cargo_script =
|
||||||
|
fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
|
||||||
|
let cargo_config_extra_env = cargo_config_env(detached_file, &config_file);
|
||||||
(
|
(
|
||||||
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
|
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
|
||||||
WorkspaceBuildScripts::default(),
|
WorkspaceBuildScripts::default(),
|
||||||
|
|
@ -1889,15 +1883,33 @@ fn sysroot_metadata_config(
|
||||||
config: &CargoConfig,
|
config: &CargoConfig,
|
||||||
targets: &[String],
|
targets: &[String],
|
||||||
toolchain_version: Option<Version>,
|
toolchain_version: Option<Version>,
|
||||||
target_dir: Utf8PathBuf,
|
|
||||||
) -> CargoMetadataConfig {
|
) -> CargoMetadataConfig {
|
||||||
CargoMetadataConfig {
|
CargoMetadataConfig {
|
||||||
features: Default::default(),
|
features: Default::default(),
|
||||||
targets: targets.to_vec(),
|
targets: targets.to_vec(),
|
||||||
extra_args: Default::default(),
|
extra_args: Default::default(),
|
||||||
extra_env: config.extra_env.clone(),
|
extra_env: config.extra_env.clone(),
|
||||||
target_dir,
|
|
||||||
toolchain_version,
|
toolchain_version,
|
||||||
kind: "sysroot",
|
kind: "sysroot",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cargo_target_dir(
|
||||||
|
manifest: &ManifestPath,
|
||||||
|
extra_env: &FxHashMap<String, Option<String>>,
|
||||||
|
sysroot: &Sysroot,
|
||||||
|
) -> Option<Utf8PathBuf> {
|
||||||
|
let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
||||||
|
let mut meta = cargo_metadata::MetadataCommand::new();
|
||||||
|
meta.cargo_path(cargo.get_program());
|
||||||
|
meta.manifest_path(manifest);
|
||||||
|
// `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve.
|
||||||
|
// So we can use it to get `target_directory` before copying lockfiles
|
||||||
|
let mut other_options = vec!["--no-deps".to_owned()];
|
||||||
|
if manifest.is_rust_manifest() {
|
||||||
|
meta.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
other_options.push("-Zscript".to_owned());
|
||||||
|
}
|
||||||
|
meta.other_options(other_options);
|
||||||
|
meta.exec().map(|m| m.target_directory).ok()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -796,7 +796,7 @@ impl flags::AnalysisStats {
|
||||||
// region:expressions
|
// region:expressions
|
||||||
let (previous_exprs, previous_unknown, previous_partially_unknown) =
|
let (previous_exprs, previous_unknown, previous_partially_unknown) =
|
||||||
(num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
|
(num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
|
||||||
for (expr_id, _) in body.exprs.iter() {
|
for (expr_id, _) in body.exprs() {
|
||||||
let ty = &inference_result[expr_id];
|
let ty = &inference_result[expr_id];
|
||||||
num_exprs += 1;
|
num_exprs += 1;
|
||||||
let unknown_or_partial = if ty.is_unknown() {
|
let unknown_or_partial = if ty.is_unknown() {
|
||||||
|
|
@ -901,7 +901,7 @@ impl flags::AnalysisStats {
|
||||||
// region:patterns
|
// region:patterns
|
||||||
let (previous_pats, previous_unknown, previous_partially_unknown) =
|
let (previous_pats, previous_unknown, previous_partially_unknown) =
|
||||||
(num_pats, num_pats_unknown, num_pats_partially_unknown);
|
(num_pats, num_pats_unknown, num_pats_partially_unknown);
|
||||||
for (pat_id, _) in body.pats.iter() {
|
for (pat_id, _) in body.pats() {
|
||||||
let ty = &inference_result[pat_id];
|
let ty = &inference_result[pat_id];
|
||||||
num_pats += 1;
|
num_pats += 1;
|
||||||
let unknown_or_partial = if ty.is_unknown() {
|
let unknown_or_partial = if ty.is_unknown() {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use hir::{ChangeWithProcMacros, Crate};
|
||||||
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
|
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
|
||||||
use ide_db::base_db;
|
use ide_db::base_db;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
use paths::Utf8PathBuf;
|
||||||
use profile::StopWatch;
|
use profile::StopWatch;
|
||||||
use project_model::toolchain_info::{QueryConfig, target_data_layout};
|
use project_model::toolchain_info::{QueryConfig, target_data_layout};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
|
|
@ -79,6 +80,7 @@ impl Tester {
|
||||||
&RustSourceWorkspaceConfig::default_cargo(),
|
&RustSourceWorkspaceConfig::default_cargo(),
|
||||||
false,
|
false,
|
||||||
&path,
|
&path,
|
||||||
|
&Utf8PathBuf::default(),
|
||||||
&|_| (),
|
&|_| (),
|
||||||
);
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
|
|
|
||||||
|
|
@ -911,7 +911,8 @@ pub(crate) fn folding_range(
|
||||||
| FoldKind::Array
|
| FoldKind::Array
|
||||||
| FoldKind::TraitAliases
|
| FoldKind::TraitAliases
|
||||||
| FoldKind::ExternCrates
|
| FoldKind::ExternCrates
|
||||||
| FoldKind::MatchArm => None,
|
| FoldKind::MatchArm
|
||||||
|
| FoldKind::Function => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let range = range(line_index, fold.range);
|
let range = range(line_index, fold.range);
|
||||||
|
|
|
||||||
|
|
@ -880,7 +880,8 @@ fn main() {{}}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diagnostics_dont_block_typing() {
|
fn diagnostics_dont_block_typing() {
|
||||||
if skip_slow_tests() {
|
if skip_slow_tests() || std::env::var("CI").is_ok() {
|
||||||
|
// FIXME: This test is failing too frequently (therefore we disable it on CI).
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ impl fmt::Debug for ErasedFileAstId {
|
||||||
Use,
|
Use,
|
||||||
Impl,
|
Impl,
|
||||||
BlockExpr,
|
BlockExpr,
|
||||||
|
AsmExpr,
|
||||||
Fixup,
|
Fixup,
|
||||||
);
|
);
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
|
|
@ -144,6 +145,10 @@ enum ErasedFileAstIdKind {
|
||||||
Impl,
|
Impl,
|
||||||
/// Associated with [`BlockExprFileAstId`].
|
/// Associated with [`BlockExprFileAstId`].
|
||||||
BlockExpr,
|
BlockExpr,
|
||||||
|
// `global_asm!()` is an item, so we need to give it an `AstId`. So we give to all inline asm
|
||||||
|
// because incrementality is not a problem, they will always be the only item in the macro file,
|
||||||
|
// and memory usage also not because they're rare.
|
||||||
|
AsmExpr,
|
||||||
/// Keep this last.
|
/// Keep this last.
|
||||||
Root,
|
Root,
|
||||||
}
|
}
|
||||||
|
|
@ -204,14 +209,17 @@ impl ErasedFileAstId {
|
||||||
.or_else(|| extern_block_ast_id(node, index_map))
|
.or_else(|| extern_block_ast_id(node, index_map))
|
||||||
.or_else(|| use_ast_id(node, index_map))
|
.or_else(|| use_ast_id(node, index_map))
|
||||||
.or_else(|| impl_ast_id(node, index_map))
|
.or_else(|| impl_ast_id(node, index_map))
|
||||||
|
.or_else(|| asm_expr_ast_id(node, index_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_alloc(node: &SyntaxNode) -> bool {
|
fn should_alloc(node: &SyntaxNode) -> bool {
|
||||||
should_alloc_has_name(node)
|
let kind = node.kind();
|
||||||
|| should_alloc_assoc_item(node)
|
should_alloc_has_name(kind)
|
||||||
|| ast::ExternBlock::can_cast(node.kind())
|
|| should_alloc_assoc_item(kind)
|
||||||
|| ast::Use::can_cast(node.kind())
|
|| ast::ExternBlock::can_cast(kind)
|
||||||
|| ast::Impl::can_cast(node.kind())
|
|| ast::Use::can_cast(kind)
|
||||||
|
|| ast::Impl::can_cast(kind)
|
||||||
|
|| ast::AsmExpr::can_cast(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -278,7 +286,6 @@ impl<N> FileAstId<N> {
|
||||||
|
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
struct ErasedHasNameFileAstId<'a> {
|
struct ErasedHasNameFileAstId<'a> {
|
||||||
kind: SyntaxKind,
|
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,6 +339,19 @@ fn use_ast_id(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AstIdNode for ast::AsmExpr {}
|
||||||
|
|
||||||
|
fn asm_expr_ast_id(
|
||||||
|
node: &SyntaxNode,
|
||||||
|
index_map: &mut ErasedAstIdNextIndexMap,
|
||||||
|
) -> Option<ErasedFileAstId> {
|
||||||
|
if ast::AsmExpr::can_cast(node.kind()) {
|
||||||
|
Some(index_map.new_id(ErasedFileAstIdKind::AsmExpr, ()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AstIdNode for ast::Impl {}
|
impl AstIdNode for ast::Impl {}
|
||||||
|
|
||||||
fn impl_ast_id(
|
fn impl_ast_id(
|
||||||
|
|
@ -433,7 +453,6 @@ macro_rules! register_has_name_ast_id {
|
||||||
)+
|
)+
|
||||||
|
|
||||||
fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
|
fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
|
||||||
let kind = node.kind();
|
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
$(
|
$(
|
||||||
|
|
@ -441,7 +460,6 @@ macro_rules! register_has_name_ast_id {
|
||||||
let name = node.$name_method();
|
let name = node.$name_method();
|
||||||
let name = name.as_ref().map_or("", |it| it.text_non_mutable());
|
let name = name.as_ref().map_or("", |it| it.text_non_mutable());
|
||||||
let result = ErasedHasNameFileAstId {
|
let result = ErasedHasNameFileAstId {
|
||||||
kind,
|
|
||||||
name,
|
name,
|
||||||
};
|
};
|
||||||
Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
|
Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
|
||||||
|
|
@ -452,8 +470,7 @@ macro_rules! register_has_name_ast_id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_alloc_has_name(node: &SyntaxNode) -> bool {
|
fn should_alloc_has_name(kind: SyntaxKind) -> bool {
|
||||||
let kind = node.kind();
|
|
||||||
false $( || ast::$ident::can_cast(kind) )*
|
false $( || ast::$ident::can_cast(kind) )*
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -483,7 +500,6 @@ macro_rules! register_assoc_item_ast_id {
|
||||||
index_map: &mut ErasedAstIdNextIndexMap,
|
index_map: &mut ErasedAstIdNextIndexMap,
|
||||||
parent: Option<&ErasedFileAstId>,
|
parent: Option<&ErasedFileAstId>,
|
||||||
) -> Option<ErasedFileAstId> {
|
) -> Option<ErasedFileAstId> {
|
||||||
let kind = node.kind();
|
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
$(
|
$(
|
||||||
|
|
@ -491,7 +507,6 @@ macro_rules! register_assoc_item_ast_id {
|
||||||
let name = $name_callback(node);
|
let name = $name_callback(node);
|
||||||
let name = name.as_ref().map_or("", |it| it.text_non_mutable());
|
let name = name.as_ref().map_or("", |it| it.text_non_mutable());
|
||||||
let properties = ErasedHasNameFileAstId {
|
let properties = ErasedHasNameFileAstId {
|
||||||
kind,
|
|
||||||
name,
|
name,
|
||||||
};
|
};
|
||||||
let result = ErasedAssocItemFileAstId {
|
let result = ErasedAssocItemFileAstId {
|
||||||
|
|
@ -506,8 +521,7 @@ macro_rules! register_assoc_item_ast_id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_alloc_assoc_item(node: &SyntaxNode) -> bool {
|
fn should_alloc_assoc_item(kind: SyntaxKind) -> bool {
|
||||||
let kind = node.kind();
|
|
||||||
false $( || ast::$ident::can_cast(kind) )*
|
false $( || ast::$ident::can_cast(kind) )*
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ Item =
|
||||||
| TypeAlias
|
| TypeAlias
|
||||||
| Union
|
| Union
|
||||||
| Use
|
| Use
|
||||||
|
| AsmExpr
|
||||||
|
|
||||||
MacroRules =
|
MacroRules =
|
||||||
Attr* Visibility?
|
Attr* Visibility?
|
||||||
|
|
@ -409,7 +410,8 @@ OffsetOfExpr =
|
||||||
// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
|
// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
|
||||||
// format_string := STRING_LITERAL / RAW_STRING_LITERAL
|
// format_string := STRING_LITERAL / RAW_STRING_LITERAL
|
||||||
AsmExpr =
|
AsmExpr =
|
||||||
Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
|
Attr* 'builtin' '#' ( 'asm' | 'global_asm' | 'naked_asm' )
|
||||||
|
'(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
|
||||||
|
|
||||||
// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
|
// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
|
||||||
AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
|
AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
|
||||||
|
|
|
||||||
|
|
@ -406,42 +406,6 @@ impl ast::WhereClause {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::TypeParam {
|
|
||||||
pub fn remove_default(&self) {
|
|
||||||
if let Some((eq, last)) = self
|
|
||||||
.syntax()
|
|
||||||
.children_with_tokens()
|
|
||||||
.find(|it| it.kind() == T![=])
|
|
||||||
.zip(self.syntax().last_child_or_token())
|
|
||||||
{
|
|
||||||
ted::remove_all(eq..=last);
|
|
||||||
|
|
||||||
// remove any trailing ws
|
|
||||||
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
|
|
||||||
last.detach();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ast::ConstParam {
|
|
||||||
pub fn remove_default(&self) {
|
|
||||||
if let Some((eq, last)) = self
|
|
||||||
.syntax()
|
|
||||||
.children_with_tokens()
|
|
||||||
.find(|it| it.kind() == T![=])
|
|
||||||
.zip(self.syntax().last_child_or_token())
|
|
||||||
{
|
|
||||||
ted::remove_all(eq..=last);
|
|
||||||
|
|
||||||
// remove any trailing ws
|
|
||||||
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
|
|
||||||
last.detach();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Removable: AstNode {
|
pub trait Removable: AstNode {
|
||||||
fn remove(&self);
|
fn remove(&self);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue