1
Fork 0

Auto merge of #123258 - lnicola:sync-from-ra, r=lnicola

Subtree update of `rust-analyzer`
This commit is contained in:
bors 2024-03-31 08:25:24 +00:00
commit 688c30dc9f
223 changed files with 7964 additions and 5865 deletions

View file

@ -71,7 +71,7 @@ jobs:
run: echo "::add-matcher::.github/rust.json" run: echo "::add-matcher::.github/rust.json"
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012
with: with:
key: ${{ env.RUST_CHANNEL }} key: ${{ env.RUST_CHANNEL }}
@ -140,7 +140,7 @@ jobs:
rustup target add ${{ env.targets }} ${{ env.targets_ide }} rustup target add ${{ env.targets }} ${{ env.targets_ide }}
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012
- name: Check - name: Check
run: | run: |

View file

@ -32,4 +32,5 @@ jobs:
git config --global user.name "GitHub Action" git config --global user.name "GitHub Action"
# Remove r-a crates from the workspaces so we don't auto-publish them as well # 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/ "crates\/\*"//' ./Cargo.toml
sed -i 's/ "xtask\/"//' ./Cargo.toml
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty

View file

@ -36,6 +36,7 @@ jobs:
- os: ubuntu-20.04 - os: ubuntu-20.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
code-target: linux-x64 code-target: linux-x64
container: rockylinux:8
- os: ubuntu-20.04 - os: ubuntu-20.04
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
code-target: linux-arm64 code-target: linux-arm64
@ -58,10 +59,18 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: ${{ env.FETCH_DEPTH }} 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 - name: Install Rust toolchain
run: | run: |
rustup update --no-self-update stable rustup update --no-self-update stable
@ -69,9 +78,9 @@ jobs:
rustup component add rust-src rustup component add rust-src
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: 16 node-version: 18
- name: Update apt repositories - name: Update apt repositories
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf'
@ -181,7 +190,7 @@ jobs:
- name: Install Nodejs - name: Install Nodejs
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 20
- run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
if: github.ref == 'refs/heads/release' if: github.ref == 'refs/heads/release'

View file

@ -594,6 +594,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"scoped-tls", "scoped-tls",
"smallvec", "smallvec",
"span",
"stdx", "stdx",
"syntax", "syntax",
"test-fixture", "test-fixture",
@ -637,6 +638,7 @@ dependencies = [
"pulldown-cmark", "pulldown-cmark",
"pulldown-cmark-to-cmark", "pulldown-cmark-to-cmark",
"smallvec", "smallvec",
"span",
"stdx", "stdx",
"syntax", "syntax",
"test-fixture", "test-fixture",
@ -732,6 +734,7 @@ dependencies = [
"ide-db", "ide-db",
"itertools", "itertools",
"once_cell", "once_cell",
"paths",
"serde_json", "serde_json",
"stdx", "stdx",
"syntax", "syntax",
@ -931,6 +934,7 @@ dependencies = [
"hir-expand", "hir-expand",
"ide-db", "ide-db",
"itertools", "itertools",
"paths",
"proc-macro-api", "proc-macro-api",
"project-model", "project-model",
"span", "span",
@ -1225,6 +1229,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]] [[package]]
name = "paths" name = "paths"
version = "0.0.0" version = "0.0.0"
dependencies = [
"camino",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
@ -1375,6 +1382,7 @@ dependencies = [
"semver", "semver",
"serde", "serde",
"serde_json", "serde_json",
"span",
"stdx", "stdx",
"toolchain", "toolchain",
"tracing", "tracing",
@ -1432,9 +1440,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_abi" name = "ra-ap-rustc_abi"
version = "0.42.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ae52e2d5b08762c9464b541345f519b8719d57b643b73632bade43ecece9dc" checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab"
dependencies = [ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"ra-ap-rustc_index", "ra-ap-rustc_index",
@ -1443,9 +1451,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_index" name = "ra-ap-rustc_index"
version = "0.42.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd7e10c7853fe79443d46e1d2d8ab09fe99926118e59653fb8b480d5045f126" checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"ra-ap-rustc_index_macros", "ra-ap-rustc_index_macros",
@ -1454,9 +1462,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_index_macros" name = "ra-ap-rustc_index_macros"
version = "0.42.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47f1d1c589be6c9a9e852fadee0e60329c0f862e87442ac2fe5adae30663cc76" checksum = "8782aaf3a113837c533dfb1c45df91cd17e1fdd1d2f9a20c2e0d1976025c4f1f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1466,9 +1474,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_lexer" name = "ra-ap-rustc_lexer"
version = "0.42.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa852373a757b4c723bbdc96ced7f575cad68a1e266e45fee12bc4c69a482d80" checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3"
dependencies = [ dependencies = [
"unicode-properties", "unicode-properties",
"unicode-xid", "unicode-xid",
@ -1476,9 +1484,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_parse_format" name = "ra-ap-rustc_parse_format"
version = "0.42.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2afe3c49accd95a53ac4d72ae13bafc7d115bdd80c8cd56ab09e6fc68f482210" checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933"
dependencies = [ dependencies = [
"ra-ap-rustc_index", "ra-ap-rustc_index",
"ra-ap-rustc_lexer", "ra-ap-rustc_lexer",
@ -1486,9 +1494,9 @@ dependencies = [
[[package]] [[package]]
name = "ra-ap-rustc_pattern_analysis" name = "ra-ap-rustc_pattern_analysis"
version = "0.42.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1253da23515d80c377a3998731e0ec3794997b62b989fd47db73efbde6a0bd7c" checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334"
dependencies = [ dependencies = [
"ra-ap-rustc_index", "ra-ap-rustc_index",
"rustc-hash", "rustc-hash",
@ -1598,6 +1606,7 @@ dependencies = [
"oorandom", "oorandom",
"parking_lot", "parking_lot",
"parser", "parser",
"paths",
"proc-macro-api", "proc-macro-api",
"profile", "profile",
"project-model", "project-model",
@ -1869,20 +1878,16 @@ dependencies = [
"itertools", "itertools",
"once_cell", "once_cell",
"parser", "parser",
"proc-macro2",
"quote",
"ra-ap-rustc_lexer", "ra-ap-rustc_lexer",
"rayon", "rayon",
"rowan", "rowan",
"rustc-hash", "rustc-hash",
"smol_str", "smol_str",
"sourcegen",
"stdx", "stdx",
"test-utils", "test-utils",
"text-edit", "text-edit",
"tracing", "tracing",
"triomphe", "triomphe",
"ungrammar",
] ]
[[package]] [[package]]
@ -2024,6 +2029,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
name = "toolchain" name = "toolchain"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"camino",
"home", "home",
] ]
@ -2109,7 +2115,6 @@ name = "tt"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"smol_str", "smol_str",
"span",
"stdx", "stdx",
"text-size", "text-size",
] ]
@ -2438,8 +2443,12 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"flate2", "flate2",
"itertools",
"proc-macro2",
"quote",
"stdx", "stdx",
"time", "time",
"ungrammar",
"write-json", "write-json",
"xflags", "xflags",
"xshell", "xshell",

View file

@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" }
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" }
ra-ap-rustc_lexer = { 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.42.0", default-features = false } ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false }
ra-ap-rustc_index = { version = "0.42.0", default-features = false } ra-ap-rustc_index = { version = "0.44.0", default-features = false }
ra-ap-rustc_abi = { version = "0.42.0", default-features = false } ra-ap-rustc_abi = { version = "0.44.0", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.42.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. # local crates that aren't published to crates.io. These should not have versions.
sourcegen = { path = "./crates/sourcegen" } sourcegen = { path = "./crates/sourcegen" }
@ -105,6 +105,7 @@ anyhow = "1.0.75"
arrayvec = "0.7.4" arrayvec = "0.7.4"
bitflags = "2.4.1" bitflags = "2.4.1"
cargo_metadata = "0.18.1" cargo_metadata = "0.18.1"
camino = "1.1.6"
chalk-solve = { version = "0.96.0", default-features = false } chalk-solve = { version = "0.96.0", default-features = false }
chalk-ir = "0.96.0" chalk-ir = "0.96.0"
chalk-recursive = { version = "0.96.0", default-features = false } chalk-recursive = { version = "0.96.0", default-features = false }

View file

@ -6,11 +6,12 @@
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
//! actual IO is done and lowered to input. //! actual IO is done and lowered to input.
use std::{fmt, mem, ops, str::FromStr}; use std::{fmt, mem, ops};
use cfg::CfgOptions; use cfg::CfgOptions;
use la_arena::{Arena, Idx, RawIdx}; use la_arena::{Arena, Idx, RawIdx};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use span::Edition;
use syntax::SmolStr; use syntax::SmolStr;
use triomphe::Arc; use triomphe::Arc;
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
@ -293,42 +294,11 @@ pub struct CrateData {
pub is_proc_macro: bool, 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)] #[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Env { pub struct Env {
entries: FxHashMap<String, String>, 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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency { pub struct Dependency {
pub crate_id: CrateId, 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) { pub fn sort_deps(&mut self) {
self.arena self.arena
.iter_mut() .iter_mut()
@ -653,6 +616,10 @@ impl CrateGraph {
} }
id_map id_map
} }
pub fn shrink_to_fit(&mut self) {
self.arena.shrink_to_fit();
}
} }
impl ops::Index<CrateId> for CrateGraph { 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 { impl Extend<(String, String)> for Env {
fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
self.entries.extend(iter); 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)] #[derive(Debug)]
pub struct CyclicDependenciesError { pub struct CyclicDependenciesError {
path: Vec<(CrateId, Option<CrateDisplayName>)>, path: Vec<(CrateId, Option<CrateDisplayName>)>,

View file

@ -14,9 +14,9 @@ use triomphe::Arc;
pub use crate::{ pub use crate::{
change::FileChange, change::FileChange,
input::{ input::{
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env,
DependencyKind, Edition, Env, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId,
SourceRootId, TargetLayoutLoadResult, TargetLayoutLoadResult,
}, },
}; };
pub use salsa::{self, Cancelled}; pub use salsa::{self, Cancelled};

View file

@ -8,10 +8,10 @@
#![warn(rust_2018_idioms, unused_lifetimes)] #![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 crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use paths::{AbsPath, AbsPathBuf}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::Deserialize; use serde::Deserialize;
@ -53,7 +53,7 @@ pub enum FlycheckConfig {
extra_args: Vec<String>, extra_args: Vec<String>,
extra_env: FxHashMap<String, String>, extra_env: FxHashMap<String, String>,
ansi_color_output: bool, ansi_color_output: bool,
target_dir: Option<PathBuf>, target_dir: Option<Utf8PathBuf>,
}, },
CustomCommand { CustomCommand {
command: String, command: String,
@ -363,7 +363,7 @@ impl FlycheckActor {
}); });
cmd.arg("--manifest-path"); 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 { for target in target_triples {
cmd.args(["--target", target.as_str()]); cmd.args(["--target", target.as_str()]);

View file

@ -55,13 +55,16 @@ pub struct CargoTestHandle {
} }
// Example of a cargo test command: // 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 { impl CargoTestHandle {
pub fn new(path: Option<&str>) -> std::io::Result<Self> { pub fn new(path: Option<&str>) -> std::io::Result<Self> {
let mut cmd = Command::new(Tool::Cargo.path()); let mut cmd = Command::new(Tool::Cargo.path());
cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.arg("test"); 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("--"); cmd.arg("--");
if let Some(path) = path { if let Some(path) = path {
cmd.arg(path); cmd.arg(path);

View file

@ -148,12 +148,12 @@ impl Attrs {
} }
} }
pub fn lang(&self) -> Option<&SmolStr> { pub fn lang(&self) -> Option<&str> {
self.by_key("lang").string_value() self.by_key("lang").string_value()
} }
pub fn lang_item(&self) -> Option<LangItem> { 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 { 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()) 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() self.by_key("export_name").string_value()
} }
@ -565,7 +565,7 @@ impl<'attr> AttrQuery<'attr> {
self.attrs().filter_map(|attr| attr.token_tree_value()) 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()) self.attrs().find_map(|attr| attr.string_value())
} }

View file

@ -453,8 +453,8 @@ impl ProcMacroData {
( (
def.name, def.name,
match def.kind { match def.kind {
ProcMacroKind::CustomDerive { helpers } => Some(helpers), ProcMacroKind::Derive { helpers } => Some(helpers),
ProcMacroKind::FnLike | ProcMacroKind::Attr => None, ProcMacroKind::Bang | ProcMacroKind::Attr => None,
}, },
) )
} else { } else {
@ -484,10 +484,11 @@ impl ExternCrateDeclData {
let extern_crate = &item_tree[loc.id.value]; let extern_crate = &item_tree[loc.id.value];
let name = extern_crate.name.clone(); let name = extern_crate.name.clone();
let krate = loc.container.krate();
let crate_id = if name == hir_expand::name![self] { let crate_id = if name == hir_expand::name![self] {
Some(loc.container.krate()) Some(krate)
} else { } else {
db.crate_def_map(loc.container.krate()) db.crate_def_map(krate)
.extern_prelude() .extern_prelude()
.find(|&(prelude_name, ..)| *prelude_name == name) .find(|&(prelude_name, ..)| *prelude_name == name)
.map(|(_, (root, _))| root.krate()) .map(|(_, (root, _))| root.krate())

View file

@ -22,8 +22,8 @@ use crate::{
lower::LowerCtx, lower::LowerCtx,
nameres::{DefMap, MacroSubNs}, nameres::{DefMap, MacroSubNs},
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup, AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
TypeOrConstParamId, TypeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
}; };
/// Data about a generic type parameter (to a function, struct, impl, ...). /// Data about a generic type parameter (to a function, struct, impl, ...).
@ -102,6 +102,52 @@ impl TypeOrConstParamData {
impl_from!(TypeParamData, ConstParamData for 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. /// Data about the generic parameters of a function, struct, impl, etc.
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct GenericParams { pub struct GenericParams {
@ -358,6 +404,15 @@ impl GenericParamsCollector {
} }
impl GenericParams { 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 /// Iterator of type_or_consts field
pub fn iter( pub fn iter(
&self, &self,
@ -365,6 +420,13 @@ impl GenericParams {
self.type_or_consts.iter() 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( pub(crate) fn generic_params_query(
db: &dyn DefDatabase, db: &dyn DefDatabase,
def: GenericDefId, def: GenericDefId,
@ -507,4 +569,18 @@ impl GenericParams {
.then(|| id) .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
}
})
}
} }

View file

@ -526,7 +526,7 @@ impl Printer<'_> {
} }
fn print_generic_params(&mut self, params: &GenericParams) { fn print_generic_params(&mut self, params: &GenericParams) {
if params.type_or_consts.is_empty() && params.lifetimes.is_empty() { if params.is_empty() {
return; return;
} }

View file

@ -192,7 +192,7 @@ impl LangItems {
pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> { pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
let attrs = db.attrs(item); 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( pub(crate) fn notable_traits_in_deps(

View file

@ -73,7 +73,7 @@ use std::{
use base_db::{ use base_db::{
impl_intern_key, impl_intern_key,
salsa::{self, impl_intern_value_trivial}, salsa::{self, impl_intern_value_trivial},
CrateId, Edition, CrateId,
}; };
use hir_expand::{ use hir_expand::{
builtin_attr_macro::BuiltinAttrExpander, builtin_attr_macro::BuiltinAttrExpander,
@ -90,7 +90,7 @@ use hir_expand::{
use item_tree::ExternBlock; use item_tree::ExternBlock;
use la_arena::Idx; use la_arena::Idx;
use nameres::DefMap; use nameres::DefMap;
use span::{AstIdNode, FileAstId, FileId, SyntaxContextId}; use span::{AstIdNode, Edition, FileAstId, FileId, SyntaxContextId};
use stdx::impl_from; use stdx::impl_from;
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};

View file

@ -1449,6 +1449,7 @@ ok!();
#[test] #[test]
fn test_new_std_matches() { fn test_new_std_matches() {
check( check(
//- edition:2021
r#" r#"
macro_rules! matches { macro_rules! matches {
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { ($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] #[test]
fn test_dollar_crate_lhs_is_not_meta() { fn test_dollar_crate_lhs_is_not_meta() {
check( check(

View file

@ -59,14 +59,14 @@ mod tests;
use std::ops::Deref; use std::ops::Deref;
use base_db::{CrateId, Edition, FileId}; use base_db::{CrateId, FileId};
use hir_expand::{ use hir_expand::{
name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
}; };
use itertools::Itertools; use itertools::Itertools;
use la_arena::Arena; use la_arena::Arena;
use rustc_hash::{FxHashMap, FxHashSet}; 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 stdx::format_to;
use syntax::{ast, SmolStr}; use syntax::{ast, SmolStr};
use triomphe::Arc; use triomphe::Arc;
@ -737,7 +737,7 @@ impl MacroSubNs {
MacroId::ProcMacroId(it) => { MacroId::ProcMacroId(it) => {
return match it.lookup(db).kind { return match it.lookup(db).kind {
ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr, ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
ProcMacroKind::FuncLike => Self::Bang, ProcMacroKind::Bang => Self::Bang,
}; };
} }
}; };

View file

@ -136,6 +136,7 @@ pub(super) fn derive_macro_as_call_id(
call_site: SyntaxContextId, call_site: SyntaxContextId,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
derive_macro_id: MacroCallId,
) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
let (macro_id, def_id) = resolver(item_attr.path.clone()) let (macro_id, def_id) = resolver(item_attr.path.clone())
.filter(|(_, def_id)| def_id.is_derive()) .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, ast_id: item_attr.ast_id,
derive_index: derive_pos, derive_index: derive_pos,
derive_attr_index, derive_attr_index,
derive_macro_id,
}, },
call_site, call_site,
); );

View file

@ -5,7 +5,7 @@
use std::{cmp::Ordering, iter, mem, ops::Not}; 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 cfg::{CfgExpr, CfgOptions};
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{
@ -22,9 +22,9 @@ use itertools::{izip, Itertools};
use la_arena::Idx; use la_arena::Idx;
use limit::Limit; use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use span::{ErasedFileAstId, FileAstId, Span, SyntaxContextId}; use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId};
use stdx::always; use stdx::always;
use syntax::{ast, SmolStr}; use syntax::ast;
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
@ -237,6 +237,8 @@ enum MacroDirectiveKind {
derive_attr: AttrId, derive_attr: AttrId,
derive_pos: usize, derive_pos: usize,
ctxt: SyntaxContextId, ctxt: SyntaxContextId,
/// The "parent" macro it is resolved to.
derive_macro_id: MacroCallId,
}, },
Attr { Attr {
ast_id: AstIdWithPath<ast::Item>, ast_id: AstIdWithPath<ast::Item>,
@ -312,7 +314,7 @@ impl DefCollector<'_> {
} }
} }
() if *attr_name == hir_expand::name![crate_type] => { () 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; self.is_proc_macro = true;
} }
} }
@ -602,7 +604,7 @@ impl DefCollector<'_> {
.intern(self.db); .intern(self.db);
self.define_proc_macro(def.name.clone(), proc_macro_id); self.define_proc_macro(def.name.clone(), proc_macro_id);
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); 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.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers);
} }
crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
@ -1146,7 +1148,13 @@ impl DefCollector<'_> {
return Resolved::Yes; 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( let id = derive_macro_as_call_id(
self.db, self.db,
ast_id, ast_id,
@ -1155,6 +1163,7 @@ impl DefCollector<'_> {
*call_site, *call_site,
self.def_map.krate, self.def_map.krate,
resolver, resolver,
*derive_macro_id,
); );
if let Ok((macro_id, def_id, call_id)) = id { if let Ok((macro_id, def_id, call_id)) = id {
@ -1224,6 +1233,8 @@ impl DefCollector<'_> {
_ => return Resolved::No, _ => 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 { if let MacroDefId {
kind: kind:
MacroDefKind::BuiltInAttr( MacroDefKind::BuiltInAttr(
@ -1252,6 +1263,7 @@ impl DefCollector<'_> {
return recollect_without(self); return recollect_without(self);
} }
}; };
let ast_id = ast_id.with_value(ast_adt_id); let ast_id = ast_id.with_value(ast_adt_id);
match attr.parse_path_comma_token_tree(self.db.upcast()) { match attr.parse_path_comma_token_tree(self.db.upcast()) {
@ -1267,6 +1279,7 @@ impl DefCollector<'_> {
derive_attr: attr.id, derive_attr: attr.id,
derive_pos: idx, derive_pos: idx,
ctxt: call_site.ctx, ctxt: call_site.ctx,
derive_macro_id: call_id,
}, },
container: directive.container, container: directive.container,
}); });
@ -1301,10 +1314,6 @@ impl DefCollector<'_> {
return recollect_without(self); 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 // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
// due to duplicating functions into macro expansions // due to duplicating functions into macro expansions
if matches!( 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( self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
directive.module_id, directive.module_id,
MacroCallKind::Derive { MacroCallKind::Derive {
ast_id: ast_id.ast_id, ast_id: ast_id.ast_id,
derive_attr_index: *derive_attr, derive_attr_index: *derive_attr,
derive_index: *derive_pos as u32, derive_index: *derive_pos as u32,
derive_macro_id: *derive_macro_id,
}, },
ast_id.path.clone(), ast_id.path.clone(),
)); ));
@ -1902,7 +1918,7 @@ impl ModCollector<'_, '_> {
} }
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) { 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 is_macro_use = attrs.by_key("macro_use").exists();
let module = &self.item_tree[module_id]; let module = &self.item_tree[module_id];
match &module.kind { match &module.kind {
@ -2146,7 +2162,7 @@ impl ModCollector<'_, '_> {
Some(it) => { Some(it) => {
// FIXME: a hacky way to create a Name from string. // FIXME: a hacky way to create a Name from string.
name = tt::Ident { name = tt::Ident {
text: it.clone(), text: it.into(),
span: Span { span: Span {
range: syntax::TextRange::empty(syntax::TextSize::new(0)), range: syntax::TextRange::empty(syntax::TextSize::new(0)),
anchor: span::SpanAnchor { anchor: span::SpanAnchor {

View file

@ -10,8 +10,8 @@
//! //!
//! `ReachedFixedPoint` signals about this. //! `ReachedFixedPoint` signals about this.
use base_db::Edition;
use hir_expand::{name::Name, Lookup}; use hir_expand::{name::Name, Lookup};
use span::Edition;
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{

View file

@ -13,18 +13,16 @@ pub struct ProcMacroDef {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum ProcMacroKind { pub enum ProcMacroKind {
CustomDerive { helpers: Box<[Name]> }, Derive { helpers: Box<[Name]> },
FnLike, Bang,
Attr, Attr,
} }
impl ProcMacroKind { impl ProcMacroKind {
pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind { pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind {
match self { match self {
ProcMacroKind::CustomDerive { .. } => { ProcMacroKind::Derive { .. } => hir_expand::proc_macro::ProcMacroKind::CustomDerive,
hir_expand::proc_macro::ProcMacroKind::CustomDerive ProcMacroKind::Bang => hir_expand::proc_macro::ProcMacroKind::Bang,
}
ProcMacroKind::FnLike => hir_expand::proc_macro::ProcMacroKind::FuncLike,
ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr, ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr,
} }
} }
@ -34,13 +32,13 @@ impl Attrs {
#[rustfmt::skip] #[rustfmt::skip]
pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> { pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
if self.is_proc_macro() { 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() { } else if self.is_proc_macro_attribute() {
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
} else if self.by_key("proc_macro_derive").exists() { } else if self.by_key("proc_macro_derive").exists() {
let derive = self.by_key("proc_macro_derive").tt_values().next()?; let derive = self.by_key("proc_macro_derive").tt_values().next()?;
let def = parse_macro_name_and_helper_attrs(&derive.token_trees) 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() { if def.is_none() {
tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive); tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);

View file

@ -24,6 +24,7 @@ use crate::{
nameres::{DefMap, MacroSubNs}, nameres::{DefMap, MacroSubNs},
path::{ModPath, Path, PathKind}, path::{ModPath, Path, PathKind},
per_ns::PerNs, per_ns::PerNs,
type_ref::LifetimeRef,
visibility::{RawVisibility, Visibility}, visibility::{RawVisibility, Visibility},
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
@ -120,6 +121,12 @@ pub enum ValueNs {
GenericParam(ConstParamId), GenericParam(ConstParamId),
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum LifetimeNs {
Static,
LifetimeParam(LifetimeParamId),
}
impl Resolver { impl Resolver {
/// Resolve known trait from std, like `std::futures::Future` /// Resolve known trait from std, like `std::futures::Future`
pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<TraitId> { 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)) 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. /// Returns a set of names available in the current scope.
/// ///
/// Note that this is a somewhat fuzzy concept -- internally, the compiler /// Note that this is a somewhat fuzzy concept -- internally, the compiler

View file

@ -8,8 +8,8 @@ use intern::Interned;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use span::{Span, SyntaxContextId}; use span::{Span, SyntaxContextId};
use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
use triomphe::Arc; use triomphe::ThinArc;
use crate::{ use crate::{
db::ExpandDatabase, db::ExpandDatabase,
@ -22,8 +22,7 @@ use crate::{
/// Syntactical attributes, without filtering of `cfg_attr`s. /// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)] #[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs { pub struct RawAttrs {
// FIXME: Make this a ThinArc entries: Option<ThinArc<(), Attr>>,
entries: Option<Arc<[Attr]>>,
} }
impl ops::Deref for RawAttrs { impl ops::Deref for RawAttrs {
@ -31,7 +30,7 @@ impl ops::Deref for RawAttrs {
fn deref(&self) -> &[Attr] { fn deref(&self) -> &[Attr] {
match &self.entries { match &self.entries {
Some(it) => it, Some(it) => &it.slice,
None => &[], None => &[],
} }
} }
@ -45,20 +44,34 @@ impl RawAttrs {
owner: &dyn ast::HasAttrs, owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>, span_map: SpanMapRef<'_>,
) -> Self { ) -> Self {
let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr { let entries: Vec<_> = collect_attrs(owner)
Either::Left(attr) => { .filter_map(|(id, attr)| match attr {
attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) 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, Either::Right(comment) => comment.doc_comment().map(|doc| {
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), let span = span_map.span_for_range(comment.syntax().text_range());
path: Interned::new(ModPath::from(crate::name!(doc))), Attr {
ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, id,
}), input: Some(Interned::new(AttrInput::Literal(tt::Literal {
}); // FIXME: Escape quotes from comment content
let entries: Arc<[Attr]> = Arc::from_iter(entries); 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( pub fn from_attrs_owner(
@ -75,16 +88,20 @@ impl RawAttrs {
(None, entries @ Some(_)) => Self { entries }, (None, entries @ Some(_)) => Self { entries },
(Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(entries), None) => Self { entries: Some(entries.clone()) },
(Some(a), Some(b)) => { (Some(a), Some(b)) => {
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32; let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
Self { let items = a
entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| { .slice
.iter()
.cloned()
.chain(b.slice.iter().map(|it| {
let mut it = it.clone(); let mut it = it.clone();
it.id.id = (it.id.ast_index() as u32 + last_ast_index) it.id.id = (it.id.ast_index() as u32 + last_ast_index)
| (it.id.cfg_attr_index().unwrap_or(0) as u32) | (it.id.cfg_attr_index().unwrap_or(0) as u32)
<< AttrId::AST_INDEX_BITS; << AttrId::AST_INDEX_BITS;
it 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 crate_graph = db.crate_graph();
let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> { let new_attrs =
let is_cfg_attr = self.iter()
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); .flat_map(|attr| -> SmallVec<[_; 1]> {
if !is_cfg_attr { let is_cfg_attr =
return smallvec![attr.clone()]; 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() { let subtree = match attr.token_tree_value() {
Some(it) => it, Some(it) => it,
_ => return smallvec![attr.clone()], _ => return smallvec![attr.clone()],
}; };
let (cfg, parts) = match parse_cfg_attr_input(subtree) { let (cfg, parts) = match parse_cfg_attr_input(subtree) {
Some(it) => it, Some(it) => it,
None => return smallvec![attr.clone()], None => return smallvec![attr.clone()],
}; };
let index = attr.id; let index = attr.id;
let attrs = parts let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
.enumerate() |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
.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_options = &crate_graph[krate].cfg_options;
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) };
let cfg = CfgExpr::parse(&cfg); let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) { if cfg_options.check(&cfg) == Some(false) {
smallvec![] smallvec![]
} else { } else {
cov_mark::hit!(cfg_attr_active); cov_mark::hit!(cfg_attr_active);
attrs.collect() attrs.collect()
} }
})); })
.collect::<Vec<_>>();
RawAttrs { entries: Some(new_attrs) } 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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AttrInput { pub enum AttrInput {
/// `#[attr = "string"]` /// `#[attr = "string"]`
// FIXME: This is losing span Literal(tt::Literal),
Literal(SmolStr),
/// `#[attr(subtree)]` /// `#[attr(subtree)]`
TokenTree(Box<tt::Subtree>), TokenTree(Box<tt::Subtree>),
} }
@ -188,7 +210,7 @@ pub enum AttrInput {
impl fmt::Display for AttrInput { impl fmt::Display for AttrInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), AttrInput::Literal(lit) => write!(f, " = {lit}"),
AttrInput::TokenTree(tt) => tt.fmt(f), AttrInput::TokenTree(tt) => tt.fmt(f),
} }
} }
@ -208,11 +230,10 @@ impl Attr {
})?); })?);
let span = span_map.span_for_range(range); let span = span_map.span_for_range(range);
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
let value = match lit.kind() { Some(Interned::new(AttrInput::Literal(tt::Literal {
ast::LiteralKind::String(string) => string.value()?.into(), text: lit.token().text().into(),
_ => lit.syntax().first_token()?.text().trim_matches('"').into(), span,
}; })))
Some(Interned::new(AttrInput::Literal(value)))
} else if let Some(tt) = ast.token_tree() { } else if let Some(tt) = ast.token_tree() {
let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span);
Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
@ -245,9 +266,8 @@ impl Attr {
} }
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => {
let input = match input.get(1) { let input = match input.get(1) {
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => { Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
//FIXME the trimming here isn't quite right, raw strings are not handled Some(Interned::new(AttrInput::Literal(lit.clone())))
Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into())))
} }
_ => None, _ => None,
}; };
@ -265,9 +285,14 @@ impl Attr {
impl Attr { impl Attr {
/// #[path = "string"] /// #[path = "string"]
pub fn string_value(&self) -> Option<&SmolStr> { pub fn string_value(&self) -> Option<&str> {
match self.input.as_deref()? { 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, _ => None,
} }
} }

View file

@ -1,11 +1,11 @@
//! Builtin macro //! Builtin macro
use base_db::{AnchoredPath, Edition, FileId}; use base_db::{AnchoredPath, FileId};
use cfg::CfgExpr; use cfg::CfgExpr;
use either::Either; use either::Either;
use itertools::Itertools; use itertools::Itertools;
use mbe::{parse_exprs_with_sep, parse_to_token_tree}; 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 syntax::ast::{self, AstToken};
use crate::{ use crate::{

View file

@ -10,7 +10,7 @@ use syntax::{
use tracing::{debug, warn}; use tracing::{debug, warn};
use tt::SmolStr; 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> { fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> {
if !attr.simple_name().as_deref().map(|v| v == "cfg")? { if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
@ -139,7 +139,7 @@ fn process_enum(
'variant: for variant in variants.variants() { 'variant: for variant in variants.variants() {
for attr in variant.attrs() { for attr in variant.attrs() {
if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { 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()); debug!("censoring type {:?}", variant.syntax());
remove.insert(variant.syntax().clone().into()); remove.insert(variant.syntax().clone().into());
// We need to remove the , as well // We need to remove the , as well
@ -180,7 +180,13 @@ pub(crate) fn process_cfg_attrs(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
) -> Option<FxHashSet<SyntaxElement>> { ) -> Option<FxHashSet<SyntaxElement>> {
// FIXME: #[cfg_eval] is not implemented. But it is not stable yet // 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; return None;
} }
let mut remove = FxHashSet::default(); let mut remove = FxHashSet::default();

View file

@ -24,7 +24,8 @@ use crate::{
HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
MacroFileId, 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. /// 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 /// 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 /// Lowers syntactic macro call to a token tree representation. That's a firewall
/// query, only typing in the macro call itself changes the returned /// query, only typing in the macro call itself changes the returned
/// subtree. /// 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. /// Fetches the expander for this macro.
#[salsa::transparent] #[salsa::transparent]
#[salsa::invoke(TokenExpander::macro_expander)] #[salsa::invoke(TokenExpander::macro_expander)]
@ -144,7 +151,7 @@ pub fn expand_speculative(
let span_map = RealSpanMap::absolute(FileId::BOGUS); let span_map = RealSpanMap::absolute(FileId::BOGUS);
let span_map = SpanMapRef::RealSpanMap(&span_map); 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 // Build the subtree and token mapping for the speculative args
let (mut tt, undo_info) = match loc.kind { 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 /// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive.
// they may differ in spans due to differing call sites... /// Other wise return the [macro_arg] for the macro_call_id.
fn macro_arg( ///
/// 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, db: &dyn ExpandDatabase,
id: MacroCallId, 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); let loc = db.lookup_intern_macro_call(id);
if let MacroCallLoc { if let MacroCallLoc {
@ -414,29 +433,30 @@ fn macro_arg(
} }
return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
} }
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro
let node = ast_id.to_ptr(db).to_node(&root); MacroCallKind::Derive { .. } => {
let censor_derive_input = censor_derive_input(derive_attr_index, &node); unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`")
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::Attr { ast_id, invoc_attr_index, .. } => { MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
let node = ast_id.to_ptr(db).to_node(&root); let node = ast_id.to_ptr(db).to_node(&root);
let attr_source = attr_source(invoc_attr_index, &node); let attr_source = attr_source(invoc_attr_index, &node);
let span = map.span_for_range( let span = map.span_for_range(
attr_source attr_source
.as_ref() .as_ref()
.and_then(|it| it.path()) .and_then(|it| it.path())
.map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), .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 { let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), 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 arg = &*macro_arg;
let res = 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>> { fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
let loc = db.lookup_intern_macro_call(id); 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 { let (expander, ast) = match loc.def.kind {
MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast), MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),

View file

@ -1,8 +1,8 @@
//! Compiled declarative macro expanders (`macro_rules!`` and `macro`) //! Compiled declarative macro expanders (`macro_rules!`` and `macro`)
use std::sync::OnceLock; use std::sync::OnceLock;
use base_db::{CrateId, Edition, VersionReq}; use base_db::{CrateId, VersionReq};
use span::{MacroCallId, Span}; use span::{MacroCallId, Span, SyntaxContextId};
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
use triomphe::Arc; use triomphe::Arc;
@ -10,13 +10,13 @@ use crate::{
attrs::RawAttrs, attrs::RawAttrs,
db::ExpandDatabase, db::ExpandDatabase,
hygiene::{apply_mark, Transparency}, hygiene::{apply_mark, Transparency},
tt, AstId, ExpandError, ExpandResult, tt, AstId, ExpandError, ExpandResult, Lookup,
}; };
/// Old-style `macro_rules` or the new macros 2.0 /// Old-style `macro_rules` or the new macros 2.0
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct DeclarativeMacroExpander { pub struct DeclarativeMacroExpander {
pub mac: mbe::DeclarativeMacro<span::Span>, pub mac: mbe::DeclarativeMacro,
pub transparency: Transparency, pub transparency: Transparency,
} }
@ -94,8 +94,6 @@ impl DeclarativeMacroExpander {
def_crate: CrateId, def_crate: CrateId,
id: AstId<ast::Macro>, id: AstId<ast::Macro>,
) -> Arc<DeclarativeMacroExpander> { ) -> 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, map) = crate::db::parse_with_map(db, id.file_id);
let root = root.syntax_node(); 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) { let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
ast::Macro::MacroRules(macro_rules) => ( ast::Macro::MacroRules(macro_rules) => (
match macro_rules.token_tree() { 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( None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
mbe::ParseError::Expected("expected a token tree".into()), "expected a token tree".into(),
is_2021, )),
),
}, },
transparency(&macro_rules).unwrap_or(Transparency::SemiTransparent), transparency(&macro_rules).unwrap_or(Transparency::SemiTransparent),
), ),
@ -163,12 +170,11 @@ impl DeclarativeMacroExpander {
map.span_for_range(macro_def.macro_token().unwrap().text_range()), 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( None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
mbe::ParseError::Expected("expected a token tree".into()), "expected a token tree".into(),
is_2021, )),
),
}, },
transparency(&macro_def).unwrap_or(Transparency::Opaque), transparency(&macro_def).unwrap_or(Transparency::Opaque),
), ),

View file

@ -30,10 +30,11 @@ use triomphe::Arc;
use std::{fmt, hash::Hash}; 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 either::Either;
use span::{ use span::{
ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData,
SyntaxContextId,
}; };
use syntax::{ use syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
@ -53,11 +54,9 @@ use crate::{
pub use crate::files::{AstId, ErasedAstId, InFile, InMacroFile, InRealFile}; 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 use span::{HirFileId, MacroCallId, MacroFileId};
pub type DeclarativeMacro = ::mbe::DeclarativeMacro<tt::Span>;
pub mod tt { pub mod tt {
pub use span::Span; pub use span::Span;
pub use tt::{DelimiterKind, Spacing}; 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). /// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
arg_id: MacroCallId, arg_id: MacroCallId,
error: Option<ExpandError>, error: Option<ExpandError>,
/// TODO: Doc /// The call site span of the eager macro
span: Span, span: Span,
} }
@ -212,7 +211,7 @@ pub enum MacroCallKind {
expand_to: ExpandTo, expand_to: ExpandTo,
/// Some if this is a macro call for an eager macro. Note that this is `None` /// Some if this is a macro call for an eager macro. Note that this is `None`
/// for the eager input macro file. /// 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 // leakage problems here
eager: Option<Arc<EagerCallInfo>>, eager: Option<Arc<EagerCallInfo>>,
}, },
@ -225,6 +224,9 @@ pub enum MacroCallKind {
derive_attr_index: AttrId, derive_attr_index: AttrId,
/// Index of the derive macro in the derive attribute /// Index of the derive macro in the derive attribute
derive_index: u32, 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 { Attr {
ast_id: AstId<ast::Item>, ast_id: AstId<ast::Item>,
@ -484,7 +486,7 @@ impl MacroDefId {
matches!( matches!(
self.kind, self.kind,
MacroDefKind::BuiltIn(..) MacroDefKind::BuiltIn(..)
| MacroDefKind::ProcMacro(_, ProcMacroKind::FuncLike, _) | MacroDefKind::ProcMacro(_, ProcMacroKind::Bang, _)
| MacroDefKind::BuiltInEager(..) | MacroDefKind::BuiltInEager(..)
| MacroDefKind::Declarative(..) | MacroDefKind::Declarative(..)
) )
@ -806,7 +808,8 @@ impl ExpansionInfo {
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; 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 = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) { let def_tt = match id.to_node(db) {

View file

@ -225,6 +225,26 @@ fn convert_path(
let mut segments = path.segments(); let mut segments = path.segments();
let segment = &segments.next()?; 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()? { let mut mod_path = match segment.kind()? {
ast::PathSegmentKind::Name(name_ref) => { ast::PathSegmentKind::Name(name_ref) => {
if name_ref.text() == "$crate" { if name_ref.text() == "$crate" {
@ -245,26 +265,8 @@ fn convert_path(
ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE)) ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE))
} }
ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()), ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
ast::PathSegmentKind::SuperKw => { ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
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::Type { .. } => { ast::PathSegmentKind::Type { .. } => {
// not allowed in imports // not allowed in imports
return None; return None;

View file

@ -23,7 +23,7 @@ impl ProcMacroId {
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum ProcMacroKind { pub enum ProcMacroKind {
CustomDerive, CustomDerive,
FuncLike, Bang,
Attr, Attr,
} }

View file

@ -47,13 +47,14 @@ hir-expand.workspace = true
base-db.workspace = true base-db.workspace = true
syntax.workspace = true syntax.workspace = true
limit.workspace = true limit.workspace = true
span.workspace = true
[dev-dependencies] [dev-dependencies]
expect-test = "1.4.0" expect-test = "1.4.0"
tracing.workspace = true tracing.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
tracing-tree.workspace = true tracing-tree.workspace = true
project-model = { path = "../project-model" } project-model.workspace = true
# local deps # local deps
test-utils.workspace = true test-utils.workspace = true

View file

@ -9,21 +9,21 @@ use chalk_ir::{
AdtId, DebruijnIndex, Scalar, AdtId, DebruijnIndex, Scalar,
}; };
use hir_def::{ use hir_def::{
builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId, builtin_type::BuiltinType, DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId,
GenericDefId, TraitId, TypeAliasId,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics,
GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy,
TyKind, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParamKind { pub enum ParamKind {
Type, Type,
Lifetime,
Const(Ty), Const(Ty),
} }
@ -107,6 +107,9 @@ impl<D> TyBuilder<D> {
ParamKind::Const(ty) => { ParamKind::Const(ty) => {
BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner) 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)); this.vec.extend(filler.take(this.remaining()).casted(Interner));
assert_eq!(this.remaining(), 0); 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 { let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x {
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
}); });
this.vec.extend(filler.casted(Interner)); this.vec.extend(filler.casted(Interner));
assert_eq!(this.remaining(), 0); assert_eq!(this.remaining(), 0);
@ -130,6 +134,7 @@ impl<D> TyBuilder<D> {
self.fill(|x| match x { self.fill(|x| match x {
ParamKind::Type => table.new_type_var().cast(Interner), ParamKind::Type => table.new_type_var().cast(Interner),
ParamKind::Const(ty) => table.new_const_var(ty.clone()).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) { fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) { match (a.data(Interner), e) {
(GenericArgData::Ty(_), ParamKind::Type) (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), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds),
} }
} }
@ -201,10 +207,11 @@ impl TyBuilder<()> {
Substitution::from_iter( Substitution::from_iter(
Interner, Interner,
params.iter_id().map(|id| match id { params.iter_id().map(|id| match id {
either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
either::Either::Right(id) => { GenericParamId::ConstParamId(id) => {
unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) 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()); assert!(generics.parent_generics().is_some() == parent_subst.is_some());
let params = generics let params = generics
.iter_self() .iter_self()
.map(|(id, data)| match data { .map(|(id, _data)| match id {
TypeOrConstParamData::TypeParamData(_) => ParamKind::Type, GenericParamId::TypeParamId(_) => ParamKind::Type,
TypeOrConstParamData::ConstParamData(_) => { GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)),
ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id))) GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
}
}) })
.collect(); .collect();
TyBuilder::new((), params, parent_subst) TyBuilder::new((), params, parent_subst)

View file

@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
}; };
chalk_ir::Binders::new(binders, bound) 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(..) => { crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
if let Some((future_trait, future_output)) = self if let Some((future_trait, future_output)) = self
.db .db

View file

@ -268,6 +268,13 @@ impl TyExt for Ty {
data.substitute(Interner, &subst).into_value_and_skipped_binders().0 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)) => { TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
@ -280,6 +287,13 @@ impl TyExt for Ty {
data.substitute(Interner, &opaque_ty.substitution) 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. // It always has an parameter for Future::Output type.
ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
}; };

View file

@ -2825,3 +2825,30 @@ fn unsized_local() {
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), |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)),
);
}

View file

@ -11,7 +11,7 @@ use base_db::{
use hir_def::{ use hir_def::{
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId,
}; };
use la_arena::ArenaMap; use la_arena::ArenaMap;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -23,9 +23,9 @@ use crate::{
layout::{Layout, LayoutError}, layout::{Layout, LayoutError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError}, mir::{BorrowckResult, MirBody, MirLowerError},
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits,
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment,
TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, TraitRef, Ty, TyDefId, ValueTyDefId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
#[salsa::invoke(crate::lower::return_type_impl_traits)] #[salsa::invoke(crate::lower::return_type_impl_traits)]
fn return_type_impl_traits( fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>;
&self,
def: FunctionId, #[salsa::invoke(crate::lower::type_alias_impl_traits)]
) -> Option<Arc<Binders<ReturnTypeImplTraits>>>; fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option<Arc<Binders<ImplTraits>>>;
#[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]

View file

@ -11,7 +11,6 @@ use hir_def::{ItemContainerId, Lookup};
use hir_expand::name; use hir_expand::name;
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use rustc_pattern_analysis::usefulness::{compute_match_usefulness, ValidityConstraint};
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
use tracing::debug; use tracing::debug;
use triomphe::Arc; use triomphe::Arc;
@ -234,13 +233,7 @@ impl ExprValidator {
return; return;
} }
let report = match compute_match_usefulness( let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) {
&cx,
m_arms.as_slice(),
scrut_ty.clone(),
ValidityConstraint::ValidOnly,
None,
) {
Ok(report) => report, Ok(report) => report,
Err(()) => return, Err(()) => return,
}; };
@ -282,13 +275,7 @@ impl ExprValidator {
continue; continue;
} }
let report = match compute_match_usefulness( let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
&cx,
&[match_arm],
ty.clone(),
ValidityConstraint::ValidOnly,
None,
) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
debug!(?e, "match usefulness error"); debug!(?e, "match usefulness error");

View file

@ -8,7 +8,8 @@ use rustc_hash::FxHashMap;
use rustc_pattern_analysis::{ use rustc_pattern_analysis::{
constructor::{Constructor, ConstructorSet, VariantVisibility}, constructor::{Constructor, ConstructorSet, VariantVisibility},
index::IdxContainer, index::IdxContainer,
Captures, PrivateUninhabitedField, TypeCx, usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
Captures, PatCx, PrivateUninhabitedField,
}; };
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use stdx::never; use stdx::never;
@ -59,6 +60,18 @@ impl<'p> MatchCheckCtx<'p> {
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns } 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 { fn is_uninhabited(&self, ty: &Ty) -> bool {
is_ty_uninhabited_from(ty, self.module, self.db) 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> { 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 ctor;
let fields: Vec<_>; let mut fields: Vec<_>;
let arity;
match pat.kind.as_ref() { match pat.kind.as_ref() {
PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
ctor = Wildcard; ctor = Wildcard;
fields = Vec::new(); fields = Vec::new();
arity = 0;
} }
PatKind::Deref { subpattern } => { PatKind::Deref { subpattern } => {
ctor = match pat.ty.kind(Interner) { ctor = match pat.ty.kind(Interner) {
@ -128,23 +143,22 @@ impl<'p> MatchCheckCtx<'p> {
} }
}; };
fields = singleton(self.lower_pat(subpattern)); fields = singleton(self.lower_pat(subpattern));
arity = 1;
} }
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { 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) { match pat.ty.kind(Interner) {
TyKind::Tuple(_, substs) => { TyKind::Tuple(_, substs) => {
ctor = Struct; ctor = Struct;
let mut wilds: Vec<_> = substs arity = substs.len(Interner);
.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
} }
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 // 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. // 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(_, // 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 // normally or through box-patterns. We'll have to figure out a proper
// solution when we introduce generalized deref patterns. Also need to // solution when we introduce generalized deref patterns. Also need to
// prevent mixing of those two options. // prevent mixing of those two options.
let pat = fields.retain(|ipat| ipat.idx == 0);
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)
};
ctor = Struct; ctor = Struct;
fields = singleton(field); arity = 1;
} }
&TyKind::Adt(adt, _) => { &TyKind::Adt(adt, _) => {
ctor = match pat.kind.as_ref() { 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(); 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 arity = variant.variant_data(self.db.upcast()).fields().len();
// 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;
} }
_ => { _ => {
never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty);
ctor = Wildcard; ctor = Wildcard;
fields = Vec::new(); fields.clear();
arity = 0;
} }
} }
} }
&PatKind::LiteralBool { value } => { &PatKind::LiteralBool { value } => {
ctor = Bool(value); ctor = Bool(value);
fields = Vec::new(); fields = Vec::new();
arity = 0;
} }
PatKind::Or { pats } => { PatKind::Or { pats } => {
ctor = Or; 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 }; 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 { 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 Error = ();
type Ty = Ty; type Ty = Ty;
type VariantIdx = EnumVariantId; type VariantIdx = EnumVariantId;
@ -453,7 +456,7 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
let variant = let variant =
pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt)); 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 { if let Some(variant) = variant {
match variant { match variant {
VariantId::EnumVariantId(v) => { VariantId::EnumVariantId(v) => {
@ -475,7 +478,6 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
} }
fn complexity_exceeded(&self) -> Result<(), Self::Error> { fn complexity_exceeded(&self) -> Result<(), Self::Error> {
// FIXME(Nadrieril): make use of the complexity counter.
Err(()) Err(())
} }
} }

View file

@ -938,18 +938,32 @@ impl HirDisplay for Ty {
f.end_location_link(); f.end_location_link();
if parameters.len(Interner) > 0 { if parameters.len(Interner) > 0 {
let generics = generics(db.upcast(), def.into()); let generics = generics(db.upcast(), def.into());
let (parent_params, self_param, type_params, const_params, _impl_trait_params) = let (
generics.provenance_split(); parent_params,
let total_len = parent_params + self_param + type_params + const_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? // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
if total_len > 0 { 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). // parent's params (those from enclosing impl or trait, if any).
let parameters = parameters.as_slice(Interner); let parameters = parameters.as_slice(Interner);
let fn_params_len = self_param + type_params + const_params; 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_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 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, "<")?; write!(f, "<")?;
f.write_joined(params, ", ")?; f.write_joined(params, ", ")?;
write!(f, ">")?; 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 // 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, &parameters);
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, ..) => { ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
let future_trait = db let future_trait = db
.lang_item(body.module(db.upcast()).krate(), LangItem::Future) .lang_item(body.module(db.upcast()).krate(), LangItem::Future)
@ -1228,6 +1256,20 @@ impl HirDisplay for Ty {
SizedByDefault::Sized { anchor: krate }, 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(..) => { ImplTraitId::AsyncBlockTypeImplTrait(..) => {
write!(f, "{{async block}}")?; write!(f, "{{async block}}")?;
} }
@ -1280,8 +1322,17 @@ fn hir_fmt_generics(
generic_def: Option<hir_def::GenericDefId>, generic_def: Option<hir_def::GenericDefId>,
) -> Result<(), HirDisplayError> { ) -> Result<(), HirDisplayError> {
let db = f.db; let db = f.db;
let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len()); if parameters.len(Interner) > 0 {
if parameters.len(Interner) + lifetime_args_count > 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() { let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
match generic_def match generic_def
.map(|generic_def_id| db.generic_defaults(generic_def_id)) .map(|generic_def_id| db.generic_defaults(generic_def_id))
@ -1307,6 +1358,11 @@ fn hir_fmt_generics(
return true; 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) { let default_parameter = match default_parameters.get(i) {
Some(it) => it, Some(it) => it,
None => return true, None => return true,
@ -1327,16 +1383,12 @@ fn hir_fmt_generics(
} else { } else {
parameters.as_slice(Interner) 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, "<")?; write!(f, "<")?;
let mut first = true; 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 { for generic_arg in parameters_to_write {
if !first { if !first {
write!(f, ", ")?; write!(f, ", ")?;

View file

@ -25,8 +25,11 @@ pub(crate) mod unify;
use std::{convert::identity, iter, ops::Index}; use std::{convert::identity, iter, ops::Index};
use chalk_ir::{ use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, cast::Cast,
Scalar, TyKind, TypeFlags, Variance, fold::TypeFoldable,
interner::HasInterner,
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance,
}; };
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
@ -39,7 +42,7 @@ use hir_def::{
layout::Integer, layout::Integer,
path::{ModPath, Path}, path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef, type_ref::{LifetimeRef, TypeRef},
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
TupleFieldId, TupleId, TypeAliasId, VariantId, TupleFieldId, TupleId, TypeAliasId, VariantId,
}; };
@ -53,14 +56,14 @@ use triomphe::Arc;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
fold_tys, fold_tys,
infer::coerce::CoerceMany, infer::{coerce::CoerceMany, unify::InferenceTable},
lower::ImplTraitLoweringMode, lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id, static_lifetime, to_assoc_type_id,
traits::FnTrait, traits::FnTrait,
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment, ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
TraitRef, Ty, TyBuilder, TyExt, TraitEnvironment, Ty, TyBuilder, TyExt,
}; };
// This lint has a false positive here. See the link below for details. // 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. /// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>, pub type_of_pat: ArenaMap<PatId, Ty>,
pub type_of_binding: ArenaMap<BindingId, 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. /// 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>, pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> {
} }
fn collect_const(&mut self, data: &ConstData) { 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) { fn collect_static(&mut self, data: &StaticData) {
@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> {
self.write_binding_ty(self_param, ty); 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) { for (ty, pat) in param_tys.zip(&*self.body.params) {
let ty = self.insert_type_vars(ty); let ty = self.insert_type_vars(ty);
let ty = self.normalize_associated_types_in(ty); let ty = self.normalize_associated_types_in(ty);
self.infer_top_pat(*pat, &ty); self.infer_top_pat(*pat, &ty);
params_and_ret_tys.push(ty);
} }
let return_ty = &*data.ret_type; 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) { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function. // RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
let result = let result = self.insert_inference_vars_for_impl_trait(
self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); return_ty,
rpits.clone(),
fn_placeholders,
);
let rpits = rpits.skip_binders(); let rpits = rpits.skip_binders();
for (id, _) in rpits.impl_traits.iter() { for (id, _) in rpits.impl_traits.iter() {
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { 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_ty = self.normalize_associated_types_in(return_ty);
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); 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, &mut self,
t: T, t: T,
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>, rpits: Arc<chalk_ir::Binders<crate::ImplTraits>>,
fn_placeholders: Substitution, placeholders: Substitution,
) -> T ) -> T
where where
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>, 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()) { let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx,
_ => unreachable!(), _ => unreachable!(),
}; };
let bounds = let bounds =
@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> {
let var = self.table.new_type_var(); let var = self.table.new_type_var();
let var_subst = Substitution::from1(Interner, var.clone()); let var_subst = Substitution::from1(Interner, var.clone());
for bound in bounds { for bound in bounds {
let predicate = let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders);
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
let (var_predicate, binders) = let (var_predicate, binders) =
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled 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, var_predicate,
rpits.clone(), rpits.clone(),
fn_placeholders.clone(), placeholders.clone(),
); );
self.push_obligation(var_predicate.cast(Interner)); 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) { fn infer_body(&mut self) {
match self.return_coercion { match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr), Some(_) => self.infer_return(self.body.body_expr),
@ -918,6 +1037,12 @@ impl<'a> InferenceContext<'a> {
self.result.standard_types.unknown.clone() 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. /// 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 { fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
self.table.insert_type_vars_shallow(ty) self.table.insert_type_vars_shallow(ty)

View file

@ -276,6 +276,23 @@ impl InferenceTable<'_> {
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); 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 // Consider coercing the subtype to a DST
if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) {
return Ok(ret); return Ok(ret);

View file

@ -8,13 +8,12 @@ use std::{
use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
generics::TypeOrConstParamData,
hir::{ hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
}, },
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path}, path::{GenericArgs, Path},
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId, BlockId, FieldId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
}; };
use hir_expand::name::{name, Name}; use hir_expand::name::{name, Name};
use stdx::always; use stdx::always;
@ -1816,10 +1815,17 @@ impl InferenceContext<'_> {
def_generics: Generics, def_generics: Generics,
generic_args: Option<&GenericArgs>, generic_args: Option<&GenericArgs>,
) -> Substitution { ) -> Substitution {
let (parent_params, self_params, type_params, const_params, impl_trait_params) = let (
def_generics.provenance_split(); 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 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); let mut substs = Vec::with_capacity(total_len);
// handle provided arguments // handle provided arguments
@ -1828,8 +1834,7 @@ impl InferenceContext<'_> {
for (arg, kind_id) in generic_args for (arg, kind_id) in generic_args
.args .args
.iter() .iter()
.filter(|arg| !matches!(arg, GenericArg::Lifetime(_))) .take(type_params + const_params + lifetime_params)
.take(type_params + const_params)
.zip(def_generics.iter_id()) .zip(def_generics.iter_id())
{ {
if let Some(g) = generic_arg_to_chalk( if let Some(g) = generic_arg_to_chalk(
@ -1850,6 +1855,7 @@ impl InferenceContext<'_> {
DebruijnIndex::INNERMOST, DebruijnIndex::INNERMOST,
) )
}, },
|this, lt_ref| this.make_lifetime(lt_ref),
) { ) {
substs.push(g); substs.push(g);
} }
@ -1858,16 +1864,17 @@ impl InferenceContext<'_> {
// Handle everything else as unknown. This also handles generic arguments for the method's // 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. // parent (impl or trait), which should come after those for the method.
for (id, data) in def_generics.iter().skip(substs.len()) { for (id, _data) in def_generics.iter().skip(substs.len()) {
match data { match id {
TypeOrConstParamData::TypeParamData(_) => { GenericParamId::TypeParamId(_) => {
substs.push(self.table.new_type_var().cast(Interner)) substs.push(self.table.new_type_var().cast(Interner))
} }
TypeOrConstParamData::ConstParamData(_) => substs.push( GenericParamId::ConstParamId(id) => {
self.table substs.push(self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner))
.new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id))) }
.cast(Interner), GenericParamId::LifetimeParamId(_) => {
), substs.push(self.table.new_lifetime_var().cast(Interner))
}
} }
} }
assert_eq!(substs.len(), total_len); assert_eq!(substs.len(), total_len);

View file

@ -11,15 +11,15 @@ use stdx::never;
use crate::{ use crate::{
builder::ParamKind, builder::ParamKind,
consteval, consteval, error_lifetime,
method_resolution::{self, VisibleFromModule}, method_resolution::{self, VisibleFromModule},
to_chalk_trait_id, to_chalk_trait_id,
utils::generics, utils::generics,
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
ValueTyDefId, TyKind, ValueTyDefId,
}; };
use super::{ExprOrPatId, InferenceContext, TraitRef}; use super::{ExprOrPatId, InferenceContext};
impl InferenceContext<'_> { impl InferenceContext<'_> {
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> { 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 { it.next().unwrap_or_else(|| match x {
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
}) })
}) })
.build(); .build();

View file

@ -10,16 +10,18 @@ use chalk_solve::infer::ParameterEnaVariableExt;
use either::Either; use either::Either;
use ena::unify::UnifyKey; use ena::unify::UnifyKey;
use hir_expand::name; use hir_expand::name;
use rustc_hash::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use triomphe::Arc; use triomphe::Arc;
use super::{InferOk, InferResult, InferenceContext, TypeError}; use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{ use crate::{
consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts,
to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData,
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
TyKind, VariableKind, WhereClause,
}; };
impl InferenceContext<'_> { impl InferenceContext<'_> {
@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
pub(crate) struct InferenceTable<'a> { pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase, pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>, pub(crate) trait_env: Arc<TraitEnvironment>,
pub(crate) atpit_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
var_unification_table: ChalkInferenceTable, var_unification_table: ChalkInferenceTable,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>, type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> {
InferenceTable { InferenceTable {
db, db,
trait_env, trait_env,
atpit_coercion_table: None,
var_unification_table: ChalkInferenceTable::new(), var_unification_table: ChalkInferenceTable::new(),
type_variable_table: SmallVec::new(), type_variable_table: SmallVec::new(),
pending_obligations: Vec::new(), pending_obligations: Vec::new(),
@ -803,6 +807,7 @@ impl<'a> InferenceTable<'a> {
.fill(|it| { .fill(|it| {
let arg = match it { let arg = match it {
ParamKind::Type => self.new_type_var(), ParamKind::Type => self.new_type_var(),
ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"),
ParamKind::Const(_) => unreachable!("Tuple with const parameter"), ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
}; };
arg_tys.push(arg.clone()); arg_tys.push(arg.clone());
@ -857,11 +862,16 @@ impl<'a> InferenceTable<'a> {
where where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>, T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{ {
fold_tys_and_consts( fold_generic_args(
ty, ty,
|it, _| match it { |arg, _| match arg {
Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), GenericArgData::Ty(ty) => GenericArgData::Ty(self.insert_type_vars_shallow(ty)),
Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), // 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, DebruijnIndex::INNERMOST,
) )

View file

@ -389,6 +389,9 @@ pub fn layout_of_ty_query(
let infer = db.infer(func.into()); let infer = db.infer(func.into());
return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
} }
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
return Err(LayoutError::NotImplemented);
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
return Err(LayoutError::NotImplemented) return Err(LayoutError::NotImplemented)
} }

View file

@ -15,7 +15,8 @@ extern crate rustc_abi;
#[cfg(not(feature = "in-rust-tree"))] #[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_abi as rustc_abi; 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; extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
mod builder; mod builder;
@ -89,8 +90,8 @@ pub use lower::{
}; };
pub use mapping::{ pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, 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, lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id,
to_placeholder_idx, to_foreign_def_id, to_placeholder_idx,
}; };
pub use method_resolution::check_orphan_rules; pub use method_resolution::check_orphan_rules;
pub use traits::TraitEnvironment; pub use traits::TraitEnvironment;
@ -334,11 +335,23 @@ pub(crate) fn make_binders_with_count<T: HasInterner<Interner = Interner>>(
generics: &Generics, generics: &Generics,
value: T, value: T,
) -> Binders<T> { ) -> Binders<T> {
let it = generics.iter_id().take(count).map(|id| match id { let it = generics.iter_id().take(count);
Either::Left(_) => None,
Either::Right(id) => Some(db.const_param_ty(id)), Binders::new(
}); VariableKinds::from_iter(
crate::make_type_and_const_binders(it, value) 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>>( 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)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ImplTraitId { pub enum ImplTraitId {
ReturnTypeImplTrait(hir_def::FunctionId, RpitId), ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
} }
impl_intern_value_trivial!(ImplTraitId); impl_intern_value_trivial!(ImplTraitId);
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ReturnTypeImplTraits { pub struct ImplTraits {
pub(crate) impl_traits: Arena<ReturnTypeImplTrait>, pub(crate) impl_traits: Arena<ImplTrait>,
} }
has_interner!(ReturnTypeImplTraits); has_interner!(ImplTraits);
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ReturnTypeImplTrait { pub struct ImplTrait {
pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>, pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
} }
pub type RpitId = Idx<ReturnTypeImplTrait>; pub type ImplTraitIdx = Idx<ImplTrait>;
pub fn static_lifetime() -> Lifetime { pub fn static_lifetime() -> Lifetime {
LifetimeData::Static.intern(Interner) LifetimeData::Static.intern(Interner)
} }
pub fn error_lifetime() -> Lifetime {
static_lifetime()
}
pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>( pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
t: T, t: T,
for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty, 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) 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 /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
/// ensures there are no unbound variables or inference variables anywhere in /// ensures there are no unbound variables or inference variables anywhere in
/// the `t`. /// the `t`.

View file

@ -24,17 +24,20 @@ use hir_def::{
data::adt::StructKind, data::adt::StructKind,
expander::Expander, expander::Expander,
generics::{ generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
WherePredicateTypeTarget,
}, },
lang_item::LangItem, lang_item::LangItem,
nameres::MacroSubNs, nameres::MacroSubNs,
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs}, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, type_ref::{
ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
},
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId,
ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, Lookup, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId,
TypeParamId, UnionId, VariantId, UnionId, VariantId,
}; };
use hir_expand::{name::Name, ExpandResult}; use hir_expand::{name::Name, ExpandResult};
use intern::Interned; use intern::Interned;
@ -52,18 +55,18 @@ use crate::{
unknown_const_as_generic, unknown_const_as_generic,
}, },
db::HirDatabase, db::HirDatabase,
make_binders, error_lifetime, make_binders,
mapping::{from_chalk_trait_id, ToChalk}, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::Generics,
utils::{ 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, InTypeConstIdMetadata,
}, },
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
TyKind, WhereClause,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -76,7 +79,7 @@ enum ImplTraitLoweringState {
/// we're grouping the mutable data (the counter and this field) together /// we're grouping the mutable data (the counter and this field) together
/// with the immutable context (the references to the DB and resolver). /// with the immutable context (the references to the DB and resolver).
/// Splitting this up would be a possible fix. /// Splitting this up would be a possible fix.
Opaque(RefCell<Arena<ReturnTypeImplTrait>>), Opaque(RefCell<Arena<ImplTrait>>),
Param(Cell<u16>), Param(Cell<u16>),
Variable(Cell<u16>), Variable(Cell<u16>),
Disallowed, Disallowed,
@ -275,9 +278,11 @@ impl<'a> TyLoweringContext<'a> {
let inner_ty = self.lower_ty(inner); let inner_ty = self.lower_ty(inner);
TyKind::Slice(inner_ty).intern(Interner) TyKind::Slice(inner_ty).intern(Interner)
} }
TypeRef::Reference(inner, _, mutability) => { TypeRef::Reference(inner, lifetime, mutability) => {
let inner_ty = self.lower_ty(inner); 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) TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
.intern(Interner) .intern(Interner)
} }
@ -301,15 +306,18 @@ impl<'a> TyLoweringContext<'a> {
TypeRef::ImplTrait(bounds) => { TypeRef::ImplTrait(bounds) => {
match &self.impl_trait_mode { match &self.impl_trait_mode {
ImplTraitLoweringState::Opaque(opaque_type_data) => { ImplTraitLoweringState::Opaque(opaque_type_data) => {
let func = match self.resolver.generic_def() { let origin = match self.resolver.generic_def() {
Some(GenericDefId::FunctionId(f)) => f, Some(GenericDefId::FunctionId(it)) => Either::Left(it),
_ => panic!("opaque impl trait lowering in non-function"), 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 // this dance is to make sure the data is in the right
// place even if we encounter more opaque types while // place even if we encounter more opaque types while
// lowering the bounds // 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()), bounds: crate::make_single_type_binders(Vec::new()),
}); });
// We don't want to lower the bounds inside the binders // We don't want to lower the bounds inside the binders
@ -323,13 +331,17 @@ impl<'a> TyLoweringContext<'a> {
// away instead of two. // away instead of two.
let actual_opaque_type_data = self let actual_opaque_type_data = self
.with_debruijn(DebruijnIndex::INNERMOST, |ctx| { .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; 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 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); let parameters = generics.bound_vars_subst(self.db, self.in_binders);
TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner)
} }
@ -344,13 +356,18 @@ impl<'a> TyLoweringContext<'a> {
.filter(|(_, data)| { .filter(|(_, data)| {
matches!( matches!(
data, data,
TypeOrConstParamData::TypeParamData(data) GenericParamDataRef::TypeParamData(data)
if data.provenance == TypeParamProvenance::ArgumentImplTrait if data.provenance == TypeParamProvenance::ArgumentImplTrait
) )
}) })
.nth(idx as usize) .nth(idx as usize)
.map_or(TyKind::Error, |(id, _)| { .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) param.intern(Interner)
} else { } else {
@ -367,11 +384,12 @@ impl<'a> TyLoweringContext<'a> {
list_params, list_params,
const_params, const_params,
_impl_trait_params, _impl_trait_params,
_lifetime_params,
) = if let Some(def) = self.resolver.generic_def() { ) = if let Some(def) = self.resolver.generic_def() {
let generics = generics(self.db.upcast(), def); let generics = generics(self.db.upcast(), def);
generics.provenance_split() generics.provenance_split()
} else { } else {
(0, 0, 0, 0, 0) (0, 0, 0, 0, 0, 0)
}; };
TyKind::BoundVar(BoundVar::new( TyKind::BoundVar(BoundVar::new(
self.in_binders, self.in_binders,
@ -808,9 +826,16 @@ impl<'a> TyLoweringContext<'a> {
return Substitution::empty(Interner); return Substitution::empty(Interner);
}; };
let def_generics = generics(self.db.upcast(), def); let def_generics = generics(self.db.upcast(), def);
let (parent_params, self_params, type_params, const_params, impl_trait_params) = let (
def_generics.provenance_split(); parent_params,
let item_len = self_params + type_params + const_params + impl_trait_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 total_len = parent_params + item_len;
let ty_error = TyKind::Error.intern(Interner).cast(Interner); let ty_error = TyKind::Error.intern(Interner).cast(Interner);
@ -825,7 +850,10 @@ impl<'a> TyLoweringContext<'a> {
.take(self_params) .take(self_params)
{ {
if let Some(id) = def_generic_iter.next() { if let Some(id) = def_generic_iter.next() {
assert!(id.is_left()); assert!(matches!(
id,
GenericParamId::TypeParamId(_) | GenericParamId::LifetimeParamId(_)
));
substs.push(x); substs.push(x);
} }
} }
@ -858,6 +886,7 @@ impl<'a> TyLoweringContext<'a> {
&mut (), &mut (),
|_, type_ref| self.lower_ty(type_ref), |_, type_ref| self.lower_ty(type_ref),
|_, const_ref, ty| self.lower_const(const_ref, ty), |_, const_ref, ty| self.lower_const(const_ref, ty),
|_, lifetime_ref| self.lower_lifetime(lifetime_ref),
) { ) {
had_explicit_args = true; had_explicit_args = true;
substs.push(x); 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 { } else {
fill_self_params(); fill_self_params();
} }
// These params include those of parent. // These params include those of parent.
let remaining_params: SmallVec<[_; 2]> = def_generic_iter let remaining_params: SmallVec<[_; 2]> = def_generic_iter
.map(|eid| match eid { .map(|id| match id {
Either::Left(_) => ty_error.clone(), GenericParamId::ConstParamId(x) => {
Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)), unknown_const_as_generic(self.db.const_param_ty(x))
}
GenericParamId::TypeParamId(_) => ty_error.clone(),
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
}) })
.collect(); .collect();
assert_eq!(remaining_params.len() + substs.len(), total_len); 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(), binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
); );
if let Some(type_ref) = &binding.type_ref { if let Some(type_ref) = &binding.type_ref {
if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = if let (
(type_ref, &self.impl_trait_mode) TypeRef::ImplTrait(bounds),
ImplTraitLoweringState::Param(_)
| ImplTraitLoweringState::Variable(_)
| ImplTraitLoweringState::Disallowed,
) = (type_ref, &self.impl_trait_mode)
{ {
for bound in bounds { for bound in bounds {
predicates.extend( predicates.extend(
@ -1270,11 +1333,7 @@ impl<'a> TyLoweringContext<'a> {
} }
} }
fn lower_impl_trait( fn lower_impl_trait(&self, bounds: &[Interned<TypeBound>], krate: CrateId) -> ImplTrait {
&self,
bounds: &[Interned<TypeBound>],
func: FunctionId,
) -> ReturnTypeImplTrait {
cov_mark::hit!(lower_rpit); cov_mark::hit!(lower_rpit);
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
@ -1284,7 +1343,6 @@ impl<'a> TyLoweringContext<'a> {
.collect(); .collect();
if !ctx.unsized_types.borrow().contains(&self_ty) { if !ctx.unsized_types.borrow().contains(&self_ty) {
let krate = func.krate(ctx.db.upcast());
let sized_trait = ctx let sized_trait = ctx
.db .db
.lang_item(krate, LangItem::Sized) .lang_item(krate, LangItem::Sized)
@ -1301,7 +1359,34 @@ impl<'a> TyLoweringContext<'a> {
} }
predicates 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))| { let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| {
match p { match p {
TypeOrConstParamData::TypeParamData(p) => { GenericParamDataRef::TypeParamData(p) => {
let mut ty = let mut ty =
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
// Each default can only refer to previous parameters. // 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); ty = fallback_bound_vars(ty, idx, parent_start_idx);
crate::make_binders(db, &generic_params, ty.cast(Interner)) 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( let mut val = p.default.as_ref().map_or_else(
|| { || unknown_const_as_generic(db.const_param_ty(id)),
unknown_const_as_generic(
db.const_param_ty(ConstParamId::from_unchecked(id)),
)
},
|c| { |c| {
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
c.cast(Interner) c.cast(Interner)
@ -1710,6 +1795,10 @@ pub(crate) fn generic_defaults_query(
val = fallback_bound_vars(val, idx, parent_start_idx); val = fallback_bound_vars(val, idx, parent_start_idx);
make_binders(db, &generic_params, val) 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 // we still need one default per parameter
let defaults = Arc::from_iter(generic_params.iter_id().map(|id| { let defaults = Arc::from_iter(generic_params.iter_id().map(|id| {
let val = match id { let val = match id {
Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), 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) 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 generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast()); let resolver = t.resolver(db.upcast());
let ctx = TyLoweringContext::new(db, &resolver, t.into()) let ctx = TyLoweringContext::new(db, &resolver, t.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable); .with_type_param_mode(ParamLoweringMode::Variable);
let type_alias_data = db.type_alias_data(t); let type_alias_data = db.type_alias_data(t);
if type_alias_data.is_extern { 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( pub(crate) fn return_type_impl_traits(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: hir_def::FunctionId, 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 // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = db.function_data(def); let data = db.function_data(def);
let resolver = def.resolver(db.upcast()); let resolver = def.resolver(db.upcast());
@ -2038,7 +2129,7 @@ pub(crate) fn return_type_impl_traits(
.with_type_param_mode(ParamLoweringMode::Variable); .with_type_param_mode(ParamLoweringMode::Variable);
let _ret = ctx_ret.lower_ty(&data.ret_type); let _ret = ctx_ret.lower_ty(&data.ret_type);
let generics = generics(db.upcast(), def.into()); 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 { impl_traits: match ctx_ret.impl_trait_mode {
ImplTraitLoweringState::Opaque(x) => x.into_inner(), ImplTraitLoweringState::Opaque(x) => x.into_inner(),
_ => unreachable!(), _ => 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 { pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability {
match m { match m {
hir_def::type_ref::Mutability::Shared => Mutability::Not, 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. /// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime.
pub(crate) fn generic_arg_to_chalk<'a, T>( pub(crate) fn generic_arg_to_chalk<'a, T>(
db: &dyn HirDatabase, db: &dyn HirDatabase,
kind_id: Either<TypeParamId, ConstParamId>, kind_id: GenericParamId,
arg: &'a GenericArg, arg: &'a GenericArg,
this: &mut T, this: &mut T,
for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a, for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a, for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
) -> Option<crate::GenericArg> { ) -> Option<crate::GenericArg> {
let kind = match kind_id { let kind = match kind_id {
Either::Left(_) => ParamKind::Type, GenericParamId::TypeParamId(_) => ParamKind::Type,
Either::Right(id) => { GenericParamId::ConstParamId(id) => {
let ty = db.const_param_ty(id); let ty = db.const_param_ty(id);
ParamKind::Const(ty) ParamKind::Const(ty)
} }
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
}; };
Some(match (arg, kind) { Some(match (arg, kind) {
(GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner), (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::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::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)) => { (GenericArg::Type(t), ParamKind::Const(c_ty)) => {
// We want to recover simple idents, which parser detects them // We want to recover simple idents, which parser detects them
// as types. Maybe here is not the best place to do it, but // 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) 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),
}) })
} }

View file

@ -151,6 +151,14 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L
db.lookup_intern_lifetime_param_id(interned_id) 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 { pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId {
chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id))
} }

View file

@ -4,7 +4,7 @@
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
use std::ops::ControlFlow; use std::ops::ControlFlow;
use base_db::{CrateId, Edition}; use base_db::CrateId;
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
use hir_def::{ use hir_def::{
data::{adt::StructFlags, ImplData}, data::{adt::StructFlags, ImplData},
@ -15,6 +15,7 @@ use hir_def::{
use hir_expand::name::Name; use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use span::Edition;
use stdx::never; use stdx::never;
use triomphe::Arc; use triomphe::Arc;
@ -643,7 +644,7 @@ pub fn is_dyn_method(
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
return None; 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 fn_params = fn_subst.len(Interner) - trait_params;
let trait_ref = TraitRef { let trait_ref = TraitRef {
trait_id: to_chalk_trait_id(trait_id), 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 { let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
return (func, fn_subst); 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 fn_params = fn_subst.len(Interner) - trait_params;
let trait_ref = TraitRef { let trait_ref = TraitRef {
trait_id: to_chalk_trait_id(trait_id), 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 // the methods by autoderef order of *receiver types*, not *self
// types*. // types*.
let mut table = InferenceTable::new(db, env.clone()); let mut table = InferenceTable::new(db, env);
let ty = table.instantiate_canonical(ty.clone()); let ty = table.instantiate_canonical(ty.clone());
let deref_chain = autoderef_method_receiver(&mut table, ty); let deref_chain = autoderef_method_receiver(&mut table, ty);
@ -1044,7 +1045,7 @@ fn iterate_method_candidates_with_autoref(
let ref_muted = Canonical { let ref_muted = Canonical {
value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
.intern(Interner), .intern(Interner),
binders: receiver_ty.binders.clone(), binders: receiver_ty.binders,
}; };
iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) 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>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> 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 // 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 // be found in any of the derefs of receiver_ty, so we have to go through
// that, including raw derefs. // that, including raw derefs.
@ -1456,7 +1457,7 @@ fn is_valid_trait_method_candidate(
if let Some(receiver_ty) = receiver_ty { if let Some(receiver_ty) = receiver_ty {
check_that!(data.has_self_param()); 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) .fill_with_inference_vars(table)
.build(); .build();

View file

@ -1931,7 +1931,11 @@ impl Evaluator<'_> {
ty: &Ty, ty: &Ty,
locals: &Locals, locals: &Locals,
mm: &mut ComplexMemoryMap, mm: &mut ComplexMemoryMap,
stack_depth_limit: usize,
) -> Result<()> { ) -> Result<()> {
if stack_depth_limit.checked_sub(1).is_none() {
return Err(MirEvalError::StackOverflow);
}
match ty.kind(Interner) { match ty.kind(Interner) {
TyKind::Ref(_, _, t) => { TyKind::Ref(_, _, t) => {
let size = this.size_align_of(t, locals)?; let size = this.size_align_of(t, locals)?;
@ -1970,7 +1974,14 @@ impl Evaluator<'_> {
if let Some(ty) = check_inner { if let Some(ty) = check_inner {
for i in 0..count { for i in 0..count {
let offset = element_size * i; 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")?; let size = this.size_of_sized(inner, locals, "inner of array")?;
for i in 0..len { for i in 0..len {
let offset = i * size; 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) => { chalk_ir::TyKind::Tuple(_, subst) => {
@ -1993,7 +2011,14 @@ impl Evaluator<'_> {
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
let offset = layout.fields.offset(id).bytes_usize(); let offset = layout.fields.offset(id).bytes_usize();
let size = this.layout(ty)?.size.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 { chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
@ -2008,7 +2033,14 @@ impl Evaluator<'_> {
.bytes_usize(); .bytes_usize();
let ty = &field_types[f].clone().substitute(Interner, subst); let ty = &field_types[f].clone().substitute(Interner, subst);
let size = this.layout(ty)?.size.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,
)?;
} }
} }
AdtId::EnumId(e) => { AdtId::EnumId(e) => {
@ -2027,7 +2059,14 @@ impl Evaluator<'_> {
l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
let ty = &field_types[f].clone().substitute(Interner, subst); let ty = &field_types[f].clone().substitute(Interner, subst);
let size = this.layout(ty)?.size.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,
)?;
} }
} }
} }
@ -2038,7 +2077,7 @@ impl Evaluator<'_> {
Ok(()) Ok(())
} }
let mut mm = ComplexMemoryMap::default(); 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) Ok(mm)
} }
@ -2317,7 +2356,7 @@ impl Evaluator<'_> {
fn exec_fn_with_args( fn exec_fn_with_args(
&mut self, &mut self,
def: FunctionId, mut def: FunctionId,
args: &[IntervalAndTy], args: &[IntervalAndTy],
generic_args: Substitution, generic_args: Substitution,
locals: &Locals, locals: &Locals,
@ -2335,6 +2374,9 @@ impl Evaluator<'_> {
)? { )? {
return Ok(None); 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)); let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? {
MirOrDynIndex::Dyn(self_ty_idx) => { MirOrDynIndex::Dyn(self_ty_idx) => {

View file

@ -13,7 +13,7 @@ use crate::mir::eval::{
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId,
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy,
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan,
ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, Mutability, Result, Substitution, Ty, TyBuilder, TyExt,
}; };
mod simd; mod simd;
@ -158,6 +158,25 @@ impl Evaluator<'_> {
Ok(false) 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 /// Clone has special impls for tuples and function pointers
fn exec_clone( fn exec_clone(
&mut self, &mut self,
@ -291,9 +310,14 @@ impl Evaluator<'_> {
use LangItem::*; use LangItem::*;
let candidate = self.db.lang_attr(def.into())?; let candidate = self.db.lang_attr(def.into())?;
// We want to execute these functions with special logic // 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); 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 None
} }
@ -309,43 +333,6 @@ impl Evaluator<'_> {
let mut args = args.iter(); let mut args = args.iter();
match it { match it {
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())), 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 => { SliceLen => {
let arg = args.next().ok_or(MirEvalError::InternalError( let arg = args.next().ok_or(MirEvalError::InternalError(
"argument of <[T]>::len() is not provided".into(), "argument of <[T]>::len() is not provided".into(),

View file

@ -82,6 +82,9 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
}; };
filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
} }
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
not_supported!("associated type impl trait");
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
not_supported!("async block impl trait"); not_supported!("async block impl trait");
} }
@ -181,8 +184,16 @@ impl Filler<'_> {
self.generics self.generics
.as_ref() .as_ref()
.and_then(|it| it.iter().nth(b.index)) .and_then(|it| it.iter().nth(b.index))
.unwrap() .and_then(|(id, _)| match id {
.0, 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(), self.subst.clone(),
) )
})? })?

View file

@ -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() { if let Some(syntax_ptr) = body_source_map.self_param_syntax() {
let root = db.parse_or_expand(syntax_ptr.file_id); let root = db.parse_or_expand(syntax_ptr.file_id);
let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone());
types.push((node.clone(), ty)); types.push((node, ty));
} }
} }

View file

@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
trait Foo<'a> {} trait Foo<'a> {}
fn foo(foo: &dyn for<'a> Foo<'a>) {} fn foo(foo: &dyn for<'a> Foo<'a>) {}
// ^^^ &dyn Foo // ^^^ &dyn Foo<'static>
"#, "#,
); );
} }

View file

@ -1109,7 +1109,7 @@ fn var_args() {
#[lang = "va_list"] #[lang = "va_list"]
pub struct VaListImpl<'f>; pub struct VaListImpl<'f>;
fn my_fn(foo: ...) {} fn my_fn(foo: ...) {}
//^^^ VaListImpl<'_> //^^^ VaListImpl<'static>
"#, "#,
); );
} }

View file

@ -896,13 +896,13 @@ fn flush(&self) {
"#, "#,
expect![[r#" expect![[r#"
123..127 'self': &Mutex<T> 123..127 'self': &Mutex<T>
150..152 '{}': MutexGuard<'_, T> 150..152 '{}': MutexGuard<'static, T>
234..238 'self': &{unknown} 234..238 'self': &{unknown}
240..290 '{ ...()); }': () 240..290 '{ ...()); }': ()
250..251 'w': &Mutex<BufWriter> 250..251 'w': &Mutex<BufWriter>
276..287 '*(w.lock())': BufWriter 276..287 '*(w.lock())': BufWriter
278..279 'w': &Mutex<BufWriter> 278..279 'w': &Mutex<BufWriter>
278..286 'w.lock()': MutexGuard<'_, BufWriter> 278..286 'w.lock()': MutexGuard<'static, BufWriter>
"#]], "#]],
); );
} }

View file

@ -3092,7 +3092,7 @@ fn main() {
389..394 'boxed': Box<Foo<i32>> 389..394 'boxed': Box<Foo<i32>>
389..406 'boxed....nner()': &i32 389..406 'boxed....nner()': &i32
416..421 'good1': &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 424..446 'Foo::g...boxed)': &i32
439..445 '&boxed': &Box<Foo<i32>> 439..445 '&boxed': &Box<Foo<i32>>
440..445 'boxed': Box<Foo<i32>> 440..445 'boxed': Box<Foo<i32>>
@ -3100,7 +3100,7 @@ fn main() {
464..469 'boxed': Box<Foo<i32>> 464..469 'boxed': Box<Foo<i32>>
464..480 'boxed....self()': &Foo<i32> 464..480 'boxed....self()': &Foo<i32>
490..495 'good2': &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> 498..519 'Foo::g...boxed)': &Foo<i32>
512..518 '&boxed': &Box<Foo<i32>> 512..518 '&boxed': &Box<Foo<i32>>
513..518 'boxed': Box<Foo<i32>> 513..518 'boxed': Box<Foo<i32>>
@ -3659,7 +3659,7 @@ fn main() {
let are = "are"; let are = "are";
let count = 10; let count = 10;
builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_> // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static>
} }
"#, "#,
); );

View file

@ -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] #[test]
fn simple_return_pos_impl_trait() { fn simple_return_pos_impl_trait() {
cov_mark::check!(lower_rpit); 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>>
}
"#,
);
}

View file

@ -9,18 +9,18 @@ use chalk_ir::{
fold::{FallibleTypeFolder, Shift}, fold::{FallibleTypeFolder, Shift},
BoundVar, DebruijnIndex, BoundVar, DebruijnIndex,
}; };
use either::Either;
use hir_def::{ use hir_def::{
db::DefDatabase, db::DefDatabase,
generics::{ generics::{
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
WherePredicateTypeTarget, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
}, },
lang_item::LangItem, lang_item::LangItem,
resolver::{HasResolver, TypeNs}, resolver::{HasResolver, TypeNs},
type_ref::{TraitBoundModifier, TypeRef}, type_ref::{TraitBoundModifier, TypeRef},
ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, Lookup, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ItemContainerId,
OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, LifetimeParamId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId,
TypeParamId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use intern::Interned; use intern::Interned;
@ -270,64 +270,130 @@ pub(crate) struct Generics {
} }
impl Generics { impl Generics {
pub(crate) fn iter_id(&self) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + '_ { pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
self.iter().map(|(id, data)| match data { self.iter().map(|(id, _)| id)
TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)),
TypeOrConstParamData::ConstParamData(_) => {
Either::Right(ConstParamId::from_unchecked(id))
}
})
} }
/// Iterator over types and const params of self, then parent. /// Iterator over types and const params of self, then parent.
pub(crate) fn iter<'a>( pub(crate) fn iter<'a>(
&'a self, &'a self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a { ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
let to_toc_id = |it: &'a Generics| { let from_toc_id = |it: &'a Generics| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) 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. /// Iterate over types and const params without parent params.
pub(crate) fn iter_self<'a>( pub(crate) fn iter_self<'a>(
&'a self, &'a self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a { ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
let to_toc_id = |it: &'a Generics| { let from_toc_id = |it: &'a Generics| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) 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. /// Iterator over types and const params of parent.
pub(crate) fn iter_parent( #[allow(clippy::needless_lifetimes)]
&self, pub(crate) fn iter_parent<'a>(
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &TypeOrConstParamData)> { &'a self,
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
self.parent_generics().into_iter().flat_map(|it| { self.parent_generics().into_iter().flat_map(|it| {
let to_toc_id = let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); let id = TypeOrConstParamId { parent: it.def, local_id };
it.params.iter().map(to_toc_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. /// Returns total number of generic parameters in scope, including those from parent.
pub(crate) fn len(&self) -> usize { pub(crate) fn len(&self) -> usize {
let parent = self.parent_generics().map_or(0, Generics::len); 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 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 { 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() self.params.type_or_consts.len()
} }
/// (parent total, self param, type param list, const param list, impl trait) /// (parent total, self param, type params, const params, impl trait list, lifetimes)
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) { pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) {
let mut self_params = 0; let mut self_params = 0;
let mut type_params = 0; let mut type_params = 0;
let mut impl_trait_params = 0; let mut impl_trait_params = 0;
let mut const_params = 0; let mut const_params = 0;
let mut lifetime_params = 0;
self.params.iter().for_each(|(_, data)| match data { self.params.iter().for_each(|(_, data)| match data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList => type_params += 1, TypeParamProvenance::TypeParamList => type_params += 1,
@ -337,8 +403,10 @@ impl Generics {
TypeOrConstParamData::ConstParamData(_) => const_params += 1, 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); 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> { 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> { pub(crate) fn parent_generics(&self) -> Option<&Generics> {
self.parent_generics.as_deref() self.parent_generics.as_deref()
} }
@ -371,10 +459,15 @@ impl Generics {
Substitution::from_iter( Substitution::from_iter(
Interner, Interner,
self.iter_id().enumerate().map(|(idx, id)| match id { self.iter_id().enumerate().map(|(idx, id)| match id {
Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner), GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx)
Either::Right(id) => BoundVar::new(debruijn, idx)
.to_const(Interner, db.const_param_ty(id)) .to_const(Interner, db.const_param_ty(id))
.cast(Interner), .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( Substitution::from_iter(
Interner, Interner,
self.iter_id().map(|id| match id { 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) 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)) .to_const(Interner, db.const_param_ty(id))
.cast(Interner), .cast(Interner),
GenericParamId::LifetimeParamId(id) => {
crate::lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner)
}
}), }),
) )
} }

View file

@ -186,18 +186,29 @@ impl HirDisplay for Struct {
} }
StructKind::Record => { StructKind::Record => {
let has_where_clause = write_where_clause(def_id, f)?; let has_where_clause = write_where_clause(def_id, f)?;
let fields = self.fields(f.db); if let Some(limit) = f.entity_limit {
f.write_char(if !has_where_clause { ' ' } else { '\n' })?; let fields = self.fields(f.db);
if fields.is_empty() { let count = fields.len().min(limit);
f.write_str("{}")?; f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
} else { if count == 0 {
f.write_str("{\n")?; if fields.is_empty() {
for field in self.fields(f.db) { f.write_str("{}")?;
f.write_str(" ")?; } else {
field.hir_fmt(f)?; f.write_str("{ /* … */ }")?;
f.write_str(",\n")?; }
} 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)?, StructKind::Unit => _ = write_where_clause(def_id, f)?,

View file

@ -38,7 +38,7 @@ mod display;
use std::{iter, mem::discriminant, ops::ControlFlow}; use std::{iter, mem::discriminant, ops::ControlFlow};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId};
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
body::{BodyDiagnostic, SyntheticSyntax}, body::{BodyDiagnostic, SyntheticSyntax},
@ -65,7 +65,7 @@ use hir_ty::{
consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
db::InternedClosure, db::InternedClosure,
diagnostics::BodyValidationDiagnostic, diagnostics::BodyValidationDiagnostic,
known_const_to_ast, error_lifetime, known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
method_resolution::{self, TyFingerprint}, method_resolution::{self, TyFingerprint},
mir::{interpret_mir, MutBorrowKind}, mir::{interpret_mir, MutBorrowKind},
@ -79,6 +79,7 @@ use hir_ty::{
use itertools::Itertools; use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind; use nameres::diagnostics::DefDiagnosticKind;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use span::Edition;
use stdx::{impl_from, never}; use stdx::{impl_from, never};
use syntax::{ use syntax::{
ast::{self, HasAttrs as _, HasName}, ast::{self, HasAttrs as _, HasName},
@ -971,7 +972,7 @@ fn precise_macro_call_location(
MacroKind::ProcMacro, 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()); let node = ast_id.to_node(db.upcast());
// Compute the precise location of the macro name's token in the derive // Compute the precise location of the macro name's token in the derive
// list. // list.
@ -1099,13 +1100,14 @@ impl Field {
VariantDef::Union(it) => it.id.into(), VariantDef::Union(it) => it.id.into(),
VariantDef::Variant(it) => it.parent_enum(db).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) let substs = TyBuilder::subst_for_def(db, def_id, None)
.fill(|x| match x { .fill(|x| match x {
ParamKind::Type => { ParamKind::Type => {
generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
} }
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
}) })
.build(); .build();
let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); 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> { 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); return Err(LayoutError::HasPlaceholder);
} }
let krate = self.krate(db).id; let krate = self.krate(db).id;
@ -1440,13 +1442,14 @@ impl Adt {
/// the greatest API, FIXME find a better one. /// the greatest API, FIXME find a better one.
pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator<Item = Type>) -> Type { pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator<Item = Type>) -> Type {
let id = AdtId::from(self); 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) let ty = TyBuilder::def_ty(db, id.into(), None)
.fill(|x| { .fill(|x| {
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x { match x {
ParamKind::Type => r.cast(Interner), ParamKind::Type => r.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
} }
}) })
.build(); .build();
@ -1859,12 +1862,13 @@ impl Function {
ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::TraitId(it) => Some(it.into()),
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, 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 { let mut filler = |x: &_| match x {
ParamKind::Type => { ParamKind::Type => {
generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
} }
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
}; };
let parent_substs = let parent_substs =
@ -1954,7 +1958,7 @@ impl Function {
ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::TraitId(it) => Some(it.into()),
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, 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| { let parent_substs = parent_id.map(|id| {
TyBuilder::subst_for_def(db, id, None) TyBuilder::subst_for_def(db, id, None)
.fill(|x| match x { .fill(|x| match x {
@ -1963,6 +1967,7 @@ impl Function {
.unwrap_or_else(|| TyKind::Error.intern(Interner)) .unwrap_or_else(|| TyKind::Error.intern(Interner))
.cast(Interner), .cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
}) })
.build() .build()
}); });
@ -2007,8 +2012,7 @@ impl Function {
} }
let data = db.function_data(self.id); let data = db.function_data(self.id);
data.name.to_smol_str() == "main" data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main")
|| data.attrs.export_name().map(core::ops::Deref::deref) == Some("main")
} }
/// Does this function have the ignore attribute? /// 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 { let mut filler = |x: &_| match x {
ParamKind::Type => { ParamKind::Type => {
generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
} }
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), 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(); 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 { MacroId::ProcMacroId(it) => match it.lookup(db.upcast()).kind {
ProcMacroKind::CustomDerive => MacroKind::Derive, ProcMacroKind::CustomDerive => MacroKind::Derive,
ProcMacroKind::FuncLike => MacroKind::ProcMacro, ProcMacroKind::Bang => MacroKind::ProcMacro,
ProcMacroKind::Attr => MacroKind::Attr, ProcMacroKind::Attr => MacroKind::Attr,
}, },
} }
@ -3628,16 +3633,41 @@ impl Impl {
.filter(filter), .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 all
} }
pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> { 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(); let mut all = Vec::new();
for Crate { id } in krate.transitive_reverse_dependencies(db) { for Crate { id } in krate.transitive_reverse_dependencies(db) {
let impls = db.trait_impls_in_crate(id); let impls = db.trait_impls_in_crate(id);
all.extend(impls.for_trait(trait_.id).map(Self::from)) 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 all
} }
@ -3683,7 +3713,7 @@ impl Impl {
let macro_file = src.file_id.macro_file()?; let macro_file = src.file_id.macro_file()?;
let loc = macro_file.macro_call_id.lookup(db.upcast()); let loc = macro_file.macro_call_id.lookup(db.upcast());
let (derive_attr, derive_index) = match loc.kind { 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; let module_id = self.id.lookup(db.upcast()).container;
( (
db.crate_def_map(module_id.krate())[module_id.local_id] 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. // FIXME: this code is not covered in tests.
unknown_const_as_generic(ty.clone()) unknown_const_as_generic(ty.clone())
} }
ParamKind::Lifetime => error_lifetime().cast(Interner),
} }
}) })
.build(); .build();
@ -4144,6 +4175,7 @@ impl Type {
match it { match it {
ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner), ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Lifetime => error_lifetime().cast(Interner),
} }
}) })
.build(); .build();

View file

@ -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 // Note that we need special case for 0 param constructors because of multi cartesian
// product // product
let variant_exprs: Vec<Expr> = if param_exprs.is_empty() { 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 { } else {
param_exprs param_exprs
.into_iter() .into_iter()
@ -462,7 +462,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
/// # Impl method tactic /// # 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. /// 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 /// Methods defined in impl blocks that are generic and methods that are themselves have
/// generics are ignored for performance reasons. /// generics are ignored for performance reasons.

View file

@ -5617,7 +5617,7 @@ fn func<T: Debug>(i: Struct<'_, T>) {
fun_name(i); fun_name(i);
} }
fn $0fun_name(i: Struct<'_, T>) { fn $0fun_name(i: Struct<'static, T>) {
foo(i); foo(i);
} }
"#, "#,

View file

@ -614,7 +614,7 @@ struct Foo<'a, T> {
} }
impl<'a, T> 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) self.field.bar(b)
} }
} }

View file

@ -961,11 +961,11 @@ struct Foo { field: i32 }
impl Foo { fn foo(&self) { $0 } }"#, impl Foo { fn foo(&self) { $0 } }"#,
expect![[r#" expect![[r#"
fd self.field i32 fd self.field i32
me self.foo() fn(&self)
lc self &Foo lc self &Foo
sp Self Foo sp Self Foo
st Foo Foo st Foo Foo
bt u32 u32 bt u32 u32
me self.foo() fn(&self)
"#]], "#]],
); );
check( check(
@ -975,11 +975,11 @@ struct Foo(i32);
impl Foo { fn foo(&mut self) { $0 } }"#, impl Foo { fn foo(&mut self) { $0 } }"#,
expect![[r#" expect![[r#"
fd self.0 i32 fd self.0 i32
me self.foo() fn(&mut self)
lc self &mut Foo lc self &mut Foo
sp Self Foo sp Self Foo
st Foo Foo st Foo Foo
bt u32 u32 bt u32 u32
me self.foo() fn(&mut self)
"#]], "#]],
); );
} }

View file

@ -186,11 +186,11 @@ fn add_function_impl(
if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
); );
let completion_kind = if func.has_self_param(ctx.db) { let completion_kind = CompletionItemKind::SymbolKind(if func.has_self_param(ctx.db) {
CompletionItemKind::Method SymbolKind::Method
} else { } else {
CompletionItemKind::SymbolKind(SymbolKind::Function) SymbolKind::Function
}; });
let mut item = CompletionItem::new(completion_kind, replacement_range, label); let mut item = CompletionItem::new(completion_kind, replacement_range, label);
item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) item.lookup_by(format!("fn {}", fn_name.display(ctx.db)))

View file

@ -75,8 +75,8 @@ impl Future for A {}
fn foo(a: A) { a.$0 } fn foo(a: A) { a.$0 }
"#, "#,
expect![[r#" expect![[r#"
kw await expr.await
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
kw await expr.await
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
@ -102,8 +102,8 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
kw await expr.await
me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
kw await expr.await
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)
@ -131,8 +131,8 @@ impl IntoFuture for A {}
fn foo(a: A) { a.$0 } fn foo(a: A) { a.$0 }
"#, "#,
expect![[r#" expect![[r#"
kw await expr.await
me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
kw await expr.await
sn box Box::new(expr) sn box Box::new(expr)
sn call function(expr) sn call function(expr)
sn dbg dbg!(expr) sn dbg dbg!(expr)

View file

@ -540,7 +540,7 @@ impl CompletionContext<'_> {
/// Whether the given trait is an operator trait or not. /// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
match trait_.attrs(self.db).lang() { 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, None => false,
} }
} }

View file

@ -342,7 +342,6 @@ pub enum CompletionItemKind {
BuiltinType, BuiltinType,
InferredType, InferredType,
Keyword, Keyword,
Method,
Snippet, Snippet,
UnresolvedReference, UnresolvedReference,
Expression, Expression,
@ -369,6 +368,7 @@ impl CompletionItemKind {
SymbolKind::LifetimeParam => "lt", SymbolKind::LifetimeParam => "lt",
SymbolKind::Local => "lc", SymbolKind::Local => "lc",
SymbolKind::Macro => "ma", SymbolKind::Macro => "ma",
SymbolKind::Method => "me",
SymbolKind::ProcMacro => "pm", SymbolKind::ProcMacro => "pm",
SymbolKind::Module => "md", SymbolKind::Module => "md",
SymbolKind::SelfParam => "sp", SymbolKind::SelfParam => "sp",
@ -388,7 +388,6 @@ impl CompletionItemKind {
CompletionItemKind::BuiltinType => "bt", CompletionItemKind::BuiltinType => "bt",
CompletionItemKind::InferredType => "it", CompletionItemKind::InferredType => "it",
CompletionItemKind::Keyword => "kw", CompletionItemKind::Keyword => "kw",
CompletionItemKind::Method => "me",
CompletionItemKind::Snippet => "sn", CompletionItemKind::Snippet => "sn",
CompletionItemKind::UnresolvedReference => "??", CompletionItemKind::UnresolvedReference => "??",
CompletionItemKind::Expression => "ex", CompletionItemKind::Expression => "ex",

View file

@ -312,7 +312,7 @@ pub(crate) fn render_expr(
None => ctx.source_range(), 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!( let snippet = format!(
"{}$0", "{}$0",
@ -677,10 +677,11 @@ mod tests {
#[track_caller] #[track_caller]
fn check_function_relevance(ra_fixture: &str, expect: Expect) { fn check_function_relevance(ra_fixture: &str, expect: Expect) {
let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::Method) let actual: Vec<_> =
.into_iter() do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method))
.map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) .into_iter()
.collect(); .map(|item| (item.detail.unwrap_or_default(), item.relevance.function))
.collect();
expect.assert_debug_eq(&actual); expect.assert_debug_eq(&actual);
} }
@ -1392,7 +1393,10 @@ impl S {
/// Method docs /// Method docs
fn bar(self) { self.$0 } fn bar(self) { self.$0 }
}"#, }"#,
&[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], &[
CompletionItemKind::SymbolKind(SymbolKind::Method),
CompletionItemKind::SymbolKind(SymbolKind::Field),
],
expect![[r#" expect![[r#"
[ [
CompletionItem { CompletionItem {
@ -1400,7 +1404,9 @@ impl S {
source_range: 94..94, source_range: 94..94,
delete: 94..94, delete: 94..94,
insert: "bar()$0", insert: "bar()$0",
kind: Method, kind: SymbolKind(
Method,
),
lookup: "bar", lookup: "bar",
detail: "fn(self)", detail: "fn(self)",
documentation: Documentation( documentation: Documentation(
@ -1520,7 +1526,7 @@ impl S {
} }
fn foo(s: S) { s.$0 } fn foo(s: S) { s.$0 }
"#, "#,
CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Method),
expect![[r#" expect![[r#"
[ [
CompletionItem { CompletionItem {
@ -1528,7 +1534,9 @@ fn foo(s: S) { s.$0 }
source_range: 81..81, source_range: 81..81,
delete: 81..81, delete: 81..81,
insert: "the_method()$0", insert: "the_method()$0",
kind: Method, kind: SymbolKind(
Method,
),
lookup: "the_method", lookup: "the_method",
detail: "fn(&self)", detail: "fn(&self)",
relevance: CompletionRelevance { relevance: CompletionRelevance {
@ -2408,7 +2416,10 @@ impl Foo { fn baz(&self) -> u32 { 0 } }
fn foo(f: Foo) { let _: &u32 = f.b$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#" expect![[r#"
[ [
CompletionItem { CompletionItem {
@ -2416,7 +2427,9 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
source_range: 109..110, source_range: 109..110,
delete: 109..110, delete: 109..110,
insert: "baz()$0", insert: "baz()$0",
kind: Method, kind: SymbolKind(
Method,
),
lookup: "baz", lookup: "baz",
detail: "fn(&self) -> u32", detail: "fn(&self) -> u32",
relevance: CompletionRelevance { relevance: CompletionRelevance {
@ -2631,7 +2644,7 @@ fn main() {
let _: bool = (9 > 2).not$0; let _: bool = (9 > 2).not$0;
} }
"#, "#,
&[CompletionItemKind::Snippet, CompletionItemKind::Method], &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
expect![[r#" expect![[r#"
sn not [snippet] sn not [snippet]
me not() (use ops::Not) [type_could_unify+requires_import] me not() (use ops::Not) [type_could_unify+requires_import]
@ -2664,7 +2677,7 @@ fn main() {
S.$0 S.$0
} }
"#, "#,
&[CompletionItemKind::Snippet, CompletionItemKind::Method], &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
expect![[r#" expect![[r#"
me f() [] me f() []
sn ref [] sn ref []
@ -2907,7 +2920,7 @@ fn main() {
} }
"#, "#,
&[ &[
CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Method),
CompletionItemKind::SymbolKind(SymbolKind::Field), CompletionItemKind::SymbolKind(SymbolKind::Field),
CompletionItemKind::SymbolKind(SymbolKind::Function), CompletionItemKind::SymbolKind(SymbolKind::Function),
], ],
@ -2918,7 +2931,9 @@ fn main() {
source_range: 193..193, source_range: 193..193,
delete: 193..193, delete: 193..193,
insert: "flush()$0", insert: "flush()$0",
kind: Method, kind: SymbolKind(
Method,
),
lookup: "flush", lookup: "flush",
detail: "fn(&self)", detail: "fn(&self)",
relevance: CompletionRelevance { relevance: CompletionRelevance {
@ -2941,7 +2956,9 @@ fn main() {
source_range: 193..193, source_range: 193..193,
delete: 193..193, delete: 193..193,
insert: "write()$0", insert: "write()$0",
kind: Method, kind: SymbolKind(
Method,
),
lookup: "write", lookup: "write",
detail: "fn(&self)", detail: "fn(&self)",
relevance: CompletionRelevance { relevance: CompletionRelevance {

View file

@ -68,11 +68,11 @@ fn render(
}; };
let has_self_param = func.self_param(db).is_some(); let has_self_param = func.self_param(db).is_some();
let mut item = CompletionItem::new( let mut item = CompletionItem::new(
if has_self_param { CompletionItemKind::SymbolKind(if has_self_param {
CompletionItemKind::Method SymbolKind::Method
} else { } else {
CompletionItemKind::SymbolKind(SymbolKind::Function) SymbolKind::Function
}, }),
ctx.source_range(), ctx.source_range(),
call.clone(), call.clone(),
); );

View file

@ -127,6 +127,7 @@ impl Unit {
en Enum Enum en Enum Enum
fn function() fn() fn function() fn()
fn local_func() fn() fn local_func() fn()
me self.foo() fn(self)
lc self Unit lc self Unit
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
@ -166,7 +167,6 @@ impl Unit {
kw use kw use
kw while kw while
kw while let kw while let
me self.foo() fn(self)
sn macro_rules sn macro_rules
sn pd sn pd
sn ppd sn ppd

View file

@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'static, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'static, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit

View file

@ -1,6 +1,7 @@
//! Tests that don't fit into a specific category. //! Tests that don't fit into a specific category.
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use ide_db::SymbolKind;
use crate::{ use crate::{
tests::{ tests::{
@ -316,15 +317,15 @@ trait Sub: Super {
fn foo<T: Sub>() { T::$0 } fn foo<T: Sub>() { T::$0 }
"#, "#,
expect![[r#" expect![[r#"
ct C2 (as Sub) const C2: () ct C2 (as Sub) const C2: ()
ct CONST (as Super) const CONST: u8 ct CONST (as Super) const CONST: u8
fn func() (as Super) fn() fn func() (as Super) fn()
fn subfunc() (as Sub) fn() fn subfunc() (as Sub) fn()
ta SubTy (as Sub) type SubTy me method() (as Super) fn(&self)
ta Ty (as Super) type Ty me submethod() (as Sub) fn(&self)
me method() (as Super) fn(&self) ta SubTy (as Sub) type SubTy
me submethod() (as Sub) fn(&self) ta Ty (as Super) type Ty
"#]], "#]],
); );
} }
@ -356,15 +357,15 @@ impl<T> Sub for Wrap<T> {
} }
"#, "#,
expect![[r#" expect![[r#"
ct C2 (as Sub) const C2: () ct C2 (as Sub) const C2: ()
ct CONST (as Super) const CONST: u8 ct CONST (as Super) const CONST: u8
fn func() (as Super) fn() fn func() (as Super) fn()
fn subfunc() (as Sub) fn() fn subfunc() (as Sub) fn()
ta SubTy (as Sub) type SubTy me method() (as Super) fn(&self)
ta Ty (as Super) type Ty me submethod() (as Sub) fn(&self)
me method() (as Super) fn(&self) ta SubTy (as Sub) type SubTy
me submethod() (as Sub) fn(&self) ta Ty (as Super) type Ty
"#]], "#]],
); );
} }
@ -555,10 +556,10 @@ impl Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
ev Bar Bar me foo() fn(self)
ev Baz Baz ev Bar Bar
me foo() fn(self) ev Baz Baz
"#]], "#]],
); );
} }
@ -1399,7 +1400,7 @@ fn main() {
bar.b$0 bar.b$0
} }
"#, "#,
CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Method),
expect!("const fn(&'foo mut self, &Foo) -> !"), expect!("const fn(&'foo mut self, &Foo) -> !"),
expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"),
); );

View file

@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> {
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Foo<'_, {unknown}, _> sp Self Foo<'static, {unknown}, _>
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'static, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0);
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Foo<'_, {unknown}, _> sp Self Foo<'static, {unknown}, _>
st Foo<> Foo<'_, {unknown}, _> st Foo<> Foo<'static, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit

View file

@ -346,6 +346,7 @@ pub enum SymbolKind {
Enum, Enum,
Field, Field,
Function, Function,
Method,
Impl, Impl,
Label, Label,
LifetimeParam, LifetimeParam,

View file

@ -26,6 +26,7 @@ text-edit.workspace = true
cfg.workspace = true cfg.workspace = true
hir.workspace = true hir.workspace = true
ide-db.workspace = true ide-db.workspace = true
paths.workspace = true
[dev-dependencies] [dev-dependencies]
expect-test = "1.4.0" expect-test = "1.4.0"

View file

@ -23,6 +23,7 @@ mod tests {
}, },
DiagnosticsConfig, DiagnosticsConfig,
}; };
use test_utils::skip_slow_tests;
#[track_caller] #[track_caller]
fn check_diagnostics_no_bails(ra_fixture: &str) { 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 { mod rust_unstable {
use super::*; use super::*;

View file

@ -7,7 +7,11 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: need-mut // Diagnostic: need-mut
// //
// This diagnostic is triggered on mutating an immutable variable. // 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 = (|| { let fixes = (|| {
if d.local.is_ref(ctx.sema.db) { if d.local.is_ref(ctx.sema.db) {
// There is no simple way to add `mut` to `ref x` and `ref mut x` // 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, use_range,
)]) )])
})(); })();
Diagnostic::new_with_syntax_node_ptr( Some(
ctx, Diagnostic::new_with_syntax_node_ptr(
// FIXME: `E0384` is not the only error that this diagnostic handles ctx,
DiagnosticCode::RustcHardError("E0384"), // FIXME: `E0384` is not the only error that this diagnostic handles
format!( DiagnosticCode::RustcHardError("E0384"),
"cannot mutate immutable variable `{}`", format!(
d.local.name(ctx.sema.db).display(ctx.sema.db) "cannot mutate immutable variable `{}`",
), d.local.name(ctx.sema.db).display(ctx.sema.db)
d.span, ),
d.span,
)
.with_fixes(fixes),
) )
.with_fixes(fixes)
} }
// Diagnostic: unused-mut // Diagnostic: unused-mut
// //
// This diagnostic is triggered when a mutable variable isn't actually mutated. // 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(); 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 fixes = (|| {
let file_id = ast.file_id.file_id()?; let file_id = ast.file_id.file_id()?;
let mut edit_builder = TextEdit::builder(); 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(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
Diagnostic::new_with_syntax_node_ptr( Some(
ctx, Diagnostic::new_with_syntax_node_ptr(
DiagnosticCode::RustcLint("unused_mut"), ctx,
"variable does not need to be mutable", DiagnosticCode::RustcLint("unused_mut"),
ast, "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> { pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {

View file

@ -12,7 +12,12 @@ use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, Diagnostics
pub(crate) fn remove_trailing_return( pub(crate) fn remove_trailing_return(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
d: &RemoveTrailingReturn, 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| { let display_range = adjusted_display_range(ctx, d.return_expr, &|return_expr| {
return_expr return_expr
.syntax() .syntax()
@ -20,12 +25,14 @@ pub(crate) fn remove_trailing_return(
.and_then(ast::ExprStmt::cast) .and_then(ast::ExprStmt::cast)
.map(|stmt| stmt.syntax().text_range()) .map(|stmt| stmt.syntax().text_range())
}); });
Diagnostic::new( Some(
DiagnosticCode::Clippy("needless_return"), Diagnostic::new(
"replace return <expr>; with <expr>", DiagnosticCode::Clippy("needless_return"),
display_range, "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>> { fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option<Vec<Assist>> {

View file

@ -21,23 +21,30 @@ use crate::{
pub(crate) fn remove_unnecessary_else( pub(crate) fn remove_unnecessary_else(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
d: &RemoveUnnecessaryElse, 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| { let display_range = adjusted_display_range(ctx, d.if_expr, &|if_expr| {
if_expr.else_token().as_ref().map(SyntaxToken::text_range) if_expr.else_token().as_ref().map(SyntaxToken::text_range)
}); });
Diagnostic::new( Some(
DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), Diagnostic::new(
"remove unnecessary else block", DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning),
display_range, "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>> { fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> {
let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); 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 = 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 mut indent = IndentLevel::from_node(if_expr.syntax());
let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some(); let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some();

View file

@ -8,6 +8,7 @@ use ide_db::{
source_change::SourceChange, source_change::SourceChange,
RootDatabase, RootDatabase,
}; };
use paths::Utf8Component;
use syntax::{ use syntax::{
ast::{self, edit::IndentLevel, HasModuleItem, HasName}, ast::{self, edit::IndentLevel, HasModuleItem, HasName},
AstNode, TextRange, 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 // try resolving the relative difference of the paths as inline modules
let mut current = root_module; let mut current = root_module;
for ele in rel.as_ref().components() { for ele in rel.as_utf8_path().components() {
let seg = match ele { let seg = match ele {
std::path::Component::Normal(seg) => seg.to_str()?, Utf8Component::Normal(seg) => seg,
std::path::Component::RootDir => continue, Utf8Component::RootDir => continue,
// shouldn't occur // shouldn't occur
_ => continue 'crates, _ => continue 'crates,
}; };

View file

@ -14,18 +14,24 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
pub(crate) fn unused_variables( pub(crate) fn unused_variables(
ctx: &DiagnosticsContext<'_>, ctx: &DiagnosticsContext<'_>,
d: &hir::UnusedVariable, d: &hir::UnusedVariable,
) -> Diagnostic { ) -> Option<Diagnostic> {
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); 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 diagnostic_range = ctx.sema.diagnostics_display_range(ast);
let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string(); let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string();
Diagnostic::new_with_syntax_node_ptr( Some(
ctx, Diagnostic::new_with_syntax_node_ptr(
DiagnosticCode::RustcLint("unused_variables"), ctx,
"unused variable", DiagnosticCode::RustcLint("unused_variables"),
ast, "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>> { 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)] #[cfg(test)]
mod tests { mod tests {
use crate::tests::{check_diagnostics, check_fix, check_no_fix}; use crate::tests::{check_diagnostics, check_fix};
#[test] #[test]
fn unused_variables_simple() { fn unused_variables_simple() {
@ -193,7 +199,7 @@ fn main() {
#[test] #[test]
fn no_fix_for_marco() { fn no_fix_for_marco() {
check_no_fix( check_diagnostics(
r#" r#"
macro_rules! my_macro { macro_rules! my_macro {
() => { () => {
@ -202,7 +208,7 @@ macro_rules! my_macro {
} }
fn main() { fn main() {
$0my_macro!(); my_macro!();
} }
"#, "#,
); );

View file

@ -330,7 +330,6 @@ pub fn diagnostics(
} }
for diag in diags { for diag in diags {
#[rustfmt::skip]
let d = match diag { let d = match diag {
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&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::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&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::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::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d),
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&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), 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::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&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::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::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) {
AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&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::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::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::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) {
AnyDiagnostic::RemoveUnnecessaryElse(d) => handlers::remove_unnecessary_else::remove_unnecessary_else(&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) res.push(d)
} }
@ -399,9 +413,9 @@ pub fn diagnostics(
.iter_mut() .iter_mut()
.filter_map(|it| { .filter_map(|it| {
Some(( Some((
it.main_node it.main_node.map(|ptr| {
.map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))
.clone()?, })?,
it, it,
)) ))
}) })

View file

@ -283,6 +283,10 @@ fn test_disabled_diagnostics() {
#[test] #[test]
fn minicore_smoke_test() { fn minicore_smoke_test() {
if test_utils::skip_slow_tests() {
return;
}
fn check(minicore: MiniCore) { fn check(minicore: MiniCore) {
let source = minicore.source_code(); let source = minicore.source_code();
let mut config = DiagnosticsConfig::test_sample(); let mut config = DiagnosticsConfig::test_sample();

View file

@ -36,6 +36,7 @@ ide-ssr.workspace = true
profile.workspace = true profile.workspace = true
stdx.workspace = true stdx.workspace = true
syntax.workspace = true syntax.workspace = true
span.workspace = true
text-edit.workspace = true text-edit.workspace = true
# ide should depend only on the top-level `hir` package. if you need # ide should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`. # something from some `hir-xxx` subpackage, reexport the API via `hir`.

View file

@ -5,8 +5,6 @@ mod tests;
mod intra_doc_links; mod intra_doc_links;
use std::ffi::OsStr;
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions}; use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
use stdx::format_to; use stdx::format_to;
@ -134,8 +132,8 @@ pub(crate) fn remove_links(markdown: &str) -> String {
pub(crate) fn external_docs( pub(crate) fn external_docs(
db: &RootDatabase, db: &RootDatabase,
FilePosition { file_id, offset }: FilePosition, FilePosition { file_id, offset }: FilePosition,
target_dir: Option<&OsStr>, target_dir: Option<&str>,
sysroot: Option<&OsStr>, sysroot: Option<&str>,
) -> Option<DocumentationLinks> { ) -> Option<DocumentationLinks> {
let sema = &Semantics::new(db); let sema = &Semantics::new(db);
let file = sema.parse(file_id).syntax().clone(); 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( fn get_doc_links(
db: &RootDatabase, db: &RootDatabase,
def: Definition, def: Definition,
target_dir: Option<&OsStr>, target_dir: Option<&str>,
sysroot: Option<&OsStr>, sysroot: Option<&str>,
) -> DocumentationLinks { ) -> DocumentationLinks {
let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> { let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
base_url.and_then(|url| url.join(path).ok()) base_url.and_then(|url| url.join(path).ok())
@ -479,15 +477,13 @@ fn map_links<'e>(
fn get_doc_base_urls( fn get_doc_base_urls(
db: &RootDatabase, db: &RootDatabase,
def: Definition, def: Definition,
target_dir: Option<&OsStr>, target_dir: Option<&str>,
sysroot: Option<&OsStr>, sysroot: Option<&str>,
) -> (Option<Url>, Option<Url>) { ) -> (Option<Url>, Option<Url>) {
let local_doc = target_dir let local_doc = target_dir
.and_then(|path| path.to_str())
.and_then(|path| Url::parse(&format!("file:///{path}/")).ok()) .and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
.and_then(|it| it.join("doc/").ok()); .and_then(|it| it.join("doc/").ok());
let system_doc = sysroot let system_doc = sysroot
.and_then(|it| it.to_str())
.map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/")) .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
.and_then(|it| Url::parse(&it).ok()); .and_then(|it| Url::parse(&it).ok());

View file

@ -1,4 +1,4 @@
use std::{ffi::OsStr, iter}; use std::iter;
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use hir::Semantics; use hir::Semantics;
@ -18,10 +18,10 @@ use crate::{
fn check_external_docs( fn check_external_docs(
ra_fixture: &str, ra_fixture: &str,
target_dir: Option<&OsStr>, target_dir: Option<&str>,
expect_web_url: Option<Expect>, expect_web_url: Option<Expect>,
expect_local_url: Option<Expect>, expect_local_url: Option<Expect>,
sysroot: Option<&OsStr>, sysroot: Option<&str>,
) { ) {
let (analysis, position) = fixture::position(ra_fixture); let (analysis, position) = fixture::position(ra_fixture);
let links = analysis.external_docs(position, target_dir, sysroot).unwrap(); let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
@ -127,10 +127,10 @@ fn external_docs_doc_builtin_type() {
//- /main.rs crate:foo //- /main.rs crate:foo
let x: u3$02 = 0; 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#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
Some(expect![[r#"file:///sysroot/share/doc/rust/html/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 //- /lib.rs crate:foo
pub struct 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#"https://docs.rs/foo/*/foo/index.html"#]]),
Some(expect![[r#"file:///home/user/project/doc/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 //- /main.rs crate:std
use self$0; 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!["https://doc.rust-lang.org/stable/std/index.html"]),
Some(expect!["file:///sysroot/share/doc/rust/html/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 //- /main.rs crate:foo
pub struct Fo$0o; 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#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
Some(expect![[r#"file:///home/user/project/doc/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 //- /main.rs crate:foo
pub struct Fo$0o; 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#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
Some(expect![[r#"file:///C:/Users/user/project/doc/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 //- /main.rs crate:foo
pub struct Fo$0o; 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#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
Some(OsStr::new("/sysroot")), Some("/sysroot"),
); );
} }

View file

@ -134,15 +134,22 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
if let Some(type_param_list) = it.generic_param_list() { if let Some(type_param_list) = it.generic_param_list() {
collapse_ws(type_param_list.syntax(), &mut detail); 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); collapse_ws(param_list.syntax(), &mut detail);
} param_list.self_param().is_some()
} else {
false
};
if let Some(ret_type) = it.ret_type() { if let Some(ret_type) = it.ret_type() {
detail.push(' '); detail.push(' ');
collapse_ws(ret_type.syntax(), &mut detail); 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::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)),
ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)), ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)),

View file

@ -337,6 +337,77 @@ impl Tr for S {
const C: usize = 4; 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() {}
//^^^
}
}
"#, "#,
); );
} }

View file

@ -33,6 +33,7 @@ pub struct HoverConfig {
pub keywords: bool, pub keywords: bool,
pub format: HoverDocFormat, pub format: HoverDocFormat,
pub max_trait_assoc_items_count: Option<usize>, pub max_trait_assoc_items_count: Option<usize>,
pub max_struct_field_count: Option<usize>,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]

View file

@ -410,6 +410,9 @@ pub(super) fn definition(
Definition::Trait(trait_) => { Definition::Trait(trait_) => {
trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() 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), _ => def.label(db),
}; };
let docs = def.docs(db, famous_defs); let docs = def.docs(db, famous_defs);

View file

@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
format: HoverDocFormat::Markdown, format: HoverDocFormat::Markdown,
keywords: true, keywords: true,
max_trait_assoc_items_count: None, max_trait_assoc_items_count: None,
max_struct_field_count: None,
}; };
fn check_hover_no_result(ra_fixture: &str) { fn check_hover_no_result(ra_fixture: &str) {
@ -49,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual) 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] #[track_caller]
fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) { fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture); let (analysis, position) = fixture::position(ra_fixture);
@ -853,9 +876,7 @@ struct Foo$0 { field: u32 }
```rust ```rust
// size = 4, align = 4 // size = 4, align = 4
struct Foo { struct Foo
field: u32,
}
``` ```
"#]], "#]],
); );
@ -875,8 +896,74 @@ struct Foo$0 where u32: Copy { field: u32 }
struct Foo struct Foo
where where
u32: Copy, 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 ```rust
struct Thing { struct Thing
x: u32,
}
``` ```
"#]], "#]],
); );
@ -1365,9 +1450,7 @@ impl Thing {
``` ```
```rust ```rust
struct Thing { struct Thing
x: u32,
}
``` ```
"#]], "#]],
); );
@ -2599,7 +2682,7 @@ fn main() { let s$0t = S{ f1:0 }; }
focus_range: 7..8, focus_range: 7..8,
name: "S", name: "S",
kind: Struct, 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, focus_range: 24..25,
name: "S", name: "S",
kind: Struct, 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, focus_range: 24..25,
name: "S", name: "S",
kind: Struct, 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, focus_range: 39..41,
name: "S1", name: "S1",
kind: Struct, kind: Struct,
description: "struct S1 {}", description: "struct S1",
}, },
}, },
HoverGotoTypeData { HoverGotoTypeData {
@ -2970,7 +3053,7 @@ fn main() { let s$0t = foo(); }
focus_range: 52..54, focus_range: 52..54,
name: "S2", name: "S2",
kind: Struct, kind: Struct,
description: "struct S2 {}", description: "struct S2",
}, },
}, },
], ],
@ -3061,7 +3144,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
focus_range: 36..37, focus_range: 36..37,
name: "S", name: "S",
kind: Struct, kind: Struct,
description: "struct S {}", description: "struct S",
}, },
}, },
], ],
@ -3161,7 +3244,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
focus_range: 23..24, focus_range: 23..24,
name: "S", name: "S",
kind: Struct, kind: Struct,
description: "struct S {}", description: "struct S",
}, },
}, },
], ],
@ -3198,7 +3281,7 @@ fn main() { let s$0t = foo(); }
focus_range: 49..50, focus_range: 49..50,
name: "B", name: "B",
kind: Struct, kind: Struct,
description: "struct B<T> {}", description: "struct B<T>",
}, },
}, },
HoverGotoTypeData { HoverGotoTypeData {
@ -3287,7 +3370,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
focus_range: 23..24, focus_range: 23..24,
name: "S", name: "S",
kind: Struct, 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, focus_range: 50..51,
name: "B", name: "B",
kind: Struct, kind: Struct,
description: "struct B<T> {}", description: "struct B<T>",
}, },
}, },
HoverGotoTypeData { HoverGotoTypeData {
@ -3361,7 +3444,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
focus_range: 65..66, focus_range: 65..66,
name: "S", name: "S",
kind: Struct, 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] #[test]
fn array_repeat_exp() { fn array_repeat_exp() {
check( 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>
```
"#]],
)
}

View file

@ -1,5 +1,6 @@
use std::{ use std::{
fmt::{self, Write}, fmt::{self, Write},
hash::{BuildHasher, BuildHasherDefault},
mem::take, mem::take,
}; };
@ -8,7 +9,7 @@ use hir::{
known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
ModuleDefId, Semantics, 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 itertools::Itertools;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use stdx::never; use stdx::never;
@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode {
PreferPostfix, PreferPostfix,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum InlayKind { pub enum InlayKind {
Adjustment, Adjustment,
BindingMode, BindingMode,
@ -132,7 +133,7 @@ pub enum InlayKind {
RangeExclusive, RangeExclusive,
} }
#[derive(Debug)] #[derive(Debug, Hash)]
pub enum InlayHintPosition { pub enum InlayHintPosition {
Before, Before,
After, After,
@ -151,13 +152,23 @@ pub struct InlayHint {
pub label: InlayHintLabel, pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint. /// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<TextEdit>, 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 { impl InlayHint {
fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint { InlayHint {
needs_resolve: false,
range, range,
kind, kind,
label: InlayHintLabel::from(")"), label: InlayHintLabel::from(")"),
@ -167,9 +178,9 @@ impl InlayHint {
pad_right: false, pad_right: false,
} }
} }
fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint { InlayHint {
needs_resolve: false,
range, range,
kind, kind,
label: InlayHintLabel::from("("), label: InlayHintLabel::from("("),
@ -179,15 +190,19 @@ impl InlayHint {
pad_right: false, 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 { pub enum InlayTooltip {
String(String), String(String),
Markdown(String), Markdown(String),
} }
#[derive(Default)] #[derive(Default, Hash)]
pub struct InlayHintLabel { pub struct InlayHintLabel {
pub parts: SmallVec<[InlayHintLabelPart; 1]>, pub parts: SmallVec<[InlayHintLabelPart; 1]>,
} }
@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel {
} }
} }
#[derive(Hash)]
pub struct InlayHintLabelPart { pub struct InlayHintLabelPart {
pub text: String, pub text: String,
/// Source location represented by this label part. The client will use this to fetch the part's /// 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<'_> { impl HirWrite for InlayHintLabelBuilder<'_> {
fn start_location_link(&mut self, def: ModuleDefId) { fn start_location_link(&mut self, def: ModuleDefId) {
if self.location.is_some() { never!(self.location.is_some(), "location link is already started");
never!("location link is already started");
}
self.make_new_part(); self.make_new_part();
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site(); let location = location.call_site();
@ -425,11 +439,6 @@ fn ty_to_text_edit(
Some(builder.finish()) Some(builder.finish())
} }
pub enum RangeLimit {
Fixed(TextRange),
NearestParent(TextSize),
}
// Feature: Inlay Hints // Feature: Inlay Hints
// //
// rust-analyzer shows additional information inline with the source code. // rust-analyzer shows additional information inline with the source code.
@ -451,7 +460,7 @@ pub enum RangeLimit {
pub(crate) fn inlay_hints( pub(crate) fn inlay_hints(
db: &RootDatabase, db: &RootDatabase,
file_id: FileId, file_id: FileId,
range_limit: Option<RangeLimit>, range_limit: Option<TextRange>,
config: &InlayHintsConfig, config: &InlayHintsConfig,
) -> Vec<InlayHint> { ) -> Vec<InlayHint> {
let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); 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); let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
match range_limit { match range_limit {
Some(RangeLimit::Fixed(range)) => match file.covering_element(range) { Some(range) => match file.covering_element(range) {
NodeOrToken::Token(_) => return acc, NodeOrToken::Token(_) => return acc,
NodeOrToken::Node(n) => n NodeOrToken::Node(n) => n
.descendants() .descendants()
.filter(|descendant| range.intersect(descendant.text_range()).is_some()) .filter(|descendant| range.intersect(descendant.text_range()).is_some())
.for_each(hints), .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), None => file.descendants().for_each(hints),
}; };
} }
@ -498,6 +489,39 @@ pub(crate) fn inlay_hints(
acc 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( fn hints(
hints: &mut Vec<InlayHint>, hints: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,

View file

@ -147,7 +147,6 @@ pub(super) fn hints(
None, None,
); );
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(), range: expr.syntax().text_range(),
pad_left: false, pad_left: false,
pad_right: false, pad_right: false,

View file

@ -99,7 +99,6 @@ pub(super) fn hints(
None => pat.syntax().text_range(), None => pat.syntax().text_range(),
}; };
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: label.needs_resolve() || text_edit.is_some(),
range: match type_ascriptable { range: match type_ascriptable {
Some(Some(t)) => text_range.cover(t.text_range()), Some(Some(t)) => text_range.cover(t.text_range()),
_ => text_range, _ => text_range,
@ -177,11 +176,7 @@ mod tests {
use syntax::{TextRange, TextSize}; use syntax::{TextRange, TextSize};
use test_utils::extract_annotations; use test_utils::extract_annotations;
use crate::{ use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
fixture,
inlay_hints::{InlayHintsConfig, RangeLimit},
ClosureReturnTypeHints,
};
use crate::inlay_hints::tests::{ use crate::inlay_hints::tests::{
check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
@ -404,7 +399,7 @@ fn main() {
.inlay_hints( .inlay_hints(
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
file_id, file_id,
Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))), Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
) )
.unwrap(); .unwrap();
let actual = let actual =

View file

@ -50,7 +50,6 @@ pub(super) fn hints(
_ => return, _ => return,
}; };
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: false,
range, range,
kind: InlayKind::BindingMode, kind: InlayKind::BindingMode,
label: r.into(), label: r.into(),
@ -69,7 +68,6 @@ pub(super) fn hints(
hir::BindingMode::Ref(Mutability::Shared) => "ref", hir::BindingMode::Ref(Mutability::Shared) => "ref",
}; };
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: false,
range: pat.syntax().text_range(), range: pat.syntax().text_range(),
kind: InlayKind::BindingMode, kind: InlayKind::BindingMode,
label: bm.into(), label: bm.into(),

View file

@ -59,7 +59,6 @@ pub(super) fn hints(
} }
let label = label_of_ty(famous_defs, config, &ty)?; let label = label_of_ty(famous_defs, config, &ty)?;
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(), range: expr.syntax().text_range(),
kind: InlayKind::Chaining, kind: InlayKind::Chaining,
label, label,

View file

@ -109,7 +109,6 @@ pub(super) fn hints(
let linked_location = name_range.map(|range| FileRange { file_id, range }); let linked_location = name_range.map(|range| FileRange { file_id, range });
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: linked_location.is_some(),
range: closing_token.text_range(), range: closing_token.text_range(),
kind: InlayKind::ClosingBrace, kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location), label: InlayHintLabel::simple(label, None, linked_location),

View file

@ -32,7 +32,6 @@ pub(super) fn hints(
let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = closure.syntax().first_token()?.prev_token()?.text_range();
let range = TextRange::new(range.end() - TextSize::from(1), range.end()); let range = TextRange::new(range.end() - TextSize::from(1), range.end());
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: false,
range, range,
kind: InlayKind::ClosureCapture, kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from("move"), label: InlayHintLabel::from("move"),
@ -45,7 +44,6 @@ pub(super) fn hints(
} }
}; };
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: false,
range: move_kw_range, range: move_kw_range,
kind: InlayKind::ClosureCapture, kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from("("), label: InlayHintLabel::from("("),
@ -79,7 +77,6 @@ pub(super) fn hints(
}), }),
); );
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: label.needs_resolve(),
range: move_kw_range, range: move_kw_range,
kind: InlayKind::ClosureCapture, kind: InlayKind::ClosureCapture,
label, label,
@ -91,7 +88,6 @@ pub(super) fn hints(
if idx != last { if idx != last {
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: false,
range: move_kw_range, range: move_kw_range,
kind: InlayKind::ClosureCapture, kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(", "), label: InlayHintLabel::from(", "),
@ -103,7 +99,6 @@ pub(super) fn hints(
} }
} }
acc.push(InlayHint { acc.push(InlayHint {
needs_resolve: false,
range: move_kw_range, range: move_kw_range,
kind: InlayKind::ClosureCapture, kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(")"), label: InlayHintLabel::from(")"),

Some files were not shown because too many files have changed in this diff Show more