mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-14 00:47:18 +00:00
Merge commit 'f5a9250147f6569d8d89334dc9cca79c0322729f' into sync-from-ra
This commit is contained in:
parent
5a95a53a39
commit
d3fb9f798a
223 changed files with 7964 additions and 5865 deletions
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
|
@ -71,7 +71,7 @@ jobs:
|
|||
run: echo "::add-matcher::.github/rust.json"
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
|
||||
uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012
|
||||
with:
|
||||
key: ${{ env.RUST_CHANNEL }}
|
||||
|
||||
|
@ -140,7 +140,7 @@ jobs:
|
|||
rustup target add ${{ env.targets }} ${{ env.targets_ide }}
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
|
||||
uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012
|
||||
|
||||
- name: Check
|
||||
run: |
|
||||
|
|
1
.github/workflows/publish-libs.yaml
vendored
1
.github/workflows/publish-libs.yaml
vendored
|
@ -32,4 +32,5 @@ jobs:
|
|||
git config --global user.name "GitHub Action"
|
||||
# Remove r-a crates from the workspaces so we don't auto-publish them as well
|
||||
sed -i 's/ "crates\/\*"//' ./Cargo.toml
|
||||
sed -i 's/ "xtask\/"//' ./Cargo.toml
|
||||
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty
|
||||
|
|
17
.github/workflows/release.yaml
vendored
17
.github/workflows/release.yaml
vendored
|
@ -36,6 +36,7 @@ jobs:
|
|||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
code-target: linux-x64
|
||||
container: rockylinux:8
|
||||
- os: ubuntu-20.04
|
||||
target: aarch64-unknown-linux-gnu
|
||||
code-target: linux-arm64
|
||||
|
@ -58,10 +59,18 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: ${{ env.FETCH_DEPTH }}
|
||||
|
||||
- name: Install toolchain dependencies
|
||||
if: matrix.container == 'rockylinux:8'
|
||||
shell: bash
|
||||
run: |
|
||||
dnf install -y gcc
|
||||
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
|
||||
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
|
@ -69,9 +78,9 @@ jobs:
|
|||
rustup component add rust-src
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Update apt repositories
|
||||
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf'
|
||||
|
@ -181,7 +190,7 @@ jobs:
|
|||
- name: Install Nodejs
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
|
||||
if: github.ref == 'refs/heads/release'
|
||||
|
|
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -594,6 +594,7 @@ dependencies = [
|
|||
"rustc-hash",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"test-fixture",
|
||||
|
@ -637,6 +638,7 @@ dependencies = [
|
|||
"pulldown-cmark",
|
||||
"pulldown-cmark-to-cmark",
|
||||
"smallvec",
|
||||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
"test-fixture",
|
||||
|
@ -732,6 +734,7 @@ dependencies = [
|
|||
"ide-db",
|
||||
"itertools",
|
||||
"once_cell",
|
||||
"paths",
|
||||
"serde_json",
|
||||
"stdx",
|
||||
"syntax",
|
||||
|
@ -931,6 +934,7 @@ dependencies = [
|
|||
"hir-expand",
|
||||
"ide-db",
|
||||
"itertools",
|
||||
"paths",
|
||||
"proc-macro-api",
|
||||
"project-model",
|
||||
"span",
|
||||
|
@ -1225,6 +1229,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
|||
[[package]]
|
||||
name = "paths"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"camino",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
|
@ -1375,6 +1382,7 @@ dependencies = [
|
|||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"span",
|
||||
"stdx",
|
||||
"toolchain",
|
||||
"tracing",
|
||||
|
@ -1432,9 +1440,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_abi"
|
||||
version = "0.42.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2ae52e2d5b08762c9464b541345f519b8719d57b643b73632bade43ecece9dc"
|
||||
checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"ra-ap-rustc_index",
|
||||
|
@ -1443,9 +1451,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index"
|
||||
version = "0.42.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd7e10c7853fe79443d46e1d2d8ab09fe99926118e59653fb8b480d5045f126"
|
||||
checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"ra-ap-rustc_index_macros",
|
||||
|
@ -1454,9 +1462,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index_macros"
|
||||
version = "0.42.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47f1d1c589be6c9a9e852fadee0e60329c0f862e87442ac2fe5adae30663cc76"
|
||||
checksum = "8782aaf3a113837c533dfb1c45df91cd17e1fdd1d2f9a20c2e0d1976025c4f1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1466,9 +1474,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_lexer"
|
||||
version = "0.42.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa852373a757b4c723bbdc96ced7f575cad68a1e266e45fee12bc4c69a482d80"
|
||||
checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3"
|
||||
dependencies = [
|
||||
"unicode-properties",
|
||||
"unicode-xid",
|
||||
|
@ -1476,9 +1484,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_parse_format"
|
||||
version = "0.42.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2afe3c49accd95a53ac4d72ae13bafc7d115bdd80c8cd56ab09e6fc68f482210"
|
||||
checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"ra-ap-rustc_lexer",
|
||||
|
@ -1486,9 +1494,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_pattern_analysis"
|
||||
version = "0.42.0"
|
||||
version = "0.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1253da23515d80c377a3998731e0ec3794997b62b989fd47db73efbde6a0bd7c"
|
||||
checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"rustc-hash",
|
||||
|
@ -1598,6 +1606,7 @@ dependencies = [
|
|||
"oorandom",
|
||||
"parking_lot",
|
||||
"parser",
|
||||
"paths",
|
||||
"proc-macro-api",
|
||||
"profile",
|
||||
"project-model",
|
||||
|
@ -1869,20 +1878,16 @@ dependencies = [
|
|||
"itertools",
|
||||
"once_cell",
|
||||
"parser",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ra-ap-rustc_lexer",
|
||||
"rayon",
|
||||
"rowan",
|
||||
"rustc-hash",
|
||||
"smol_str",
|
||||
"sourcegen",
|
||||
"stdx",
|
||||
"test-utils",
|
||||
"text-edit",
|
||||
"tracing",
|
||||
"triomphe",
|
||||
"ungrammar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2024,6 +2029,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
name = "toolchain"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"home",
|
||||
]
|
||||
|
||||
|
@ -2109,7 +2115,6 @@ name = "tt"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"smol_str",
|
||||
"span",
|
||||
"stdx",
|
||||
"text-size",
|
||||
]
|
||||
|
@ -2438,8 +2443,12 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"flate2",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"stdx",
|
||||
"time",
|
||||
"ungrammar",
|
||||
"write-json",
|
||||
"xflags",
|
||||
"xshell",
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" }
|
|||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
|
||||
ra-ap-rustc_lexer = { version = "0.42.0", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.42.0", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.42.0", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.42.0", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.42.0", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.44.0", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.44.0", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.44.0", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.44.0", default-features = false }
|
||||
|
||||
# local crates that aren't published to crates.io. These should not have versions.
|
||||
sourcegen = { path = "./crates/sourcegen" }
|
||||
|
@ -105,6 +105,7 @@ anyhow = "1.0.75"
|
|||
arrayvec = "0.7.4"
|
||||
bitflags = "2.4.1"
|
||||
cargo_metadata = "0.18.1"
|
||||
camino = "1.1.6"
|
||||
chalk-solve = { version = "0.96.0", default-features = false }
|
||||
chalk-ir = "0.96.0"
|
||||
chalk-recursive = { version = "0.96.0", default-features = false }
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
|
||||
//! actual IO is done and lowered to input.
|
||||
|
||||
use std::{fmt, mem, ops, str::FromStr};
|
||||
use std::{fmt, mem, ops};
|
||||
|
||||
use cfg::CfgOptions;
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::Edition;
|
||||
use syntax::SmolStr;
|
||||
use triomphe::Arc;
|
||||
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
|
||||
|
@ -293,42 +294,11 @@ pub struct CrateData {
|
|||
pub is_proc_macro: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Edition {
|
||||
Edition2015,
|
||||
Edition2018,
|
||||
Edition2021,
|
||||
Edition2024,
|
||||
}
|
||||
|
||||
impl Edition {
|
||||
pub const CURRENT: Edition = Edition::Edition2021;
|
||||
pub const DEFAULT: Edition = Edition::Edition2015;
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Env {
|
||||
entries: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new_for_test_fixture() -> Self {
|
||||
Env {
|
||||
entries: FxHashMap::from_iter([(
|
||||
String::from("__ra_is_test_fixture"),
|
||||
String::from("__ra_is_test_fixture"),
|
||||
)]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum DependencyKind {
|
||||
Normal,
|
||||
Dev,
|
||||
Build,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Dependency {
|
||||
pub crate_id: CrateId,
|
||||
|
@ -530,13 +500,6 @@ impl CrateGraph {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: this only finds one crate with the given root; we could have multiple
|
||||
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
|
||||
let (crate_id, _) =
|
||||
self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?;
|
||||
Some(crate_id)
|
||||
}
|
||||
|
||||
pub fn sort_deps(&mut self) {
|
||||
self.arena
|
||||
.iter_mut()
|
||||
|
@ -653,6 +616,10 @@ impl CrateGraph {
|
|||
}
|
||||
id_map
|
||||
}
|
||||
|
||||
pub fn shrink_to_fit(&mut self) {
|
||||
self.arena.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<CrateId> for CrateGraph {
|
||||
|
@ -670,32 +637,6 @@ impl CrateData {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Edition {
|
||||
type Err = ParseEditionError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let res = match s {
|
||||
"2015" => Edition::Edition2015,
|
||||
"2018" => Edition::Edition2018,
|
||||
"2021" => Edition::Edition2021,
|
||||
"2024" => Edition::Edition2024,
|
||||
_ => return Err(ParseEditionError { invalid_input: s.to_owned() }),
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Edition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
Edition::Edition2015 => "2015",
|
||||
Edition::Edition2018 => "2018",
|
||||
Edition::Edition2021 => "2021",
|
||||
Edition::Edition2024 => "2024",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<(String, String)> for Env {
|
||||
fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
|
||||
self.entries.extend(iter);
|
||||
|
@ -722,19 +663,6 @@ impl Env {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseEditionError {
|
||||
invalid_input: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseEditionError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "invalid edition: {:?}", self.invalid_input)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseEditionError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CyclicDependenciesError {
|
||||
path: Vec<(CrateId, Option<CrateDisplayName>)>,
|
||||
|
|
|
@ -14,9 +14,9 @@ use triomphe::Arc;
|
|||
pub use crate::{
|
||||
change::FileChange,
|
||||
input::{
|
||||
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
|
||||
DependencyKind, Edition, Env, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot,
|
||||
SourceRootId, TargetLayoutLoadResult,
|
||||
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env,
|
||||
LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId,
|
||||
TargetLayoutLoadResult,
|
||||
},
|
||||
};
|
||||
pub use salsa::{self, Cancelled};
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use std::{fmt, io, path::PathBuf, process::Command, time::Duration};
|
||||
use std::{fmt, io, process::Command, time::Duration};
|
||||
|
||||
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
||||
use paths::{AbsPath, AbsPathBuf};
|
||||
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -53,7 +53,7 @@ pub enum FlycheckConfig {
|
|||
extra_args: Vec<String>,
|
||||
extra_env: FxHashMap<String, String>,
|
||||
ansi_color_output: bool,
|
||||
target_dir: Option<PathBuf>,
|
||||
target_dir: Option<Utf8PathBuf>,
|
||||
},
|
||||
CustomCommand {
|
||||
command: String,
|
||||
|
@ -363,7 +363,7 @@ impl FlycheckActor {
|
|||
});
|
||||
|
||||
cmd.arg("--manifest-path");
|
||||
cmd.arg(self.root.join("Cargo.toml").as_os_str());
|
||||
cmd.arg(self.root.join("Cargo.toml"));
|
||||
|
||||
for target in target_triples {
|
||||
cmd.args(["--target", target.as_str()]);
|
||||
|
|
|
@ -55,13 +55,16 @@ pub struct CargoTestHandle {
|
|||
}
|
||||
|
||||
// Example of a cargo test command:
|
||||
// cargo test -- module::func -Z unstable-options --format=json
|
||||
// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
|
||||
|
||||
impl CargoTestHandle {
|
||||
pub fn new(path: Option<&str>) -> std::io::Result<Self> {
|
||||
let mut cmd = Command::new(Tool::Cargo.path());
|
||||
cmd.env("RUSTC_BOOTSTRAP", "1");
|
||||
cmd.arg("test");
|
||||
cmd.arg("--workspace");
|
||||
// --no-fail-fast is needed to ensure that all requested tests will run
|
||||
cmd.arg("--no-fail-fast");
|
||||
cmd.arg("--");
|
||||
if let Some(path) = path {
|
||||
cmd.arg(path);
|
||||
|
|
|
@ -148,12 +148,12 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lang(&self) -> Option<&SmolStr> {
|
||||
pub fn lang(&self) -> Option<&str> {
|
||||
self.by_key("lang").string_value()
|
||||
}
|
||||
|
||||
pub fn lang_item(&self) -> Option<LangItem> {
|
||||
self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
|
||||
self.by_key("lang").string_value().and_then(LangItem::from_str)
|
||||
}
|
||||
|
||||
pub fn has_doc_hidden(&self) -> bool {
|
||||
|
@ -178,7 +178,7 @@ impl Attrs {
|
|||
self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
|
||||
}
|
||||
|
||||
pub fn export_name(&self) -> Option<&SmolStr> {
|
||||
pub fn export_name(&self) -> Option<&str> {
|
||||
self.by_key("export_name").string_value()
|
||||
}
|
||||
|
||||
|
@ -565,7 +565,7 @@ impl<'attr> AttrQuery<'attr> {
|
|||
self.attrs().filter_map(|attr| attr.token_tree_value())
|
||||
}
|
||||
|
||||
pub fn string_value(self) -> Option<&'attr SmolStr> {
|
||||
pub fn string_value(self) -> Option<&'attr str> {
|
||||
self.attrs().find_map(|attr| attr.string_value())
|
||||
}
|
||||
|
||||
|
|
|
@ -453,8 +453,8 @@ impl ProcMacroData {
|
|||
(
|
||||
def.name,
|
||||
match def.kind {
|
||||
ProcMacroKind::CustomDerive { helpers } => Some(helpers),
|
||||
ProcMacroKind::FnLike | ProcMacroKind::Attr => None,
|
||||
ProcMacroKind::Derive { helpers } => Some(helpers),
|
||||
ProcMacroKind::Bang | ProcMacroKind::Attr => None,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
|
@ -484,10 +484,11 @@ impl ExternCrateDeclData {
|
|||
let extern_crate = &item_tree[loc.id.value];
|
||||
|
||||
let name = extern_crate.name.clone();
|
||||
let krate = loc.container.krate();
|
||||
let crate_id = if name == hir_expand::name![self] {
|
||||
Some(loc.container.krate())
|
||||
Some(krate)
|
||||
} else {
|
||||
db.crate_def_map(loc.container.krate())
|
||||
db.crate_def_map(krate)
|
||||
.extern_prelude()
|
||||
.find(|&(prelude_name, ..)| *prelude_name == name)
|
||||
.map(|(_, (root, _))| root.krate())
|
||||
|
|
|
@ -22,8 +22,8 @@ use crate::{
|
|||
lower::LowerCtx,
|
||||
nameres::{DefMap, MacroSubNs},
|
||||
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
|
||||
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup,
|
||||
TypeOrConstParamId, TypeParamId,
|
||||
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
|
||||
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
||||
};
|
||||
|
||||
/// Data about a generic type parameter (to a function, struct, impl, ...).
|
||||
|
@ -102,6 +102,52 @@ impl TypeOrConstParamData {
|
|||
|
||||
impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum GenericParamData {
|
||||
TypeParamData(TypeParamData),
|
||||
ConstParamData(ConstParamData),
|
||||
LifetimeParamData(LifetimeParamData),
|
||||
}
|
||||
|
||||
impl GenericParamData {
|
||||
pub fn name(&self) -> Option<&Name> {
|
||||
match self {
|
||||
GenericParamData::TypeParamData(it) => it.name.as_ref(),
|
||||
GenericParamData::ConstParamData(it) => Some(&it.name),
|
||||
GenericParamData::LifetimeParamData(it) => Some(&it.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_param(&self) -> Option<&TypeParamData> {
|
||||
match self {
|
||||
GenericParamData::TypeParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_param(&self) -> Option<&ConstParamData> {
|
||||
match self {
|
||||
GenericParamData::ConstParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lifetime_param(&self) -> Option<&LifetimeParamData> {
|
||||
match self {
|
||||
GenericParamData::LifetimeParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
|
||||
|
||||
pub enum GenericParamDataRef<'a> {
|
||||
TypeParamData(&'a TypeParamData),
|
||||
ConstParamData(&'a ConstParamData),
|
||||
LifetimeParamData(&'a LifetimeParamData),
|
||||
}
|
||||
|
||||
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct GenericParams {
|
||||
|
@ -358,6 +404,15 @@ impl GenericParamsCollector {
|
|||
}
|
||||
|
||||
impl GenericParams {
|
||||
/// Number of Generic parameters (type_or_consts + lifetimes)
|
||||
pub fn len(&self) -> usize {
|
||||
self.type_or_consts.len() + self.lifetimes.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Iterator of type_or_consts field
|
||||
pub fn iter(
|
||||
&self,
|
||||
|
@ -365,6 +420,13 @@ impl GenericParams {
|
|||
self.type_or_consts.iter()
|
||||
}
|
||||
|
||||
/// Iterator of lifetimes field
|
||||
pub fn iter_lt(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (Idx<LifetimeParamData>, &LifetimeParamData)> {
|
||||
self.lifetimes.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn generic_params_query(
|
||||
db: &dyn DefDatabase,
|
||||
def: GenericDefId,
|
||||
|
@ -507,4 +569,18 @@ impl GenericParams {
|
|||
.then(|| id)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_lifetime_by_name(
|
||||
&self,
|
||||
name: &Name,
|
||||
parent: GenericDefId,
|
||||
) -> Option<LifetimeParamId> {
|
||||
self.lifetimes.iter().find_map(|(id, p)| {
|
||||
if &p.name == name {
|
||||
Some(LifetimeParamId { local_id: id, parent })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -526,7 +526,7 @@ impl Printer<'_> {
|
|||
}
|
||||
|
||||
fn print_generic_params(&mut self, params: &GenericParams) {
|
||||
if params.type_or_consts.is_empty() && params.lifetimes.is_empty() {
|
||||
if params.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ impl LangItems {
|
|||
|
||||
pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
|
||||
let attrs = db.attrs(item);
|
||||
attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
|
||||
attrs.by_key("lang").string_value().and_then(LangItem::from_str)
|
||||
}
|
||||
|
||||
pub(crate) fn notable_traits_in_deps(
|
||||
|
|
|
@ -73,7 +73,7 @@ use std::{
|
|||
use base_db::{
|
||||
impl_intern_key,
|
||||
salsa::{self, impl_intern_value_trivial},
|
||||
CrateId, Edition,
|
||||
CrateId,
|
||||
};
|
||||
use hir_expand::{
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
|
@ -90,7 +90,7 @@ use hir_expand::{
|
|||
use item_tree::ExternBlock;
|
||||
use la_arena::Idx;
|
||||
use nameres::DefMap;
|
||||
use span::{AstIdNode, FileAstId, FileId, SyntaxContextId};
|
||||
use span::{AstIdNode, Edition, FileAstId, FileId, SyntaxContextId};
|
||||
use stdx::impl_from;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
|
|
|
@ -1449,6 +1449,7 @@ ok!();
|
|||
#[test]
|
||||
fn test_new_std_matches() {
|
||||
check(
|
||||
//- edition:2021
|
||||
r#"
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
|
||||
|
@ -1480,6 +1481,90 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hygienic_pat() {
|
||||
check(
|
||||
r#"
|
||||
//- /new.rs crate:new deps:old edition:2015
|
||||
old::make!();
|
||||
fn main() {
|
||||
matches!(0, 0 | 1 if true);
|
||||
}
|
||||
//- /old.rs crate:old edition:2021
|
||||
#[macro_export]
|
||||
macro_rules! make {
|
||||
() => {
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat if $guard:expr ) => {
|
||||
match $expression {
|
||||
$pattern if $guard => true,
|
||||
_ => false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules !matches {
|
||||
($expression: expr, $pattern: pat if $guard: expr) = > {
|
||||
match $expression {
|
||||
$pattern if $guard = > true , _ = > false
|
||||
}
|
||||
}
|
||||
;
|
||||
}
|
||||
fn main() {
|
||||
match 0 {
|
||||
0|1 if true =>true , _=>false
|
||||
};
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
//- /new.rs crate:new deps:old edition:2021
|
||||
old::make!();
|
||||
fn main() {
|
||||
matches/*+errors*/!(0, 0 | 1 if true);
|
||||
}
|
||||
//- /old.rs crate:old edition:2015
|
||||
#[macro_export]
|
||||
macro_rules! make {
|
||||
() => {
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat if $guard:expr ) => {
|
||||
match $expression {
|
||||
$pattern if $guard => true,
|
||||
_ => false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules !matches {
|
||||
($expression: expr, $pattern: pat if $guard: expr) = > {
|
||||
match $expression {
|
||||
$pattern if $guard = > true , _ = > false
|
||||
}
|
||||
}
|
||||
;
|
||||
}
|
||||
fn main() {
|
||||
/* error: unexpected token in input *//* parse error: expected expression */
|
||||
/* parse error: expected FAT_ARROW */
|
||||
/* parse error: expected `,` */
|
||||
/* parse error: expected pattern */
|
||||
match 0 {
|
||||
0 if $guard=>true , _=>false
|
||||
};
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dollar_crate_lhs_is_not_meta() {
|
||||
check(
|
||||
|
|
|
@ -59,14 +59,14 @@ mod tests;
|
|||
|
||||
use std::ops::Deref;
|
||||
|
||||
use base_db::{CrateId, Edition, FileId};
|
||||
use base_db::{CrateId, FileId};
|
||||
use hir_expand::{
|
||||
name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{FileAstId, ROOT_ERASED_FILE_AST_ID};
|
||||
use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
|
@ -737,7 +737,7 @@ impl MacroSubNs {
|
|||
MacroId::ProcMacroId(it) => {
|
||||
return match it.lookup(db).kind {
|
||||
ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
|
||||
ProcMacroKind::FuncLike => Self::Bang,
|
||||
ProcMacroKind::Bang => Self::Bang,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -136,6 +136,7 @@ pub(super) fn derive_macro_as_call_id(
|
|||
call_site: SyntaxContextId,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
derive_macro_id: MacroCallId,
|
||||
) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
|
||||
let (macro_id, def_id) = resolver(item_attr.path.clone())
|
||||
.filter(|(_, def_id)| def_id.is_derive())
|
||||
|
@ -147,6 +148,7 @@ pub(super) fn derive_macro_as_call_id(
|
|||
ast_id: item_attr.ast_id,
|
||||
derive_index: derive_pos,
|
||||
derive_attr_index,
|
||||
derive_macro_id,
|
||||
},
|
||||
call_site,
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use std::{cmp::Ordering, iter, mem, ops::Not};
|
||||
|
||||
use base_db::{CrateId, Dependency, Edition, FileId};
|
||||
use base_db::{CrateId, Dependency, FileId};
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
|
@ -22,9 +22,9 @@ use itertools::{izip, Itertools};
|
|||
use la_arena::Idx;
|
||||
use limit::Limit;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{ErasedFileAstId, FileAstId, Span, SyntaxContextId};
|
||||
use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId};
|
||||
use stdx::always;
|
||||
use syntax::{ast, SmolStr};
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
@ -237,6 +237,8 @@ enum MacroDirectiveKind {
|
|||
derive_attr: AttrId,
|
||||
derive_pos: usize,
|
||||
ctxt: SyntaxContextId,
|
||||
/// The "parent" macro it is resolved to.
|
||||
derive_macro_id: MacroCallId,
|
||||
},
|
||||
Attr {
|
||||
ast_id: AstIdWithPath<ast::Item>,
|
||||
|
@ -312,7 +314,7 @@ impl DefCollector<'_> {
|
|||
}
|
||||
}
|
||||
() if *attr_name == hir_expand::name![crate_type] => {
|
||||
if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
|
||||
if let Some("proc-macro") = attr.string_value() {
|
||||
self.is_proc_macro = true;
|
||||
}
|
||||
}
|
||||
|
@ -602,7 +604,7 @@ impl DefCollector<'_> {
|
|||
.intern(self.db);
|
||||
self.define_proc_macro(def.name.clone(), proc_macro_id);
|
||||
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
|
||||
if let ProcMacroKind::CustomDerive { helpers } = def.kind {
|
||||
if let ProcMacroKind::Derive { helpers } = def.kind {
|
||||
crate_data.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers);
|
||||
}
|
||||
crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
|
||||
|
@ -1146,7 +1148,13 @@ impl DefCollector<'_> {
|
|||
return Resolved::Yes;
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => {
|
||||
MacroDirectiveKind::Derive {
|
||||
ast_id,
|
||||
derive_attr,
|
||||
derive_pos,
|
||||
ctxt: call_site,
|
||||
derive_macro_id,
|
||||
} => {
|
||||
let id = derive_macro_as_call_id(
|
||||
self.db,
|
||||
ast_id,
|
||||
|
@ -1155,6 +1163,7 @@ impl DefCollector<'_> {
|
|||
*call_site,
|
||||
self.def_map.krate,
|
||||
resolver,
|
||||
*derive_macro_id,
|
||||
);
|
||||
|
||||
if let Ok((macro_id, def_id, call_id)) = id {
|
||||
|
@ -1224,6 +1233,8 @@ impl DefCollector<'_> {
|
|||
_ => return Resolved::No,
|
||||
};
|
||||
|
||||
let call_id =
|
||||
attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
|
||||
if let MacroDefId {
|
||||
kind:
|
||||
MacroDefKind::BuiltInAttr(
|
||||
|
@ -1252,6 +1263,7 @@ impl DefCollector<'_> {
|
|||
return recollect_without(self);
|
||||
}
|
||||
};
|
||||
|
||||
let ast_id = ast_id.with_value(ast_adt_id);
|
||||
|
||||
match attr.parse_path_comma_token_tree(self.db.upcast()) {
|
||||
|
@ -1267,6 +1279,7 @@ impl DefCollector<'_> {
|
|||
derive_attr: attr.id,
|
||||
derive_pos: idx,
|
||||
ctxt: call_site.ctx,
|
||||
derive_macro_id: call_id,
|
||||
},
|
||||
container: directive.container,
|
||||
});
|
||||
|
@ -1301,10 +1314,6 @@ impl DefCollector<'_> {
|
|||
return recollect_without(self);
|
||||
}
|
||||
|
||||
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
|
||||
let call_id =
|
||||
attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
|
||||
|
||||
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||
// due to duplicating functions into macro expansions
|
||||
if matches!(
|
||||
|
@ -1460,13 +1469,20 @@ impl DefCollector<'_> {
|
|||
));
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => {
|
||||
MacroDirectiveKind::Derive {
|
||||
ast_id,
|
||||
derive_attr,
|
||||
derive_pos,
|
||||
derive_macro_id,
|
||||
..
|
||||
} => {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
directive.module_id,
|
||||
MacroCallKind::Derive {
|
||||
ast_id: ast_id.ast_id,
|
||||
derive_attr_index: *derive_attr,
|
||||
derive_index: *derive_pos as u32,
|
||||
derive_macro_id: *derive_macro_id,
|
||||
},
|
||||
ast_id.path.clone(),
|
||||
));
|
||||
|
@ -1902,7 +1918,7 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
|
||||
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
|
||||
let path_attr = attrs.by_key("path").string_value().map(SmolStr::as_str);
|
||||
let path_attr = attrs.by_key("path").string_value();
|
||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||
let module = &self.item_tree[module_id];
|
||||
match &module.kind {
|
||||
|
@ -2146,7 +2162,7 @@ impl ModCollector<'_, '_> {
|
|||
Some(it) => {
|
||||
// FIXME: a hacky way to create a Name from string.
|
||||
name = tt::Ident {
|
||||
text: it.clone(),
|
||||
text: it.into(),
|
||||
span: Span {
|
||||
range: syntax::TextRange::empty(syntax::TextSize::new(0)),
|
||||
anchor: span::SpanAnchor {
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
//!
|
||||
//! `ReachedFixedPoint` signals about this.
|
||||
|
||||
use base_db::Edition;
|
||||
use hir_expand::{name::Name, Lookup};
|
||||
use span::Edition;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
|
@ -13,18 +13,16 @@ pub struct ProcMacroDef {
|
|||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ProcMacroKind {
|
||||
CustomDerive { helpers: Box<[Name]> },
|
||||
FnLike,
|
||||
Derive { helpers: Box<[Name]> },
|
||||
Bang,
|
||||
Attr,
|
||||
}
|
||||
|
||||
impl ProcMacroKind {
|
||||
pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind {
|
||||
match self {
|
||||
ProcMacroKind::CustomDerive { .. } => {
|
||||
hir_expand::proc_macro::ProcMacroKind::CustomDerive
|
||||
}
|
||||
ProcMacroKind::FnLike => hir_expand::proc_macro::ProcMacroKind::FuncLike,
|
||||
ProcMacroKind::Derive { .. } => hir_expand::proc_macro::ProcMacroKind::CustomDerive,
|
||||
ProcMacroKind::Bang => hir_expand::proc_macro::ProcMacroKind::Bang,
|
||||
ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr,
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +32,13 @@ impl Attrs {
|
|||
#[rustfmt::skip]
|
||||
pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
|
||||
if self.is_proc_macro() {
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike })
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang })
|
||||
} else if self.is_proc_macro_attribute() {
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
|
||||
} else if self.by_key("proc_macro_derive").exists() {
|
||||
let derive = self.by_key("proc_macro_derive").tt_values().next()?;
|
||||
let def = parse_macro_name_and_helper_attrs(&derive.token_trees)
|
||||
.map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } });
|
||||
.map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } });
|
||||
|
||||
if def.is_none() {
|
||||
tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::{
|
|||
nameres::{DefMap, MacroSubNs},
|
||||
path::{ModPath, Path, PathKind},
|
||||
per_ns::PerNs,
|
||||
type_ref::LifetimeRef,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
|
||||
ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
|
||||
|
@ -120,6 +121,12 @@ pub enum ValueNs {
|
|||
GenericParam(ConstParamId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum LifetimeNs {
|
||||
Static,
|
||||
LifetimeParam(LifetimeParamId),
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
/// Resolve known trait from std, like `std::futures::Future`
|
||||
pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<TraitId> {
|
||||
|
@ -418,6 +425,19 @@ impl Resolver {
|
|||
self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| db.macro_def(it))
|
||||
}
|
||||
|
||||
pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option<LifetimeNs> {
|
||||
if lifetime.name == name::known::STATIC_LIFETIME {
|
||||
return Some(LifetimeNs::Static);
|
||||
}
|
||||
|
||||
self.scopes().find_map(|scope| match scope {
|
||||
Scope::GenericParams { def, params } => {
|
||||
params.find_lifetime_by_name(&lifetime.name, *def).map(LifetimeNs::LifetimeParam)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a set of names available in the current scope.
|
||||
///
|
||||
/// Note that this is a somewhat fuzzy concept -- internally, the compiler
|
||||
|
|
|
@ -8,8 +8,8 @@ use intern::Interned;
|
|||
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::{Span, SyntaxContextId};
|
||||
use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
|
||||
use triomphe::Arc;
|
||||
use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
|
||||
use triomphe::ThinArc;
|
||||
|
||||
use crate::{
|
||||
db::ExpandDatabase,
|
||||
|
@ -22,8 +22,7 @@ use crate::{
|
|||
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RawAttrs {
|
||||
// FIXME: Make this a ThinArc
|
||||
entries: Option<Arc<[Attr]>>,
|
||||
entries: Option<ThinArc<(), Attr>>,
|
||||
}
|
||||
|
||||
impl ops::Deref for RawAttrs {
|
||||
|
@ -31,7 +30,7 @@ impl ops::Deref for RawAttrs {
|
|||
|
||||
fn deref(&self) -> &[Attr] {
|
||||
match &self.entries {
|
||||
Some(it) => it,
|
||||
Some(it) => &it.slice,
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
|
@ -45,20 +44,34 @@ impl RawAttrs {
|
|||
owner: &dyn ast::HasAttrs,
|
||||
span_map: SpanMapRef<'_>,
|
||||
) -> Self {
|
||||
let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr {
|
||||
Either::Left(attr) => {
|
||||
attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
|
||||
}
|
||||
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
|
||||
id,
|
||||
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
|
||||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||
ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx,
|
||||
}),
|
||||
});
|
||||
let entries: Arc<[Attr]> = Arc::from_iter(entries);
|
||||
let entries: Vec<_> = collect_attrs(owner)
|
||||
.filter_map(|(id, attr)| match attr {
|
||||
Either::Left(attr) => {
|
||||
attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
|
||||
}
|
||||
Either::Right(comment) => comment.doc_comment().map(|doc| {
|
||||
let span = span_map.span_for_range(comment.syntax().text_range());
|
||||
Attr {
|
||||
id,
|
||||
input: Some(Interned::new(AttrInput::Literal(tt::Literal {
|
||||
// FIXME: Escape quotes from comment content
|
||||
text: SmolStr::new(format_smolstr!("\"{doc}\"",)),
|
||||
span,
|
||||
}))),
|
||||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||
ctxt: span.ctx,
|
||||
}
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
|
||||
let entries = if entries.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ThinArc::from_header_and_iter((), entries.into_iter()))
|
||||
};
|
||||
|
||||
RawAttrs { entries }
|
||||
}
|
||||
|
||||
pub fn from_attrs_owner(
|
||||
|
@ -75,16 +88,20 @@ impl RawAttrs {
|
|||
(None, entries @ Some(_)) => Self { entries },
|
||||
(Some(entries), None) => Self { entries: Some(entries.clone()) },
|
||||
(Some(a), Some(b)) => {
|
||||
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
|
||||
Self {
|
||||
entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| {
|
||||
let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
|
||||
let items = a
|
||||
.slice
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(b.slice.iter().map(|it| {
|
||||
let mut it = it.clone();
|
||||
it.id.id = (it.id.ast_index() as u32 + last_ast_index)
|
||||
| (it.id.cfg_attr_index().unwrap_or(0) as u32)
|
||||
<< AttrId::AST_INDEX_BITS;
|
||||
it
|
||||
})))),
|
||||
}
|
||||
}))
|
||||
.collect::<Vec<_>>();
|
||||
Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,41 +117,47 @@ impl RawAttrs {
|
|||
}
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
let new_attrs =
|
||||
self.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
|
||||
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
|
||||
Some(it) => it,
|
||||
None => return smallvec![attr.clone()],
|
||||
};
|
||||
let index = attr.id;
|
||||
let attrs = parts
|
||||
.enumerate()
|
||||
.take(1 << AttrId::CFG_ATTR_BITS)
|
||||
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
|
||||
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
|
||||
Some(it) => it,
|
||||
None => return smallvec![attr.clone()],
|
||||
};
|
||||
let index = attr.id;
|
||||
let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
|
||||
|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
|
||||
);
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) };
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) };
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
|
||||
attrs.collect()
|
||||
}
|
||||
}));
|
||||
|
||||
RawAttrs { entries: Some(new_attrs) }
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let entries = if new_attrs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ThinArc::from_header_and_iter((), new_attrs.into_iter()))
|
||||
};
|
||||
RawAttrs { entries }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,8 +202,7 @@ pub struct Attr {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AttrInput {
|
||||
/// `#[attr = "string"]`
|
||||
// FIXME: This is losing span
|
||||
Literal(SmolStr),
|
||||
Literal(tt::Literal),
|
||||
/// `#[attr(subtree)]`
|
||||
TokenTree(Box<tt::Subtree>),
|
||||
}
|
||||
|
@ -188,7 +210,7 @@ pub enum AttrInput {
|
|||
impl fmt::Display for AttrInput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
|
||||
AttrInput::Literal(lit) => write!(f, " = {lit}"),
|
||||
AttrInput::TokenTree(tt) => tt.fmt(f),
|
||||
}
|
||||
}
|
||||
|
@ -208,11 +230,10 @@ impl Attr {
|
|||
})?);
|
||||
let span = span_map.span_for_range(range);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
let value = match lit.kind() {
|
||||
ast::LiteralKind::String(string) => string.value()?.into(),
|
||||
_ => lit.syntax().first_token()?.text().trim_matches('"').into(),
|
||||
};
|
||||
Some(Interned::new(AttrInput::Literal(value)))
|
||||
Some(Interned::new(AttrInput::Literal(tt::Literal {
|
||||
text: lit.token().text().into(),
|
||||
span,
|
||||
})))
|
||||
} else if let Some(tt) = ast.token_tree() {
|
||||
let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span);
|
||||
Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
|
||||
|
@ -245,9 +266,8 @@ impl Attr {
|
|||
}
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => {
|
||||
let input = match input.get(1) {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => {
|
||||
//FIXME the trimming here isn't quite right, raw strings are not handled
|
||||
Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into())))
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
|
||||
Some(Interned::new(AttrInput::Literal(lit.clone())))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
@ -265,9 +285,14 @@ impl Attr {
|
|||
|
||||
impl Attr {
|
||||
/// #[path = "string"]
|
||||
pub fn string_value(&self) -> Option<&SmolStr> {
|
||||
pub fn string_value(&self) -> Option<&str> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(it) => Some(it),
|
||||
AttrInput::Literal(it) => match it.text.strip_prefix('r') {
|
||||
Some(it) => it.trim_matches('#'),
|
||||
None => it.text.as_str(),
|
||||
}
|
||||
.strip_prefix('"')?
|
||||
.strip_suffix('"'),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Builtin macro
|
||||
|
||||
use base_db::{AnchoredPath, Edition, FileId};
|
||||
use base_db::{AnchoredPath, FileId};
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use itertools::Itertools;
|
||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
|
||||
use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use syntax::ast::{self, AstToken};
|
||||
|
||||
use crate::{
|
||||
|
|
|
@ -10,7 +10,7 @@ use syntax::{
|
|||
use tracing::{debug, warn};
|
||||
use tt::SmolStr;
|
||||
|
||||
use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc};
|
||||
use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
|
||||
|
||||
fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
|
||||
if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
|
||||
|
@ -139,7 +139,7 @@ fn process_enum(
|
|||
'variant: for variant in variants.variants() {
|
||||
for attr in variant.attrs() {
|
||||
if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() {
|
||||
// Rustc does not strip the attribute if it is enabled. So we will will leave it
|
||||
// Rustc does not strip the attribute if it is enabled. So we will leave it
|
||||
debug!("censoring type {:?}", variant.syntax());
|
||||
remove.insert(variant.syntax().clone().into());
|
||||
// We need to remove the , as well
|
||||
|
@ -180,7 +180,13 @@ pub(crate) fn process_cfg_attrs(
|
|||
db: &dyn ExpandDatabase,
|
||||
) -> Option<FxHashSet<SyntaxElement>> {
|
||||
// FIXME: #[cfg_eval] is not implemented. But it is not stable yet
|
||||
if !matches!(loc.kind, MacroCallKind::Derive { .. }) {
|
||||
let is_derive = match loc.def.kind {
|
||||
MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) => true,
|
||||
MacroDefKind::BuiltInAttr(expander, _) => expander.is_derive(),
|
||||
_ => false,
|
||||
};
|
||||
if !is_derive {
|
||||
return None;
|
||||
}
|
||||
let mut remove = FxHashSet::default();
|
||||
|
|
|
@ -24,7 +24,8 @@ use crate::{
|
|||
HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||
MacroFileId,
|
||||
};
|
||||
|
||||
/// This is just to ensure the types of smart_macro_arg and macro_arg are the same
|
||||
type MacroArgResult = (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
|
||||
/// Total limit on the number of tokens produced by any macro invocation.
|
||||
///
|
||||
/// If an invocation produces more tokens than this limit, it will not be stored in the database and
|
||||
|
@ -98,7 +99,13 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
/// Lowers syntactic macro call to a token tree representation. That's a firewall
|
||||
/// query, only typing in the macro call itself changes the returned
|
||||
/// subtree.
|
||||
fn macro_arg(&self, id: MacroCallId) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
|
||||
fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
|
||||
#[salsa::transparent]
|
||||
fn macro_arg_considering_derives(
|
||||
&self,
|
||||
id: MacroCallId,
|
||||
kind: &MacroCallKind,
|
||||
) -> MacroArgResult;
|
||||
/// Fetches the expander for this macro.
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(TokenExpander::macro_expander)]
|
||||
|
@ -144,7 +151,7 @@ pub fn expand_speculative(
|
|||
let span_map = RealSpanMap::absolute(FileId::BOGUS);
|
||||
let span_map = SpanMapRef::RealSpanMap(&span_map);
|
||||
|
||||
let (_, _, span) = db.macro_arg(actual_macro_call);
|
||||
let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
|
||||
|
||||
// Build the subtree and token mapping for the speculative args
|
||||
let (mut tt, undo_info) = match loc.kind {
|
||||
|
@ -339,12 +346,24 @@ pub(crate) fn parse_with_map(
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: for derive attributes, this will return separate copies of the same structures! Though
|
||||
// they may differ in spans due to differing call sites...
|
||||
fn macro_arg(
|
||||
/// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive.
|
||||
/// Other wise return the [macro_arg] for the macro_call_id.
|
||||
///
|
||||
/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is
|
||||
fn macro_arg_considering_derives(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span) {
|
||||
kind: &MacroCallKind,
|
||||
) -> MacroArgResult {
|
||||
match kind {
|
||||
// Get the macro arg for the derive macro
|
||||
MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id),
|
||||
// Normal macro arg
|
||||
_ => db.macro_arg(id),
|
||||
}
|
||||
}
|
||||
|
||||
fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
|
||||
if let MacroCallLoc {
|
||||
|
@ -414,29 +433,30 @@ fn macro_arg(
|
|||
}
|
||||
return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
|
||||
let node = ast_id.to_ptr(db).to_node(&root);
|
||||
let censor_derive_input = censor_derive_input(derive_attr_index, &node);
|
||||
let item_node = node.into();
|
||||
let attr_source = attr_source(derive_attr_index, &item_node);
|
||||
// FIXME: This is wrong, this should point to the path of the derive attribute`
|
||||
let span =
|
||||
map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else(
|
||||
|| item_node.syntax().text_range(),
|
||||
|it| it.syntax().text_range(),
|
||||
));
|
||||
(censor_derive_input, item_node, span)
|
||||
// MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro
|
||||
MacroCallKind::Derive { .. } => {
|
||||
unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`")
|
||||
}
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
||||
let node = ast_id.to_ptr(db).to_node(&root);
|
||||
let attr_source = attr_source(invoc_attr_index, &node);
|
||||
|
||||
let span = map.span_for_range(
|
||||
attr_source
|
||||
.as_ref()
|
||||
.and_then(|it| it.path())
|
||||
.map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
|
||||
);
|
||||
(attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
|
||||
// If derive attribute we need to censor the derive input
|
||||
if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
|
||||
&& ast::Adt::can_cast(node.syntax().kind())
|
||||
{
|
||||
let adt = ast::Adt::cast(node.syntax().clone()).unwrap();
|
||||
let censor_derive_input = censor_derive_input(invoc_attr_index, &adt);
|
||||
(censor_derive_input, node, span)
|
||||
} else {
|
||||
(attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -526,7 +546,8 @@ fn macro_expand(
|
|||
let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
|
||||
_ => {
|
||||
let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id);
|
||||
let (macro_arg, undo_info, span) =
|
||||
db.macro_arg_considering_derives(macro_call_id, &loc.kind);
|
||||
|
||||
let arg = &*macro_arg;
|
||||
let res =
|
||||
|
@ -603,7 +624,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
|
|||
|
||||
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let (macro_arg, undo_info, span) = db.macro_arg(id);
|
||||
let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind);
|
||||
|
||||
let (expander, ast) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Compiled declarative macro expanders (`macro_rules!`` and `macro`)
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use base_db::{CrateId, Edition, VersionReq};
|
||||
use span::{MacroCallId, Span};
|
||||
use base_db::{CrateId, VersionReq};
|
||||
use span::{MacroCallId, Span, SyntaxContextId};
|
||||
use syntax::{ast, AstNode};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -10,13 +10,13 @@ use crate::{
|
|||
attrs::RawAttrs,
|
||||
db::ExpandDatabase,
|
||||
hygiene::{apply_mark, Transparency},
|
||||
tt, AstId, ExpandError, ExpandResult,
|
||||
tt, AstId, ExpandError, ExpandResult, Lookup,
|
||||
};
|
||||
|
||||
/// Old-style `macro_rules` or the new macros 2.0
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct DeclarativeMacroExpander {
|
||||
pub mac: mbe::DeclarativeMacro<span::Span>,
|
||||
pub mac: mbe::DeclarativeMacro,
|
||||
pub transparency: Transparency,
|
||||
}
|
||||
|
||||
|
@ -94,8 +94,6 @@ impl DeclarativeMacroExpander {
|
|||
def_crate: CrateId,
|
||||
id: AstId<ast::Macro>,
|
||||
) -> Arc<DeclarativeMacroExpander> {
|
||||
let crate_data = &db.crate_graph()[def_crate];
|
||||
let is_2021 = crate_data.edition >= Edition::Edition2021;
|
||||
let (root, map) = crate::db::parse_with_map(db, id.file_id);
|
||||
let root = root.syntax_node();
|
||||
|
||||
|
@ -133,6 +131,16 @@ impl DeclarativeMacroExpander {
|
|||
)
|
||||
});
|
||||
|
||||
let edition = |ctx: SyntaxContextId| {
|
||||
let crate_graph = db.crate_graph();
|
||||
if ctx.is_root() {
|
||||
crate_graph[def_crate].edition
|
||||
} else {
|
||||
let data = db.lookup_intern_syntax_context(ctx);
|
||||
// UNWRAP-SAFETY: Only the root context has no outer expansion
|
||||
crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition
|
||||
}
|
||||
};
|
||||
let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
|
||||
ast::Macro::MacroRules(macro_rules) => (
|
||||
match macro_rules.token_tree() {
|
||||
|
@ -145,12 +153,11 @@ impl DeclarativeMacroExpander {
|
|||
),
|
||||
);
|
||||
|
||||
mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars)
|
||||
mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars)
|
||||
}
|
||||
None => mbe::DeclarativeMacro::from_err(
|
||||
mbe::ParseError::Expected("expected a token tree".into()),
|
||||
is_2021,
|
||||
),
|
||||
None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
|
||||
"expected a token tree".into(),
|
||||
)),
|
||||
},
|
||||
transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent),
|
||||
),
|
||||
|
@ -163,12 +170,11 @@ impl DeclarativeMacroExpander {
|
|||
map.span_for_range(macro_def.macro_token().unwrap().text_range()),
|
||||
);
|
||||
|
||||
mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars)
|
||||
mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars)
|
||||
}
|
||||
None => mbe::DeclarativeMacro::from_err(
|
||||
mbe::ParseError::Expected("expected a token tree".into()),
|
||||
is_2021,
|
||||
),
|
||||
None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
|
||||
"expected a token tree".into(),
|
||||
)),
|
||||
},
|
||||
transparency(¯o_def).unwrap_or(Transparency::Opaque),
|
||||
),
|
||||
|
|
|
@ -30,10 +30,11 @@ use triomphe::Arc;
|
|||
|
||||
use std::{fmt, hash::Hash};
|
||||
|
||||
use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId};
|
||||
use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId};
|
||||
use either::Either;
|
||||
use span::{
|
||||
ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId,
|
||||
Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData,
|
||||
SyntaxContextId,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
|
@ -53,11 +54,9 @@ use crate::{
|
|||
|
||||
pub use crate::files::{AstId, ErasedAstId, InFile, InMacroFile, InRealFile};
|
||||
|
||||
pub use mbe::ValueResult;
|
||||
pub use mbe::{DeclarativeMacro, ValueResult};
|
||||
pub use span::{HirFileId, MacroCallId, MacroFileId};
|
||||
|
||||
pub type DeclarativeMacro = ::mbe::DeclarativeMacro<tt::Span>;
|
||||
|
||||
pub mod tt {
|
||||
pub use span::Span;
|
||||
pub use tt::{DelimiterKind, Spacing};
|
||||
|
@ -201,7 +200,7 @@ pub struct EagerCallInfo {
|
|||
/// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
|
||||
arg_id: MacroCallId,
|
||||
error: Option<ExpandError>,
|
||||
/// TODO: Doc
|
||||
/// The call site span of the eager macro
|
||||
span: Span,
|
||||
}
|
||||
|
||||
|
@ -212,7 +211,7 @@ pub enum MacroCallKind {
|
|||
expand_to: ExpandTo,
|
||||
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
||||
/// for the eager input macro file.
|
||||
// FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
|
||||
// FIXME: This is being interned, subtrees can vary quickly differing just slightly causing
|
||||
// leakage problems here
|
||||
eager: Option<Arc<EagerCallInfo>>,
|
||||
},
|
||||
|
@ -225,6 +224,9 @@ pub enum MacroCallKind {
|
|||
derive_attr_index: AttrId,
|
||||
/// Index of the derive macro in the derive attribute
|
||||
derive_index: u32,
|
||||
/// The "parent" macro call.
|
||||
/// We will resolve the same token tree for all derive macros in the same derive attribute.
|
||||
derive_macro_id: MacroCallId,
|
||||
},
|
||||
Attr {
|
||||
ast_id: AstId<ast::Item>,
|
||||
|
@ -484,7 +486,7 @@ impl MacroDefId {
|
|||
matches!(
|
||||
self.kind,
|
||||
MacroDefKind::BuiltIn(..)
|
||||
| MacroDefKind::ProcMacro(_, ProcMacroKind::FuncLike, _)
|
||||
| MacroDefKind::ProcMacro(_, ProcMacroKind::Bang, _)
|
||||
| MacroDefKind::BuiltInEager(..)
|
||||
| MacroDefKind::Declarative(..)
|
||||
)
|
||||
|
@ -806,7 +808,8 @@ impl ExpansionInfo {
|
|||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
|
||||
|
||||
let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id);
|
||||
let (macro_arg, _, _) =
|
||||
db.macro_arg_considering_derives(macro_file.macro_call_id, &loc.kind);
|
||||
|
||||
let def = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
|
|
|
@ -225,6 +225,26 @@ fn convert_path(
|
|||
let mut segments = path.segments();
|
||||
|
||||
let segment = &segments.next()?;
|
||||
let handle_super_kw = &mut |init_deg| {
|
||||
let mut deg = init_deg;
|
||||
let mut next_segment = None;
|
||||
for segment in segments.by_ref() {
|
||||
match segment.kind()? {
|
||||
ast::PathSegmentKind::SuperKw => deg += 1,
|
||||
ast::PathSegmentKind::Name(name) => {
|
||||
next_segment = Some(name.as_name());
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. }
|
||||
| ast::PathSegmentKind::SelfTypeKw
|
||||
| ast::PathSegmentKind::SelfKw
|
||||
| ast::PathSegmentKind::CrateKw => return None,
|
||||
}
|
||||
}
|
||||
|
||||
Some(ModPath::from_segments(PathKind::Super(deg), next_segment))
|
||||
};
|
||||
|
||||
let mut mod_path = match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
if name_ref.text() == "$crate" {
|
||||
|
@ -245,26 +265,8 @@ fn convert_path(
|
|||
ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE))
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
|
||||
ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()),
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
let mut deg = 1;
|
||||
let mut next_segment = None;
|
||||
for segment in segments.by_ref() {
|
||||
match segment.kind()? {
|
||||
ast::PathSegmentKind::SuperKw => deg += 1,
|
||||
ast::PathSegmentKind::Name(name) => {
|
||||
next_segment = Some(name.as_name());
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. }
|
||||
| ast::PathSegmentKind::SelfTypeKw
|
||||
| ast::PathSegmentKind::SelfKw
|
||||
| ast::PathSegmentKind::CrateKw => return None,
|
||||
}
|
||||
}
|
||||
|
||||
ModPath::from_segments(PathKind::Super(deg), next_segment)
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
|
||||
ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
|
||||
ast::PathSegmentKind::Type { .. } => {
|
||||
// not allowed in imports
|
||||
return None;
|
||||
|
|
|
@ -23,7 +23,7 @@ impl ProcMacroId {
|
|||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
|
||||
pub enum ProcMacroKind {
|
||||
CustomDerive,
|
||||
FuncLike,
|
||||
Bang,
|
||||
Attr,
|
||||
}
|
||||
|
||||
|
|
|
@ -47,13 +47,14 @@ hir-expand.workspace = true
|
|||
base-db.workspace = true
|
||||
syntax.workspace = true
|
||||
limit.workspace = true
|
||||
span.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing-tree.workspace = true
|
||||
project-model = { path = "../project-model" }
|
||||
project-model.workspace = true
|
||||
|
||||
# local deps
|
||||
test-utils.workspace = true
|
||||
|
|
|
@ -9,21 +9,21 @@ use chalk_ir::{
|
|||
AdtId, DebruijnIndex, Scalar,
|
||||
};
|
||||
use hir_def::{
|
||||
builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId,
|
||||
GenericDefId, TraitId, TypeAliasId,
|
||||
builtin_type::BuiltinType, DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
|
||||
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
|
||||
GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
|
||||
TyKind,
|
||||
consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime,
|
||||
infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics,
|
||||
Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy,
|
||||
Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ParamKind {
|
||||
Type,
|
||||
Lifetime,
|
||||
Const(Ty),
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,9 @@ impl<D> TyBuilder<D> {
|
|||
ParamKind::Const(ty) => {
|
||||
BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner)
|
||||
}
|
||||
ParamKind::Lifetime => {
|
||||
BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
});
|
||||
this.vec.extend(filler.take(this.remaining()).casted(Interner));
|
||||
assert_eq!(this.remaining(), 0);
|
||||
|
@ -119,6 +122,7 @@ impl<D> TyBuilder<D> {
|
|||
let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x {
|
||||
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
});
|
||||
this.vec.extend(filler.casted(Interner));
|
||||
assert_eq!(this.remaining(), 0);
|
||||
|
@ -130,6 +134,7 @@ impl<D> TyBuilder<D> {
|
|||
self.fill(|x| match x {
|
||||
ParamKind::Type => table.new_type_var().cast(Interner),
|
||||
ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
|
||||
ParamKind::Lifetime => table.new_lifetime_var().cast(Interner),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -142,7 +147,8 @@ impl<D> TyBuilder<D> {
|
|||
fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
|
||||
match (a.data(Interner), e) {
|
||||
(GenericArgData::Ty(_), ParamKind::Type)
|
||||
| (GenericArgData::Const(_), ParamKind::Const(_)) => (),
|
||||
| (GenericArgData::Const(_), ParamKind::Const(_))
|
||||
| (GenericArgData::Lifetime(_), ParamKind::Lifetime) => (),
|
||||
_ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds),
|
||||
}
|
||||
}
|
||||
|
@ -201,10 +207,11 @@ impl TyBuilder<()> {
|
|||
Substitution::from_iter(
|
||||
Interner,
|
||||
params.iter_id().map(|id| match id {
|
||||
either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
either::Either::Right(id) => {
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
unknown_const_as_generic(db.const_param_ty(id)).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -219,11 +226,10 @@ impl TyBuilder<()> {
|
|||
assert!(generics.parent_generics().is_some() == parent_subst.is_some());
|
||||
let params = generics
|
||||
.iter_self()
|
||||
.map(|(id, data)| match data {
|
||||
TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
|
||||
TypeOrConstParamData::ConstParamData(_) => {
|
||||
ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
|
||||
}
|
||||
.map(|(id, _data)| match id {
|
||||
GenericParamId::TypeParamId(_) => ParamKind::Type,
|
||||
GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)),
|
||||
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
|
||||
})
|
||||
.collect();
|
||||
TyBuilder::new((), params, parent_subst)
|
||||
|
|
|
@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
|||
};
|
||||
chalk_ir::Binders::new(binders, bound)
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
let datas = self
|
||||
.db
|
||||
.type_alias_impl_traits(alias)
|
||||
.expect("impl trait id without impl traits");
|
||||
let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
|
||||
let data = &datas.impl_traits[idx];
|
||||
let bound = OpaqueTyDatumBound {
|
||||
bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
|
||||
where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
|
||||
};
|
||||
chalk_ir::Binders::new(binders, bound)
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
|
||||
if let Some((future_trait, future_output)) = self
|
||||
.db
|
||||
|
|
|
@ -268,6 +268,13 @@ impl TyExt for Ty {
|
|||
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
|
||||
})
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
db.type_alias_impl_traits(alias).map(|it| {
|
||||
let data =
|
||||
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
|
||||
|
@ -280,6 +287,13 @@ impl TyExt for Ty {
|
|||
data.substitute(Interner, &opaque_ty.substitution)
|
||||
})
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
db.type_alias_impl_traits(alias).map(|it| {
|
||||
let data =
|
||||
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
data.substitute(Interner, &opaque_ty.substitution)
|
||||
})
|
||||
}
|
||||
// It always has an parameter for Future::Output type.
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
|
||||
};
|
||||
|
|
|
@ -2825,3 +2825,30 @@ fn unsized_local() {
|
|||
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_adt() {
|
||||
check_fail(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
pub enum TagTree {
|
||||
Leaf,
|
||||
Choice(&'static [TagTree]),
|
||||
}
|
||||
const GOAL: TagTree = {
|
||||
const TAG_TREE: TagTree = TagTree::Choice(&[
|
||||
{
|
||||
const VARIANT_TAG_TREE: TagTree = TagTree::Choice(
|
||||
&[
|
||||
TagTree::Leaf,
|
||||
],
|
||||
);
|
||||
VARIANT_TAG_TREE
|
||||
},
|
||||
]);
|
||||
TAG_TREE
|
||||
};
|
||||
"#,
|
||||
|e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use base_db::{
|
|||
use hir_def::{
|
||||
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
|
||||
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
|
||||
LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
|
||||
LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId,
|
||||
};
|
||||
use la_arena::ArenaMap;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -23,9 +23,9 @@ use crate::{
|
|||
layout::{Layout, LayoutError},
|
||||
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
|
||||
mir::{BorrowckResult, MirBody, MirLowerError},
|
||||
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
|
||||
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution,
|
||||
TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
|
||||
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits,
|
||||
InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment,
|
||||
TraitRef, Ty, TyDefId, ValueTyDefId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
|
@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
|
||||
|
||||
#[salsa::invoke(crate::lower::return_type_impl_traits)]
|
||||
fn return_type_impl_traits(
|
||||
&self,
|
||||
def: FunctionId,
|
||||
) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
|
||||
fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>;
|
||||
|
||||
#[salsa::invoke(crate::lower::type_alias_impl_traits)]
|
||||
fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option<Arc<Binders<ImplTraits>>>;
|
||||
|
||||
#[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
|
||||
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
|
||||
|
|
|
@ -11,7 +11,6 @@ use hir_def::{ItemContainerId, Lookup};
|
|||
use hir_expand::name;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustc_pattern_analysis::usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||
use syntax::{ast, AstNode};
|
||||
use tracing::debug;
|
||||
use triomphe::Arc;
|
||||
|
@ -234,13 +233,7 @@ impl ExprValidator {
|
|||
return;
|
||||
}
|
||||
|
||||
let report = match compute_match_usefulness(
|
||||
&cx,
|
||||
m_arms.as_slice(),
|
||||
scrut_ty.clone(),
|
||||
ValidityConstraint::ValidOnly,
|
||||
None,
|
||||
) {
|
||||
let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) {
|
||||
Ok(report) => report,
|
||||
Err(()) => return,
|
||||
};
|
||||
|
@ -282,13 +275,7 @@ impl ExprValidator {
|
|||
continue;
|
||||
}
|
||||
|
||||
let report = match compute_match_usefulness(
|
||||
&cx,
|
||||
&[match_arm],
|
||||
ty.clone(),
|
||||
ValidityConstraint::ValidOnly,
|
||||
None,
|
||||
) {
|
||||
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
debug!(?e, "match usefulness error");
|
||||
|
|
|
@ -8,7 +8,8 @@ use rustc_hash::FxHashMap;
|
|||
use rustc_pattern_analysis::{
|
||||
constructor::{Constructor, ConstructorSet, VariantVisibility},
|
||||
index::IdxContainer,
|
||||
Captures, PrivateUninhabitedField, TypeCx,
|
||||
usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
|
||||
Captures, PatCx, PrivateUninhabitedField,
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use stdx::never;
|
||||
|
@ -59,6 +60,18 @@ impl<'p> MatchCheckCtx<'p> {
|
|||
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
|
||||
}
|
||||
|
||||
pub(crate) fn compute_match_usefulness(
|
||||
&self,
|
||||
arms: &[MatchArm<'p>],
|
||||
scrut_ty: Ty,
|
||||
) -> Result<UsefulnessReport<'p, Self>, ()> {
|
||||
// FIXME: Determine place validity correctly. For now, err on the safe side.
|
||||
let place_validity = PlaceValidity::MaybeInvalid;
|
||||
// Measured to take ~100ms on modern hardware.
|
||||
let complexity_limit = Some(500000);
|
||||
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
|
||||
}
|
||||
|
||||
fn is_uninhabited(&self, ty: &Ty) -> bool {
|
||||
is_ty_uninhabited_from(ty, self.module, self.db)
|
||||
}
|
||||
|
@ -107,15 +120,17 @@ impl<'p> MatchCheckCtx<'p> {
|
|||
}
|
||||
|
||||
pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'p> {
|
||||
let singleton = |pat| vec![pat];
|
||||
let singleton = |pat: DeconstructedPat<'p>| vec![pat.at_index(0)];
|
||||
let ctor;
|
||||
let fields: Vec<_>;
|
||||
let mut fields: Vec<_>;
|
||||
let arity;
|
||||
|
||||
match pat.kind.as_ref() {
|
||||
PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
|
||||
PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
|
||||
ctor = Wildcard;
|
||||
fields = Vec::new();
|
||||
arity = 0;
|
||||
}
|
||||
PatKind::Deref { subpattern } => {
|
||||
ctor = match pat.ty.kind(Interner) {
|
||||
|
@ -128,23 +143,22 @@ impl<'p> MatchCheckCtx<'p> {
|
|||
}
|
||||
};
|
||||
fields = singleton(self.lower_pat(subpattern));
|
||||
arity = 1;
|
||||
}
|
||||
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
|
||||
fields = subpatterns
|
||||
.iter()
|
||||
.map(|pat| {
|
||||
let idx: u32 = pat.field.into_raw().into();
|
||||
self.lower_pat(&pat.pattern).at_index(idx as usize)
|
||||
})
|
||||
.collect();
|
||||
match pat.ty.kind(Interner) {
|
||||
TyKind::Tuple(_, substs) => {
|
||||
ctor = Struct;
|
||||
let mut wilds: Vec<_> = substs
|
||||
.iter(Interner)
|
||||
.map(|arg| arg.assert_ty_ref(Interner).clone())
|
||||
.map(DeconstructedPat::wildcard)
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
let idx: u32 = pat.field.into_raw().into();
|
||||
wilds[idx as usize] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
fields = wilds
|
||||
arity = substs.len(Interner);
|
||||
}
|
||||
TyKind::Adt(adt, substs) if is_box(self.db, adt.0) => {
|
||||
TyKind::Adt(adt, _) if is_box(self.db, adt.0) => {
|
||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
|
||||
|
@ -157,16 +171,9 @@ impl<'p> MatchCheckCtx<'p> {
|
|||
// normally or through box-patterns. We'll have to figure out a proper
|
||||
// solution when we introduce generalized deref patterns. Also need to
|
||||
// prevent mixing of those two options.
|
||||
let pat =
|
||||
subpatterns.iter().find(|pat| pat.field.into_raw() == 0u32.into());
|
||||
let field = if let Some(pat) = pat {
|
||||
self.lower_pat(&pat.pattern)
|
||||
} else {
|
||||
let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
|
||||
DeconstructedPat::wildcard(ty)
|
||||
};
|
||||
fields.retain(|ipat| ipat.idx == 0);
|
||||
ctor = Struct;
|
||||
fields = singleton(field);
|
||||
arity = 1;
|
||||
}
|
||||
&TyKind::Adt(adt, _) => {
|
||||
ctor = match pat.kind.as_ref() {
|
||||
|
@ -181,37 +188,33 @@ impl<'p> MatchCheckCtx<'p> {
|
|||
}
|
||||
};
|
||||
let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap();
|
||||
// Fill a vec with wildcards, then place the fields we have at the right
|
||||
// index.
|
||||
let mut wilds: Vec<_> = self
|
||||
.list_variant_fields(&pat.ty, variant)
|
||||
.map(|(_, ty)| ty)
|
||||
.map(DeconstructedPat::wildcard)
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
let field_id: u32 = pat.field.into_raw().into();
|
||||
wilds[field_id as usize] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
fields = wilds;
|
||||
arity = variant.variant_data(self.db.upcast()).fields().len();
|
||||
}
|
||||
_ => {
|
||||
never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty);
|
||||
ctor = Wildcard;
|
||||
fields = Vec::new();
|
||||
fields.clear();
|
||||
arity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&PatKind::LiteralBool { value } => {
|
||||
ctor = Bool(value);
|
||||
fields = Vec::new();
|
||||
arity = 0;
|
||||
}
|
||||
PatKind::Or { pats } => {
|
||||
ctor = Or;
|
||||
fields = pats.iter().map(|pat| self.lower_pat(pat)).collect();
|
||||
fields = pats
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, pat)| self.lower_pat(pat).at_index(i))
|
||||
.collect();
|
||||
arity = pats.len();
|
||||
}
|
||||
}
|
||||
let data = PatData { db: self.db };
|
||||
DeconstructedPat::new(ctor, fields, pat.ty.clone(), data)
|
||||
DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), data)
|
||||
}
|
||||
|
||||
pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat {
|
||||
|
@ -271,7 +274,7 @@ impl<'p> MatchCheckCtx<'p> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'p> TypeCx for MatchCheckCtx<'p> {
|
||||
impl<'p> PatCx for MatchCheckCtx<'p> {
|
||||
type Error = ();
|
||||
type Ty = Ty;
|
||||
type VariantIdx = EnumVariantId;
|
||||
|
@ -453,7 +456,7 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
|
|||
let variant =
|
||||
pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt));
|
||||
|
||||
let db = pat.data().unwrap().db;
|
||||
let db = pat.data().db;
|
||||
if let Some(variant) = variant {
|
||||
match variant {
|
||||
VariantId::EnumVariantId(v) => {
|
||||
|
@ -475,7 +478,6 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
|
|||
}
|
||||
|
||||
fn complexity_exceeded(&self) -> Result<(), Self::Error> {
|
||||
// FIXME(Nadrieril): make use of the complexity counter.
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -938,18 +938,32 @@ impl HirDisplay for Ty {
|
|||
f.end_location_link();
|
||||
if parameters.len(Interner) > 0 {
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let (parent_params, self_param, type_params, const_params, _impl_trait_params) =
|
||||
generics.provenance_split();
|
||||
let total_len = parent_params + self_param + type_params + const_params;
|
||||
let (
|
||||
parent_params,
|
||||
self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
_impl_trait_params,
|
||||
lifetime_params,
|
||||
) = generics.provenance_split();
|
||||
let total_len =
|
||||
parent_params + self_param + type_params + const_params + lifetime_params;
|
||||
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
|
||||
if total_len > 0 {
|
||||
// `parameters` are in the order of fn's params (including impl traits),
|
||||
// `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
|
||||
// parent's params (those from enclosing impl or trait, if any).
|
||||
let parameters = parameters.as_slice(Interner);
|
||||
let fn_params_len = self_param + type_params + const_params;
|
||||
// This will give slice till last type or const
|
||||
let fn_params = parameters.get(..fn_params_len);
|
||||
let fn_lt_params =
|
||||
parameters.get(fn_params_len..(fn_params_len + lifetime_params));
|
||||
let parent_params = parameters.get(parameters.len() - parent_params..);
|
||||
let params = parent_params.into_iter().chain(fn_params).flatten();
|
||||
let params = parent_params
|
||||
.into_iter()
|
||||
.chain(fn_lt_params)
|
||||
.chain(fn_params)
|
||||
.flatten();
|
||||
write!(f, "<")?;
|
||||
f.write_joined(params, ", ")?;
|
||||
write!(f, ">")?;
|
||||
|
@ -1063,6 +1077,20 @@ impl HirDisplay for Ty {
|
|||
)?;
|
||||
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
let datas =
|
||||
db.type_alias_impl_traits(alias).expect("impl trait id without data");
|
||||
let data =
|
||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
let bounds = data.substitute(Interner, ¶meters);
|
||||
let krate = alias.krate(db.upcast());
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
bounds.skip_binders(),
|
||||
SizedByDefault::Sized { anchor: krate },
|
||||
)?;
|
||||
}
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
|
||||
let future_trait = db
|
||||
.lang_item(body.module(db.upcast()).krate(), LangItem::Future)
|
||||
|
@ -1228,6 +1256,20 @@ impl HirDisplay for Ty {
|
|||
SizedByDefault::Sized { anchor: krate },
|
||||
)?;
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
let datas =
|
||||
db.type_alias_impl_traits(alias).expect("impl trait id without data");
|
||||
let data =
|
||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
let bounds = data.substitute(Interner, &opaque_ty.substitution);
|
||||
let krate = alias.krate(db.upcast());
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
bounds.skip_binders(),
|
||||
SizedByDefault::Sized { anchor: krate },
|
||||
)?;
|
||||
}
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(..) => {
|
||||
write!(f, "{{async block}}")?;
|
||||
}
|
||||
|
@ -1280,8 +1322,17 @@ fn hir_fmt_generics(
|
|||
generic_def: Option<hir_def::GenericDefId>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
let db = f.db;
|
||||
let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len());
|
||||
if parameters.len(Interner) + lifetime_args_count > 0 {
|
||||
if parameters.len(Interner) > 0 {
|
||||
use std::cmp::Ordering;
|
||||
let param_compare =
|
||||
|a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) {
|
||||
(crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => {
|
||||
Ordering::Equal
|
||||
}
|
||||
(crate::GenericArgData::Lifetime(_), _) => Ordering::Less,
|
||||
(_, crate::GenericArgData::Lifetime(_)) => Ordering::Less,
|
||||
(_, _) => Ordering::Equal,
|
||||
};
|
||||
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
|
||||
match generic_def
|
||||
.map(|generic_def_id| db.generic_defaults(generic_def_id))
|
||||
|
@ -1307,6 +1358,11 @@ fn hir_fmt_generics(
|
|||
return true;
|
||||
}
|
||||
}
|
||||
if parameter.lifetime(Interner).map(|it| it.data(Interner))
|
||||
== Some(&crate::LifetimeData::Static)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
let default_parameter = match default_parameters.get(i) {
|
||||
Some(it) => it,
|
||||
None => return true,
|
||||
|
@ -1327,16 +1383,12 @@ fn hir_fmt_generics(
|
|||
} else {
|
||||
parameters.as_slice(Interner)
|
||||
};
|
||||
if !parameters_to_write.is_empty() || lifetime_args_count != 0 {
|
||||
//FIXME: Should handle the ordering of lifetimes when creating substitutions
|
||||
let mut parameters_to_write = parameters_to_write.to_vec();
|
||||
parameters_to_write.sort_by(param_compare);
|
||||
if !parameters_to_write.is_empty() {
|
||||
write!(f, "<")?;
|
||||
let mut first = true;
|
||||
for _ in 0..lifetime_args_count {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(f, "'_")?;
|
||||
}
|
||||
for generic_arg in parameters_to_write {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
|
|
|
@ -25,8 +25,11 @@ pub(crate) mod unify;
|
|||
use std::{convert::identity, iter, ops::Index};
|
||||
|
||||
use chalk_ir::{
|
||||
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
|
||||
Scalar, TyKind, TypeFlags, Variance,
|
||||
cast::Cast,
|
||||
fold::TypeFoldable,
|
||||
interner::HasInterner,
|
||||
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
||||
DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance,
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
|
@ -39,7 +42,7 @@ use hir_def::{
|
|||
layout::Integer,
|
||||
path::{ModPath, Path},
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
type_ref::TypeRef,
|
||||
type_ref::{LifetimeRef, TypeRef},
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
|
||||
TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
};
|
||||
|
@ -53,14 +56,14 @@ use triomphe::Arc;
|
|||
use crate::{
|
||||
db::HirDatabase,
|
||||
fold_tys,
|
||||
infer::coerce::CoerceMany,
|
||||
infer::{coerce::CoerceMany, unify::InferenceTable},
|
||||
lower::ImplTraitLoweringMode,
|
||||
static_lifetime, to_assoc_type_id,
|
||||
traits::FnTrait,
|
||||
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
|
||||
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||
InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment,
|
||||
TraitRef, Ty, TyBuilder, TyExt,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
|
||||
TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
// This lint has a false positive here. See the link below for details.
|
||||
|
@ -422,7 +425,7 @@ pub struct InferenceResult {
|
|||
/// unresolved or missing subpatterns or subpatterns of mismatched types.
|
||||
pub type_of_pat: ArenaMap<PatId, Ty>,
|
||||
pub type_of_binding: ArenaMap<BindingId, Ty>,
|
||||
pub type_of_rpit: ArenaMap<RpitId, Ty>,
|
||||
pub type_of_rpit: ArenaMap<ImplTraitIdx, Ty>,
|
||||
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
|
||||
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
|
||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
||||
|
@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
|
||||
fn collect_const(&mut self, data: &ConstData) {
|
||||
self.return_ty = self.make_ty(&data.type_ref);
|
||||
let return_ty = self.make_ty(&data.type_ref);
|
||||
|
||||
// Constants might be associated items that define ATPITs.
|
||||
self.insert_atpit_coercion_table(iter::once(&return_ty));
|
||||
|
||||
self.return_ty = return_ty;
|
||||
}
|
||||
|
||||
fn collect_static(&mut self, data: &StaticData) {
|
||||
|
@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> {
|
|||
self.write_binding_ty(self_param, ty);
|
||||
}
|
||||
}
|
||||
let mut params_and_ret_tys = Vec::new();
|
||||
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
||||
let ty = self.insert_type_vars(ty);
|
||||
let ty = self.normalize_associated_types_in(ty);
|
||||
|
||||
self.infer_top_pat(*pat, &ty);
|
||||
params_and_ret_tys.push(ty);
|
||||
}
|
||||
let return_ty = &*data.ret_type;
|
||||
|
||||
|
@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> {
|
|||
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
|
||||
// RPIT opaque types use substitution of their parent function.
|
||||
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
|
||||
let result =
|
||||
self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
|
||||
let result = self.insert_inference_vars_for_impl_trait(
|
||||
return_ty,
|
||||
rpits.clone(),
|
||||
fn_placeholders,
|
||||
);
|
||||
let rpits = rpits.skip_binders();
|
||||
for (id, _) in rpits.impl_traits.iter() {
|
||||
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
|
||||
|
@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
self.return_ty = self.normalize_associated_types_in(return_ty);
|
||||
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
|
||||
|
||||
// Functions might be associated items that define ATPITs.
|
||||
// To define an ATPITs, that ATPIT must appear in the function's signatures.
|
||||
// So, it suffices to check for params and return types.
|
||||
params_and_ret_tys.push(self.return_ty.clone());
|
||||
self.insert_atpit_coercion_table(params_and_ret_tys.iter());
|
||||
}
|
||||
|
||||
fn insert_inference_vars_for_rpit<T>(
|
||||
fn insert_inference_vars_for_impl_trait<T>(
|
||||
&mut self,
|
||||
t: T,
|
||||
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
|
||||
fn_placeholders: Substitution,
|
||||
rpits: Arc<chalk_ir::Binders<crate::ImplTraits>>,
|
||||
placeholders: Substitution,
|
||||
) -> T
|
||||
where
|
||||
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
|
||||
|
@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> {
|
|||
};
|
||||
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
|
||||
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
|
||||
ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let bounds =
|
||||
|
@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> {
|
|||
let var = self.table.new_type_var();
|
||||
let var_subst = Substitution::from1(Interner, var.clone());
|
||||
for bound in bounds {
|
||||
let predicate =
|
||||
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
|
||||
let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders);
|
||||
let (var_predicate, binders) =
|
||||
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
|
||||
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
|
||||
let var_predicate = self.insert_inference_vars_for_rpit(
|
||||
let var_predicate = self.insert_inference_vars_for_impl_trait(
|
||||
var_predicate,
|
||||
rpits.clone(),
|
||||
fn_placeholders.clone(),
|
||||
placeholders.clone(),
|
||||
);
|
||||
self.push_obligation(var_predicate.cast(Interner));
|
||||
}
|
||||
|
@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
/// The coercion of a non-inference var into an opaque type should fail,
|
||||
/// but not in the defining sites of the ATPITs.
|
||||
/// In such cases, we insert an proxy inference var for each ATPIT,
|
||||
/// and coerce into it instead of ATPIT itself.
|
||||
///
|
||||
/// The inference var stretagy is effective because;
|
||||
///
|
||||
/// - It can still unify types that coerced into ATPIT
|
||||
/// - We are pushing `impl Trait` bounds into it
|
||||
///
|
||||
/// This function inserts a map that maps the opaque type to that proxy inference var.
|
||||
fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator<Item = &'b Ty>) {
|
||||
struct OpaqueTyCollector<'a, 'b> {
|
||||
table: &'b mut InferenceTable<'a>,
|
||||
opaque_tys: FxHashMap<OpaqueTyId, Ty>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> {
|
||||
type BreakTy = ();
|
||||
|
||||
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
|
||||
self
|
||||
}
|
||||
|
||||
fn interner(&self) -> Interner {
|
||||
Interner
|
||||
}
|
||||
|
||||
fn visit_ty(
|
||||
&mut self,
|
||||
ty: &chalk_ir::Ty<Interner>,
|
||||
outer_binder: DebruijnIndex,
|
||||
) -> std::ops::ControlFlow<Self::BreakTy> {
|
||||
let ty = self.table.resolve_ty_shallow(ty);
|
||||
|
||||
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
|
||||
self.opaque_tys.insert(*id, ty.clone());
|
||||
}
|
||||
|
||||
ty.super_visit_with(self, outer_binder)
|
||||
}
|
||||
}
|
||||
|
||||
// Early return if this is not happening inside the impl block
|
||||
let impl_id = if let Some(impl_id) = self.resolver.impl_def() {
|
||||
impl_id
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let assoc_tys: FxHashSet<_> = self
|
||||
.db
|
||||
.impl_data(impl_id)
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
AssocItemId::TypeAliasId(alias) => Some(*alias),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
if assoc_tys.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut collector =
|
||||
OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() };
|
||||
for ty in tys {
|
||||
ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
|
||||
}
|
||||
let atpit_coercion_table: FxHashMap<_, _> = collector
|
||||
.opaque_tys
|
||||
.into_iter()
|
||||
.filter_map(|(opaque_ty_id, ty)| {
|
||||
if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) =
|
||||
self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
|
||||
{
|
||||
if assoc_tys.contains(&alias_id) {
|
||||
let atpits = self
|
||||
.db
|
||||
.type_alias_impl_traits(alias_id)
|
||||
.expect("Marked as ATPIT but no impl traits!");
|
||||
let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
|
||||
let ty = self.insert_inference_vars_for_impl_trait(
|
||||
ty,
|
||||
atpits,
|
||||
alias_placeholders,
|
||||
);
|
||||
return Some((opaque_ty_id, ty));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !atpit_coercion_table.is_empty() {
|
||||
self.table.atpit_coercion_table = Some(atpit_coercion_table);
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_body(&mut self) {
|
||||
match self.return_coercion {
|
||||
Some(_) => self.infer_return(self.body.body_expr),
|
||||
|
@ -918,6 +1037,12 @@ impl<'a> InferenceContext<'a> {
|
|||
self.result.standard_types.unknown.clone()
|
||||
}
|
||||
|
||||
fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
|
||||
let lt = ctx.lower_lifetime(lifetime_ref);
|
||||
self.insert_type_vars(lt)
|
||||
}
|
||||
|
||||
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
|
||||
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
|
||||
self.table.insert_type_vars_shallow(ty)
|
||||
|
|
|
@ -276,6 +276,23 @@ impl InferenceTable<'_> {
|
|||
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
|
||||
}
|
||||
|
||||
// If we are coercing into an ATPIT, coerce into its proxy inference var, instead.
|
||||
let mut to_ty = to_ty;
|
||||
let _to;
|
||||
if let Some(atpit_table) = &self.atpit_coercion_table {
|
||||
if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) {
|
||||
if !matches!(
|
||||
from_ty.kind(Interner),
|
||||
TyKind::InferenceVar(..) | TyKind::OpaqueType(..)
|
||||
) {
|
||||
if let Some(ty) = atpit_table.get(opaque_ty_id) {
|
||||
_to = ty.clone();
|
||||
to_ty = &_to;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consider coercing the subtype to a DST
|
||||
if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) {
|
||||
return Ok(ret);
|
||||
|
|
|
@ -8,13 +8,12 @@ use std::{
|
|||
use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
generics::TypeOrConstParamData,
|
||||
hir::{
|
||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
||||
path::{GenericArgs, Path},
|
||||
BlockId, FieldId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use stdx::always;
|
||||
|
@ -1816,10 +1815,17 @@ impl InferenceContext<'_> {
|
|||
def_generics: Generics,
|
||||
generic_args: Option<&GenericArgs>,
|
||||
) -> Substitution {
|
||||
let (parent_params, self_params, type_params, const_params, impl_trait_params) =
|
||||
def_generics.provenance_split();
|
||||
let (
|
||||
parent_params,
|
||||
self_params,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
assert_eq!(self_params, 0); // method shouldn't have another Self param
|
||||
let total_len = parent_params + type_params + const_params + impl_trait_params;
|
||||
let total_len =
|
||||
parent_params + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
let mut substs = Vec::with_capacity(total_len);
|
||||
|
||||
// handle provided arguments
|
||||
|
@ -1828,8 +1834,7 @@ impl InferenceContext<'_> {
|
|||
for (arg, kind_id) in generic_args
|
||||
.args
|
||||
.iter()
|
||||
.filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
|
||||
.take(type_params + const_params)
|
||||
.take(type_params + const_params + lifetime_params)
|
||||
.zip(def_generics.iter_id())
|
||||
{
|
||||
if let Some(g) = generic_arg_to_chalk(
|
||||
|
@ -1850,6 +1855,7 @@ impl InferenceContext<'_> {
|
|||
DebruijnIndex::INNERMOST,
|
||||
)
|
||||
},
|
||||
|this, lt_ref| this.make_lifetime(lt_ref),
|
||||
) {
|
||||
substs.push(g);
|
||||
}
|
||||
|
@ -1858,16 +1864,17 @@ impl InferenceContext<'_> {
|
|||
|
||||
// Handle everything else as unknown. This also handles generic arguments for the method's
|
||||
// parent (impl or trait), which should come after those for the method.
|
||||
for (id, data) in def_generics.iter().skip(substs.len()) {
|
||||
match data {
|
||||
TypeOrConstParamData::TypeParamData(_) => {
|
||||
for (id, _data) in def_generics.iter().skip(substs.len()) {
|
||||
match id {
|
||||
GenericParamId::TypeParamId(_) => {
|
||||
substs.push(self.table.new_type_var().cast(Interner))
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(_) => substs.push(
|
||||
self.table
|
||||
.new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id)))
|
||||
.cast(Interner),
|
||||
),
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
substs.push(self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner))
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
substs.push(self.table.new_lifetime_var().cast(Interner))
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(substs.len(), total_len);
|
||||
|
|
|
@ -11,15 +11,15 @@ use stdx::never;
|
|||
|
||||
use crate::{
|
||||
builder::ParamKind,
|
||||
consteval,
|
||||
consteval, error_lifetime,
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
to_chalk_trait_id,
|
||||
utils::generics,
|
||||
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
|
||||
ValueTyDefId,
|
||||
InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
|
||||
TyKind, ValueTyDefId,
|
||||
};
|
||||
|
||||
use super::{ExprOrPatId, InferenceContext, TraitRef};
|
||||
use super::{ExprOrPatId, InferenceContext};
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
|
||||
|
@ -111,6 +111,7 @@ impl InferenceContext<'_> {
|
|||
it.next().unwrap_or_else(|| match x {
|
||||
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
|
||||
ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
})
|
||||
})
|
||||
.build();
|
||||
|
|
|
@ -10,16 +10,18 @@ use chalk_solve::infer::ParameterEnaVariableExt;
|
|||
use either::Either;
|
||||
use ena::unify::UnifyKey;
|
||||
use hir_expand::name;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||
use crate::{
|
||||
consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime,
|
||||
to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
|
||||
DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment,
|
||||
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause,
|
||||
consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts,
|
||||
static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
|
||||
Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData,
|
||||
Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy,
|
||||
ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
TyKind, VariableKind, WhereClause,
|
||||
};
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
|
@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
|
|||
pub(crate) struct InferenceTable<'a> {
|
||||
pub(crate) db: &'a dyn HirDatabase,
|
||||
pub(crate) trait_env: Arc<TraitEnvironment>,
|
||||
pub(crate) atpit_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
|
||||
var_unification_table: ChalkInferenceTable,
|
||||
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
|
||||
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
|
@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> {
|
|||
InferenceTable {
|
||||
db,
|
||||
trait_env,
|
||||
atpit_coercion_table: None,
|
||||
var_unification_table: ChalkInferenceTable::new(),
|
||||
type_variable_table: SmallVec::new(),
|
||||
pending_obligations: Vec::new(),
|
||||
|
@ -803,6 +807,7 @@ impl<'a> InferenceTable<'a> {
|
|||
.fill(|it| {
|
||||
let arg = match it {
|
||||
ParamKind::Type => self.new_type_var(),
|
||||
ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"),
|
||||
ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
|
||||
};
|
||||
arg_tys.push(arg.clone());
|
||||
|
@ -857,11 +862,16 @@ impl<'a> InferenceTable<'a> {
|
|||
where
|
||||
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
|
||||
{
|
||||
fold_tys_and_consts(
|
||||
fold_generic_args(
|
||||
ty,
|
||||
|it, _| match it {
|
||||
Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
|
||||
Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
|
||||
|arg, _| match arg {
|
||||
GenericArgData::Ty(ty) => GenericArgData::Ty(self.insert_type_vars_shallow(ty)),
|
||||
// FIXME: insert lifetime vars once LifetimeData::InferenceVar
|
||||
// and specific error variant for lifetimes start being constructed
|
||||
GenericArgData::Lifetime(lt) => GenericArgData::Lifetime(lt),
|
||||
GenericArgData::Const(c) => {
|
||||
GenericArgData::Const(self.insert_const_vars_shallow(c))
|
||||
}
|
||||
},
|
||||
DebruijnIndex::INNERMOST,
|
||||
)
|
||||
|
|
|
@ -389,6 +389,9 @@ pub fn layout_of_ty_query(
|
|||
let infer = db.infer(func.into());
|
||||
return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
|
||||
return Err(LayoutError::NotImplemented);
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
|
||||
return Err(LayoutError::NotImplemented)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ extern crate rustc_abi;
|
|||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_abi as rustc_abi;
|
||||
|
||||
// No need to use the in-tree one.
|
||||
// Use the crates.io version unconditionally until the API settles enough that we can switch to
|
||||
// using the in-tree one.
|
||||
extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
|
||||
|
||||
mod builder;
|
||||
|
@ -89,8 +90,8 @@ pub use lower::{
|
|||
};
|
||||
pub use mapping::{
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
|
||||
lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id,
|
||||
to_placeholder_idx,
|
||||
lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id,
|
||||
to_foreign_def_id, to_placeholder_idx,
|
||||
};
|
||||
pub use method_resolution::check_orphan_rules;
|
||||
pub use traits::TraitEnvironment;
|
||||
|
@ -334,11 +335,23 @@ pub(crate) fn make_binders_with_count<T: HasInterner<Interner = Interner>>(
|
|||
generics: &Generics,
|
||||
value: T,
|
||||
) -> Binders<T> {
|
||||
let it = generics.iter_id().take(count).map(|id| match id {
|
||||
Either::Left(_) => None,
|
||||
Either::Right(id) => Some(db.const_param_ty(id)),
|
||||
});
|
||||
crate::make_type_and_const_binders(it, value)
|
||||
let it = generics.iter_id().take(count);
|
||||
|
||||
Binders::new(
|
||||
VariableKinds::from_iter(
|
||||
Interner,
|
||||
it.map(|x| match x {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
chalk_ir::VariableKind::Const(db.const_param_ty(id))
|
||||
}
|
||||
hir_def::GenericParamId::TypeParamId(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
|
||||
}),
|
||||
),
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
|
||||
|
@ -584,29 +597,34 @@ impl TypeFoldable<Interner> for CallableSig {
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum ImplTraitId {
|
||||
ReturnTypeImplTrait(hir_def::FunctionId, RpitId),
|
||||
ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
|
||||
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
|
||||
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
|
||||
}
|
||||
impl_intern_value_trivial!(ImplTraitId);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ReturnTypeImplTraits {
|
||||
pub(crate) impl_traits: Arena<ReturnTypeImplTrait>,
|
||||
pub struct ImplTraits {
|
||||
pub(crate) impl_traits: Arena<ImplTrait>,
|
||||
}
|
||||
|
||||
has_interner!(ReturnTypeImplTraits);
|
||||
has_interner!(ImplTraits);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ReturnTypeImplTrait {
|
||||
pub struct ImplTrait {
|
||||
pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
|
||||
}
|
||||
|
||||
pub type RpitId = Idx<ReturnTypeImplTrait>;
|
||||
pub type ImplTraitIdx = Idx<ImplTrait>;
|
||||
|
||||
pub fn static_lifetime() -> Lifetime {
|
||||
LifetimeData::Static.intern(Interner)
|
||||
}
|
||||
|
||||
pub fn error_lifetime() -> Lifetime {
|
||||
static_lifetime()
|
||||
}
|
||||
|
||||
pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
|
||||
t: T,
|
||||
for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
|
||||
|
@ -696,6 +714,55 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
|
|||
t.fold_with(&mut TyFolder(f), binders)
|
||||
}
|
||||
|
||||
pub(crate) fn fold_generic_args<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
|
||||
t: T,
|
||||
f: impl FnMut(GenericArgData, DebruijnIndex) -> GenericArgData,
|
||||
binders: DebruijnIndex,
|
||||
) -> T {
|
||||
use chalk_ir::fold::{TypeFolder, TypeSuperFoldable};
|
||||
#[derive(chalk_derive::FallibleTypeFolder)]
|
||||
#[has_interner(Interner)]
|
||||
struct TyFolder<F: FnMut(GenericArgData, DebruijnIndex) -> GenericArgData>(F);
|
||||
impl<F: FnMut(GenericArgData, DebruijnIndex) -> GenericArgData> TypeFolder<Interner>
|
||||
for TyFolder<F>
|
||||
{
|
||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner> {
|
||||
self
|
||||
}
|
||||
|
||||
fn interner(&self) -> Interner {
|
||||
Interner
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty {
|
||||
let ty = ty.super_fold_with(self.as_dyn(), outer_binder);
|
||||
self.0(GenericArgData::Ty(ty), outer_binder)
|
||||
.intern(Interner)
|
||||
.ty(Interner)
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const {
|
||||
self.0(GenericArgData::Const(c), outer_binder)
|
||||
.intern(Interner)
|
||||
.constant(Interner)
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn fold_lifetime(&mut self, lt: Lifetime, outer_binder: DebruijnIndex) -> Lifetime {
|
||||
let lt = lt.super_fold_with(self.as_dyn(), outer_binder);
|
||||
self.0(GenericArgData::Lifetime(lt), outer_binder)
|
||||
.intern(Interner)
|
||||
.lifetime(Interner)
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
t.fold_with(&mut TyFolder(f), binders)
|
||||
}
|
||||
|
||||
/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
|
||||
/// ensures there are no unbound variables or inference variables anywhere in
|
||||
/// the `t`.
|
||||
|
|
|
@ -24,17 +24,20 @@ use hir_def::{
|
|||
data::adt::StructKind,
|
||||
expander::Expander,
|
||||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
||||
WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
nameres::MacroSubNs,
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
|
||||
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
|
||||
type_ref::{
|
||||
ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
|
||||
},
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
|
||||
GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup,
|
||||
ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId,
|
||||
TypeParamId, UnionId, VariantId,
|
||||
GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId,
|
||||
Lookup, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId,
|
||||
UnionId, VariantId,
|
||||
};
|
||||
use hir_expand::{name::Name, ExpandResult};
|
||||
use intern::Interned;
|
||||
|
@ -52,18 +55,18 @@ use crate::{
|
|||
unknown_const_as_generic,
|
||||
},
|
||||
db::HirDatabase,
|
||||
make_binders,
|
||||
mapping::{from_chalk_trait_id, ToChalk},
|
||||
error_lifetime, make_binders,
|
||||
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
|
||||
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
|
||||
utils::Generics,
|
||||
utils::{
|
||||
all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
|
||||
all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics,
|
||||
InTypeConstIdMetadata,
|
||||
},
|
||||
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
|
||||
FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
|
||||
QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
|
||||
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
|
||||
FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
|
||||
LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
|
||||
QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
|
||||
TyKind, WhereClause,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -76,7 +79,7 @@ enum ImplTraitLoweringState {
|
|||
/// we're grouping the mutable data (the counter and this field) together
|
||||
/// with the immutable context (the references to the DB and resolver).
|
||||
/// Splitting this up would be a possible fix.
|
||||
Opaque(RefCell<Arena<ReturnTypeImplTrait>>),
|
||||
Opaque(RefCell<Arena<ImplTrait>>),
|
||||
Param(Cell<u16>),
|
||||
Variable(Cell<u16>),
|
||||
Disallowed,
|
||||
|
@ -275,9 +278,11 @@ impl<'a> TyLoweringContext<'a> {
|
|||
let inner_ty = self.lower_ty(inner);
|
||||
TyKind::Slice(inner_ty).intern(Interner)
|
||||
}
|
||||
TypeRef::Reference(inner, _, mutability) => {
|
||||
TypeRef::Reference(inner, lifetime, mutability) => {
|
||||
let inner_ty = self.lower_ty(inner);
|
||||
let lifetime = static_lifetime();
|
||||
// FIXME: It should infer the eldided lifetimes instead of stubbing with static
|
||||
let lifetime =
|
||||
lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr));
|
||||
TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
|
||||
.intern(Interner)
|
||||
}
|
||||
|
@ -301,15 +306,18 @@ impl<'a> TyLoweringContext<'a> {
|
|||
TypeRef::ImplTrait(bounds) => {
|
||||
match &self.impl_trait_mode {
|
||||
ImplTraitLoweringState::Opaque(opaque_type_data) => {
|
||||
let func = match self.resolver.generic_def() {
|
||||
Some(GenericDefId::FunctionId(f)) => f,
|
||||
_ => panic!("opaque impl trait lowering in non-function"),
|
||||
let origin = match self.resolver.generic_def() {
|
||||
Some(GenericDefId::FunctionId(it)) => Either::Left(it),
|
||||
Some(GenericDefId::TypeAliasId(it)) => Either::Right(it),
|
||||
_ => panic!(
|
||||
"opaque impl trait lowering must be in function or type alias"
|
||||
),
|
||||
};
|
||||
|
||||
// this dance is to make sure the data is in the right
|
||||
// place even if we encounter more opaque types while
|
||||
// lowering the bounds
|
||||
let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait {
|
||||
let idx = opaque_type_data.borrow_mut().alloc(ImplTrait {
|
||||
bounds: crate::make_single_type_binders(Vec::new()),
|
||||
});
|
||||
// We don't want to lower the bounds inside the binders
|
||||
|
@ -323,13 +331,17 @@ impl<'a> TyLoweringContext<'a> {
|
|||
// away instead of two.
|
||||
let actual_opaque_type_data = self
|
||||
.with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
|
||||
ctx.lower_impl_trait(bounds, func)
|
||||
ctx.lower_impl_trait(bounds, self.resolver.krate())
|
||||
});
|
||||
opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data;
|
||||
|
||||
let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx);
|
||||
let impl_trait_id = origin.either(
|
||||
|f| ImplTraitId::ReturnTypeImplTrait(f, idx),
|
||||
|a| ImplTraitId::AssociatedTypeImplTrait(a, idx),
|
||||
);
|
||||
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
|
||||
let generics = generics(self.db.upcast(), func.into());
|
||||
let generics =
|
||||
generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into()));
|
||||
let parameters = generics.bound_vars_subst(self.db, self.in_binders);
|
||||
TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner)
|
||||
}
|
||||
|
@ -344,13 +356,18 @@ impl<'a> TyLoweringContext<'a> {
|
|||
.filter(|(_, data)| {
|
||||
matches!(
|
||||
data,
|
||||
TypeOrConstParamData::TypeParamData(data)
|
||||
GenericParamDataRef::TypeParamData(data)
|
||||
if data.provenance == TypeParamProvenance::ArgumentImplTrait
|
||||
)
|
||||
})
|
||||
.nth(idx as usize)
|
||||
.map_or(TyKind::Error, |(id, _)| {
|
||||
TyKind::Placeholder(to_placeholder_idx(self.db, id))
|
||||
if let GenericParamId::TypeParamId(id) = id {
|
||||
TyKind::Placeholder(to_placeholder_idx(self.db, id.into()))
|
||||
} else {
|
||||
// we just filtered them out
|
||||
unreachable!("Unexpected lifetime or const argument");
|
||||
}
|
||||
});
|
||||
param.intern(Interner)
|
||||
} else {
|
||||
|
@ -367,11 +384,12 @@ impl<'a> TyLoweringContext<'a> {
|
|||
list_params,
|
||||
const_params,
|
||||
_impl_trait_params,
|
||||
_lifetime_params,
|
||||
) = if let Some(def) = self.resolver.generic_def() {
|
||||
let generics = generics(self.db.upcast(), def);
|
||||
generics.provenance_split()
|
||||
} else {
|
||||
(0, 0, 0, 0, 0)
|
||||
(0, 0, 0, 0, 0, 0)
|
||||
};
|
||||
TyKind::BoundVar(BoundVar::new(
|
||||
self.in_binders,
|
||||
|
@ -808,9 +826,16 @@ impl<'a> TyLoweringContext<'a> {
|
|||
return Substitution::empty(Interner);
|
||||
};
|
||||
let def_generics = generics(self.db.upcast(), def);
|
||||
let (parent_params, self_params, type_params, const_params, impl_trait_params) =
|
||||
def_generics.provenance_split();
|
||||
let item_len = self_params + type_params + const_params + impl_trait_params;
|
||||
let (
|
||||
parent_params,
|
||||
self_params,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
let item_len =
|
||||
self_params + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
let total_len = parent_params + item_len;
|
||||
|
||||
let ty_error = TyKind::Error.intern(Interner).cast(Interner);
|
||||
|
@ -825,7 +850,10 @@ impl<'a> TyLoweringContext<'a> {
|
|||
.take(self_params)
|
||||
{
|
||||
if let Some(id) = def_generic_iter.next() {
|
||||
assert!(id.is_left());
|
||||
assert!(matches!(
|
||||
id,
|
||||
GenericParamId::TypeParamId(_) | GenericParamId::LifetimeParamId(_)
|
||||
));
|
||||
substs.push(x);
|
||||
}
|
||||
}
|
||||
|
@ -858,6 +886,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
&mut (),
|
||||
|_, type_ref| self.lower_ty(type_ref),
|
||||
|_, const_ref, ty| self.lower_const(const_ref, ty),
|
||||
|_, lifetime_ref| self.lower_lifetime(lifetime_ref),
|
||||
) {
|
||||
had_explicit_args = true;
|
||||
substs.push(x);
|
||||
|
@ -867,15 +896,45 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for arg in generic_args
|
||||
.args
|
||||
.iter()
|
||||
.filter(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
.take(lifetime_params)
|
||||
{
|
||||
// Taking into the fact that def_generic_iter will always have lifetimes at the end
|
||||
// Should have some test cases tho to test this behaviour more properly
|
||||
if let Some(id) = def_generic_iter.next() {
|
||||
if let Some(x) = generic_arg_to_chalk(
|
||||
self.db,
|
||||
id,
|
||||
arg,
|
||||
&mut (),
|
||||
|_, type_ref| self.lower_ty(type_ref),
|
||||
|_, const_ref, ty| self.lower_const(const_ref, ty),
|
||||
|_, lifetime_ref| self.lower_lifetime(lifetime_ref),
|
||||
) {
|
||||
had_explicit_args = true;
|
||||
substs.push(x);
|
||||
} else {
|
||||
// Never return a None explicitly
|
||||
never!("Unexpected None by generic_arg_to_chalk");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fill_self_params();
|
||||
}
|
||||
|
||||
// These params include those of parent.
|
||||
let remaining_params: SmallVec<[_; 2]> = def_generic_iter
|
||||
.map(|eid| match eid {
|
||||
Either::Left(_) => ty_error.clone(),
|
||||
Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)),
|
||||
.map(|id| match id {
|
||||
GenericParamId::ConstParamId(x) => {
|
||||
unknown_const_as_generic(self.db.const_param_ty(x))
|
||||
}
|
||||
GenericParamId::TypeParamId(_) => ty_error.clone(),
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(remaining_params.len() + substs.len(), total_len);
|
||||
|
@ -1107,8 +1166,12 @@ impl<'a> TyLoweringContext<'a> {
|
|||
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
|
||||
);
|
||||
if let Some(type_ref) = &binding.type_ref {
|
||||
if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) =
|
||||
(type_ref, &self.impl_trait_mode)
|
||||
if let (
|
||||
TypeRef::ImplTrait(bounds),
|
||||
ImplTraitLoweringState::Param(_)
|
||||
| ImplTraitLoweringState::Variable(_)
|
||||
| ImplTraitLoweringState::Disallowed,
|
||||
) = (type_ref, &self.impl_trait_mode)
|
||||
{
|
||||
for bound in bounds {
|
||||
predicates.extend(
|
||||
|
@ -1270,11 +1333,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_impl_trait(
|
||||
&self,
|
||||
bounds: &[Interned<TypeBound>],
|
||||
func: FunctionId,
|
||||
) -> ReturnTypeImplTrait {
|
||||
fn lower_impl_trait(&self, bounds: &[Interned<TypeBound>], krate: CrateId) -> ImplTrait {
|
||||
cov_mark::hit!(lower_rpit);
|
||||
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
|
||||
let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
||||
|
@ -1284,7 +1343,6 @@ impl<'a> TyLoweringContext<'a> {
|
|||
.collect();
|
||||
|
||||
if !ctx.unsized_types.borrow().contains(&self_ty) {
|
||||
let krate = func.krate(ctx.db.upcast());
|
||||
let sized_trait = ctx
|
||||
.db
|
||||
.lang_item(krate, LangItem::Sized)
|
||||
|
@ -1301,7 +1359,34 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
predicates
|
||||
});
|
||||
ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) }
|
||||
ImplTrait { bounds: crate::make_single_type_binders(predicates) }
|
||||
}
|
||||
|
||||
pub fn lower_lifetime(&self, lifetime: &LifetimeRef) -> Lifetime {
|
||||
match self.resolver.resolve_lifetime(lifetime) {
|
||||
Some(resolution) => match resolution {
|
||||
LifetimeNs::Static => static_lifetime(),
|
||||
LifetimeNs::LifetimeParam(id) => match self.type_param_mode {
|
||||
ParamLoweringMode::Placeholder => {
|
||||
LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id))
|
||||
}
|
||||
ParamLoweringMode::Variable => {
|
||||
let generics = generics(
|
||||
self.db.upcast(),
|
||||
self.resolver.generic_def().expect("generics in scope"),
|
||||
);
|
||||
let idx = match generics.lifetime_idx(id) {
|
||||
None => return error_lifetime(),
|
||||
Some(idx) => idx,
|
||||
};
|
||||
|
||||
LifetimeData::BoundVar(BoundVar::new(self.in_binders, idx))
|
||||
}
|
||||
}
|
||||
.intern(Interner),
|
||||
},
|
||||
None => error_lifetime(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1685,7 +1770,7 @@ pub(crate) fn generic_defaults_query(
|
|||
|
||||
let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| {
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => {
|
||||
GenericParamDataRef::TypeParamData(p) => {
|
||||
let mut ty =
|
||||
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
|
||||
// Each default can only refer to previous parameters.
|
||||
|
@ -1694,13 +1779,13 @@ pub(crate) fn generic_defaults_query(
|
|||
ty = fallback_bound_vars(ty, idx, parent_start_idx);
|
||||
crate::make_binders(db, &generic_params, ty.cast(Interner))
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(p) => {
|
||||
GenericParamDataRef::ConstParamData(p) => {
|
||||
let GenericParamId::ConstParamId(id) = id else {
|
||||
unreachable!("Unexpected lifetime or type argument")
|
||||
};
|
||||
|
||||
let mut val = p.default.as_ref().map_or_else(
|
||||
|| {
|
||||
unknown_const_as_generic(
|
||||
db.const_param_ty(ConstParamId::from_unchecked(id)),
|
||||
)
|
||||
},
|
||||
|| unknown_const_as_generic(db.const_param_ty(id)),
|
||||
|c| {
|
||||
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
|
||||
c.cast(Interner)
|
||||
|
@ -1710,6 +1795,10 @@ pub(crate) fn generic_defaults_query(
|
|||
val = fallback_bound_vars(val, idx, parent_start_idx);
|
||||
make_binders(db, &generic_params, val)
|
||||
}
|
||||
GenericParamDataRef::LifetimeParamData(_) => {
|
||||
// using static because it requires defaults
|
||||
make_binders(db, &generic_params, static_lifetime().cast(Interner))
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -1726,8 +1815,9 @@ pub(crate) fn generic_defaults_recover(
|
|||
// we still need one default per parameter
|
||||
let defaults = Arc::from_iter(generic_params.iter_id().map(|id| {
|
||||
let val = match id {
|
||||
Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
|
||||
GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner),
|
||||
};
|
||||
crate::make_binders(db, &generic_params, val)
|
||||
}));
|
||||
|
@ -1869,6 +1959,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
|
|||
let generics = generics(db.upcast(), t.into());
|
||||
let resolver = t.resolver(db.upcast());
|
||||
let ctx = TyLoweringContext::new(db, &resolver, t.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let type_alias_data = db.type_alias_data(t);
|
||||
if type_alias_data.is_extern {
|
||||
|
@ -2029,7 +2120,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
|
|||
pub(crate) fn return_type_impl_traits(
|
||||
db: &dyn HirDatabase,
|
||||
def: hir_def::FunctionId,
|
||||
) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
|
||||
) -> Option<Arc<Binders<ImplTraits>>> {
|
||||
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
|
||||
let data = db.function_data(def);
|
||||
let resolver = def.resolver(db.upcast());
|
||||
|
@ -2038,7 +2129,7 @@ pub(crate) fn return_type_impl_traits(
|
|||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let _ret = ctx_ret.lower_ty(&data.ret_type);
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let return_type_impl_traits = ReturnTypeImplTraits {
|
||||
let return_type_impl_traits = ImplTraits {
|
||||
impl_traits: match ctx_ret.impl_trait_mode {
|
||||
ImplTraitLoweringState::Opaque(x) => x.into_inner(),
|
||||
_ => unreachable!(),
|
||||
|
@ -2051,6 +2142,32 @@ pub(crate) fn return_type_impl_traits(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_alias_impl_traits(
|
||||
db: &dyn HirDatabase,
|
||||
def: hir_def::TypeAliasId,
|
||||
) -> Option<Arc<Binders<ImplTraits>>> {
|
||||
let data = db.type_alias_data(def);
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let ctx = TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
if let Some(type_ref) = &data.type_ref {
|
||||
let _ty = ctx.lower_ty(type_ref);
|
||||
}
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let type_alias_impl_traits = ImplTraits {
|
||||
impl_traits: match ctx.impl_trait_mode {
|
||||
ImplTraitLoweringState::Opaque(x) => x.into_inner(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
if type_alias_impl_traits.impl_traits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability {
|
||||
match m {
|
||||
hir_def::type_ref::Mutability::Shared => Mutability::Not,
|
||||
|
@ -2064,23 +2181,29 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
|
|||
/// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime.
|
||||
pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
db: &dyn HirDatabase,
|
||||
kind_id: Either<TypeParamId, ConstParamId>,
|
||||
kind_id: GenericParamId,
|
||||
arg: &'a GenericArg,
|
||||
this: &mut T,
|
||||
for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
|
||||
for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
|
||||
for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
|
||||
) -> Option<crate::GenericArg> {
|
||||
let kind = match kind_id {
|
||||
Either::Left(_) => ParamKind::Type,
|
||||
Either::Right(id) => {
|
||||
GenericParamId::TypeParamId(_) => ParamKind::Type,
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
let ty = db.const_param_ty(id);
|
||||
ParamKind::Const(ty)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
|
||||
};
|
||||
Some(match (arg, kind) {
|
||||
(GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner),
|
||||
(GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
|
||||
(GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
|
||||
for_lifetime(this, lifetime_ref).cast(Interner)
|
||||
}
|
||||
(GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
(GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
(GenericArg::Type(t), ParamKind::Const(c_ty)) => {
|
||||
// We want to recover simple idents, which parser detects them
|
||||
// as types. Maybe here is not the best place to do it, but
|
||||
|
@ -2096,7 +2219,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
|
|||
}
|
||||
unknown_const_as_generic(c_ty)
|
||||
}
|
||||
(GenericArg::Lifetime(_), _) => return None,
|
||||
(GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty),
|
||||
(GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
(GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -151,6 +151,14 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L
|
|||
db.lookup_intern_lifetime_param_id(interned_id)
|
||||
}
|
||||
|
||||
pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex {
|
||||
let interned_id = db.intern_lifetime_param_id(id);
|
||||
PlaceholderIndex {
|
||||
ui: chalk_ir::UniverseIndex::ROOT,
|
||||
idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId {
|
||||
chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id))
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use base_db::{CrateId, Edition};
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
|
||||
use hir_def::{
|
||||
data::{adt::StructFlags, ImplData},
|
||||
|
@ -15,6 +15,7 @@ use hir_def::{
|
|||
use hir_expand::name::Name;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::Edition;
|
||||
use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -643,7 +644,7 @@ pub fn is_dyn_method(
|
|||
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
|
||||
return None;
|
||||
};
|
||||
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||
let trait_params = db.generic_params(trait_id.into()).len();
|
||||
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||
let trait_ref = TraitRef {
|
||||
trait_id: to_chalk_trait_id(trait_id),
|
||||
|
@ -685,7 +686,7 @@ pub(crate) fn lookup_impl_method_query(
|
|||
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
|
||||
return (func, fn_subst);
|
||||
};
|
||||
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||
let trait_params = db.generic_params(trait_id.into()).len();
|
||||
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||
let trait_ref = TraitRef {
|
||||
trait_id: to_chalk_trait_id(trait_id),
|
||||
|
@ -966,7 +967,7 @@ pub fn iterate_method_candidates_dyn(
|
|||
// the methods by autoderef order of *receiver types*, not *self
|
||||
// types*.
|
||||
|
||||
let mut table = InferenceTable::new(db, env.clone());
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty.clone());
|
||||
let deref_chain = autoderef_method_receiver(&mut table, ty);
|
||||
|
||||
|
@ -1044,7 +1045,7 @@ fn iterate_method_candidates_with_autoref(
|
|||
let ref_muted = Canonical {
|
||||
value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
|
||||
.intern(Interner),
|
||||
binders: receiver_ty.binders.clone(),
|
||||
binders: receiver_ty.binders,
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut))
|
||||
|
@ -1060,7 +1061,7 @@ fn iterate_method_candidates_by_receiver(
|
|||
name: Option<&Name>,
|
||||
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
|
||||
let receiver_ty = table.instantiate_canonical(receiver_ty);
|
||||
// We're looking for methods with *receiver* type receiver_ty. These could
|
||||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
// that, including raw derefs.
|
||||
|
@ -1456,7 +1457,7 @@ fn is_valid_trait_method_candidate(
|
|||
if let Some(receiver_ty) = receiver_ty {
|
||||
check_that!(data.has_self_param());
|
||||
|
||||
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
|
||||
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst))
|
||||
.fill_with_inference_vars(table)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -1931,7 +1931,11 @@ impl Evaluator<'_> {
|
|||
ty: &Ty,
|
||||
locals: &Locals,
|
||||
mm: &mut ComplexMemoryMap,
|
||||
stack_depth_limit: usize,
|
||||
) -> Result<()> {
|
||||
if stack_depth_limit.checked_sub(1).is_none() {
|
||||
return Err(MirEvalError::StackOverflow);
|
||||
}
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Ref(_, _, t) => {
|
||||
let size = this.size_align_of(t, locals)?;
|
||||
|
@ -1970,7 +1974,14 @@ impl Evaluator<'_> {
|
|||
if let Some(ty) = check_inner {
|
||||
for i in 0..count {
|
||||
let offset = element_size * i;
|
||||
rec(this, &b[offset..offset + element_size], ty, locals, mm)?;
|
||||
rec(
|
||||
this,
|
||||
&b[offset..offset + element_size],
|
||||
ty,
|
||||
locals,
|
||||
mm,
|
||||
stack_depth_limit - 1,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1984,7 +1995,14 @@ impl Evaluator<'_> {
|
|||
let size = this.size_of_sized(inner, locals, "inner of array")?;
|
||||
for i in 0..len {
|
||||
let offset = i * size;
|
||||
rec(this, &bytes[offset..offset + size], inner, locals, mm)?;
|
||||
rec(
|
||||
this,
|
||||
&bytes[offset..offset + size],
|
||||
inner,
|
||||
locals,
|
||||
mm,
|
||||
stack_depth_limit - 1,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
chalk_ir::TyKind::Tuple(_, subst) => {
|
||||
|
@ -1993,7 +2011,14 @@ impl Evaluator<'_> {
|
|||
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
|
||||
let offset = layout.fields.offset(id).bytes_usize();
|
||||
let size = this.layout(ty)?.size.bytes_usize();
|
||||
rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
|
||||
rec(
|
||||
this,
|
||||
&bytes[offset..offset + size],
|
||||
ty,
|
||||
locals,
|
||||
mm,
|
||||
stack_depth_limit - 1,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
|
||||
|
@ -2008,7 +2033,14 @@ impl Evaluator<'_> {
|
|||
.bytes_usize();
|
||||
let ty = &field_types[f].clone().substitute(Interner, subst);
|
||||
let size = this.layout(ty)?.size.bytes_usize();
|
||||
rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
|
||||
rec(
|
||||
this,
|
||||
&bytes[offset..offset + size],
|
||||
ty,
|
||||
locals,
|
||||
mm,
|
||||
stack_depth_limit - 1,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
AdtId::EnumId(e) => {
|
||||
|
@ -2027,7 +2059,14 @@ impl Evaluator<'_> {
|
|||
l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
|
||||
let ty = &field_types[f].clone().substitute(Interner, subst);
|
||||
let size = this.layout(ty)?.size.bytes_usize();
|
||||
rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
|
||||
rec(
|
||||
this,
|
||||
&bytes[offset..offset + size],
|
||||
ty,
|
||||
locals,
|
||||
mm,
|
||||
stack_depth_limit - 1,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2038,7 +2077,7 @@ impl Evaluator<'_> {
|
|||
Ok(())
|
||||
}
|
||||
let mut mm = ComplexMemoryMap::default();
|
||||
rec(self, bytes, ty, locals, &mut mm)?;
|
||||
rec(self, bytes, ty, locals, &mut mm, self.stack_depth_limit - 1)?;
|
||||
Ok(mm)
|
||||
}
|
||||
|
||||
|
@ -2317,7 +2356,7 @@ impl Evaluator<'_> {
|
|||
|
||||
fn exec_fn_with_args(
|
||||
&mut self,
|
||||
def: FunctionId,
|
||||
mut def: FunctionId,
|
||||
args: &[IntervalAndTy],
|
||||
generic_args: Substitution,
|
||||
locals: &Locals,
|
||||
|
@ -2335,6 +2374,9 @@ impl Evaluator<'_> {
|
|||
)? {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? {
|
||||
def = redirect_def;
|
||||
}
|
||||
let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
|
||||
match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
|
||||
MirOrDynIndex::Dyn(self_ty_idx) => {
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::mir::eval::{
|
|||
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
|
||||
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
|
||||
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
|
||||
ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
|
||||
Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
mod simd;
|
||||
|
@ -158,6 +158,25 @@ impl Evaluator<'_> {
|
|||
Ok(false)
|
||||
}
|
||||
|
||||
pub(super) fn detect_and_redirect_special_function(
|
||||
&mut self,
|
||||
def: FunctionId,
|
||||
) -> Result<Option<FunctionId>> {
|
||||
// `PanicFmt` is redirected to `ConstPanicFmt`
|
||||
if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) {
|
||||
let resolver =
|
||||
self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast());
|
||||
|
||||
let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) =
|
||||
self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt)
|
||||
else {
|
||||
not_supported!("const_panic_fmt lang item not found or not a function");
|
||||
};
|
||||
return Ok(Some(const_panic_fmt));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Clone has special impls for tuples and function pointers
|
||||
fn exec_clone(
|
||||
&mut self,
|
||||
|
@ -291,9 +310,14 @@ impl Evaluator<'_> {
|
|||
use LangItem::*;
|
||||
let candidate = self.db.lang_attr(def.into())?;
|
||||
// We want to execute these functions with special logic
|
||||
if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
|
||||
// `PanicFmt` is not detected here as it's redirected later.
|
||||
if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
|
||||
return Some(candidate);
|
||||
}
|
||||
if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() {
|
||||
// `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
|
||||
return Some(LangItem::BeginPanic);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -309,43 +333,6 @@ impl Evaluator<'_> {
|
|||
let mut args = args.iter();
|
||||
match it {
|
||||
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
|
||||
PanicFmt => {
|
||||
let message = (|| {
|
||||
let resolver = self
|
||||
.db
|
||||
.crate_def_map(self.crate_id)
|
||||
.crate_root()
|
||||
.resolver(self.db.upcast());
|
||||
let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
|
||||
self.db.upcast(),
|
||||
&hir_def::path::Path::from_known_path_with_no_generic(
|
||||
ModPath::from_segments(
|
||||
hir_expand::mod_path::PathKind::Abs,
|
||||
[name![std], name![fmt], name![format]],
|
||||
),
|
||||
),
|
||||
) else {
|
||||
not_supported!("std::fmt::format not found");
|
||||
};
|
||||
let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else {
|
||||
not_supported!("std::fmt::format is not a function")
|
||||
};
|
||||
let interval = self.interpret_mir(
|
||||
self.db
|
||||
.mir_body(format_fn.into())
|
||||
.map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
|
||||
args.map(|x| IntervalOrOwned::Owned(x.clone())),
|
||||
)?;
|
||||
let message_string = interval.get(self)?;
|
||||
let addr =
|
||||
Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
|
||||
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
|
||||
Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?)
|
||||
.into_owned())
|
||||
})()
|
||||
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
|
||||
Err(MirEvalError::Panic(message))
|
||||
}
|
||||
SliceLen => {
|
||||
let arg = args.next().ok_or(MirEvalError::InternalError(
|
||||
"argument of <[T]>::len() is not provided".into(),
|
||||
|
|
|
@ -82,6 +82,9 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
|
|||
};
|
||||
filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
|
||||
not_supported!("associated type impl trait");
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
|
||||
not_supported!("async block impl trait");
|
||||
}
|
||||
|
@ -181,8 +184,16 @@ impl Filler<'_> {
|
|||
self.generics
|
||||
.as_ref()
|
||||
.and_then(|it| it.iter().nth(b.index))
|
||||
.unwrap()
|
||||
.0,
|
||||
.and_then(|(id, _)| match id {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
Some(hir_def::TypeOrConstParamId::from(id))
|
||||
}
|
||||
hir_def::GenericParamId::TypeParamId(id) => {
|
||||
Some(hir_def::TypeOrConstParamId::from(id))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap(),
|
||||
self.subst.clone(),
|
||||
)
|
||||
})?
|
||||
|
|
|
@ -298,7 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
|||
if let Some(syntax_ptr) = body_source_map.self_param_syntax() {
|
||||
let root = db.parse_or_expand(syntax_ptr.file_id);
|
||||
let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone());
|
||||
types.push((node.clone(), ty));
|
||||
types.push((node, ty));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
|
|||
trait Foo<'a> {}
|
||||
|
||||
fn foo(foo: &dyn for<'a> Foo<'a>) {}
|
||||
// ^^^ &dyn Foo
|
||||
// ^^^ &dyn Foo<'static>
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1109,7 +1109,7 @@ fn var_args() {
|
|||
#[lang = "va_list"]
|
||||
pub struct VaListImpl<'f>;
|
||||
fn my_fn(foo: ...) {}
|
||||
//^^^ VaListImpl<'_>
|
||||
//^^^ VaListImpl<'static>
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -896,13 +896,13 @@ fn flush(&self) {
|
|||
"#,
|
||||
expect![[r#"
|
||||
123..127 'self': &Mutex<T>
|
||||
150..152 '{}': MutexGuard<'_, T>
|
||||
150..152 '{}': MutexGuard<'static, T>
|
||||
234..238 'self': &{unknown}
|
||||
240..290 '{ ...()); }': ()
|
||||
250..251 'w': &Mutex<BufWriter>
|
||||
276..287 '*(w.lock())': BufWriter
|
||||
278..279 'w': &Mutex<BufWriter>
|
||||
278..286 'w.lock()': MutexGuard<'_, BufWriter>
|
||||
278..286 'w.lock()': MutexGuard<'static, BufWriter>
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3092,7 +3092,7 @@ fn main() {
|
|||
389..394 'boxed': Box<Foo<i32>>
|
||||
389..406 'boxed....nner()': &i32
|
||||
416..421 'good1': &i32
|
||||
424..438 'Foo::get_inner': fn get_inner<i32>(&Box<Foo<i32>>) -> &i32
|
||||
424..438 'Foo::get_inner': fn get_inner<i32, 'static>(&Box<Foo<i32>>) -> &i32
|
||||
424..446 'Foo::g...boxed)': &i32
|
||||
439..445 '&boxed': &Box<Foo<i32>>
|
||||
440..445 'boxed': Box<Foo<i32>>
|
||||
|
@ -3100,7 +3100,7 @@ fn main() {
|
|||
464..469 'boxed': Box<Foo<i32>>
|
||||
464..480 'boxed....self()': &Foo<i32>
|
||||
490..495 'good2': &Foo<i32>
|
||||
498..511 'Foo::get_self': fn get_self<i32>(&Box<Foo<i32>>) -> &Foo<i32>
|
||||
498..511 'Foo::get_self': fn get_self<i32, 'static>(&Box<Foo<i32>>) -> &Foo<i32>
|
||||
498..519 'Foo::g...boxed)': &Foo<i32>
|
||||
512..518 '&boxed': &Box<Foo<i32>>
|
||||
513..518 'boxed': Box<Foo<i32>>
|
||||
|
@ -3659,7 +3659,7 @@ fn main() {
|
|||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_>
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
@ -1278,6 +1278,40 @@ fn bar() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_assoc_impl_trait() {
|
||||
check_infer(
|
||||
r#"
|
||||
trait Outer {
|
||||
type Item;
|
||||
}
|
||||
|
||||
trait Inner { }
|
||||
|
||||
fn foo<T: Outer<Item = impl Inner>>(baz: T) {
|
||||
}
|
||||
|
||||
impl Outer for usize {
|
||||
type Item = usize;
|
||||
}
|
||||
|
||||
impl Inner for usize {}
|
||||
|
||||
fn main() {
|
||||
foo(2);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
85..88 'baz': T
|
||||
93..96 '{ }': ()
|
||||
182..197 '{ foo(2); }': ()
|
||||
188..191 'foo': fn foo<usize>(usize)
|
||||
188..194 'foo(2)': ()
|
||||
192..193 '2': usize
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_return_pos_impl_trait() {
|
||||
cov_mark::check!(lower_rpit);
|
||||
|
@ -4655,3 +4689,78 @@ fn f<T: Send, U>() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_impl_trait() {
|
||||
check_types(
|
||||
r#"
|
||||
trait Foo {}
|
||||
struct S1;
|
||||
impl Foo for S1 {}
|
||||
|
||||
trait Bar {
|
||||
type Item;
|
||||
fn bar(&self) -> Self::Item;
|
||||
}
|
||||
struct S2;
|
||||
impl Bar for S2 {
|
||||
type Item = impl Foo;
|
||||
fn bar(&self) -> Self::Item {
|
||||
S1
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let x = S2.bar();
|
||||
//^ impl Foo + ?Sized
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_impl_traits_complex() {
|
||||
check_types(
|
||||
r#"
|
||||
struct Unary<T>(T);
|
||||
struct Binary<T, U>(T, U);
|
||||
|
||||
trait Foo {}
|
||||
struct S1;
|
||||
impl Foo for S1 {}
|
||||
|
||||
trait Bar {
|
||||
type Item;
|
||||
fn bar(&self) -> Unary<Self::Item>;
|
||||
}
|
||||
struct S2;
|
||||
impl Bar for S2 {
|
||||
type Item = Unary<impl Foo>;
|
||||
fn bar(&self) -> Unary<<Self as Bar>::Item> {
|
||||
Unary(Unary(S1))
|
||||
}
|
||||
}
|
||||
|
||||
trait Baz {
|
||||
type Target1;
|
||||
type Target2;
|
||||
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
|
||||
}
|
||||
struct S3;
|
||||
impl Baz for S3 {
|
||||
type Target1 = impl Foo;
|
||||
type Target2 = Unary<impl Bar>;
|
||||
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
|
||||
Binary(S1, Unary(S2))
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let x = S3.baz();
|
||||
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
|
||||
let y = x.1.0.bar();
|
||||
//^ Unary<Bar::Item<impl Bar + ?Sized>>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,18 +9,18 @@ use chalk_ir::{
|
|||
fold::{FallibleTypeFolder, Shift},
|
||||
BoundVar, DebruijnIndex,
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
generics::{
|
||||
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
||||
WherePredicateTypeTarget,
|
||||
GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
|
||||
TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
resolver::{HasResolver, TypeNs},
|
||||
type_ref::{TraitBoundModifier, TypeRef},
|
||||
ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, Lookup,
|
||||
OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
|
||||
ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ItemContainerId,
|
||||
LifetimeParamId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId,
|
||||
TypeParamId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
|
@ -270,64 +270,130 @@ pub(crate) struct Generics {
|
|||
}
|
||||
|
||||
impl Generics {
|
||||
pub(crate) fn iter_id(&self) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + '_ {
|
||||
self.iter().map(|(id, data)| match data {
|
||||
TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)),
|
||||
TypeOrConstParamData::ConstParamData(_) => {
|
||||
Either::Right(ConstParamId::from_unchecked(id))
|
||||
}
|
||||
})
|
||||
pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
|
||||
self.iter().map(|(id, _)| id)
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of self, then parent.
|
||||
pub(crate) fn iter<'a>(
|
||||
&'a self,
|
||||
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
|
||||
let to_toc_id = |it: &'a Generics| {
|
||||
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
|
||||
let from_toc_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a TypeOrConstParamData)| {
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.params.iter().map(to_toc_id(self)).chain(self.iter_parent())
|
||||
|
||||
let from_lt_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a LifetimeParamData)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let lt_iter = self.params.iter_lt().map(from_lt_id(self));
|
||||
self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent())
|
||||
}
|
||||
|
||||
/// Iterate over types and const params without parent params.
|
||||
pub(crate) fn iter_self<'a>(
|
||||
&'a self,
|
||||
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
|
||||
let to_toc_id = |it: &'a Generics| {
|
||||
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
|
||||
let from_toc_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a TypeOrConstParamData)| {
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.params.iter().map(to_toc_id(self))
|
||||
|
||||
let from_lt_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a LifetimeParamData)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self)))
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of parent.
|
||||
pub(crate) fn iter_parent(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &TypeOrConstParamData)> {
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
pub(crate) fn iter_parent<'a>(
|
||||
&'a self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
|
||||
self.parent_generics().into_iter().flat_map(|it| {
|
||||
let to_toc_id =
|
||||
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p);
|
||||
it.params.iter().map(to_toc_id)
|
||||
let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| {
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
};
|
||||
let lt_iter = it.params.iter_lt().map(from_lt_id);
|
||||
it.params.iter().map(from_toc_id).chain(lt_iter)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns total number of generic parameters in scope, including those from parent.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
let parent = self.parent_generics().map_or(0, Generics::len);
|
||||
let child = self.params.type_or_consts.len();
|
||||
let child = self.params.len();
|
||||
parent + child
|
||||
}
|
||||
|
||||
/// Returns numbers of generic parameters excluding those from parent.
|
||||
/// Returns numbers of generic parameters and lifetimes excluding those from parent.
|
||||
pub(crate) fn len_self(&self) -> usize {
|
||||
self.params.len()
|
||||
}
|
||||
|
||||
/// Returns number of generic parameter excluding those from parent
|
||||
fn len_params(&self) -> usize {
|
||||
self.params.type_or_consts.len()
|
||||
}
|
||||
|
||||
/// (parent total, self param, type param list, const param list, impl trait)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) {
|
||||
/// (parent total, self param, type params, const params, impl trait list, lifetimes)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) {
|
||||
let mut self_params = 0;
|
||||
let mut type_params = 0;
|
||||
let mut impl_trait_params = 0;
|
||||
let mut const_params = 0;
|
||||
let mut lifetime_params = 0;
|
||||
self.params.iter().for_each(|(_, data)| match data {
|
||||
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
|
||||
TypeParamProvenance::TypeParamList => type_params += 1,
|
||||
|
@ -337,8 +403,10 @@ impl Generics {
|
|||
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
|
||||
});
|
||||
|
||||
self.params.iter_lt().for_each(|(_, _)| lifetime_params += 1);
|
||||
|
||||
let parent_len = self.parent_generics().map_or(0, Generics::len);
|
||||
(parent_len, self_params, type_params, const_params, impl_trait_params)
|
||||
(parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params)
|
||||
}
|
||||
|
||||
pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
|
||||
|
@ -358,6 +426,26 @@ impl Generics {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> {
|
||||
Some(self.find_lifetime(lifetime)?.0)
|
||||
}
|
||||
|
||||
fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> {
|
||||
if lifetime.parent == self.def {
|
||||
let (idx, (_local_id, data)) = self
|
||||
.params
|
||||
.iter_lt()
|
||||
.enumerate()
|
||||
.find(|(_, (idx, _))| *idx == lifetime.local_id)?;
|
||||
|
||||
Some((self.len_params() + idx, data))
|
||||
} else {
|
||||
self.parent_generics()
|
||||
.and_then(|g| g.find_lifetime(lifetime))
|
||||
.map(|(idx, data)| (self.len_self() + idx, data))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parent_generics(&self) -> Option<&Generics> {
|
||||
self.parent_generics.as_deref()
|
||||
}
|
||||
|
@ -371,10 +459,15 @@ impl Generics {
|
|||
Substitution::from_iter(
|
||||
Interner,
|
||||
self.iter_id().enumerate().map(|(idx, id)| match id {
|
||||
Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner),
|
||||
Either::Right(id) => BoundVar::new(debruijn, idx)
|
||||
GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx)
|
||||
.to_const(Interner, db.const_param_ty(id))
|
||||
.cast(Interner),
|
||||
GenericParamId::TypeParamId(_) => {
|
||||
BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -384,12 +477,15 @@ impl Generics {
|
|||
Substitution::from_iter(
|
||||
Interner,
|
||||
self.iter_id().map(|id| match id {
|
||||
Either::Left(id) => {
|
||||
GenericParamId::TypeParamId(id) => {
|
||||
crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
Either::Right(id) => crate::to_placeholder_idx(db, id.into())
|
||||
GenericParamId::ConstParamId(id) => crate::to_placeholder_idx(db, id.into())
|
||||
.to_const(Interner, db.const_param_ty(id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(id) => {
|
||||
crate::lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -186,18 +186,29 @@ impl HirDisplay for Struct {
|
|||
}
|
||||
StructKind::Record => {
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
let fields = self.fields(f.db);
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
if fields.is_empty() {
|
||||
f.write_str("{}")?;
|
||||
} else {
|
||||
f.write_str("{\n")?;
|
||||
for field in self.fields(f.db) {
|
||||
f.write_str(" ")?;
|
||||
field.hir_fmt(f)?;
|
||||
f.write_str(",\n")?;
|
||||
if let Some(limit) = f.entity_limit {
|
||||
let fields = self.fields(f.db);
|
||||
let count = fields.len().min(limit);
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
if count == 0 {
|
||||
if fields.is_empty() {
|
||||
f.write_str("{}")?;
|
||||
} else {
|
||||
f.write_str("{ /* … */ }")?;
|
||||
}
|
||||
} else {
|
||||
f.write_str(" {\n")?;
|
||||
for field in &fields[..count] {
|
||||
f.write_str(" ")?;
|
||||
field.hir_fmt(f)?;
|
||||
f.write_str(",\n")?;
|
||||
}
|
||||
|
||||
if fields.len() > count {
|
||||
f.write_str(" /* … */\n")?;
|
||||
}
|
||||
f.write_str("}")?;
|
||||
}
|
||||
f.write_str("}")?;
|
||||
}
|
||||
}
|
||||
StructKind::Unit => _ = write_where_clause(def_id, f)?,
|
||||
|
|
|
@ -38,7 +38,7 @@ mod display;
|
|||
use std::{iter, mem::discriminant, ops::ControlFlow};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId};
|
||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body::{BodyDiagnostic, SyntheticSyntax},
|
||||
|
@ -65,7 +65,7 @@ use hir_ty::{
|
|||
consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
|
||||
db::InternedClosure,
|
||||
diagnostics::BodyValidationDiagnostic,
|
||||
known_const_to_ast,
|
||||
error_lifetime, known_const_to_ast,
|
||||
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
|
||||
method_resolution::{self, TyFingerprint},
|
||||
mir::{interpret_mir, MutBorrowKind},
|
||||
|
@ -79,6 +79,7 @@ use hir_ty::{
|
|||
use itertools::Itertools;
|
||||
use nameres::diagnostics::DefDiagnosticKind;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::Edition;
|
||||
use stdx::{impl_from, never};
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs as _, HasName},
|
||||
|
@ -971,7 +972,7 @@ fn precise_macro_call_location(
|
|||
MacroKind::ProcMacro,
|
||||
)
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
// Compute the precise location of the macro name's token in the derive
|
||||
// list.
|
||||
|
@ -1099,13 +1100,14 @@ impl Field {
|
|||
VariantDef::Union(it) => it.id.into(),
|
||||
VariantDef::Variant(it) => it.parent_enum(db).id.into(),
|
||||
};
|
||||
let mut generics = generics.map(|it| it.ty.clone());
|
||||
let mut generics = generics.map(|it| it.ty);
|
||||
let substs = TyBuilder::subst_for_def(db, def_id, None)
|
||||
.fill(|x| match x {
|
||||
ParamKind::Type => {
|
||||
generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
|
||||
}
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
})
|
||||
.build();
|
||||
let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs);
|
||||
|
@ -1416,7 +1418,7 @@ impl Adt {
|
|||
}
|
||||
|
||||
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
|
||||
if db.generic_params(self.into()).iter().count() != 0 {
|
||||
if !db.generic_params(self.into()).is_empty() {
|
||||
return Err(LayoutError::HasPlaceholder);
|
||||
}
|
||||
let krate = self.krate(db).id;
|
||||
|
@ -1440,13 +1442,14 @@ impl Adt {
|
|||
/// the greatest API, FIXME find a better one.
|
||||
pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator<Item = Type>) -> Type {
|
||||
let id = AdtId::from(self);
|
||||
let mut it = args.map(|t| t.ty.clone());
|
||||
let mut it = args.map(|t| t.ty);
|
||||
let ty = TyBuilder::def_ty(db, id.into(), None)
|
||||
.fill(|x| {
|
||||
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
|
||||
match x {
|
||||
ParamKind::Type => r.cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
@ -1859,12 +1862,13 @@ impl Function {
|
|||
ItemContainerId::TraitId(it) => Some(it.into()),
|
||||
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
|
||||
};
|
||||
let mut generics = generics.map(|it| it.ty.clone());
|
||||
let mut generics = generics.map(|it| it.ty);
|
||||
let mut filler = |x: &_| match x {
|
||||
ParamKind::Type => {
|
||||
generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
|
||||
}
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
};
|
||||
|
||||
let parent_substs =
|
||||
|
@ -1954,7 +1958,7 @@ impl Function {
|
|||
ItemContainerId::TraitId(it) => Some(it.into()),
|
||||
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
|
||||
};
|
||||
let mut generics = generics.map(|it| it.ty.clone());
|
||||
let mut generics = generics.map(|it| it.ty);
|
||||
let parent_substs = parent_id.map(|id| {
|
||||
TyBuilder::subst_for_def(db, id, None)
|
||||
.fill(|x| match x {
|
||||
|
@ -1963,6 +1967,7 @@ impl Function {
|
|||
.unwrap_or_else(|| TyKind::Error.intern(Interner))
|
||||
.cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
})
|
||||
.build()
|
||||
});
|
||||
|
@ -2007,8 +2012,7 @@ impl Function {
|
|||
}
|
||||
let data = db.function_data(self.id);
|
||||
|
||||
data.name.to_smol_str() == "main"
|
||||
|| data.attrs.export_name().map(core::ops::Deref::deref) == Some("main")
|
||||
data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main")
|
||||
}
|
||||
|
||||
/// Does this function have the ignore attribute?
|
||||
|
@ -2215,12 +2219,13 @@ impl SelfParam {
|
|||
}
|
||||
};
|
||||
|
||||
let mut generics = generics.map(|it| it.ty.clone());
|
||||
let mut generics = generics.map(|it| it.ty);
|
||||
let mut filler = |x: &_| match x {
|
||||
ParamKind::Type => {
|
||||
generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
|
||||
}
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
};
|
||||
|
||||
let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build();
|
||||
|
@ -2592,7 +2597,7 @@ impl Macro {
|
|||
},
|
||||
MacroId::ProcMacroId(it) => match it.lookup(db.upcast()).kind {
|
||||
ProcMacroKind::CustomDerive => MacroKind::Derive,
|
||||
ProcMacroKind::FuncLike => MacroKind::ProcMacro,
|
||||
ProcMacroKind::Bang => MacroKind::ProcMacro,
|
||||
ProcMacroKind::Attr => MacroKind::Attr,
|
||||
},
|
||||
}
|
||||
|
@ -3628,16 +3633,41 @@ impl Impl {
|
|||
.filter(filter),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(block) =
|
||||
ty.adt_id(Interner).and_then(|def| def.0.module(db.upcast()).containing_block())
|
||||
{
|
||||
if let Some(inherent_impls) = db.inherent_impls_in_block(block) {
|
||||
all.extend(
|
||||
inherent_impls.for_self_ty(&ty).iter().cloned().map(Self::from).filter(filter),
|
||||
);
|
||||
}
|
||||
if let Some(trait_impls) = db.trait_impls_in_block(block) {
|
||||
all.extend(
|
||||
trait_impls
|
||||
.for_self_ty_without_blanket_impls(fp)
|
||||
.map(Self::from)
|
||||
.filter(filter),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
all
|
||||
}
|
||||
|
||||
pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> {
|
||||
let krate = trait_.module(db).krate();
|
||||
let module = trait_.module(db);
|
||||
let krate = module.krate();
|
||||
let mut all = Vec::new();
|
||||
for Crate { id } in krate.transitive_reverse_dependencies(db) {
|
||||
let impls = db.trait_impls_in_crate(id);
|
||||
all.extend(impls.for_trait(trait_.id).map(Self::from))
|
||||
}
|
||||
if let Some(block) = module.id.containing_block() {
|
||||
if let Some(trait_impls) = db.trait_impls_in_block(block) {
|
||||
all.extend(trait_impls.for_trait(trait_.id).map(Self::from));
|
||||
}
|
||||
}
|
||||
all
|
||||
}
|
||||
|
||||
|
@ -3683,7 +3713,7 @@ impl Impl {
|
|||
let macro_file = src.file_id.macro_file()?;
|
||||
let loc = macro_file.macro_call_id.lookup(db.upcast());
|
||||
let (derive_attr, derive_index) = match loc.kind {
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => {
|
||||
let module_id = self.id.lookup(db.upcast()).container;
|
||||
(
|
||||
db.crate_def_map(module_id.krate())[module_id.local_id]
|
||||
|
@ -4114,6 +4144,7 @@ impl Type {
|
|||
// FIXME: this code is not covered in tests.
|
||||
unknown_const_as_generic(ty.clone())
|
||||
}
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
@ -4144,6 +4175,7 @@ impl Type {
|
|||
match it {
|
||||
ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
|
|
@ -177,7 +177,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||
// Note that we need special case for 0 param constructors because of multi cartesian
|
||||
// product
|
||||
let variant_exprs: Vec<Expr> = if param_exprs.is_empty() {
|
||||
vec![Expr::Variant { variant, generics: generics.clone(), params: Vec::new() }]
|
||||
vec![Expr::Variant { variant, generics, params: Vec::new() }]
|
||||
} else {
|
||||
param_exprs
|
||||
.into_iter()
|
||||
|
@ -462,7 +462,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
|
|||
|
||||
/// # Impl method tactic
|
||||
///
|
||||
/// Attempts to to call methods on types from lookup table.
|
||||
/// Attempts to call methods on types from lookup table.
|
||||
/// This includes both functions from direct impl blocks as well as functions from traits.
|
||||
/// Methods defined in impl blocks that are generic and methods that are themselves have
|
||||
/// generics are ignored for performance reasons.
|
||||
|
|
|
@ -5617,7 +5617,7 @@ fn func<T: Debug>(i: Struct<'_, T>) {
|
|||
fun_name(i);
|
||||
}
|
||||
|
||||
fn $0fun_name(i: Struct<'_, T>) {
|
||||
fn $0fun_name(i: Struct<'static, T>) {
|
||||
foo(i);
|
||||
}
|
||||
"#,
|
||||
|
|
|
@ -614,7 +614,7 @@ struct Foo<'a, T> {
|
|||
}
|
||||
|
||||
impl<'a, T> Foo<'a, T> {
|
||||
$0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> {
|
||||
$0fn bar(self, mut b: Vec<&'a Bar<'a, T>>) -> &'a Bar<'a, T> {
|
||||
self.field.bar(b)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -961,11 +961,11 @@ struct Foo { field: i32 }
|
|||
impl Foo { fn foo(&self) { $0 } }"#,
|
||||
expect![[r#"
|
||||
fd self.field i32
|
||||
me self.foo() fn(&self)
|
||||
lc self &Foo
|
||||
sp Self Foo
|
||||
st Foo Foo
|
||||
bt u32 u32
|
||||
me self.foo() fn(&self)
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
|
@ -975,11 +975,11 @@ struct Foo(i32);
|
|||
impl Foo { fn foo(&mut self) { $0 } }"#,
|
||||
expect![[r#"
|
||||
fd self.0 i32
|
||||
me self.foo() fn(&mut self)
|
||||
lc self &mut Foo
|
||||
sp Self Foo
|
||||
st Foo Foo
|
||||
bt u32 u32
|
||||
me self.foo() fn(&mut self)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -186,11 +186,11 @@ fn add_function_impl(
|
|||
if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
|
||||
);
|
||||
|
||||
let completion_kind = if func.has_self_param(ctx.db) {
|
||||
CompletionItemKind::Method
|
||||
let completion_kind = CompletionItemKind::SymbolKind(if func.has_self_param(ctx.db) {
|
||||
SymbolKind::Method
|
||||
} else {
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Function)
|
||||
};
|
||||
SymbolKind::Function
|
||||
});
|
||||
|
||||
let mut item = CompletionItem::new(completion_kind, replacement_range, label);
|
||||
item.lookup_by(format!("fn {}", fn_name.display(ctx.db)))
|
||||
|
|
|
@ -75,8 +75,8 @@ impl Future for A {}
|
|||
fn foo(a: A) { a.$0 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
kw await expr.await
|
||||
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||
kw await expr.await
|
||||
sn box Box::new(expr)
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
|
@ -102,8 +102,8 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
kw await expr.await
|
||||
me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||
kw await expr.await
|
||||
sn box Box::new(expr)
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
|
@ -131,8 +131,8 @@ impl IntoFuture for A {}
|
|||
fn foo(a: A) { a.$0 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
kw await expr.await
|
||||
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||
kw await expr.await
|
||||
sn box Box::new(expr)
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
|
|
|
@ -540,7 +540,7 @@ impl CompletionContext<'_> {
|
|||
/// Whether the given trait is an operator trait or not.
|
||||
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
|
||||
match trait_.attrs(self.db).lang() {
|
||||
Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
|
||||
Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,7 +342,6 @@ pub enum CompletionItemKind {
|
|||
BuiltinType,
|
||||
InferredType,
|
||||
Keyword,
|
||||
Method,
|
||||
Snippet,
|
||||
UnresolvedReference,
|
||||
Expression,
|
||||
|
@ -369,6 +368,7 @@ impl CompletionItemKind {
|
|||
SymbolKind::LifetimeParam => "lt",
|
||||
SymbolKind::Local => "lc",
|
||||
SymbolKind::Macro => "ma",
|
||||
SymbolKind::Method => "me",
|
||||
SymbolKind::ProcMacro => "pm",
|
||||
SymbolKind::Module => "md",
|
||||
SymbolKind::SelfParam => "sp",
|
||||
|
@ -388,7 +388,6 @@ impl CompletionItemKind {
|
|||
CompletionItemKind::BuiltinType => "bt",
|
||||
CompletionItemKind::InferredType => "it",
|
||||
CompletionItemKind::Keyword => "kw",
|
||||
CompletionItemKind::Method => "me",
|
||||
CompletionItemKind::Snippet => "sn",
|
||||
CompletionItemKind::UnresolvedReference => "??",
|
||||
CompletionItemKind::Expression => "ex",
|
||||
|
|
|
@ -312,7 +312,7 @@ pub(crate) fn render_expr(
|
|||
None => ctx.source_range(),
|
||||
};
|
||||
|
||||
let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label.clone());
|
||||
let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label);
|
||||
|
||||
let snippet = format!(
|
||||
"{}$0",
|
||||
|
@ -677,10 +677,11 @@ mod tests {
|
|||
|
||||
#[track_caller]
|
||||
fn check_function_relevance(ra_fixture: &str, expect: Expect) {
|
||||
let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::Method)
|
||||
.into_iter()
|
||||
.map(|item| (item.detail.unwrap_or_default(), item.relevance.function))
|
||||
.collect();
|
||||
let actual: Vec<_> =
|
||||
do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method))
|
||||
.into_iter()
|
||||
.map(|item| (item.detail.unwrap_or_default(), item.relevance.function))
|
||||
.collect();
|
||||
|
||||
expect.assert_debug_eq(&actual);
|
||||
}
|
||||
|
@ -1392,7 +1393,10 @@ impl S {
|
|||
/// Method docs
|
||||
fn bar(self) { self.$0 }
|
||||
}"#,
|
||||
&[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)],
|
||||
&[
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Method),
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Field),
|
||||
],
|
||||
expect![[r#"
|
||||
[
|
||||
CompletionItem {
|
||||
|
@ -1400,7 +1404,9 @@ impl S {
|
|||
source_range: 94..94,
|
||||
delete: 94..94,
|
||||
insert: "bar()$0",
|
||||
kind: Method,
|
||||
kind: SymbolKind(
|
||||
Method,
|
||||
),
|
||||
lookup: "bar",
|
||||
detail: "fn(self)",
|
||||
documentation: Documentation(
|
||||
|
@ -1520,7 +1526,7 @@ impl S {
|
|||
}
|
||||
fn foo(s: S) { s.$0 }
|
||||
"#,
|
||||
CompletionItemKind::Method,
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Method),
|
||||
expect![[r#"
|
||||
[
|
||||
CompletionItem {
|
||||
|
@ -1528,7 +1534,9 @@ fn foo(s: S) { s.$0 }
|
|||
source_range: 81..81,
|
||||
delete: 81..81,
|
||||
insert: "the_method()$0",
|
||||
kind: Method,
|
||||
kind: SymbolKind(
|
||||
Method,
|
||||
),
|
||||
lookup: "the_method",
|
||||
detail: "fn(&self)",
|
||||
relevance: CompletionRelevance {
|
||||
|
@ -2408,7 +2416,10 @@ impl Foo { fn baz(&self) -> u32 { 0 } }
|
|||
|
||||
fn foo(f: Foo) { let _: &u32 = f.b$0 }
|
||||
"#,
|
||||
&[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)],
|
||||
&[
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Method),
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Field),
|
||||
],
|
||||
expect![[r#"
|
||||
[
|
||||
CompletionItem {
|
||||
|
@ -2416,7 +2427,9 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
|
|||
source_range: 109..110,
|
||||
delete: 109..110,
|
||||
insert: "baz()$0",
|
||||
kind: Method,
|
||||
kind: SymbolKind(
|
||||
Method,
|
||||
),
|
||||
lookup: "baz",
|
||||
detail: "fn(&self) -> u32",
|
||||
relevance: CompletionRelevance {
|
||||
|
@ -2631,7 +2644,7 @@ fn main() {
|
|||
let _: bool = (9 > 2).not$0;
|
||||
}
|
||||
"#,
|
||||
&[CompletionItemKind::Snippet, CompletionItemKind::Method],
|
||||
&[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
|
||||
expect![[r#"
|
||||
sn not [snippet]
|
||||
me not() (use ops::Not) [type_could_unify+requires_import]
|
||||
|
@ -2664,7 +2677,7 @@ fn main() {
|
|||
S.$0
|
||||
}
|
||||
"#,
|
||||
&[CompletionItemKind::Snippet, CompletionItemKind::Method],
|
||||
&[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
|
||||
expect![[r#"
|
||||
me f() []
|
||||
sn ref []
|
||||
|
@ -2907,7 +2920,7 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
&[
|
||||
CompletionItemKind::Method,
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Method),
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Field),
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Function),
|
||||
],
|
||||
|
@ -2918,7 +2931,9 @@ fn main() {
|
|||
source_range: 193..193,
|
||||
delete: 193..193,
|
||||
insert: "flush()$0",
|
||||
kind: Method,
|
||||
kind: SymbolKind(
|
||||
Method,
|
||||
),
|
||||
lookup: "flush",
|
||||
detail: "fn(&self)",
|
||||
relevance: CompletionRelevance {
|
||||
|
@ -2941,7 +2956,9 @@ fn main() {
|
|||
source_range: 193..193,
|
||||
delete: 193..193,
|
||||
insert: "write()$0",
|
||||
kind: Method,
|
||||
kind: SymbolKind(
|
||||
Method,
|
||||
),
|
||||
lookup: "write",
|
||||
detail: "fn(&self)",
|
||||
relevance: CompletionRelevance {
|
||||
|
|
|
@ -68,11 +68,11 @@ fn render(
|
|||
};
|
||||
let has_self_param = func.self_param(db).is_some();
|
||||
let mut item = CompletionItem::new(
|
||||
if has_self_param {
|
||||
CompletionItemKind::Method
|
||||
CompletionItemKind::SymbolKind(if has_self_param {
|
||||
SymbolKind::Method
|
||||
} else {
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Function)
|
||||
},
|
||||
SymbolKind::Function
|
||||
}),
|
||||
ctx.source_range(),
|
||||
call.clone(),
|
||||
);
|
||||
|
|
|
@ -127,6 +127,7 @@ impl Unit {
|
|||
en Enum Enum
|
||||
fn function() fn()
|
||||
fn local_func() fn()
|
||||
me self.foo() fn(self)
|
||||
lc self Unit
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
|
@ -166,7 +167,6 @@ impl Unit {
|
|||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
me self.foo() fn(self)
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
|
|
|
@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
|
|||
en Enum Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
st Foo<…> Foo<'_, {unknown}, _>
|
||||
st Foo<…> Foo<'static, {unknown}, _>
|
||||
st Record Record
|
||||
st Tuple Tuple
|
||||
st Unit Unit
|
||||
|
@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
|
|||
en Enum Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
st Foo<…> Foo<'_, {unknown}, _>
|
||||
st Foo<…> Foo<'static, {unknown}, _>
|
||||
st Record Record
|
||||
st Tuple Tuple
|
||||
st Unit Unit
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Tests that don't fit into a specific category.
|
||||
|
||||
use expect_test::{expect, Expect};
|
||||
use ide_db::SymbolKind;
|
||||
|
||||
use crate::{
|
||||
tests::{
|
||||
|
@ -316,15 +317,15 @@ trait Sub: Super {
|
|||
fn foo<T: Sub>() { T::$0 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct C2 (as Sub) const C2: ()
|
||||
ct CONST (as Super) const CONST: u8
|
||||
fn func() (as Super) fn()
|
||||
fn subfunc() (as Sub) fn()
|
||||
ta SubTy (as Sub) type SubTy
|
||||
ta Ty (as Super) type Ty
|
||||
me method(…) (as Super) fn(&self)
|
||||
me submethod(…) (as Sub) fn(&self)
|
||||
"#]],
|
||||
ct C2 (as Sub) const C2: ()
|
||||
ct CONST (as Super) const CONST: u8
|
||||
fn func() (as Super) fn()
|
||||
fn subfunc() (as Sub) fn()
|
||||
me method(…) (as Super) fn(&self)
|
||||
me submethod(…) (as Sub) fn(&self)
|
||||
ta SubTy (as Sub) type SubTy
|
||||
ta Ty (as Super) type Ty
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -356,15 +357,15 @@ impl<T> Sub for Wrap<T> {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ct C2 (as Sub) const C2: ()
|
||||
ct CONST (as Super) const CONST: u8
|
||||
fn func() (as Super) fn()
|
||||
fn subfunc() (as Sub) fn()
|
||||
ta SubTy (as Sub) type SubTy
|
||||
ta Ty (as Super) type Ty
|
||||
me method(…) (as Super) fn(&self)
|
||||
me submethod(…) (as Sub) fn(&self)
|
||||
"#]],
|
||||
ct C2 (as Sub) const C2: ()
|
||||
ct CONST (as Super) const CONST: u8
|
||||
fn func() (as Super) fn()
|
||||
fn subfunc() (as Sub) fn()
|
||||
me method(…) (as Super) fn(&self)
|
||||
me submethod(…) (as Sub) fn(&self)
|
||||
ta SubTy (as Sub) type SubTy
|
||||
ta Ty (as Super) type Ty
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -555,10 +556,10 @@ impl Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ev Bar Bar
|
||||
ev Baz Baz
|
||||
me foo(…) fn(self)
|
||||
"#]],
|
||||
me foo(…) fn(self)
|
||||
ev Bar Bar
|
||||
ev Baz Baz
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1399,7 +1400,7 @@ fn main() {
|
|||
bar.b$0
|
||||
}
|
||||
"#,
|
||||
CompletionItemKind::Method,
|
||||
CompletionItemKind::SymbolKind(SymbolKind::Method),
|
||||
expect!("const fn(&'foo mut self, &Foo) -> !"),
|
||||
expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"),
|
||||
);
|
||||
|
|
|
@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> {
|
|||
en Enum Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
sp Self Foo<'_, {unknown}, _>
|
||||
st Foo<…> Foo<'_, {unknown}, _>
|
||||
sp Self Foo<'static, {unknown}, _>
|
||||
st Foo<…> Foo<'static, {unknown}, _>
|
||||
st Record Record
|
||||
st Tuple Tuple
|
||||
st Unit Unit
|
||||
|
@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0);
|
|||
en Enum Enum
|
||||
ma makro!(…) macro_rules! makro
|
||||
md module
|
||||
sp Self Foo<'_, {unknown}, _>
|
||||
st Foo<…> Foo<'_, {unknown}, _>
|
||||
sp Self Foo<'static, {unknown}, _>
|
||||
st Foo<…> Foo<'static, {unknown}, _>
|
||||
st Record Record
|
||||
st Tuple Tuple
|
||||
st Unit Unit
|
||||
|
|
|
@ -346,6 +346,7 @@ pub enum SymbolKind {
|
|||
Enum,
|
||||
Field,
|
||||
Function,
|
||||
Method,
|
||||
Impl,
|
||||
Label,
|
||||
LifetimeParam,
|
||||
|
|
|
@ -26,6 +26,7 @@ text-edit.workspace = true
|
|||
cfg.workspace = true
|
||||
hir.workspace = true
|
||||
ide-db.workspace = true
|
||||
paths.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
|
|
|
@ -23,6 +23,7 @@ mod tests {
|
|||
},
|
||||
DiagnosticsConfig,
|
||||
};
|
||||
use test_utils::skip_slow_tests;
|
||||
|
||||
#[track_caller]
|
||||
fn check_diagnostics_no_bails(ra_fixture: &str) {
|
||||
|
@ -1004,6 +1005,32 @@ fn f() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exponential_match() {
|
||||
if skip_slow_tests() {
|
||||
return;
|
||||
}
|
||||
// Constructs a match where match checking takes exponential time. Ensures we bail early.
|
||||
use std::fmt::Write;
|
||||
let struct_arity = 50;
|
||||
let mut code = String::new();
|
||||
write!(code, "struct BigStruct {{").unwrap();
|
||||
for i in 0..struct_arity {
|
||||
write!(code, " field{i}: bool,").unwrap();
|
||||
}
|
||||
write!(code, "}}").unwrap();
|
||||
write!(code, "fn big_match(s: BigStruct) {{").unwrap();
|
||||
write!(code, " match s {{").unwrap();
|
||||
for i in 0..struct_arity {
|
||||
write!(code, " BigStruct {{ field{i}: true, ..}} => {{}},").unwrap();
|
||||
write!(code, " BigStruct {{ field{i}: false, ..}} => {{}},").unwrap();
|
||||
}
|
||||
write!(code, " _ => {{}},").unwrap();
|
||||
write!(code, " }}").unwrap();
|
||||
write!(code, "}}").unwrap();
|
||||
check_diagnostics_no_bails(&code);
|
||||
}
|
||||
|
||||
mod rust_unstable {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -7,7 +7,11 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
|||
// Diagnostic: need-mut
|
||||
//
|
||||
// This diagnostic is triggered on mutating an immutable variable.
|
||||
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic {
|
||||
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option<Diagnostic> {
|
||||
if d.span.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
}
|
||||
let fixes = (|| {
|
||||
if d.local.is_ref(ctx.sema.db) {
|
||||
// There is no simple way to add `mut` to `ref x` and `ref mut x`
|
||||
|
@ -29,24 +33,30 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno
|
|||
use_range,
|
||||
)])
|
||||
})();
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
// FIXME: `E0384` is not the only error that this diagnostic handles
|
||||
DiagnosticCode::RustcHardError("E0384"),
|
||||
format!(
|
||||
"cannot mutate immutable variable `{}`",
|
||||
d.local.name(ctx.sema.db).display(ctx.sema.db)
|
||||
),
|
||||
d.span,
|
||||
Some(
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
// FIXME: `E0384` is not the only error that this diagnostic handles
|
||||
DiagnosticCode::RustcHardError("E0384"),
|
||||
format!(
|
||||
"cannot mutate immutable variable `{}`",
|
||||
d.local.name(ctx.sema.db).display(ctx.sema.db)
|
||||
),
|
||||
d.span,
|
||||
)
|
||||
.with_fixes(fixes),
|
||||
)
|
||||
.with_fixes(fixes)
|
||||
}
|
||||
|
||||
// Diagnostic: unused-mut
|
||||
//
|
||||
// This diagnostic is triggered when a mutable variable isn't actually mutated.
|
||||
pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic {
|
||||
pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option<Diagnostic> {
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
if ast.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
}
|
||||
let fixes = (|| {
|
||||
let file_id = ast.file_id.file_id()?;
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
|
@ -70,14 +80,16 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di
|
|||
)])
|
||||
})();
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcLint("unused_mut"),
|
||||
"variable does not need to be mutable",
|
||||
ast,
|
||||
Some(
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcLint("unused_mut"),
|
||||
"variable does not need to be mutable",
|
||||
ast,
|
||||
)
|
||||
.experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive.
|
||||
.with_fixes(fixes),
|
||||
)
|
||||
.experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive.
|
||||
.with_fixes(fixes)
|
||||
}
|
||||
|
||||
pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
|
||||
|
|
|
@ -12,7 +12,12 @@ use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, Diagnostics
|
|||
pub(crate) fn remove_trailing_return(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &RemoveTrailingReturn,
|
||||
) -> Diagnostic {
|
||||
) -> Option<Diagnostic> {
|
||||
if d.return_expr.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
}
|
||||
|
||||
let display_range = adjusted_display_range(ctx, d.return_expr, &|return_expr| {
|
||||
return_expr
|
||||
.syntax()
|
||||
|
@ -20,12 +25,14 @@ pub(crate) fn remove_trailing_return(
|
|||
.and_then(ast::ExprStmt::cast)
|
||||
.map(|stmt| stmt.syntax().text_range())
|
||||
});
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::Clippy("needless_return"),
|
||||
"replace return <expr>; with <expr>",
|
||||
display_range,
|
||||
Some(
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::Clippy("needless_return"),
|
||||
"replace return <expr>; with <expr>",
|
||||
display_range,
|
||||
)
|
||||
.with_fixes(fixes(ctx, d)),
|
||||
)
|
||||
.with_fixes(fixes(ctx, d))
|
||||
}
|
||||
|
||||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option<Vec<Assist>> {
|
||||
|
|
|
@ -21,23 +21,30 @@ use crate::{
|
|||
pub(crate) fn remove_unnecessary_else(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &RemoveUnnecessaryElse,
|
||||
) -> Diagnostic {
|
||||
) -> Option<Diagnostic> {
|
||||
if d.if_expr.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
}
|
||||
|
||||
let display_range = adjusted_display_range(ctx, d.if_expr, &|if_expr| {
|
||||
if_expr.else_token().as_ref().map(SyntaxToken::text_range)
|
||||
});
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning),
|
||||
"remove unnecessary else block",
|
||||
display_range,
|
||||
Some(
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning),
|
||||
"remove unnecessary else block",
|
||||
display_range,
|
||||
)
|
||||
.experimental()
|
||||
.with_fixes(fixes(ctx, d)),
|
||||
)
|
||||
.experimental()
|
||||
.with_fixes(fixes(ctx, d))
|
||||
}
|
||||
|
||||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> {
|
||||
let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id);
|
||||
let if_expr = d.if_expr.value.to_node(&root);
|
||||
let if_expr = ctx.sema.original_ast_node(if_expr.clone())?;
|
||||
let if_expr = ctx.sema.original_ast_node(if_expr)?;
|
||||
|
||||
let mut indent = IndentLevel::from_node(if_expr.syntax());
|
||||
let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some();
|
||||
|
|
|
@ -8,6 +8,7 @@ use ide_db::{
|
|||
source_change::SourceChange,
|
||||
RootDatabase,
|
||||
};
|
||||
use paths::Utf8Component;
|
||||
use syntax::{
|
||||
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
|
||||
AstNode, TextRange,
|
||||
|
@ -84,10 +85,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
|
|||
|
||||
// try resolving the relative difference of the paths as inline modules
|
||||
let mut current = root_module;
|
||||
for ele in rel.as_ref().components() {
|
||||
for ele in rel.as_utf8_path().components() {
|
||||
let seg = match ele {
|
||||
std::path::Component::Normal(seg) => seg.to_str()?,
|
||||
std::path::Component::RootDir => continue,
|
||||
Utf8Component::Normal(seg) => seg,
|
||||
Utf8Component::RootDir => continue,
|
||||
// shouldn't occur
|
||||
_ => continue 'crates,
|
||||
};
|
||||
|
|
|
@ -14,18 +14,24 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
|||
pub(crate) fn unused_variables(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::UnusedVariable,
|
||||
) -> Diagnostic {
|
||||
) -> Option<Diagnostic> {
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
if ast.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
}
|
||||
let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
|
||||
let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string();
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcLint("unused_variables"),
|
||||
"unused variable",
|
||||
ast,
|
||||
Some(
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcLint("unused_variables"),
|
||||
"unused variable",
|
||||
ast,
|
||||
)
|
||||
.with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro()))
|
||||
.experimental(),
|
||||
)
|
||||
.with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro()))
|
||||
.experimental()
|
||||
}
|
||||
|
||||
fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option<Vec<Assist>> {
|
||||
|
@ -47,7 +53,7 @@ fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> O
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_diagnostics, check_fix, check_no_fix};
|
||||
use crate::tests::{check_diagnostics, check_fix};
|
||||
|
||||
#[test]
|
||||
fn unused_variables_simple() {
|
||||
|
@ -193,7 +199,7 @@ fn main() {
|
|||
|
||||
#[test]
|
||||
fn no_fix_for_marco() {
|
||||
check_no_fix(
|
||||
check_diagnostics(
|
||||
r#"
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
|
@ -202,7 +208,7 @@ macro_rules! my_macro {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
$0my_macro!();
|
||||
my_macro!();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
@ -330,7 +330,6 @@ pub fn diagnostics(
|
|||
}
|
||||
|
||||
for diag in diags {
|
||||
#[rustfmt::skip]
|
||||
let d = match diag {
|
||||
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
|
||||
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
|
||||
|
@ -361,7 +360,10 @@ pub fn diagnostics(
|
|||
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
|
||||
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
|
||||
AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
|
||||
AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
|
||||
AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
},
|
||||
AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d),
|
||||
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
|
||||
AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
|
||||
|
@ -385,12 +387,24 @@ pub fn diagnostics(
|
|||
AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
|
||||
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
|
||||
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
|
||||
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
|
||||
AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d),
|
||||
AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
},
|
||||
AnyDiagnostic::UnusedVariable(d) => match handlers::unused_variables::unused_variables(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
},
|
||||
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
||||
AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
|
||||
AnyDiagnostic::RemoveTrailingReturn(d) => handlers::remove_trailing_return::remove_trailing_return(&ctx, &d),
|
||||
AnyDiagnostic::RemoveUnnecessaryElse(d) => handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d),
|
||||
AnyDiagnostic::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
},
|
||||
AnyDiagnostic::RemoveUnnecessaryElse(d) => match handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
},
|
||||
};
|
||||
res.push(d)
|
||||
}
|
||||
|
@ -399,9 +413,9 @@ pub fn diagnostics(
|
|||
.iter_mut()
|
||||
.filter_map(|it| {
|
||||
Some((
|
||||
it.main_node
|
||||
.map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id))))
|
||||
.clone()?,
|
||||
it.main_node.map(|ptr| {
|
||||
ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))
|
||||
})?,
|
||||
it,
|
||||
))
|
||||
})
|
||||
|
|
|
@ -283,6 +283,10 @@ fn test_disabled_diagnostics() {
|
|||
|
||||
#[test]
|
||||
fn minicore_smoke_test() {
|
||||
if test_utils::skip_slow_tests() {
|
||||
return;
|
||||
}
|
||||
|
||||
fn check(minicore: MiniCore) {
|
||||
let source = minicore.source_code();
|
||||
let mut config = DiagnosticsConfig::test_sample();
|
||||
|
|
|
@ -36,6 +36,7 @@ ide-ssr.workspace = true
|
|||
profile.workspace = true
|
||||
stdx.workspace = true
|
||||
syntax.workspace = true
|
||||
span.workspace = true
|
||||
text-edit.workspace = true
|
||||
# ide should depend only on the top-level `hir` package. if you need
|
||||
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
|
||||
|
|
|
@ -5,8 +5,6 @@ mod tests;
|
|||
|
||||
mod intra_doc_links;
|
||||
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
|
||||
use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
|
||||
use stdx::format_to;
|
||||
|
@ -134,8 +132,8 @@ pub(crate) fn remove_links(markdown: &str) -> String {
|
|||
pub(crate) fn external_docs(
|
||||
db: &RootDatabase,
|
||||
FilePosition { file_id, offset }: FilePosition,
|
||||
target_dir: Option<&OsStr>,
|
||||
sysroot: Option<&OsStr>,
|
||||
target_dir: Option<&str>,
|
||||
sysroot: Option<&str>,
|
||||
) -> Option<DocumentationLinks> {
|
||||
let sema = &Semantics::new(db);
|
||||
let file = sema.parse(file_id).syntax().clone();
|
||||
|
@ -331,8 +329,8 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)
|
|||
fn get_doc_links(
|
||||
db: &RootDatabase,
|
||||
def: Definition,
|
||||
target_dir: Option<&OsStr>,
|
||||
sysroot: Option<&OsStr>,
|
||||
target_dir: Option<&str>,
|
||||
sysroot: Option<&str>,
|
||||
) -> DocumentationLinks {
|
||||
let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
|
||||
base_url.and_then(|url| url.join(path).ok())
|
||||
|
@ -479,15 +477,13 @@ fn map_links<'e>(
|
|||
fn get_doc_base_urls(
|
||||
db: &RootDatabase,
|
||||
def: Definition,
|
||||
target_dir: Option<&OsStr>,
|
||||
sysroot: Option<&OsStr>,
|
||||
target_dir: Option<&str>,
|
||||
sysroot: Option<&str>,
|
||||
) -> (Option<Url>, Option<Url>) {
|
||||
let local_doc = target_dir
|
||||
.and_then(|path| path.to_str())
|
||||
.and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
|
||||
.and_then(|it| it.join("doc/").ok());
|
||||
let system_doc = sysroot
|
||||
.and_then(|it| it.to_str())
|
||||
.map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
|
||||
.and_then(|it| Url::parse(&it).ok());
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::OsStr, iter};
|
||||
use std::iter;
|
||||
|
||||
use expect_test::{expect, Expect};
|
||||
use hir::Semantics;
|
||||
|
@ -18,10 +18,10 @@ use crate::{
|
|||
|
||||
fn check_external_docs(
|
||||
ra_fixture: &str,
|
||||
target_dir: Option<&OsStr>,
|
||||
target_dir: Option<&str>,
|
||||
expect_web_url: Option<Expect>,
|
||||
expect_local_url: Option<Expect>,
|
||||
sysroot: Option<&OsStr>,
|
||||
sysroot: Option<&str>,
|
||||
) {
|
||||
let (analysis, position) = fixture::position(ra_fixture);
|
||||
let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
|
||||
|
@ -127,10 +127,10 @@ fn external_docs_doc_builtin_type() {
|
|||
//- /main.rs crate:foo
|
||||
let x: u3$02 = 0;
|
||||
"#,
|
||||
Some(OsStr::new("/home/user/project")),
|
||||
Some("/home/user/project"),
|
||||
Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
|
||||
Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]),
|
||||
Some(OsStr::new("/sysroot")),
|
||||
Some("/sysroot"),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -143,10 +143,10 @@ use foo$0::Foo;
|
|||
//- /lib.rs crate:foo
|
||||
pub struct Foo;
|
||||
"#,
|
||||
Some(OsStr::new("/home/user/project")),
|
||||
Some("/home/user/project"),
|
||||
Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]),
|
||||
Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]),
|
||||
Some(OsStr::new("/sysroot")),
|
||||
Some("/sysroot"),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -157,10 +157,10 @@ fn external_docs_doc_url_std_crate() {
|
|||
//- /main.rs crate:std
|
||||
use self$0;
|
||||
"#,
|
||||
Some(OsStr::new("/home/user/project")),
|
||||
Some("/home/user/project"),
|
||||
Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]),
|
||||
Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]),
|
||||
Some(OsStr::new("/sysroot")),
|
||||
Some("/sysroot"),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -171,10 +171,10 @@ fn external_docs_doc_url_struct() {
|
|||
//- /main.rs crate:foo
|
||||
pub struct Fo$0o;
|
||||
"#,
|
||||
Some(OsStr::new("/home/user/project")),
|
||||
Some("/home/user/project"),
|
||||
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
||||
Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]),
|
||||
Some(OsStr::new("/sysroot")),
|
||||
Some("/sysroot"),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -185,10 +185,10 @@ fn external_docs_doc_url_windows_backslash_path() {
|
|||
//- /main.rs crate:foo
|
||||
pub struct Fo$0o;
|
||||
"#,
|
||||
Some(OsStr::new(r"C:\Users\user\project")),
|
||||
Some(r"C:\Users\user\project"),
|
||||
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
||||
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
|
||||
Some(OsStr::new("/sysroot")),
|
||||
Some("/sysroot"),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -199,10 +199,10 @@ fn external_docs_doc_url_windows_slash_path() {
|
|||
//- /main.rs crate:foo
|
||||
pub struct Fo$0o;
|
||||
"#,
|
||||
Some(OsStr::new(r"C:/Users/user/project")),
|
||||
Some("C:/Users/user/project"),
|
||||
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
||||
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
|
||||
Some(OsStr::new("/sysroot")),
|
||||
Some("/sysroot"),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -134,15 +134,22 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
|||
if let Some(type_param_list) = it.generic_param_list() {
|
||||
collapse_ws(type_param_list.syntax(), &mut detail);
|
||||
}
|
||||
if let Some(param_list) = it.param_list() {
|
||||
let has_self_param = if let Some(param_list) = it.param_list() {
|
||||
collapse_ws(param_list.syntax(), &mut detail);
|
||||
}
|
||||
param_list.self_param().is_some()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if let Some(ret_type) = it.ret_type() {
|
||||
detail.push(' ');
|
||||
collapse_ws(ret_type.syntax(), &mut detail);
|
||||
}
|
||||
|
||||
decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(SymbolKind::Function))
|
||||
decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(if has_self_param {
|
||||
SymbolKind::Method
|
||||
} else {
|
||||
SymbolKind::Function
|
||||
}))
|
||||
},
|
||||
ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)),
|
||||
ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)),
|
||||
|
|
|
@ -337,6 +337,77 @@ impl Tr for S {
|
|||
const C: usize = 4;
|
||||
//^
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_adt_implementation_inside_block() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: copy, derive
|
||||
trait Bar {}
|
||||
|
||||
fn test() {
|
||||
#[derive(Copy)]
|
||||
//^^^^^^^^^^^^^^^
|
||||
struct Foo$0;
|
||||
|
||||
impl Foo {}
|
||||
//^^^
|
||||
|
||||
trait Baz {}
|
||||
|
||||
impl Bar for Foo {}
|
||||
//^^^
|
||||
|
||||
impl Baz for Foo {}
|
||||
//^^^
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_trait_implementation_inside_block() {
|
||||
check(
|
||||
r#"
|
||||
struct Bar;
|
||||
|
||||
fn test() {
|
||||
trait Foo$0 {}
|
||||
|
||||
struct Baz;
|
||||
|
||||
impl Foo for Bar {}
|
||||
//^^^
|
||||
|
||||
impl Foo for Baz {}
|
||||
//^^^
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
struct Bar;
|
||||
|
||||
fn test() {
|
||||
trait Foo {
|
||||
fn foo$0() {}
|
||||
}
|
||||
|
||||
struct Baz;
|
||||
|
||||
impl Foo for Bar {
|
||||
fn foo() {}
|
||||
//^^^
|
||||
}
|
||||
|
||||
impl Foo for Baz {
|
||||
fn foo() {}
|
||||
//^^^
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ pub struct HoverConfig {
|
|||
pub keywords: bool,
|
||||
pub format: HoverDocFormat,
|
||||
pub max_trait_assoc_items_count: Option<usize>,
|
||||
pub max_struct_field_count: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -410,6 +410,9 @@ pub(super) fn definition(
|
|||
Definition::Trait(trait_) => {
|
||||
trait_.display_limited(db, config.max_trait_assoc_items_count).to_string()
|
||||
}
|
||||
Definition::Adt(Adt::Struct(struct_)) => {
|
||||
struct_.display_limited(db, config.max_struct_field_count).to_string()
|
||||
}
|
||||
_ => def.label(db),
|
||||
};
|
||||
let docs = def.docs(db, famous_defs);
|
||||
|
|
|
@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
|
|||
format: HoverDocFormat::Markdown,
|
||||
keywords: true,
|
||||
max_trait_assoc_items_count: None,
|
||||
max_struct_field_count: None,
|
||||
};
|
||||
|
||||
fn check_hover_no_result(ra_fixture: &str) {
|
||||
|
@ -49,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) {
|
|||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) {
|
||||
let (analysis, position) = fixture::position(ra_fixture);
|
||||
let hover = analysis
|
||||
.hover(
|
||||
&HoverConfig {
|
||||
links_in_hover: true,
|
||||
max_struct_field_count: Some(count),
|
||||
..HOVER_BASE_CONFIG
|
||||
},
|
||||
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let content = analysis.db.file_text(position.file_id);
|
||||
let hovered_element = &content[hover.range];
|
||||
|
||||
let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
|
||||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
|
||||
let (analysis, position) = fixture::position(ra_fixture);
|
||||
|
@ -853,9 +876,7 @@ struct Foo$0 { field: u32 }
|
|||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
struct Foo {
|
||||
field: u32,
|
||||
}
|
||||
struct Foo
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -875,8 +896,74 @@ struct Foo$0 where u32: Copy { field: u32 }
|
|||
struct Foo
|
||||
where
|
||||
u32: Copy,
|
||||
{
|
||||
field: u32,
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_record_struct_limit() {
|
||||
check_hover_struct_limit(
|
||||
3,
|
||||
r#"
|
||||
struct Foo$0 { a: u32, b: i32, c: i32 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
// size = 12 (0xC), align = 4
|
||||
struct Foo {
|
||||
a: u32,
|
||||
b: i32,
|
||||
c: i32,
|
||||
}
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
check_hover_struct_limit(
|
||||
3,
|
||||
r#"
|
||||
struct Foo$0 { a: u32 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
// size = 4, align = 4
|
||||
struct Foo {
|
||||
a: u32,
|
||||
}
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
check_hover_struct_limit(
|
||||
3,
|
||||
r#"
|
||||
struct Foo$0 { a: u32, b: i32, c: i32, d: u32 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Foo*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
// size = 16 (0x10), align = 4
|
||||
struct Foo {
|
||||
a: u32,
|
||||
b: i32,
|
||||
c: i32,
|
||||
/* … */
|
||||
}
|
||||
```
|
||||
"#]],
|
||||
|
@ -1344,9 +1431,7 @@ impl Thing {
|
|||
```
|
||||
|
||||
```rust
|
||||
struct Thing {
|
||||
x: u32,
|
||||
}
|
||||
struct Thing
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -1365,9 +1450,7 @@ impl Thing {
|
|||
```
|
||||
|
||||
```rust
|
||||
struct Thing {
|
||||
x: u32,
|
||||
}
|
||||
struct Thing
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
@ -2599,7 +2682,7 @@ fn main() { let s$0t = S{ f1:0 }; }
|
|||
focus_range: 7..8,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S {\n f1: u32,\n}",
|
||||
description: "struct S",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -2645,7 +2728,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
|
|||
focus_range: 24..25,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S<T> {\n f1: T,\n}",
|
||||
description: "struct S<T>",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -2704,7 +2787,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
|
|||
focus_range: 24..25,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S<T> {\n f1: T,\n}",
|
||||
description: "struct S<T>",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -2957,7 +3040,7 @@ fn main() { let s$0t = foo(); }
|
|||
focus_range: 39..41,
|
||||
name: "S1",
|
||||
kind: Struct,
|
||||
description: "struct S1 {}",
|
||||
description: "struct S1",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -2970,7 +3053,7 @@ fn main() { let s$0t = foo(); }
|
|||
focus_range: 52..54,
|
||||
name: "S2",
|
||||
kind: Struct,
|
||||
description: "struct S2 {}",
|
||||
description: "struct S2",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -3061,7 +3144,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
|
|||
focus_range: 36..37,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S {}",
|
||||
description: "struct S",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -3161,7 +3244,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
|
|||
focus_range: 23..24,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S {}",
|
||||
description: "struct S",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -3198,7 +3281,7 @@ fn main() { let s$0t = foo(); }
|
|||
focus_range: 49..50,
|
||||
name: "B",
|
||||
kind: Struct,
|
||||
description: "struct B<T> {}",
|
||||
description: "struct B<T>",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -3287,7 +3370,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
|
|||
focus_range: 23..24,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S {}",
|
||||
description: "struct S",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -3322,7 +3405,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
|
|||
focus_range: 50..51,
|
||||
name: "B",
|
||||
kind: Struct,
|
||||
description: "struct B<T> {}",
|
||||
description: "struct B<T>",
|
||||
},
|
||||
},
|
||||
HoverGotoTypeData {
|
||||
|
@ -3361,7 +3444,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
|
|||
focus_range: 65..66,
|
||||
name: "S",
|
||||
kind: Struct,
|
||||
description: "struct S {}",
|
||||
description: "struct S",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -5105,6 +5188,32 @@ fn foo(e: E) {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_const_value() {
|
||||
check(
|
||||
r#"
|
||||
pub enum AA {
|
||||
BB,
|
||||
}
|
||||
const CONST: AA = AA::BB;
|
||||
pub fn the_function() -> AA {
|
||||
CON$0ST
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*CONST*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
const CONST: AA = BB
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_repeat_exp() {
|
||||
check(
|
||||
|
@ -7747,3 +7856,25 @@ impl Iterator for S {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_lifetime_regression_16963() {
|
||||
check(
|
||||
r#"
|
||||
struct Pedro$0<'a> {
|
||||
hola: &'a str
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*Pedro*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
struct Pedro<'a>
|
||||
```
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{
|
||||
fmt::{self, Write},
|
||||
hash::{BuildHasher, BuildHasherDefault},
|
||||
mem::take,
|
||||
};
|
||||
|
||||
|
@ -8,7 +9,7 @@ use hir::{
|
|||
known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
|
||||
ModuleDefId, Semantics,
|
||||
};
|
||||
use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
|
||||
use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase};
|
||||
use itertools::Itertools;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use stdx::never;
|
||||
|
@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode {
|
|||
PreferPostfix,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum InlayKind {
|
||||
Adjustment,
|
||||
BindingMode,
|
||||
|
@ -132,7 +133,7 @@ pub enum InlayKind {
|
|||
RangeExclusive,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub enum InlayHintPosition {
|
||||
Before,
|
||||
After,
|
||||
|
@ -151,13 +152,23 @@ pub struct InlayHint {
|
|||
pub label: InlayHintLabel,
|
||||
/// Text edit to apply when "accepting" this inlay hint.
|
||||
pub text_edit: Option<TextEdit>,
|
||||
pub needs_resolve: bool,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for InlayHint {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.range.hash(state);
|
||||
self.position.hash(state);
|
||||
self.pad_left.hash(state);
|
||||
self.pad_right.hash(state);
|
||||
self.kind.hash(state);
|
||||
self.label.hash(state);
|
||||
self.text_edit.is_some().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl InlayHint {
|
||||
fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
|
||||
InlayHint {
|
||||
needs_resolve: false,
|
||||
range,
|
||||
kind,
|
||||
label: InlayHintLabel::from(")"),
|
||||
|
@ -167,9 +178,9 @@ impl InlayHint {
|
|||
pad_right: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
|
||||
InlayHint {
|
||||
needs_resolve: false,
|
||||
range,
|
||||
kind,
|
||||
label: InlayHintLabel::from("("),
|
||||
|
@ -179,15 +190,19 @@ impl InlayHint {
|
|||
pad_right: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn needs_resolve(&self) -> bool {
|
||||
self.text_edit.is_some() || self.label.needs_resolve()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub enum InlayTooltip {
|
||||
String(String),
|
||||
Markdown(String),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Hash)]
|
||||
pub struct InlayHintLabel {
|
||||
pub parts: SmallVec<[InlayHintLabelPart; 1]>,
|
||||
}
|
||||
|
@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Hash)]
|
||||
pub struct InlayHintLabelPart {
|
||||
pub text: String,
|
||||
/// Source location represented by this label part. The client will use this to fetch the part's
|
||||
|
@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> {
|
|||
|
||||
impl HirWrite for InlayHintLabelBuilder<'_> {
|
||||
fn start_location_link(&mut self, def: ModuleDefId) {
|
||||
if self.location.is_some() {
|
||||
never!("location link is already started");
|
||||
}
|
||||
never!(self.location.is_some(), "location link is already started");
|
||||
self.make_new_part();
|
||||
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
|
||||
let location = location.call_site();
|
||||
|
@ -425,11 +439,6 @@ fn ty_to_text_edit(
|
|||
Some(builder.finish())
|
||||
}
|
||||
|
||||
pub enum RangeLimit {
|
||||
Fixed(TextRange),
|
||||
NearestParent(TextSize),
|
||||
}
|
||||
|
||||
// Feature: Inlay Hints
|
||||
//
|
||||
// rust-analyzer shows additional information inline with the source code.
|
||||
|
@ -451,7 +460,7 @@ pub enum RangeLimit {
|
|||
pub(crate) fn inlay_hints(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
range_limit: Option<RangeLimit>,
|
||||
range_limit: Option<TextRange>,
|
||||
config: &InlayHintsConfig,
|
||||
) -> Vec<InlayHint> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered();
|
||||
|
@ -466,31 +475,13 @@ pub(crate) fn inlay_hints(
|
|||
|
||||
let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
|
||||
match range_limit {
|
||||
Some(RangeLimit::Fixed(range)) => match file.covering_element(range) {
|
||||
Some(range) => match file.covering_element(range) {
|
||||
NodeOrToken::Token(_) => return acc,
|
||||
NodeOrToken::Node(n) => n
|
||||
.descendants()
|
||||
.filter(|descendant| range.intersect(descendant.text_range()).is_some())
|
||||
.for_each(hints),
|
||||
},
|
||||
Some(RangeLimit::NearestParent(position)) => {
|
||||
match file.token_at_offset(position).left_biased() {
|
||||
Some(token) => {
|
||||
if let Some(parent_block) =
|
||||
token.parent_ancestors().find_map(ast::BlockExpr::cast)
|
||||
{
|
||||
parent_block.syntax().descendants().for_each(hints)
|
||||
} else if let Some(parent_item) =
|
||||
token.parent_ancestors().find_map(ast::Item::cast)
|
||||
{
|
||||
parent_item.syntax().descendants().for_each(hints)
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
None => return acc,
|
||||
}
|
||||
}
|
||||
None => file.descendants().for_each(hints),
|
||||
};
|
||||
}
|
||||
|
@ -498,6 +489,39 @@ pub(crate) fn inlay_hints(
|
|||
acc
|
||||
}
|
||||
|
||||
pub(crate) fn inlay_hints_resolve(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
position: TextSize,
|
||||
hash: u64,
|
||||
config: &InlayHintsConfig,
|
||||
) -> Option<InlayHint> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered();
|
||||
let sema = Semantics::new(db);
|
||||
let file = sema.parse(file_id);
|
||||
let file = file.syntax();
|
||||
|
||||
let scope = sema.scope(file)?;
|
||||
let famous_defs = FamousDefs(&sema, scope.krate());
|
||||
let mut acc = Vec::new();
|
||||
|
||||
let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
|
||||
match file.token_at_offset(position).left_biased() {
|
||||
Some(token) => {
|
||||
if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) {
|
||||
parent_block.syntax().descendants().for_each(hints)
|
||||
} else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) {
|
||||
parent_item.syntax().descendants().for_each(hints)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
|
||||
acc.into_iter().find(|hint| BuildHasherDefault::<FxHasher>::default().hash_one(hint) == hash)
|
||||
}
|
||||
|
||||
fn hints(
|
||||
hints: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
|
|
|
@ -147,7 +147,6 @@ pub(super) fn hints(
|
|||
None,
|
||||
);
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: label.needs_resolve(),
|
||||
range: expr.syntax().text_range(),
|
||||
pad_left: false,
|
||||
pad_right: false,
|
||||
|
|
|
@ -99,7 +99,6 @@ pub(super) fn hints(
|
|||
None => pat.syntax().text_range(),
|
||||
};
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: label.needs_resolve() || text_edit.is_some(),
|
||||
range: match type_ascriptable {
|
||||
Some(Some(t)) => text_range.cover(t.text_range()),
|
||||
_ => text_range,
|
||||
|
@ -177,11 +176,7 @@ mod tests {
|
|||
use syntax::{TextRange, TextSize};
|
||||
use test_utils::extract_annotations;
|
||||
|
||||
use crate::{
|
||||
fixture,
|
||||
inlay_hints::{InlayHintsConfig, RangeLimit},
|
||||
ClosureReturnTypeHints,
|
||||
};
|
||||
use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
|
||||
|
||||
use crate::inlay_hints::tests::{
|
||||
check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
|
||||
|
@ -404,7 +399,7 @@ fn main() {
|
|||
.inlay_hints(
|
||||
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
|
||||
file_id,
|
||||
Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))),
|
||||
Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
|
||||
)
|
||||
.unwrap();
|
||||
let actual =
|
||||
|
|
|
@ -50,7 +50,6 @@ pub(super) fn hints(
|
|||
_ => return,
|
||||
};
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: false,
|
||||
range,
|
||||
kind: InlayKind::BindingMode,
|
||||
label: r.into(),
|
||||
|
@ -69,7 +68,6 @@ pub(super) fn hints(
|
|||
hir::BindingMode::Ref(Mutability::Shared) => "ref",
|
||||
};
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: false,
|
||||
range: pat.syntax().text_range(),
|
||||
kind: InlayKind::BindingMode,
|
||||
label: bm.into(),
|
||||
|
|
|
@ -59,7 +59,6 @@ pub(super) fn hints(
|
|||
}
|
||||
let label = label_of_ty(famous_defs, config, &ty)?;
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: label.needs_resolve(),
|
||||
range: expr.syntax().text_range(),
|
||||
kind: InlayKind::Chaining,
|
||||
label,
|
||||
|
|
|
@ -109,7 +109,6 @@ pub(super) fn hints(
|
|||
|
||||
let linked_location = name_range.map(|range| FileRange { file_id, range });
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: linked_location.is_some(),
|
||||
range: closing_token.text_range(),
|
||||
kind: InlayKind::ClosingBrace,
|
||||
label: InlayHintLabel::simple(label, None, linked_location),
|
||||
|
|
|
@ -32,7 +32,6 @@ pub(super) fn hints(
|
|||
let range = closure.syntax().first_token()?.prev_token()?.text_range();
|
||||
let range = TextRange::new(range.end() - TextSize::from(1), range.end());
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: false,
|
||||
range,
|
||||
kind: InlayKind::ClosureCapture,
|
||||
label: InlayHintLabel::from("move"),
|
||||
|
@ -45,7 +44,6 @@ pub(super) fn hints(
|
|||
}
|
||||
};
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: false,
|
||||
range: move_kw_range,
|
||||
kind: InlayKind::ClosureCapture,
|
||||
label: InlayHintLabel::from("("),
|
||||
|
@ -79,7 +77,6 @@ pub(super) fn hints(
|
|||
}),
|
||||
);
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: label.needs_resolve(),
|
||||
range: move_kw_range,
|
||||
kind: InlayKind::ClosureCapture,
|
||||
label,
|
||||
|
@ -91,7 +88,6 @@ pub(super) fn hints(
|
|||
|
||||
if idx != last {
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: false,
|
||||
range: move_kw_range,
|
||||
kind: InlayKind::ClosureCapture,
|
||||
label: InlayHintLabel::from(", "),
|
||||
|
@ -103,7 +99,6 @@ pub(super) fn hints(
|
|||
}
|
||||
}
|
||||
acc.push(InlayHint {
|
||||
needs_resolve: false,
|
||||
range: move_kw_range,
|
||||
kind: InlayKind::ClosureCapture,
|
||||
label: InlayHintLabel::from(")"),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue