Auto merge of #136803 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
This commit is contained in:
commit
80c091958f
206 changed files with 5691 additions and 3913 deletions
|
@ -14,3 +14,4 @@ f247090558c9ba3c551566eae5882b7ca865225f
|
||||||
b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15
|
b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15
|
||||||
c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1
|
c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1
|
||||||
f532576ac53ddcc666bc8d59e0b6437065e2f599
|
f532576ac53ddcc666bc8d59e0b6437065e2f599
|
||||||
|
4704881b641884de50645637108b6b6f5b68aaf9
|
||||||
|
|
|
@ -11,6 +11,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
|
if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
|
||||||
name: publish
|
name: publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -9,6 +9,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-libs:
|
publish-libs:
|
||||||
|
if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
|
||||||
name: publish
|
name: publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -22,6 +22,7 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
dist:
|
dist:
|
||||||
|
if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
@ -138,6 +139,7 @@ jobs:
|
||||||
path: ./dist
|
path: ./dist
|
||||||
|
|
||||||
dist-x86_64-unknown-linux-musl:
|
dist-x86_64-unknown-linux-musl:
|
||||||
|
if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
|
||||||
name: dist (x86_64-unknown-linux-musl)
|
name: dist (x86_64-unknown-linux-musl)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
|
@ -183,6 +185,7 @@ jobs:
|
||||||
path: ./dist
|
path: ./dist
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
|
if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
|
||||||
name: publish
|
name: publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: ["dist", "dist-x86_64-unknown-linux-musl"]
|
needs: ["dist", "dist-x86_64-unknown-linux-musl"]
|
||||||
|
@ -257,24 +260,24 @@ jobs:
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
|
|
||||||
- name: Publish Extension (Code Marketplace, release)
|
- name: Publish Extension (Code Marketplace, release)
|
||||||
if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
# token from https://dev.azure.com/rust-analyzer/
|
# token from https://dev.azure.com/rust-analyzer/
|
||||||
run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
||||||
|
|
||||||
- name: Publish Extension (OpenVSX, release)
|
- name: Publish Extension (OpenVSX, release)
|
||||||
if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
|
|
||||||
- name: Publish Extension (Code Marketplace, nightly)
|
- name: Publish Extension (Code Marketplace, nightly)
|
||||||
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release
|
run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release
|
||||||
|
|
||||||
- name: Publish Extension (OpenVSX, nightly)
|
- name: Publish Extension (OpenVSX, nightly)
|
||||||
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
|
|
7
src/tools/rust-analyzer/.gitignore
vendored
7
src/tools/rust-analyzer/.gitignore
vendored
|
@ -6,10 +6,11 @@ target/
|
||||||
*.log
|
*.log
|
||||||
*.iml
|
*.iml
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
generated_assists.adoc
|
|
||||||
generated_features.adoc
|
|
||||||
generated_diagnostic.adoc
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/out/
|
/out/
|
||||||
/dump.lsif
|
/dump.lsif
|
||||||
.envrc
|
.envrc
|
||||||
|
docs/book/book
|
||||||
|
docs/book/src/assists_generated.md
|
||||||
|
docs/book/src/diagnostics_generated.md
|
||||||
|
docs/book/src/features_generated.md
|
||||||
|
|
|
@ -1408,9 +1408,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1514,9 +1514,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_abi"
|
name = "ra-ap-rustc_abi"
|
||||||
version = "0.91.0"
|
version = "0.94.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a"
|
checksum = "2fa4333df7b71217edb44a36702cafd2bcfc9850677bdf78b32c9f2c98e5df40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.7.0",
|
"bitflags 2.7.0",
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
|
@ -1525,9 +1525,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index"
|
name = "ra-ap-rustc_index"
|
||||||
version = "0.91.0"
|
version = "0.94.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160"
|
checksum = "d200275ff3d952cc11108f4dc6a692473659758623d63f2bdcea6104a7f1cec8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index_macros",
|
"ra-ap-rustc_index_macros",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
@ -1535,9 +1535,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index_macros"
|
name = "ra-ap-rustc_index_macros"
|
||||||
version = "0.91.0"
|
version = "0.94.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889"
|
checksum = "06eb63df8c1ce2dcb07647305bed811c9c5ebd157def01a81c1b9479b8592b3b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1546,19 +1546,20 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_lexer"
|
name = "ra-ap-rustc_lexer"
|
||||||
version = "0.91.0"
|
version = "0.94.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f"
|
checksum = "b7a4d402b2f85650e8c1f78e2e2defc241b03948d6e30d9f5254c9b82755cc4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_parse_format"
|
name = "ra-ap-rustc_parse_format"
|
||||||
version = "0.91.0"
|
version = "0.94.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8"
|
checksum = "a23a382dbe392beb26360c1a8ce9193155ef74eeac59bcda0fa0a233e047323a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
"ra-ap-rustc_lexer",
|
"ra-ap-rustc_lexer",
|
||||||
|
@ -1566,9 +1567,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_pattern_analysis"
|
name = "ra-ap-rustc_pattern_analysis"
|
||||||
version = "0.91.0"
|
version = "0.94.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d"
|
checksum = "d746955d67f315ab79767f1d0bbc17fee4f0970d4a00b9ad76bf09cc7d3cd17e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ra-ap-rustc_index",
|
"ra-ap-rustc_index",
|
||||||
"rustc-hash 2.0.0",
|
"rustc-hash 2.0.0",
|
||||||
|
@ -2001,9 +2002,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tenthash"
|
name = "tenthash"
|
||||||
version = "0.4.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d67f9f3cf70e0852941d7bc3cb884b49b24b8ee956baf91ad0abae31f5ef11fb"
|
checksum = "2d092d622df8bb64e5de8dc86a3667702d5f1e0fe2f0604c6035540703c8cd1e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "test-fixture"
|
name = "test-fixture"
|
||||||
|
|
|
@ -87,11 +87,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.91", default-features = false }
|
ra-ap-rustc_lexer = { version = "0.94", default-features = false }
|
||||||
ra-ap-rustc_parse_format = { version = "0.91", default-features = false }
|
ra-ap-rustc_parse_format = { version = "0.94", default-features = false }
|
||||||
ra-ap-rustc_index = { version = "0.91", default-features = false }
|
ra-ap-rustc_index = { version = "0.94", default-features = false }
|
||||||
ra-ap-rustc_abi = { version = "0.91", default-features = false }
|
ra-ap-rustc_abi = { version = "0.94", default-features = false }
|
||||||
ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false }
|
ra-ap-rustc_pattern_analysis = { version = "0.94", 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.
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
See the [Privacy](https://rust-analyzer.github.io/manual.html#privacy) section of the user manual.
|
See the [Privacy](https://rust-analyzer.github.io/book/privacy.html) section of the user manual.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/rust-analyzer/rust-analyzer/master/assets/logo-wide.svg"
|
src="https://raw.githubusercontent.com/rust-lang/rust-analyzer/master/assets/logo-wide.svg"
|
||||||
alt="rust-analyzer logo">
|
alt="rust-analyzer logo">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
https://rust-analyzer.github.io/manual.html#installation
|
https://rust-analyzer.github.io/book/installation.html
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
@ -18,12 +18,13 @@ if you are just curious about how things work under the hood, check the [./docs/
|
||||||
folder.
|
folder.
|
||||||
|
|
||||||
If you want to **use** rust-analyzer's language server with your editor of
|
If you want to **use** rust-analyzer's language server with your editor of
|
||||||
choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder.
|
choice, check [the manual](https://rust-analyzer.github.io/book/).
|
||||||
It also contains some tips & tricks to help you be more productive when using rust-analyzer.
|
It also contains some tips & tricks to help you be more productive when using rust-analyzer.
|
||||||
|
|
||||||
## Security and Privacy
|
## Security and Privacy
|
||||||
|
|
||||||
See the corresponding sections of [the manual](https://rust-analyzer.github.io/manual.html#security).
|
See the [security](https://rust-analyzer.github.io/book/security.html) and
|
||||||
|
[privacy](https://rust-analyzer.github.io/book/privacy.html) sections of the manual.
|
||||||
|
|
||||||
## Communication
|
## Communication
|
||||||
|
|
||||||
|
|
|
@ -129,9 +129,9 @@ impl fmt::Display for CrateName {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Deref for CrateName {
|
impl ops::Deref for CrateName {
|
||||||
type Target = str;
|
type Target = Symbol;
|
||||||
fn deref(&self) -> &str {
|
fn deref(&self) -> &Symbol {
|
||||||
self.0.as_str()
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +230,8 @@ impl fmt::Display for CrateDisplayName {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Deref for CrateDisplayName {
|
impl ops::Deref for CrateDisplayName {
|
||||||
type Target = str;
|
type Target = Symbol;
|
||||||
fn deref(&self) -> &str {
|
fn deref(&self) -> &Symbol {
|
||||||
&self.crate_name
|
&self.crate_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,10 +95,14 @@ impl FunctionData {
|
||||||
.map(Box::new);
|
.map(Box::new);
|
||||||
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
||||||
if flags.contains(FnFlags::HAS_UNSAFE_KW)
|
if flags.contains(FnFlags::HAS_UNSAFE_KW)
|
||||||
&& !crate_graph[krate].edition.at_least_2024()
|
|
||||||
&& attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
|
&& attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
|
||||||
{
|
{
|
||||||
flags.remove(FnFlags::HAS_UNSAFE_KW);
|
flags.remove(FnFlags::HAS_UNSAFE_KW);
|
||||||
|
flags.insert(FnFlags::DEPRECATED_SAFE_2024);
|
||||||
|
}
|
||||||
|
|
||||||
|
if attrs.by_key(&sym::target_feature).exists() {
|
||||||
|
flags.insert(FnFlags::HAS_TARGET_FEATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Arc::new(FunctionData {
|
Arc::new(FunctionData {
|
||||||
|
@ -148,6 +152,10 @@ impl FunctionData {
|
||||||
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
|
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_deprecated_safe_2024(&self) -> bool {
|
||||||
|
self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_safe(&self) -> bool {
|
pub fn is_safe(&self) -> bool {
|
||||||
self.flags.contains(FnFlags::HAS_SAFE_KW)
|
self.flags.contains(FnFlags::HAS_SAFE_KW)
|
||||||
}
|
}
|
||||||
|
@ -155,6 +163,10 @@ impl FunctionData {
|
||||||
pub fn is_varargs(&self) -> bool {
|
pub fn is_varargs(&self) -> bool {
|
||||||
self.flags.contains(FnFlags::IS_VARARGS)
|
self.flags.contains(FnFlags::IS_VARARGS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_target_feature(&self) -> bool {
|
||||||
|
self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
|
fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
|
||||||
|
|
|
@ -10,12 +10,12 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::{Attrs, AttrsWithOwner},
|
attr::{Attrs, AttrsWithOwner},
|
||||||
body::{scope::ExprScopes, Body, BodySourceMap},
|
|
||||||
data::{
|
data::{
|
||||||
adt::{EnumData, EnumVariantData, StructData, VariantData},
|
adt::{EnumData, EnumVariantData, StructData, VariantData},
|
||||||
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
|
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
|
||||||
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
|
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
|
||||||
},
|
},
|
||||||
|
expr_store::{scope::ExprScopes, Body, BodySourceMap},
|
||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
import_map::ImportMap,
|
import_map::ImportMap,
|
||||||
item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
|
item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
//! Defines `Body`: a lowered representation of bodies of functions, statics and
|
//! Defines `ExpressionStore`: a lowered representation of functions, statics and
|
||||||
//! consts.
|
//! consts.
|
||||||
|
mod body;
|
||||||
mod lower;
|
mod lower;
|
||||||
mod pretty;
|
mod pretty;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use std::ops::{Deref, Index};
|
use std::ops::{Deref, Index};
|
||||||
|
|
||||||
use base_db::CrateId;
|
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{name::Name, ExpandError, InFile};
|
use hir_expand::{name::Name, ExpandError, InFile};
|
||||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
use la_arena::{Arena, ArenaMap};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use span::{Edition, MacroFileId, SyntaxContextData};
|
use span::{Edition, MacroFileId, SyntaxContextData};
|
||||||
|
@ -22,19 +23,18 @@ use tt::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
expander::Expander,
|
|
||||||
hir::{
|
hir::{
|
||||||
dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label,
|
Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
|
||||||
LabelId, Pat, PatId, RecordFieldPat, Statement,
|
PatId, RecordFieldPat, Statement,
|
||||||
},
|
},
|
||||||
item_tree::AttrOwner,
|
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
path::{ModPath, Path},
|
path::{ModPath, Path},
|
||||||
src::HasSource,
|
|
||||||
type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
||||||
BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax,
|
BlockId, DefWithBodyId, Lookup, SyntheticSyntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use self::body::{Body, BodySourceMap};
|
||||||
|
|
||||||
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct HygieneId(span::SyntaxContextId);
|
pub struct HygieneId(span::SyntaxContextId);
|
||||||
|
@ -58,47 +58,6 @@ impl HygieneId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The body of an item (function, const etc.).
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Body {
|
|
||||||
pub exprs: Arena<Expr>,
|
|
||||||
pub pats: Arena<Pat>,
|
|
||||||
pub bindings: Arena<Binding>,
|
|
||||||
pub labels: Arena<Label>,
|
|
||||||
/// 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.
|
|
||||||
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
|
||||||
/// The patterns for the function's parameters. While the parameter types are
|
|
||||||
/// part of the function signature, the patterns are not (they don't change
|
|
||||||
/// the external type of the function).
|
|
||||||
///
|
|
||||||
/// If this `Body` is for the body of a constant, this will just be
|
|
||||||
/// empty.
|
|
||||||
pub params: Box<[PatId]>,
|
|
||||||
pub self_param: Option<BindingId>,
|
|
||||||
/// The `ExprId` of the actual body expression.
|
|
||||||
pub body_expr: ExprId,
|
|
||||||
pub types: TypesMap,
|
|
||||||
/// Block expressions in this body that may contain inner items.
|
|
||||||
block_scopes: Vec<BlockId>,
|
|
||||||
|
|
||||||
/// A map from binding to its hygiene ID.
|
|
||||||
///
|
|
||||||
/// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here.
|
|
||||||
/// If a binding does not appear here it has `SyntaxContextId::ROOT`.
|
|
||||||
///
|
|
||||||
/// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
|
|
||||||
/// expansions are attributed to their parent expansion (recursively).
|
|
||||||
binding_hygiene: FxHashMap<BindingId, HygieneId>,
|
|
||||||
/// A map from an variable usages to their hygiene ID.
|
|
||||||
///
|
|
||||||
/// Expressions that can be recorded here are single segment path, although not all single segments path refer
|
|
||||||
/// to variables and have hygiene (some refer to items, we don't know at this stage).
|
|
||||||
expr_hygiene: FxHashMap<ExprId, HygieneId>,
|
|
||||||
/// A map from a destructuring assignment possible variable usages to their hygiene ID.
|
|
||||||
pat_hygiene: FxHashMap<PatId, HygieneId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ExprPtr = AstPtr<ast::Expr>;
|
pub type ExprPtr = AstPtr<ast::Expr>;
|
||||||
pub type ExprSource = InFile<ExprPtr>;
|
pub type ExprSource = InFile<ExprPtr>;
|
||||||
|
|
||||||
|
@ -117,19 +76,39 @@ pub type PatFieldSource = InFile<PatFieldPtr>;
|
||||||
pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
|
pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
|
||||||
pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
|
pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
|
||||||
|
|
||||||
/// An item body together with the mapping from syntax nodes to HIR expression
|
pub type SelfParamPtr = AstPtr<ast::SelfParam>;
|
||||||
/// IDs. This is needed to go from e.g. a position in a file to the HIR
|
pub type MacroCallPtr = AstPtr<ast::MacroCall>;
|
||||||
/// expression containing it; but for type inference etc., we want to operate on
|
|
||||||
/// a structure that is agnostic to the actual positions of expressions in the
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
/// file, so that we don't recompute types whenever some whitespace is typed.
|
pub struct ExpressionStore {
|
||||||
|
pub exprs: Arena<Expr>,
|
||||||
|
pub pats: Arena<Pat>,
|
||||||
|
pub bindings: Arena<Binding>,
|
||||||
|
pub labels: Arena<Label>,
|
||||||
|
/// 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.
|
||||||
|
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
||||||
|
pub types: TypesMap,
|
||||||
|
/// Block expressions in this store that may contain inner items.
|
||||||
|
block_scopes: Box<[BlockId]>,
|
||||||
|
|
||||||
|
/// A map from binding to its hygiene ID.
|
||||||
///
|
///
|
||||||
/// One complication here is that, due to macro expansion, a single `Body` might
|
/// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here.
|
||||||
/// be spread across several files. So, for each ExprId and PatId, we record
|
/// If a binding does not appear here it has `SyntaxContextId::ROOT`.
|
||||||
/// both the HirFileId and the position inside the file. However, we only store
|
///
|
||||||
/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
|
/// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
|
||||||
/// this properly for macros.
|
/// expansions are attributed to their parent expansion (recursively).
|
||||||
#[derive(Default, Debug, Eq, PartialEq)]
|
binding_hygiene: FxHashMap<BindingId, HygieneId>,
|
||||||
pub struct BodySourceMap {
|
/// A map from an variable usages to their hygiene ID.
|
||||||
|
///
|
||||||
|
/// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer
|
||||||
|
/// to variables and have hygiene (some refer to items, we don't know at this stage).
|
||||||
|
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Default)]
|
||||||
|
pub struct ExpressionStoreSourceMap {
|
||||||
// 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>,
|
||||||
|
@ -141,7 +120,6 @@ pub struct BodySourceMap {
|
||||||
label_map: FxHashMap<LabelSource, LabelId>,
|
label_map: FxHashMap<LabelSource, LabelId>,
|
||||||
label_map_back: ArenaMap<LabelId, LabelSource>,
|
label_map_back: ArenaMap<LabelId, LabelSource>,
|
||||||
|
|
||||||
self_param: Option<InFile<AstPtr<ast::SelfParam>>>,
|
|
||||||
binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>,
|
binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>,
|
||||||
|
|
||||||
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
|
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
|
||||||
|
@ -153,11 +131,25 @@ pub struct BodySourceMap {
|
||||||
|
|
||||||
template_map: Option<Box<FormatTemplate>>,
|
template_map: Option<Box<FormatTemplate>>,
|
||||||
|
|
||||||
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
|
expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>,
|
||||||
|
|
||||||
/// Diagnostics accumulated during body 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).
|
||||||
diagnostics: Vec<BodyDiagnostic>,
|
diagnostics: Vec<ExpressionStoreDiagnostics>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 binding_owners: FxHashMap<BindingId, ExprId>,
|
||||||
|
pub types: TypesMap,
|
||||||
|
block_scopes: Vec<BlockId>,
|
||||||
|
binding_hygiene: FxHashMap<BindingId, HygieneId>,
|
||||||
|
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Eq, PartialEq)]
|
#[derive(Default, Debug, Eq, PartialEq)]
|
||||||
|
@ -171,166 +163,62 @@ struct FormatTemplate {
|
||||||
/// The value stored for each capture is its template literal and offset inside it. The template literal
|
/// The value stored for each capture is its template literal and offset inside it. The template literal
|
||||||
/// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written
|
/// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written
|
||||||
/// template.
|
/// template.
|
||||||
implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>,
|
implicit_capture_to_source: FxHashMap<ExprId, InFile<(ExprPtr, TextRange)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum BodyDiagnostic {
|
pub enum ExpressionStoreDiagnostics {
|
||||||
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
||||||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError },
|
MacroError { node: InFile<MacroCallPtr>, err: ExpandError },
|
||||||
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
|
UnresolvedMacroCall { node: InFile<MacroCallPtr>, path: ModPath },
|
||||||
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||||
AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
|
AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
|
||||||
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl ExpressionStoreBuilder {
|
||||||
pub(crate) fn body_with_source_map_query(
|
fn finish(self) -> ExpressionStore {
|
||||||
db: &dyn DefDatabase,
|
|
||||||
def: DefWithBodyId,
|
|
||||||
) -> (Arc<Body>, Arc<BodySourceMap>) {
|
|
||||||
let _p = tracing::info_span!("body_with_source_map_query").entered();
|
|
||||||
let mut params = None;
|
|
||||||
|
|
||||||
let mut is_async_fn = false;
|
|
||||||
let InFile { file_id, value: body } = {
|
|
||||||
match def {
|
|
||||||
DefWithBodyId::FunctionId(f) => {
|
|
||||||
let data = db.function_data(f);
|
|
||||||
let f = f.lookup(db);
|
|
||||||
let src = f.source(db);
|
|
||||||
params = src.value.param_list().map(move |param_list| {
|
|
||||||
let item_tree = f.id.item_tree(db);
|
|
||||||
let func = &item_tree[f.id.value];
|
|
||||||
let krate = f.container.module(db).krate;
|
|
||||||
let crate_graph = db.crate_graph();
|
|
||||||
(
|
|
||||||
param_list,
|
|
||||||
(0..func.params.len()).map(move |idx| {
|
|
||||||
item_tree
|
|
||||||
.attrs(
|
|
||||||
db,
|
|
||||||
krate,
|
|
||||||
AttrOwner::Param(
|
|
||||||
f.id.value,
|
|
||||||
Idx::from_raw(RawIdx::from(idx as u32)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
is_async_fn = data.is_async();
|
|
||||||
src.map(|it| it.body().map(ast::Expr::from))
|
|
||||||
}
|
|
||||||
DefWithBodyId::ConstId(c) => {
|
|
||||||
let c = c.lookup(db);
|
|
||||||
let src = c.source(db);
|
|
||||||
src.map(|it| it.body())
|
|
||||||
}
|
|
||||||
DefWithBodyId::StaticId(s) => {
|
|
||||||
let s = s.lookup(db);
|
|
||||||
let src = s.source(db);
|
|
||||||
src.map(|it| it.body())
|
|
||||||
}
|
|
||||||
DefWithBodyId::VariantId(v) => {
|
|
||||||
let s = v.lookup(db);
|
|
||||||
let src = s.source(db);
|
|
||||||
src.map(|it| it.expr())
|
|
||||||
}
|
|
||||||
DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let module = def.module(db);
|
|
||||||
let expander = Expander::new(db, file_id, module);
|
|
||||||
let (mut body, mut source_map) =
|
|
||||||
Body::new(db, def, expander, params, body, module.krate, is_async_fn);
|
|
||||||
body.shrink_to_fit();
|
|
||||||
source_map.shrink_to_fit();
|
|
||||||
|
|
||||||
(Arc::new(body), Arc::new(source_map))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
|
|
||||||
db.body_with_source_map(def).0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all block expressions in this body that define inner items.
|
|
||||||
pub fn blocks<'a>(
|
|
||||||
&'a self,
|
|
||||||
db: &'a dyn DefDatabase,
|
|
||||||
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a {
|
|
||||||
self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pretty_print(
|
|
||||||
&self,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
edition: Edition,
|
|
||||||
) -> String {
|
|
||||||
pretty::print_body_hir(db, self, owner, edition)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pretty_print_expr(
|
|
||||||
&self,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
expr: ExprId,
|
|
||||||
edition: Edition,
|
|
||||||
) -> String {
|
|
||||||
pretty::print_expr_hir(db, self, owner, expr, edition)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pretty_print_pat(
|
|
||||||
&self,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
pat: PatId,
|
|
||||||
oneline: bool,
|
|
||||||
edition: Edition,
|
|
||||||
) -> String {
|
|
||||||
pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
expander: Expander,
|
|
||||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
|
||||||
body: Option<ast::Expr>,
|
|
||||||
krate: CrateId,
|
|
||||||
is_async_fn: bool,
|
|
||||||
) -> (Body, BodySourceMap) {
|
|
||||||
lower::lower(db, owner, expander, params, body, krate, is_async_fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shrink_to_fit(&mut self) {
|
|
||||||
let Self {
|
let Self {
|
||||||
body_expr: _,
|
|
||||||
params: _,
|
|
||||||
self_param: _,
|
|
||||||
block_scopes,
|
block_scopes,
|
||||||
exprs,
|
mut exprs,
|
||||||
labels,
|
mut labels,
|
||||||
pats,
|
mut pats,
|
||||||
bindings,
|
mut bindings,
|
||||||
binding_owners,
|
mut binding_owners,
|
||||||
binding_hygiene,
|
mut binding_hygiene,
|
||||||
expr_hygiene,
|
mut ident_hygiene,
|
||||||
pat_hygiene,
|
mut types,
|
||||||
types,
|
|
||||||
} = self;
|
} = self;
|
||||||
block_scopes.shrink_to_fit();
|
|
||||||
exprs.shrink_to_fit();
|
exprs.shrink_to_fit();
|
||||||
labels.shrink_to_fit();
|
labels.shrink_to_fit();
|
||||||
pats.shrink_to_fit();
|
pats.shrink_to_fit();
|
||||||
bindings.shrink_to_fit();
|
bindings.shrink_to_fit();
|
||||||
binding_owners.shrink_to_fit();
|
binding_owners.shrink_to_fit();
|
||||||
binding_hygiene.shrink_to_fit();
|
binding_hygiene.shrink_to_fit();
|
||||||
expr_hygiene.shrink_to_fit();
|
ident_hygiene.shrink_to_fit();
|
||||||
pat_hygiene.shrink_to_fit();
|
|
||||||
types.shrink_to_fit();
|
types.shrink_to_fit();
|
||||||
|
|
||||||
|
ExpressionStore {
|
||||||
|
exprs,
|
||||||
|
pats,
|
||||||
|
bindings,
|
||||||
|
labels,
|
||||||
|
binding_owners,
|
||||||
|
types,
|
||||||
|
block_scopes: block_scopes.into_boxed_slice(),
|
||||||
|
binding_hygiene,
|
||||||
|
ident_hygiene,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpressionStore {
|
||||||
|
/// Returns an iterator over all block expressions in this store that define inner items.
|
||||||
|
pub fn blocks<'a>(
|
||||||
|
&'a self,
|
||||||
|
db: &'a dyn DefDatabase,
|
||||||
|
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + 'a {
|
||||||
|
self.block_scopes.iter().map(move |&block| (block, db.block_def_map(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)) {
|
||||||
|
@ -658,11 +546,11 @@ impl Body {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
|
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
|
||||||
self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT)
|
self.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.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT)
|
self.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 {
|
||||||
|
@ -673,27 +561,7 @@ impl Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Body {
|
impl Index<ExprId> for ExpressionStore {
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
body_expr: dummy_expr_id(),
|
|
||||||
exprs: Default::default(),
|
|
||||||
pats: Default::default(),
|
|
||||||
bindings: Default::default(),
|
|
||||||
labels: Default::default(),
|
|
||||||
params: Default::default(),
|
|
||||||
block_scopes: Default::default(),
|
|
||||||
binding_owners: Default::default(),
|
|
||||||
self_param: Default::default(),
|
|
||||||
binding_hygiene: Default::default(),
|
|
||||||
expr_hygiene: Default::default(),
|
|
||||||
pat_hygiene: Default::default(),
|
|
||||||
types: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<ExprId> for Body {
|
|
||||||
type Output = Expr;
|
type Output = Expr;
|
||||||
|
|
||||||
fn index(&self, expr: ExprId) -> &Expr {
|
fn index(&self, expr: ExprId) -> &Expr {
|
||||||
|
@ -701,7 +569,7 @@ impl Index<ExprId> for Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<PatId> for Body {
|
impl Index<PatId> for ExpressionStore {
|
||||||
type Output = Pat;
|
type Output = Pat;
|
||||||
|
|
||||||
fn index(&self, pat: PatId) -> &Pat {
|
fn index(&self, pat: PatId) -> &Pat {
|
||||||
|
@ -709,7 +577,7 @@ impl Index<PatId> for Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<LabelId> for Body {
|
impl Index<LabelId> for ExpressionStore {
|
||||||
type Output = Label;
|
type Output = Label;
|
||||||
|
|
||||||
fn index(&self, label: LabelId) -> &Label {
|
fn index(&self, label: LabelId) -> &Label {
|
||||||
|
@ -717,7 +585,7 @@ impl Index<LabelId> for Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<BindingId> for Body {
|
impl Index<BindingId> for ExpressionStore {
|
||||||
type Output = Binding;
|
type Output = Binding;
|
||||||
|
|
||||||
fn index(&self, b: BindingId) -> &Binding {
|
fn index(&self, b: BindingId) -> &Binding {
|
||||||
|
@ -725,7 +593,7 @@ impl Index<BindingId> for Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<TypeRefId> for Body {
|
impl Index<TypeRefId> for ExpressionStore {
|
||||||
type Output = TypeRef;
|
type Output = TypeRef;
|
||||||
|
|
||||||
fn index(&self, b: TypeRefId) -> &TypeRef {
|
fn index(&self, b: TypeRefId) -> &TypeRef {
|
||||||
|
@ -735,7 +603,7 @@ impl Index<TypeRefId> for Body {
|
||||||
|
|
||||||
// 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 BodySourceMap {
|
impl ExpressionStoreSourceMap {
|
||||||
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).map(|it| it.map(AstPtr::wrap_left)),
|
ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
|
||||||
|
@ -757,9 +625,7 @@ impl BodySourceMap {
|
||||||
self.expansions.get(&src).cloned()
|
self.expansions.get(&src).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn macro_calls(
|
pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroFileId)> + '_ {
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ {
|
|
||||||
self.expansions.iter().map(|(&a, &b)| (a, b))
|
self.expansions.iter().map(|(&a, &b)| (a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,10 +633,6 @@ impl BodySourceMap {
|
||||||
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
|
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> {
|
|
||||||
self.self_param
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
|
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
|
||||||
self.pat_map.get(&node.map(AstPtr::new)).cloned()
|
self.pat_map.get(&node.map(AstPtr::new)).cloned()
|
||||||
}
|
}
|
||||||
|
@ -801,9 +663,7 @@ impl BodySourceMap {
|
||||||
self.expr_map.get(&src).copied()
|
self.expr_map.get(&src).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expansions(
|
pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroFileId)> {
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> {
|
|
||||||
self.expansions.iter()
|
self.expansions.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,7 +683,7 @@ impl BodySourceMap {
|
||||||
pub fn format_args_implicit_capture(
|
pub fn format_args_implicit_capture(
|
||||||
&self,
|
&self,
|
||||||
capture_expr: ExprId,
|
capture_expr: ExprId,
|
||||||
) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> {
|
) -> Option<InFile<(ExprPtr, TextRange)>> {
|
||||||
self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
|
self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,14 +697,13 @@ impl BodySourceMap {
|
||||||
.zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
|
.zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the body source map's diagnostics.
|
/// Get a reference to the source map's diagnostics.
|
||||||
pub fn diagnostics(&self) -> &[BodyDiagnostic] {
|
pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
|
||||||
&self.diagnostics
|
&self.diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shrink_to_fit(&mut self) {
|
fn shrink_to_fit(&mut self) {
|
||||||
let Self {
|
let Self {
|
||||||
self_param: _,
|
|
||||||
expr_map,
|
expr_map,
|
||||||
expr_map_back,
|
expr_map_back,
|
||||||
pat_map,
|
pat_map,
|
175
src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
Normal file
175
src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
//! Defines `Body`: a lowered representation of functions, statics and
|
||||||
|
//! consts.
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
|
use hir_expand::{InFile, Lookup};
|
||||||
|
use la_arena::{Idx, RawIdx};
|
||||||
|
use span::Edition;
|
||||||
|
use syntax::ast;
|
||||||
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::DefDatabase,
|
||||||
|
expander::Expander,
|
||||||
|
expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr},
|
||||||
|
hir::{BindingId, ExprId, PatId},
|
||||||
|
item_tree::AttrOwner,
|
||||||
|
src::HasSource,
|
||||||
|
DefWithBodyId, HasModule,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The body of an item (function, const etc.).
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Body {
|
||||||
|
pub store: ExpressionStore,
|
||||||
|
/// The patterns for the function's parameters. While the parameter types are
|
||||||
|
/// part of the function signature, the patterns are not (they don't change
|
||||||
|
/// the external type of the function).
|
||||||
|
///
|
||||||
|
/// If this `Body` is for the body of a constant, this will just be
|
||||||
|
/// empty.
|
||||||
|
pub params: Box<[PatId]>,
|
||||||
|
pub self_param: Option<BindingId>,
|
||||||
|
/// The `ExprId` of the actual body expression.
|
||||||
|
pub body_expr: ExprId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for Body {
|
||||||
|
type Target = ExpressionStore;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An item body together with the mapping from syntax nodes to HIR expression
|
||||||
|
/// IDs. This is needed to go from e.g. a position in a file to the HIR
|
||||||
|
/// expression containing it; but for type inference etc., we want to operate on
|
||||||
|
/// a structure that is agnostic to the actual positions of expressions in the
|
||||||
|
/// file, so that we don't recompute types whenever some whitespace is typed.
|
||||||
|
///
|
||||||
|
/// One complication here is that, due to macro expansion, a single `Body` might
|
||||||
|
/// be spread across several files. So, for each ExprId and PatId, we record
|
||||||
|
/// both the HirFileId and the position inside the file. However, we only store
|
||||||
|
/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
|
||||||
|
/// this properly for macros.
|
||||||
|
#[derive(Default, Debug, Eq, PartialEq)]
|
||||||
|
pub struct BodySourceMap {
|
||||||
|
pub self_param: Option<InFile<SelfParamPtr>>,
|
||||||
|
pub store: ExpressionStoreSourceMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for BodySourceMap {
|
||||||
|
type Target = ExpressionStoreSourceMap;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub(crate) fn body_with_source_map_query(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
def: DefWithBodyId,
|
||||||
|
) -> (Arc<Body>, Arc<BodySourceMap>) {
|
||||||
|
let _p = tracing::info_span!("body_with_source_map_query").entered();
|
||||||
|
let mut params = None;
|
||||||
|
|
||||||
|
let mut is_async_fn = false;
|
||||||
|
let InFile { file_id, value: body } = {
|
||||||
|
match def {
|
||||||
|
DefWithBodyId::FunctionId(f) => {
|
||||||
|
let data = db.function_data(f);
|
||||||
|
let f = f.lookup(db);
|
||||||
|
let src = f.source(db);
|
||||||
|
params = src.value.param_list().map(move |param_list| {
|
||||||
|
let item_tree = f.id.item_tree(db);
|
||||||
|
let func = &item_tree[f.id.value];
|
||||||
|
let krate = f.container.module(db).krate;
|
||||||
|
let crate_graph = db.crate_graph();
|
||||||
|
(
|
||||||
|
param_list,
|
||||||
|
(0..func.params.len()).map(move |idx| {
|
||||||
|
item_tree
|
||||||
|
.attrs(
|
||||||
|
db,
|
||||||
|
krate,
|
||||||
|
AttrOwner::Param(
|
||||||
|
f.id.value,
|
||||||
|
Idx::from_raw(RawIdx::from(idx as u32)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
is_async_fn = data.is_async();
|
||||||
|
src.map(|it| it.body().map(ast::Expr::from))
|
||||||
|
}
|
||||||
|
DefWithBodyId::ConstId(c) => {
|
||||||
|
let c = c.lookup(db);
|
||||||
|
let src = c.source(db);
|
||||||
|
src.map(|it| it.body())
|
||||||
|
}
|
||||||
|
DefWithBodyId::StaticId(s) => {
|
||||||
|
let s = s.lookup(db);
|
||||||
|
let src = s.source(db);
|
||||||
|
src.map(|it| it.body())
|
||||||
|
}
|
||||||
|
DefWithBodyId::VariantId(v) => {
|
||||||
|
let s = v.lookup(db);
|
||||||
|
let src = s.source(db);
|
||||||
|
src.map(|it| it.expr())
|
||||||
|
}
|
||||||
|
DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let module = def.module(db);
|
||||||
|
let expander = Expander::new(db, file_id, module);
|
||||||
|
let (body, mut source_map) =
|
||||||
|
lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn);
|
||||||
|
source_map.store.shrink_to_fit();
|
||||||
|
|
||||||
|
(Arc::new(body), Arc::new(source_map))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
|
||||||
|
db.body_with_source_map(def).0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
owner: DefWithBodyId,
|
||||||
|
edition: Edition,
|
||||||
|
) -> String {
|
||||||
|
pretty::print_body_hir(db, self, owner, edition)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_expr(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
owner: DefWithBodyId,
|
||||||
|
expr: ExprId,
|
||||||
|
edition: Edition,
|
||||||
|
) -> String {
|
||||||
|
pretty::print_expr_hir(db, self, owner, expr, edition)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_pat(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
owner: DefWithBodyId,
|
||||||
|
pat: PatId,
|
||||||
|
oneline: bool,
|
||||||
|
edition: Edition,
|
||||||
|
) -> String {
|
||||||
|
pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BodySourceMap {
|
||||||
|
pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> {
|
||||||
|
self.self_param
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,11 +29,14 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs,
|
attr::Attrs,
|
||||||
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
|
|
||||||
builtin_type::BuiltinUint,
|
builtin_type::BuiltinUint,
|
||||||
data::adt::StructKind,
|
data::adt::StructKind,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
expander::Expander,
|
expander::Expander,
|
||||||
|
expr_store::{
|
||||||
|
Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
|
||||||
|
ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, PatPtr,
|
||||||
|
},
|
||||||
hir::{
|
hir::{
|
||||||
format_args::{
|
format_args::{
|
||||||
self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
|
self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
|
||||||
|
@ -55,11 +58,11 @@ use crate::{
|
||||||
|
|
||||||
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
||||||
|
|
||||||
pub(super) fn lower(
|
pub(super) fn lower_body(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
owner: DefWithBodyId,
|
owner: DefWithBodyId,
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
parameters: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
is_async_fn: bool,
|
is_async_fn: bool,
|
||||||
|
@ -75,35 +78,146 @@ pub(super) fn lower(
|
||||||
};
|
};
|
||||||
Arc::clone(span_map)
|
Arc::clone(span_map)
|
||||||
});
|
});
|
||||||
ExprCollector {
|
|
||||||
db,
|
let mut self_param = None;
|
||||||
owner,
|
let mut source_map_self_param = None;
|
||||||
krate,
|
let mut params = vec![];
|
||||||
def_map: expander.module.def_map(db),
|
let mut collector = ExprCollector::new(db, owner, expander, krate, span_map);
|
||||||
source_map: BodySourceMap::default(),
|
|
||||||
ast_id_map: db.ast_id_map(expander.current_file_id()),
|
let skip_body = match owner {
|
||||||
body: Body::default(),
|
DefWithBodyId::FunctionId(it) => db.attrs(it.into()),
|
||||||
expander,
|
DefWithBodyId::StaticId(it) => db.attrs(it.into()),
|
||||||
current_try_block_label: None,
|
DefWithBodyId::ConstId(it) => db.attrs(it.into()),
|
||||||
is_lowering_coroutine: false,
|
DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
|
||||||
label_ribs: Vec::new(),
|
DefWithBodyId::VariantId(it) => db.attrs(it.into()),
|
||||||
current_binding_owner: None,
|
|
||||||
awaitable_context: None,
|
|
||||||
current_span_map: span_map,
|
|
||||||
current_block_legacy_macro_defs_count: FxHashMap::default(),
|
|
||||||
}
|
}
|
||||||
.collect(params, body, is_async_fn)
|
.rust_analyzer_tool()
|
||||||
|
.any(|attr| *attr.path() == tool_path![skip]);
|
||||||
|
// If #[rust_analyzer::skip] annotated, only construct enough information for the signature
|
||||||
|
// and skip the body.
|
||||||
|
if skip_body {
|
||||||
|
if let Some((param_list, mut attr_enabled)) = parameters {
|
||||||
|
if let Some(self_param_syn) =
|
||||||
|
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
|
||||||
|
{
|
||||||
|
let is_mutable =
|
||||||
|
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
|
||||||
|
let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
|
||||||
|
Name::new_symbol_root(sym::self_.clone()),
|
||||||
|
BindingAnnotation::new(is_mutable, false),
|
||||||
|
);
|
||||||
|
self_param = Some(binding_id);
|
||||||
|
source_map_self_param =
|
||||||
|
Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
|
||||||
}
|
}
|
||||||
|
params = param_list
|
||||||
|
.params()
|
||||||
|
.zip(attr_enabled)
|
||||||
|
.filter(|(_, enabled)| *enabled)
|
||||||
|
.map(|_| collector.missing_pat())
|
||||||
|
.collect();
|
||||||
|
};
|
||||||
|
let body_expr = collector.missing_expr();
|
||||||
|
return (
|
||||||
|
Body {
|
||||||
|
store: collector.store.finish(),
|
||||||
|
params: params.into_boxed_slice(),
|
||||||
|
self_param,
|
||||||
|
body_expr,
|
||||||
|
},
|
||||||
|
BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((param_list, mut attr_enabled)) = parameters {
|
||||||
|
if let Some(self_param_syn) =
|
||||||
|
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
|
||||||
|
{
|
||||||
|
let is_mutable =
|
||||||
|
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
|
||||||
|
let binding_id: la_arena::Idx<Binding> = collector.alloc_binding(
|
||||||
|
Name::new_symbol_root(sym::self_.clone()),
|
||||||
|
BindingAnnotation::new(is_mutable, false),
|
||||||
|
);
|
||||||
|
let hygiene = self_param_syn
|
||||||
|
.name()
|
||||||
|
.map(|name| collector.hygiene_id_for(name.syntax().text_range().start()))
|
||||||
|
.unwrap_or(HygieneId::ROOT);
|
||||||
|
if !hygiene.is_root() {
|
||||||
|
collector.store.binding_hygiene.insert(binding_id, hygiene);
|
||||||
|
}
|
||||||
|
self_param = Some(binding_id);
|
||||||
|
source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) {
|
||||||
|
let param_pat = collector.collect_pat_top(param.pat());
|
||||||
|
params.push(param_pat);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let body_expr = collector.collect(
|
||||||
|
body,
|
||||||
|
if is_async_fn {
|
||||||
|
Awaitable::Yes
|
||||||
|
} else {
|
||||||
|
match owner {
|
||||||
|
DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
|
||||||
|
DefWithBodyId::StaticId(..) => Awaitable::No("static"),
|
||||||
|
DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
|
||||||
|
Awaitable::No("constant")
|
||||||
|
}
|
||||||
|
DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
Body {
|
||||||
|
store: collector.store.finish(),
|
||||||
|
params: params.into_boxed_slice(),
|
||||||
|
self_param,
|
||||||
|
body_expr,
|
||||||
|
},
|
||||||
|
BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn lower(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
owner: ExprStoreOwnerId,
|
||||||
|
expander: Expander,
|
||||||
|
body: Option<ast::Expr>,
|
||||||
|
krate: CrateId,
|
||||||
|
) -> (ExpressionStore, ExpressionStoreSourceMap) {
|
||||||
|
// We cannot leave the root span map empty and let any identifier from it be treated as root,
|
||||||
|
// because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
|
||||||
|
// with the inner macro, and that will cause confusion because they won't be the same as `ROOT`
|
||||||
|
// even though they should be the same. Also, when the body comes from multiple expansions, their
|
||||||
|
// hygiene is different.
|
||||||
|
let span_map = expander.current_file_id().macro_file().map(|_| {
|
||||||
|
let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else {
|
||||||
|
panic!("in a macro file there should be `ExpansionSpanMap`");
|
||||||
|
};
|
||||||
|
Arc::clone(span_map)
|
||||||
|
});
|
||||||
|
let mut expr_collector = ExprCollector::new(db, owner, expander, krate, span_map);
|
||||||
|
expr_collector.collect(body, Awaitable::No("?"));
|
||||||
|
(expr_collector.store.finish(), expr_collector.source_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExprStoreOwnerId = DefWithBodyId;
|
||||||
|
|
||||||
struct ExprCollector<'a> {
|
struct ExprCollector<'a> {
|
||||||
db: &'a dyn DefDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
owner: DefWithBodyId,
|
owner: ExprStoreOwnerId,
|
||||||
def_map: Arc<DefMap>,
|
def_map: Arc<DefMap>,
|
||||||
ast_id_map: Arc<AstIdMap>,
|
ast_id_map: Arc<AstIdMap>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
body: Body,
|
store: ExpressionStoreBuilder,
|
||||||
source_map: BodySourceMap,
|
source_map: ExpressionStoreSourceMap,
|
||||||
|
|
||||||
is_lowering_coroutine: bool,
|
is_lowering_coroutine: bool,
|
||||||
|
|
||||||
|
@ -157,6 +271,7 @@ impl RibKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
enum Awaitable {
|
enum Awaitable {
|
||||||
Yes,
|
Yes,
|
||||||
No(&'static str),
|
No(&'static str),
|
||||||
|
@ -180,12 +295,12 @@ impl BindingList {
|
||||||
let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| {
|
let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| {
|
||||||
let id = ec.alloc_binding(name.clone(), mode);
|
let id = ec.alloc_binding(name.clone(), mode);
|
||||||
if !hygiene.is_root() {
|
if !hygiene.is_root() {
|
||||||
ec.body.binding_hygiene.insert(id, hygiene);
|
ec.store.binding_hygiene.insert(id, hygiene);
|
||||||
}
|
}
|
||||||
id
|
id
|
||||||
});
|
});
|
||||||
if ec.body.bindings[id].mode != mode {
|
if ec.store.bindings[id].mode != mode {
|
||||||
ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
|
ec.store.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
|
||||||
}
|
}
|
||||||
self.check_is_used(ec, id);
|
self.check_is_used(ec, id);
|
||||||
id
|
id
|
||||||
|
@ -195,11 +310,11 @@ impl BindingList {
|
||||||
match self.is_used.get(&id) {
|
match self.is_used.get(&id) {
|
||||||
None => {
|
None => {
|
||||||
if self.reject_new {
|
if self.reject_new {
|
||||||
ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
|
ec.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
|
ec.store.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
|
||||||
}
|
}
|
||||||
Some(false) => {}
|
Some(false) => {}
|
||||||
}
|
}
|
||||||
|
@ -208,93 +323,37 @@ impl BindingList {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprCollector<'_> {
|
impl ExprCollector<'_> {
|
||||||
fn collect(
|
fn new(
|
||||||
mut self,
|
db: &dyn DefDatabase,
|
||||||
param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
owner: ExprStoreOwnerId,
|
||||||
body: Option<ast::Expr>,
|
expander: Expander,
|
||||||
is_async_fn: bool,
|
krate: CrateId,
|
||||||
) -> (Body, BodySourceMap) {
|
span_map: Option<Arc<ExpansionSpanMap>>,
|
||||||
let skip_body = match self.owner {
|
) -> ExprCollector<'_> {
|
||||||
DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()),
|
ExprCollector {
|
||||||
DefWithBodyId::StaticId(it) => self.db.attrs(it.into()),
|
db,
|
||||||
DefWithBodyId::ConstId(it) => self.db.attrs(it.into()),
|
owner,
|
||||||
DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
|
krate,
|
||||||
DefWithBodyId::VariantId(it) => self.db.attrs(it.into()),
|
def_map: expander.module.def_map(db),
|
||||||
|
source_map: ExpressionStoreSourceMap::default(),
|
||||||
|
ast_id_map: db.ast_id_map(expander.current_file_id()),
|
||||||
|
store: ExpressionStoreBuilder::default(),
|
||||||
|
expander,
|
||||||
|
current_try_block_label: None,
|
||||||
|
is_lowering_coroutine: false,
|
||||||
|
label_ribs: Vec::new(),
|
||||||
|
current_binding_owner: None,
|
||||||
|
awaitable_context: None,
|
||||||
|
current_span_map: span_map,
|
||||||
|
current_block_legacy_macro_defs_count: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
.rust_analyzer_tool()
|
|
||||||
.any(|attr| *attr.path() == tool_path![skip]);
|
|
||||||
// If #[rust_analyzer::skip] annotated, only construct enough information for the signature
|
|
||||||
// and skip the body.
|
|
||||||
if skip_body {
|
|
||||||
self.body.body_expr = self.missing_expr();
|
|
||||||
if let Some((param_list, mut attr_enabled)) = param_list {
|
|
||||||
if let Some(self_param) =
|
|
||||||
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
|
|
||||||
{
|
|
||||||
let is_mutable =
|
|
||||||
self_param.mut_token().is_some() && self_param.amp_token().is_none();
|
|
||||||
let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
|
|
||||||
Name::new_symbol_root(sym::self_.clone()),
|
|
||||||
BindingAnnotation::new(is_mutable, false),
|
|
||||||
);
|
|
||||||
self.body.self_param = Some(binding_id);
|
|
||||||
self.source_map.self_param =
|
|
||||||
Some(self.expander.in_file(AstPtr::new(&self_param)));
|
|
||||||
}
|
|
||||||
self.body.params = param_list
|
|
||||||
.params()
|
|
||||||
.zip(attr_enabled)
|
|
||||||
.filter(|(_, enabled)| *enabled)
|
|
||||||
.map(|_| self.missing_pat())
|
|
||||||
.collect();
|
|
||||||
};
|
|
||||||
return (self.body, self.source_map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.awaitable_context.replace(if is_async_fn {
|
fn collect(&mut self, expr: Option<ast::Expr>, awaitable: Awaitable) -> ExprId {
|
||||||
Awaitable::Yes
|
self.awaitable_context.replace(awaitable);
|
||||||
} else {
|
self.with_label_rib(RibKind::Closure, |this| {
|
||||||
match self.owner {
|
if awaitable == Awaitable::Yes {
|
||||||
DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
|
match expr {
|
||||||
DefWithBodyId::StaticId(..) => Awaitable::No("static"),
|
|
||||||
DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
|
|
||||||
Awaitable::No("constant")
|
|
||||||
}
|
|
||||||
DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if let Some((param_list, mut attr_enabled)) = param_list {
|
|
||||||
let mut params = vec![];
|
|
||||||
if let Some(self_param) =
|
|
||||||
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
|
|
||||||
{
|
|
||||||
let is_mutable =
|
|
||||||
self_param.mut_token().is_some() && self_param.amp_token().is_none();
|
|
||||||
let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
|
|
||||||
Name::new_symbol_root(sym::self_.clone()),
|
|
||||||
BindingAnnotation::new(is_mutable, false),
|
|
||||||
);
|
|
||||||
let hygiene = self_param
|
|
||||||
.name()
|
|
||||||
.map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
|
|
||||||
.unwrap_or(HygieneId::ROOT);
|
|
||||||
if !hygiene.is_root() {
|
|
||||||
self.body.binding_hygiene.insert(binding_id, hygiene);
|
|
||||||
}
|
|
||||||
self.body.self_param = Some(binding_id);
|
|
||||||
self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
|
|
||||||
{
|
|
||||||
let param_pat = self.collect_pat_top(param.pat());
|
|
||||||
params.push(param_pat);
|
|
||||||
}
|
|
||||||
self.body.params = params.into_boxed_slice();
|
|
||||||
};
|
|
||||||
self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
|
|
||||||
if is_async_fn {
|
|
||||||
match body {
|
|
||||||
Some(e) => {
|
Some(e) => {
|
||||||
let syntax_ptr = AstPtr::new(&e);
|
let syntax_ptr = AstPtr::new(&e);
|
||||||
let expr = this.collect_expr(e);
|
let expr = this.collect_expr(e);
|
||||||
|
@ -306,15 +365,13 @@ impl ExprCollector<'_> {
|
||||||
None => this.missing_expr(),
|
None => this.missing_expr(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.collect_expr_opt(body)
|
this.collect_expr_opt(expr)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
(self.body, self.source_map)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ctx(&mut self) -> LowerCtx<'_> {
|
fn ctx(&mut self) -> LowerCtx<'_> {
|
||||||
self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types)
|
self.expander.ctx(self.db, &mut self.store.types, &mut self.source_map.types)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
|
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
|
||||||
|
@ -390,7 +447,7 @@ impl ExprCollector<'_> {
|
||||||
parent: this.owner,
|
parent: this.owner,
|
||||||
root: inner_expr,
|
root: inner_expr,
|
||||||
});
|
});
|
||||||
this.body.exprs[result_expr_id] = Expr::Const(it);
|
this.store.exprs[result_expr_id] = Expr::Const(it);
|
||||||
this.current_binding_owner = prev_binding_owner;
|
this.current_binding_owner = prev_binding_owner;
|
||||||
result_expr_id
|
result_expr_id
|
||||||
})
|
})
|
||||||
|
@ -480,7 +537,7 @@ impl ExprCollector<'_> {
|
||||||
.unwrap_or((Expr::Missing, HygieneId::ROOT));
|
.unwrap_or((Expr::Missing, HygieneId::ROOT));
|
||||||
let expr_id = self.alloc_expr(path, syntax_ptr);
|
let expr_id = self.alloc_expr(path, syntax_ptr);
|
||||||
if !hygiene.is_root() {
|
if !hygiene.is_root() {
|
||||||
self.body.expr_hygiene.insert(expr_id, hygiene);
|
self.store.ident_hygiene.insert(expr_id.into(), hygiene);
|
||||||
}
|
}
|
||||||
expr_id
|
expr_id
|
||||||
}
|
}
|
||||||
|
@ -562,10 +619,12 @@ 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(BodyDiagnostic::AwaitOutsideOfAsync {
|
self.source_map.diagnostics.push(
|
||||||
|
ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
|
||||||
node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
|
node: InFile::new(self.expander.current_file_id(), 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)
|
||||||
}
|
}
|
||||||
|
@ -646,7 +705,7 @@ impl ExprCollector<'_> {
|
||||||
this.is_lowering_coroutine = prev_is_lowering_coroutine;
|
this.is_lowering_coroutine = prev_is_lowering_coroutine;
|
||||||
this.current_binding_owner = prev_binding_owner;
|
this.current_binding_owner = prev_binding_owner;
|
||||||
this.current_try_block_label = prev_try_block_label;
|
this.current_try_block_label = prev_try_block_label;
|
||||||
this.body.exprs[result_expr_id] = Expr::Closure {
|
this.store.exprs[result_expr_id] = Expr::Closure {
|
||||||
args: args.into(),
|
args: args.into(),
|
||||||
arg_types: arg_types.into(),
|
arg_types: arg_types.into(),
|
||||||
ret_type,
|
ret_type,
|
||||||
|
@ -752,7 +811,7 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
|
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
|
||||||
self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types)
|
self.expander.parse_path(self.db, path, &mut self.store.types, &mut self.source_map.types)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> {
|
fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> {
|
||||||
|
@ -781,7 +840,7 @@ impl ExprCollector<'_> {
|
||||||
let src = self.expander.in_file(AstPtr::new(&expr).wrap_left());
|
let src = self.expander.in_file(AstPtr::new(&expr).wrap_left());
|
||||||
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.body.pats.alloc(Pat::Expr(expr));
|
let id = self.store.pats.alloc(Pat::Expr(expr));
|
||||||
self.source_map.pat_map_back.insert(id, src);
|
self.source_map.pat_map_back.insert(id, src);
|
||||||
id
|
id
|
||||||
})
|
})
|
||||||
|
@ -835,7 +894,7 @@ impl ExprCollector<'_> {
|
||||||
.unwrap_or((Pat::Missing, HygieneId::ROOT));
|
.unwrap_or((Pat::Missing, HygieneId::ROOT));
|
||||||
let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
|
let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
|
||||||
if !hygiene.is_root() {
|
if !hygiene.is_root() {
|
||||||
self.body.pat_hygiene.insert(pat_id, hygiene);
|
self.store.ident_hygiene.insert(pat_id.into(), hygiene);
|
||||||
}
|
}
|
||||||
pat_id
|
pat_id
|
||||||
}
|
}
|
||||||
|
@ -967,7 +1026,7 @@ impl ExprCollector<'_> {
|
||||||
) -> ExprId {
|
) -> ExprId {
|
||||||
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
|
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
|
||||||
let tmp = job(self);
|
let tmp = job(self);
|
||||||
self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
|
self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing);
|
||||||
self.current_binding_owner = prev_owner;
|
self.current_binding_owner = prev_owner;
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -979,8 +1038,9 @@ impl ExprCollector<'_> {
|
||||||
let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else {
|
let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else {
|
||||||
return self.collect_block(e);
|
return self.collect_block(e);
|
||||||
};
|
};
|
||||||
let label = self
|
let label = self.alloc_label_desugared(Label {
|
||||||
.alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) });
|
name: Name::generate_new_name(self.store.labels.len()),
|
||||||
|
});
|
||||||
let old_label = self.current_try_block_label.replace(label);
|
let old_label = self.current_try_block_label.replace(label);
|
||||||
|
|
||||||
let ptr = AstPtr::new(&e).upcast();
|
let ptr = AstPtr::new(&e).upcast();
|
||||||
|
@ -1006,7 +1066,7 @@ impl ExprCollector<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
|
let Expr::Block { tail, .. } = &mut self.store.exprs[expr_id] else {
|
||||||
unreachable!("block was lowered to non-block");
|
unreachable!("block was lowered to non-block");
|
||||||
};
|
};
|
||||||
*tail = Some(next_tail);
|
*tail = Some(next_tail);
|
||||||
|
@ -1112,7 +1172,7 @@ impl ExprCollector<'_> {
|
||||||
this.collect_expr_opt(e.loop_body().map(|it| it.into()))
|
this.collect_expr_opt(e.loop_body().map(|it| it.into()))
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
let iter_name = Name::generate_new_name(self.body.exprs.len());
|
let iter_name = Name::generate_new_name(self.store.exprs.len());
|
||||||
let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
|
let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
|
||||||
let iter_expr_mut = self.alloc_expr(
|
let iter_expr_mut = self.alloc_expr(
|
||||||
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
|
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
|
||||||
|
@ -1177,7 +1237,7 @@ impl ExprCollector<'_> {
|
||||||
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
|
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
|
||||||
let expr = self
|
let expr = self
|
||||||
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
|
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
|
||||||
let continue_name = Name::generate_new_name(self.body.bindings.len());
|
let continue_name = Name::generate_new_name(self.store.bindings.len());
|
||||||
let continue_binding =
|
let continue_binding =
|
||||||
self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
|
self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
|
||||||
let continue_bpat =
|
let continue_bpat =
|
||||||
|
@ -1192,7 +1252,7 @@ impl ExprCollector<'_> {
|
||||||
guard: None,
|
guard: None,
|
||||||
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
|
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
|
||||||
};
|
};
|
||||||
let break_name = Name::generate_new_name(self.body.bindings.len());
|
let break_name = Name::generate_new_name(self.store.bindings.len());
|
||||||
let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
|
let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
|
||||||
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
||||||
self.add_definition_to_binding(break_binding, break_bpat);
|
self.add_definition_to_binding(break_binding, break_bpat);
|
||||||
|
@ -1261,17 +1321,19 @@ 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(BodyDiagnostic::UnresolvedMacroCall {
|
self.source_map.diagnostics.push(
|
||||||
|
ExpressionStoreDiagnostics::UnresolvedMacroCall {
|
||||||
node: InFile::new(outer_file, syntax_ptr),
|
node: InFile::new(outer_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.diagnostics.push(BodyDiagnostic::MacroError {
|
self.source_map.diagnostics.push(ExpressionStoreDiagnostics::MacroError {
|
||||||
node: InFile::new(outer_file, syntax_ptr),
|
node: InFile::new(outer_file, syntax_ptr),
|
||||||
err,
|
err,
|
||||||
});
|
});
|
||||||
|
@ -1464,7 +1526,7 @@ impl ExprCollector<'_> {
|
||||||
let (module, def_map) =
|
let (module, def_map) =
|
||||||
match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
|
match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
|
||||||
Some((def_map, block_id)) => {
|
Some((def_map, block_id)) => {
|
||||||
self.body.block_scopes.push(block_id);
|
self.store.block_scopes.push(block_id);
|
||||||
(def_map.module_id(DefMap::ROOT), def_map)
|
(def_map.module_id(DefMap::ROOT), def_map)
|
||||||
}
|
}
|
||||||
None => (self.expander.module, self.def_map.clone()),
|
None => (self.expander.module, self.def_map.clone()),
|
||||||
|
@ -1621,7 +1683,7 @@ impl ExprCollector<'_> {
|
||||||
pats.push(self.collect_pat(rest, binding_list));
|
pats.push(self.collect_pat(rest, binding_list));
|
||||||
for (&id, &is_used) in binding_list.is_used.iter() {
|
for (&id, &is_used) in binding_list.is_used.iter() {
|
||||||
if !is_used {
|
if !is_used {
|
||||||
self.body.bindings[id].problems =
|
self.store.bindings[id].problems =
|
||||||
Some(BindingProblems::NotBoundAcrossAll);
|
Some(BindingProblems::NotBoundAcrossAll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1825,7 +1887,7 @@ impl ExprCollector<'_> {
|
||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode {
|
self.source_map.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.expander.cfg_options().clone(),
|
opts: self.expander.cfg_options().clone(),
|
||||||
|
@ -1853,7 +1915,7 @@ impl ExprCollector<'_> {
|
||||||
fn resolve_label(
|
fn resolve_label(
|
||||||
&self,
|
&self,
|
||||||
lifetime: Option<ast::Lifetime>,
|
lifetime: Option<ast::Lifetime>,
|
||||||
) -> Result<Option<LabelId>, BodyDiagnostic> {
|
) -> Result<Option<LabelId>, ExpressionStoreDiagnostics> {
|
||||||
let Some(lifetime) = lifetime else { return Ok(None) };
|
let Some(lifetime) = lifetime else { return Ok(None) };
|
||||||
let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
|
let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
|
||||||
None => (HygieneId::ROOT, None),
|
None => (HygieneId::ROOT, None),
|
||||||
|
@ -1877,7 +1939,7 @@ impl ExprCollector<'_> {
|
||||||
return if self.is_label_valid_from_rib(rib_idx) {
|
return if self.is_label_valid_from_rib(rib_idx) {
|
||||||
Ok(Some(*id))
|
Ok(Some(*id))
|
||||||
} else {
|
} else {
|
||||||
Err(BodyDiagnostic::UnreachableLabel {
|
Err(ExpressionStoreDiagnostics::UnreachableLabel {
|
||||||
name,
|
name,
|
||||||
node: self.expander.in_file(AstPtr::new(&lifetime)),
|
node: self.expander.in_file(AstPtr::new(&lifetime)),
|
||||||
})
|
})
|
||||||
|
@ -1903,7 +1965,7 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(BodyDiagnostic::UndeclaredLabel {
|
Err(ExpressionStoreDiagnostics::UndeclaredLabel {
|
||||||
name,
|
name,
|
||||||
node: self.expander.in_file(AstPtr::new(&lifetime)),
|
node: self.expander.in_file(AstPtr::new(&lifetime)),
|
||||||
})
|
})
|
||||||
|
@ -1934,7 +1996,7 @@ impl ExprCollector<'_> {
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
self.label_ribs.push(LabelRib::new(RibKind::Normal(
|
self.label_ribs.push(LabelRib::new(RibKind::Normal(
|
||||||
self.body[label].name.clone(),
|
self.store.labels[label].name.clone(),
|
||||||
label,
|
label,
|
||||||
hygiene,
|
hygiene,
|
||||||
)));
|
)));
|
||||||
|
@ -2023,7 +2085,7 @@ impl ExprCollector<'_> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !hygiene.is_root() {
|
if !hygiene.is_root() {
|
||||||
self.body.expr_hygiene.insert(expr_id, hygiene);
|
self.store.ident_hygiene.insert(expr_id.into(), hygiene);
|
||||||
}
|
}
|
||||||
expr_id
|
expr_id
|
||||||
},
|
},
|
||||||
|
@ -2171,17 +2233,27 @@ impl ExprCollector<'_> {
|
||||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
|
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
|
||||||
let unsafe_arg_new =
|
let unsafe_arg_new =
|
||||||
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
|
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
|
||||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
||||||
|
id: None,
|
||||||
|
statements: Box::new([]),
|
||||||
|
tail: Some(unsafe_arg_new),
|
||||||
|
});
|
||||||
|
if !fmt.orphans.is_empty() {
|
||||||
|
unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {
|
||||||
id: None,
|
id: None,
|
||||||
// We collect the unused expressions here so that we still infer them instead of
|
// We collect the unused expressions here so that we still infer them instead of
|
||||||
// dropping them out of the expression tree
|
// dropping them out of the expression tree. We cannot store them in the `Unsafe`
|
||||||
|
// block because then unsafe blocks within them will get a false "unused unsafe"
|
||||||
|
// diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
|
||||||
statements: fmt
|
statements: fmt
|
||||||
.orphans
|
.orphans
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|expr| Statement::Expr { expr, has_semi: true })
|
.map(|expr| Statement::Expr { expr, has_semi: true })
|
||||||
.collect(),
|
.collect(),
|
||||||
tail: Some(unsafe_arg_new),
|
tail: Some(unsafe_arg_new),
|
||||||
|
label: None,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let idx = self.alloc_expr(
|
let idx = self.alloc_expr(
|
||||||
Expr::Call {
|
Expr::Call {
|
||||||
|
@ -2417,7 +2489,7 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)>
|
||||||
impl ExprCollector<'_> {
|
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.body.exprs.alloc(expr);
|
let id = self.store.exprs.alloc(expr);
|
||||||
self.source_map.expr_map_back.insert(id, src);
|
self.source_map.expr_map_back.insert(id, src);
|
||||||
self.source_map.expr_map.insert(src, id.into());
|
self.source_map.expr_map.insert(src, id.into());
|
||||||
id
|
id
|
||||||
|
@ -2425,11 +2497,11 @@ impl ExprCollector<'_> {
|
||||||
// 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.
|
||||||
// Migrate to alloc_expr_desugared_with_ptr and then rename back
|
// Migrate to alloc_expr_desugared_with_ptr and then rename back
|
||||||
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
||||||
self.body.exprs.alloc(expr)
|
self.store.exprs.alloc(expr)
|
||||||
}
|
}
|
||||||
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.body.exprs.alloc(expr);
|
let id = self.store.exprs.alloc(expr);
|
||||||
self.source_map.expr_map_back.insert(id, src);
|
self.source_map.expr_map_back.insert(id, src);
|
||||||
// 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.source_map.expr_map.insert(src, id);
|
||||||
|
@ -2440,45 +2512,45 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
|
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
|
||||||
let binding = self.body.bindings.alloc(Binding { name, mode, problems: None });
|
let binding = self.store.bindings.alloc(Binding { name, mode, problems: None });
|
||||||
if let Some(owner) = self.current_binding_owner {
|
if let Some(owner) = self.current_binding_owner {
|
||||||
self.body.binding_owners.insert(binding, owner);
|
self.store.binding_owners.insert(binding, owner);
|
||||||
}
|
}
|
||||||
binding
|
binding
|
||||||
}
|
}
|
||||||
|
|
||||||
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.body.pats.alloc(pat);
|
let id = self.store.pats.alloc(pat);
|
||||||
self.source_map.expr_map.insert(src, id.into());
|
self.source_map.expr_map.insert(src, id.into());
|
||||||
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
|
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
|
||||||
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.body.pats.alloc(pat);
|
let id = self.store.pats.alloc(pat);
|
||||||
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
|
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
|
||||||
self.source_map.pat_map.insert(src, id);
|
self.source_map.pat_map.insert(src, id);
|
||||||
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.
|
||||||
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
|
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
|
||||||
self.body.pats.alloc(pat)
|
self.store.pats.alloc(pat)
|
||||||
}
|
}
|
||||||
fn missing_pat(&mut self) -> PatId {
|
fn missing_pat(&mut self) -> PatId {
|
||||||
self.body.pats.alloc(Pat::Missing)
|
self.store.pats.alloc(Pat::Missing)
|
||||||
}
|
}
|
||||||
|
|
||||||
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.body.labels.alloc(label);
|
let id = self.store.labels.alloc(label);
|
||||||
self.source_map.label_map_back.insert(id, src);
|
self.source_map.label_map_back.insert(id, src);
|
||||||
self.source_map.label_map.insert(src, id);
|
self.source_map.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.
|
||||||
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
|
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
|
||||||
self.body.labels.alloc(label)
|
self.store.labels.alloc(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_lowering_awaitable_block(&self) -> &Awaitable {
|
fn is_lowering_awaitable_block(&self) -> &Awaitable {
|
|
@ -9,7 +9,7 @@ use syntax::{
|
||||||
use tt::TextRange;
|
use tt::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::lower::{ExprCollector, FxIndexSet},
|
expr_store::lower::{ExprCollector, FxIndexSet},
|
||||||
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
|
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub(super) fn print_body_hir(
|
||||||
|
|
||||||
let mut p = Printer {
|
let mut p = Printer {
|
||||||
db,
|
db,
|
||||||
body,
|
store: body,
|
||||||
buf: header,
|
buf: header,
|
||||||
indent_level: 0,
|
indent_level: 0,
|
||||||
line_format: LineFormat::Newline,
|
line_format: LineFormat::Newline,
|
||||||
|
@ -103,14 +103,14 @@ pub(super) fn print_body_hir(
|
||||||
|
|
||||||
pub(super) fn print_expr_hir(
|
pub(super) fn print_expr_hir(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
body: &Body,
|
store: &ExpressionStore,
|
||||||
_owner: DefWithBodyId,
|
_owner: DefWithBodyId,
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut p = Printer {
|
let mut p = Printer {
|
||||||
db,
|
db,
|
||||||
body,
|
store,
|
||||||
buf: String::new(),
|
buf: String::new(),
|
||||||
indent_level: 0,
|
indent_level: 0,
|
||||||
line_format: LineFormat::Newline,
|
line_format: LineFormat::Newline,
|
||||||
|
@ -122,7 +122,7 @@ pub(super) fn print_expr_hir(
|
||||||
|
|
||||||
pub(super) fn print_pat_hir(
|
pub(super) fn print_pat_hir(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
body: &Body,
|
store: &ExpressionStore,
|
||||||
_owner: DefWithBodyId,
|
_owner: DefWithBodyId,
|
||||||
pat: PatId,
|
pat: PatId,
|
||||||
oneline: bool,
|
oneline: bool,
|
||||||
|
@ -130,7 +130,7 @@ pub(super) fn print_pat_hir(
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut p = Printer {
|
let mut p = Printer {
|
||||||
db,
|
db,
|
||||||
body,
|
store,
|
||||||
buf: String::new(),
|
buf: String::new(),
|
||||||
indent_level: 0,
|
indent_level: 0,
|
||||||
line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline },
|
line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline },
|
||||||
|
@ -157,7 +157,7 @@ macro_rules! wln {
|
||||||
|
|
||||||
struct Printer<'a> {
|
struct Printer<'a> {
|
||||||
db: &'a dyn DefDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
body: &'a Body,
|
store: &'a ExpressionStore,
|
||||||
buf: String,
|
buf: String,
|
||||||
indent_level: usize,
|
indent_level: usize,
|
||||||
line_format: LineFormat,
|
line_format: LineFormat,
|
||||||
|
@ -233,7 +233,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_expr(&mut self, expr: ExprId) {
|
fn print_expr(&mut self, expr: ExprId) {
|
||||||
let expr = &self.body[expr];
|
let expr = &self.store[expr];
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Missing => w!(self, "<EFBFBD>"),
|
Expr::Missing => w!(self, "<EFBFBD>"),
|
||||||
|
@ -241,7 +241,7 @@ impl Printer<'_> {
|
||||||
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
|
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
|
||||||
Expr::OffsetOf(offset_of) => {
|
Expr::OffsetOf(offset_of) => {
|
||||||
w!(self, "builtin#offset_of(");
|
w!(self, "builtin#offset_of(");
|
||||||
self.print_type_ref(offset_of.container, &self.body.types);
|
self.print_type_ref(offset_of.container, &self.store.types);
|
||||||
let edition = self.edition;
|
let edition = self.edition;
|
||||||
w!(
|
w!(
|
||||||
self,
|
self,
|
||||||
|
@ -271,7 +271,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
Expr::Loop { body, label } => {
|
Expr::Loop { body, label } => {
|
||||||
if let Some(lbl) = label {
|
if let Some(lbl) = label {
|
||||||
w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast(), self.edition));
|
w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition));
|
||||||
}
|
}
|
||||||
w!(self, "loop ");
|
w!(self, "loop ");
|
||||||
self.print_expr(*body);
|
self.print_expr(*body);
|
||||||
|
@ -295,7 +295,7 @@ impl Printer<'_> {
|
||||||
if let Some(args) = generic_args {
|
if let Some(args) = generic_args {
|
||||||
w!(self, "::<");
|
w!(self, "::<");
|
||||||
let edition = self.edition;
|
let edition = self.edition;
|
||||||
print_generic_args(self.db, args, &self.body.types, self, edition).unwrap();
|
print_generic_args(self.db, args, &self.store.types, self, edition).unwrap();
|
||||||
w!(self, ">");
|
w!(self, ">");
|
||||||
}
|
}
|
||||||
w!(self, "(");
|
w!(self, "(");
|
||||||
|
@ -330,13 +330,13 @@ impl Printer<'_> {
|
||||||
Expr::Continue { label } => {
|
Expr::Continue { label } => {
|
||||||
w!(self, "continue");
|
w!(self, "continue");
|
||||||
if let Some(lbl) = label {
|
if let Some(lbl) = label {
|
||||||
w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
|
w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Break { expr, label } => {
|
Expr::Break { expr, label } => {
|
||||||
w!(self, "break");
|
w!(self, "break");
|
||||||
if let Some(lbl) = label {
|
if let Some(lbl) = label {
|
||||||
w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
|
w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
|
||||||
}
|
}
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
|
@ -404,7 +404,7 @@ impl Printer<'_> {
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
self.print_expr(*expr);
|
self.print_expr(*expr);
|
||||||
w!(self, " as ");
|
w!(self, " as ");
|
||||||
self.print_type_ref(*type_ref, &self.body.types);
|
self.print_type_ref(*type_ref, &self.store.types);
|
||||||
}
|
}
|
||||||
Expr::Ref { expr, rawness, mutability } => {
|
Expr::Ref { expr, rawness, mutability } => {
|
||||||
w!(self, "&");
|
w!(self, "&");
|
||||||
|
@ -492,13 +492,13 @@ impl Printer<'_> {
|
||||||
self.print_pat(*pat);
|
self.print_pat(*pat);
|
||||||
if let Some(ty) = ty {
|
if let Some(ty) = ty {
|
||||||
w!(self, ": ");
|
w!(self, ": ");
|
||||||
self.print_type_ref(*ty, &self.body.types);
|
self.print_type_ref(*ty, &self.store.types);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w!(self, "|");
|
w!(self, "|");
|
||||||
if let Some(ret_ty) = ret_type {
|
if let Some(ret_ty) = ret_type {
|
||||||
w!(self, " -> ");
|
w!(self, " -> ");
|
||||||
self.print_type_ref(*ret_ty, &self.body.types);
|
self.print_type_ref(*ret_ty, &self.store.types);
|
||||||
}
|
}
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
self.print_expr(*body);
|
self.print_expr(*body);
|
||||||
|
@ -534,7 +534,7 @@ impl Printer<'_> {
|
||||||
Expr::Literal(lit) => self.print_literal(lit),
|
Expr::Literal(lit) => self.print_literal(lit),
|
||||||
Expr::Block { id: _, statements, tail, label } => {
|
Expr::Block { id: _, statements, tail, label } => {
|
||||||
let label = label.map(|lbl| {
|
let label = label.map(|lbl| {
|
||||||
format!("{}: ", self.body[lbl].name.display(self.db.upcast(), self.edition))
|
format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition))
|
||||||
});
|
});
|
||||||
self.print_block(label.as_deref(), statements, tail);
|
self.print_block(label.as_deref(), statements, tail);
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_pat(&mut self, pat: PatId) {
|
fn print_pat(&mut self, pat: PatId) {
|
||||||
let pat = &self.body[pat];
|
let pat = &self.store[pat];
|
||||||
|
|
||||||
match pat {
|
match pat {
|
||||||
Pat::Missing => w!(self, "<EFBFBD>"),
|
Pat::Missing => w!(self, "<EFBFBD>"),
|
||||||
|
@ -623,9 +623,9 @@ impl Printer<'_> {
|
||||||
let field_name = arg.name.display(self.db.upcast(), edition).to_string();
|
let field_name = arg.name.display(self.db.upcast(), edition).to_string();
|
||||||
|
|
||||||
let mut same_name = false;
|
let mut same_name = false;
|
||||||
if let Pat::Bind { id, subpat: None } = &self.body[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.body.bindings[*id]
|
&self.store.bindings[*id]
|
||||||
{
|
{
|
||||||
if name.as_str() == field_name {
|
if name.as_str() == field_name {
|
||||||
same_name = true;
|
same_name = true;
|
||||||
|
@ -734,7 +734,7 @@ impl Printer<'_> {
|
||||||
self.print_pat(*pat);
|
self.print_pat(*pat);
|
||||||
if let Some(ty) = type_ref {
|
if let Some(ty) = type_ref {
|
||||||
w!(self, ": ");
|
w!(self, ": ");
|
||||||
self.print_type_ref(*ty, &self.body.types);
|
self.print_type_ref(*ty, &self.store.types);
|
||||||
}
|
}
|
||||||
if let Some(init) = initializer {
|
if let Some(init) = initializer {
|
||||||
w!(self, " = ");
|
w!(self, " = ");
|
||||||
|
@ -799,11 +799,11 @@ impl Printer<'_> {
|
||||||
|
|
||||||
fn print_path(&mut self, path: &Path) {
|
fn print_path(&mut self, path: &Path) {
|
||||||
let edition = self.edition;
|
let edition = self.edition;
|
||||||
print_path(self.db, path, &self.body.types, self, edition).unwrap();
|
print_path(self.db, path, &self.store.types, self, edition).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_binding(&mut self, id: BindingId) {
|
fn print_binding(&mut self, id: BindingId) {
|
||||||
let Binding { name, mode, .. } = &self.body.bindings[id];
|
let Binding { name, mode, .. } = &self.store.bindings[id];
|
||||||
let mode = match mode {
|
let mode = match mode {
|
||||||
BindingAnnotation::Unannotated => "",
|
BindingAnnotation::Unannotated => "",
|
||||||
BindingAnnotation::Mutable => "mut ",
|
BindingAnnotation::Mutable => "mut ",
|
|
@ -4,8 +4,8 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{Body, HygieneId},
|
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
expr_store::{Body, ExpressionStore, HygieneId},
|
||||||
hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
|
hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
|
||||||
BlockId, ConstBlockId, DefWithBodyId,
|
BlockId, ConstBlockId, DefWithBodyId,
|
||||||
};
|
};
|
||||||
|
@ -53,7 +53,7 @@ pub struct ScopeData {
|
||||||
impl ExprScopes {
|
impl ExprScopes {
|
||||||
pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
|
pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
|
||||||
let body = db.body(def);
|
let body = db.body(def);
|
||||||
let mut scopes = ExprScopes::new(&body, |const_block| {
|
let mut scopes = ExprScopes::new_body(&body, |const_block| {
|
||||||
db.lookup_intern_anonymous_const(const_block).root
|
db.lookup_intern_anonymous_const(const_block).root
|
||||||
});
|
});
|
||||||
scopes.shrink_to_fit();
|
scopes.shrink_to_fit();
|
||||||
|
@ -104,7 +104,7 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprScopes {
|
impl ExprScopes {
|
||||||
fn new(
|
fn new_body(
|
||||||
body: &Body,
|
body: &Body,
|
||||||
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
||||||
) -> ExprScopes {
|
) -> ExprScopes {
|
||||||
|
@ -179,28 +179,28 @@ impl ExprScopes {
|
||||||
|
|
||||||
fn add_bindings(
|
fn add_bindings(
|
||||||
&mut self,
|
&mut self,
|
||||||
body: &Body,
|
store: &ExpressionStore,
|
||||||
scope: ScopeId,
|
scope: ScopeId,
|
||||||
binding: BindingId,
|
binding: BindingId,
|
||||||
hygiene: HygieneId,
|
hygiene: HygieneId,
|
||||||
) {
|
) {
|
||||||
let Binding { name, .. } = &body.bindings[binding];
|
let Binding { name, .. } = &store.bindings[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);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
|
fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) {
|
||||||
let pattern = &body[pat];
|
let pattern = &store[pat];
|
||||||
if let Pat::Bind { id, .. } = *pattern {
|
if let Pat::Bind { id, .. } = *pattern {
|
||||||
self.add_bindings(body, scope, id, body.binding_hygiene(id));
|
self.add_bindings(store, scope, id, store.binding_hygiene(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
|
pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
|
fn add_params_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, params: &[PatId]) {
|
||||||
params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat));
|
params.iter().for_each(|pat| self.add_pat_bindings(store, scope, *pat));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
|
fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
|
||||||
|
@ -218,7 +218,7 @@ impl ExprScopes {
|
||||||
fn compute_block_scopes(
|
fn compute_block_scopes(
|
||||||
statements: &[Statement],
|
statements: &[Statement],
|
||||||
tail: Option<ExprId>,
|
tail: Option<ExprId>,
|
||||||
body: &Body,
|
store: &ExpressionStore,
|
||||||
scopes: &mut ExprScopes,
|
scopes: &mut ExprScopes,
|
||||||
scope: &mut ScopeId,
|
scope: &mut ScopeId,
|
||||||
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
||||||
|
@ -227,17 +227,17 @@ fn compute_block_scopes(
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Let { pat, initializer, else_branch, .. } => {
|
Statement::Let { pat, initializer, else_branch, .. } => {
|
||||||
if let Some(expr) = initializer {
|
if let Some(expr) = initializer {
|
||||||
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
|
compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
if let Some(expr) = else_branch {
|
if let Some(expr) = else_branch {
|
||||||
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
|
compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
*scope = scopes.new_scope(*scope);
|
*scope = scopes.new_scope(*scope);
|
||||||
scopes.add_pat_bindings(body, *scope, *pat);
|
scopes.add_pat_bindings(store, *scope, *pat);
|
||||||
}
|
}
|
||||||
Statement::Expr { expr, .. } => {
|
Statement::Expr { expr, .. } => {
|
||||||
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
|
compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
Statement::Item(Item::MacroDef(macro_id)) => {
|
Statement::Item(Item::MacroDef(macro_id)) => {
|
||||||
*scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
|
*scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
|
||||||
|
@ -246,32 +246,32 @@ fn compute_block_scopes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(expr) = tail {
|
if let Some(expr) = tail {
|
||||||
compute_expr_scopes(expr, body, scopes, scope, resolve_const_block);
|
compute_expr_scopes(expr, store, scopes, scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_expr_scopes(
|
fn compute_expr_scopes(
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
body: &Body,
|
store: &ExpressionStore,
|
||||||
scopes: &mut ExprScopes,
|
scopes: &mut ExprScopes,
|
||||||
scope: &mut ScopeId,
|
scope: &mut ScopeId,
|
||||||
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
||||||
) {
|
) {
|
||||||
let make_label =
|
let make_label =
|
||||||
|label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
|
|label: &Option<LabelId>| label.map(|label| (label, store.labels[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, body, scopes, scope, resolve_const_block)
|
compute_expr_scopes(expr, store, scopes, scope, resolve_const_block)
|
||||||
};
|
};
|
||||||
|
|
||||||
scopes.set_scope(expr, *scope);
|
scopes.set_scope(expr, *scope);
|
||||||
match &body[expr] {
|
match &store[expr] {
|
||||||
Expr::Block { statements, tail, id, label } => {
|
Expr::Block { statements, tail, id, label } => {
|
||||||
let mut scope = scopes.new_block_scope(*scope, *id, make_label(label));
|
let mut scope = scopes.new_block_scope(*scope, *id, make_label(label));
|
||||||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||||
// via the block itself (important for blocks that only contain items, no expressions).
|
// via the block itself (important for blocks that only contain items, no expressions).
|
||||||
scopes.set_scope(expr, scope);
|
scopes.set_scope(expr, scope);
|
||||||
compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block);
|
compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
Expr::Const(id) => {
|
Expr::Const(id) => {
|
||||||
let mut scope = scopes.root_scope();
|
let mut scope = scopes.root_scope();
|
||||||
|
@ -282,7 +282,7 @@ fn compute_expr_scopes(
|
||||||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||||
// via the block itself (important for blocks that only contain items, no expressions).
|
// via the block itself (important for blocks that only contain items, no expressions).
|
||||||
scopes.set_scope(expr, scope);
|
scopes.set_scope(expr, scope);
|
||||||
compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block);
|
compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
|
||||||
}
|
}
|
||||||
Expr::Loop { body: body_expr, label } => {
|
Expr::Loop { body: body_expr, label } => {
|
||||||
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
|
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
|
||||||
|
@ -290,14 +290,14 @@ fn compute_expr_scopes(
|
||||||
}
|
}
|
||||||
Expr::Closure { args, body: body_expr, .. } => {
|
Expr::Closure { args, body: body_expr, .. } => {
|
||||||
let mut scope = scopes.new_scope(*scope);
|
let mut scope = scopes.new_scope(*scope);
|
||||||
scopes.add_params_bindings(body, scope, args);
|
scopes.add_params_bindings(store, scope, args);
|
||||||
compute_expr_scopes(scopes, *body_expr, &mut scope);
|
compute_expr_scopes(scopes, *body_expr, &mut scope);
|
||||||
}
|
}
|
||||||
Expr::Match { expr, arms } => {
|
Expr::Match { expr, arms } => {
|
||||||
compute_expr_scopes(scopes, *expr, scope);
|
compute_expr_scopes(scopes, *expr, scope);
|
||||||
for arm in arms.iter() {
|
for arm in arms.iter() {
|
||||||
let mut scope = scopes.new_scope(*scope);
|
let mut scope = scopes.new_scope(*scope);
|
||||||
scopes.add_pat_bindings(body, scope, arm.pat);
|
scopes.add_pat_bindings(store, scope, arm.pat);
|
||||||
if let Some(guard) = arm.guard {
|
if let Some(guard) = arm.guard {
|
||||||
scope = scopes.new_scope(scope);
|
scope = scopes.new_scope(scope);
|
||||||
compute_expr_scopes(scopes, guard, &mut scope);
|
compute_expr_scopes(scopes, guard, &mut scope);
|
||||||
|
@ -316,9 +316,9 @@ fn compute_expr_scopes(
|
||||||
&Expr::Let { pat, expr } => {
|
&Expr::Let { pat, expr } => {
|
||||||
compute_expr_scopes(scopes, expr, scope);
|
compute_expr_scopes(scopes, expr, scope);
|
||||||
*scope = scopes.new_scope(*scope);
|
*scope = scopes.new_scope(*scope);
|
||||||
scopes.add_pat_bindings(body, *scope, pat);
|
scopes.add_pat_bindings(store, *scope, pat);
|
||||||
}
|
}
|
||||||
_ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
|
_ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod block;
|
mod block;
|
||||||
|
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
use la_arena::RawIdx;
|
||||||
use test_fixture::WithFixture;
|
use test_fixture::WithFixture;
|
||||||
|
|
||||||
use crate::{test_db::TestDB, ModuleDefId};
|
use crate::{test_db::TestDB, ModuleDefId};
|
|
@ -433,7 +433,7 @@ impl GenericParams {
|
||||||
GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
|
GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
|
GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
|
GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::ConstId(_) => (
|
GenericDefId::ConstId(_) | GenericDefId::StaticId(_) => (
|
||||||
Arc::new(GenericParams {
|
Arc::new(GenericParams {
|
||||||
type_or_consts: Default::default(),
|
type_or_consts: Default::default(),
|
||||||
lifetimes: Default::default(),
|
lifetimes: Default::default(),
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::fmt;
|
||||||
|
|
||||||
use hir_expand::{name::Name, MacroDefId};
|
use hir_expand::{name::Name, MacroDefId};
|
||||||
use intern::Symbol;
|
use intern::Symbol;
|
||||||
use la_arena::{Idx, RawIdx};
|
use la_arena::Idx;
|
||||||
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
|
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use type_ref::TypeRefId;
|
use type_ref::TypeRefId;
|
||||||
|
@ -37,13 +37,10 @@ pub type BindingId = Idx<Binding>;
|
||||||
|
|
||||||
pub type ExprId = Idx<Expr>;
|
pub type ExprId = Idx<Expr>;
|
||||||
|
|
||||||
/// FIXME: this is a hacky function which should be removed
|
|
||||||
pub(crate) fn dummy_expr_id() -> ExprId {
|
|
||||||
ExprId::from_raw(RawIdx::from(u32::MAX))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type PatId = Idx<Pat>;
|
pub type PatId = Idx<Pat>;
|
||||||
|
|
||||||
|
// FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts
|
||||||
|
// are local to the body.
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub enum ExprOrPatId {
|
pub enum ExprOrPatId {
|
||||||
ExprId(ExprId),
|
ExprId(ExprId),
|
||||||
|
|
|
@ -519,7 +519,7 @@ mod tests {
|
||||||
crate_graph[krate]
|
crate_graph[krate]
|
||||||
.display_name
|
.display_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|it| &**it.crate_name() == crate_name)
|
.is_some_and(|it| it.crate_name().as_str() == crate_name)
|
||||||
})
|
})
|
||||||
.expect("could not find crate");
|
.expect("could not find crate");
|
||||||
|
|
||||||
|
|
|
@ -937,7 +937,7 @@ pub struct Param {
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||||
pub(crate) struct FnFlags: u8 {
|
pub(crate) struct FnFlags: u16 {
|
||||||
const HAS_SELF_PARAM = 1 << 0;
|
const HAS_SELF_PARAM = 1 << 0;
|
||||||
const HAS_BODY = 1 << 1;
|
const HAS_BODY = 1 << 1;
|
||||||
const HAS_DEFAULT_KW = 1 << 2;
|
const HAS_DEFAULT_KW = 1 << 2;
|
||||||
|
@ -946,6 +946,12 @@ bitflags::bitflags! {
|
||||||
const HAS_UNSAFE_KW = 1 << 5;
|
const HAS_UNSAFE_KW = 1 << 5;
|
||||||
const IS_VARARGS = 1 << 6;
|
const IS_VARARGS = 1 << 6;
|
||||||
const HAS_SAFE_KW = 1 << 7;
|
const HAS_SAFE_KW = 1 << 7;
|
||||||
|
/// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
|
||||||
|
/// but keeping it for all functions will consume a lot of memory when there are
|
||||||
|
/// only very few functions with it. So we only encode its existence here, and lookup
|
||||||
|
/// it if needed.
|
||||||
|
const HAS_TARGET_FEATURE = 1 << 8;
|
||||||
|
const DEPRECATED_SAFE_2024 = 1 << 9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -502,4 +502,5 @@ language_item_table! {
|
||||||
|
|
||||||
String, sym::String, string, Target::Struct, GenericRequirement::None;
|
String, sym::String, string, Target::Struct, GenericRequirement::None;
|
||||||
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
||||||
|
Ordering, sym::Ordering, ordering, Target::Enum, GenericRequirement::None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub mod lang_item;
|
||||||
|
|
||||||
pub mod hir;
|
pub mod hir;
|
||||||
pub use self::hir::type_ref;
|
pub use self::hir::type_ref;
|
||||||
pub mod body;
|
pub mod expr_store;
|
||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
|
|
||||||
pub mod nameres;
|
pub mod nameres;
|
||||||
|
@ -693,6 +693,7 @@ impl TypeOwnerId {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it),
|
TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it),
|
||||||
TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it),
|
TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it),
|
||||||
|
TypeOwnerId::StaticId(it) => GenericDefId::StaticId(it),
|
||||||
TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it),
|
TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it),
|
||||||
TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it),
|
TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it),
|
||||||
TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it),
|
TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it),
|
||||||
|
@ -701,7 +702,7 @@ impl TypeOwnerId {
|
||||||
TypeOwnerId::EnumVariantId(it) => {
|
TypeOwnerId::EnumVariantId(it) => {
|
||||||
GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
|
GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
|
||||||
}
|
}
|
||||||
TypeOwnerId::InTypeConstId(_) | TypeOwnerId::StaticId(_) => return None,
|
TypeOwnerId::InTypeConstId(_) => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -743,6 +744,7 @@ impl From<GenericDefId> for TypeOwnerId {
|
||||||
GenericDefId::TypeAliasId(it) => it.into(),
|
GenericDefId::TypeAliasId(it) => it.into(),
|
||||||
GenericDefId::ImplId(it) => it.into(),
|
GenericDefId::ImplId(it) => it.into(),
|
||||||
GenericDefId::ConstId(it) => it.into(),
|
GenericDefId::ConstId(it) => it.into(),
|
||||||
|
GenericDefId::StaticId(it) => it.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,7 +853,7 @@ impl GeneralConstId {
|
||||||
pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
||||||
match self {
|
match self {
|
||||||
GeneralConstId::ConstId(it) => Some(it.into()),
|
GeneralConstId::ConstId(it) => Some(it.into()),
|
||||||
GeneralConstId::StaticId(_) => None,
|
GeneralConstId::StaticId(it) => Some(it.into()),
|
||||||
GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
|
GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
|
||||||
GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
|
GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
|
||||||
}
|
}
|
||||||
|
@ -897,7 +899,7 @@ impl DefWithBodyId {
|
||||||
pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
||||||
match self {
|
match self {
|
||||||
DefWithBodyId::FunctionId(f) => Some(f.into()),
|
DefWithBodyId::FunctionId(f) => Some(f.into()),
|
||||||
DefWithBodyId::StaticId(_) => None,
|
DefWithBodyId::StaticId(s) => Some(s.into()),
|
||||||
DefWithBodyId::ConstId(c) => Some(c.into()),
|
DefWithBodyId::ConstId(c) => Some(c.into()),
|
||||||
DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()),
|
DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()),
|
||||||
// FIXME: stable rust doesn't allow generics in constants, but we should
|
// FIXME: stable rust doesn't allow generics in constants, but we should
|
||||||
|
@ -922,23 +924,28 @@ impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
pub enum GenericDefId {
|
pub enum GenericDefId {
|
||||||
FunctionId(FunctionId),
|
|
||||||
AdtId(AdtId),
|
AdtId(AdtId),
|
||||||
TraitId(TraitId),
|
|
||||||
TraitAliasId(TraitAliasId),
|
|
||||||
TypeAliasId(TypeAliasId),
|
|
||||||
ImplId(ImplId),
|
|
||||||
// consts can have type parameters from their parents (i.e. associated consts of traits)
|
// consts can have type parameters from their parents (i.e. associated consts of traits)
|
||||||
ConstId(ConstId),
|
ConstId(ConstId),
|
||||||
|
FunctionId(FunctionId),
|
||||||
|
ImplId(ImplId),
|
||||||
|
// can't actually have generics currently, but they might in the future
|
||||||
|
// More importantly, this completes the set of items that contain type references
|
||||||
|
// which is to be used by the signature expression store in the future.
|
||||||
|
StaticId(StaticId),
|
||||||
|
TraitAliasId(TraitAliasId),
|
||||||
|
TraitId(TraitId),
|
||||||
|
TypeAliasId(TypeAliasId),
|
||||||
}
|
}
|
||||||
impl_from!(
|
impl_from!(
|
||||||
FunctionId,
|
|
||||||
AdtId(StructId, EnumId, UnionId),
|
AdtId(StructId, EnumId, UnionId),
|
||||||
TraitId,
|
ConstId,
|
||||||
TraitAliasId,
|
FunctionId,
|
||||||
TypeAliasId,
|
|
||||||
ImplId,
|
ImplId,
|
||||||
ConstId
|
StaticId,
|
||||||
|
TraitAliasId,
|
||||||
|
TraitId,
|
||||||
|
TypeAliasId
|
||||||
for GenericDefId
|
for GenericDefId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -969,6 +976,7 @@ impl GenericDefId {
|
||||||
GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
|
GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
|
GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None),
|
GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None),
|
||||||
|
GenericDefId::StaticId(it) => (it.lookup(db).id.file_id(), None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1350,6 +1358,7 @@ impl HasModule for GenericDefId {
|
||||||
GenericDefId::TypeAliasId(it) => it.module(db),
|
GenericDefId::TypeAliasId(it) => it.module(db),
|
||||||
GenericDefId::ImplId(it) => it.module(db),
|
GenericDefId::ImplId(it) => it.module(db),
|
||||||
GenericDefId::ConstId(it) => it.module(db),
|
GenericDefId::ConstId(it) => it.module(db),
|
||||||
|
GenericDefId::StaticId(it) => it.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,7 +337,7 @@ impl DefMap {
|
||||||
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> {
|
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> {
|
||||||
let crate_graph = db.crate_graph();
|
let crate_graph = db.crate_graph();
|
||||||
let krate = &crate_graph[crate_id];
|
let krate = &crate_graph[crate_id];
|
||||||
let name = krate.display_name.as_deref().unwrap_or_default();
|
let name = krate.display_name.as_deref().map(Symbol::as_str).unwrap_or_default();
|
||||||
let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
|
let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
|
||||||
|
|
||||||
let module_data = ModuleData::new(
|
let module_data = ModuleData::new(
|
||||||
|
|
|
@ -10,13 +10,13 @@ use smallvec::{smallvec, SmallVec};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{
|
|
||||||
scope::{ExprScopes, ScopeId},
|
|
||||||
HygieneId,
|
|
||||||
},
|
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
data::ExternCrateDeclData,
|
data::ExternCrateDeclData,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
expr_store::{
|
||||||
|
scope::{ExprScopes, ScopeId},
|
||||||
|
HygieneId,
|
||||||
|
},
|
||||||
generics::{GenericParams, TypeOrConstParamData},
|
generics::{GenericParams, TypeOrConstParamData},
|
||||||
hir::{BindingId, ExprId, LabelId},
|
hir::{BindingId, ExprId, LabelId},
|
||||||
item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
|
item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
|
||||||
|
@ -1264,6 +1264,7 @@ impl HasResolver for GenericDefId {
|
||||||
GenericDefId::TypeAliasId(inner) => inner.resolver(db),
|
GenericDefId::TypeAliasId(inner) => inner.resolver(db),
|
||||||
GenericDefId::ImplId(inner) => inner.resolver(db),
|
GenericDefId::ImplId(inner) => inner.resolver(db),
|
||||||
GenericDefId::ConstId(inner) => inner.resolver(db),
|
GenericDefId::ConstId(inner) => inner.resolver(db),
|
||||||
|
GenericDefId::StaticId(inner) => inner.resolver(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,6 +262,6 @@ impl AsName for ast::FieldKind {
|
||||||
|
|
||||||
impl AsName for base_db::Dependency {
|
impl AsName for base_db::Dependency {
|
||||||
fn as_name(&self) -> Name {
|
fn as_name(&self) -> Name {
|
||||||
Name::new_root(&self.name)
|
Name::new_symbol_root((*self.name).clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,9 @@ pub fn prettify_macro_expansion(
|
||||||
} else if let Some(dep) =
|
} else if let Some(dep) =
|
||||||
target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
|
target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
|
||||||
{
|
{
|
||||||
make::tokens::ident(&dep.name)
|
make::tokens::ident(dep.name.as_str())
|
||||||
} else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
|
} else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
|
||||||
make::tokens::ident(crate_name.crate_name())
|
make::tokens::ident(crate_name.crate_name().as_str())
|
||||||
} else {
|
} else {
|
||||||
return dollar_crate.clone();
|
return dollar_crate.clone();
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,11 @@ pub(crate) fn deref_by_trait(
|
||||||
}
|
}
|
||||||
|
|
||||||
let trait_id = || {
|
let trait_id = || {
|
||||||
if use_receiver_trait {
|
// FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will
|
||||||
|
// effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the
|
||||||
|
// blanked impl on `Deref`.
|
||||||
|
#[expect(clippy::overly_complex_bool_expr)]
|
||||||
|
if use_receiver_trait && false {
|
||||||
if let Some(receiver) =
|
if let Some(receiver) =
|
||||||
db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
|
db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use base_db::{ra_salsa::Cycle, CrateId};
|
use base_db::{ra_salsa::Cycle, CrateId};
|
||||||
use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
|
use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{Body, HygieneId},
|
expr_store::{Body, HygieneId},
|
||||||
hir::{Expr, ExprId},
|
hir::{Expr, ExprId},
|
||||||
path::Path,
|
path::Path,
|
||||||
resolver::{Resolver, ValueNs},
|
resolver::{Resolver, ValueNs},
|
||||||
|
@ -124,6 +124,7 @@ pub(crate) fn path_to_const<'g>(
|
||||||
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
|
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
|
||||||
expected_ty,
|
expected_ty,
|
||||||
)),
|
)),
|
||||||
|
// FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors.
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use hir_def::{
|
pub(crate) use hir_def::{
|
||||||
body::Body,
|
expr_store::Body,
|
||||||
hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
|
hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
|
||||||
LocalFieldId, VariantId,
|
LocalFieldId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,8 @@ pub(crate) mod pat_analysis;
|
||||||
|
|
||||||
use chalk_ir::Mutability;
|
use chalk_ir::Mutability;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
|
data::adt::VariantData, expr_store::Body, hir::PatId, AdtId, EnumVariantId, LocalFieldId,
|
||||||
|
VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use span::Edition;
|
use span::Edition;
|
||||||
|
|
|
@ -5,28 +5,31 @@ use std::mem;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
expr_store::Body,
|
||||||
hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
|
hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
|
||||||
path::Path,
|
path::Path,
|
||||||
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
||||||
type_ref::Rawness,
|
type_ref::Rawness,
|
||||||
AdtId, DefWithBodyId, FieldId, VariantId,
|
AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
|
||||||
};
|
};
|
||||||
|
use span::Edition;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind,
|
db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TargetFeatures, TyExt,
|
||||||
|
TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns `(unsafe_exprs, fn_is_unsafe)`.
|
#[derive(Debug, Default)]
|
||||||
///
|
pub struct MissingUnsafeResult {
|
||||||
|
pub unsafe_exprs: Vec<(ExprOrPatId, UnsafetyReason)>,
|
||||||
/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
|
/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
|
||||||
pub fn missing_unsafe(
|
pub fn_is_unsafe: bool,
|
||||||
db: &dyn HirDatabase,
|
pub deprecated_safe_calls: Vec<ExprId>,
|
||||||
def: DefWithBodyId,
|
}
|
||||||
) -> (Vec<(ExprOrPatId, UnsafetyReason)>, bool) {
|
|
||||||
|
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafeResult {
|
||||||
let _p = tracing::info_span!("missing_unsafe").entered();
|
let _p = tracing::info_span!("missing_unsafe").entered();
|
||||||
|
|
||||||
let mut res = Vec::new();
|
|
||||||
let is_unsafe = match def {
|
let is_unsafe = match def {
|
||||||
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
|
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
|
||||||
DefWithBodyId::StaticId(_)
|
DefWithBodyId::StaticId(_)
|
||||||
|
@ -35,11 +38,19 @@ pub fn missing_unsafe(
|
||||||
| DefWithBodyId::InTypeConstId(_) => false,
|
| DefWithBodyId::InTypeConstId(_) => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut res = MissingUnsafeResult { fn_is_unsafe: is_unsafe, ..MissingUnsafeResult::default() };
|
||||||
let body = db.body(def);
|
let body = db.body(def);
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
let mut callback = |node, inside_unsafe_block, reason| {
|
let mut callback = |diag| match diag {
|
||||||
|
UnsafeDiagnostic::UnsafeOperation { node, inside_unsafe_block, reason } => {
|
||||||
if inside_unsafe_block == InsideUnsafeBlock::No {
|
if inside_unsafe_block == InsideUnsafeBlock::No {
|
||||||
res.push((node, reason));
|
res.unsafe_exprs.push((node, reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnsafeDiagnostic::DeprecatedSafe2024 { node, inside_unsafe_block } => {
|
||||||
|
if inside_unsafe_block == InsideUnsafeBlock::No {
|
||||||
|
res.deprecated_safe_calls.push(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback);
|
let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback);
|
||||||
|
@ -54,7 +65,7 @@ pub fn missing_unsafe(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(res, is_unsafe)
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -73,15 +84,31 @@ pub enum InsideUnsafeBlock {
|
||||||
Yes,
|
Yes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum UnsafeDiagnostic {
|
||||||
|
UnsafeOperation {
|
||||||
|
node: ExprOrPatId,
|
||||||
|
inside_unsafe_block: InsideUnsafeBlock,
|
||||||
|
reason: UnsafetyReason,
|
||||||
|
},
|
||||||
|
/// A lint.
|
||||||
|
DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock },
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unsafe_expressions(
|
pub fn unsafe_expressions(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
infer: &InferenceResult,
|
infer: &InferenceResult,
|
||||||
def: DefWithBodyId,
|
def: DefWithBodyId,
|
||||||
body: &Body,
|
body: &Body,
|
||||||
current: ExprId,
|
current: ExprId,
|
||||||
unsafe_expr_cb: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
|
callback: &mut dyn FnMut(InsideUnsafeBlock),
|
||||||
) {
|
) {
|
||||||
let mut visitor = UnsafeVisitor::new(db, infer, body, def, unsafe_expr_cb);
|
let mut visitor_callback = |diag| {
|
||||||
|
if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag {
|
||||||
|
callback(inside_unsafe_block);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback);
|
||||||
_ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current);
|
_ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current);
|
||||||
visitor.walk_expr(current);
|
visitor.walk_expr(current);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +122,10 @@ struct UnsafeVisitor<'a> {
|
||||||
inside_unsafe_block: InsideUnsafeBlock,
|
inside_unsafe_block: InsideUnsafeBlock,
|
||||||
inside_assignment: bool,
|
inside_assignment: bool,
|
||||||
inside_union_destructure: bool,
|
inside_union_destructure: bool,
|
||||||
unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
|
callback: &'a mut dyn FnMut(UnsafeDiagnostic),
|
||||||
|
def_target_features: TargetFeatures,
|
||||||
|
// FIXME: This needs to be the edition of the span of each call.
|
||||||
|
edition: Edition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> UnsafeVisitor<'a> {
|
impl<'a> UnsafeVisitor<'a> {
|
||||||
|
@ -104,9 +134,14 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
infer: &'a InferenceResult,
|
infer: &'a InferenceResult,
|
||||||
body: &'a Body,
|
body: &'a Body,
|
||||||
def: DefWithBodyId,
|
def: DefWithBodyId,
|
||||||
unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
|
unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let resolver = def.resolver(db.upcast());
|
let resolver = def.resolver(db.upcast());
|
||||||
|
let def_target_features = match def {
|
||||||
|
DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
|
||||||
|
_ => TargetFeatures::default(),
|
||||||
|
};
|
||||||
|
let edition = db.crate_graph()[resolver.module().krate()].edition;
|
||||||
Self {
|
Self {
|
||||||
db,
|
db,
|
||||||
infer,
|
infer,
|
||||||
|
@ -116,12 +151,34 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
inside_unsafe_block: InsideUnsafeBlock::No,
|
inside_unsafe_block: InsideUnsafeBlock::No,
|
||||||
inside_assignment: false,
|
inside_assignment: false,
|
||||||
inside_union_destructure: false,
|
inside_union_destructure: false,
|
||||||
unsafe_expr_cb,
|
callback: unsafe_expr_cb,
|
||||||
|
def_target_features,
|
||||||
|
edition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_cb(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
|
fn on_unsafe_op(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
|
||||||
(self.unsafe_expr_cb)(node, self.inside_unsafe_block, reason);
|
(self.callback)(UnsafeDiagnostic::UnsafeOperation {
|
||||||
|
node,
|
||||||
|
inside_unsafe_block: self.inside_unsafe_block,
|
||||||
|
reason,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_call(&mut self, node: ExprId, func: FunctionId) {
|
||||||
|
let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition);
|
||||||
|
match unsafety {
|
||||||
|
crate::utils::Unsafety::Safe => {}
|
||||||
|
crate::utils::Unsafety::Unsafe => {
|
||||||
|
self.on_unsafe_op(node.into(), UnsafetyReason::UnsafeFnCall)
|
||||||
|
}
|
||||||
|
crate::utils::Unsafety::DeprecatedSafe2024 => {
|
||||||
|
(self.callback)(UnsafeDiagnostic::DeprecatedSafe2024 {
|
||||||
|
node,
|
||||||
|
inside_unsafe_block: self.inside_unsafe_block,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) {
|
fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) {
|
||||||
|
@ -146,7 +203,9 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
| Pat::Ref { .. }
|
| Pat::Ref { .. }
|
||||||
| Pat::Box { .. }
|
| Pat::Box { .. }
|
||||||
| Pat::Expr(..)
|
| Pat::Expr(..)
|
||||||
| Pat::ConstBlock(..) => self.call_cb(current.into(), UnsafetyReason::UnionField),
|
| Pat::ConstBlock(..) => {
|
||||||
|
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField)
|
||||||
|
}
|
||||||
// `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
|
// `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
|
||||||
Pat::Missing | Pat::Wild | Pat::Or(_) => {}
|
Pat::Missing | Pat::Wild | Pat::Or(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -180,9 +239,13 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
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, .. } => {
|
||||||
if let Some(func) = self.infer[callee].as_fn_def(self.db) {
|
let callee = &self.infer[callee];
|
||||||
if is_fn_unsafe_to_call(self.db, func) {
|
if let Some(func) = callee.as_fn_def(self.db) {
|
||||||
self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
|
self.check_call(current, func);
|
||||||
|
}
|
||||||
|
if let TyKind::Function(fn_ptr) = callee.kind(Interner) {
|
||||||
|
if fn_ptr.sig.safety == chalk_ir::Safety::Unsafe {
|
||||||
|
self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,18 +272,13 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::MethodCall { .. } => {
|
Expr::MethodCall { .. } => {
|
||||||
if self
|
if let Some((func, _)) = self.infer.method_resolution(current) {
|
||||||
.infer
|
self.check_call(current, func);
|
||||||
.method_resolution(current)
|
|
||||||
.map(|(func, _)| is_fn_unsafe_to_call(self.db, func))
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||||
if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) {
|
if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) {
|
||||||
self.call_cb(current.into(), UnsafetyReason::RawPtrDeref);
|
self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Unsafe { .. } => {
|
Expr::Unsafe { .. } => {
|
||||||
|
@ -235,7 +293,7 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
self.walk_pats_top(std::iter::once(target), current);
|
self.walk_pats_top(std::iter::once(target), current);
|
||||||
self.inside_assignment = old_inside_assignment;
|
self.inside_assignment = old_inside_assignment;
|
||||||
}
|
}
|
||||||
Expr::InlineAsm(_) => self.call_cb(current.into(), UnsafetyReason::InlineAsm),
|
Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm),
|
||||||
// rustc allows union assignment to propagate through field accesses and casts.
|
// rustc allows union assignment to propagate through field accesses and casts.
|
||||||
Expr::Cast { .. } => self.inside_assignment = inside_assignment,
|
Expr::Cast { .. } => self.inside_assignment = inside_assignment,
|
||||||
Expr::Field { .. } => {
|
Expr::Field { .. } => {
|
||||||
|
@ -244,7 +302,7 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) =
|
if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) =
|
||||||
self.infer.field_resolution(current)
|
self.infer.field_resolution(current)
|
||||||
{
|
{
|
||||||
self.call_cb(current.into(), UnsafetyReason::UnionField);
|
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,9 +337,9 @@ impl<'a> UnsafeVisitor<'a> {
|
||||||
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
|
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
|
||||||
let static_data = self.db.static_data(id);
|
let static_data = self.db.static_data(id);
|
||||||
if static_data.mutable {
|
if static_data.mutable {
|
||||||
self.call_cb(node, UnsafetyReason::MutableStatic);
|
self.on_unsafe_op(node, UnsafetyReason::MutableStatic);
|
||||||
} else if static_data.is_extern && !static_data.has_safe_kw {
|
} else if static_data.is_extern && !static_data.has_safe_kw {
|
||||||
self.call_cb(node, UnsafetyReason::ExternStatic);
|
self.on_unsafe_op(node, UnsafetyReason::ExternStatic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,7 +262,8 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
|
||||||
GenericDefId::FunctionId(it) => it.lookup(db).container,
|
GenericDefId::FunctionId(it) => it.lookup(db).container,
|
||||||
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
|
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
|
||||||
GenericDefId::ConstId(it) => it.lookup(db).container,
|
GenericDefId::ConstId(it) => it.lookup(db).container,
|
||||||
GenericDefId::AdtId(_)
|
GenericDefId::StaticId(_)
|
||||||
|
| GenericDefId::AdtId(_)
|
||||||
| GenericDefId::TraitId(_)
|
| GenericDefId::TraitId(_)
|
||||||
| GenericDefId::ImplId(_)
|
| GenericDefId::ImplId(_)
|
||||||
| GenericDefId::TraitAliasId(_) => return None,
|
| GenericDefId::TraitAliasId(_) => return None,
|
||||||
|
|
|
@ -34,9 +34,9 @@ use chalk_ir::{
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{Body, HygieneId},
|
|
||||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||||
data::{ConstData, StaticData},
|
data::{ConstData, StaticData},
|
||||||
|
expr_store::{Body, HygieneId},
|
||||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
layout::Integer,
|
layout::Integer,
|
||||||
|
@ -466,6 +466,9 @@ pub struct InferenceResult {
|
||||||
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
|
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
|
||||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
||||||
/// Whether there are any type-mismatching errors in the result.
|
/// Whether there are any type-mismatching errors in the result.
|
||||||
|
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
|
||||||
|
// `TyKind::Error`.
|
||||||
|
// Which will then mark this field.
|
||||||
pub(crate) has_errors: bool,
|
pub(crate) has_errors: bool,
|
||||||
/// Interned common types to return references to.
|
/// Interned common types to return references to.
|
||||||
// FIXME: Move this into `InferenceContext`
|
// FIXME: Move this into `InferenceContext`
|
||||||
|
@ -943,7 +946,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
let ty = self.normalize_associated_types_in(ty);
|
let ty = self.normalize_associated_types_in(ty);
|
||||||
|
|
||||||
self.infer_top_pat(*pat, &ty);
|
self.infer_top_pat(*pat, &ty, None);
|
||||||
if ty
|
if ty
|
||||||
.data(Interner)
|
.data(Interner)
|
||||||
.flags
|
.flags
|
||||||
|
|
|
@ -374,6 +374,7 @@ enum PointerKind {
|
||||||
|
|
||||||
fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
|
fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
|
||||||
let ty = table.resolve_ty_shallow(ty);
|
let ty = table.resolve_ty_shallow(ty);
|
||||||
|
let ty = table.normalize_associated_types_in(ty);
|
||||||
|
|
||||||
if table.is_sized(&ty) {
|
if table.is_sized(&ty) {
|
||||||
return Ok(Some(PointerKind::Thin));
|
return Ok(Some(PointerKind::Thin));
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use hir_def::body::HygieneId;
|
use hir_def::expr_store::HygieneId;
|
||||||
use hir_def::hir::ExprOrPatId;
|
use hir_def::hir::ExprOrPatId;
|
||||||
use hir_def::path::{Path, PathSegment, PathSegments};
|
use hir_def::path::{Path, PathSegment, PathSegments};
|
||||||
use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
|
use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
|
||||||
|
|
|
@ -43,9 +43,9 @@ use crate::{
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
|
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
|
||||||
FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
|
DeclOrigin, FnAbi, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution,
|
||||||
TyBuilder, TyExt, TyKind,
|
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -334,7 +334,11 @@ impl InferenceContext<'_> {
|
||||||
ExprIsRead::No
|
ExprIsRead::No
|
||||||
};
|
};
|
||||||
let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read);
|
let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read);
|
||||||
self.infer_top_pat(pat, &input_ty);
|
self.infer_top_pat(
|
||||||
|
pat,
|
||||||
|
&input_ty,
|
||||||
|
Some(DeclContext { origin: DeclOrigin::LetExpr }),
|
||||||
|
);
|
||||||
self.result.standard_types.bool_.clone()
|
self.result.standard_types.bool_.clone()
|
||||||
}
|
}
|
||||||
Expr::Block { statements, tail, label, id } => {
|
Expr::Block { statements, tail, label, id } => {
|
||||||
|
@ -461,7 +465,7 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
// Now go through the argument patterns
|
// Now go through the argument patterns
|
||||||
for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) {
|
for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) {
|
||||||
self.infer_top_pat(*arg_pat, arg_ty);
|
self.infer_top_pat(*arg_pat, arg_ty, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: lift these out into a struct
|
// FIXME: lift these out into a struct
|
||||||
|
@ -582,7 +586,7 @@ impl InferenceContext<'_> {
|
||||||
let mut all_arms_diverge = Diverges::Always;
|
let mut all_arms_diverge = Diverges::Always;
|
||||||
for arm in arms.iter() {
|
for arm in arms.iter() {
|
||||||
let input_ty = self.resolve_ty_shallow(&input_ty);
|
let input_ty = self.resolve_ty_shallow(&input_ty);
|
||||||
self.infer_top_pat(arm.pat, &input_ty);
|
self.infer_top_pat(arm.pat, &input_ty, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = expected.adjust_for_branches(&mut self.table);
|
let expected = expected.adjust_for_branches(&mut self.table);
|
||||||
|
@ -927,7 +931,7 @@ impl InferenceContext<'_> {
|
||||||
let resolver_guard =
|
let resolver_guard =
|
||||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||||
self.inside_assignment = true;
|
self.inside_assignment = true;
|
||||||
self.infer_top_pat(target, &rhs_ty);
|
self.infer_top_pat(target, &rhs_ty, None);
|
||||||
self.inside_assignment = false;
|
self.inside_assignment = false;
|
||||||
self.resolver.reset_to_guard(resolver_guard);
|
self.resolver.reset_to_guard(resolver_guard);
|
||||||
}
|
}
|
||||||
|
@ -1632,8 +1636,11 @@ impl InferenceContext<'_> {
|
||||||
decl_ty
|
decl_ty
|
||||||
};
|
};
|
||||||
|
|
||||||
this.infer_top_pat(*pat, &ty);
|
let decl = DeclContext {
|
||||||
|
origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() },
|
||||||
|
};
|
||||||
|
|
||||||
|
this.infer_top_pat(*pat, &ty, Some(decl));
|
||||||
if let Some(expr) = else_branch {
|
if let Some(expr) = else_branch {
|
||||||
let previous_diverges =
|
let previous_diverges =
|
||||||
mem::replace(&mut this.diverges, Diverges::Maybe);
|
mem::replace(&mut this.diverges, Diverges::Maybe);
|
||||||
|
|
|
@ -3,23 +3,24 @@
|
||||||
use std::iter::repeat_with;
|
use std::iter::repeat_with;
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
expr_store::Body,
|
||||||
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
|
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
|
||||||
path::Path,
|
path::Path,
|
||||||
|
HasModule,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use stdx::TupleExt;
|
use stdx::TupleExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::{try_const_usize, usize_const},
|
consteval::{self, try_const_usize, usize_const},
|
||||||
infer::{
|
infer::{
|
||||||
coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
|
coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
},
|
},
|
||||||
lower::lower_to_chalk_mutability,
|
lower::lower_to_chalk_mutability,
|
||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
|
static_lifetime, DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar,
|
||||||
TyBuilder, TyExt, TyKind,
|
Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl InferenceContext<'_> {
|
impl InferenceContext<'_> {
|
||||||
|
@ -34,6 +35,7 @@ impl InferenceContext<'_> {
|
||||||
id: PatId,
|
id: PatId,
|
||||||
ellipsis: Option<u32>,
|
ellipsis: Option<u32>,
|
||||||
subs: &[PatId],
|
subs: &[PatId],
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let (ty, def) = self.resolve_variant(id.into(), path, true);
|
let (ty, def) = self.resolve_variant(id.into(), path, true);
|
||||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||||
|
@ -92,13 +94,13 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.infer_pat(subpat, &expected_ty, default_bm);
|
self.infer_pat(subpat, &expected_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let err_ty = self.err_ty();
|
let err_ty = self.err_ty();
|
||||||
for &inner in subs {
|
for &inner in subs {
|
||||||
self.infer_pat(inner, &err_ty, default_bm);
|
self.infer_pat(inner, &err_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +116,7 @@ impl InferenceContext<'_> {
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
id: PatId,
|
id: PatId,
|
||||||
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let (ty, def) = self.resolve_variant(id.into(), path, false);
|
let (ty, def) = self.resolve_variant(id.into(), path, false);
|
||||||
if let Some(variant) = def {
|
if let Some(variant) = def {
|
||||||
|
@ -162,13 +165,13 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.infer_pat(inner, &expected_ty, default_bm);
|
self.infer_pat(inner, &expected_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let err_ty = self.err_ty();
|
let err_ty = self.err_ty();
|
||||||
for (_, inner) in subs {
|
for (_, inner) in subs {
|
||||||
self.infer_pat(inner, &err_ty, default_bm);
|
self.infer_pat(inner, &err_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +188,7 @@ impl InferenceContext<'_> {
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
ellipsis: Option<u32>,
|
ellipsis: Option<u32>,
|
||||||
subs: &[PatId],
|
subs: &[PatId],
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let expected = self.resolve_ty_shallow(expected);
|
let expected = self.resolve_ty_shallow(expected);
|
||||||
let expectations = match expected.as_tuple() {
|
let expectations = match expected.as_tuple() {
|
||||||
|
@ -209,12 +213,12 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
// Process pre
|
// Process pre
|
||||||
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
||||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
*ty = self.infer_pat(*pat, ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process post
|
// Process post
|
||||||
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
|
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
|
||||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
*ty = self.infer_pat(*pat, ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
||||||
|
@ -223,11 +227,17 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
/// The resolver needs to be updated to the surrounding expression when inside assignment
|
/// The resolver needs to be updated to the surrounding expression when inside assignment
|
||||||
/// (because there, `Pat::Path` can refer to a variable).
|
/// (because there, `Pat::Path` can refer to a variable).
|
||||||
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
|
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty, decl: Option<DeclContext>) {
|
||||||
self.infer_pat(pat, expected, BindingMode::default());
|
self.infer_pat(pat, expected, BindingMode::default(), decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
|
fn infer_pat(
|
||||||
|
&mut self,
|
||||||
|
pat: PatId,
|
||||||
|
expected: &Ty,
|
||||||
|
mut default_bm: BindingMode,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
|
) -> Ty {
|
||||||
let mut expected = self.resolve_ty_shallow(expected);
|
let mut expected = self.resolve_ty_shallow(expected);
|
||||||
|
|
||||||
if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
|
if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
|
||||||
|
@ -261,11 +271,11 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
let ty = match &self.body[pat] {
|
let ty = match &self.body[pat] {
|
||||||
Pat::Tuple { args, ellipsis } => {
|
Pat::Tuple { args, ellipsis } => {
|
||||||
self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args)
|
self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args, decl)
|
||||||
}
|
}
|
||||||
Pat::Or(pats) => {
|
Pat::Or(pats) => {
|
||||||
for pat in pats.iter() {
|
for pat in pats.iter() {
|
||||||
self.infer_pat(*pat, &expected, default_bm);
|
self.infer_pat(*pat, &expected, default_bm, decl);
|
||||||
}
|
}
|
||||||
expected.clone()
|
expected.clone()
|
||||||
}
|
}
|
||||||
|
@ -274,6 +284,7 @@ impl InferenceContext<'_> {
|
||||||
lower_to_chalk_mutability(mutability),
|
lower_to_chalk_mutability(mutability),
|
||||||
&expected,
|
&expected,
|
||||||
default_bm,
|
default_bm,
|
||||||
|
decl,
|
||||||
),
|
),
|
||||||
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
|
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
|
||||||
.infer_tuple_struct_pat_like(
|
.infer_tuple_struct_pat_like(
|
||||||
|
@ -283,10 +294,11 @@ impl InferenceContext<'_> {
|
||||||
pat,
|
pat,
|
||||||
*ellipsis,
|
*ellipsis,
|
||||||
subpats,
|
subpats,
|
||||||
|
decl,
|
||||||
),
|
),
|
||||||
Pat::Record { path: p, args: fields, ellipsis: _ } => {
|
Pat::Record { path: p, args: fields, ellipsis: _ } => {
|
||||||
let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
|
let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
|
||||||
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
|
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs, decl)
|
||||||
}
|
}
|
||||||
Pat::Path(path) => {
|
Pat::Path(path) => {
|
||||||
let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
|
let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
|
||||||
|
@ -319,10 +331,10 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pat::Bind { id, subpat } => {
|
Pat::Bind { id, subpat } => {
|
||||||
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
|
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected, decl);
|
||||||
}
|
}
|
||||||
Pat::Slice { prefix, slice, suffix } => {
|
Pat::Slice { prefix, slice, suffix } => {
|
||||||
self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
|
self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm, decl)
|
||||||
}
|
}
|
||||||
Pat::Wild => expected.clone(),
|
Pat::Wild => expected.clone(),
|
||||||
Pat::Range { .. } => {
|
Pat::Range { .. } => {
|
||||||
|
@ -345,7 +357,7 @@ impl InferenceContext<'_> {
|
||||||
_ => (self.result.standard_types.unknown.clone(), None),
|
_ => (self.result.standard_types.unknown.clone(), None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm);
|
let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm, decl);
|
||||||
let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty);
|
let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty);
|
||||||
|
|
||||||
if let Some(alloc_ty) = alloc_ty {
|
if let Some(alloc_ty) = alloc_ty {
|
||||||
|
@ -420,6 +432,7 @@ impl InferenceContext<'_> {
|
||||||
mutability: Mutability,
|
mutability: Mutability,
|
||||||
expected: &Ty,
|
expected: &Ty,
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let (expectation_type, expectation_lt) = match expected.as_reference() {
|
let (expectation_type, expectation_lt) = match expected.as_reference() {
|
||||||
Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
|
Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
|
||||||
|
@ -433,7 +446,7 @@ impl InferenceContext<'_> {
|
||||||
(inner_ty, inner_lt)
|
(inner_ty, inner_lt)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let subty = self.infer_pat(inner_pat, &expectation_type, default_bm);
|
let subty = self.infer_pat(inner_pat, &expectation_type, default_bm, decl);
|
||||||
TyKind::Ref(mutability, expectation_lt, subty).intern(Interner)
|
TyKind::Ref(mutability, expectation_lt, subty).intern(Interner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,6 +457,7 @@ impl InferenceContext<'_> {
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
subpat: Option<PatId>,
|
subpat: Option<PatId>,
|
||||||
expected: &Ty,
|
expected: &Ty,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let Binding { mode, .. } = self.body.bindings[binding];
|
let Binding { mode, .. } = self.body.bindings[binding];
|
||||||
let mode = if mode == BindingAnnotation::Unannotated {
|
let mode = if mode == BindingAnnotation::Unannotated {
|
||||||
|
@ -454,7 +468,7 @@ impl InferenceContext<'_> {
|
||||||
self.result.binding_modes.insert(pat, mode);
|
self.result.binding_modes.insert(pat, mode);
|
||||||
|
|
||||||
let inner_ty = match subpat {
|
let inner_ty = match subpat {
|
||||||
Some(subpat) => self.infer_pat(subpat, expected, default_bm),
|
Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl),
|
||||||
None => expected.clone(),
|
None => expected.clone(),
|
||||||
};
|
};
|
||||||
let inner_ty = self.insert_type_vars_shallow(inner_ty);
|
let inner_ty = self.insert_type_vars_shallow(inner_ty);
|
||||||
|
@ -478,14 +492,28 @@ impl InferenceContext<'_> {
|
||||||
slice: &Option<PatId>,
|
slice: &Option<PatId>,
|
||||||
suffix: &[PatId],
|
suffix: &[PatId],
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
|
let expected = self.resolve_ty_shallow(expected);
|
||||||
|
|
||||||
|
// If `expected` is an infer ty, we try to equate it to an array if the given pattern
|
||||||
|
// allows it. See issue #16609
|
||||||
|
if self.pat_is_irrefutable(decl) && expected.is_ty_var() {
|
||||||
|
if let Some(resolved_array_ty) =
|
||||||
|
self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice)
|
||||||
|
{
|
||||||
|
self.unify(&expected, &resolved_array_ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected = self.resolve_ty_shallow(&expected);
|
||||||
let elem_ty = match expected.kind(Interner) {
|
let elem_ty = match expected.kind(Interner) {
|
||||||
TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
|
TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
|
||||||
_ => self.err_ty(),
|
_ => self.err_ty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for &pat_id in prefix.iter().chain(suffix.iter()) {
|
for &pat_id in prefix.iter().chain(suffix.iter()) {
|
||||||
self.infer_pat(pat_id, &elem_ty, default_bm);
|
self.infer_pat(pat_id, &elem_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let &Some(slice_pat_id) = slice {
|
if let &Some(slice_pat_id) = slice {
|
||||||
|
@ -499,7 +527,7 @@ impl InferenceContext<'_> {
|
||||||
_ => TyKind::Slice(elem_ty.clone()),
|
_ => TyKind::Slice(elem_ty.clone()),
|
||||||
}
|
}
|
||||||
.intern(Interner);
|
.intern(Interner);
|
||||||
self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
|
self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
match expected.kind(Interner) {
|
match expected.kind(Interner) {
|
||||||
|
@ -528,7 +556,7 @@ impl InferenceContext<'_> {
|
||||||
self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
|
self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
|
fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool {
|
||||||
match &body[pat] {
|
match &body[pat] {
|
||||||
Pat::Tuple { .. }
|
Pat::Tuple { .. }
|
||||||
| Pat::TupleStruct { .. }
|
| Pat::TupleStruct { .. }
|
||||||
|
@ -553,6 +581,59 @@ impl InferenceContext<'_> {
|
||||||
| Pat::Expr(_) => false,
|
| Pat::Expr(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_resolve_slice_ty_to_array_ty(
|
||||||
|
&mut self,
|
||||||
|
before: &[PatId],
|
||||||
|
suffix: &[PatId],
|
||||||
|
slice: &Option<PatId>,
|
||||||
|
) -> Option<Ty> {
|
||||||
|
if !slice.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = before.len() + suffix.len();
|
||||||
|
let size =
|
||||||
|
consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db.upcast()));
|
||||||
|
|
||||||
|
let elem_ty = self.table.new_type_var();
|
||||||
|
let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner);
|
||||||
|
Some(array_ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to determine whether we can infer the expected type in the slice pattern to be of type array.
|
||||||
|
/// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable
|
||||||
|
/// patterns we wouldn't e.g. report ambiguity in the following situation:
|
||||||
|
///
|
||||||
|
/// ```ignore(rust)
|
||||||
|
/// struct Zeroes;
|
||||||
|
/// const ARR: [usize; 2] = [0; 2];
|
||||||
|
/// const ARR2: [usize; 2] = [2; 2];
|
||||||
|
///
|
||||||
|
/// impl Into<&'static [usize; 2]> for Zeroes {
|
||||||
|
/// fn into(self) -> &'static [usize; 2] {
|
||||||
|
/// &ARR
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Into<&'static [usize]> for Zeroes {
|
||||||
|
/// fn into(self) -> &'static [usize] {
|
||||||
|
/// &ARR2
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let &[a, b]: &[usize] = Zeroes.into() else {
|
||||||
|
/// ..
|
||||||
|
/// };
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If we're in an irrefutable pattern we prefer the array impl candidate given that
|
||||||
|
/// the slice impl candidate would be rejected anyway (if no ambiguity existed).
|
||||||
|
fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool {
|
||||||
|
matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -86,10 +86,9 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let generic_def_id = value_def.to_generic_def_id(self.db);
|
let generic_def = value_def.to_generic_def_id(self.db);
|
||||||
let Some(generic_def) = generic_def_id else {
|
if let GenericDefId::StaticId(_) = generic_def {
|
||||||
// `value_def` is the kind of item that can never be generic (i.e. statics, at least
|
// `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
|
||||||
// currently). We can just skip the binders to get its type.
|
|
||||||
let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders();
|
let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders();
|
||||||
stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",);
|
stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",);
|
||||||
return Some(ValuePathResolution::NonGeneric(ty));
|
return Some(ValuePathResolution::NonGeneric(ty));
|
||||||
|
@ -122,7 +121,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent_substs = self_subst.or_else(|| {
|
let parent_substs = self_subst.or_else(|| {
|
||||||
let generics = generics(self.db.upcast(), generic_def_id?);
|
let generics = generics(self.db.upcast(), generic_def);
|
||||||
let parent_params_len = generics.parent_generics()?.len();
|
let parent_params_len = generics.parent_generics()?.len();
|
||||||
let parent_args = &substs[substs.len() - parent_params_len..];
|
let parent_args = &substs[substs.len() - parent_params_len..];
|
||||||
Some(Substitution::from_iter(Interner, parent_args))
|
Some(Substitution::from_iter(Interner, parent_args))
|
||||||
|
|
|
@ -100,7 +100,9 @@ pub use mapping::{
|
||||||
};
|
};
|
||||||
pub use method_resolution::check_orphan_rules;
|
pub use method_resolution::check_orphan_rules;
|
||||||
pub use traits::TraitEnvironment;
|
pub use traits::TraitEnvironment;
|
||||||
pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call};
|
pub use utils::{
|
||||||
|
all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures, Unsafety,
|
||||||
|
};
|
||||||
pub use variance::Variance;
|
pub use variance::Variance;
|
||||||
|
|
||||||
pub use chalk_ir::{
|
pub use chalk_ir::{
|
||||||
|
@ -1047,3 +1049,20 @@ pub fn known_const_to_ast(
|
||||||
}
|
}
|
||||||
Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
|
Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) enum DeclOrigin {
|
||||||
|
LetExpr,
|
||||||
|
/// from `let x = ..`
|
||||||
|
LocalDecl {
|
||||||
|
has_else: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides context for checking patterns in declarations. More specifically this
|
||||||
|
/// allows us to infer array types if the pattern is irrefutable and allows us to infer
|
||||||
|
/// the size of the array. See issue rust-lang/rust#76342.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) struct DeclContext {
|
||||||
|
pub(crate) origin: DeclOrigin,
|
||||||
|
}
|
||||||
|
|
|
@ -23,10 +23,10 @@ use chalk_ir::{
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::HygieneId,
|
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
data::{adt::StructKind, TraitFlags},
|
data::{adt::StructKind, TraitFlags},
|
||||||
expander::Expander,
|
expander::Expander,
|
||||||
|
expr_store::HygieneId,
|
||||||
generics::{
|
generics::{
|
||||||
GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
||||||
WherePredicateTypeTarget,
|
WherePredicateTypeTarget,
|
||||||
|
@ -2471,14 +2471,14 @@ pub enum ValueTyDefId {
|
||||||
impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
|
impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
|
||||||
|
|
||||||
impl ValueTyDefId {
|
impl ValueTyDefId {
|
||||||
pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> Option<GenericDefId> {
|
pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> GenericDefId {
|
||||||
match self {
|
match self {
|
||||||
Self::FunctionId(id) => Some(id.into()),
|
Self::FunctionId(id) => id.into(),
|
||||||
Self::StructId(id) => Some(id.into()),
|
Self::StructId(id) => id.into(),
|
||||||
Self::UnionId(id) => Some(id.into()),
|
Self::UnionId(id) => id.into(),
|
||||||
Self::EnumVariantId(var) => Some(var.lookup(db.upcast()).parent.into()),
|
Self::EnumVariantId(var) => var.lookup(db.upcast()).parent.into(),
|
||||||
Self::ConstId(id) => Some(id.into()),
|
Self::ConstId(id) => id.into(),
|
||||||
Self::StaticId(_) => None,
|
Self::StaticId(id) => id.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
|
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
use chalk_ir::{cast::Cast, UniverseIndex, WithKind};
|
use chalk_ir::{cast::Cast, UniverseIndex, WithKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
|
@ -732,15 +733,27 @@ fn lookup_impl_assoc_item_for_trait_ref(
|
||||||
let self_ty = trait_ref.self_type_parameter(Interner);
|
let self_ty = trait_ref.self_type_parameter(Interner);
|
||||||
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
|
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
|
||||||
let impls = db.trait_impls_in_deps(env.krate);
|
let impls = db.trait_impls_in_deps(env.krate);
|
||||||
let self_impls = match self_ty.kind(Interner) {
|
|
||||||
TyKind::Adt(id, _) => {
|
let trait_module = hir_trait_id.module(db.upcast());
|
||||||
id.0.module(db.upcast()).containing_block().and_then(|it| db.trait_impls_in_block(it))
|
let type_module = match self_ty_fp {
|
||||||
|
TyFingerprint::Adt(adt_id) => Some(adt_id.module(db.upcast())),
|
||||||
|
TyFingerprint::ForeignType(type_id) => {
|
||||||
|
Some(from_foreign_def_id(type_id).module(db.upcast()))
|
||||||
}
|
}
|
||||||
|
TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db.upcast())),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let def_blocks: ArrayVec<_, 2> =
|
||||||
|
[trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter_map(|block_id| db.trait_impls_in_block(block_id))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let impls = impls
|
let impls = impls
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self_impls.as_ref())
|
.chain(&def_blocks)
|
||||||
.flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp));
|
.flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp));
|
||||||
|
|
||||||
let table = InferenceTable::new(db, env);
|
let table = InferenceTable::new(db, env);
|
||||||
|
|
|
@ -16,7 +16,7 @@ use base_db::CrateId;
|
||||||
use chalk_ir::Mutability;
|
use chalk_ir::Mutability;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
expr_store::Body,
|
||||||
hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
|
hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
|
||||||
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
|
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,9 +6,9 @@ use base_db::CrateId;
|
||||||
use chalk_ir::{cast::Cast, Mutability};
|
use chalk_ir::{cast::Cast, Mutability};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::HygieneId,
|
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
data::adt::{StructFlags, VariantData},
|
data::adt::{StructFlags, VariantData},
|
||||||
|
expr_store::HygieneId,
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
layout::{TagEncoding, Variants},
|
layout::{TagEncoding, Variants},
|
||||||
resolver::{HasResolver, TypeNs, ValueNs},
|
resolver::{HasResolver, TypeNs, ValueNs},
|
||||||
|
@ -1644,14 +1644,15 @@ impl Evaluator<'_> {
|
||||||
Variants::Multiple { tag, tag_encoding, variants, .. } => {
|
Variants::Multiple { tag, tag_encoding, variants, .. } => {
|
||||||
let size = tag.size(&*self.target_data_layout).bytes_usize();
|
let size = tag.size(&*self.target_data_layout).bytes_usize();
|
||||||
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
|
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
|
||||||
|
let is_signed = tag.is_signed();
|
||||||
match tag_encoding {
|
match tag_encoding {
|
||||||
TagEncoding::Direct => {
|
TagEncoding::Direct => {
|
||||||
let tag = &bytes[offset..offset + size];
|
let tag = &bytes[offset..offset + size];
|
||||||
Ok(i128::from_le_bytes(pad16(tag, false)))
|
Ok(i128::from_le_bytes(pad16(tag, is_signed)))
|
||||||
}
|
}
|
||||||
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
|
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
|
||||||
let tag = &bytes[offset..offset + size];
|
let tag = &bytes[offset..offset + size];
|
||||||
let candidate_tag = i128::from_le_bytes(pad16(tag, false))
|
let candidate_tag = i128::from_le_bytes(pad16(tag, is_signed))
|
||||||
.wrapping_sub(*niche_start as i128)
|
.wrapping_sub(*niche_start as i128)
|
||||||
as usize;
|
as usize;
|
||||||
let idx = variants
|
let idx = variants
|
||||||
|
@ -2943,10 +2944,10 @@ pub fn render_const_using_debug_impl(
|
||||||
// a3 = ::core::fmt::Arguments::new_v1(a1, a2)
|
// a3 = ::core::fmt::Arguments::new_v1(a1, a2)
|
||||||
// FIXME: similarly, we should call function here, not directly working with memory.
|
// FIXME: similarly, we should call function here, not directly working with memory.
|
||||||
let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?;
|
let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?;
|
||||||
evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?;
|
evaluator.write_memory(a3, &a1.to_bytes())?;
|
||||||
|
evaluator.write_memory(a3.offset(evaluator.ptr_size()), &[1])?;
|
||||||
|
evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a2.to_bytes())?;
|
||||||
evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?;
|
evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?;
|
||||||
evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?;
|
|
||||||
evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?;
|
|
||||||
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
|
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
&hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
|
&hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
|
//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
|
||||||
//! is not available.
|
//! is not available.
|
||||||
//!
|
//!
|
||||||
use std::cmp;
|
use std::cmp::{self, Ordering};
|
||||||
|
|
||||||
use chalk_ir::TyKind;
|
use chalk_ir::TyKind;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
builtin_type::{BuiltinInt, BuiltinUint},
|
builtin_type::{BuiltinInt, BuiltinUint},
|
||||||
|
lang_item::LangItemTarget,
|
||||||
resolver::HasResolver,
|
resolver::HasResolver,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
@ -1317,6 +1318,82 @@ impl Evaluator<'_> {
|
||||||
self.write_memory_using_ref(dst, size)?.fill(val);
|
self.write_memory_using_ref(dst, size)?.fill(val);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
"ptr_metadata" => {
|
||||||
|
let [ptr] = args else {
|
||||||
|
return Err(MirEvalError::InternalError(
|
||||||
|
"ptr_metadata args are not provided".into(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let arg = ptr.interval.get(self)?.to_owned();
|
||||||
|
let metadata = &arg[self.ptr_size()..];
|
||||||
|
destination.write_from_bytes(self, metadata)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
"three_way_compare" => {
|
||||||
|
let [lhs, rhs] = args else {
|
||||||
|
return Err(MirEvalError::InternalError(
|
||||||
|
"three_way_compare args are not provided".into(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let Some(ty) =
|
||||||
|
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
|
||||||
|
else {
|
||||||
|
return Err(MirEvalError::InternalError(
|
||||||
|
"three_way_compare generic arg is not provided".into(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let signed = match ty.as_builtin().unwrap() {
|
||||||
|
BuiltinType::Int(_) => true,
|
||||||
|
BuiltinType::Uint(_) => false,
|
||||||
|
_ => {
|
||||||
|
return Err(MirEvalError::InternalError(
|
||||||
|
"three_way_compare expects an integral type".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let rhs = rhs.get(self)?;
|
||||||
|
let lhs = lhs.get(self)?;
|
||||||
|
let mut result = Ordering::Equal;
|
||||||
|
for (l, r) in lhs.iter().zip(rhs).rev() {
|
||||||
|
let it = l.cmp(r);
|
||||||
|
if it != Ordering::Equal {
|
||||||
|
result = it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if signed {
|
||||||
|
if let Some((&l, &r)) = lhs.iter().zip(rhs).next_back() {
|
||||||
|
if l != r {
|
||||||
|
result = (l as i8).cmp(&(r as i8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(LangItemTarget::EnumId(e)) =
|
||||||
|
self.db.lang_item(self.crate_id, LangItem::Ordering)
|
||||||
|
{
|
||||||
|
let ty = self.db.ty(e.into());
|
||||||
|
let r = self
|
||||||
|
.compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?;
|
||||||
|
destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(MirEvalError::InternalError("Ordering enum not found".into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"aggregate_raw_ptr" => {
|
||||||
|
let [data, meta] = args else {
|
||||||
|
return Err(MirEvalError::InternalError(
|
||||||
|
"aggregate_raw_ptr args are not provided".into(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
destination.write_from_interval(self, data.interval)?;
|
||||||
|
Interval {
|
||||||
|
addr: destination.addr.offset(data.interval.size),
|
||||||
|
size: destination.size - data.interval.size,
|
||||||
|
}
|
||||||
|
.write_from_interval(self, meta.interval)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
_ if needs_override => not_supported!("intrinsic {name} is not implemented"),
|
_ if needs_override => not_supported!("intrinsic {name} is not implemented"),
|
||||||
_ => return Ok(false),
|
_ => return Ok(false),
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ use std::{fmt::Write, iter, mem};
|
||||||
use base_db::ra_salsa::Cycle;
|
use base_db::ra_salsa::Cycle;
|
||||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{Body, HygieneId},
|
|
||||||
data::adt::{StructKind, VariantData},
|
data::adt::{StructKind, VariantData},
|
||||||
|
expr_store::{Body, HygieneId},
|
||||||
hir::{
|
hir::{
|
||||||
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
|
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
|
||||||
LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
|
LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
|
||||||
|
@ -2156,7 +2156,7 @@ pub fn lower_to_mir(
|
||||||
// need to take this input explicitly.
|
// need to take this input explicitly.
|
||||||
root_expr: ExprId,
|
root_expr: ExprId,
|
||||||
) -> Result<MirBody> {
|
) -> Result<MirBody> {
|
||||||
if infer.has_errors {
|
if infer.type_mismatches().next().is_some() {
|
||||||
return Err(MirLowerError::HasErrors);
|
return Err(MirLowerError::HasErrors);
|
||||||
}
|
}
|
||||||
let mut ctx = MirLowerCtx::new(db, owner, body, infer);
|
let mut ctx = MirLowerCtx::new(db, owner, body, infer);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{body::Body, hir::BindingId};
|
use hir_def::{expr_store::Body, hir::BindingId};
|
||||||
use hir_expand::{name::Name, Lookup};
|
use hir_expand::{name::Name, Lookup};
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
use span::Edition;
|
use span::Edition;
|
||||||
|
|
|
@ -18,8 +18,8 @@ use std::sync::LazyLock;
|
||||||
use base_db::SourceDatabaseFileInputExt as _;
|
use base_db::SourceDatabaseFileInputExt as _;
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{Body, BodySourceMap},
|
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
expr_store::{Body, BodySourceMap},
|
||||||
hir::{ExprId, Pat, PatId},
|
hir::{ExprId, Pat, PatId},
|
||||||
item_scope::ItemScope,
|
item_scope::ItemScope,
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
|
|
|
@ -2163,9 +2163,9 @@ impl Receiver for Bar {
|
||||||
fn main() {
|
fn main() {
|
||||||
let bar = Bar;
|
let bar = Bar;
|
||||||
let _v1 = bar.foo1();
|
let _v1 = bar.foo1();
|
||||||
//^^^ type: i32
|
//^^^ type: {unknown}
|
||||||
let _v2 = bar.foo2();
|
let _v2 = bar.foo2();
|
||||||
//^^^ type: bool
|
//^^^ type: {unknown}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -3814,3 +3814,50 @@ async fn foo(a: (), b: i32) -> u32 {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn irrefutable_slices() {
|
||||||
|
check_infer(
|
||||||
|
r#"
|
||||||
|
//- minicore: from
|
||||||
|
struct A;
|
||||||
|
|
||||||
|
impl From<A> for [u8; 2] {
|
||||||
|
fn from(a: A) -> Self {
|
||||||
|
[0; 2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<A> for [u8; 3] {
|
||||||
|
fn from(a: A) -> Self {
|
||||||
|
[0; 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = A;
|
||||||
|
let [b, c] = a.into();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
50..51 'a': A
|
||||||
|
64..86 '{ ... }': [u8; 2]
|
||||||
|
74..80 '[0; 2]': [u8; 2]
|
||||||
|
75..76 '0': u8
|
||||||
|
78..79 '2': usize
|
||||||
|
128..129 'a': A
|
||||||
|
142..164 '{ ... }': [u8; 3]
|
||||||
|
152..158 '[0; 3]': [u8; 3]
|
||||||
|
153..154 '0': u8
|
||||||
|
156..157 '3': usize
|
||||||
|
179..224 '{ ...o(); }': ()
|
||||||
|
189..190 'a': A
|
||||||
|
193..194 'A': A
|
||||||
|
204..210 '[b, c]': [u8; 2]
|
||||||
|
205..206 'b': u8
|
||||||
|
208..209 'c': u8
|
||||||
|
213..214 'a': A
|
||||||
|
213..221 'a.into()': [u8; 2]
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -9,19 +9,22 @@ use chalk_ir::{
|
||||||
DebruijnIndex,
|
DebruijnIndex,
|
||||||
};
|
};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
|
attr::Attrs,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
generics::{WherePredicate, WherePredicateTypeTarget},
|
generics::{WherePredicate, WherePredicateTypeTarget},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
resolver::{HasResolver, TypeNs},
|
resolver::{HasResolver, TypeNs},
|
||||||
|
tt,
|
||||||
type_ref::{TraitBoundModifier, TypeRef},
|
type_ref::{TraitBoundModifier, TypeRef},
|
||||||
EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId,
|
EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId,
|
||||||
TypeOrConstParamId,
|
TypeOrConstParamId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use intern::sym;
|
use intern::{sym, Symbol};
|
||||||
use rustc_abi::TargetDataLayout;
|
use rustc_abi::TargetDataLayout;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use span::Edition;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -264,10 +267,65 @@ impl<'a> ClosureSubst<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetFeatures {
|
||||||
|
enabled: FxHashSet<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetFeatures {
|
||||||
|
pub fn from_attrs(attrs: &Attrs) -> Self {
|
||||||
|
let enabled = attrs
|
||||||
|
.by_key(&sym::target_feature)
|
||||||
|
.tt_values()
|
||||||
|
.filter_map(|tt| {
|
||||||
|
match tt.token_trees().flat_tokens() {
|
||||||
|
[
|
||||||
|
tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)),
|
||||||
|
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })),
|
||||||
|
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { kind: tt::LitKind::Str, symbol: features, .. })),
|
||||||
|
] if enable_ident.sym == sym::enable => Some(features),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flat_map(|features| features.as_str().split(',').map(Symbol::intern))
|
||||||
|
.collect();
|
||||||
|
Self { enabled }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Unsafety {
|
||||||
|
Safe,
|
||||||
|
Unsafe,
|
||||||
|
/// A lint.
|
||||||
|
DeprecatedSafe2024,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fn_unsafe_to_call(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
func: FunctionId,
|
||||||
|
caller_target_features: &TargetFeatures,
|
||||||
|
call_edition: Edition,
|
||||||
|
) -> Unsafety {
|
||||||
let data = db.function_data(func);
|
let data = db.function_data(func);
|
||||||
if data.is_unsafe() {
|
if data.is_unsafe() {
|
||||||
return true;
|
return Unsafety::Unsafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.has_target_feature() {
|
||||||
|
// RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
|
||||||
|
let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into()));
|
||||||
|
if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) {
|
||||||
|
return Unsafety::Unsafe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.is_deprecated_safe_2024() {
|
||||||
|
if call_edition.at_least_2024() {
|
||||||
|
return Unsafety::Unsafe;
|
||||||
|
} else {
|
||||||
|
return Unsafety::DeprecatedSafe2024;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let loc = func.lookup(db.upcast());
|
let loc = func.lookup(db.upcast());
|
||||||
|
@ -279,14 +337,22 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
|
||||||
if is_intrinsic_block {
|
if is_intrinsic_block {
|
||||||
// legacy intrinsics
|
// legacy intrinsics
|
||||||
// extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
|
// extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
|
||||||
!db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists()
|
if db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() {
|
||||||
|
Unsafety::Safe
|
||||||
|
} else {
|
||||||
|
Unsafety::Unsafe
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Function in an `extern` block are always unsafe to call, except when
|
// Function in an `extern` block are always unsafe to call, except when
|
||||||
// it is marked as `safe`.
|
// it is marked as `safe`.
|
||||||
!data.is_safe()
|
if data.is_safe() {
|
||||||
|
Unsafety::Safe
|
||||||
|
} else {
|
||||||
|
Unsafety::Unsafe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => false,
|
}
|
||||||
|
_ => Unsafety::Safe,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1028,6 +1028,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
|
||||||
}
|
}
|
||||||
GenericDefId::ImplId(_) => return None,
|
GenericDefId::ImplId(_) => return None,
|
||||||
GenericDefId::ConstId(_) => return None,
|
GenericDefId::ConstId(_) => return None,
|
||||||
|
GenericDefId::StaticId(_) => return None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
|
@ -271,11 +271,17 @@ pub struct PrivateField {
|
||||||
pub field: Field,
|
pub field: Field,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum UnsafeLint {
|
||||||
|
HardError,
|
||||||
|
UnsafeOpInUnsafeFn,
|
||||||
|
DeprecatedSafe2024,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MissingUnsafe {
|
pub struct MissingUnsafe {
|
||||||
pub node: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
pub node: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
||||||
/// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error.
|
pub lint: UnsafeLint,
|
||||||
pub only_lint: bool,
|
|
||||||
pub reason: UnsafetyReason,
|
pub reason: UnsafetyReason,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +417,7 @@ impl AnyDiagnostic {
|
||||||
pub(crate) fn body_validation_diagnostic(
|
pub(crate) fn body_validation_diagnostic(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
diagnostic: BodyValidationDiagnostic,
|
diagnostic: BodyValidationDiagnostic,
|
||||||
source_map: &hir_def::body::BodySourceMap,
|
source_map: &hir_def::expr_store::BodySourceMap,
|
||||||
) -> Option<AnyDiagnostic> {
|
) -> Option<AnyDiagnostic> {
|
||||||
match diagnostic {
|
match diagnostic {
|
||||||
BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
|
BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
|
||||||
|
@ -547,7 +553,7 @@ impl AnyDiagnostic {
|
||||||
def: DefWithBodyId,
|
def: DefWithBodyId,
|
||||||
d: &InferenceDiagnostic,
|
d: &InferenceDiagnostic,
|
||||||
outer_types_source_map: &TypesSourceMap,
|
outer_types_source_map: &TypesSourceMap,
|
||||||
source_map: &hir_def::body::BodySourceMap,
|
source_map: &hir_def::expr_store::BodySourceMap,
|
||||||
) -> Option<AnyDiagnostic> {
|
) -> Option<AnyDiagnostic> {
|
||||||
let expr_syntax = |expr| {
|
let expr_syntax = |expr| {
|
||||||
source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
|
source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
|
||||||
|
|
|
@ -80,7 +80,9 @@ impl HirDisplay for Function {
|
||||||
if data.is_async() {
|
if data.is_async() {
|
||||||
f.write_str("async ")?;
|
f.write_str("async ")?;
|
||||||
}
|
}
|
||||||
if self.is_unsafe_to_call(db) {
|
// FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
|
||||||
|
// (they are conditionally unsafe to call). We probably should show something else.
|
||||||
|
if self.is_unsafe_to_call(db, None, f.edition()) {
|
||||||
f.write_str("unsafe ")?;
|
f.write_str("unsafe ")?;
|
||||||
}
|
}
|
||||||
if let Some(abi) = &data.abi {
|
if let Some(abi) = &data.abi {
|
||||||
|
|
|
@ -183,6 +183,7 @@ impl From<GenericDef> for GenericDefId {
|
||||||
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
|
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
|
||||||
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
|
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
|
||||||
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
|
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
|
||||||
|
GenericDef::Static(it) => GenericDefId::StaticId(it.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +198,7 @@ impl From<GenericDefId> for GenericDef {
|
||||||
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
|
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
|
||||||
GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
|
GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
|
||||||
GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
|
GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
|
||||||
|
GenericDefId::StaticId(it) => GenericDef::Static(it.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,8 @@ use arrayvec::ArrayVec;
|
||||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin};
|
use base_db::{CrateDisplayName, CrateId, CrateOrigin};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::BodyDiagnostic,
|
|
||||||
data::{adt::VariantData, TraitFlags},
|
data::{adt::VariantData, TraitFlags},
|
||||||
|
expr_store::ExpressionStoreDiagnostics,
|
||||||
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
||||||
hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
|
hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
|
||||||
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
|
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
|
||||||
|
@ -1892,10 +1892,10 @@ impl DefWithBody {
|
||||||
|
|
||||||
for diag in source_map.diagnostics() {
|
for diag in source_map.diagnostics() {
|
||||||
acc.push(match diag {
|
acc.push(match diag {
|
||||||
BodyDiagnostic::InactiveCode { node, cfg, opts } => {
|
ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => {
|
||||||
InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
|
InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
|
||||||
}
|
}
|
||||||
BodyDiagnostic::MacroError { node, err } => {
|
ExpressionStoreDiagnostics::MacroError { node, err } => {
|
||||||
let RenderedExpandError { message, error, kind } =
|
let RenderedExpandError { message, error, kind } =
|
||||||
err.render_to_string(db.upcast());
|
err.render_to_string(db.upcast());
|
||||||
|
|
||||||
|
@ -1919,20 +1919,22 @@ impl DefWithBody {
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
BodyDiagnostic::UnresolvedMacroCall { node, path } => UnresolvedMacroCall {
|
ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => {
|
||||||
|
UnresolvedMacroCall {
|
||||||
macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
|
macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
|
||||||
precise_location: None,
|
precise_location: None,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
is_bang: true,
|
is_bang: true,
|
||||||
}
|
}
|
||||||
.into(),
|
.into()
|
||||||
BodyDiagnostic::AwaitOutsideOfAsync { node, location } => {
|
}
|
||||||
|
ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => {
|
||||||
AwaitOutsideOfAsync { node: *node, location: location.clone() }.into()
|
AwaitOutsideOfAsync { node: *node, location: location.clone() }.into()
|
||||||
}
|
}
|
||||||
BodyDiagnostic::UnreachableLabel { node, name } => {
|
ExpressionStoreDiagnostics::UnreachableLabel { node, name } => {
|
||||||
UnreachableLabel { node: *node, name: name.clone() }.into()
|
UnreachableLabel { node: *node, name: name.clone() }.into()
|
||||||
}
|
}
|
||||||
BodyDiagnostic::UndeclaredLabel { node, name } => {
|
ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => {
|
||||||
UndeclaredLabel { node: *node, name: name.clone() }.into()
|
UndeclaredLabel { node: *node, name: name.clone() }.into()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1976,16 +1978,40 @@ impl DefWithBody {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (unsafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into());
|
let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, self.into());
|
||||||
for (node, reason) in unsafe_exprs {
|
for (node, reason) in missing_unsafe.unsafe_exprs {
|
||||||
match source_map.expr_or_pat_syntax(node) {
|
match source_map.expr_or_pat_syntax(node) {
|
||||||
Ok(node) => acc.push(MissingUnsafe { node, only_lint, reason }.into()),
|
Ok(node) => acc.push(
|
||||||
|
MissingUnsafe {
|
||||||
|
node,
|
||||||
|
lint: if missing_unsafe.fn_is_unsafe {
|
||||||
|
UnsafeLint::UnsafeOpInUnsafeFn
|
||||||
|
} else {
|
||||||
|
UnsafeLint::HardError
|
||||||
|
},
|
||||||
|
reason,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
Err(SyntheticSyntax) => {
|
Err(SyntheticSyntax) => {
|
||||||
// FIXME: Here and elsewhere in this file, the `expr` was
|
// FIXME: Here and elsewhere in this file, the `expr` was
|
||||||
// desugared, report or assert that this doesn't happen.
|
// desugared, report or assert that this doesn't happen.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for node in missing_unsafe.deprecated_safe_calls {
|
||||||
|
match source_map.expr_syntax(node) {
|
||||||
|
Ok(node) => acc.push(
|
||||||
|
MissingUnsafe {
|
||||||
|
node: node.map(|it| it.wrap_left()),
|
||||||
|
lint: UnsafeLint::DeprecatedSafe2024,
|
||||||
|
reason: UnsafetyReason::UnsafeFnCall,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
Err(SyntheticSyntax) => never!("synthetic DeprecatedSafe2024"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(borrowck_results) = db.borrowck(self.into()) {
|
if let Ok(borrowck_results) = db.borrowck(self.into()) {
|
||||||
for borrowck_result in borrowck_results.iter() {
|
for borrowck_result in borrowck_results.iter() {
|
||||||
|
@ -2361,8 +2387,19 @@ impl Function {
|
||||||
db.attrs(self.id.into()).is_unstable()
|
db.attrs(self.id.into()).is_unstable()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool {
|
pub fn is_unsafe_to_call(
|
||||||
hir_ty::is_fn_unsafe_to_call(db, self.id)
|
self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
caller: Option<Function>,
|
||||||
|
call_edition: Edition,
|
||||||
|
) -> bool {
|
||||||
|
let target_features = caller
|
||||||
|
.map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())))
|
||||||
|
.unwrap_or_default();
|
||||||
|
matches!(
|
||||||
|
hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition),
|
||||||
|
hir_ty::Unsafety::Unsafe
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this function declaration has a definition.
|
/// Whether this function declaration has a definition.
|
||||||
|
@ -3453,6 +3490,7 @@ pub enum GenericDef {
|
||||||
Impl(Impl),
|
Impl(Impl),
|
||||||
// consts can have type parameters from their parents (i.e. associated consts of traits)
|
// consts can have type parameters from their parents (i.e. associated consts of traits)
|
||||||
Const(Const),
|
Const(Const),
|
||||||
|
Static(Static),
|
||||||
}
|
}
|
||||||
impl_from!(
|
impl_from!(
|
||||||
Function,
|
Function,
|
||||||
|
@ -3461,7 +3499,8 @@ impl_from!(
|
||||||
TraitAlias,
|
TraitAlias,
|
||||||
TypeAlias,
|
TypeAlias,
|
||||||
Impl,
|
Impl,
|
||||||
Const
|
Const,
|
||||||
|
Static
|
||||||
for GenericDef
|
for GenericDef
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3511,6 +3550,7 @@ impl GenericDef {
|
||||||
GenericDef::TypeAlias(it) => it.id.into(),
|
GenericDef::TypeAlias(it) => it.id.into(),
|
||||||
GenericDef::Impl(it) => it.id.into(),
|
GenericDef::Impl(it) => it.id.into(),
|
||||||
GenericDef::Const(it) => it.id.into(),
|
GenericDef::Const(it) => it.id.into(),
|
||||||
|
GenericDef::Static(it) => it.id.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3568,6 +3608,7 @@ impl GenericDef {
|
||||||
item_tree_source_maps.impl_(id.value).generics()
|
item_tree_source_maps.impl_(id.value).generics()
|
||||||
}
|
}
|
||||||
GenericDefId::ConstId(_) => return,
|
GenericDefId::ConstId(_) => return,
|
||||||
|
GenericDefId::StaticId(_) => return,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4624,17 +4665,6 @@ impl Type {
|
||||||
Type { env: TraitEnvironment::empty(krate), ty }
|
Type { env: TraitEnvironment::empty(krate), ty }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reference(inner: &Type, m: Mutability) -> Type {
|
|
||||||
inner.derived(
|
|
||||||
TyKind::Ref(
|
|
||||||
if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
|
|
||||||
hir_ty::error_lifetime(),
|
|
||||||
inner.ty.clone(),
|
|
||||||
)
|
|
||||||
.intern(Interner),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
|
fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
|
||||||
let resolver = lexical_env.resolver(db.upcast());
|
let resolver = lexical_env.resolver(db.upcast());
|
||||||
let environment = resolver
|
let environment = resolver
|
||||||
|
@ -4866,6 +4896,17 @@ impl Type {
|
||||||
self.normalize_trait_assoc_type(db, &[], iterator_item.into())
|
self.normalize_trait_assoc_type(db, &[], iterator_item.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
let Some(iterator_trait) =
|
||||||
|
db.lang_item(self.env.krate, LangItem::Iterator).and_then(|it| it.as_trait())
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let canonical_ty =
|
||||||
|
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||||
|
method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, iterator_trait)
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
|
/// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
|
||||||
pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
|
pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
|
||||||
let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
|
let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
|
||||||
|
|
|
@ -2040,6 +2040,13 @@ impl SemanticsScope<'_> {
|
||||||
Crate { id: self.resolver.krate() }
|
Crate { id: self.resolver.krate() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn containing_function(&self) -> Option<Function> {
|
||||||
|
self.resolver.body_owner().and_then(|owner| match owner {
|
||||||
|
DefWithBodyId::FunctionId(id) => Some(id.into()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn resolver(&self) -> &Resolver {
|
pub(crate) fn resolver(&self) -> &Resolver {
|
||||||
&self.resolver
|
&self.resolver
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{
|
expr_store::{
|
||||||
scope::{ExprScopes, ScopeId},
|
scope::{ExprScopes, ScopeId},
|
||||||
Body, BodySourceMap, HygieneId,
|
Body, BodySourceMap, HygieneId,
|
||||||
},
|
},
|
||||||
|
@ -1105,16 +1105,9 @@ impl SourceAnalyzer {
|
||||||
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
|
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
|
||||||
let mut is_unsafe = false;
|
let mut is_unsafe = false;
|
||||||
let mut walk_expr = |expr_id| {
|
let mut walk_expr = |expr_id| {
|
||||||
unsafe_expressions(
|
unsafe_expressions(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
|
||||||
db,
|
|
||||||
infer,
|
|
||||||
*def,
|
|
||||||
body,
|
|
||||||
expr_id,
|
|
||||||
&mut |_, inside_unsafe_block, _| {
|
|
||||||
is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
|
is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
|
||||||
},
|
})
|
||||||
)
|
|
||||||
};
|
};
|
||||||
match expanded_expr {
|
match expanded_expr {
|
||||||
ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr),
|
ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr),
|
||||||
|
@ -1259,7 +1252,11 @@ fn scope_for(
|
||||||
node: InFile<&SyntaxNode>,
|
node: InFile<&SyntaxNode>,
|
||||||
) -> Option<ScopeId> {
|
) -> Option<ScopeId> {
|
||||||
node.ancestors_with_macros(db.upcast())
|
node.ancestors_with_macros(db.upcast())
|
||||||
.take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
|
.take_while(|it| {
|
||||||
|
!ast::Item::can_cast(it.kind())
|
||||||
|
|| ast::MacroCall::can_cast(it.kind())
|
||||||
|
|| ast::Use::can_cast(it.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())
|
||||||
.find_map(|it| scopes.scope_for(it))
|
.find_map(|it| scopes.scope_for(it))
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl LookupTable {
|
||||||
self.data
|
self.data
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(t, _)| {
|
.find(|(t, _)| {
|
||||||
Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, ty)
|
t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty)
|
||||||
})
|
})
|
||||||
.map(|(t, it)| {
|
.map(|(t, it)| {
|
||||||
it.exprs(t)
|
it.exprs(t)
|
||||||
|
|
|
@ -15,6 +15,7 @@ use hir_ty::mir::BorrowKind;
|
||||||
use hir_ty::TyBuilder;
|
use hir_ty::TyBuilder;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use span::Edition;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef,
|
Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef,
|
||||||
|
@ -365,7 +366,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
|
||||||
let ret_ty = it.ret_type_with_args(db, generics.iter().cloned());
|
let ret_ty = it.ret_type_with_args(db, generics.iter().cloned());
|
||||||
// Filter out private and unsafe functions
|
// Filter out private and unsafe functions
|
||||||
if !it.is_visible_from(db, module)
|
if !it.is_visible_from(db, module)
|
||||||
|| it.is_unsafe_to_call(db)
|
|| it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
|
||||||
|| it.is_unstable(db)
|
|| it.is_unstable(db)
|
||||||
|| ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
|
|| ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
|
||||||
|| ret_ty.is_raw_ptr()
|
|| ret_ty.is_raw_ptr()
|
||||||
|
@ -470,7 +471,10 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out private and unsafe functions
|
// Filter out private and unsafe functions
|
||||||
if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
|
if !it.is_visible_from(db, module)
|
||||||
|
|| it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
|
||||||
|
|| it.is_unstable(db)
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +662,10 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out private and unsafe functions
|
// Filter out private and unsafe functions
|
||||||
if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
|
if !it.is_visible_from(db, module)
|
||||||
|
|| it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
|
||||||
|
|| it.is_unstable(db)
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
|
||||||
|
|
||||||
// Assist: apply_demorgan
|
// Assist: apply_demorgan
|
||||||
//
|
//
|
||||||
// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
|
// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
|
||||||
// This transforms expressions of the form `!l || !r` into `!(l && r)`.
|
// This transforms expressions of the form `!l || !r` into `!(l && r)`.
|
||||||
// This also works with `&&`. This assist can only be applied with the cursor
|
// This also works with `&&`. This assist can only be applied with the cursor
|
||||||
// on either `||` or `&&`.
|
// on either `||` or `&&`.
|
||||||
|
@ -131,7 +131,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
|
|
||||||
// Assist: apply_demorgan_iterator
|
// Assist: apply_demorgan_iterator
|
||||||
//
|
//
|
||||||
// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law] to
|
// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
|
||||||
// `Iterator::all` and `Iterator::any`.
|
// `Iterator::all` and `Iterator::any`.
|
||||||
//
|
//
|
||||||
// This transforms expressions of the form `!iter.any(|x| predicate(x))` into
|
// This transforms expressions of the form `!iter.any(|x| predicate(x))` into
|
||||||
|
|
|
@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
|
||||||
// use super::AssistContext;
|
// use super::AssistContext;
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// .Import Granularity
|
// #### Import Granularity
|
||||||
//
|
//
|
||||||
// It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
|
// It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
|
||||||
// It has the following configurations:
|
// It has the following configurations:
|
||||||
|
@ -54,7 +54,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
|
||||||
//
|
//
|
||||||
// In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`.
|
// In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`.
|
||||||
//
|
//
|
||||||
// .Import Prefix
|
// #### Import Prefix
|
||||||
//
|
//
|
||||||
// The style of imports in the same crate is configurable through the `imports.prefix` setting.
|
// The style of imports in the same crate is configurable through the `imports.prefix` setting.
|
||||||
// It has the following configurations:
|
// It has the following configurations:
|
||||||
|
@ -68,7 +68,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
|
||||||
//
|
//
|
||||||
// In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`.
|
// In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`.
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[]
|
// 
|
||||||
|
|
||||||
// Assist: auto_import
|
// Assist: auto_import
|
||||||
//
|
//
|
||||||
|
|
|
@ -140,8 +140,10 @@ fn edit_struct_references(
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::TupleStructPat(tuple_struct_pat) => {
|
ast::TupleStructPat(tuple_struct_pat) => {
|
||||||
|
let file_range = ctx.sema.original_range_opt(&node)?;
|
||||||
|
edit.edit_file(file_range.file_id);
|
||||||
edit.replace(
|
edit.replace(
|
||||||
tuple_struct_pat.syntax().text_range(),
|
file_range.range,
|
||||||
ast::make::record_pat_with_fields(
|
ast::make::record_pat_with_fields(
|
||||||
tuple_struct_pat.path()?,
|
tuple_struct_pat.path()?,
|
||||||
ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
|
ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
|
||||||
|
@ -921,6 +923,104 @@ pub struct $0Foo(#[my_custom_attr] u32);
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
pub struct Foo { #[my_custom_attr] field1: u32 }
|
pub struct Foo { #[my_custom_attr] field1: u32 }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_in_macro_pattern_args() {
|
||||||
|
check_assist(
|
||||||
|
convert_tuple_struct_to_named_struct,
|
||||||
|
r#"
|
||||||
|
macro_rules! foo {
|
||||||
|
($expression:expr, $pattern:pat) => {
|
||||||
|
match $expression {
|
||||||
|
$pattern => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
enum Expr {
|
||||||
|
A$0(usize),
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let e = Expr::A(0);
|
||||||
|
foo!(e, Expr::A(0));
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
macro_rules! foo {
|
||||||
|
($expression:expr, $pattern:pat) => {
|
||||||
|
match $expression {
|
||||||
|
$pattern => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
enum Expr {
|
||||||
|
A { field1: usize },
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let e = Expr::A { field1: 0 };
|
||||||
|
foo!(e, Expr::A { field1: 0 });
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_in_multi_file_macro_pattern_args() {
|
||||||
|
check_assist(
|
||||||
|
convert_tuple_struct_to_named_struct,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
enum Test {
|
||||||
|
A$0(i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::Test;
|
||||||
|
|
||||||
|
macro_rules! foo {
|
||||||
|
($expression:expr, $pattern:pat) => {
|
||||||
|
match $expression {
|
||||||
|
$pattern => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
let a = Test::A(0);
|
||||||
|
foo!(a, Test::A(0));
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
enum Test {
|
||||||
|
A { field1: i32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::Test;
|
||||||
|
|
||||||
|
macro_rules! foo {
|
||||||
|
($expression:expr, $pattern:pat) => {
|
||||||
|
match $expression {
|
||||||
|
$pattern => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
let a = Test::A { field1: 0 };
|
||||||
|
foo!(a, Test::A { field1: 0 });
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! Generated by `cargo codegen assists-doc-tests`, do not edit by hand.
|
//! Generated by `cargo xtask codegen assists-doc-tests`, do not edit by hand.
|
||||||
|
|
||||||
use super::check_doc_test;
|
use super::check_doc_test;
|
||||||
|
|
||||||
|
|
|
@ -793,8 +793,8 @@ pub(crate) fn convert_reference_type(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool {
|
fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool {
|
||||||
let ty_ref = hir::Type::reference(ty, hir::Mutability::Shared);
|
let ty_ref = ty.add_reference(hir::Mutability::Shared);
|
||||||
let target_ref = hir::Type::reference(target, hir::Mutability::Shared);
|
let target_ref = target.add_reference(hir::Mutability::Shared);
|
||||||
ty_ref.could_coerce_to(db, &target_ref)
|
ty_ref.could_coerce_to(db, &target_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ pub(crate) fn complete_dot(
|
||||||
acc.add_method(ctx, dot_access, func, None, None)
|
acc.add_method(ctx, dot_access, func, None, None)
|
||||||
});
|
});
|
||||||
|
|
||||||
if ctx.config.enable_auto_iter {
|
if ctx.config.enable_auto_iter && !receiver_ty.strip_references().impls_iterator(ctx.db) {
|
||||||
// FIXME:
|
// FIXME:
|
||||||
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
|
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
|
||||||
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
|
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
|
||||||
|
@ -1499,9 +1499,31 @@ fn main() {
|
||||||
let bar = Bar;
|
let bar = Bar;
|
||||||
bar.$0
|
bar.$0
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_iter_suggestion_on_iterator() {
|
||||||
|
check_no_kw(
|
||||||
|
r#"
|
||||||
|
//- minicore: iterator
|
||||||
|
struct MyIter;
|
||||||
|
impl Iterator for MyIter {
|
||||||
|
type Item = ();
|
||||||
|
fn next(&mut self) -> Option<Self::Item> { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
MyIter.$0
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
me foo() fn(self: Bar)
|
me by_ref() (as Iterator) fn(&mut self) -> &mut Self
|
||||||
|
me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
|
||||||
|
me next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item>
|
||||||
|
me nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,19 +83,19 @@ use crate::{
|
||||||
// NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
|
// NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
|
||||||
// no imports will be proposed.
|
// no imports will be proposed.
|
||||||
//
|
//
|
||||||
// .Fuzzy search details
|
// #### Fuzzy search details
|
||||||
//
|
//
|
||||||
// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
|
// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
|
||||||
// (i.e. in `HashMap` in the `std::collections::HashMap` path).
|
// (i.e. in `HashMap` in the `std::collections::HashMap` path).
|
||||||
// For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
|
// For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
|
||||||
// (but shows all associated items for any input length).
|
// (but shows all associated items for any input length).
|
||||||
//
|
//
|
||||||
// .Import configuration
|
// #### Import configuration
|
||||||
//
|
//
|
||||||
// It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
|
// It is possible to configure how use-trees are merged with the `imports.granularity.group` setting.
|
||||||
// Mimics the corresponding behavior of the `Auto Import` feature.
|
// Mimics the corresponding behavior of the `Auto Import` feature.
|
||||||
//
|
//
|
||||||
// .LSP and performance implications
|
// #### LSP and performance implications
|
||||||
//
|
//
|
||||||
// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
|
// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
|
||||||
// (case-sensitive) resolve client capability in its client capabilities.
|
// (case-sensitive) resolve client capability in its client capabilities.
|
||||||
|
@ -103,7 +103,7 @@ use crate::{
|
||||||
// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
|
// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
|
||||||
// which might be slow ergo the feature is automatically disabled.
|
// which might be slow ergo the feature is automatically disabled.
|
||||||
//
|
//
|
||||||
// .Feature toggle
|
// #### Feature toggle
|
||||||
//
|
//
|
||||||
// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
|
// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
|
||||||
// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
|
// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// ** `logw` -> `log::warn!(...)`
|
// ** `logw` -> `log::warn!(...)`
|
||||||
// ** `loge` -> `log::error!(...)`
|
// ** `loge` -> `log::error!(...)`
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
|
// 
|
||||||
|
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg},
|
syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg},
|
||||||
|
|
|
@ -442,6 +442,8 @@ pub(crate) struct CompletionContext<'a> {
|
||||||
pub(crate) krate: hir::Crate,
|
pub(crate) krate: hir::Crate,
|
||||||
/// The module of the `scope`.
|
/// The module of the `scope`.
|
||||||
pub(crate) module: hir::Module,
|
pub(crate) module: hir::Module,
|
||||||
|
/// The function where we're completing, if inside a function.
|
||||||
|
pub(crate) containing_function: Option<hir::Function>,
|
||||||
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
|
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
|
||||||
pub(crate) is_nightly: bool,
|
pub(crate) is_nightly: bool,
|
||||||
/// The edition of the current crate
|
/// The edition of the current crate
|
||||||
|
@ -760,6 +762,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
|
|
||||||
let krate = scope.krate();
|
let krate = scope.krate();
|
||||||
let module = scope.module();
|
let module = scope.module();
|
||||||
|
let containing_function = scope.containing_function();
|
||||||
let edition = krate.edition(db);
|
let edition = krate.edition(db);
|
||||||
|
|
||||||
let toolchain = db.toolchain_channel(krate.into());
|
let toolchain = db.toolchain_channel(krate.into());
|
||||||
|
@ -874,6 +877,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
token,
|
token,
|
||||||
krate,
|
krate,
|
||||||
module,
|
module,
|
||||||
|
containing_function,
|
||||||
is_nightly,
|
is_nightly,
|
||||||
edition,
|
edition,
|
||||||
expected_name,
|
expected_name,
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub(super) fn expand_and_analyze(
|
||||||
// make the offset point to the start of the original token, as that is what the
|
// make the offset point to the start of the original token, as that is what the
|
||||||
// intermediate offsets calculated in expansion always points to
|
// intermediate offsets calculated in expansion always points to
|
||||||
let offset = offset - relative_offset;
|
let offset = offset - relative_offset;
|
||||||
let expansion = expand(
|
let expansion = expand_maybe_stop(
|
||||||
sema,
|
sema,
|
||||||
original_file.clone(),
|
original_file.clone(),
|
||||||
speculative_file.clone(),
|
speculative_file.clone(),
|
||||||
|
@ -118,6 +118,47 @@ fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Opt
|
||||||
/// that we check, we subtract `COMPLETION_MARKER.len()`. This may not be accurate because proc macros
|
/// that we check, we subtract `COMPLETION_MARKER.len()`. This may not be accurate because proc macros
|
||||||
/// can insert the text of the completion marker in other places while removing the span, but this is
|
/// can insert the text of the completion marker in other places while removing the span, but this is
|
||||||
/// the best we can do.
|
/// the best we can do.
|
||||||
|
fn expand_maybe_stop(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
original_file: SyntaxNode,
|
||||||
|
speculative_file: SyntaxNode,
|
||||||
|
original_offset: TextSize,
|
||||||
|
fake_ident_token: SyntaxToken,
|
||||||
|
relative_offset: TextSize,
|
||||||
|
) -> Option<ExpansionResult> {
|
||||||
|
if let result @ Some(_) = expand(
|
||||||
|
sema,
|
||||||
|
original_file.clone(),
|
||||||
|
speculative_file.clone(),
|
||||||
|
original_offset,
|
||||||
|
fake_ident_token.clone(),
|
||||||
|
relative_offset,
|
||||||
|
) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This needs to come after the recursive call, because our "inside macro" detection is subtly wrong
|
||||||
|
// with regard to attribute macros named `test` that are not std's test. So hopefully we will expand
|
||||||
|
// them successfully above and be able to analyze.
|
||||||
|
// Left biased since there may already be an identifier token there, and we appended to it.
|
||||||
|
if !sema.might_be_inside_macro_call(&fake_ident_token)
|
||||||
|
&& token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset)
|
||||||
|
.is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
|
||||||
|
{
|
||||||
|
// Recursion base case.
|
||||||
|
Some(ExpansionResult {
|
||||||
|
original_file,
|
||||||
|
speculative_file,
|
||||||
|
original_offset,
|
||||||
|
speculative_offset: fake_ident_token.text_range().start(),
|
||||||
|
fake_ident_token,
|
||||||
|
derive_ctx: None,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expand(
|
fn expand(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
original_file: SyntaxNode,
|
original_file: SyntaxNode,
|
||||||
|
@ -128,22 +169,6 @@ fn expand(
|
||||||
) -> Option<ExpansionResult> {
|
) -> Option<ExpansionResult> {
|
||||||
let _p = tracing::info_span!("CompletionContext::expand").entered();
|
let _p = tracing::info_span!("CompletionContext::expand").entered();
|
||||||
|
|
||||||
// Left biased since there may already be an identifier token there, and we appended to it.
|
|
||||||
if !sema.might_be_inside_macro_call(&fake_ident_token)
|
|
||||||
&& token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset)
|
|
||||||
.is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
|
|
||||||
{
|
|
||||||
// Recursion base case.
|
|
||||||
return Some(ExpansionResult {
|
|
||||||
original_file,
|
|
||||||
speculative_file,
|
|
||||||
original_offset,
|
|
||||||
speculative_offset: fake_ident_token.text_range().start(),
|
|
||||||
fake_ident_token,
|
|
||||||
derive_ctx: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let parent_item =
|
let parent_item =
|
||||||
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
|
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
|
||||||
let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset)
|
let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset)
|
||||||
|
@ -197,7 +222,7 @@ fn expand(
|
||||||
// stop here to prevent problems from happening
|
// stop here to prevent problems from happening
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let result = expand(
|
let result = expand_maybe_stop(
|
||||||
sema,
|
sema,
|
||||||
actual_expansion.clone(),
|
actual_expansion.clone(),
|
||||||
fake_expansion.clone(),
|
fake_expansion.clone(),
|
||||||
|
@ -317,7 +342,7 @@ fn expand(
|
||||||
// stop here to prevent problems from happening
|
// stop here to prevent problems from happening
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let result = expand(
|
let result = expand_maybe_stop(
|
||||||
sema,
|
sema,
|
||||||
actual_expansion.clone(),
|
actual_expansion.clone(),
|
||||||
fake_expansion.clone(),
|
fake_expansion.clone(),
|
||||||
|
@ -386,7 +411,7 @@ fn expand(
|
||||||
// stop here to prevent problems from happening
|
// stop here to prevent problems from happening
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let result = expand(
|
let result = expand_maybe_stop(
|
||||||
sema,
|
sema,
|
||||||
actual_expansion.clone(),
|
actual_expansion.clone(),
|
||||||
fake_expansion.clone(),
|
fake_expansion.clone(),
|
||||||
|
|
|
@ -106,11 +106,13 @@ impl CompletionFieldsToResolve {
|
||||||
//
|
//
|
||||||
// There also snippet completions:
|
// There also snippet completions:
|
||||||
//
|
//
|
||||||
// .Expressions
|
// #### Expressions
|
||||||
|
//
|
||||||
// - `pd` -> `eprintln!(" = {:?}", );`
|
// - `pd` -> `eprintln!(" = {:?}", );`
|
||||||
// - `ppd` -> `eprintln!(" = {:#?}", );`
|
// - `ppd` -> `eprintln!(" = {:#?}", );`
|
||||||
//
|
//
|
||||||
// .Items
|
// #### Items
|
||||||
|
//
|
||||||
// - `tfn` -> `#[test] fn feature(){}`
|
// - `tfn` -> `#[test] fn feature(){}`
|
||||||
// - `tmod` ->
|
// - `tmod` ->
|
||||||
// ```rust
|
// ```rust
|
||||||
|
@ -127,7 +129,7 @@ impl CompletionFieldsToResolve {
|
||||||
// Those are the additional completion options with automatic `use` import and options from all project importable items,
|
// Those are the additional completion options with automatic `use` import and options from all project importable items,
|
||||||
// fuzzy matched against the completion input.
|
// fuzzy matched against the completion input.
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[]
|
// 
|
||||||
|
|
||||||
/// Main entry point for completion. We run completion as a two-phase process.
|
/// Main entry point for completion. We run completion as a two-phase process.
|
||||||
///
|
///
|
||||||
|
|
|
@ -144,7 +144,7 @@ fn render(
|
||||||
let detail = if ctx.completion.config.full_function_signatures {
|
let detail = if ctx.completion.config.full_function_signatures {
|
||||||
detail_full(db, func, ctx.completion.edition)
|
detail_full(db, func, ctx.completion.edition)
|
||||||
} else {
|
} else {
|
||||||
detail(db, func, ctx.completion.edition)
|
detail(ctx.completion, func, ctx.completion.edition)
|
||||||
};
|
};
|
||||||
item.set_documentation(ctx.docs(func))
|
item.set_documentation(ctx.docs(func))
|
||||||
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
|
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
|
||||||
|
@ -307,26 +307,26 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detail(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
|
fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> String {
|
||||||
let mut ret_ty = func.ret_type(db);
|
let mut ret_ty = func.ret_type(ctx.db);
|
||||||
let mut detail = String::new();
|
let mut detail = String::new();
|
||||||
|
|
||||||
if func.is_const(db) {
|
if func.is_const(ctx.db) {
|
||||||
format_to!(detail, "const ");
|
format_to!(detail, "const ");
|
||||||
}
|
}
|
||||||
if func.is_async(db) {
|
if func.is_async(ctx.db) {
|
||||||
format_to!(detail, "async ");
|
format_to!(detail, "async ");
|
||||||
if let Some(async_ret) = func.async_ret_type(db) {
|
if let Some(async_ret) = func.async_ret_type(ctx.db) {
|
||||||
ret_ty = async_ret;
|
ret_ty = async_ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if func.is_unsafe_to_call(db) {
|
if func.is_unsafe_to_call(ctx.db, ctx.containing_function, ctx.edition) {
|
||||||
format_to!(detail, "unsafe ");
|
format_to!(detail, "unsafe ");
|
||||||
}
|
}
|
||||||
|
|
||||||
format_to!(detail, "fn({})", params_display(db, func, edition));
|
format_to!(detail, "fn({})", params_display(ctx.db, func, edition));
|
||||||
if !ret_ty.is_unit() {
|
if !ret_ty.is_unit() {
|
||||||
format_to!(detail, " -> {}", ret_ty.display(db, edition));
|
format_to!(detail, " -> {}", ret_ty.display(ctx.db, edition));
|
||||||
}
|
}
|
||||||
detail
|
detail
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
//
|
//
|
||||||
// A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively.
|
// A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively.
|
||||||
//
|
//
|
||||||
// [source,json]
|
// ```json
|
||||||
// ----
|
|
||||||
// {
|
// {
|
||||||
// "rust-analyzer.completion.snippets.custom": {
|
// "rust-analyzer.completion.snippets.custom": {
|
||||||
// "thread spawn": {
|
// "thread spawn": {
|
||||||
|
@ -25,7 +24,7 @@
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ----
|
// ```
|
||||||
//
|
//
|
||||||
// In the example above:
|
// In the example above:
|
||||||
//
|
//
|
||||||
|
@ -39,6 +38,7 @@
|
||||||
// * `description` is an optional description of the snippet, if unset the snippet name will be used.
|
// * `description` is an optional description of the snippet, if unset the snippet name will be used.
|
||||||
//
|
//
|
||||||
// * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered.
|
// * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered.
|
||||||
|
|
||||||
// On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if
|
// On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if
|
||||||
// the items aren't yet in scope.
|
// the items aren't yet in scope.
|
||||||
//
|
//
|
||||||
|
@ -55,8 +55,8 @@
|
||||||
//
|
//
|
||||||
// For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed
|
// For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed
|
||||||
// by overwriting the settings object mentioned above, the defaults are:
|
// by overwriting the settings object mentioned above, the defaults are:
|
||||||
// [source,json]
|
//
|
||||||
// ----
|
// ```json
|
||||||
// {
|
// {
|
||||||
// "Arc::new": {
|
// "Arc::new": {
|
||||||
// "postfix": "arc",
|
// "postfix": "arc",
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
// "scope": "expr"
|
// "scope": "expr"
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ----
|
// ````
|
||||||
|
|
||||||
use hir::{ModPath, Name, Symbol};
|
use hir::{ModPath, Name, Symbol};
|
||||||
use ide_db::imports::import_assets::LocatedImport;
|
use ide_db::imports::import_assets::LocatedImport;
|
||||||
|
|
|
@ -1986,3 +1986,53 @@ fn foo() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_std_test_attr_macro() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- proc_macros: identity
|
||||||
|
use proc_macros::identity as test;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn foo() {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo() fn()
|
||||||
|
md proc_macros
|
||||||
|
bt u32 u32
|
||||||
|
kw async
|
||||||
|
kw const
|
||||||
|
kw crate::
|
||||||
|
kw enum
|
||||||
|
kw extern
|
||||||
|
kw false
|
||||||
|
kw fn
|
||||||
|
kw for
|
||||||
|
kw if
|
||||||
|
kw if let
|
||||||
|
kw impl
|
||||||
|
kw let
|
||||||
|
kw loop
|
||||||
|
kw match
|
||||||
|
kw mod
|
||||||
|
kw return
|
||||||
|
kw self::
|
||||||
|
kw static
|
||||||
|
kw struct
|
||||||
|
kw trait
|
||||||
|
kw true
|
||||||
|
kw type
|
||||||
|
kw union
|
||||||
|
kw unsafe
|
||||||
|
kw use
|
||||||
|
kw while
|
||||||
|
kw while let
|
||||||
|
sn macro_rules
|
||||||
|
sn pd
|
||||||
|
sn ppd
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -44,12 +44,11 @@ impl RootDatabase {
|
||||||
//
|
//
|
||||||
// Clears rust-analyzer's internal database and prints memory usage statistics.
|
// Clears rust-analyzer's internal database and prints memory usage statistics.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Action Name |
|
||||||
// | Editor | Action Name
|
// |---------|-------------|
|
||||||
//
|
|
||||||
// | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
|
// | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
|
||||||
// |===
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
|
// 
|
||||||
pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> {
|
pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> {
|
||||||
let mut acc: Vec<(String, Bytes, usize)> = vec![];
|
let mut acc: Vec<(String, Bytes, usize)> = vec![];
|
||||||
|
|
||||||
|
|
|
@ -986,6 +986,7 @@ impl From<GenericDef> for Definition {
|
||||||
GenericDef::TypeAlias(it) => it.into(),
|
GenericDef::TypeAlias(it) => it.into(),
|
||||||
GenericDef::Impl(it) => it.into(),
|
GenericDef::Impl(it) => it.into(),
|
||||||
GenericDef::Const(it) => it.into(),
|
GenericDef::Const(it) => it.into(),
|
||||||
|
GenericDef::Static(it) => it.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,13 @@ mod topologic_sort;
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use hir::db::DefDatabase;
|
use hir::{db::DefDatabase, Symbol};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base_db::{
|
base_db::{
|
||||||
ra_salsa::{Database, ParallelDatabase, Snapshot},
|
ra_salsa::{Database, ParallelDatabase, Snapshot},
|
||||||
Cancelled, CrateId, SourceDatabase, SourceRootDatabase,
|
Cancelled, CrateId, SourceDatabase,
|
||||||
},
|
},
|
||||||
symbol_index::SymbolsDatabase,
|
symbol_index::SymbolsDatabase,
|
||||||
FxIndexMap, RootDatabase,
|
FxIndexMap, RootDatabase,
|
||||||
|
@ -21,11 +22,12 @@ use crate::{
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ParallelPrimeCachesProgress {
|
pub struct ParallelPrimeCachesProgress {
|
||||||
/// the crates that we are currently priming.
|
/// the crates that we are currently priming.
|
||||||
pub crates_currently_indexing: Vec<String>,
|
pub crates_currently_indexing: Vec<Symbol>,
|
||||||
/// the total number of crates we want to prime.
|
/// the total number of crates we want to prime.
|
||||||
pub crates_total: usize,
|
pub crates_total: usize,
|
||||||
/// the total number of crates that have finished priming
|
/// the total number of crates that have finished priming
|
||||||
pub crates_done: usize,
|
pub crates_done: usize,
|
||||||
|
pub work_type: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parallel_prime_caches(
|
pub fn parallel_prime_caches(
|
||||||
|
@ -47,41 +49,32 @@ pub fn parallel_prime_caches(
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ParallelPrimeCacheWorkerProgress {
|
enum ParallelPrimeCacheWorkerProgress {
|
||||||
BeginCrate { crate_id: CrateId, crate_name: String },
|
BeginCrate { crate_id: CrateId, crate_name: Symbol },
|
||||||
EndCrate { crate_id: CrateId },
|
EndCrate { crate_id: CrateId },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We split off def map computation from other work,
|
||||||
|
// as the def map is the relevant one. Once the defmaps are computed
|
||||||
|
// the project is ready to go, the other indices are just nice to have for some IDE features.
|
||||||
|
#[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone)]
|
||||||
|
enum PrimingPhase {
|
||||||
|
DefMap,
|
||||||
|
ImportMap,
|
||||||
|
CrateSymbols,
|
||||||
|
}
|
||||||
|
|
||||||
let (work_sender, progress_receiver) = {
|
let (work_sender, progress_receiver) = {
|
||||||
let (progress_sender, progress_receiver) = crossbeam_channel::unbounded();
|
let (progress_sender, progress_receiver) = crossbeam_channel::unbounded();
|
||||||
let (work_sender, work_receiver) = crossbeam_channel::unbounded();
|
let (work_sender, work_receiver) = crossbeam_channel::unbounded();
|
||||||
let graph = graph.clone();
|
|
||||||
let local_roots = db.local_roots();
|
|
||||||
let prime_caches_worker = move |db: Snapshot<RootDatabase>| {
|
let prime_caches_worker = move |db: Snapshot<RootDatabase>| {
|
||||||
while let Ok((crate_id, crate_name)) = work_receiver.recv() {
|
while let Ok((crate_id, crate_name, kind)) = work_receiver.recv() {
|
||||||
progress_sender
|
progress_sender
|
||||||
.send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?;
|
.send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?;
|
||||||
|
|
||||||
// Compute the DefMap and possibly ImportMap
|
match kind {
|
||||||
let file_id = graph[crate_id].root_file_id;
|
PrimingPhase::DefMap => _ = db.crate_def_map(crate_id),
|
||||||
let root_id = db.file_source_root(file_id);
|
PrimingPhase::ImportMap => _ = db.import_map(crate_id),
|
||||||
if db.source_root(root_id).is_library {
|
PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()),
|
||||||
db.crate_def_map(crate_id);
|
|
||||||
} else {
|
|
||||||
// This also computes the DefMap
|
|
||||||
db.import_map(crate_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the symbol search index.
|
|
||||||
// This primes the cache for `ide_db::symbol_index::world_symbols()`.
|
|
||||||
//
|
|
||||||
// We do this for workspace crates only (members of local_roots), because doing it
|
|
||||||
// for all dependencies could be *very* unnecessarily slow in a large project.
|
|
||||||
//
|
|
||||||
// FIXME: We should do it unconditionally if the configuration is set to default to
|
|
||||||
// searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we
|
|
||||||
// would need to pipe that configuration information down here.
|
|
||||||
if local_roots.contains(&root_id) {
|
|
||||||
db.crate_symbols(crate_id.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?;
|
progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?;
|
||||||
|
@ -112,16 +105,34 @@ pub fn parallel_prime_caches(
|
||||||
let mut crates_currently_indexing =
|
let mut crates_currently_indexing =
|
||||||
FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default());
|
FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default());
|
||||||
|
|
||||||
|
let mut additional_phases = vec![];
|
||||||
|
|
||||||
while crates_done < crates_total {
|
while crates_done < crates_total {
|
||||||
db.unwind_if_cancelled();
|
db.unwind_if_cancelled();
|
||||||
|
|
||||||
for crate_id in &mut crates_to_prime {
|
for crate_id in &mut crates_to_prime {
|
||||||
work_sender
|
let krate = &graph[crate_id];
|
||||||
.send((
|
let name = krate
|
||||||
crate_id,
|
.display_name
|
||||||
graph[crate_id].display_name.as_deref().unwrap_or_default().to_owned(),
|
.as_deref()
|
||||||
))
|
.cloned()
|
||||||
.ok();
|
.unwrap_or_else(|| Symbol::integer(crate_id.into_raw().into_u32() as usize));
|
||||||
|
if krate.origin.is_lang() {
|
||||||
|
additional_phases.push((crate_id, name.clone(), PrimingPhase::ImportMap));
|
||||||
|
} else if krate.origin.is_local() {
|
||||||
|
// Compute the symbol search index.
|
||||||
|
// This primes the cache for `ide_db::symbol_index::world_symbols()`.
|
||||||
|
//
|
||||||
|
// We do this for workspace crates only (members of local_roots), because doing it
|
||||||
|
// for all dependencies could be *very* unnecessarily slow in a large project.
|
||||||
|
//
|
||||||
|
// FIXME: We should do it unconditionally if the configuration is set to default to
|
||||||
|
// searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we
|
||||||
|
// would need to pipe that configuration information down here.
|
||||||
|
additional_phases.push((crate_id, name.clone(), PrimingPhase::CrateSymbols));
|
||||||
|
}
|
||||||
|
|
||||||
|
work_sender.send((crate_id, name, PrimingPhase::DefMap)).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision
|
// recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision
|
||||||
|
@ -153,6 +164,50 @@ pub fn parallel_prime_caches(
|
||||||
crates_currently_indexing: crates_currently_indexing.values().cloned().collect(),
|
crates_currently_indexing: crates_currently_indexing.values().cloned().collect(),
|
||||||
crates_done,
|
crates_done,
|
||||||
crates_total,
|
crates_total,
|
||||||
|
work_type: "Indexing",
|
||||||
|
};
|
||||||
|
|
||||||
|
cb(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut crates_done = 0;
|
||||||
|
let crates_total = additional_phases.len();
|
||||||
|
for w in additional_phases.into_iter().sorted_by_key(|&(_, _, phase)| phase) {
|
||||||
|
work_sender.send(w).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
while crates_done < crates_total {
|
||||||
|
db.unwind_if_cancelled();
|
||||||
|
|
||||||
|
// recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision
|
||||||
|
// is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or
|
||||||
|
// if this thread exits, and closes the work channel.
|
||||||
|
let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(crossbeam_channel::RecvTimeoutError::Timeout) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
|
||||||
|
// our workers may have died from a cancelled task, so we'll check and re-raise here.
|
||||||
|
db.unwind_if_cancelled();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match worker_progress {
|
||||||
|
ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => {
|
||||||
|
crates_currently_indexing.insert(crate_id, crate_name);
|
||||||
|
}
|
||||||
|
ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => {
|
||||||
|
crates_currently_indexing.swap_remove(&crate_id);
|
||||||
|
crates_done += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let progress = ParallelPrimeCachesProgress {
|
||||||
|
crates_currently_indexing: crates_currently_indexing.values().cloned().collect(),
|
||||||
|
crates_done,
|
||||||
|
crates_total,
|
||||||
|
work_type: "Populating symbols",
|
||||||
};
|
};
|
||||||
|
|
||||||
cb(progress);
|
cb(progress);
|
||||||
|
|
|
@ -354,6 +354,7 @@ impl Definition {
|
||||||
hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
|
hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
|
||||||
hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
|
hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
|
||||||
hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
|
hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
|
||||||
|
hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()),
|
||||||
};
|
};
|
||||||
return match def {
|
return match def {
|
||||||
Some(def) => SearchScope::file_range(
|
Some(def) => SearchScope::file_range(
|
||||||
|
|
|
@ -193,11 +193,9 @@ impl<DB> std::ops::Deref for Snap<DB> {
|
||||||
// `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed
|
// `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed
|
||||||
// with `__` are hidden from the search results unless configured otherwise.
|
// with `__` are hidden from the search results unless configured otherwise.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Shortcut |
|
||||||
// | Editor | Shortcut
|
// |---------|-----------|
|
||||||
//
|
// | VS Code | <kbd>Ctrl+T</kbd>
|
||||||
// | VS Code | kbd:[Ctrl+T]
|
|
||||||
// |===
|
|
||||||
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
|
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
|
||||||
let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
|
let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
|
|
||||||
// Diagnostic: incorrect-ident-case
|
// Diagnostic: incorrect-ident-case
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
|
// This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html).
|
||||||
pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
|
pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
|
||||||
let code = match d.expected_case {
|
let code = match d.expected_case {
|
||||||
CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"),
|
CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"),
|
||||||
|
@ -936,6 +936,7 @@ fn func() {
|
||||||
fn override_lint_level() {
|
fn override_lint_level() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
|
#![allow(unused_variables)]
|
||||||
#[warn(nonstandard_style)]
|
#[warn(nonstandard_style)]
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let BAR;
|
let BAR;
|
||||||
|
@ -992,6 +993,7 @@ struct QUX;
|
||||||
const foo: i32 = 0;
|
const foo: i32 = 0;
|
||||||
fn BAR() {
|
fn BAR() {
|
||||||
let BAZ;
|
let BAZ;
|
||||||
|
_ = BAZ;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1125,6 +1125,41 @@ fn main() {
|
||||||
let lparam: isize = 0;
|
let lparam: isize = 0;
|
||||||
|
|
||||||
let _wrap = Wrap(unsafe { &mut *(lparam as *mut _) });
|
let _wrap = Wrap(unsafe { &mut *(lparam as *mut _) });
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_18682() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized
|
||||||
|
struct Flexible {
|
||||||
|
body: [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Field {
|
||||||
|
type Type: ?Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for Flexible {
|
||||||
|
type Type = [u8];
|
||||||
|
}
|
||||||
|
|
||||||
|
trait KnownLayout {
|
||||||
|
type MaybeUninit: ?Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> KnownLayout for [T] {
|
||||||
|
type MaybeUninit = [T];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ZerocopyKnownLayoutMaybeUninit(<<Flexible as Field>::Type as KnownLayout>::MaybeUninit);
|
||||||
|
|
||||||
|
fn test(ptr: *mut [u8]) -> *mut ZerocopyKnownLayoutMaybeUninit {
|
||||||
|
ptr as *mut _
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use hir::db::ExpandDatabase;
|
use hir::db::ExpandDatabase;
|
||||||
use hir::{HirFileIdExt, UnsafetyReason};
|
use hir::{HirFileIdExt, UnsafeLint, UnsafetyReason};
|
||||||
use ide_db::text_edit::TextEdit;
|
use ide_db::text_edit::TextEdit;
|
||||||
use ide_db::{assists::Assist, source_change::SourceChange};
|
use ide_db::{assists::Assist, source_change::SourceChange};
|
||||||
use syntax::{ast, SyntaxNode};
|
use syntax::{ast, SyntaxNode};
|
||||||
|
@ -11,10 +11,10 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
|
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
|
||||||
pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
|
pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
|
||||||
let code = if d.only_lint {
|
let code = match d.lint {
|
||||||
DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn")
|
UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"),
|
||||||
} else {
|
UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"),
|
||||||
DiagnosticCode::RustcHardError("E0133")
|
UnsafeLint::DeprecatedSafe2024 => DiagnosticCode::RustcLint("deprecated_safe_2024"),
|
||||||
};
|
};
|
||||||
let operation = display_unsafety_reason(d.reason);
|
let operation = display_unsafety_reason(d.reason);
|
||||||
Diagnostic::new_with_syntax_node_ptr(
|
Diagnostic::new_with_syntax_node_ptr(
|
||||||
|
@ -585,24 +585,58 @@ fn main() {
|
||||||
r#"
|
r#"
|
||||||
//- /ed2021.rs crate:ed2021 edition:2021
|
//- /ed2021.rs crate:ed2021 edition:2021
|
||||||
#[rustc_deprecated_safe_2024]
|
#[rustc_deprecated_safe_2024]
|
||||||
unsafe fn safe() -> u8 {
|
unsafe fn deprecated_safe() -> u8 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /ed2024.rs crate:ed2024 edition:2024
|
//- /ed2024.rs crate:ed2024 edition:2024
|
||||||
#[rustc_deprecated_safe_2024]
|
#[rustc_deprecated_safe_2024]
|
||||||
unsafe fn not_safe() -> u8 {
|
unsafe fn deprecated_safe() -> u8 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
//- /main.rs crate:main deps:ed2021,ed2024
|
|
||||||
|
//- /dep1.rs crate:dep1 deps:ed2021,ed2024 edition:2021
|
||||||
fn main() {
|
fn main() {
|
||||||
ed2021::safe();
|
ed2021::deprecated_safe();
|
||||||
ed2024::not_safe();
|
ed2024::deprecated_safe();
|
||||||
//^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
|
}
|
||||||
|
|
||||||
|
//- /dep2.rs crate:dep2 deps:ed2021,ed2024 edition:2024
|
||||||
|
fn main() {
|
||||||
|
ed2021::deprecated_safe();
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
|
||||||
|
ed2024::deprecated_safe();
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /dep3.rs crate:dep3 deps:ed2021,ed2024 edition:2021
|
||||||
|
#![warn(deprecated_safe)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
ed2021::deprecated_safe();
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block
|
||||||
|
ed2024::deprecated_safe();
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn orphan_unsafe_format_args() {
|
||||||
|
// Checks that we don't place orphan arguments for formatting under an unsafe block.
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: fmt
|
||||||
|
fn foo() {
|
||||||
|
let p = 0xDEADBEEF as *const i32;
|
||||||
|
format_args!("", *p);
|
||||||
|
// ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() {
|
fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
@ -812,4 +846,36 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn target_feature() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
#[target_feature(enable = "avx")]
|
||||||
|
fn foo() {}
|
||||||
|
|
||||||
|
#[target_feature(enable = "avx,avx2")]
|
||||||
|
fn bar() {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn baz() {
|
||||||
|
foo();
|
||||||
|
// ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unsafe_fn_ptr_call() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn f(it: unsafe fn()){
|
||||||
|
it();
|
||||||
|
// ^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -831,13 +831,14 @@ fn f() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn or_pattern() {
|
fn or_pattern() {
|
||||||
// FIXME: `None` is inferred as unknown here for some reason
|
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: option
|
//- minicore: option
|
||||||
fn f(_: i32) {}
|
fn f(_: i32) {}
|
||||||
fn main() {
|
fn main() {
|
||||||
let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return };
|
let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return };
|
||||||
|
//^^^^^ 💡 warn: variable does not need to be mutable
|
||||||
|
|
||||||
f(x);
|
f(x);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
|
use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
famous_defs::FamousDefs,
|
famous_defs::FamousDefs,
|
||||||
source_change::{SourceChange, SourceChangeBuilder},
|
source_change::{SourceChange, SourceChangeBuilder},
|
||||||
|
@ -88,7 +88,7 @@ fn add_reference(
|
||||||
let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into()));
|
let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into()));
|
||||||
|
|
||||||
let (_, mutability) = d.expected.as_reference()?;
|
let (_, mutability) = d.expected.as_reference()?;
|
||||||
let actual_with_ref = Type::reference(&d.actual, mutability);
|
let actual_with_ref = d.actual.add_reference(mutability);
|
||||||
if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
|
if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,10 @@
|
||||||
//
|
//
|
||||||
// Supported constraints:
|
// Supported constraints:
|
||||||
//
|
//
|
||||||
// |===
|
// | Constraint | Restricts placeholder |
|
||||||
// | Constraint | Restricts placeholder
|
// |---------------|------------------------|
|
||||||
//
|
// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) |
|
||||||
// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
|
// | not(a) | Negates the constraint `a` |
|
||||||
// | not(a) | Negates the constraint `a`
|
|
||||||
// |===
|
|
||||||
//
|
//
|
||||||
// Available via the command `rust-analyzer.ssr`.
|
// Available via the command `rust-analyzer.ssr`.
|
||||||
//
|
//
|
||||||
|
@ -52,11 +50,9 @@
|
||||||
// String::from((y + 5).foo(z))
|
// String::from((y + 5).foo(z))
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Action Name |
|
||||||
// | Editor | Action Name
|
// |---------|--------------|
|
||||||
//
|
// | VS Code | **rust-analyzer: Structural Search Replace** |
|
||||||
// | VS Code | **rust-analyzer: Structural Search Replace**
|
|
||||||
// |===
|
|
||||||
//
|
//
|
||||||
// Also available as an assist, by writing a comment containing the structural
|
// Also available as an assist, by writing a comment containing the structural
|
||||||
// search and replace rule. You will only see the assist if the comment can
|
// search and replace rule. You will only see the assist if the comment can
|
||||||
|
|
|
@ -21,7 +21,7 @@ mod fn_references;
|
||||||
// Provides user with annotations above items for looking up references or impl blocks
|
// Provides user with annotations above items for looking up references or impl blocks
|
||||||
// and running/debugging binaries.
|
// and running/debugging binaries.
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[]
|
// 
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct Annotation {
|
pub struct Annotation {
|
||||||
pub range: TextRange,
|
pub range: TextRange,
|
||||||
|
|
|
@ -122,11 +122,9 @@ pub(crate) fn remove_links(markdown: &str) -> String {
|
||||||
// The simplest way to use this feature is via the context menu. Right-click on
|
// The simplest way to use this feature is via the context menu. Right-click on
|
||||||
// the selected item. The context menu opens. Select **Open Docs**.
|
// the selected item. The context menu opens. Select **Open Docs**.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Action Name |
|
||||||
// | Editor | Action Name
|
// |---------|-------------|
|
||||||
//
|
// | VS Code | **rust-analyzer: Open Docs** |
|
||||||
// | VS Code | **rust-analyzer: Open Docs**
|
|
||||||
// |===
|
|
||||||
pub(crate) fn external_docs(
|
pub(crate) fn external_docs(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
|
|
|
@ -19,13 +19,11 @@ pub struct ExpandedMacro {
|
||||||
//
|
//
|
||||||
// Shows the full macro expansion of the macro at the current caret position.
|
// Shows the full macro expansion of the macro at the current caret position.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Action Name |
|
||||||
// | Editor | Action Name
|
// |---------|-------------|
|
||||||
|
// | VS Code | **rust-analyzer: Expand macro recursively at caret** |
|
||||||
//
|
//
|
||||||
// | VS Code | **rust-analyzer: Expand macro recursively at caret**
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
|
|
||||||
pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
|
pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let file = sema.parse_guess_edition(position.file_id);
|
let file = sema.parse_guess_edition(position.file_id);
|
||||||
|
|
|
@ -17,13 +17,11 @@ use crate::FileRange;
|
||||||
// Extends or shrinks the current selection to the encompassing syntactic construct
|
// Extends or shrinks the current selection to the encompassing syntactic construct
|
||||||
// (expression, statement, item, module, etc). It works with multiple cursors.
|
// (expression, statement, item, module, etc). It works with multiple cursors.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Shortcut |
|
||||||
// | Editor | Shortcut
|
// |---------|----------|
|
||||||
|
// | VS Code | <kbd>Alt+Shift+→</kbd>, <kbd>Alt+Shift+←</kbd> |
|
||||||
//
|
//
|
||||||
// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←]
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[]
|
|
||||||
pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
|
pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let src = sema.parse_guess_edition(frange.file_id);
|
let src = sema.parse_guess_edition(frange.file_id);
|
||||||
|
|
|
@ -14,13 +14,11 @@ pub struct CrateInfo {
|
||||||
//
|
//
|
||||||
// Shows a view tree with all the dependencies of this project
|
// Shows a view tree with all the dependencies of this project
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Panel Name |
|
||||||
// | Editor | Panel Name
|
// |---------|------------|
|
||||||
|
// | VS Code | **Rust Dependencies** |
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Dependencies**
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[]
|
|
||||||
pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
|
pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
|
||||||
let crate_graph = db.crate_graph();
|
let crate_graph = db.crate_graph();
|
||||||
crate_graph
|
crate_graph
|
||||||
|
|
|
@ -31,14 +31,11 @@ pub enum StructureNodeKind {
|
||||||
// * draw breadcrumbs to describe the context around the cursor
|
// * draw breadcrumbs to describe the context around the cursor
|
||||||
// * draw outline of the file
|
// * draw outline of the file
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Shortcut |
|
||||||
// | Editor | Shortcut
|
// |---------|----------|
|
||||||
|
// | VS Code | <kbd>Ctrl+Shift+O</kbd> |
|
||||||
//
|
//
|
||||||
// | VS Code | kbd:[Ctrl+Shift+O]
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[]
|
|
||||||
|
|
||||||
pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
|
pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
|
|
|
@ -31,13 +31,11 @@ use syntax::{
|
||||||
//
|
//
|
||||||
// For outline modules, this will navigate to the source file of the module.
|
// For outline modules, this will navigate to the source file of the module.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Shortcut |
|
||||||
// | Editor | Shortcut
|
// |---------|----------|
|
||||||
|
// | VS Code | <kbd>F12</kbd> |
|
||||||
//
|
//
|
||||||
// | VS Code | kbd:[F12]
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
|
|
||||||
pub(crate) fn goto_definition(
|
pub(crate) fn goto_definition(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
|
@ -3270,6 +3268,24 @@ impl core::fmt::Display for A {
|
||||||
}
|
}
|
||||||
fn f() {
|
fn f() {
|
||||||
A.to_string$0();
|
A.to_string$0();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn use_inside_body() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
mod nice_module {
|
||||||
|
pub(super) struct NiceStruct;
|
||||||
|
// ^^^^^^^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
use nice_module::NiceStruct$0;
|
||||||
|
|
||||||
|
let _ = NiceStruct;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,13 +12,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
||||||
//
|
//
|
||||||
// Navigates to the impl items of types.
|
// Navigates to the impl items of types.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Shortcut |
|
||||||
// | Editor | Shortcut
|
// |---------|----------|
|
||||||
|
// | VS Code | <kbd>Ctrl+F12</kbd>
|
||||||
//
|
//
|
||||||
// | VS Code | kbd:[Ctrl+F12]
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
|
|
||||||
pub(crate) fn goto_implementation(
|
pub(crate) fn goto_implementation(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
|
|
|
@ -8,13 +8,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
||||||
//
|
//
|
||||||
// Navigates to the type of an identifier.
|
// Navigates to the type of an identifier.
|
||||||
//
|
//
|
||||||
// |===
|
// | Editor | Action Name |
|
||||||
// | Editor | Action Name
|
// |---------|-------------|
|
||||||
|
// | VS Code | **Go to Type Definition** |
|
||||||
//
|
//
|
||||||
// | VS Code | **Go to Type Definition**
|
// 
|
||||||
// |===
|
|
||||||
//
|
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
|
|
||||||
pub(crate) fn goto_type_definition(
|
pub(crate) fn goto_type_definition(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
|
|
|
@ -43,12 +43,12 @@ pub struct HighlightRelatedConfig {
|
||||||
//
|
//
|
||||||
// Highlights constructs related to the thing under the cursor:
|
// Highlights constructs related to the thing under the cursor:
|
||||||
//
|
//
|
||||||
// . if on an identifier, highlights all references to that identifier in the current file
|
// 1. if on an identifier, highlights all references to that identifier in the current file
|
||||||
// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
|
// * additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
|
||||||
// . if on an `async` or `await` token, highlights all yield points for that async context
|
// 1. if on an `async` or `await` token, highlights all yield points for that async context
|
||||||
// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
|
// 1. if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
|
||||||
// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
|
// 1. if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
|
||||||
// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
|
// 1. if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
|
||||||
//
|
//
|
||||||
// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
|
// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
|
||||||
pub(crate) fn highlight_related(
|
pub(crate) fn highlight_related(
|
||||||
|
|
|
@ -118,7 +118,7 @@ pub struct HoverResult {
|
||||||
// Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
|
// Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
|
||||||
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
|
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
|
// 
|
||||||
pub(crate) fn hover(
|
pub(crate) fn hover(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
frange @ FileRange { file_id, range }: FileRange,
|
frange @ FileRange { file_id, range }: FileRange,
|
||||||
|
|
|
@ -434,6 +434,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) -
|
||||||
None => it.name(db),
|
None => it.name(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hir::GenericDef::Static(it) => Some(it.name(db)),
|
||||||
},
|
},
|
||||||
Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
|
Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
|
||||||
d => {
|
d => {
|
||||||
|
|
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