Auto merge of #150546 - JonathanBrouwer:rollup-jkqji1j, r=JonathanBrouwer
Rollup of 5 pull requests Successful merges: - rust-lang/rust#146798 (RISC-V: Implement (Zkne or Zknd) intrinsics correctly) - rust-lang/rust#150337 (docs: fix typo in std::io::buffered) - rust-lang/rust#150530 (Remove `feature(string_deref_patterns)`) - rust-lang/rust#150543 (`rust-analyzer` subtree update) - rust-lang/rust#150544 (Use --print target-libdir in run-make tests) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
8d670b93d4
230 changed files with 7862 additions and 3079 deletions
|
|
@ -262,6 +262,11 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
"power8-crypto" => Some(LLVMFeature::new("crypto")),
|
||||
s => Some(LLVMFeature::new(s)),
|
||||
},
|
||||
Arch::RiscV32 | Arch::RiscV64 => match s {
|
||||
// Filter out Rust-specific *virtual* target feature
|
||||
"zkne_or_zknd" => None,
|
||||
s => Some(LLVMFeature::new(s)),
|
||||
},
|
||||
Arch::Sparc | Arch::Sparc64 => match s {
|
||||
"leoncasa" => Some(LLVMFeature::new("hasleoncasa")),
|
||||
s => Some(LLVMFeature::new(s)),
|
||||
|
|
|
|||
|
|
@ -271,6 +271,8 @@ declare_features! (
|
|||
/// Allows `#[link(kind = "static-nobundle", ...)]`.
|
||||
(removed, static_nobundle, "1.63.0", Some(37403),
|
||||
Some(r#"subsumed by `#[link(kind = "static", modifiers = "-bundle", ...)]`"#), 95818),
|
||||
/// Allows string patterns to dereference values to match them.
|
||||
(removed, string_deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), Some("superseded by `deref_patterns`"), 150530),
|
||||
(removed, struct_inherit, "1.0.0", None, None),
|
||||
(removed, test_removed_feature, "1.0.0", None, None),
|
||||
/// Allows using items which are missing stability attributes
|
||||
|
|
|
|||
|
|
@ -647,8 +647,6 @@ declare_features! (
|
|||
(unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
|
||||
/// Allows lints part of the strict provenance effort.
|
||||
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
|
||||
/// Allows string patterns to dereference values to match them.
|
||||
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
|
||||
/// Allows `super let` statements.
|
||||
(unstable, super_let, "1.88.0", Some(139076)),
|
||||
/// Allows subtrait items to shadow supertrait items.
|
||||
|
|
|
|||
|
|
@ -996,20 +996,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pat_ty = self.tcx.types.str_;
|
||||
}
|
||||
|
||||
if self.tcx.features().string_deref_patterns()
|
||||
&& let hir::PatExprKind::Lit {
|
||||
lit: Spanned { node: ast::LitKind::Str(..), .. }, ..
|
||||
} = lt.kind
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
let expected = self.resolve_vars_if_possible(expected);
|
||||
pat_ty = match expected.kind() {
|
||||
ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => expected,
|
||||
ty::Str => Ty::new_static_str(tcx),
|
||||
_ => pat_ty,
|
||||
};
|
||||
}
|
||||
|
||||
// Somewhat surprising: in this case, the subtyping relation goes the
|
||||
// opposite way as the other cases. Actually what we really want is not
|
||||
// a subtyping relation at all but rather that there exists a LUB
|
||||
|
|
|
|||
|
|
@ -827,7 +827,6 @@ pub enum PatKind<'tcx> {
|
|||
/// much simpler.
|
||||
/// * raw pointers derived from integers, other raw pointers will have already resulted in an
|
||||
/// error.
|
||||
/// * `String`, if `string_deref_patterns` is enabled.
|
||||
Constant {
|
||||
value: ty::Value<'tcx>,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let mut expect = self.literal_operand(test.span, Const::from_ty_value(tcx, value));
|
||||
|
||||
let mut place = place;
|
||||
let mut block = block;
|
||||
|
||||
match cast_ty.kind() {
|
||||
ty::Str => {
|
||||
// String literal patterns may have type `str` if `deref_patterns` is
|
||||
|
|
@ -175,34 +175,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
place = ref_place;
|
||||
cast_ty = ref_str_ty;
|
||||
}
|
||||
ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => {
|
||||
if !tcx.features().string_deref_patterns() {
|
||||
span_bug!(
|
||||
test.span,
|
||||
"matching on `String` went through without enabling string_deref_patterns"
|
||||
);
|
||||
}
|
||||
let re_erased = tcx.lifetimes.re_erased;
|
||||
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
|
||||
let ref_str = self.temp(ref_str_ty, test.span);
|
||||
let eq_block = self.cfg.start_new_block();
|
||||
// `let ref_str: &str = <String as Deref>::deref(&place);`
|
||||
self.call_deref(
|
||||
block,
|
||||
eq_block,
|
||||
place,
|
||||
Mutability::Not,
|
||||
cast_ty,
|
||||
ref_str,
|
||||
test.span,
|
||||
);
|
||||
// Since we generated a `ref_str = <String as Deref>::deref(&place) -> eq_block` terminator,
|
||||
// we need to add all further statements to `eq_block`.
|
||||
// Similarly, the normal test code should be generated for the `&str`, instead of the `String`.
|
||||
block = eq_block;
|
||||
place = ref_str;
|
||||
cast_ty = ref_str_ty;
|
||||
}
|
||||
&ty::Pat(base, _) => {
|
||||
assert_eq!(cast_ty, value.ty);
|
||||
assert!(base.is_trivially_pure_clone_copy());
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc_hir::{self as hir, LangItem, RangeEnd};
|
||||
use rustc_hir::{self as hir, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::mir::interpret::LitToConstInput;
|
||||
use rustc_middle::thir::{
|
||||
|
|
@ -626,23 +626,7 @@ impl<'tcx> PatCtxt<'tcx> {
|
|||
// the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the
|
||||
// pattern's type means we'll properly translate it to a slice reference pattern. This works
|
||||
// because slices and arrays have the same valtree representation.
|
||||
// HACK: As an exception, use the literal's type if `pat_ty` is `String`; this can happen if
|
||||
// `string_deref_patterns` is enabled. There's a special case for that when lowering to MIR.
|
||||
// FIXME(deref_patterns): This hack won't be necessary once `string_deref_patterns` is
|
||||
// superseded by a more general implementation of deref patterns.
|
||||
let ct_ty = match pat_ty {
|
||||
Some(pat_ty)
|
||||
if let ty::Adt(def, _) = *pat_ty.kind()
|
||||
&& self.tcx.is_lang_item(def.did(), LangItem::String) =>
|
||||
{
|
||||
if !self.tcx.features().string_deref_patterns() {
|
||||
span_bug!(
|
||||
expr.span,
|
||||
"matching on `String` went through without enabling string_deref_patterns"
|
||||
);
|
||||
}
|
||||
self.typeck_results.node_type(expr.hir_id)
|
||||
}
|
||||
Some(pat_ty) => pat_ty,
|
||||
None => self.typeck_results.node_type(expr.hir_id),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -690,8 +690,9 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
|
|||
("zimop", Unstable(sym::riscv_target_feature), &[]),
|
||||
("zk", Stable, &["zkn", "zkr", "zkt"]),
|
||||
("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]),
|
||||
("zknd", Stable, &[]),
|
||||
("zkne", Stable, &[]),
|
||||
("zknd", Stable, &["zkne_or_zknd"]),
|
||||
("zkne", Stable, &["zkne_or_zknd"]),
|
||||
("zkne_or_zknd", Unstable(sym::riscv_target_feature), &[]), // Not an extension
|
||||
("zknh", Stable, &[]),
|
||||
("zkr", Stable, &[]),
|
||||
("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]),
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ impl Buffer {
|
|||
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
// SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and
|
||||
// SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
|
||||
// that region is initialized because those are all invariants of this type.
|
||||
unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> {
|
|||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
|
||||
/// Writes some data into this BufReader with line buffering.
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
|
|
@ -146,7 +146,7 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
|
|||
self.buffer.flush()
|
||||
}
|
||||
|
||||
/// Writes some vectored data into this BufReader with line buffering.
|
||||
/// Writes some vectored data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// and including the buffer containing the last newline is sent directly to
|
||||
|
|
@ -256,7 +256,7 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
|
|||
self.inner().is_write_vectored()
|
||||
}
|
||||
|
||||
/// Writes some data into this BufReader with line buffering.
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#[cfg(test)]
|
||||
use stdarch_test::assert_instr;
|
||||
|
||||
use crate::arch::asm;
|
||||
|
||||
unsafe extern "unadjusted" {
|
||||
#[link_name = "llvm.riscv.aes64es"]
|
||||
fn _aes64es(rs1: i64, rs2: i64) -> i64;
|
||||
|
|
@ -14,12 +16,6 @@ unsafe extern "unadjusted" {
|
|||
#[link_name = "llvm.riscv.aes64dsm"]
|
||||
fn _aes64dsm(rs1: i64, rs2: i64) -> i64;
|
||||
|
||||
#[link_name = "llvm.riscv.aes64ks1i"]
|
||||
fn _aes64ks1i(rs1: i64, rnum: i32) -> i64;
|
||||
|
||||
#[link_name = "llvm.riscv.aes64ks2"]
|
||||
fn _aes64ks2(rs1: i64, rs2: i64) -> i64;
|
||||
|
||||
#[link_name = "llvm.riscv.aes64im"]
|
||||
fn _aes64im(rs1: i64) -> i64;
|
||||
|
||||
|
|
@ -133,15 +129,26 @@ pub fn aes64dsm(rs1: u64, rs2: u64) -> u64 {
|
|||
/// # Note
|
||||
///
|
||||
/// The `RNUM` parameter is expected to be a constant value inside the range of `0..=10`.
|
||||
#[target_feature(enable = "zkne", enable = "zknd")]
|
||||
#[target_feature(enable = "zkne_or_zknd")]
|
||||
#[rustc_legacy_const_generics(1)]
|
||||
#[cfg_attr(test, assert_instr(aes64ks1i, RNUM = 0))]
|
||||
#[inline]
|
||||
#[unstable(feature = "riscv_ext_intrinsics", issue = "114544")]
|
||||
pub fn aes64ks1i<const RNUM: u8>(rs1: u64) -> u64 {
|
||||
static_assert!(RNUM <= 10);
|
||||
|
||||
unsafe { _aes64ks1i(rs1 as i64, RNUM as i32) as u64 }
|
||||
unsafe {
|
||||
let rd: u64;
|
||||
asm!(
|
||||
".option push",
|
||||
".option arch, +zkne",
|
||||
"aes64ks1i {}, {}, {}",
|
||||
".option pop",
|
||||
lateout(reg) rd,
|
||||
in(reg) rs1,
|
||||
const RNUM,
|
||||
options(pure, nomem, nostack, preserves_flags)
|
||||
);
|
||||
rd
|
||||
}
|
||||
}
|
||||
|
||||
/// This instruction implements part of the KeySchedule operation for the AES Block cipher.
|
||||
|
|
@ -155,12 +162,24 @@ pub fn aes64ks1i<const RNUM: u8>(rs1: u64) -> u64 {
|
|||
/// Version: v1.0.1
|
||||
///
|
||||
/// Section: 3.11
|
||||
#[target_feature(enable = "zkne", enable = "zknd")]
|
||||
#[cfg_attr(test, assert_instr(aes64ks2))]
|
||||
#[target_feature(enable = "zkne_or_zknd")]
|
||||
#[inline]
|
||||
#[unstable(feature = "riscv_ext_intrinsics", issue = "114544")]
|
||||
pub fn aes64ks2(rs1: u64, rs2: u64) -> u64 {
|
||||
unsafe { _aes64ks2(rs1 as i64, rs2 as i64) as u64 }
|
||||
unsafe {
|
||||
let rd: u64;
|
||||
asm!(
|
||||
".option push",
|
||||
".option arch, +zkne",
|
||||
"aes64ks2 {}, {}, {}",
|
||||
".option pop",
|
||||
lateout(reg) rd,
|
||||
in(reg) rs1,
|
||||
in(reg) rs2,
|
||||
options(pure, nomem, nostack, preserves_flags)
|
||||
);
|
||||
rd
|
||||
}
|
||||
}
|
||||
|
||||
/// This instruction accelerates the inverse MixColumns step of the AES Block Cipher, and is used to aid creation of
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ The tracking issue for this feature is: [#87121]
|
|||
------------------------
|
||||
|
||||
> **Note**: This feature is incomplete. In the future, it is meant to supersede
|
||||
> [`box_patterns`] and [`string_deref_patterns`].
|
||||
> [`box_patterns`].
|
||||
|
||||
This feature permits pattern matching on [smart pointers in the standard library] through their
|
||||
`Deref` target types, either implicitly or with explicit `deref!(_)` patterns (the syntax of which
|
||||
|
|
@ -103,5 +103,4 @@ match *(b"test" as &[u8]) {
|
|||
```
|
||||
|
||||
[`box_patterns`]: ./box-patterns.md
|
||||
[`string_deref_patterns`]: ./string-deref-patterns.md
|
||||
[smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
# `string_deref_patterns`
|
||||
|
||||
The tracking issue for this feature is: [#87121]
|
||||
|
||||
[#87121]: https://github.com/rust-lang/rust/issues/87121
|
||||
|
||||
------------------------
|
||||
|
||||
> **Note**: This feature will be superseded by [`deref_patterns`] in the future.
|
||||
|
||||
This feature permits pattern matching `String` to `&str` through [its `Deref` implementation].
|
||||
|
||||
```rust
|
||||
#![feature(string_deref_patterns)]
|
||||
|
||||
pub enum Value {
|
||||
String(String),
|
||||
Number(u32),
|
||||
}
|
||||
|
||||
pub fn is_it_the_answer(value: Value) -> bool {
|
||||
match value {
|
||||
Value::String("42") => true,
|
||||
Value::Number(42) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Without this feature other constructs such as match guards have to be used.
|
||||
|
||||
```rust
|
||||
# pub enum Value {
|
||||
# String(String),
|
||||
# Number(u32),
|
||||
# }
|
||||
#
|
||||
pub fn is_it_the_answer(value: Value) -> bool {
|
||||
match value {
|
||||
Value::String(s) if s == "42" => true,
|
||||
Value::Number(42) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[`deref_patterns`]: ./deref-patterns.md
|
||||
[its `Deref` implementation]: https://doc.rust-lang.org/std/string/struct.String.html#impl-Deref-for-String
|
||||
|
|
@ -122,6 +122,12 @@ jobs:
|
|||
- name: Run tests
|
||||
run: cargo nextest run --no-fail-fast --hide-progress-bar --status-level fail
|
||||
|
||||
- name: Install cargo-machete
|
||||
uses: taiki-e/install-action@cargo-machete
|
||||
|
||||
- name: Run cargo-machete
|
||||
run: cargo machete
|
||||
|
||||
- name: Run Clippy
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr
|
||||
|
|
|
|||
|
|
@ -38,6 +38,6 @@ considered accepted feel free to just drop a comment and ask!
|
|||
|
||||
AI tool use is not discouraged on the rust-analyzer codebase, as long as it meets our quality standards.
|
||||
We kindly ask you to disclose usage of AI tools in your contributions.
|
||||
If you used them without disclosing it, we may reject your contribution on that basis alone due to the assumption that you likely not reviewed your own submission (so why should we?).
|
||||
If you used them without disclosing it, we may reject your contribution on that basis alone due to the assumption that you have, most likely, not reviewed your own submission (so why should we?).
|
||||
|
||||
We may still reject AI-assisted contributions if we deem the quality of the contribution to be unsatisfactory as to reduce impact on the team's review budget.
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ dependencies = [
|
|||
"intern",
|
||||
"oorandom",
|
||||
"rustc-hash 2.1.1",
|
||||
"span",
|
||||
"syntax",
|
||||
"syntax-bridge",
|
||||
"tracing",
|
||||
|
|
@ -821,7 +822,6 @@ dependencies = [
|
|||
"intern",
|
||||
"itertools 0.14.0",
|
||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mbe",
|
||||
"query-group-macro",
|
||||
"ra-ap-rustc_abi",
|
||||
"ra-ap-rustc_parse_format",
|
||||
|
|
@ -1219,7 +1219,6 @@ dependencies = [
|
|||
"hashbrown 0.14.5",
|
||||
"rayon",
|
||||
"rustc-hash 2.1.1",
|
||||
"smallvec",
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
|
|
@ -1882,7 +1881,6 @@ dependencies = [
|
|||
"postcard",
|
||||
"proc-macro-api",
|
||||
"proc-macro-srv",
|
||||
"tt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2782,7 +2780,6 @@ dependencies = [
|
|||
"hir-expand",
|
||||
"intern",
|
||||
"paths",
|
||||
"rustc-hash 2.1.1",
|
||||
"span",
|
||||
"stdx",
|
||||
"test-utils",
|
||||
|
|
@ -3088,8 +3085,11 @@ name = "tt"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"indexmap",
|
||||
"intern",
|
||||
"ra-ap-rustc_lexer",
|
||||
"rustc-hash 2.1.1",
|
||||
"span",
|
||||
"stdx",
|
||||
"text-size 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use salsa::Durability;
|
||||
use rustc_hash::FxHashSet;
|
||||
use salsa::{Durability, Setter as _};
|
||||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
|
||||
use crate::{CrateGraphBuilder, CratesIdMap, RootQueryDb, SourceRoot, SourceRootId};
|
||||
use crate::{
|
||||
CrateGraphBuilder, CratesIdMap, LibraryRoots, LocalRoots, RootQueryDb, SourceRoot, SourceRootId,
|
||||
};
|
||||
|
||||
/// Encapsulate a bunch of raw `.set` calls on the database.
|
||||
#[derive(Default)]
|
||||
|
|
@ -49,8 +52,15 @@ impl FileChange {
|
|||
pub fn apply(self, db: &mut dyn RootQueryDb) -> Option<CratesIdMap> {
|
||||
let _p = tracing::info_span!("FileChange::apply").entered();
|
||||
if let Some(roots) = self.roots {
|
||||
let mut local_roots = FxHashSet::default();
|
||||
let mut library_roots = FxHashSet::default();
|
||||
for (idx, root) in roots.into_iter().enumerate() {
|
||||
let root_id = SourceRootId(idx as u32);
|
||||
if root.is_library {
|
||||
library_roots.insert(root_id);
|
||||
} else {
|
||||
local_roots.insert(root_id);
|
||||
}
|
||||
let durability = source_root_durability(&root);
|
||||
for file_id in root.iter() {
|
||||
db.set_file_source_root_with_durability(file_id, root_id, durability);
|
||||
|
|
@ -58,6 +68,8 @@ impl FileChange {
|
|||
|
||||
db.set_source_root_with_durability(root_id, Arc::new(root), durability);
|
||||
}
|
||||
LocalRoots::get(db).set_roots(db).to(local_roots);
|
||||
LibraryRoots::get(db).set_roots(db).to(library_roots);
|
||||
}
|
||||
|
||||
for (file_id, text) in self.files_changed {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub use crate::{
|
|||
};
|
||||
use dashmap::{DashMap, mapref::entry::Entry};
|
||||
pub use query_group::{self};
|
||||
use rustc_hash::FxHasher;
|
||||
use rustc_hash::{FxHashSet, FxHasher};
|
||||
use salsa::{Durability, Setter};
|
||||
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
|
||||
use syntax::{Parse, SyntaxError, ast};
|
||||
|
|
@ -203,6 +203,22 @@ impl Files {
|
|||
}
|
||||
}
|
||||
|
||||
/// The set of roots for crates.io libraries.
|
||||
/// Files in libraries are assumed to never change.
|
||||
#[salsa::input(singleton, debug)]
|
||||
pub struct LibraryRoots {
|
||||
#[returns(ref)]
|
||||
pub roots: FxHashSet<SourceRootId>,
|
||||
}
|
||||
|
||||
/// The set of "local" (that is, from the current workspace) roots.
|
||||
/// Files in local roots are assumed to change frequently.
|
||||
#[salsa::input(singleton, debug)]
|
||||
pub struct LocalRoots {
|
||||
#[returns(ref)]
|
||||
pub roots: FxHashSet<SourceRootId>,
|
||||
}
|
||||
|
||||
#[salsa_macros::input(debug)]
|
||||
pub struct FileText {
|
||||
#[returns(ref)]
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ tracing.workspace = true
|
|||
# locals deps
|
||||
tt = { workspace = true, optional = true }
|
||||
syntax = { workspace = true, optional = true }
|
||||
span = { path = "../span", version = "0.0", optional = true }
|
||||
intern.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
@ -35,6 +36,8 @@ cfg = { path = ".", default-features = false, features = ["tt"] }
|
|||
|
||||
[features]
|
||||
default = []
|
||||
syntax = ["dep:syntax", "dep:span"]
|
||||
tt = ["dep:tt"]
|
||||
in-rust-tree = []
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -96,12 +96,12 @@ impl CfgExpr {
|
|||
// FIXME: Parsing from `tt` is only used in a handful of places, reconsider
|
||||
// if we should switch them to AST.
|
||||
#[cfg(feature = "tt")]
|
||||
pub fn parse<S: Copy>(tt: &tt::TopSubtree<S>) -> CfgExpr {
|
||||
pub fn parse(tt: &tt::TopSubtree) -> CfgExpr {
|
||||
next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tt")]
|
||||
pub fn parse_from_iter<S: Copy>(tt: &mut tt::iter::TtIter<'_, S>) -> CfgExpr {
|
||||
pub fn parse_from_iter(tt: &mut tt::iter::TtIter<'_>) -> CfgExpr {
|
||||
next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,16 @@ fn next_cfg_expr_from_ast(
|
|||
if let Some(NodeOrToken::Token(literal)) = it.peek()
|
||||
&& matches!(literal.kind(), SyntaxKind::STRING)
|
||||
{
|
||||
let literal = tt::token_to_literal(literal.text(), ()).symbol;
|
||||
let dummy_span = span::Span {
|
||||
range: span::TextRange::empty(span::TextSize::new(0)),
|
||||
anchor: span::SpanAnchor {
|
||||
file_id: span::EditionedFileId::from_raw(0),
|
||||
ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER,
|
||||
},
|
||||
ctx: span::SyntaxContext::root(span::Edition::Edition2015),
|
||||
};
|
||||
let literal =
|
||||
Symbol::intern(tt::token_to_literal(literal.text(), dummy_span).text());
|
||||
it.next();
|
||||
CfgAtom::KeyValue { key: name, value: literal.clone() }.into()
|
||||
} else {
|
||||
|
|
@ -179,7 +188,7 @@ fn next_cfg_expr_from_ast(
|
|||
}
|
||||
|
||||
#[cfg(feature = "tt")]
|
||||
fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
|
||||
fn next_cfg_expr(it: &mut tt::iter::TtIter<'_>) -> Option<CfgExpr> {
|
||||
use intern::sym;
|
||||
use tt::iter::TtElement;
|
||||
|
||||
|
|
@ -189,20 +198,21 @@ fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
|
|||
Some(_) => return Some(CfgExpr::Invalid),
|
||||
};
|
||||
|
||||
let ret = match it.peek() {
|
||||
let mut it_clone = it.clone();
|
||||
let ret = match it_clone.next() {
|
||||
Some(TtElement::Leaf(tt::Leaf::Punct(punct)))
|
||||
// Don't consume on e.g. `=>`.
|
||||
if punct.char == '='
|
||||
&& (punct.spacing == tt::Spacing::Alone
|
||||
|| it.remaining().flat_tokens().get(1).is_none_or(|peek2| {
|
||||
!matches!(peek2, tt::TokenTree::Leaf(tt::Leaf::Punct(_)))
|
||||
|| it_clone.peek().is_none_or(|peek2| {
|
||||
!matches!(peek2, tt::TtElement::Leaf(tt::Leaf::Punct(_)))
|
||||
})) =>
|
||||
{
|
||||
match it.remaining().flat_tokens().get(1) {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||
match it_clone.next() {
|
||||
Some(tt::TtElement::Leaf(tt::Leaf::Literal(literal))) => {
|
||||
it.next();
|
||||
it.next();
|
||||
CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into()
|
||||
CfgAtom::KeyValue { key: name, value: Symbol::intern(literal.text()) }.into()
|
||||
}
|
||||
_ => return Some(CfgExpr::Invalid),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ intern.workspace = true
|
|||
base-db.workspace = true
|
||||
syntax.workspace = true
|
||||
hir-expand.workspace = true
|
||||
mbe.workspace = true
|
||||
cfg.workspace = true
|
||||
tt.workspace = true
|
||||
span.workspace = true
|
||||
|
|
|
|||
|
|
@ -99,6 +99,20 @@ fn extract_ra_completions(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_ra_macro_style(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
|
||||
let tt = TokenTreeChildren::new(&tt);
|
||||
if let Ok(NodeOrToken::Token(option)) = Itertools::exactly_one(tt)
|
||||
&& option.kind().is_any_identifier()
|
||||
{
|
||||
match option.text() {
|
||||
"braces" => attr_flags.insert(AttrFlags::MACRO_STYLE_BRACES),
|
||||
"brackets" => attr_flags.insert(AttrFlags::MACRO_STYLE_BRACKETS),
|
||||
"parentheses" => attr_flags.insert(AttrFlags::MACRO_STYLE_PARENTHESES),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
|
||||
let iter = TokenTreeChildren::new(&tt);
|
||||
for kind in iter {
|
||||
|
|
@ -163,6 +177,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
|
|||
2 => match path.segments[0].text() {
|
||||
"rust_analyzer" => match path.segments[1].text() {
|
||||
"completions" => extract_ra_completions(attr_flags, tt),
|
||||
"macro_style" => extract_ra_macro_style(attr_flags, tt),
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
|
|
@ -188,6 +203,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
|
|||
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
|
||||
"macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT),
|
||||
"no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE),
|
||||
"pointee" => attr_flags.insert(AttrFlags::IS_POINTEE),
|
||||
"non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE),
|
||||
"ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
|
||||
"bench" => attr_flags.insert(AttrFlags::IS_BENCH),
|
||||
|
|
@ -289,6 +305,11 @@ bitflags::bitflags! {
|
|||
const RUSTC_PAREN_SUGAR = 1 << 42;
|
||||
const RUSTC_COINDUCTIVE = 1 << 43;
|
||||
const RUSTC_FORCE_INLINE = 1 << 44;
|
||||
const IS_POINTEE = 1 << 45;
|
||||
|
||||
const MACRO_STYLE_BRACES = 1 << 46;
|
||||
const MACRO_STYLE_BRACKETS = 1 << 47;
|
||||
const MACRO_STYLE_PARENTHESES = 1 << 48;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
149
src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs
Normal file
149
src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
//! Definition of builtin derive impls.
|
||||
//!
|
||||
//! To save time and memory, builtin derives are not really expanded. Instead, we record them
|
||||
//! and create their impls based on lowered data, see crates/hir-ty/src/builtin_derive.rs.
|
||||
|
||||
use hir_expand::{InFile, builtin::BuiltinDeriveExpander, name::Name};
|
||||
use intern::{Symbol, sym};
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase,
|
||||
};
|
||||
|
||||
macro_rules! declare_enum {
|
||||
( $( $trait:ident => [ $( $method:ident ),* ] ),* $(,)? ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinDeriveImplTrait {
|
||||
$( $trait, )*
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum BuiltinDeriveImplMethod {
|
||||
$( $( $method, )* )*
|
||||
}
|
||||
|
||||
impl BuiltinDeriveImplTrait {
|
||||
#[inline]
|
||||
pub fn name(self) -> Symbol {
|
||||
match self {
|
||||
$( Self::$trait => sym::$trait, )*
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_id(self, lang_items: &crate::lang_item::LangItems) -> Option<crate::TraitId> {
|
||||
match self {
|
||||
$( Self::$trait => lang_items.$trait, )*
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_method(self, method_name: &Symbol) -> Option<BuiltinDeriveImplMethod> {
|
||||
match self {
|
||||
$(
|
||||
Self::$trait => {
|
||||
match method_name {
|
||||
$( _ if *method_name == sym::$method => Some(BuiltinDeriveImplMethod::$method), )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn all_methods(self) -> &'static [BuiltinDeriveImplMethod] {
|
||||
match self {
|
||||
$( Self::$trait => &[ $(BuiltinDeriveImplMethod::$method),* ], )*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuiltinDeriveImplMethod {
|
||||
#[inline]
|
||||
pub fn name(self) -> Symbol {
|
||||
match self {
|
||||
$( $( BuiltinDeriveImplMethod::$method => sym::$method, )* )*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare_enum!(
|
||||
Copy => [],
|
||||
Clone => [clone],
|
||||
Default => [default],
|
||||
Debug => [fmt],
|
||||
Hash => [hash],
|
||||
Ord => [cmp],
|
||||
PartialOrd => [partial_cmp],
|
||||
Eq => [],
|
||||
PartialEq => [eq],
|
||||
CoerceUnsized => [],
|
||||
DispatchFromDyn => [],
|
||||
);
|
||||
|
||||
impl BuiltinDeriveImplMethod {
|
||||
pub fn trait_method(
|
||||
self,
|
||||
db: &dyn DefDatabase,
|
||||
impl_: BuiltinDeriveImplId,
|
||||
) -> Option<FunctionId> {
|
||||
let loc = impl_.loc(db);
|
||||
let lang_items = crate::lang_item::lang_items(db, loc.krate(db));
|
||||
let trait_ = impl_.loc(db).trait_.get_id(lang_items)?;
|
||||
trait_.trait_items(db).method_by_name(&Name::new_symbol_root(self.name()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_derive_traits(
|
||||
derive: BuiltinDeriveExpander,
|
||||
mut f: impl FnMut(BuiltinDeriveImplTrait),
|
||||
) {
|
||||
let trait_ = match derive {
|
||||
BuiltinDeriveExpander::Copy => BuiltinDeriveImplTrait::Copy,
|
||||
BuiltinDeriveExpander::Clone => BuiltinDeriveImplTrait::Clone,
|
||||
BuiltinDeriveExpander::Default => BuiltinDeriveImplTrait::Default,
|
||||
BuiltinDeriveExpander::Debug => BuiltinDeriveImplTrait::Debug,
|
||||
BuiltinDeriveExpander::Hash => BuiltinDeriveImplTrait::Hash,
|
||||
BuiltinDeriveExpander::Ord => BuiltinDeriveImplTrait::Ord,
|
||||
BuiltinDeriveExpander::PartialOrd => BuiltinDeriveImplTrait::PartialOrd,
|
||||
BuiltinDeriveExpander::Eq => BuiltinDeriveImplTrait::Eq,
|
||||
BuiltinDeriveExpander::PartialEq => BuiltinDeriveImplTrait::PartialEq,
|
||||
BuiltinDeriveExpander::CoercePointee => {
|
||||
f(BuiltinDeriveImplTrait::CoerceUnsized);
|
||||
f(BuiltinDeriveImplTrait::DispatchFromDyn);
|
||||
return;
|
||||
}
|
||||
};
|
||||
f(trait_);
|
||||
}
|
||||
|
||||
impl BuiltinDeriveImplLoc {
|
||||
pub fn source(&self, db: &dyn DefDatabase) -> InFile<TextRange> {
|
||||
let (adt_ast_id, module) = match self.adt {
|
||||
AdtId::StructId(adt) => {
|
||||
let adt_loc = adt.loc(db);
|
||||
(adt_loc.id.upcast(), adt_loc.container)
|
||||
}
|
||||
AdtId::UnionId(adt) => {
|
||||
let adt_loc = adt.loc(db);
|
||||
(adt_loc.id.upcast(), adt_loc.container)
|
||||
}
|
||||
AdtId::EnumId(adt) => {
|
||||
let adt_loc = adt.loc(db);
|
||||
(adt_loc.id.upcast(), adt_loc.container)
|
||||
}
|
||||
};
|
||||
let derive_range = self.derive_attr_id.find_derive_range(
|
||||
db,
|
||||
module.krate(db),
|
||||
adt_ast_id,
|
||||
self.derive_index,
|
||||
);
|
||||
adt_ast_id.with_value(derive_range)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
//! Lowering of `format_args!()`.
|
||||
|
||||
use base_db::FxIndexSet;
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use hir_expand::name::Name;
|
||||
use intern::{Symbol, sym};
|
||||
use syntax::{
|
||||
AstPtr, AstToken as _,
|
||||
ast::{self, HasName},
|
||||
};
|
||||
use syntax::{AstPtr, AstToken as _, ast};
|
||||
|
||||
use crate::{
|
||||
builtin_type::BuiltinUint,
|
||||
|
|
@ -32,8 +29,8 @@ impl<'db> ExprCollector<'db> {
|
|||
let mut args = FormatArgumentsCollector::default();
|
||||
f.args().for_each(|arg| {
|
||||
args.add(FormatArgument {
|
||||
kind: match arg.name() {
|
||||
Some(name) => FormatArgumentKind::Named(name.as_name()),
|
||||
kind: match arg.arg_name() {
|
||||
Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),
|
||||
None => FormatArgumentKind::Normal,
|
||||
},
|
||||
expr: self.collect_expr_opt(arg.expr()),
|
||||
|
|
|
|||
|
|
@ -190,13 +190,13 @@ fn f() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
ModuleIdLt {
|
||||
[salsa id]: Id(3003),
|
||||
[salsa id]: Id(3803),
|
||||
krate: Crate(
|
||||
Id(1c00),
|
||||
Id(2400),
|
||||
),
|
||||
block: Some(
|
||||
BlockId(
|
||||
3c01,
|
||||
4401,
|
||||
),
|
||||
),
|
||||
}"#]],
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ use indexmap::map::Entry;
|
|||
use itertools::Itertools;
|
||||
use la_arena::Idx;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use smallvec::SmallVec;
|
||||
use span::Edition;
|
||||
use stdx::format_to;
|
||||
use syntax::ast;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::{
|
||||
AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
|
||||
Lookup, MacroCallStyles, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
|
||||
AdtId, BuiltinDeriveImplId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap,
|
||||
HasModule, ImplId, Lookup, MacroCallStyles, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
|
||||
db::DefDatabase,
|
||||
per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
|
||||
visibility::Visibility,
|
||||
|
|
@ -159,6 +159,7 @@ pub struct ItemScope {
|
|||
declarations: ThinVec<ModuleDefId>,
|
||||
|
||||
impls: ThinVec<ImplId>,
|
||||
builtin_derive_impls: ThinVec<BuiltinDeriveImplId>,
|
||||
extern_blocks: ThinVec<ExternBlockId>,
|
||||
unnamed_consts: ThinVec<ConstId>,
|
||||
/// Traits imported via `use Trait as _;`.
|
||||
|
|
@ -329,6 +330,10 @@ impl ItemScope {
|
|||
self.impls.iter().copied()
|
||||
}
|
||||
|
||||
pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator<Item = BuiltinDeriveImplId> + '_ {
|
||||
self.builtin_derive_impls.iter().copied()
|
||||
}
|
||||
|
||||
pub fn all_macro_calls(&self) -> impl Iterator<Item = MacroCallId> + '_ {
|
||||
self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain(
|
||||
self.derive_macros.values().flat_map(|it| {
|
||||
|
|
@ -471,6 +476,10 @@ impl ItemScope {
|
|||
self.impls.push(imp);
|
||||
}
|
||||
|
||||
pub(crate) fn define_builtin_derive_impl(&mut self, imp: BuiltinDeriveImplId) {
|
||||
self.builtin_derive_impls.push(imp);
|
||||
}
|
||||
|
||||
pub(crate) fn define_extern_block(&mut self, extern_block: ExternBlockId) {
|
||||
self.extern_blocks.push(extern_block);
|
||||
}
|
||||
|
|
@ -522,12 +531,13 @@ impl ItemScope {
|
|||
adt: AstId<ast::Adt>,
|
||||
attr_id: AttrId,
|
||||
attr_call_id: MacroCallId,
|
||||
len: usize,
|
||||
mut derive_call_ids: SmallVec<[Option<MacroCallId>; 4]>,
|
||||
) {
|
||||
derive_call_ids.shrink_to_fit();
|
||||
self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
|
||||
attr_id,
|
||||
attr_call_id,
|
||||
derive_call_ids: smallvec![None; len],
|
||||
derive_call_ids,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -811,6 +821,7 @@ impl ItemScope {
|
|||
unresolved,
|
||||
declarations,
|
||||
impls,
|
||||
builtin_derive_impls,
|
||||
unnamed_consts,
|
||||
unnamed_trait_imports,
|
||||
legacy_macros,
|
||||
|
|
@ -834,6 +845,7 @@ impl ItemScope {
|
|||
unresolved.shrink_to_fit();
|
||||
declarations.shrink_to_fit();
|
||||
impls.shrink_to_fit();
|
||||
builtin_derive_impls.shrink_to_fit();
|
||||
unnamed_consts.shrink_to_fit();
|
||||
unnamed_trait_imports.shrink_to_fit();
|
||||
legacy_macros.shrink_to_fit();
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ fn lower_extra_crate_attrs<'a>(
|
|||
struct FakeSpanMap {
|
||||
file_id: span::EditionedFileId,
|
||||
}
|
||||
impl syntax_bridge::SpanMapper<Span> for FakeSpanMap {
|
||||
impl syntax_bridge::SpanMapper for FakeSpanMap {
|
||||
fn span_for(&self, range: TextRange) -> Span {
|
||||
Span {
|
||||
range,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use hir_expand::{
|
|||
name::Name,
|
||||
};
|
||||
use intern::{Interned, Symbol, sym};
|
||||
use span::Span;
|
||||
use syntax::{AstNode, T, ast};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use tt::token_to_literal;
|
||||
|
|
@ -49,7 +48,7 @@ impl AttrsOrCfg {
|
|||
span_map: S,
|
||||
) -> AttrsOrCfg
|
||||
where
|
||||
S: syntax_bridge::SpanMapper<Span> + Copy,
|
||||
S: syntax_bridge::SpanMapper + Copy,
|
||||
{
|
||||
let mut attrs = Vec::new();
|
||||
let result =
|
||||
|
|
@ -227,7 +226,7 @@ impl<'attr> AttrQuery<'attr> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
|
||||
pub(crate) fn string_value_with_span(self) -> Option<(&'attr str, span::Span)> {
|
||||
self.attrs().find_map(|attr| attr.string_value_with_span())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
//!
|
||||
//! This attribute to tell the compiler about semi built-in std library
|
||||
//! features, such as Fn family of traits.
|
||||
use hir_expand::name::Name;
|
||||
use intern::{Symbol, sym};
|
||||
use stdx::impl_from;
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ use crate::{
|
|||
StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
attrs::AttrFlags,
|
||||
db::DefDatabase,
|
||||
nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map},
|
||||
nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -93,6 +94,10 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
|
|||
}
|
||||
}
|
||||
|
||||
if matches!(krate.data(db).origin, base_db::CrateOrigin::Lang(base_db::LangCrateOrigin::Core)) {
|
||||
lang_items.fill_non_lang_core_traits(db, crate_def_map);
|
||||
}
|
||||
|
||||
if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) }
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +140,31 @@ impl LangItems {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_core_trait(
|
||||
db: &dyn DefDatabase,
|
||||
core_def_map: &DefMap,
|
||||
modules: &[Symbol],
|
||||
name: Symbol,
|
||||
) -> Option<TraitId> {
|
||||
let mut current = &core_def_map[core_def_map.root];
|
||||
for module in modules {
|
||||
let Some((ModuleDefId::ModuleId(cur), _)) =
|
||||
current.scope.type_(&Name::new_symbol_root(module.clone()))
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() {
|
||||
return None;
|
||||
}
|
||||
current = &core_def_map[cur];
|
||||
}
|
||||
let Some((ModuleDefId::TraitId(trait_), _)) = current.scope.type_(&Name::new_symbol_root(name))
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
Some(trait_)
|
||||
}
|
||||
|
||||
#[salsa::tracked(returns(as_deref))]
|
||||
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> {
|
||||
let mut traits = Vec::new();
|
||||
|
|
@ -158,6 +188,10 @@ macro_rules! language_item_table {
|
|||
(
|
||||
$LangItems:ident =>
|
||||
$( $(#[$attr:meta])* $lang_item:ident, $module:ident :: $name:ident, $target:ident; )*
|
||||
|
||||
@non_lang_core_traits:
|
||||
|
||||
$( core::$($non_lang_module:ident)::*, $non_lang_trait:ident; )*
|
||||
) => {
|
||||
#[allow(non_snake_case)] // FIXME: Should we remove this?
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -166,6 +200,9 @@ macro_rules! language_item_table {
|
|||
$(#[$attr])*
|
||||
pub $lang_item: Option<$target>,
|
||||
)*
|
||||
$(
|
||||
pub $non_lang_trait: Option<TraitId>,
|
||||
)*
|
||||
}
|
||||
|
||||
impl LangItems {
|
||||
|
|
@ -176,6 +213,7 @@ macro_rules! language_item_table {
|
|||
/// Merges `self` with `other`, with preference to `self` items.
|
||||
fn merge_prefer_self(&mut self, other: &Self) {
|
||||
$( self.$lang_item = self.$lang_item.or(other.$lang_item); )*
|
||||
$( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )*
|
||||
}
|
||||
|
||||
fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) {
|
||||
|
|
@ -190,6 +228,10 @@ macro_rules! language_item_table {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_non_lang_core_traits(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) {
|
||||
$( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_module),* ], sym::$non_lang_trait); )*
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -426,4 +468,11 @@ language_item_table! { LangItems =>
|
|||
String, sym::String, StructId;
|
||||
CStr, sym::CStr, StructId;
|
||||
Ordering, sym::Ordering, EnumId;
|
||||
|
||||
@non_lang_core_traits:
|
||||
core::default, Default;
|
||||
core::fmt, Debug;
|
||||
core::hash, Hash;
|
||||
core::cmp, Ord;
|
||||
core::cmp, Eq;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ pub mod dyn_map;
|
|||
|
||||
pub mod item_tree;
|
||||
|
||||
pub mod builtin_derive;
|
||||
pub mod lang_item;
|
||||
|
||||
pub mod hir;
|
||||
|
|
@ -63,6 +64,7 @@ use base_db::{Crate, impl_intern_key};
|
|||
use hir_expand::{
|
||||
AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallStyles,
|
||||
MacroDefId, MacroDefKind,
|
||||
attrs::AttrId,
|
||||
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
|
||||
db::ExpandDatabase,
|
||||
eager::expand_eager_macro_input,
|
||||
|
|
@ -80,6 +82,7 @@ pub use hir_expand::{Intern, Lookup, tt};
|
|||
|
||||
use crate::{
|
||||
attrs::AttrFlags,
|
||||
builtin_derive::BuiltinDeriveImplTrait,
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
expr_store::ExpressionStoreSourceMap,
|
||||
|
|
@ -331,6 +334,21 @@ impl ImplId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct BuiltinDeriveImplLoc {
|
||||
pub adt: AdtId,
|
||||
pub trait_: BuiltinDeriveImplTrait,
|
||||
pub derive_attr_id: AttrId,
|
||||
pub derive_index: u32,
|
||||
}
|
||||
|
||||
#[salsa::interned(debug, no_lifetime)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct BuiltinDeriveImplId {
|
||||
#[returns(ref)]
|
||||
pub loc: BuiltinDeriveImplLoc,
|
||||
}
|
||||
|
||||
type UseLoc = ItemLoc<ast::Use>;
|
||||
impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
|
||||
|
||||
|
|
@ -660,6 +678,18 @@ impl_from!(
|
|||
for ModuleDefId
|
||||
);
|
||||
|
||||
impl From<DefWithBodyId> for ModuleDefId {
|
||||
#[inline]
|
||||
fn from(value: DefWithBodyId) -> Self {
|
||||
match value {
|
||||
DefWithBodyId::FunctionId(id) => id.into(),
|
||||
DefWithBodyId::StaticId(id) => id.into(),
|
||||
DefWithBodyId::ConstId(id) => id.into(),
|
||||
DefWithBodyId::VariantId(id) => id.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A constant, which might appears as a const item, an anonymous const block in expressions
|
||||
/// or patterns, or as a constant in types with const generics.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
|
||||
|
|
@ -1009,6 +1039,20 @@ fn module_for_assoc_item_loc<'db>(
|
|||
id.lookup(db).container.module(db)
|
||||
}
|
||||
|
||||
impl HasModule for BuiltinDeriveImplLoc {
|
||||
#[inline]
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
self.adt.module(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for BuiltinDeriveImplId {
|
||||
#[inline]
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
self.loc(db).module(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for FunctionId {
|
||||
#[inline]
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ macro_rules! f {
|
|||
};
|
||||
}
|
||||
|
||||
struct#0:MacroRules[BE8F, 0]@58..64#15360# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#15360#
|
||||
map#0:MacroRules[BE8F, 0]@86..89#15360#:#0:MacroRules[BE8F, 0]@89..90#15360# #0:MacroRules[BE8F, 0]@89..90#15360#::#0:MacroRules[BE8F, 0]@91..93#15360#std#0:MacroRules[BE8F, 0]@93..96#15360#::#0:MacroRules[BE8F, 0]@96..98#15360#collections#0:MacroRules[BE8F, 0]@98..109#15360#::#0:MacroRules[BE8F, 0]@109..111#15360#HashSet#0:MacroRules[BE8F, 0]@111..118#15360#<#0:MacroRules[BE8F, 0]@118..119#15360#(#0:MacroRules[BE8F, 0]@119..120#15360#)#0:MacroRules[BE8F, 0]@120..121#15360#>#0:MacroRules[BE8F, 0]@121..122#15360#,#0:MacroRules[BE8F, 0]@122..123#15360#
|
||||
}#0:MacroRules[BE8F, 0]@132..133#15360#
|
||||
struct#0:MacroRules[BE8F, 0]@58..64#17408# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#17408#
|
||||
map#0:MacroRules[BE8F, 0]@86..89#17408#:#0:MacroRules[BE8F, 0]@89..90#17408# #0:MacroRules[BE8F, 0]@89..90#17408#::#0:MacroRules[BE8F, 0]@91..93#17408#std#0:MacroRules[BE8F, 0]@93..96#17408#::#0:MacroRules[BE8F, 0]@96..98#17408#collections#0:MacroRules[BE8F, 0]@98..109#17408#::#0:MacroRules[BE8F, 0]@109..111#17408#HashSet#0:MacroRules[BE8F, 0]@111..118#17408#<#0:MacroRules[BE8F, 0]@118..119#17408#(#0:MacroRules[BE8F, 0]@119..120#17408#)#0:MacroRules[BE8F, 0]@120..121#17408#>#0:MacroRules[BE8F, 0]@121..122#17408#,#0:MacroRules[BE8F, 0]@122..123#17408#
|
||||
}#0:MacroRules[BE8F, 0]@132..133#17408#
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -197,7 +197,7 @@ macro_rules! mk_struct {
|
|||
#[macro_use]
|
||||
mod foo;
|
||||
|
||||
struct#1:MacroRules[DB0C, 0]@59..65#15360# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#15360#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#15360#;#1:MacroRules[DB0C, 0]@75..76#15360#
|
||||
struct#1:MacroRules[DB0C, 0]@59..65#17408# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#17408#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#17408#;#1:MacroRules[DB0C, 0]@75..76#17408#
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -423,10 +423,10 @@ m! { foo, bar }
|
|||
macro_rules! m {
|
||||
($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
|
||||
}
|
||||
impl#\15360# Bar#\15360# {#\15360#
|
||||
fn#\15360# foo#\ROOT2024#(#\15360#)#\15360# {#\15360#}#\15360#
|
||||
fn#\15360# bar#\ROOT2024#(#\15360#)#\15360# {#\15360#}#\15360#
|
||||
}#\15360#
|
||||
impl#\17408# Bar#\17408# {#\17408#
|
||||
fn#\17408# foo#\ROOT2024#(#\17408#)#\17408# {#\17408#}#\17408#
|
||||
fn#\17408# bar#\ROOT2024#(#\17408#)#\17408# {#\17408#}#\17408#
|
||||
}#\17408#
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ mod proc_macros;
|
|||
|
||||
use std::{any::TypeId, iter, ops::Range, sync};
|
||||
|
||||
use base_db::RootQueryDb;
|
||||
use base_db::{RootQueryDb, SourceDatabase};
|
||||
use expect_test::Expect;
|
||||
use hir_expand::{
|
||||
AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind,
|
||||
|
|
@ -53,6 +53,8 @@ use crate::{
|
|||
|
||||
#[track_caller]
|
||||
fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(false);
|
||||
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let krate = db.fetch_test_crate();
|
||||
let def_map = crate_def_map(&db, krate);
|
||||
|
|
@ -80,10 +82,15 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
|
|||
.sorted_unstable_by_key(|(range, _)| range.start())
|
||||
.format_with("\n", |(range, err), format| format(&format_args!("{range:?}: {err}")))
|
||||
.to_string();
|
||||
|
||||
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(true);
|
||||
|
||||
expect.assert_eq(&errors);
|
||||
}
|
||||
|
||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
|
||||
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(false);
|
||||
|
||||
let extra_proc_macros = vec![(
|
||||
r#"
|
||||
#[proc_macro_attribute]
|
||||
|
|
@ -246,6 +253,8 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
}
|
||||
}
|
||||
|
||||
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(true);
|
||||
|
||||
expect.indent(false);
|
||||
expect.assert_eq(&expanded_text);
|
||||
}
|
||||
|
|
@ -378,6 +387,7 @@ struct IdentityWhenValidProcMacroExpander;
|
|||
impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
|
||||
fn expand(
|
||||
&self,
|
||||
_: &dyn SourceDatabase,
|
||||
subtree: &TopSubtree,
|
||||
_: Option<&TopSubtree>,
|
||||
_: &base_db::Env,
|
||||
|
|
|
|||
|
|
@ -122,16 +122,16 @@ struct Foo {
|
|||
v4: bool // No comma here
|
||||
}
|
||||
|
||||
#[attr1]
|
||||
#[derive(Bar)]
|
||||
#[attr2] struct S;
|
||||
#[attr1]
|
||||
#[my_cool_derive()] struct Foo {
|
||||
v1: i32, #[attr3]v2: fn(#[attr4]param2: u32), v3: Foo< {
|
||||
456
|
||||
}
|
||||
>,
|
||||
}
|
||||
#[attr1]
|
||||
#[derive(Bar)]
|
||||
#[attr2] struct S;"#]],
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,25 @@ use crate::{
|
|||
|
||||
pub use self::path_resolution::ResolvePathResultPrefixInfo;
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// HACK: In order to test builtin derive expansion, we gate their fast path with this atomic when cfg(test).
|
||||
pub(crate) static ENABLE_BUILTIN_DERIVE_FAST_PATH: std::cell::Cell<bool> =
|
||||
const { std::cell::Cell::new(true) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(test)]
|
||||
fn enable_builtin_derive_fast_path() -> bool {
|
||||
ENABLE_BUILTIN_DERIVE_FAST_PATH.get()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(test))]
|
||||
fn enable_builtin_derive_fast_path() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
const PREDEFINED_TOOLS: &[SmolStr] = &[
|
||||
SmolStr::new_static("clippy"),
|
||||
SmolStr::new_static("rustfmt"),
|
||||
|
|
@ -483,6 +502,7 @@ impl DefMap {
|
|||
}
|
||||
|
||||
impl DefMap {
|
||||
/// Returns all modules in the crate that are associated with the given file.
|
||||
pub fn modules_for_file<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn DefDatabase,
|
||||
|
|
@ -490,16 +510,33 @@ impl DefMap {
|
|||
) -> impl Iterator<Item = ModuleId> + 'a {
|
||||
self.modules
|
||||
.iter()
|
||||
.filter(move |(_id, data)| {
|
||||
.filter(move |(_, data)| {
|
||||
data.origin.file_id().map(|file_id| file_id.file_id(db)) == Some(file_id)
|
||||
})
|
||||
.map(|(id, _data)| id)
|
||||
.map(|(id, _)| id)
|
||||
}
|
||||
|
||||
pub fn modules(&self) -> impl Iterator<Item = (ModuleId, &ModuleData)> + '_ {
|
||||
self.modules.iter()
|
||||
}
|
||||
|
||||
/// Returns all inline modules (mod name { ... }) in the crate that are associated with the given macro expansion.
|
||||
pub fn inline_modules_for_macro_file(
|
||||
&self,
|
||||
file_id: MacroCallId,
|
||||
) -> impl Iterator<Item = ModuleId> + '_ {
|
||||
self.modules
|
||||
.iter()
|
||||
.filter(move |(_, data)| {
|
||||
matches!(
|
||||
data.origin,
|
||||
ModuleOrigin::Inline { definition_tree_id, .. }
|
||||
if definition_tree_id.file_id().macro_file() == Some(file_id)
|
||||
)
|
||||
})
|
||||
.map(|(id, _)| id)
|
||||
}
|
||||
|
||||
pub fn derive_helpers_in_scope(
|
||||
&self,
|
||||
id: AstId<ast::Adt>,
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ pub(super) fn attr_macro_as_call_id(
|
|||
let arg = match macro_attr.input.as_deref() {
|
||||
Some(AttrInput::TokenTree(tt)) => {
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
|
||||
Some(tt)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,26 +12,28 @@ use hir_expand::{
|
|||
AttrMacroAttrIds, EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId,
|
||||
MacroCallKind, MacroDefId, MacroDefKind,
|
||||
attrs::{Attr, AttrId},
|
||||
builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
|
||||
builtin::{BuiltinDeriveExpander, find_builtin_attr, find_builtin_derive, find_builtin_macro},
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::{AsName, Name},
|
||||
proc_macro::CustomProcMacroExpander,
|
||||
};
|
||||
use intern::{Interned, sym};
|
||||
use intern::{Interned, Symbol, sym};
|
||||
use itertools::izip;
|
||||
use la_arena::Idx;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::SmallVec;
|
||||
use span::{Edition, FileAstId, SyntaxContext};
|
||||
use stdx::always;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, EnumLoc, ExternBlockLoc, ExternCrateId,
|
||||
ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern, ItemContainerId, Lookup,
|
||||
Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags,
|
||||
ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc,
|
||||
UnionLoc, UnresolvedMacro, UseId, UseLoc,
|
||||
AdtId, AssocItemId, AstId, AstIdWithPath, BuiltinDeriveImplId, BuiltinDeriveImplLoc, ConstLoc,
|
||||
EnumLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap,
|
||||
ImplLoc, Intern, ItemContainerId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
|
||||
MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
|
||||
ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
|
||||
UseLoc,
|
||||
db::DefDatabase,
|
||||
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
|
||||
item_tree::{
|
||||
|
|
@ -104,6 +106,7 @@ pub(super) fn collect_defs(
|
|||
prev_active_attrs: Default::default(),
|
||||
unresolved_extern_crates: Default::default(),
|
||||
is_proc_macro: krate.is_proc_macro,
|
||||
deferred_builtin_derives: Default::default(),
|
||||
};
|
||||
if tree_id.is_block() {
|
||||
collector.seed_with_inner(tree_id);
|
||||
|
|
@ -214,6 +217,17 @@ enum MacroDirectiveKind<'db> {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DeferredBuiltinDerive {
|
||||
call_id: MacroCallId,
|
||||
derive: BuiltinDeriveExpander,
|
||||
module_id: ModuleId,
|
||||
depth: usize,
|
||||
container: ItemContainerId,
|
||||
derive_attr_id: AttrId,
|
||||
derive_index: u32,
|
||||
}
|
||||
|
||||
/// Walks the tree of module recursively
|
||||
struct DefCollector<'db> {
|
||||
db: &'db dyn DefDatabase,
|
||||
|
|
@ -252,6 +266,11 @@ struct DefCollector<'db> {
|
|||
/// on the same item. Therefore, this holds all active attributes that we already
|
||||
/// expanded.
|
||||
prev_active_attrs: FxHashMap<AstId<ast::Item>, SmallVec<[AttrId; 1]>>,
|
||||
/// To save memory, we do not really expand builtin derives. Instead, we save them as a `BuiltinDeriveImplId`.
|
||||
///
|
||||
/// However, we can only do that when the derive is directly above the item, and there is no attribute in between.
|
||||
/// Otherwise, all sorts of weird things can happen, like the item name resolving to something else.
|
||||
deferred_builtin_derives: FxHashMap<AstId<ast::Item>, Vec<DeferredBuiltinDerive>>,
|
||||
}
|
||||
|
||||
impl<'db> DefCollector<'db> {
|
||||
|
|
@ -273,13 +292,13 @@ impl<'db> DefCollector<'db> {
|
|||
match () {
|
||||
() if *attr_name == sym::recursion_limit => {
|
||||
if let Some(limit) = attr.string_value()
|
||||
&& let Ok(limit) = limit.as_str().parse()
|
||||
&& let Ok(limit) = limit.parse()
|
||||
{
|
||||
crate_data.recursion_limit = Some(limit);
|
||||
}
|
||||
}
|
||||
() if *attr_name == sym::crate_type => {
|
||||
if attr.string_value() == Some(&sym::proc_dash_macro) {
|
||||
if attr.string_value() == Some("proc-macro") {
|
||||
self.is_proc_macro = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1241,7 +1260,7 @@ impl<'db> DefCollector<'db> {
|
|||
fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
let mut macros = mem::take(&mut self.unresolved_macros);
|
||||
let mut resolved = Vec::new();
|
||||
let mut push_resolved = |directive: &MacroDirective<'_>, call_id| {
|
||||
let push_resolved = |resolved: &mut Vec<_>, directive: &MacroDirective<'_>, call_id| {
|
||||
let attr_macro_item = match &directive.kind {
|
||||
MacroDirectiveKind::Attr { ast_id, .. } => Some(ast_id.ast_id),
|
||||
MacroDirectiveKind::FnLike { .. } | MacroDirectiveKind::Derive { .. } => None,
|
||||
|
|
@ -1271,8 +1290,8 @@ impl<'db> DefCollector<'db> {
|
|||
MacroSubNs::Attr
|
||||
}
|
||||
};
|
||||
let resolver = |path: &_| {
|
||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||
let resolver = |def_map: &DefMap, path: &_| {
|
||||
let resolved_res = def_map.resolve_path_fp_with_macro(
|
||||
self.crate_local_def_map.unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
ResolveMode::Other,
|
||||
|
|
@ -1283,7 +1302,7 @@ impl<'db> DefCollector<'db> {
|
|||
);
|
||||
resolved_res.resolved_def.take_macros().map(|it| (it, self.db.macro_def(it)))
|
||||
};
|
||||
let resolver_def_id = |path: &_| resolver(path).map(|(_, it)| it);
|
||||
let resolver_def_id = |path: &_| resolver(&self.def_map, path).map(|(_, it)| it);
|
||||
|
||||
match &directive.kind {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
|
||||
|
|
@ -1306,7 +1325,7 @@ impl<'db> DefCollector<'db> {
|
|||
.scope
|
||||
.add_macro_invoc(ast_id.ast_id, call_id);
|
||||
|
||||
push_resolved(directive, call_id);
|
||||
push_resolved(&mut resolved, directive, call_id);
|
||||
|
||||
res = ReachedFixedPoint::No;
|
||||
return Resolved::Yes;
|
||||
|
|
@ -1320,6 +1339,7 @@ impl<'db> DefCollector<'db> {
|
|||
ctxt: call_site,
|
||||
derive_macro_id,
|
||||
} => {
|
||||
// FIXME: This code is almost duplicate below.
|
||||
let id = derive_macro_as_call_id(
|
||||
self.db,
|
||||
ast_id,
|
||||
|
|
@ -1327,7 +1347,7 @@ impl<'db> DefCollector<'db> {
|
|||
*derive_pos as u32,
|
||||
*call_site,
|
||||
self.def_map.krate,
|
||||
resolver,
|
||||
|path| resolver(&self.def_map, path),
|
||||
*derive_macro_id,
|
||||
);
|
||||
|
||||
|
|
@ -1354,7 +1374,8 @@ impl<'db> DefCollector<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
push_resolved(directive, call_id);
|
||||
push_resolved(&mut resolved, directive, call_id);
|
||||
|
||||
res = ReachedFixedPoint::No;
|
||||
return Resolved::Yes;
|
||||
}
|
||||
|
|
@ -1460,29 +1481,85 @@ impl<'db> DefCollector<'db> {
|
|||
|
||||
let ast_id = ast_id.with_value(ast_adt_id);
|
||||
|
||||
let mut derive_call_ids = SmallVec::new();
|
||||
match attr.parse_path_comma_token_tree(self.db) {
|
||||
Some(derive_macros) => {
|
||||
let call_id = call_id();
|
||||
let mut len = 0;
|
||||
for (idx, (path, call_site, _)) in derive_macros.enumerate() {
|
||||
let ast_id = AstIdWithPath::new(
|
||||
file_id,
|
||||
ast_id.value,
|
||||
Interned::new(path),
|
||||
);
|
||||
self.unresolved_macros.push(MacroDirective {
|
||||
module_id: directive.module_id,
|
||||
depth: directive.depth + 1,
|
||||
kind: MacroDirectiveKind::Derive {
|
||||
ast_id,
|
||||
derive_attr: *attr_id,
|
||||
derive_pos: idx,
|
||||
ctxt: call_site.ctx,
|
||||
derive_macro_id: call_id,
|
||||
},
|
||||
container: directive.container,
|
||||
});
|
||||
len = idx;
|
||||
|
||||
// Try to resolve the derive immediately. If we succeed, we can also use the fast path
|
||||
// for builtin derives. If not, we cannot use it, as it can cause the ADT to become
|
||||
// interned while the derive is still unresolved, which will cause it to get forgotten.
|
||||
let id = derive_macro_as_call_id(
|
||||
self.db,
|
||||
&ast_id,
|
||||
*attr_id,
|
||||
idx as u32,
|
||||
call_site.ctx,
|
||||
self.def_map.krate,
|
||||
|path| resolver(&self.def_map, path),
|
||||
call_id,
|
||||
);
|
||||
|
||||
if let Ok((macro_id, def_id, call_id)) = id {
|
||||
derive_call_ids.push(Some(call_id));
|
||||
// Record its helper attributes.
|
||||
if def_id.krate != self.def_map.krate {
|
||||
let def_map = crate_def_map(self.db, def_id.krate);
|
||||
if let Some(helpers) =
|
||||
def_map.data.exported_derives.get(¯o_id)
|
||||
{
|
||||
self.def_map
|
||||
.derive_helpers_in_scope
|
||||
.entry(ast_id.ast_id.map(|it| it.upcast()))
|
||||
.or_default()
|
||||
.extend(izip!(
|
||||
helpers.iter().cloned(),
|
||||
iter::repeat(macro_id),
|
||||
iter::repeat(call_id),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if super::enable_builtin_derive_fast_path()
|
||||
&& let MacroDefKind::BuiltInDerive(_, builtin_derive) =
|
||||
def_id.kind
|
||||
{
|
||||
self.deferred_builtin_derives
|
||||
.entry(ast_id.ast_id.upcast())
|
||||
.or_default()
|
||||
.push(DeferredBuiltinDerive {
|
||||
call_id,
|
||||
derive: builtin_derive,
|
||||
module_id: directive.module_id,
|
||||
container: directive.container,
|
||||
depth: directive.depth,
|
||||
derive_attr_id: *attr_id,
|
||||
derive_index: idx as u32,
|
||||
});
|
||||
} else {
|
||||
push_resolved(&mut resolved, directive, call_id);
|
||||
}
|
||||
} else {
|
||||
derive_call_ids.push(None);
|
||||
self.unresolved_macros.push(MacroDirective {
|
||||
module_id: directive.module_id,
|
||||
depth: directive.depth + 1,
|
||||
kind: MacroDirectiveKind::Derive {
|
||||
ast_id,
|
||||
derive_attr: *attr_id,
|
||||
derive_pos: idx,
|
||||
ctxt: call_site.ctx,
|
||||
derive_macro_id: call_id,
|
||||
},
|
||||
container: directive.container,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// We treat the #[derive] macro as an attribute call, but we do not resolve it for nameres collection.
|
||||
|
|
@ -1491,7 +1568,12 @@ impl<'db> DefCollector<'db> {
|
|||
// Check the comment in [`builtin_attr_macro`].
|
||||
self.def_map.modules[directive.module_id]
|
||||
.scope
|
||||
.init_derive_attribute(ast_id, *attr_id, call_id, len + 1);
|
||||
.init_derive_attribute(
|
||||
ast_id,
|
||||
*attr_id,
|
||||
call_id,
|
||||
derive_call_ids,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
let diag = DefDiagnostic::malformed_derive(
|
||||
|
|
@ -1522,12 +1604,25 @@ impl<'db> DefCollector<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
// Clear deferred derives for this item, unfortunately we cannot use them due to the attribute.
|
||||
if let Some(deferred_derives) = self.deferred_builtin_derives.remove(&ast_id) {
|
||||
resolved.extend(deferred_derives.into_iter().map(|derive| {
|
||||
(
|
||||
derive.module_id,
|
||||
derive.depth,
|
||||
derive.container,
|
||||
derive.call_id,
|
||||
Some(ast_id),
|
||||
)
|
||||
}));
|
||||
}
|
||||
|
||||
let call_id = call_id();
|
||||
self.def_map.modules[directive.module_id]
|
||||
.scope
|
||||
.add_attr_macro_invoc(ast_id, call_id);
|
||||
|
||||
push_resolved(directive, call_id);
|
||||
push_resolved(&mut resolved, directive, call_id);
|
||||
res = ReachedFixedPoint::No;
|
||||
return Resolved::Yes;
|
||||
}
|
||||
|
|
@ -1709,6 +1804,12 @@ impl<'db> DefCollector<'db> {
|
|||
));
|
||||
}
|
||||
|
||||
always!(
|
||||
self.deferred_builtin_derives.is_empty(),
|
||||
"self.deferred_builtin_derives={:#?}",
|
||||
self.deferred_builtin_derives,
|
||||
);
|
||||
|
||||
(self.def_map, self.local_def_map)
|
||||
}
|
||||
}
|
||||
|
|
@ -1751,6 +1852,33 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
let db = self.def_collector.db;
|
||||
let module_id = self.module_id;
|
||||
let consider_deferred_derives =
|
||||
|file_id: HirFileId,
|
||||
deferred_derives: &mut FxHashMap<_, Vec<DeferredBuiltinDerive>>,
|
||||
ast_id: FileAstId<ast::Adt>,
|
||||
id: AdtId,
|
||||
def_map: &mut DefMap| {
|
||||
let Some(deferred_derives) =
|
||||
deferred_derives.remove(&InFile::new(file_id, ast_id.upcast()))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let module = &mut def_map.modules[module_id];
|
||||
for deferred_derive in deferred_derives {
|
||||
crate::builtin_derive::with_derive_traits(deferred_derive.derive, |trait_| {
|
||||
let impl_id = BuiltinDeriveImplId::new(
|
||||
db,
|
||||
BuiltinDeriveImplLoc {
|
||||
adt: id,
|
||||
trait_,
|
||||
derive_attr_id: deferred_derive.derive_attr_id,
|
||||
derive_index: deferred_derive.derive_index,
|
||||
},
|
||||
);
|
||||
module.scope.define_builtin_derive_impl(impl_id);
|
||||
});
|
||||
}
|
||||
};
|
||||
let update_def =
|
||||
|def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
|
||||
def_collector.def_map.modules[module_id].scope.declare(id);
|
||||
|
|
@ -1928,11 +2056,21 @@ impl ModCollector<'_, '_> {
|
|||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
let interned = StructLoc {
|
||||
container: module_id,
|
||||
id: InFile::new(self.tree_id.file_id(), id),
|
||||
}
|
||||
.intern(db);
|
||||
consider_deferred_derives(
|
||||
self.tree_id.file_id(),
|
||||
&mut self.def_collector.deferred_builtin_derives,
|
||||
id.upcast(),
|
||||
interned.into(),
|
||||
def_map,
|
||||
);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
StructLoc { container: module_id, id: InFile::new(self.file_id(), id) }
|
||||
.intern(db)
|
||||
.into(),
|
||||
interned.into(),
|
||||
&it.name,
|
||||
vis,
|
||||
!matches!(it.shape, FieldsShape::Record),
|
||||
|
|
@ -1942,15 +2080,19 @@ impl ModCollector<'_, '_> {
|
|||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
UnionLoc { container: module_id, id: InFile::new(self.file_id(), id) }
|
||||
.intern(db)
|
||||
.into(),
|
||||
&it.name,
|
||||
vis,
|
||||
false,
|
||||
let interned = UnionLoc {
|
||||
container: module_id,
|
||||
id: InFile::new(self.tree_id.file_id(), id),
|
||||
}
|
||||
.intern(db);
|
||||
consider_deferred_derives(
|
||||
self.tree_id.file_id(),
|
||||
&mut self.def_collector.deferred_builtin_derives,
|
||||
id.upcast(),
|
||||
interned.into(),
|
||||
def_map,
|
||||
);
|
||||
update_def(self.def_collector, interned.into(), &it.name, vis, false);
|
||||
}
|
||||
ModItemId::Enum(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
|
@ -1960,6 +2102,13 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
.intern(db);
|
||||
|
||||
consider_deferred_derives(
|
||||
self.tree_id.file_id(),
|
||||
&mut self.def_collector.deferred_builtin_derives,
|
||||
id.upcast(),
|
||||
enum_.into(),
|
||||
def_map,
|
||||
);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(self.def_collector, enum_.into(), &it.name, vis, false);
|
||||
}
|
||||
|
|
@ -2311,14 +2460,14 @@ impl ModCollector<'_, '_> {
|
|||
let name;
|
||||
let name = match attrs.by_key(sym::rustc_builtin_macro).string_value_with_span() {
|
||||
Some((it, span)) => {
|
||||
name = Name::new_symbol(it.clone(), span.ctx);
|
||||
name = Name::new_symbol(Symbol::intern(it), span.ctx);
|
||||
&name
|
||||
}
|
||||
None => {
|
||||
let explicit_name =
|
||||
attrs.by_key(sym::rustc_builtin_macro).tt_values().next().and_then(|tt| {
|
||||
match tt.token_trees().flat_tokens().first() {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
|
||||
match tt.token_trees().iter().next() {
|
||||
Some(tt::TtElement::Leaf(tt::Leaf::Ident(name))) => Some(name),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use intern::sym;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
item_tree::Attrs,
|
||||
tt::{Leaf, TokenTree, TopSubtree, TtElement},
|
||||
tt::{Leaf, TopSubtree, TtElement},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
|
@ -61,35 +62,35 @@ impl Attrs<'_> {
|
|||
|
||||
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
|
||||
// the same structure.
|
||||
#[rustfmt::skip]
|
||||
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &TopSubtree) -> Option<(Name, Box<[Name]>)> {
|
||||
match tt.token_trees().flat_tokens() {
|
||||
if let Some([TtElement::Leaf(Leaf::Ident(trait_name))]) =
|
||||
tt.token_trees().iter().collect_array()
|
||||
{
|
||||
// `#[proc_macro_derive(Trait)]`
|
||||
// `#[rustc_builtin_macro(Trait)]`
|
||||
[TokenTree::Leaf(Leaf::Ident(trait_name))] => Some((trait_name.as_name(), Box::new([]))),
|
||||
|
||||
Some((trait_name.as_name(), Box::new([])))
|
||||
} else if let Some(
|
||||
[
|
||||
TtElement::Leaf(Leaf::Ident(trait_name)),
|
||||
TtElement::Leaf(Leaf::Punct(comma)),
|
||||
TtElement::Leaf(Leaf::Ident(attributes)),
|
||||
TtElement::Subtree(_, helpers),
|
||||
],
|
||||
) = tt.token_trees().iter().collect_array()
|
||||
&& comma.char == ','
|
||||
&& attributes.sym == sym::attributes
|
||||
{
|
||||
// `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
|
||||
// `#[rustc_builtin_macro(Trait, attributes(helper1, helper2, ...))]`
|
||||
[
|
||||
TokenTree::Leaf(Leaf::Ident(trait_name)),
|
||||
TokenTree::Leaf(Leaf::Punct(comma)),
|
||||
TokenTree::Leaf(Leaf::Ident(attributes)),
|
||||
TokenTree::Subtree(_),
|
||||
..
|
||||
] if comma.char == ',' && attributes.sym == sym::attributes =>
|
||||
{
|
||||
let helpers = tt::TokenTreesView::new(&tt.token_trees().flat_tokens()[3..]).try_into_subtree()?;
|
||||
let helpers = helpers
|
||||
.iter()
|
||||
.filter_map(|tt| match tt {
|
||||
TtElement::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
let helpers = helpers
|
||||
.filter_map(|tt| match tt {
|
||||
TtElement::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
|
||||
Some((trait_name.as_name(), helpers))
|
||||
}
|
||||
|
||||
_ => None,
|
||||
Some((trait_name.as_name(), helpers))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -784,7 +784,7 @@ macro_rules! foo {
|
|||
|
||||
pub use core::clone::Clone;
|
||||
"#,
|
||||
|map| assert_eq!(map.modules[map.root].scope.impls().len(), 1),
|
||||
|map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 1),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -806,7 +806,7 @@ pub macro Copy {}
|
|||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
"#,
|
||||
|map| assert_eq!(map.modules[map.root].scope.impls().len(), 2),
|
||||
|map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 2),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -849,7 +849,7 @@ pub macro derive($item:item) {}
|
|||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
"#,
|
||||
|map| assert_eq!(map.modules[map.root].scope.impls().len(), 1),
|
||||
|map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 1),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1609,7 +1609,7 @@ macro_rules! derive { () => {} }
|
|||
#[derive(Clone)]
|
||||
struct S;
|
||||
"#,
|
||||
|map| assert_eq!(map.modules[map.root].scope.impls().len(), 1),
|
||||
|map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 1),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,9 @@ impl UnionSignature {
|
|||
bitflags! {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct EnumFlags: u8 {
|
||||
/// Indicates whether this enum has `#[repr]`.
|
||||
const HAS_REPR = 1 << 0;
|
||||
/// Indicates whether the enum has a `#[rustc_has_incoherent_inherent_impls]` attribute.
|
||||
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -205,6 +208,9 @@ impl EnumSignature {
|
|||
if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
|
||||
flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
|
||||
}
|
||||
if attrs.contains(AttrFlags::HAS_REPR) {
|
||||
flags |= EnumFlags::HAS_REPR;
|
||||
}
|
||||
|
||||
let InFile { file_id, value: source } = loc.source(db);
|
||||
let (store, generic_params, source_map) = lower_generic_params(
|
||||
|
|
@ -233,6 +239,11 @@ impl EnumSignature {
|
|||
_ => IntegerType::Pointer(true),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn repr(&self, db: &dyn DefDatabase, id: EnumId) -> Option<ReprOptions> {
|
||||
if self.flags.contains(EnumFlags::HAS_REPR) { AttrFlags::repr(db, id.into()) } else { None }
|
||||
}
|
||||
}
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ impl Default for TestDB {
|
|||
this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
|
||||
// This needs to be here otherwise `CrateGraphBuilder` panics.
|
||||
this.set_all_crates(Arc::new(Box::new([])));
|
||||
_ = base_db::LibraryRoots::builder(Default::default())
|
||||
.durability(Durability::MEDIUM)
|
||||
.new(&this);
|
||||
_ = base_db::LocalRoots::builder(Default::default())
|
||||
.durability(Durability::MEDIUM)
|
||||
.new(&this);
|
||||
CrateGraphBuilder::default().set_in_db(&mut this);
|
||||
this
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ use arrayvec::ArrayVec;
|
|||
use base_db::Crate;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use intern::{Interned, Symbol};
|
||||
use intern::Interned;
|
||||
use itertools::Itertools;
|
||||
use mbe::{DelimiterKind, Punct};
|
||||
use parser::T;
|
||||
use smallvec::SmallVec;
|
||||
|
|
@ -416,47 +417,42 @@ impl fmt::Display for AttrInput {
|
|||
|
||||
impl Attr {
|
||||
/// #[path = "string"]
|
||||
pub fn string_value(&self) -> Option<&Symbol> {
|
||||
pub fn string_value(&self) -> Option<&str> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
..
|
||||
}) => Some(text),
|
||||
AttrInput::Literal(
|
||||
lit @ tt::Literal { kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), .. },
|
||||
) => Some(lit.text()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path = "string"]
|
||||
pub fn string_value_with_span(&self) -> Option<(&Symbol, span::Span)> {
|
||||
pub fn string_value_with_span(&self) -> Option<(&str, span::Span)> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
span,
|
||||
suffix: _,
|
||||
}) => Some((text, *span)),
|
||||
AttrInput::Literal(
|
||||
lit @ tt::Literal { kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), span, .. },
|
||||
) => Some((lit.text(), *span)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_value_unescape(&self) -> Option<Cow<'_, str>> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(tt::Literal {
|
||||
symbol: text, kind: tt::LitKind::StrRaw(_), ..
|
||||
}) => Some(Cow::Borrowed(text.as_str())),
|
||||
AttrInput::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. }) => {
|
||||
unescape(text.as_str())
|
||||
AttrInput::Literal(lit @ tt::Literal { kind: tt::LitKind::StrRaw(_), .. }) => {
|
||||
Some(Cow::Borrowed(lit.text()))
|
||||
}
|
||||
AttrInput::Literal(lit @ tt::Literal { kind: tt::LitKind::Str, .. }) => {
|
||||
unescape(lit.text())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path(ident)]
|
||||
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
|
||||
pub fn single_ident_value(&self) -> Option<tt::Ident> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::TokenTree(tt) => match tt.token_trees().flat_tokens() {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
|
||||
AttrInput::TokenTree(tt) => match tt.token_trees().iter().collect_array() {
|
||||
Some([tt::TtElement::Leaf(tt::Leaf::Ident(ident))]) => Some(ident),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
|
|
@ -492,7 +488,7 @@ fn parse_path_comma_token_tree<'a>(
|
|||
args.token_trees()
|
||||
.split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
|
||||
.filter_map(move |tts| {
|
||||
let span = tts.flat_tokens().first()?.first_span();
|
||||
let span = tts.first_span()?;
|
||||
Some((ModPath::from_tt(db, tts)?, span, tts))
|
||||
})
|
||||
}
|
||||
|
|
@ -611,16 +607,12 @@ impl AttrId {
|
|||
else {
|
||||
return derive_attr_range;
|
||||
};
|
||||
let (Some(first_tt), Some(last_tt)) =
|
||||
(derive_tts.flat_tokens().first(), derive_tts.flat_tokens().last())
|
||||
let (Some(first_span), Some(last_span)) = (derive_tts.first_span(), derive_tts.last_span())
|
||||
else {
|
||||
return derive_attr_range;
|
||||
};
|
||||
let start = first_tt.first_span().range.start();
|
||||
let end = match last_tt {
|
||||
tt::TokenTree::Leaf(it) => it.span().range.end(),
|
||||
tt::TokenTree::Subtree(it) => it.delimiter.close.range.end(),
|
||||
};
|
||||
let start = first_span.range.start();
|
||||
let end = last_span.range.end();
|
||||
TextRange::new(start, end)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use intern::sym;
|
|||
use itertools::{Itertools, izip};
|
||||
use parser::SyntaxKind;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{Edition, Span, SyntaxContext};
|
||||
use span::{Edition, Span};
|
||||
use stdx::never;
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use tracing::debug;
|
||||
|
|
@ -28,7 +28,7 @@ use syntax::{
|
|||
};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( $($trait:ident => $expand:ident),* ) => {
|
||||
( $($trait:ident => $expand:ident),* $(,)? ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinDeriveExpander {
|
||||
$($trait),*
|
||||
|
|
@ -48,7 +48,6 @@ macro_rules! register_builtin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +74,7 @@ register_builtin! {
|
|||
PartialOrd => partial_ord_expand,
|
||||
Eq => eq_expand,
|
||||
PartialEq => partial_eq_expand,
|
||||
CoercePointee => coerce_pointee_expand
|
||||
CoercePointee => coerce_pointee_expand,
|
||||
}
|
||||
|
||||
pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> {
|
||||
|
|
@ -239,7 +238,7 @@ fn parse_adt(
|
|||
|
||||
fn parse_adt_from_syntax(
|
||||
adt: &ast::Adt,
|
||||
tm: &span::SpanMap<SyntaxContext>,
|
||||
tm: &span::SpanMap,
|
||||
call_site: Span,
|
||||
) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let (name, generic_param_list, where_clause, shape) = match &adt {
|
||||
|
|
@ -391,7 +390,7 @@ fn to_adt_syntax(
|
|||
db: &dyn ExpandDatabase,
|
||||
tt: &tt::TopSubtree,
|
||||
call_site: Span,
|
||||
) -> Result<(ast::Adt, span::SpanMap<SyntaxContext>), ExpandError> {
|
||||
) -> Result<(ast::Adt, span::SpanMap), ExpandError> {
|
||||
let (parsed, tm) = crate::db::token_tree_to_syntax_node(db, tt, crate::ExpandTo::Items);
|
||||
let macro_items = ast::MacroItems::cast(parsed.syntax_node())
|
||||
.ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! Builtin macro
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use base_db::AnchoredPath;
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
|
|
@ -13,7 +15,7 @@ use span::{Edition, FileId, Span};
|
|||
use stdx::format_to;
|
||||
use syntax::{
|
||||
format_smolstr,
|
||||
unescape::{unescape_byte, unescape_char, unescape_str},
|
||||
unescape::{unescape_byte, unescape_char},
|
||||
};
|
||||
use syntax_bridge::syntax_node_to_token_tree;
|
||||
|
||||
|
|
@ -177,12 +179,7 @@ fn line_expand(
|
|||
// not incremental
|
||||
ExpandResult::ok(tt::TopSubtree::invisible_from_leaves(
|
||||
span,
|
||||
[tt::Leaf::Literal(tt::Literal {
|
||||
symbol: sym::INTEGER_0,
|
||||
span,
|
||||
kind: tt::LitKind::Integer,
|
||||
suffix: Some(sym::u32),
|
||||
})],
|
||||
[tt::Leaf::Literal(tt::Literal::new("0", span, tt::LitKind::Integer, "u32"))],
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +207,7 @@ fn stringify_expand(
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let pretty = ::tt::pretty(tt.token_trees().flat_tokens());
|
||||
let pretty = ::tt::pretty(tt.token_trees());
|
||||
|
||||
let expanded = quote! {span =>
|
||||
#pretty
|
||||
|
|
@ -283,7 +280,7 @@ fn format_args_expand(
|
|||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let pound = mk_pound(span);
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Parenthesis);
|
||||
ExpandResult::ok(quote! {span =>
|
||||
builtin #pound format_args #tt
|
||||
})
|
||||
|
|
@ -297,14 +294,15 @@ fn format_args_nl_expand(
|
|||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let pound = mk_pound(span);
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str,
|
||||
..
|
||||
}))) = tt.0.get_mut(1)
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Parenthesis);
|
||||
let lit = tt.as_token_trees().iter_flat_tokens().nth(1);
|
||||
if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(
|
||||
mut lit @ tt::Literal { kind: tt::LitKind::Str, .. },
|
||||
))) = lit
|
||||
{
|
||||
*text = Symbol::intern(&format_smolstr!("{}\\n", text.as_str()));
|
||||
let (text, suffix) = lit.text_and_suffix();
|
||||
lit.text_and_suffix = Symbol::intern(&format_smolstr!("{text}\\n{suffix}"));
|
||||
tt.set_token(1, lit.into());
|
||||
}
|
||||
ExpandResult::ok(quote! {span =>
|
||||
builtin #pound format_args #tt
|
||||
|
|
@ -318,7 +316,7 @@ fn asm_expand(
|
|||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Parenthesis);
|
||||
let pound = mk_pound(span);
|
||||
let expanded = quote! {span =>
|
||||
builtin #pound asm #tt
|
||||
|
|
@ -333,7 +331,7 @@ fn global_asm_expand(
|
|||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Parenthesis);
|
||||
let pound = mk_pound(span);
|
||||
let expanded = quote! {span =>
|
||||
builtin #pound global_asm #tt
|
||||
|
|
@ -348,7 +346,7 @@ fn naked_asm_expand(
|
|||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Parenthesis);
|
||||
let pound = mk_pound(span);
|
||||
let expanded = quote! {span =>
|
||||
builtin #pound naked_asm #tt
|
||||
|
|
@ -478,11 +476,11 @@ fn unreachable_expand(
|
|||
|
||||
// Pass the original arguments
|
||||
let mut subtree = tt.clone();
|
||||
*subtree.top_subtree_delimiter_mut() = tt::Delimiter {
|
||||
subtree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Parenthesis);
|
||||
subtree.set_top_subtree_delimiter_span(tt::DelimSpan {
|
||||
open: call_site_span,
|
||||
close: call_site_span,
|
||||
kind: tt::DelimiterKind::Parenthesis,
|
||||
};
|
||||
});
|
||||
|
||||
// Expand to a macro call `$crate::panic::panic_{edition}`
|
||||
let call = quote!(call_site_span =>#dollar_crate::panic::#mac! #subtree);
|
||||
|
|
@ -518,16 +516,14 @@ fn compile_error_expand(
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let err = match &*tt.0 {
|
||||
[
|
||||
_,
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span: _,
|
||||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
suffix: _,
|
||||
})),
|
||||
] => ExpandError::other(span, Box::from(unescape_symbol(text).as_str())),
|
||||
let err = match tt.iter().collect_array() {
|
||||
Some(
|
||||
[
|
||||
tt::TtElement::Leaf(tt::Leaf::Literal(
|
||||
lit @ tt::Literal { kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), .. },
|
||||
)),
|
||||
],
|
||||
) => ExpandError::other(span, Box::from(unescape_str(lit.text()))),
|
||||
_ => ExpandError::other(span, "`compile_error!` argument must be a string"),
|
||||
};
|
||||
|
||||
|
|
@ -556,7 +552,7 @@ fn concat_expand(
|
|||
// to ensure the right parsing order, so skip the parentheses here. Ideally we'd
|
||||
// implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623
|
||||
if let TtElement::Subtree(subtree, subtree_iter) = &t
|
||||
&& let [tt::TokenTree::Leaf(tt)] = subtree_iter.remaining().flat_tokens()
|
||||
&& let Some([tt::TtElement::Leaf(tt)]) = subtree_iter.clone().collect_array()
|
||||
&& subtree.delimiter.kind == tt::DelimiterKind::Parenthesis
|
||||
{
|
||||
t = TtElement::Leaf(tt);
|
||||
|
|
@ -568,20 +564,20 @@ fn concat_expand(
|
|||
// as-is.
|
||||
match it.kind {
|
||||
tt::LitKind::Char => {
|
||||
if let Ok(c) = unescape_char(it.symbol.as_str()) {
|
||||
if let Ok(c) = unescape_char(it.text()) {
|
||||
text.push(c);
|
||||
}
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::Integer | tt::LitKind::Float => {
|
||||
format_to!(text, "{}", it.symbol.as_str())
|
||||
format_to!(text, "{}", it.text())
|
||||
}
|
||||
tt::LitKind::Str => {
|
||||
text.push_str(unescape_symbol(&it.symbol).as_str());
|
||||
text.push_str(&unescape_str(it.text()));
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::StrRaw(_) => {
|
||||
format_to!(text, "{}", it.symbol.as_str());
|
||||
format_to!(text, "{}", it.text());
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::Byte
|
||||
|
|
@ -619,7 +615,7 @@ fn concat_expand(
|
|||
TtElement::Leaf(tt::Leaf::Literal(it))
|
||||
if matches!(it.kind, tt::LitKind::Integer | tt::LitKind::Float) =>
|
||||
{
|
||||
format_to!(text, "-{}", it.symbol.as_str());
|
||||
format_to!(text, "-{}", it.text());
|
||||
record_span(punct.span.cover(it.span));
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -657,29 +653,25 @@ fn concat_bytes_expand(
|
|||
};
|
||||
for (i, t) in tt.iter().enumerate() {
|
||||
match t {
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind,
|
||||
suffix: _,
|
||||
})) => {
|
||||
record_span(*span);
|
||||
TtElement::Leaf(tt::Leaf::Literal(lit @ tt::Literal { span, kind, .. })) => {
|
||||
let text = lit.text();
|
||||
record_span(span);
|
||||
match kind {
|
||||
tt::LitKind::Byte => {
|
||||
if let Ok(b) = unescape_byte(text.as_str()) {
|
||||
if let Ok(b) = unescape_byte(text) {
|
||||
bytes.extend(
|
||||
b.escape_ascii().filter_map(|it| char::from_u32(it as u32)),
|
||||
);
|
||||
}
|
||||
}
|
||||
tt::LitKind::ByteStr => {
|
||||
bytes.push_str(text.as_str());
|
||||
bytes.push_str(text);
|
||||
}
|
||||
tt::LitKind::ByteStrRaw(_) => {
|
||||
bytes.extend(text.as_str().escape_debug());
|
||||
bytes.extend(text.escape_debug());
|
||||
}
|
||||
_ => {
|
||||
err.get_or_insert(ExpandError::other(*span, "unexpected token"));
|
||||
err.get_or_insert(ExpandError::other(span, "unexpected token"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -705,12 +697,7 @@ fn concat_bytes_expand(
|
|||
ExpandResult {
|
||||
value: tt::TopSubtree::invisible_from_leaves(
|
||||
span,
|
||||
[tt::Leaf::Literal(tt::Literal {
|
||||
symbol: Symbol::intern(&bytes),
|
||||
span,
|
||||
kind: tt::LitKind::ByteStr,
|
||||
suffix: None,
|
||||
})],
|
||||
[tt::Leaf::Literal(tt::Literal::new_no_suffix(&bytes, span, tt::LitKind::ByteStr))],
|
||||
),
|
||||
err,
|
||||
}
|
||||
|
|
@ -724,25 +711,19 @@ fn concat_bytes_expand_subtree(
|
|||
) -> Result<(), ExpandError> {
|
||||
for (ti, tt) in tree_iter.enumerate() {
|
||||
match tt {
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::Byte,
|
||||
suffix: _,
|
||||
})) => {
|
||||
if let Ok(b) = unescape_byte(text.as_str()) {
|
||||
TtElement::Leaf(tt::Leaf::Literal(
|
||||
lit @ tt::Literal { span, kind: tt::LitKind::Byte, .. },
|
||||
)) => {
|
||||
if let Ok(b) = unescape_byte(lit.text()) {
|
||||
bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32)));
|
||||
}
|
||||
record_span(*span);
|
||||
record_span(span);
|
||||
}
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::Integer,
|
||||
suffix: _,
|
||||
})) => {
|
||||
record_span(*span);
|
||||
if let Ok(b) = text.as_str().parse::<u8>() {
|
||||
TtElement::Leaf(tt::Leaf::Literal(
|
||||
lit @ tt::Literal { span, kind: tt::LitKind::Integer, .. },
|
||||
)) => {
|
||||
record_span(span);
|
||||
if let Ok(b) = lit.text().parse::<u8>() {
|
||||
bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32)));
|
||||
}
|
||||
}
|
||||
|
|
@ -791,18 +772,16 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> {
|
|||
}
|
||||
|
||||
match tt {
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
TtElement::Leaf(tt::Leaf::Literal(lit @ tt::Literal {
|
||||
span,
|
||||
kind: tt::LitKind::Str,
|
||||
suffix: _,
|
||||
})) => Ok((unescape_symbol(text), *span)),
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
..
|
||||
})) => Ok((Symbol::intern(&unescape_str(lit.text())), span)),
|
||||
TtElement::Leaf(tt::Leaf::Literal(lit @ tt::Literal {
|
||||
span,
|
||||
kind: tt::LitKind::StrRaw(_),
|
||||
suffix: _,
|
||||
})) => Ok((text.clone(), *span)),
|
||||
..
|
||||
})) => Ok((Symbol::intern(lit.text()), span)),
|
||||
TtElement::Leaf(l) => Err(*l.span()),
|
||||
TtElement::Subtree(tt, _) => Err(tt.delimiter.open.cover(tt.delimiter.close)),
|
||||
}
|
||||
|
|
@ -854,10 +833,10 @@ fn include_bytes_expand(
|
|||
let res = tt::TopSubtree::invisible_from_leaves(
|
||||
span,
|
||||
[tt::Leaf::Literal(tt::Literal {
|
||||
symbol: Symbol::empty(),
|
||||
text_and_suffix: Symbol::empty(),
|
||||
span,
|
||||
kind: tt::LitKind::ByteStrRaw(1),
|
||||
suffix: None,
|
||||
suffix_len: 0,
|
||||
})],
|
||||
);
|
||||
ExpandResult::ok(res)
|
||||
|
|
@ -978,17 +957,16 @@ fn quote_expand(
|
|||
)
|
||||
}
|
||||
|
||||
fn unescape_symbol(s: &Symbol) -> Symbol {
|
||||
if s.as_str().contains('\\') {
|
||||
let s = s.as_str();
|
||||
fn unescape_str(s: &str) -> Cow<'_, str> {
|
||||
if s.contains('\\') {
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
unescape_str(s, |_, c| {
|
||||
syntax::unescape::unescape_str(s, |_, c| {
|
||||
if let Ok(c) = c {
|
||||
buf.push(c)
|
||||
}
|
||||
});
|
||||
Symbol::intern(&buf)
|
||||
Cow::Owned(buf)
|
||||
} else {
|
||||
s.clone()
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use tt::IdentIsRaw;
|
|||
|
||||
use crate::{name::Name, tt::TopSubtreeBuilder};
|
||||
|
||||
pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> {
|
||||
pub(crate) fn dollar_crate(span: Span) -> tt::Ident {
|
||||
tt::Ident { sym: sym::dollar_crate, span, is_raw: tt::IdentIsRaw::No }
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ impl ToTokenTree for crate::tt::SubtreeView<'_> {
|
|||
|
||||
impl ToTokenTree for crate::tt::TopSubtree {
|
||||
fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) {
|
||||
builder.extend_tt_dangerous(self.0);
|
||||
builder.extend_with_tt(self.as_token_trees());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -172,10 +172,9 @@ impl ToTokenTree for crate::tt::TtElement<'_> {
|
|||
match self {
|
||||
crate::tt::TtElement::Leaf(leaf) => builder.push(leaf.clone()),
|
||||
crate::tt::TtElement::Subtree(subtree, subtree_iter) => {
|
||||
builder.extend_tt_dangerous(
|
||||
std::iter::once(crate::tt::TokenTree::Subtree(subtree.clone()))
|
||||
.chain(subtree_iter.remaining().flat_tokens().iter().cloned()),
|
||||
);
|
||||
builder.open(subtree.delimiter.kind, subtree.delimiter.open);
|
||||
builder.extend_with_tt(subtree_iter.remaining());
|
||||
builder.close(subtree.delimiter.close);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -200,16 +199,16 @@ impl<T: ToTokenTree + Clone> ToTokenTree for &T {
|
|||
}
|
||||
|
||||
impl_to_to_tokentrees! {
|
||||
span: u32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
|
||||
span: usize => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
|
||||
span: i32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
|
||||
span: u32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
|
||||
span: usize => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
|
||||
span: i32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
|
||||
span: bool => self { crate::tt::Ident{sym: if self { sym::true_ } else { sym::false_ }, span, is_raw: tt::IdentIsRaw::No } };
|
||||
_span: crate::tt::Leaf => self { self };
|
||||
_span: crate::tt::Literal => self { self };
|
||||
_span: crate::tt::Ident => self { self };
|
||||
_span: crate::tt::Punct => self { self };
|
||||
span: &str => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }};
|
||||
span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }};
|
||||
span: &str => self { crate::tt::Literal{text_and_suffix: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix_len: 0 }};
|
||||
span: String => self { crate::tt::Literal{text_and_suffix: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix_len: 0 }};
|
||||
span: Name => self {
|
||||
let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str());
|
||||
crate::tt::Ident{sym: Symbol::intern(s), span, is_raw }
|
||||
|
|
|
|||
|
|
@ -237,7 +237,8 @@ pub fn expand_speculative(
|
|||
span,
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
*tree.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span);
|
||||
tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
|
||||
tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
|
||||
tree
|
||||
},
|
||||
)
|
||||
|
|
@ -255,7 +256,7 @@ pub fn expand_speculative(
|
|||
span,
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
attr_arg.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
|
||||
attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
|
||||
Some(attr_arg)
|
||||
}
|
||||
_ => None,
|
||||
|
|
@ -270,7 +271,8 @@ pub fn expand_speculative(
|
|||
let mut speculative_expansion = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(ast, expander, _) => {
|
||||
let span = db.proc_macro_span(ast);
|
||||
*tt.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span);
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
|
||||
tt.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
|
||||
expander.expand(
|
||||
db,
|
||||
loc.def.krate,
|
||||
|
|
@ -430,7 +432,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
|||
(
|
||||
Arc::new(tt::TopSubtree::from_token_trees(
|
||||
tt::Delimiter { open: span, close: span, kind },
|
||||
tt::TokenTreesView::new(&[]),
|
||||
tt::TokenTreesView::empty(),
|
||||
)),
|
||||
SyntaxFixupUndoInfo::default(),
|
||||
span,
|
||||
|
|
@ -478,7 +480,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
|||
);
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
|
||||
}
|
||||
return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
|
||||
}
|
||||
|
|
@ -512,7 +514,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
|||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
|
||||
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
|
||||
}
|
||||
|
||||
(Arc::new(tt), undo_info, span)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ pub fn expand_eager_macro_input(
|
|||
DocCommentDesugarMode::Mbe,
|
||||
);
|
||||
|
||||
subtree.top_subtree_delimiter_mut().kind = crate::tt::DelimiterKind::Invisible;
|
||||
subtree.set_top_subtree_delimiter_kind(crate::tt::DelimiterKind::Invisible);
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use syntax::{
|
|||
};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
use tt::Spacing;
|
||||
use tt::{Spacing, TransformTtAction, transform_tt};
|
||||
|
||||
use crate::{
|
||||
span_map::SpanMapRef,
|
||||
|
|
@ -343,93 +343,29 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool {
|
|||
pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInfo) {
|
||||
let Some(undo_info) = undo_info.original.as_deref() else { return };
|
||||
let undo_info = &**undo_info;
|
||||
let delimiter = tt.top_subtree_delimiter_mut();
|
||||
let top_subtree = tt.top_subtree();
|
||||
let open_span = top_subtree.delimiter.open;
|
||||
let close_span = top_subtree.delimiter.close;
|
||||
#[allow(deprecated)]
|
||||
if never!(
|
||||
delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
|| delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
close_span.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
|| open_span.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
) {
|
||||
let span = |file_id| Span {
|
||||
range: TextRange::empty(TextSize::new(0)),
|
||||
anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
ctx: SyntaxContext::root(span::Edition::Edition2015),
|
||||
};
|
||||
delimiter.open = span(delimiter.open.anchor.file_id);
|
||||
delimiter.close = span(delimiter.close.anchor.file_id);
|
||||
tt.set_top_subtree_delimiter_span(tt::DelimSpan {
|
||||
open: span(open_span.anchor.file_id),
|
||||
close: span(close_span.anchor.file_id),
|
||||
});
|
||||
}
|
||||
reverse_fixups_(tt, undo_info);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TransformTtAction<'a> {
|
||||
Keep,
|
||||
ReplaceWith(tt::TokenTreesView<'a>),
|
||||
}
|
||||
|
||||
impl TransformTtAction<'_> {
|
||||
fn remove() -> Self {
|
||||
Self::ReplaceWith(tt::TokenTreesView::new(&[]))
|
||||
}
|
||||
}
|
||||
|
||||
/// This function takes a token tree, and calls `callback` with each token tree in it.
|
||||
/// Then it does what the callback says: keeps the tt or replaces it with a (possibly empty)
|
||||
/// tts view.
|
||||
fn transform_tt<'a, 'b>(
|
||||
tt: &'a mut Vec<tt::TokenTree>,
|
||||
mut callback: impl FnMut(&mut tt::TokenTree) -> TransformTtAction<'b>,
|
||||
) {
|
||||
// We need to keep a stack of the currently open subtrees, because we need to update
|
||||
// them if we change the number of items in them.
|
||||
let mut subtrees_stack = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < tt.len() {
|
||||
'pop_finished_subtrees: while let Some(&subtree_idx) = subtrees_stack.last() {
|
||||
let tt::TokenTree::Subtree(subtree) = &tt[subtree_idx] else {
|
||||
unreachable!("non-subtree on subtrees stack");
|
||||
};
|
||||
if i >= subtree_idx + 1 + subtree.usize_len() {
|
||||
subtrees_stack.pop();
|
||||
} else {
|
||||
break 'pop_finished_subtrees;
|
||||
}
|
||||
}
|
||||
|
||||
let action = callback(&mut tt[i]);
|
||||
match action {
|
||||
TransformTtAction::Keep => {
|
||||
// This cannot be shared with the replaced case, because then we may push the same subtree
|
||||
// twice, and will update it twice which will lead to errors.
|
||||
if let tt::TokenTree::Subtree(_) = &tt[i] {
|
||||
subtrees_stack.push(i);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
TransformTtAction::ReplaceWith(replacement) => {
|
||||
let old_len = 1 + match &tt[i] {
|
||||
tt::TokenTree::Leaf(_) => 0,
|
||||
tt::TokenTree::Subtree(subtree) => subtree.usize_len(),
|
||||
};
|
||||
let len_diff = replacement.len() as i64 - old_len as i64;
|
||||
tt.splice(i..i + old_len, replacement.flat_tokens().iter().cloned());
|
||||
// Skip the newly inserted replacement, we don't want to visit it.
|
||||
i += replacement.len();
|
||||
|
||||
for &subtree_idx in &subtrees_stack {
|
||||
let tt::TokenTree::Subtree(subtree) = &mut tt[subtree_idx] else {
|
||||
unreachable!("non-subtree on subtrees stack");
|
||||
};
|
||||
subtree.len = (i64::from(subtree.len) + len_diff).try_into().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) {
|
||||
let mut tts = std::mem::take(&mut tt.0).into_vec();
|
||||
transform_tt(&mut tts, |tt| match tt {
|
||||
transform_tt(tt, |tt| match tt {
|
||||
tt::TokenTree::Leaf(leaf) => {
|
||||
let span = leaf.span();
|
||||
let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID;
|
||||
|
|
@ -459,7 +395,6 @@ fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) {
|
|||
TransformTtAction::Keep
|
||||
}
|
||||
});
|
||||
tt.0 = tts.into_boxed_slice();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -480,7 +415,7 @@ mod tests {
|
|||
// `TokenTree`s, see the last assertion in `check()`.
|
||||
fn check_leaf_eq(a: &tt::Leaf, b: &tt::Leaf) -> bool {
|
||||
match (a, b) {
|
||||
(tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.symbol == b.symbol,
|
||||
(tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.text_and_suffix == b.text_and_suffix,
|
||||
(tt::Leaf::Punct(a), tt::Leaf::Punct(b)) => a.char == b.char,
|
||||
(tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.sym == b.sym,
|
||||
_ => false,
|
||||
|
|
@ -488,9 +423,9 @@ mod tests {
|
|||
}
|
||||
|
||||
fn check_subtree_eq(a: &tt::TopSubtree, b: &tt::TopSubtree) -> bool {
|
||||
let a = a.view().as_token_trees().flat_tokens();
|
||||
let b = b.view().as_token_trees().flat_tokens();
|
||||
a.len() == b.len() && std::iter::zip(a, b).all(|(a, b)| check_tt_eq(a, b))
|
||||
let a = a.view().as_token_trees().iter_flat_tokens();
|
||||
let b = b.view().as_token_trees().iter_flat_tokens();
|
||||
a.len() == b.len() && std::iter::zip(a, b).all(|(a, b)| check_tt_eq(&a, &b))
|
||||
}
|
||||
|
||||
fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool {
|
||||
|
|
@ -545,7 +480,7 @@ mod tests {
|
|||
|
||||
// the fixed-up tree should not contain braces as punct
|
||||
// FIXME: should probably instead check that it's a valid punctuation character
|
||||
for x in tt.token_trees().flat_tokens() {
|
||||
for x in tt.token_trees().iter_flat_tokens() {
|
||||
match x {
|
||||
::tt::TokenTree::Leaf(::tt::Leaf::Punct(punct)) => {
|
||||
assert!(!matches!(punct.char, '{' | '}' | '(' | ')' | '[' | ']'))
|
||||
|
|
|
|||
|
|
@ -66,25 +66,7 @@ pub use crate::{
|
|||
pub use base_db::EditionedFileId;
|
||||
pub use mbe::{DeclarativeMacro, MacroCallStyle, MacroCallStyles, ValueResult};
|
||||
|
||||
pub mod tt {
|
||||
pub use span::Span;
|
||||
pub use tt::{DelimiterKind, IdentIsRaw, LitKind, Spacing, token_to_literal};
|
||||
|
||||
pub type Delimiter = ::tt::Delimiter<Span>;
|
||||
pub type DelimSpan = ::tt::DelimSpan<Span>;
|
||||
pub type Subtree = ::tt::Subtree<Span>;
|
||||
pub type Leaf = ::tt::Leaf<Span>;
|
||||
pub type Literal = ::tt::Literal<Span>;
|
||||
pub type Punct = ::tt::Punct<Span>;
|
||||
pub type Ident = ::tt::Ident<Span>;
|
||||
pub type TokenTree = ::tt::TokenTree<Span>;
|
||||
pub type TopSubtree = ::tt::TopSubtree<Span>;
|
||||
pub type TopSubtreeBuilder = ::tt::TopSubtreeBuilder<Span>;
|
||||
pub type TokenTreesView<'a> = ::tt::TokenTreesView<'a, Span>;
|
||||
pub type SubtreeView<'a> = ::tt::SubtreeView<'a, Span>;
|
||||
pub type TtElement<'a> = ::tt::iter::TtElement<'a, Span>;
|
||||
pub type TtIter<'a> = ::tt::iter::TtIter<'a, Span>;
|
||||
}
|
||||
pub use tt;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_intern_lookup {
|
||||
|
|
|
|||
|
|
@ -355,16 +355,16 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio
|
|||
tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs,
|
||||
_ => return None,
|
||||
},
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if *text == sym::dollar_crate => {
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if text == sym::dollar_crate => {
|
||||
resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate)
|
||||
}
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::self_ => PathKind::SELF,
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::super_ => {
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::self_ => PathKind::SELF,
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::super_ => {
|
||||
let mut deg = 1;
|
||||
while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw: _ })) =
|
||||
leaves.next()
|
||||
{
|
||||
if *text != sym::super_ {
|
||||
if text != sym::super_ {
|
||||
segments.push(Name::new_symbol(text.clone(), span.ctx));
|
||||
break;
|
||||
}
|
||||
|
|
@ -372,7 +372,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio
|
|||
}
|
||||
PathKind::Super(deg)
|
||||
}
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::crate_ => PathKind::Crate,
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::crate_ => PathKind::Crate,
|
||||
tt::Leaf::Ident(ident) => {
|
||||
segments.push(Name::new_symbol(ident.sym.clone(), ident.span.ctx));
|
||||
PathKind::Plain
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ impl AsName for ast::NameOrNameRef {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Span> AsName for tt::Ident<Span> {
|
||||
impl AsName for tt::Ident {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::new_root(self.sym.as_str())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use core::fmt;
|
|||
use std::any::Any;
|
||||
use std::{panic::RefUnwindSafe, sync};
|
||||
|
||||
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError};
|
||||
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError, SourceDatabase};
|
||||
use intern::Symbol;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::Span;
|
||||
|
|
@ -25,6 +25,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any {
|
|||
/// [`ProcMacroKind::Attr`]), environment variables, and span information.
|
||||
fn expand(
|
||||
&self,
|
||||
db: &dyn SourceDatabase,
|
||||
subtree: &tt::TopSubtree,
|
||||
attrs: Option<&tt::TopSubtree>,
|
||||
env: &Env,
|
||||
|
|
@ -309,6 +310,7 @@ impl CustomProcMacroExpander {
|
|||
let current_dir = calling_crate.data(db).proc_macro_cwd.to_string();
|
||||
|
||||
match proc_macro.expander.expand(
|
||||
db,
|
||||
tt,
|
||||
attr_arg,
|
||||
env,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Span maps for real files and macro expansions.
|
||||
|
||||
use span::{Span, SyntaxContext};
|
||||
use span::Span;
|
||||
use syntax::{AstNode, TextRange, ast};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ pub use span::RealSpanMap;
|
|||
|
||||
use crate::{HirFileId, MacroCallId, db::ExpandDatabase};
|
||||
|
||||
pub type ExpansionSpanMap = span::SpanMap<SyntaxContext>;
|
||||
pub type ExpansionSpanMap = span::SpanMap;
|
||||
|
||||
/// Spanmap for a macro file or a real file
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
@ -27,13 +27,13 @@ pub enum SpanMapRef<'a> {
|
|||
RealSpanMap(&'a RealSpanMap),
|
||||
}
|
||||
|
||||
impl syntax_bridge::SpanMapper<Span> for SpanMap {
|
||||
impl syntax_bridge::SpanMapper for SpanMap {
|
||||
fn span_for(&self, range: TextRange) -> Span {
|
||||
self.span_for_range(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl syntax_bridge::SpanMapper<Span> for SpanMapRef<'_> {
|
||||
impl syntax_bridge::SpanMapper for SpanMapRef<'_> {
|
||||
fn span_for(&self, range: TextRange) -> Span {
|
||||
self.span_for_range(range)
|
||||
}
|
||||
|
|
|
|||
599
src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs
Normal file
599
src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
//! Implementation of builtin derive impls.
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use hir_def::{
|
||||
AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, HasModule, LocalFieldId, TraitId,
|
||||
TypeOrConstParamId, TypeParamId,
|
||||
attrs::AttrFlags,
|
||||
builtin_derive::BuiltinDeriveImplTrait,
|
||||
hir::generics::{GenericParams, TypeOrConstParamData},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_type_ir::{
|
||||
AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
|
||||
inherent::{GenericArgs as _, IntoKind},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
GenericPredicates,
|
||||
db::HirDatabase,
|
||||
next_solver::{
|
||||
Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder,
|
||||
StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
|
||||
},
|
||||
};
|
||||
|
||||
fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
|
||||
// HACK: Fake the param.
|
||||
// We cannot use a dummy param here, because it can leak into the IDE layer and that'll cause panics
|
||||
// when e.g. trying to display it. So we use an existing param.
|
||||
TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent: trait_id.into(),
|
||||
local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics {
|
||||
let db = interner.db;
|
||||
let loc = id.loc(db);
|
||||
match loc.trait_ {
|
||||
BuiltinDeriveImplTrait::Copy
|
||||
| BuiltinDeriveImplTrait::Clone
|
||||
| BuiltinDeriveImplTrait::Default
|
||||
| BuiltinDeriveImplTrait::Debug
|
||||
| BuiltinDeriveImplTrait::Hash
|
||||
| BuiltinDeriveImplTrait::Ord
|
||||
| BuiltinDeriveImplTrait::PartialOrd
|
||||
| BuiltinDeriveImplTrait::Eq
|
||||
| BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()),
|
||||
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
|
||||
let mut generics = interner.generics_of(loc.adt.into());
|
||||
let trait_id = loc
|
||||
.trait_
|
||||
.get_id(interner.lang_items())
|
||||
.expect("we don't pass the impl to the solver if we can't resolve the trait");
|
||||
generics.push_param(coerce_pointee_new_type_param(trait_id).into());
|
||||
generics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
|
||||
let loc = id.loc(db);
|
||||
let adt_params = GenericParams::new(db, loc.adt.into());
|
||||
let extra_params_count = match loc.trait_ {
|
||||
BuiltinDeriveImplTrait::Copy
|
||||
| BuiltinDeriveImplTrait::Clone
|
||||
| BuiltinDeriveImplTrait::Default
|
||||
| BuiltinDeriveImplTrait::Debug
|
||||
| BuiltinDeriveImplTrait::Hash
|
||||
| BuiltinDeriveImplTrait::Ord
|
||||
| BuiltinDeriveImplTrait::PartialOrd
|
||||
| BuiltinDeriveImplTrait::Eq
|
||||
| BuiltinDeriveImplTrait::PartialEq => 0,
|
||||
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
|
||||
};
|
||||
adt_params.len() + extra_params_count
|
||||
}
|
||||
|
||||
pub fn impl_trait<'db>(
|
||||
interner: DbInterner<'db>,
|
||||
id: BuiltinDeriveImplId,
|
||||
) -> EarlyBinder<'db, TraitRef<'db>> {
|
||||
let db = interner.db;
|
||||
let loc = id.loc(db);
|
||||
let trait_id = loc
|
||||
.trait_
|
||||
.get_id(interner.lang_items())
|
||||
.expect("we don't pass the impl to the solver if we can't resolve the trait");
|
||||
match loc.trait_ {
|
||||
BuiltinDeriveImplTrait::Copy
|
||||
| BuiltinDeriveImplTrait::Clone
|
||||
| BuiltinDeriveImplTrait::Default
|
||||
| BuiltinDeriveImplTrait::Debug
|
||||
| BuiltinDeriveImplTrait::Hash
|
||||
| BuiltinDeriveImplTrait::Ord
|
||||
| BuiltinDeriveImplTrait::Eq => {
|
||||
let self_ty = Ty::new_adt(
|
||||
interner,
|
||||
loc.adt,
|
||||
GenericArgs::identity_for_item(interner, loc.adt.into()),
|
||||
);
|
||||
EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty]))
|
||||
}
|
||||
BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
|
||||
let self_ty = Ty::new_adt(
|
||||
interner,
|
||||
loc.adt,
|
||||
GenericArgs::identity_for_item(interner, loc.adt.into()),
|
||||
);
|
||||
EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, self_ty]))
|
||||
}
|
||||
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
|
||||
let generic_params = GenericParams::new(db, loc.adt.into());
|
||||
let interner = DbInterner::new_no_crate(db);
|
||||
let args = GenericArgs::identity_for_item(interner, loc.adt.into());
|
||||
let self_ty = Ty::new_adt(interner, loc.adt, args);
|
||||
let Some((pointee_param_idx, _, new_param_ty)) =
|
||||
coerce_pointee_params(interner, loc, &generic_params, trait_id)
|
||||
else {
|
||||
// Malformed derive.
|
||||
return EarlyBinder::bind(TraitRef::new(
|
||||
interner,
|
||||
trait_id.into(),
|
||||
[self_ty, self_ty],
|
||||
));
|
||||
};
|
||||
let changed_args = replace_pointee(interner, pointee_param_idx, new_param_ty, args);
|
||||
let changed_self_ty = Ty::new_adt(interner, loc.adt, changed_args);
|
||||
EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, changed_self_ty]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(returns(ref), unsafe(non_update_types))]
|
||||
pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
|
||||
let loc = impl_.loc(db);
|
||||
let generic_params = GenericParams::new(db, loc.adt.into());
|
||||
let interner = DbInterner::new_with(db, loc.module(db).krate(db));
|
||||
let adt_predicates = GenericPredicates::query(db, loc.adt.into());
|
||||
let trait_id = loc
|
||||
.trait_
|
||||
.get_id(interner.lang_items())
|
||||
.expect("we don't pass the impl to the solver if we can't resolve the trait");
|
||||
match loc.trait_ {
|
||||
BuiltinDeriveImplTrait::Copy
|
||||
| BuiltinDeriveImplTrait::Clone
|
||||
| BuiltinDeriveImplTrait::Debug
|
||||
| BuiltinDeriveImplTrait::Hash
|
||||
| BuiltinDeriveImplTrait::Ord
|
||||
| BuiltinDeriveImplTrait::PartialOrd
|
||||
| BuiltinDeriveImplTrait::Eq
|
||||
| BuiltinDeriveImplTrait::PartialEq => {
|
||||
simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
|
||||
}
|
||||
BuiltinDeriveImplTrait::Default => {
|
||||
if matches!(loc.adt, AdtId::EnumId(_)) {
|
||||
// Enums don't have extra bounds.
|
||||
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
|
||||
Clauses::new_from_slice(adt_predicates.explicit_predicates().skip_binder())
|
||||
.store(),
|
||||
))
|
||||
} else {
|
||||
simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
|
||||
}
|
||||
}
|
||||
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
|
||||
let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
|
||||
coerce_pointee_params(interner, loc, &generic_params, trait_id)
|
||||
else {
|
||||
// Malformed derive.
|
||||
return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
|
||||
Clauses::default().store(),
|
||||
));
|
||||
};
|
||||
let duplicated_bounds =
|
||||
adt_predicates.explicit_predicates().iter_identity_copied().filter_map(|pred| {
|
||||
let mentions_pointee =
|
||||
pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
|
||||
if !mentions_pointee {
|
||||
return None;
|
||||
}
|
||||
let transformed =
|
||||
replace_pointee(interner, pointee_param_idx, new_param_ty, pred);
|
||||
Some(transformed)
|
||||
});
|
||||
let unsize_trait = interner.lang_items().Unsize;
|
||||
let unsize_bound = unsize_trait.map(|unsize_trait| {
|
||||
let pointee_param_ty = Ty::new_param(interner, pointee_param_id, pointee_param_idx);
|
||||
TraitRef::new(interner, unsize_trait.into(), [pointee_param_ty, new_param_ty])
|
||||
.upcast(interner)
|
||||
});
|
||||
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
|
||||
Clauses::new_from_iter(
|
||||
interner,
|
||||
adt_predicates
|
||||
.explicit_predicates()
|
||||
.iter_identity_copied()
|
||||
.chain(duplicated_bounds)
|
||||
.chain(unsize_bound),
|
||||
)
|
||||
.store(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Not cached in a query, currently used in `hir` only. If you need this in `hir-ty` consider introducing a query.
|
||||
pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
|
||||
let predicates = predicates(interner.db, id);
|
||||
crate::lower::param_env_from_predicates(interner, predicates)
|
||||
}
|
||||
|
||||
struct MentionsPointee {
|
||||
pointee_param_idx: u32,
|
||||
}
|
||||
|
||||
impl<'db> TypeVisitor<DbInterner<'db>> for MentionsPointee {
|
||||
type Result = ControlFlow<()>;
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
|
||||
if let TyKind::Param(param) = t.kind()
|
||||
&& param.index == self.pointee_param_idx
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_pointee<'db, T: TypeFoldable<DbInterner<'db>>>(
|
||||
interner: DbInterner<'db>,
|
||||
pointee_param_idx: u32,
|
||||
new_param_ty: Ty<'db>,
|
||||
t: T,
|
||||
) -> T {
|
||||
fold_tys(interner, t, |ty| match ty.kind() {
|
||||
TyKind::Param(param) if param.index == pointee_param_idx => new_param_ty,
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
|
||||
fn simple_trait_predicates<'db>(
|
||||
interner: DbInterner<'db>,
|
||||
loc: &BuiltinDeriveImplLoc,
|
||||
generic_params: &GenericParams,
|
||||
adt_predicates: &GenericPredicates,
|
||||
trait_id: TraitId,
|
||||
) -> GenericPredicates {
|
||||
let extra_predicates = generic_params
|
||||
.iter_type_or_consts()
|
||||
.filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
|
||||
.map(|(param_idx, _)| {
|
||||
let param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent: loc.adt.into(),
|
||||
local_id: param_idx,
|
||||
});
|
||||
let param_idx =
|
||||
param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
|
||||
let param_ty = Ty::new_param(interner, param_id, param_idx);
|
||||
let trait_ref = TraitRef::new(interner, trait_id.into(), [param_ty]);
|
||||
trait_ref.upcast(interner)
|
||||
});
|
||||
let mut assoc_type_bounds = Vec::new();
|
||||
match loc.adt {
|
||||
AdtId::StructId(id) => extend_assoc_type_bounds(
|
||||
interner,
|
||||
&mut assoc_type_bounds,
|
||||
interner.db.field_types(id.into()),
|
||||
trait_id,
|
||||
),
|
||||
AdtId::UnionId(id) => extend_assoc_type_bounds(
|
||||
interner,
|
||||
&mut assoc_type_bounds,
|
||||
interner.db.field_types(id.into()),
|
||||
trait_id,
|
||||
),
|
||||
AdtId::EnumId(id) => {
|
||||
for &(variant_id, _, _) in &id.enum_variants(interner.db).variants {
|
||||
extend_assoc_type_bounds(
|
||||
interner,
|
||||
&mut assoc_type_bounds,
|
||||
interner.db.field_types(variant_id.into()),
|
||||
trait_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
|
||||
Clauses::new_from_iter(
|
||||
interner,
|
||||
adt_predicates
|
||||
.explicit_predicates()
|
||||
.iter_identity_copied()
|
||||
.chain(extra_predicates)
|
||||
.chain(assoc_type_bounds),
|
||||
)
|
||||
.store(),
|
||||
))
|
||||
}
|
||||
|
||||
fn extend_assoc_type_bounds<'db>(
|
||||
interner: DbInterner<'db>,
|
||||
assoc_type_bounds: &mut Vec<Clause<'db>>,
|
||||
fields: &ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>,
|
||||
trait_: TraitId,
|
||||
) {
|
||||
struct ProjectionFinder<'a, 'db> {
|
||||
interner: DbInterner<'db>,
|
||||
assoc_type_bounds: &'a mut Vec<Clause<'db>>,
|
||||
trait_: TraitId,
|
||||
}
|
||||
|
||||
impl<'db> TypeVisitor<DbInterner<'db>> for ProjectionFinder<'_, 'db> {
|
||||
type Result = ();
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
|
||||
if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() {
|
||||
self.assoc_type_bounds.push(
|
||||
TraitRef::new(self.interner, self.trait_.into(), [t]).upcast(self.interner),
|
||||
);
|
||||
}
|
||||
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_ };
|
||||
for (_, field) in fields.iter() {
|
||||
field.get().instantiate_identity().visit_with(&mut visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_pointee_params<'db>(
|
||||
interner: DbInterner<'db>,
|
||||
loc: &BuiltinDeriveImplLoc,
|
||||
generic_params: &GenericParams,
|
||||
trait_id: TraitId,
|
||||
) -> Option<(u32, TypeParamId, Ty<'db>)> {
|
||||
let pointee_param = {
|
||||
if let Ok((pointee_param, _)) = generic_params
|
||||
.iter_type_or_consts()
|
||||
.filter(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
|
||||
.exactly_one()
|
||||
{
|
||||
pointee_param
|
||||
} else {
|
||||
let (_, generic_param_attrs) =
|
||||
AttrFlags::query_generic_params(interner.db, loc.adt.into());
|
||||
generic_param_attrs
|
||||
.iter()
|
||||
.find(|param| param.1.contains(AttrFlags::IS_POINTEE))
|
||||
.map(|(param, _)| param)
|
||||
.or_else(|| {
|
||||
generic_params
|
||||
.iter_type_or_consts()
|
||||
.find(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
|
||||
.map(|(idx, _)| idx)
|
||||
})?
|
||||
}
|
||||
};
|
||||
let pointee_param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent: loc.adt.into(),
|
||||
local_id: pointee_param,
|
||||
});
|
||||
let pointee_param_idx =
|
||||
pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
|
||||
let new_param_idx = generic_params.len() as u32;
|
||||
let new_param_id = coerce_pointee_new_type_param(trait_id);
|
||||
let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
|
||||
Some((pointee_param_idx, pointee_param_id, new_param_ty))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{Expect, expect};
|
||||
use hir_def::nameres::crate_def_map;
|
||||
use itertools::Itertools;
|
||||
use stdx::format_to;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{builtin_derive::impl_trait, next_solver::DbInterner, test_db::TestDB};
|
||||
|
||||
fn check_trait_refs(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let def_map = crate_def_map(&db, db.test_crate());
|
||||
|
||||
let interner = DbInterner::new_with(&db, db.test_crate());
|
||||
crate::attach_db(&db, || {
|
||||
let mut trait_refs = Vec::new();
|
||||
for (_, module) in def_map.modules() {
|
||||
for derive in module.scope.builtin_derive_impls() {
|
||||
let trait_ref = impl_trait(interner, derive).skip_binder();
|
||||
trait_refs.push(format!("{trait_ref:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
expectation.assert_eq(&trait_refs.join("\n"));
|
||||
});
|
||||
}
|
||||
|
||||
fn check_predicates(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let def_map = crate_def_map(&db, db.test_crate());
|
||||
|
||||
crate::attach_db(&db, || {
|
||||
let mut predicates = String::new();
|
||||
for (_, module) in def_map.modules() {
|
||||
for derive in module.scope.builtin_derive_impls() {
|
||||
let preds = super::predicates(&db, derive).all_predicates().skip_binder();
|
||||
format_to!(
|
||||
predicates,
|
||||
"{}\n\n",
|
||||
preds.iter().format_with("\n", |pred, formatter| formatter(&format_args!(
|
||||
"{pred:?}"
|
||||
))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
expectation.assert_eq(&predicates);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_macros_trait_ref() {
|
||||
check_trait_refs(
|
||||
r#"
|
||||
//- minicore: derive, clone, copy, eq, ord, hash, fmt
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct Simple;
|
||||
|
||||
trait Trait {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
|
||||
"#,
|
||||
expect![[r#"
|
||||
Simple: Debug
|
||||
Simple: Clone
|
||||
Simple: Copy
|
||||
Simple: PartialEq<[Simple]>
|
||||
Simple: Eq
|
||||
Simple: PartialOrd<[Simple]>
|
||||
Simple: Ord
|
||||
Simple: Hash
|
||||
WithGenerics<#0, #1, #2>: Debug
|
||||
WithGenerics<#0, #1, #2>: Clone
|
||||
WithGenerics<#0, #1, #2>: Copy
|
||||
WithGenerics<#0, #1, #2>: PartialEq<[WithGenerics<#0, #1, #2>]>
|
||||
WithGenerics<#0, #1, #2>: Eq
|
||||
WithGenerics<#0, #1, #2>: PartialOrd<[WithGenerics<#0, #1, #2>]>
|
||||
WithGenerics<#0, #1, #2>: Ord
|
||||
WithGenerics<#0, #1, #2>: Hash"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_pointee_trait_ref() {
|
||||
check_trait_refs(
|
||||
r#"
|
||||
//- minicore: derive, coerce_pointee
|
||||
use core::marker::CoercePointee;
|
||||
|
||||
#[derive(CoercePointee)]
|
||||
struct Simple<T: ?Sized>(*const T);
|
||||
|
||||
#[derive(CoercePointee)]
|
||||
struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U);
|
||||
"#,
|
||||
expect![[r#"
|
||||
Simple<#0>: CoerceUnsized<[Simple<#1>]>
|
||||
Simple<#0>: DispatchFromDyn<[Simple<#1>]>
|
||||
MultiGenericParams<#0, #1, #2, #3>: CoerceUnsized<[MultiGenericParams<#0, #1, #4, #3>]>
|
||||
MultiGenericParams<#0, #1, #2, #3>: DispatchFromDyn<[MultiGenericParams<#0, #1, #4, #3>]>"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_macros_predicates() {
|
||||
check_predicates(
|
||||
r#"
|
||||
//- minicore: derive, clone, copy, eq, ord, hash, fmt
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct Simple;
|
||||
|
||||
trait Trait {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
|
||||
"#,
|
||||
expect![[r#"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: PartialEq, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: PartialOrd, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] })
|
||||
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_pointee_predicates() {
|
||||
check_predicates(
|
||||
r#"
|
||||
//- minicore: derive, coerce_pointee
|
||||
use core::marker::CoercePointee;
|
||||
|
||||
#[derive(CoercePointee)]
|
||||
struct Simple<T: ?Sized>(*const T);
|
||||
|
||||
trait Trait<T> {}
|
||||
|
||||
#[derive(CoercePointee)]
|
||||
struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U)
|
||||
where
|
||||
T: Trait<U>,
|
||||
U: Trait<U>;
|
||||
"#,
|
||||
expect![[r#"
|
||||
Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
|
||||
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
|
||||
Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
|
||||
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1568,6 +1568,7 @@ const GOAL: u8 = {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "builtin derive macros are currently not working with MIR eval"]
|
||||
fn builtin_derive_macro() {
|
||||
check_number(
|
||||
r#"
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
//! type inference-related queries.
|
||||
|
||||
use base_db::{Crate, target::TargetLoadError};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
AdtId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
|
||||
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, VariantId,
|
||||
db::DefDatabase, hir::ExprId, layout::TargetDataLayout,
|
||||
AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
|
||||
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId,
|
||||
TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, db::DefDatabase, hir::ExprId,
|
||||
layout::TargetDataLayout,
|
||||
};
|
||||
use la_arena::ArenaMap;
|
||||
use salsa::plumbing::AsId;
|
||||
|
|
@ -83,7 +85,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
|
|||
env: ParamEnvAndCrate<'db>,
|
||||
func: FunctionId,
|
||||
fn_subst: GenericArgs<'db>,
|
||||
) -> (FunctionId, GenericArgs<'db>);
|
||||
) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>);
|
||||
|
||||
// endregion:mir
|
||||
|
||||
|
|
|
|||
|
|
@ -293,12 +293,18 @@ impl<'a> DeclValidator<'a> {
|
|||
fn validate_struct(&mut self, struct_id: StructId) {
|
||||
// Check the structure name.
|
||||
let data = self.db.struct_signature(struct_id);
|
||||
self.create_incorrect_case_diagnostic_for_item_name(
|
||||
struct_id,
|
||||
&data.name,
|
||||
CaseType::UpperCamelCase,
|
||||
IdentType::Structure,
|
||||
);
|
||||
|
||||
// rustc implementation excuses repr(C) since C structs predominantly don't
|
||||
// use camel case.
|
||||
let has_repr_c = data.repr(self.db, struct_id).is_some_and(|repr| repr.c());
|
||||
if !has_repr_c {
|
||||
self.create_incorrect_case_diagnostic_for_item_name(
|
||||
struct_id,
|
||||
&data.name,
|
||||
CaseType::UpperCamelCase,
|
||||
IdentType::Structure,
|
||||
);
|
||||
}
|
||||
|
||||
// Check the field names.
|
||||
self.validate_struct_fields(struct_id);
|
||||
|
|
@ -378,15 +384,20 @@ impl<'a> DeclValidator<'a> {
|
|||
}
|
||||
|
||||
fn validate_enum(&mut self, enum_id: EnumId) {
|
||||
// Check the enum name.
|
||||
let data = self.db.enum_signature(enum_id);
|
||||
|
||||
// Check the enum name.
|
||||
self.create_incorrect_case_diagnostic_for_item_name(
|
||||
enum_id,
|
||||
&data.name,
|
||||
CaseType::UpperCamelCase,
|
||||
IdentType::Enum,
|
||||
);
|
||||
// rustc implementation excuses repr(C) since C structs predominantly don't
|
||||
// use camel case.
|
||||
let has_repr_c = data.repr(self.db, enum_id).is_some_and(|repr| repr.c());
|
||||
if !has_repr_c {
|
||||
self.create_incorrect_case_diagnostic_for_item_name(
|
||||
enum_id,
|
||||
&data.name,
|
||||
CaseType::UpperCamelCase,
|
||||
IdentType::Enum,
|
||||
);
|
||||
}
|
||||
|
||||
// Check the variant names.
|
||||
self.validate_enum_variants(enum_id)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
mem,
|
||||
};
|
||||
|
||||
use base_db::Crate;
|
||||
use base_db::{Crate, FxIndexMap};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
FindPathConfig, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
|
||||
|
|
@ -143,11 +143,11 @@ impl<'db> BoundsFormattingCtx<'db> {
|
|||
}
|
||||
|
||||
impl<'db> HirFormatter<'_, 'db> {
|
||||
fn start_location_link(&mut self, location: ModuleDefId) {
|
||||
pub fn start_location_link(&mut self, location: ModuleDefId) {
|
||||
self.fmt.start_location_link(location);
|
||||
}
|
||||
|
||||
fn end_location_link(&mut self) {
|
||||
pub fn end_location_link(&mut self) {
|
||||
self.fmt.end_location_link();
|
||||
}
|
||||
|
||||
|
|
@ -1383,37 +1383,30 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
|
|||
}
|
||||
_ => (),
|
||||
}
|
||||
let sig = substs
|
||||
.split_closure_args_untupled()
|
||||
.closure_sig_as_fn_ptr_ty
|
||||
.callable_sig(interner);
|
||||
if let Some(sig) = sig {
|
||||
let sig = sig.skip_binder();
|
||||
let InternedClosure(def, _) = db.lookup_intern_closure(id);
|
||||
let infer = InferenceResult::for_body(db, def);
|
||||
let (_, kind) = infer.closure_info(id);
|
||||
match f.closure_style {
|
||||
ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
|
||||
ClosureStyle::RANotation => write!(f, "|")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if sig.inputs().is_empty() {
|
||||
} else if f.should_truncate() {
|
||||
write!(f, "{TYPE_HINT_TRUNCATION}")?;
|
||||
} else {
|
||||
f.write_joined(sig.inputs(), ", ")?;
|
||||
};
|
||||
match f.closure_style {
|
||||
ClosureStyle::ImplFn => write!(f, ")")?,
|
||||
ClosureStyle::RANotation => write!(f, "|")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() {
|
||||
write!(f, " -> ")?;
|
||||
sig.output().hir_fmt(f)?;
|
||||
}
|
||||
let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe);
|
||||
let sig = sig.skip_binder();
|
||||
let InternedClosure(def, _) = db.lookup_intern_closure(id);
|
||||
let infer = InferenceResult::for_body(db, def);
|
||||
let (_, kind) = infer.closure_info(id);
|
||||
match f.closure_style {
|
||||
ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
|
||||
ClosureStyle::RANotation => write!(f, "|")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if sig.inputs().is_empty() {
|
||||
} else if f.should_truncate() {
|
||||
write!(f, "{TYPE_HINT_TRUNCATION}")?;
|
||||
} else {
|
||||
write!(f, "{{closure}}")?;
|
||||
f.write_joined(sig.inputs(), ", ")?;
|
||||
};
|
||||
match f.closure_style {
|
||||
ClosureStyle::ImplFn => write!(f, ")")?,
|
||||
ClosureStyle::RANotation => write!(f, "|")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() {
|
||||
write!(f, " -> ")?;
|
||||
sig.output().hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
TyKind::CoroutineClosure(id, args) => {
|
||||
|
|
@ -1978,6 +1971,49 @@ fn write_bounds_like_dyn_trait<'db>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_params_bounds<'db>(
|
||||
f: &mut HirFormatter<'_, 'db>,
|
||||
predicates: &[Clause<'db>],
|
||||
) -> Result {
|
||||
// Use an FxIndexMap to keep user's order, as far as possible.
|
||||
let mut per_type = FxIndexMap::<_, Vec<_>>::default();
|
||||
for &predicate in predicates {
|
||||
let base_ty = match predicate.kind().skip_binder() {
|
||||
ClauseKind::Trait(clause) => Either::Left(clause.self_ty()),
|
||||
ClauseKind::RegionOutlives(clause) => Either::Right(clause.0),
|
||||
ClauseKind::TypeOutlives(clause) => Either::Left(clause.0),
|
||||
ClauseKind::Projection(clause) => Either::Left(clause.self_ty()),
|
||||
ClauseKind::ConstArgHasType(..)
|
||||
| ClauseKind::WellFormed(_)
|
||||
| ClauseKind::ConstEvaluatable(_)
|
||||
| ClauseKind::HostEffect(..)
|
||||
| ClauseKind::UnstableFeature(_) => continue,
|
||||
};
|
||||
per_type.entry(base_ty).or_default().push(predicate);
|
||||
}
|
||||
|
||||
for (base_ty, clauses) in per_type {
|
||||
f.write_str(" ")?;
|
||||
match base_ty {
|
||||
Either::Left(it) => it.hir_fmt(f)?,
|
||||
Either::Right(it) => it.hir_fmt(f)?,
|
||||
}
|
||||
f.write_str(": ")?;
|
||||
// Rudimentary approximation: type params are `Sized` by default, everything else not.
|
||||
// FIXME: This is not correct, really. But I'm not sure how we can from the ty representation
|
||||
// to extract the default sizedness, and if it's possible at all.
|
||||
let default_sized = match base_ty {
|
||||
Either::Left(ty) if matches!(ty.kind(), TyKind::Param(_)) => {
|
||||
SizedByDefault::Sized { anchor: f.krate() }
|
||||
}
|
||||
_ => SizedByDefault::NotSized,
|
||||
};
|
||||
write_bounds_like_dyn_trait(f, base_ty, &clauses, default_sized)?;
|
||||
f.write_str(",\n")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'db> HirDisplay<'db> for TraitRef<'db> {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
let trait_ = self.def_id.0;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ fn has_destructor(interner: DbInterner<'_>, adt: AdtId) -> bool {
|
|||
},
|
||||
None => TraitImpls::for_crate(db, module.krate(db)),
|
||||
};
|
||||
!impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).is_empty()
|
||||
!impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).0.is_empty()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let ClosureSignatures { bound_sig, liberated_sig } =
|
||||
self.sig_of_closure(arg_types, ret_type, expected_sig);
|
||||
let body_ret_ty = bound_sig.output().skip_binder();
|
||||
let sig_ty = Ty::new_fn_ptr(interner, bound_sig);
|
||||
|
||||
let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
|
||||
// FIXME: Make this an infer var and infer it later.
|
||||
|
|
@ -117,6 +116,16 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
None => {}
|
||||
};
|
||||
let sig = bound_sig.map_bound(|sig| {
|
||||
interner.mk_fn_sig(
|
||||
[Ty::new_tup(interner, sig.inputs())],
|
||||
sig.output(),
|
||||
sig.c_variadic,
|
||||
sig.safety,
|
||||
sig.abi,
|
||||
)
|
||||
});
|
||||
let sig_ty = Ty::new_fn_ptr(interner, sig);
|
||||
// FIXME: Infer the kind later if needed.
|
||||
let parts = ClosureArgsParts {
|
||||
parent_args: parent_args.as_slice(),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
//! Post-inference closure analysis: captures and closure kind.
|
||||
|
||||
use std::{cmp, convert::Infallible, mem};
|
||||
use std::{cmp, mem};
|
||||
|
||||
use either::Either;
|
||||
use base_db::Crate;
|
||||
use hir_def::{
|
||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||
DefWithBodyId, FieldId, HasModule, VariantId,
|
||||
expr_store::path::Path,
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
|
||||
|
|
@ -15,7 +15,7 @@ use hir_def::{
|
|||
};
|
||||
use rustc_ast_ir::Mutability;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_type_ir::inherent::{IntoKind, Ty as _};
|
||||
use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use stdx::{format_to, never};
|
||||
use syntax::utils::is_raw_identifier;
|
||||
|
|
@ -23,33 +23,97 @@ use syntax::utils::is_raw_identifier;
|
|||
use crate::{
|
||||
Adjust, Adjustment, BindingMode,
|
||||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||
display::{DisplayTarget, HirDisplay as _},
|
||||
infer::InferenceContext,
|
||||
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
|
||||
next_solver::{DbInterner, GenericArgs, StoredEarlyBinder, StoredTy, Ty, TyKind},
|
||||
mir::{BorrowKind, MirSpan, MutBorrowKind},
|
||||
next_solver::{
|
||||
DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredEarlyBinder, StoredTy, Ty,
|
||||
TyKind,
|
||||
infer::{InferCtxt, traits::ObligationCause},
|
||||
obligation_ctxt::ObligationCtxt,
|
||||
},
|
||||
traits::FnTrait,
|
||||
};
|
||||
|
||||
// The below functions handle capture and closure kind (Fn, FnMut, ..)
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum HirPlaceProjection {
|
||||
Deref,
|
||||
Field(FieldId),
|
||||
TupleField(u32),
|
||||
}
|
||||
|
||||
impl HirPlaceProjection {
|
||||
fn projected_ty<'db>(
|
||||
self,
|
||||
infcx: &InferCtxt<'db>,
|
||||
env: ParamEnv<'db>,
|
||||
mut base: Ty<'db>,
|
||||
krate: Crate,
|
||||
) -> Ty<'db> {
|
||||
let interner = infcx.interner;
|
||||
let db = interner.db;
|
||||
if base.is_ty_error() {
|
||||
return Ty::new_error(interner, ErrorGuaranteed);
|
||||
}
|
||||
|
||||
if matches!(base.kind(), TyKind::Alias(..)) {
|
||||
let mut ocx = ObligationCtxt::new(infcx);
|
||||
match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) {
|
||||
Ok(it) => base = it,
|
||||
Err(_) => return Ty::new_error(interner, ErrorGuaranteed),
|
||||
}
|
||||
}
|
||||
match self {
|
||||
HirPlaceProjection::Deref => match base.kind() {
|
||||
TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner,
|
||||
TyKind::Adt(adt_def, subst) if adt_def.is_box() => subst.type_at(0),
|
||||
_ => {
|
||||
never!(
|
||||
"Overloaded deref on type {} is not a projection",
|
||||
base.display(db, DisplayTarget::from_crate(db, krate))
|
||||
);
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
}
|
||||
},
|
||||
HirPlaceProjection::Field(f) => match base.kind() {
|
||||
TyKind::Adt(_, subst) => {
|
||||
db.field_types(f.parent)[f.local_id].get().instantiate(interner, subst)
|
||||
}
|
||||
ty => {
|
||||
never!("Only adt has field, found {:?}", ty);
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
}
|
||||
},
|
||||
HirPlaceProjection::TupleField(idx) => match base.kind() {
|
||||
TyKind::Tuple(subst) => {
|
||||
subst.as_slice().get(idx as usize).copied().unwrap_or_else(|| {
|
||||
never!("Out of bound tuple field");
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
})
|
||||
}
|
||||
ty => {
|
||||
never!("Only tuple has tuple field: {:?}", ty);
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
|
||||
pub(crate) struct HirPlace {
|
||||
pub(crate) local: BindingId,
|
||||
pub(crate) projections: Vec<ProjectionElem<Infallible>>,
|
||||
pub(crate) projections: Vec<HirPlaceProjection>,
|
||||
}
|
||||
|
||||
impl HirPlace {
|
||||
fn ty<'db>(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> {
|
||||
let krate = ctx.krate();
|
||||
let mut ty = ctx.table.resolve_completely(ctx.result.binding_ty(self.local));
|
||||
for p in &self.projections {
|
||||
ty = p.projected_ty(
|
||||
&ctx.table.infer_ctxt,
|
||||
ctx.table.param_env,
|
||||
ty,
|
||||
|_, _, _| {
|
||||
unreachable!("Closure field only happens in MIR");
|
||||
},
|
||||
ctx.owner.module(ctx.db).krate(ctx.db),
|
||||
);
|
||||
ty = p.projected_ty(ctx.infcx(), ctx.table.param_env, ty, krate);
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
|
@ -62,7 +126,7 @@ impl HirPlace {
|
|||
if let CaptureKind::ByRef(BorrowKind::Mut {
|
||||
kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
|
||||
}) = current_capture
|
||||
&& self.projections[len..].contains(&ProjectionElem::Deref)
|
||||
&& self.projections[len..].contains(&HirPlaceProjection::Deref)
|
||||
{
|
||||
current_capture =
|
||||
CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture });
|
||||
|
|
@ -98,12 +162,12 @@ impl CapturedItem {
|
|||
|
||||
/// Returns whether this place has any field (aka. non-deref) projections.
|
||||
pub fn has_field_projections(&self) -> bool {
|
||||
self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref))
|
||||
self.place.projections.iter().any(|it| !matches!(it, HirPlaceProjection::Deref))
|
||||
}
|
||||
|
||||
pub fn ty<'db>(&self, db: &'db dyn HirDatabase, subst: GenericArgs<'db>) -> Ty<'db> {
|
||||
let interner = DbInterner::new_no_crate(db);
|
||||
self.ty.get().instantiate(interner, subst.split_closure_args_untupled().parent_args)
|
||||
self.ty.get().instantiate(interner, subst.as_closure().parent_args())
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> CaptureKind {
|
||||
|
|
@ -120,8 +184,8 @@ impl CapturedItem {
|
|||
let mut result = body[self.place.local].name.as_str().to_owned();
|
||||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
ProjectionElem::Deref => {}
|
||||
ProjectionElem::Field(Either::Left(f)) => {
|
||||
HirPlaceProjection::Deref => {}
|
||||
HirPlaceProjection::Field(f) => {
|
||||
let variant_data = f.parent.fields(db);
|
||||
match variant_data.shape {
|
||||
FieldsShape::Record => {
|
||||
|
|
@ -138,14 +202,8 @@ impl CapturedItem {
|
|||
FieldsShape::Unit => {}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index),
|
||||
&ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"),
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
format_to!(result, "_{idx}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,8 +221,8 @@ impl CapturedItem {
|
|||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
// In source code autoderef kicks in.
|
||||
ProjectionElem::Deref => {}
|
||||
ProjectionElem::Field(Either::Left(f)) => {
|
||||
HirPlaceProjection::Deref => {}
|
||||
HirPlaceProjection::Field(f) => {
|
||||
let variant_data = f.parent.fields(db);
|
||||
match variant_data.shape {
|
||||
FieldsShape::Record => format_to!(
|
||||
|
|
@ -184,19 +242,8 @@ impl CapturedItem {
|
|||
FieldsShape::Unit => {}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(Either::Right(f)) => {
|
||||
let field = f.index;
|
||||
format_to!(result, ".{field}");
|
||||
}
|
||||
&ProjectionElem::ClosureField(field) => {
|
||||
format_to!(result, ".{field}");
|
||||
}
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
format_to!(result, ".{idx}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +252,7 @@ impl CapturedItem {
|
|||
.projections
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|proj| matches!(proj, ProjectionElem::Deref))
|
||||
.take_while(|proj| matches!(proj, HirPlaceProjection::Deref))
|
||||
.count();
|
||||
result.insert_str(0, &"*".repeat(final_derefs_count));
|
||||
result
|
||||
|
|
@ -219,11 +266,11 @@ impl CapturedItem {
|
|||
let mut field_need_paren = false;
|
||||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
ProjectionElem::Deref => {
|
||||
HirPlaceProjection::Deref => {
|
||||
result = format!("*{result}");
|
||||
field_need_paren = true;
|
||||
}
|
||||
ProjectionElem::Field(Either::Left(f)) => {
|
||||
HirPlaceProjection::Field(f) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
|
|
@ -243,28 +290,13 @@ impl CapturedItem {
|
|||
result = format!("{result}.{field}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
ProjectionElem::Field(Either::Right(f)) => {
|
||||
let field = f.index;
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result = format!("{result}.{field}");
|
||||
result = format!("{result}.{idx}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
&ProjectionElem::ClosureField(field) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result = format!("{result}.{field}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
|
|
@ -345,7 +377,9 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let mut place = self.place_of_expr(*expr)?;
|
||||
let field = self.result.field_resolution(tgt_expr)?;
|
||||
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
|
||||
place.projections.push(ProjectionElem::Field(field));
|
||||
place.projections.push(field.either(HirPlaceProjection::Field, |f| {
|
||||
HirPlaceProjection::TupleField(f.index)
|
||||
}));
|
||||
return Some(place);
|
||||
}
|
||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||
|
|
@ -357,7 +391,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
if is_builtin_deref {
|
||||
let mut place = self.place_of_expr(*expr)?;
|
||||
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
|
||||
place.projections.push(ProjectionElem::Deref);
|
||||
place.projections.push(HirPlaceProjection::Deref);
|
||||
return Some(place);
|
||||
}
|
||||
}
|
||||
|
|
@ -832,9 +866,6 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
&self.table.infer_ctxt,
|
||||
self.table.param_env,
|
||||
ty,
|
||||
|_, _, _| {
|
||||
unreachable!("Closure field only happens in MIR");
|
||||
},
|
||||
self.owner.module(self.db).krate(self.db),
|
||||
);
|
||||
if ty.is_raw_ptr() || ty.is_union() {
|
||||
|
|
@ -853,7 +884,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let mut current_captures = std::mem::take(&mut self.current_captures);
|
||||
for capture in &mut current_captures {
|
||||
if let Some(first_deref) =
|
||||
capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
|
||||
capture.place.projections.iter().position(|proj| *proj == HirPlaceProjection::Deref)
|
||||
{
|
||||
self.truncate_capture_spans(capture, first_deref);
|
||||
capture.place.projections.truncate(first_deref);
|
||||
|
|
@ -876,7 +907,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
match it.next() {
|
||||
Some(it) => {
|
||||
lookup_place.projections.push(it.clone());
|
||||
lookup_place.projections.push(*it);
|
||||
}
|
||||
None => break None,
|
||||
}
|
||||
|
|
@ -903,7 +934,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) {
|
||||
let adjustments_count =
|
||||
self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default();
|
||||
place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref));
|
||||
place.projections.extend((0..adjustments_count).map(|_| HirPlaceProjection::Deref));
|
||||
self.current_capture_span_stack
|
||||
.extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat)));
|
||||
'reset_span_stack: {
|
||||
|
|
@ -920,10 +951,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
for (&arg, i) in it {
|
||||
let mut p = place.clone();
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(arg));
|
||||
p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
|
||||
tuple: TupleId(!0), // dummy this, as its unused anyways
|
||||
index: i as u32,
|
||||
})));
|
||||
p.projections.push(HirPlaceProjection::TupleField(i as u32));
|
||||
self.consume_with_pat(p, arg);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -950,10 +978,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
};
|
||||
let mut p = place.clone();
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(arg));
|
||||
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
|
||||
p.projections.push(HirPlaceProjection::Field(FieldId {
|
||||
parent: variant,
|
||||
local_id,
|
||||
})));
|
||||
}));
|
||||
self.consume_with_pat(p, arg);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -1005,10 +1033,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
for (&arg, (i, _)) in it {
|
||||
let mut p = place.clone();
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(arg));
|
||||
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
|
||||
p.projections.push(HirPlaceProjection::Field(FieldId {
|
||||
parent: variant,
|
||||
local_id: i,
|
||||
})));
|
||||
}));
|
||||
self.consume_with_pat(p, arg);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -1017,7 +1045,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
Pat::Ref { pat, mutability: _ } => {
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat));
|
||||
place.projections.push(ProjectionElem::Deref);
|
||||
place.projections.push(HirPlaceProjection::Deref);
|
||||
self.consume_with_pat(place, *pat);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -1071,7 +1099,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
CaptureKind::ByRef(BorrowKind::Mut {
|
||||
kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow
|
||||
})
|
||||
) && !item.place.projections.contains(&ProjectionElem::Deref)
|
||||
) && !item.place.projections.contains(&HirPlaceProjection::Deref)
|
||||
{
|
||||
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
|
||||
// MIR. I didn't do that due duplicate diagnostics.
|
||||
|
|
@ -1221,7 +1249,7 @@ fn apply_adjusts_to_place(
|
|||
match &adj.kind {
|
||||
Adjust::Deref(None) => {
|
||||
current_capture_span_stack.push(span);
|
||||
r.projections.push(ProjectionElem::Deref);
|
||||
r.projections.push(HirPlaceProjection::Deref);
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ use rustc_type_ir::{
|
|||
BoundVar, DebruijnIndex, TyVid, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
TypeVisitableExt,
|
||||
error::TypeError,
|
||||
inherent::{Const as _, GenericArg as _, IntoKind, Safety, SliceLike, Ty as _},
|
||||
inherent::{
|
||||
Const as _, GenericArg as _, GenericArgs as _, IntoKind, Safety as _, SliceLike, Ty as _,
|
||||
},
|
||||
};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use tracing::{debug, instrument};
|
||||
|
|
@ -54,7 +56,7 @@ use tracing::{debug, instrument};
|
|||
use crate::{
|
||||
Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, TargetFeatures,
|
||||
autoderef::Autoderef,
|
||||
db::{HirDatabase, InternedClosureId},
|
||||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||
infer::{
|
||||
AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead,
|
||||
},
|
||||
|
|
@ -63,6 +65,7 @@ use crate::{
|
|||
Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
|
||||
GenericArgs, ParamEnv, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
|
||||
TypingMode,
|
||||
abi::Safety,
|
||||
infer::{
|
||||
DbInternerInferExt, InferCtxt, InferOk, InferResult,
|
||||
relate::RelateResult,
|
||||
|
|
@ -71,6 +74,7 @@ use crate::{
|
|||
},
|
||||
obligation_ctxt::ObligationCtxt,
|
||||
},
|
||||
upvars::upvars_mentioned,
|
||||
utils::TargetFeatureIsSafeInTarget,
|
||||
};
|
||||
|
||||
|
|
@ -893,7 +897,7 @@ where
|
|||
fn coerce_closure_to_fn(
|
||||
&mut self,
|
||||
a: Ty<'db>,
|
||||
_closure_def_id_a: InternedClosureId,
|
||||
closure_def_id_a: InternedClosureId,
|
||||
args_a: GenericArgs<'db>,
|
||||
b: Ty<'db>,
|
||||
) -> CoerceResult<'db> {
|
||||
|
|
@ -901,19 +905,7 @@ where
|
|||
debug_assert!(self.infcx().shallow_resolve(b) == b);
|
||||
|
||||
match b.kind() {
|
||||
// FIXME: We need to have an `upvars_mentioned()` query:
|
||||
// At this point we haven't done capture analysis, which means
|
||||
// that the ClosureArgs just contains an inference variable instead
|
||||
// of tuple of captured types.
|
||||
//
|
||||
// All we care here is if any variable is being captured and not the exact paths,
|
||||
// so we check `upvars_mentioned` for root variables being captured.
|
||||
TyKind::FnPtr(_, hdr) =>
|
||||
// if self
|
||||
// .db
|
||||
// .upvars_mentioned(closure_def_id_a.expect_local())
|
||||
// .is_none_or(|u| u.is_empty()) =>
|
||||
{
|
||||
TyKind::FnPtr(_, hdr) if !is_capturing_closure(self.db(), closure_def_id_a) => {
|
||||
// We coerce the closure, which has fn type
|
||||
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
|
||||
// to
|
||||
|
|
@ -921,10 +913,8 @@ where
|
|||
// or
|
||||
// `unsafe fn(arg0,arg1,...) -> _`
|
||||
let safety = hdr.safety;
|
||||
let closure_sig = args_a.closure_sig_untupled().map_bound(|mut sig| {
|
||||
sig.safety = hdr.safety;
|
||||
sig
|
||||
});
|
||||
let closure_sig =
|
||||
self.interner().signature_unclosure(args_a.as_closure().sig(), safety);
|
||||
let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig);
|
||||
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
|
||||
self.unify_and(
|
||||
|
|
@ -1088,14 +1078,12 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
// Special-case that coercion alone cannot handle:
|
||||
// Function items or non-capturing closures of differing IDs or GenericArgs.
|
||||
let (a_sig, b_sig) = {
|
||||
let is_capturing_closure = |_ty: Ty<'db>| {
|
||||
// FIXME:
|
||||
// if let TyKind::Closure(closure_def_id, _args) = ty.kind() {
|
||||
// self.db.upvars_mentioned(closure_def_id.expect_local()).is_some()
|
||||
// } else {
|
||||
// false
|
||||
// }
|
||||
false
|
||||
let is_capturing_closure = |ty: Ty<'db>| {
|
||||
if let TyKind::Closure(closure_def_id, _args) = ty.kind() {
|
||||
is_capturing_closure(self.db, closure_def_id.0)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) {
|
||||
(None, None)
|
||||
|
|
@ -1125,23 +1113,28 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
(TyKind::Closure(_, args), TyKind::FnDef(..)) => {
|
||||
let b_sig = new_ty.fn_sig(self.table.interner());
|
||||
let a_sig = args.closure_sig_untupled().map_bound(|mut sig| {
|
||||
sig.safety = b_sig.safety();
|
||||
sig
|
||||
});
|
||||
let a_sig = self
|
||||
.interner()
|
||||
.signature_unclosure(args.as_closure().sig(), b_sig.safety());
|
||||
(Some(a_sig), Some(b_sig))
|
||||
}
|
||||
(TyKind::FnDef(..), TyKind::Closure(_, args)) => {
|
||||
let a_sig = prev_ty.fn_sig(self.table.interner());
|
||||
let b_sig = args.closure_sig_untupled().map_bound(|mut sig| {
|
||||
sig.safety = a_sig.safety();
|
||||
sig
|
||||
});
|
||||
let b_sig = self
|
||||
.interner()
|
||||
.signature_unclosure(args.as_closure().sig(), a_sig.safety());
|
||||
(Some(a_sig), Some(b_sig))
|
||||
}
|
||||
(TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => {
|
||||
(Some(args_a.closure_sig_untupled()), Some(args_b.closure_sig_untupled()))
|
||||
}
|
||||
(TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => (
|
||||
Some(
|
||||
self.interner()
|
||||
.signature_unclosure(args_a.as_closure().sig(), Safety::Safe),
|
||||
),
|
||||
Some(
|
||||
self.interner()
|
||||
.signature_unclosure(args_b.as_closure().sig(), Safety::Safe),
|
||||
),
|
||||
),
|
||||
_ => (None, None),
|
||||
}
|
||||
}
|
||||
|
|
@ -1722,3 +1715,9 @@ fn coerce<'db>(
|
|||
.collect();
|
||||
Ok((adjustments, ty))
|
||||
}
|
||||
|
||||
fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool {
|
||||
let InternedClosure(owner, expr) = closure.loc(db);
|
||||
upvars_mentioned(db, owner)
|
||||
.is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ use rustc_abi::{
|
|||
TargetDataLayout, WrappingRange,
|
||||
};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_type_ir::{FloatTy, IntTy, UintTy, inherent::IntoKind};
|
||||
use rustc_type_ir::{
|
||||
FloatTy, IntTy, UintTy,
|
||||
inherent::{GenericArgs as _, IntoKind},
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -335,10 +338,7 @@ pub fn layout_of_ty_query(
|
|||
let fields = captures
|
||||
.iter()
|
||||
.map(|it| {
|
||||
let ty = it
|
||||
.ty
|
||||
.get()
|
||||
.instantiate(interner, args.split_closure_args_untupled().parent_args);
|
||||
let ty = it.ty.get().instantiate(interner, args.as_closure().parent_args());
|
||||
db.layout_of_ty(ty.store(), trait_env.clone())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
|
|||
|
||||
extern crate self as hir_ty;
|
||||
|
||||
pub mod builtin_derive;
|
||||
mod infer;
|
||||
mod inhabitedness;
|
||||
mod lower;
|
||||
|
|
@ -49,6 +50,7 @@ pub mod method_resolution;
|
|||
pub mod mir;
|
||||
pub mod primitive;
|
||||
pub mod traits;
|
||||
pub mod upvars;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_db;
|
||||
|
|
|
|||
|
|
@ -1790,6 +1790,13 @@ impl<'db> GenericPredicates {
|
|||
}
|
||||
|
||||
impl GenericPredicates {
|
||||
#[inline]
|
||||
pub(crate) fn from_explicit_own_predicates(
|
||||
predicates: StoredEarlyBinder<StoredClauses>,
|
||||
) -> Self {
|
||||
Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query(db: &dyn HirDatabase, def: GenericDefId) -> &GenericPredicates {
|
||||
&Self::query_with_diagnostics(db, def).0
|
||||
|
|
@ -1848,6 +1855,20 @@ pub(crate) fn trait_environment_for_body_query(
|
|||
db.trait_environment(def)
|
||||
}
|
||||
|
||||
pub(crate) fn param_env_from_predicates<'db>(
|
||||
interner: DbInterner<'db>,
|
||||
predicates: &'db GenericPredicates,
|
||||
) -> ParamEnv<'db> {
|
||||
let clauses = rustc_type_ir::elaborate::elaborate(
|
||||
interner,
|
||||
predicates.all_predicates().iter_identity_copied(),
|
||||
);
|
||||
let clauses = Clauses::new_from_iter(interner, clauses);
|
||||
|
||||
// FIXME: We should normalize projections here, like rustc does.
|
||||
ParamEnv { clauses }
|
||||
}
|
||||
|
||||
pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> {
|
||||
return ParamEnv { clauses: trait_environment_query(db, def).as_ref() };
|
||||
|
||||
|
|
@ -1858,13 +1879,8 @@ pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId
|
|||
) -> StoredClauses {
|
||||
let module = def.module(db);
|
||||
let interner = DbInterner::new_with(db, module.krate(db));
|
||||
let predicates = GenericPredicates::query_all(db, def);
|
||||
let clauses =
|
||||
rustc_type_ir::elaborate::elaborate(interner, predicates.iter_identity_copied());
|
||||
let clauses = Clauses::new_from_iter(interner, clauses);
|
||||
|
||||
// FIXME: We should normalize projections here, like rustc does.
|
||||
clauses.store()
|
||||
let predicates = GenericPredicates::query(db, def);
|
||||
param_env_from_predicates(interner, predicates).clauses.store()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ use tracing::{debug, instrument};
|
|||
|
||||
use base_db::Crate;
|
||||
use hir_def::{
|
||||
AssocItemId, BlockId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId,
|
||||
ModuleId, TraitId,
|
||||
AssocItemId, BlockId, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule,
|
||||
ImplId, ItemContainerId, ModuleId, TraitId,
|
||||
attrs::AttrFlags,
|
||||
builtin_derive::BuiltinDeriveImplMethod,
|
||||
expr_store::path::GenericArgs as HirGenericArgs,
|
||||
hir::ExprId,
|
||||
lang_item::LangItems,
|
||||
nameres::{DefMap, block_def_map, crate_def_map},
|
||||
resolver::Resolver,
|
||||
};
|
||||
|
|
@ -37,7 +39,7 @@ use crate::{
|
|||
infer::{InferenceContext, unify::InferenceTable},
|
||||
lower::GenericPredicates,
|
||||
next_solver::{
|
||||
Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
|
||||
AnyImplId, Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
|
||||
SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode,
|
||||
infer::{
|
||||
BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk,
|
||||
|
|
@ -132,7 +134,7 @@ pub enum MethodError<'db> {
|
|||
// candidate can arise. Used for error reporting only.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum CandidateSource {
|
||||
Impl(ImplId),
|
||||
Impl(AnyImplId),
|
||||
Trait(TraitId),
|
||||
}
|
||||
|
||||
|
|
@ -371,9 +373,13 @@ pub fn lookup_impl_const<'db>(
|
|||
};
|
||||
|
||||
lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name)
|
||||
.and_then(
|
||||
|assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
|
||||
)
|
||||
.and_then(|assoc| {
|
||||
if let (Either::Left(AssocItemId::ConstId(id)), s) = assoc {
|
||||
Some((id, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or((const_id, subs))
|
||||
}
|
||||
|
||||
|
|
@ -419,12 +425,12 @@ pub(crate) fn lookup_impl_method_query<'db>(
|
|||
env: ParamEnvAndCrate<'db>,
|
||||
func: FunctionId,
|
||||
fn_subst: GenericArgs<'db>,
|
||||
) -> (FunctionId, GenericArgs<'db>) {
|
||||
) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>) {
|
||||
let interner = DbInterner::new_with(db, env.krate);
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||
|
||||
let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
|
||||
return (func, fn_subst);
|
||||
return (Either::Left(func), fn_subst);
|
||||
};
|
||||
let trait_params = db.generic_params(trait_id.into()).len();
|
||||
let trait_ref = TraitRef::new_from_args(
|
||||
|
|
@ -434,16 +440,19 @@ pub(crate) fn lookup_impl_method_query<'db>(
|
|||
);
|
||||
|
||||
let name = &db.function_signature(func).name;
|
||||
let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(
|
||||
&infcx,
|
||||
trait_ref,
|
||||
env.param_env,
|
||||
name,
|
||||
)
|
||||
.and_then(|assoc| {
|
||||
if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None }
|
||||
}) else {
|
||||
return (func, fn_subst);
|
||||
let Some((impl_fn, impl_subst)) =
|
||||
lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env.param_env, name).and_then(
|
||||
|(assoc, impl_args)| {
|
||||
let assoc = match assoc {
|
||||
Either::Left(AssocItemId::FunctionId(id)) => Either::Left(id),
|
||||
Either::Right(it) => Either::Right(it),
|
||||
_ => return None,
|
||||
};
|
||||
Some((assoc, impl_args))
|
||||
},
|
||||
)
|
||||
else {
|
||||
return (Either::Left(func), fn_subst);
|
||||
};
|
||||
|
||||
(
|
||||
|
|
@ -460,22 +469,33 @@ fn lookup_impl_assoc_item_for_trait_ref<'db>(
|
|||
trait_ref: TraitRef<'db>,
|
||||
env: ParamEnv<'db>,
|
||||
name: &Name,
|
||||
) -> Option<(AssocItemId, GenericArgs<'db>)> {
|
||||
) -> Option<(Either<AssocItemId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>)>
|
||||
{
|
||||
let (impl_id, impl_subst) = find_matching_impl(infcx, env, trait_ref)?;
|
||||
let impl_id = match impl_id {
|
||||
AnyImplId::ImplId(it) => it,
|
||||
AnyImplId::BuiltinDeriveImplId(impl_) => {
|
||||
return impl_
|
||||
.loc(infcx.interner.db)
|
||||
.trait_
|
||||
.get_method(name.symbol())
|
||||
.map(|method| (Either::Right((impl_, method)), impl_subst));
|
||||
}
|
||||
};
|
||||
let item =
|
||||
impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it {
|
||||
AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
|
||||
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
|
||||
AssocItemId::TypeAliasId(_) => None,
|
||||
})?;
|
||||
Some((item, impl_subst))
|
||||
Some((Either::Left(item), impl_subst))
|
||||
}
|
||||
|
||||
pub(crate) fn find_matching_impl<'db>(
|
||||
infcx: &InferCtxt<'db>,
|
||||
env: ParamEnv<'db>,
|
||||
trait_ref: TraitRef<'db>,
|
||||
) -> Option<(ImplId, GenericArgs<'db>)> {
|
||||
) -> Option<(AnyImplId, GenericArgs<'db>)> {
|
||||
let trait_ref = infcx.at(&ObligationCause::dummy(), env).deeply_normalize(trait_ref).ok()?;
|
||||
|
||||
let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), env, trait_ref);
|
||||
|
|
@ -635,13 +655,13 @@ impl InherentImpls {
|
|||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct OneTraitImpls {
|
||||
non_blanket_impls: FxHashMap<SimplifiedType, Box<[ImplId]>>,
|
||||
non_blanket_impls: FxHashMap<SimplifiedType, (Box<[ImplId]>, Box<[BuiltinDeriveImplId]>)>,
|
||||
blanket_impls: Box<[ImplId]>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct OneTraitImplsBuilder {
|
||||
non_blanket_impls: FxHashMap<SimplifiedType, Vec<ImplId>>,
|
||||
non_blanket_impls: FxHashMap<SimplifiedType, (Vec<ImplId>, Vec<BuiltinDeriveImplId>)>,
|
||||
blanket_impls: Vec<ImplId>,
|
||||
}
|
||||
|
||||
|
|
@ -650,7 +670,9 @@ impl OneTraitImplsBuilder {
|
|||
let mut non_blanket_impls = self
|
||||
.non_blanket_impls
|
||||
.into_iter()
|
||||
.map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
|
||||
.map(|(self_ty, (impls, builtin_derive_impls))| {
|
||||
(self_ty, (impls.into_boxed_slice(), builtin_derive_impls.into_boxed_slice()))
|
||||
})
|
||||
.collect::<FxHashMap<_, _>>();
|
||||
non_blanket_impls.shrink_to_fit();
|
||||
let blanket_impls = self.blanket_impls.into_boxed_slice();
|
||||
|
|
@ -691,8 +713,9 @@ impl TraitImpls {
|
|||
|
||||
impl TraitImpls {
|
||||
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
|
||||
let lang_items = hir_def::lang_item::lang_items(db, def_map.krate());
|
||||
let mut map = FxHashMap::default();
|
||||
collect(db, def_map, &mut map);
|
||||
collect(db, def_map, lang_items, &mut map);
|
||||
let mut map = map
|
||||
.into_iter()
|
||||
.map(|(trait_id, trait_map)| (trait_id, trait_map.finish()))
|
||||
|
|
@ -703,6 +726,7 @@ impl TraitImpls {
|
|||
fn collect(
|
||||
db: &dyn HirDatabase,
|
||||
def_map: &DefMap,
|
||||
lang_items: &LangItems,
|
||||
map: &mut FxHashMap<TraitId, OneTraitImplsBuilder>,
|
||||
) {
|
||||
for (_module_id, module_data) in def_map.modules() {
|
||||
|
|
@ -727,18 +751,29 @@ impl TraitImpls {
|
|||
let entry = map.entry(trait_ref.def_id.0).or_default();
|
||||
match simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer) {
|
||||
Some(self_ty) => {
|
||||
entry.non_blanket_impls.entry(self_ty).or_default().push(impl_id)
|
||||
entry.non_blanket_impls.entry(self_ty).or_default().0.push(impl_id)
|
||||
}
|
||||
None => entry.blanket_impls.push(impl_id),
|
||||
}
|
||||
}
|
||||
|
||||
for impl_id in module_data.scope.builtin_derive_impls() {
|
||||
let loc = impl_id.loc(db);
|
||||
let Some(trait_id) = loc.trait_.get_id(lang_items) else { continue };
|
||||
let entry = map.entry(trait_id).or_default();
|
||||
let entry = entry
|
||||
.non_blanket_impls
|
||||
.entry(SimplifiedType::Adt(loc.adt.into()))
|
||||
.or_default();
|
||||
entry.1.push(impl_id);
|
||||
}
|
||||
|
||||
// To better support custom derives, collect impls in all unnamed const items.
|
||||
// const _: () = { ... };
|
||||
for konst in module_data.scope.unnamed_consts() {
|
||||
let body = db.body(konst.into());
|
||||
for (_, block_def_map) in body.blocks(db) {
|
||||
collect(db, block_def_map, map);
|
||||
collect(db, block_def_map, lang_items, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -761,27 +796,41 @@ impl TraitImpls {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn for_trait_and_self_ty(&self, trait_: TraitId, self_ty: &SimplifiedType) -> &[ImplId] {
|
||||
pub fn for_trait_and_self_ty(
|
||||
&self,
|
||||
trait_: TraitId,
|
||||
self_ty: &SimplifiedType,
|
||||
) -> (&[ImplId], &[BuiltinDeriveImplId]) {
|
||||
self.map
|
||||
.get(&trait_)
|
||||
.and_then(|map| map.non_blanket_impls.get(self_ty))
|
||||
.map(|it| &**it)
|
||||
.map(|it| (&*it.0, &*it.1))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn for_trait(&self, trait_: TraitId, mut callback: impl FnMut(&[ImplId])) {
|
||||
pub fn for_trait(
|
||||
&self,
|
||||
trait_: TraitId,
|
||||
mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
|
||||
) {
|
||||
if let Some(impls) = self.map.get(&trait_) {
|
||||
callback(&impls.blanket_impls);
|
||||
callback(Either::Left(&impls.blanket_impls));
|
||||
for impls in impls.non_blanket_impls.values() {
|
||||
callback(impls);
|
||||
callback(Either::Left(&impls.0));
|
||||
callback(Either::Right(&impls.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[ImplId])) {
|
||||
pub fn for_self_ty(
|
||||
&self,
|
||||
self_ty: &SimplifiedType,
|
||||
mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
|
||||
) {
|
||||
for for_trait in self.map.values() {
|
||||
if let Some(for_ty) = for_trait.non_blanket_impls.get(self_ty) {
|
||||
callback(for_ty);
|
||||
callback(Either::Left(&for_ty.0));
|
||||
callback(Either::Right(&for_ty.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1001,7 +1001,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
|
|||
self.with_impl_item(impl_def_id, |this, item| {
|
||||
if !this.has_applicable_self(item) {
|
||||
// No receiver declared. Not a candidate.
|
||||
this.record_static_candidate(CandidateSource::Impl(impl_def_id));
|
||||
this.record_static_candidate(CandidateSource::Impl(impl_def_id.into()));
|
||||
return;
|
||||
}
|
||||
this.push_candidate(
|
||||
|
|
@ -1490,7 +1490,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
|
|||
/// so do not use to make a decision that may lead to a successful compilation.
|
||||
fn candidate_source(&self, candidate: &Candidate<'db>, self_ty: Ty<'db>) -> CandidateSource {
|
||||
match candidate.kind {
|
||||
InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id),
|
||||
InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id.into()),
|
||||
ObjectCandidate(trait_ref) | WhereClauseCandidate(trait_ref) => {
|
||||
CandidateSource::Trait(trait_ref.def_id().0)
|
||||
}
|
||||
|
|
@ -1524,7 +1524,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
|
|||
|
||||
fn candidate_source_from_pick(&self, pick: &Pick<'db>) -> CandidateSource {
|
||||
match pick.kind {
|
||||
InherentImplPick(impl_) => CandidateSource::Impl(impl_),
|
||||
InherentImplPick(impl_) => CandidateSource::Impl(impl_.into()),
|
||||
ObjectPick(trait_) | TraitPick(trait_) => CandidateSource::Trait(trait_),
|
||||
WhereClausePick(trait_ref) => CandidateSource::Trait(trait_ref.skip_binder().def_id.0),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use std::iter;
|
|||
use hir_def::{DefWithBodyId, HasModule};
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_type_ir::inherent::GenericArgs as _;
|
||||
use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
|
|
@ -123,7 +124,7 @@ fn make_fetch_closure_field<'db>(
|
|||
let InternedClosure(def, _) = db.lookup_intern_closure(c);
|
||||
let infer = InferenceResult::for_body(db, def);
|
||||
let (captures, _) = infer.closure_info(c);
|
||||
let parent_subst = subst.split_closure_args_untupled().parent_args;
|
||||
let parent_subst = subst.as_closure().parent_args();
|
||||
let interner = DbInterner::new_no_crate(db);
|
||||
captures.get(f).expect("broken closure field").ty.get().instantiate(interner, parent_subst)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use rustc_ast_ir::Mutability;
|
|||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_type_ir::{
|
||||
AliasTyKind,
|
||||
inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _},
|
||||
inherent::{AdtDef, GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _},
|
||||
};
|
||||
use span::FileId;
|
||||
use stdx::never;
|
||||
|
|
@ -77,12 +77,14 @@ macro_rules! from_bytes {
|
|||
}).into())
|
||||
};
|
||||
}
|
||||
use from_bytes;
|
||||
|
||||
macro_rules! not_supported {
|
||||
($it: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($it)))
|
||||
return Err($crate::mir::eval::MirEvalError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
use not_supported;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, GenericTypeVisitable)]
|
||||
pub struct VTableMap<'db> {
|
||||
|
|
@ -731,7 +733,7 @@ impl<'db> Evaluator<'db> {
|
|||
let InternedClosure(def, _) = self.db.lookup_intern_closure(c);
|
||||
let infer = InferenceResult::for_body(self.db, def);
|
||||
let (captures, _) = infer.closure_info(c);
|
||||
let parent_subst = subst.split_closure_args_untupled().parent_args;
|
||||
let parent_subst = subst.as_closure().parent_args();
|
||||
captures
|
||||
.get(f)
|
||||
.expect("broken closure field")
|
||||
|
|
@ -2622,6 +2624,9 @@ impl<'db> Evaluator<'db> {
|
|||
def,
|
||||
generic_args,
|
||||
);
|
||||
let Either::Left(imp) = imp else {
|
||||
not_supported!("evaluating builtin derive impls is not supported")
|
||||
};
|
||||
|
||||
let mir_body = self
|
||||
.db
|
||||
|
|
@ -2771,7 +2776,7 @@ impl<'db> Evaluator<'db> {
|
|||
TyKind::Closure(closure, subst) => self.exec_closure(
|
||||
closure.0,
|
||||
func_data,
|
||||
GenericArgs::new_from_slice(subst.split_closure_args_untupled().parent_args),
|
||||
GenericArgs::new_from_slice(subst.as_closure().parent_args()),
|
||||
destination,
|
||||
&args[1..],
|
||||
locals,
|
||||
|
|
|
|||
|
|
@ -16,29 +16,14 @@ use crate::{
|
|||
mir::eval::{
|
||||
Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay,
|
||||
InternedClosure, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, Layout, Locals,
|
||||
Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, pad16,
|
||||
Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, from_bytes, not_supported,
|
||||
pad16,
|
||||
},
|
||||
next_solver::Region,
|
||||
};
|
||||
|
||||
mod simd;
|
||||
|
||||
macro_rules! from_bytes {
|
||||
($ty:tt, $value:expr) => {
|
||||
($ty::from_le_bytes(match ($value).try_into() {
|
||||
Ok(it) => it,
|
||||
#[allow(unreachable_patterns)]
|
||||
Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
($it: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum EvalLangItem {
|
||||
BeginPanic,
|
||||
|
|
|
|||
|
|
@ -6,21 +6,6 @@ use crate::consteval::try_const_usize;
|
|||
|
||||
use super::*;
|
||||
|
||||
macro_rules! from_bytes {
|
||||
($ty:tt, $value:expr) => {
|
||||
($ty::from_le_bytes(match ($value).try_into() {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
($it: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
impl<'db> Evaluator<'db> {
|
||||
fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> {
|
||||
match ty.kind() {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use hir_expand::name::Name;
|
|||
use la_arena::ArenaMap;
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _};
|
||||
use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
|
||||
use span::{Edition, FileId};
|
||||
use syntax::TextRange;
|
||||
use triomphe::Arc;
|
||||
|
|
@ -30,7 +30,10 @@ use crate::{
|
|||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||
display::{DisplayTarget, HirDisplay, hir_display_with_store},
|
||||
generics::generics,
|
||||
infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy},
|
||||
infer::{
|
||||
CaptureKind, CapturedItem, TypeMismatch, cast::CastTy,
|
||||
closure::analysis::HirPlaceProjection,
|
||||
},
|
||||
inhabitedness::is_ty_uninhabited_from,
|
||||
layout::LayoutError,
|
||||
method_resolution::CandidateId,
|
||||
|
|
@ -44,6 +47,7 @@ use crate::{
|
|||
next_solver::{
|
||||
Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind,
|
||||
TypingMode, UnevaluatedConst,
|
||||
abi::Safety,
|
||||
infer::{DbInternerInferExt, InferCtxt},
|
||||
},
|
||||
traits::FnTrait,
|
||||
|
|
@ -1257,22 +1261,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
|
|||
.clone()
|
||||
.into_iter()
|
||||
.map(|it| match it {
|
||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||
ProjectionElem::Field(it) => ProjectionElem::Field(it),
|
||||
ProjectionElem::ClosureField(it) => {
|
||||
ProjectionElem::ClosureField(it)
|
||||
HirPlaceProjection::Deref => ProjectionElem::Deref,
|
||||
HirPlaceProjection::Field(field_id) => {
|
||||
ProjectionElem::Field(Either::Left(field_id))
|
||||
}
|
||||
ProjectionElem::ConstantIndex { offset, from_end } => {
|
||||
ProjectionElem::ConstantIndex { offset, from_end }
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
ProjectionElem::Field(Either::Right(TupleFieldId {
|
||||
tuple: TupleId(!0), // Dummy as it's unused
|
||||
index: idx,
|
||||
}))
|
||||
}
|
||||
ProjectionElem::Subslice { from, to } => {
|
||||
ProjectionElem::Subslice { from, to }
|
||||
}
|
||||
ProjectionElem::OpaqueCast(it) => {
|
||||
ProjectionElem::OpaqueCast(it)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
ProjectionElem::Index(it) => match it {},
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
|
|
@ -2138,11 +2136,7 @@ pub fn mir_body_for_closure_query<'db>(
|
|||
.store(),
|
||||
});
|
||||
ctx.result.param_locals.push(closure_local);
|
||||
let Some(sig) =
|
||||
substs.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.callable_sig(ctx.interner())
|
||||
else {
|
||||
implementation_error!("closure has not callable sig");
|
||||
};
|
||||
let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe);
|
||||
let resolver_guard = ctx.resolver.update_to_inner_scope(db, owner, expr);
|
||||
let current = ctx.lower_params_and_bindings(
|
||||
args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)),
|
||||
|
|
@ -2176,10 +2170,13 @@ pub fn mir_body_for_closure_query<'db>(
|
|||
for (it, y) in p.projection.lookup(store).iter().zip(it.0.place.projections.iter())
|
||||
{
|
||||
match (it, y) {
|
||||
(ProjectionElem::Deref, ProjectionElem::Deref) => (),
|
||||
(ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (),
|
||||
(ProjectionElem::ClosureField(it), ProjectionElem::ClosureField(y))
|
||||
(ProjectionElem::Deref, HirPlaceProjection::Deref) => (),
|
||||
(ProjectionElem::Field(Either::Left(it)), HirPlaceProjection::Field(y))
|
||||
if it == y => {}
|
||||
(
|
||||
ProjectionElem::Field(Either::Right(it)),
|
||||
HirPlaceProjection::TupleField(y),
|
||||
) if it.index == *y => (),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
//! Definition of `SolverDefId`
|
||||
|
||||
use hir_def::{
|
||||
AdtId, AttrDefId, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
|
||||
GeneralConstId, GenericDefId, HasModule, ImplId, ModuleId, StaticId, StructId, TraitId,
|
||||
TypeAliasId, UnionId, db::DefDatabase,
|
||||
AdtId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, EnumId,
|
||||
EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId,
|
||||
TypeAliasId, UnionId,
|
||||
};
|
||||
use rustc_type_ir::inherent;
|
||||
use stdx::impl_from;
|
||||
|
|
@ -24,6 +24,7 @@ pub enum SolverDefId {
|
|||
ConstId(ConstId),
|
||||
FunctionId(FunctionId),
|
||||
ImplId(ImplId),
|
||||
BuiltinDeriveImplId(BuiltinDeriveImplId),
|
||||
StaticId(StaticId),
|
||||
TraitId(TraitId),
|
||||
TypeAliasId(TypeAliasId),
|
||||
|
|
@ -57,6 +58,7 @@ impl std::fmt::Debug for SolverDefId {
|
|||
f.debug_tuple("FunctionId").field(&db.function_signature(id).name.as_str()).finish()
|
||||
}
|
||||
SolverDefId::ImplId(id) => f.debug_tuple("ImplId").field(&id).finish(),
|
||||
SolverDefId::BuiltinDeriveImplId(id) => f.debug_tuple("ImplId").field(&id).finish(),
|
||||
SolverDefId::StaticId(id) => {
|
||||
f.debug_tuple("StaticId").field(&db.static_signature(id).name.as_str()).finish()
|
||||
}
|
||||
|
|
@ -108,6 +110,7 @@ impl_from!(
|
|||
ConstId,
|
||||
FunctionId,
|
||||
ImplId,
|
||||
BuiltinDeriveImplId,
|
||||
StaticId,
|
||||
TraitId,
|
||||
TypeAliasId,
|
||||
|
|
@ -170,7 +173,8 @@ impl TryFrom<SolverDefId> for AttrDefId {
|
|||
SolverDefId::EnumVariantId(it) => Ok(it.into()),
|
||||
SolverDefId::Ctor(Ctor::Struct(it)) => Ok(it.into()),
|
||||
SolverDefId::Ctor(Ctor::Enum(it)) => Ok(it.into()),
|
||||
SolverDefId::InternedClosureId(_)
|
||||
SolverDefId::BuiltinDeriveImplId(_)
|
||||
| SolverDefId::InternedClosureId(_)
|
||||
| SolverDefId::InternedCoroutineId(_)
|
||||
| SolverDefId::InternedOpaqueTyId(_) => Err(()),
|
||||
}
|
||||
|
|
@ -191,6 +195,7 @@ impl TryFrom<SolverDefId> for DefWithBodyId {
|
|||
| SolverDefId::TraitId(_)
|
||||
| SolverDefId::TypeAliasId(_)
|
||||
| SolverDefId::ImplId(_)
|
||||
| SolverDefId::BuiltinDeriveImplId(_)
|
||||
| SolverDefId::InternedClosureId(_)
|
||||
| SolverDefId::InternedCoroutineId(_)
|
||||
| SolverDefId::Ctor(Ctor::Struct(_))
|
||||
|
|
@ -216,6 +221,7 @@ impl TryFrom<SolverDefId> for GenericDefId {
|
|||
| SolverDefId::InternedCoroutineId(_)
|
||||
| SolverDefId::InternedOpaqueTyId(_)
|
||||
| SolverDefId::EnumVariantId(_)
|
||||
| SolverDefId::BuiltinDeriveImplId(_)
|
||||
| SolverDefId::Ctor(_) => return Err(()),
|
||||
})
|
||||
}
|
||||
|
|
@ -241,28 +247,6 @@ impl SolverDefId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasModule for SolverDefId {
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
match *self {
|
||||
SolverDefId::AdtId(id) => id.module(db),
|
||||
SolverDefId::ConstId(id) => id.module(db),
|
||||
SolverDefId::FunctionId(id) => id.module(db),
|
||||
SolverDefId::ImplId(id) => id.module(db),
|
||||
SolverDefId::StaticId(id) => id.module(db),
|
||||
SolverDefId::TraitId(id) => id.module(db),
|
||||
SolverDefId::TypeAliasId(id) => id.module(db),
|
||||
SolverDefId::InternedClosureId(id) => id.loc(db).0.module(db),
|
||||
SolverDefId::InternedCoroutineId(id) => id.loc(db).0.module(db),
|
||||
SolverDefId::InternedOpaqueTyId(id) => match id.loc(db) {
|
||||
crate::ImplTraitId::ReturnTypeImplTrait(owner, _) => owner.module(db),
|
||||
crate::ImplTraitId::TypeAliasImplTrait(owner, _) => owner.module(db),
|
||||
},
|
||||
SolverDefId::Ctor(Ctor::Enum(id)) | SolverDefId::EnumVariantId(id) => id.module(db),
|
||||
SolverDefId::Ctor(Ctor::Struct(id)) => id.module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
|
||||
fn as_local(self) -> Option<SolverDefId> {
|
||||
Some(self)
|
||||
|
|
@ -332,7 +316,6 @@ declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId);
|
|||
declare_id_wrapper!(ClosureIdWrapper, InternedClosureId);
|
||||
declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId);
|
||||
declare_id_wrapper!(AdtIdWrapper, AdtId);
|
||||
declare_id_wrapper!(ImplIdWrapper, ImplId);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct GeneralConstIdWrapper(pub GeneralConstId);
|
||||
|
|
@ -433,3 +416,40 @@ impl<'db> inherent::DefId<DbInterner<'db>> for CallableIdWrapper {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum AnyImplId {
|
||||
ImplId(ImplId),
|
||||
BuiltinDeriveImplId(BuiltinDeriveImplId),
|
||||
}
|
||||
|
||||
impl_from!(ImplId, BuiltinDeriveImplId for AnyImplId);
|
||||
|
||||
impl From<AnyImplId> for SolverDefId {
|
||||
#[inline]
|
||||
fn from(value: AnyImplId) -> SolverDefId {
|
||||
match value {
|
||||
AnyImplId::ImplId(it) => it.into(),
|
||||
AnyImplId::BuiltinDeriveImplId(it) => it.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryFrom<SolverDefId> for AnyImplId {
|
||||
type Error = ();
|
||||
#[inline]
|
||||
fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
SolverDefId::ImplId(it) => Ok(it.into()),
|
||||
SolverDefId::BuiltinDeriveImplId(it) => Ok(it.into()),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'db> inherent::DefId<DbInterner<'db>> for AnyImplId {
|
||||
fn as_local(self) -> Option<SolverDefId> {
|
||||
Some(self.into())
|
||||
}
|
||||
fn is_local(self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use rustc_type_ir::{solve::GoalSource, solve::inspect::GoalEvaluation};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::next_solver::infer::InferCtxt;
|
||||
use crate::next_solver::inspect::{InspectCandidate, InspectGoal};
|
||||
use crate::next_solver::{AnyImplId, infer::InferCtxt};
|
||||
use crate::next_solver::{DbInterner, Span};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -76,14 +76,31 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> {
|
|||
use rustc_type_ir::solve::inspect::ProbeKind;
|
||||
match candidate.kind() {
|
||||
ProbeKind::TraitCandidate { source, .. } => {
|
||||
use hir_def::{Lookup, src::HasSource};
|
||||
use rustc_type_ir::solve::CandidateSource;
|
||||
let db = self.infcx.interner.db;
|
||||
match source {
|
||||
CandidateSource::Impl(impl_def_id) => {
|
||||
use hir_def::{Lookup, src::HasSource};
|
||||
let db = self.infcx.interner.db;
|
||||
let impl_src = impl_def_id.0.lookup(db).source(db);
|
||||
Some(impl_src.value.to_string())
|
||||
}
|
||||
CandidateSource::Impl(impl_def_id) => match impl_def_id {
|
||||
AnyImplId::ImplId(impl_def_id) => {
|
||||
let impl_src = impl_def_id.lookup(db).source(db);
|
||||
Some(impl_src.value.to_string())
|
||||
}
|
||||
AnyImplId::BuiltinDeriveImplId(impl_id) => {
|
||||
let impl_loc = impl_id.loc(db);
|
||||
let adt_src = match impl_loc.adt {
|
||||
hir_def::AdtId::StructId(adt) => {
|
||||
adt.loc(db).source(db).value.to_string()
|
||||
}
|
||||
hir_def::AdtId::UnionId(adt) => {
|
||||
adt.loc(db).source(db).value.to_string()
|
||||
}
|
||||
hir_def::AdtId::EnumId(adt) => {
|
||||
adt.loc(db).source(db).value.to_string()
|
||||
}
|
||||
};
|
||||
Some(format!("#[derive(${})]\n{}", impl_loc.trait_.name(), adt_src))
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull};
|
|||
use hir_def::{GenericDefId, GenericParamId};
|
||||
use intern::InternedRef;
|
||||
use rustc_type_ir::{
|
||||
ClosureArgs, ConstVid, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder, FnSigTys,
|
||||
GenericTypeVisitable, Interner, TyKind, TyVid, TypeFoldable, TypeFolder, TypeVisitable,
|
||||
TypeVisitor, Variance,
|
||||
ClosureArgs, ConstVid, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder,
|
||||
GenericTypeVisitable, Interner, TyVid, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
|
||||
Variance,
|
||||
inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _},
|
||||
relate::{Relate, VarianceDiagInfo},
|
||||
walk::TypeWalker,
|
||||
|
|
@ -21,12 +21,11 @@ use rustc_type_ir::{
|
|||
use smallvec::SmallVec;
|
||||
|
||||
use crate::next_solver::{
|
||||
ConstInterned, PolyFnSig, RegionInterned, TyInterned, impl_foldable_for_interned_slice,
|
||||
interned_slice,
|
||||
ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice,
|
||||
};
|
||||
|
||||
use super::{
|
||||
Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys,
|
||||
Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty,
|
||||
generics::Generics,
|
||||
};
|
||||
|
||||
|
|
@ -566,33 +565,6 @@ impl<'db> GenericArgs<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn closure_sig_untupled(self) -> PolyFnSig<'db> {
|
||||
let TyKind::FnPtr(inputs_and_output, hdr) =
|
||||
self.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.kind()
|
||||
else {
|
||||
unreachable!("not a function pointer")
|
||||
};
|
||||
inputs_and_output.with(hdr)
|
||||
}
|
||||
|
||||
/// A "sensible" `.split_closure_args()`, where the arguments are not in a tuple.
|
||||
pub fn split_closure_args_untupled(self) -> rustc_type_ir::ClosureArgsParts<DbInterner<'db>> {
|
||||
// FIXME: should use `ClosureSubst` when possible
|
||||
match self.as_slice() {
|
||||
[parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => {
|
||||
rustc_type_ir::ClosureArgsParts {
|
||||
parent_args,
|
||||
closure_sig_as_fn_ptr_ty: sig_ty.expect_ty(),
|
||||
closure_kind_ty: closure_kind_ty.expect_ty(),
|
||||
tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!("unexpected closure sig");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn types(self) -> impl Iterator<Item = Ty<'db>> {
|
||||
self.iter().filter_map(|it| it.as_type())
|
||||
}
|
||||
|
|
@ -688,27 +660,9 @@ impl<'db> rustc_type_ir::inherent::GenericArgs<DbInterner<'db>> for GenericArgs<
|
|||
// FIXME: should use `ClosureSubst` when possible
|
||||
match self.as_slice() {
|
||||
[parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => {
|
||||
let interner = DbInterner::conjure();
|
||||
// This is stupid, but the next solver expects the first input to actually be a tuple
|
||||
let sig_ty = match sig_ty.expect_ty().kind() {
|
||||
TyKind::FnPtr(sig_tys, header) => Ty::new(
|
||||
interner,
|
||||
TyKind::FnPtr(
|
||||
sig_tys.map_bound(|s| {
|
||||
let inputs = Ty::new_tup(interner, s.inputs());
|
||||
let output = s.output();
|
||||
FnSigTys {
|
||||
inputs_and_output: Tys::new_from_slice(&[inputs, output]),
|
||||
}
|
||||
}),
|
||||
header,
|
||||
),
|
||||
),
|
||||
_ => unreachable!("sig_ty should be last"),
|
||||
};
|
||||
rustc_type_ir::ClosureArgsParts {
|
||||
parent_args,
|
||||
closure_sig_as_fn_ptr_ty: sig_ty,
|
||||
closure_sig_as_fn_ptr_ty: sig_ty.expect_ty(),
|
||||
closure_kind_ty: closure_kind_ty.expect_ty(),
|
||||
tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@ use hir_def::{
|
|||
ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
|
||||
hir::generics::{GenericParams, TypeOrConstParamData},
|
||||
};
|
||||
use rustc_type_ir::inherent::GenericsOf;
|
||||
|
||||
use crate::{db::HirDatabase, generics::parent_generic_def};
|
||||
use crate::generics::parent_generic_def;
|
||||
|
||||
use super::SolverDefId;
|
||||
|
||||
use super::DbInterner;
|
||||
|
||||
pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
|
||||
pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics {
|
||||
let mk_lt = |parent, index, local_id| {
|
||||
let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id });
|
||||
GenericParamDef { index, id }
|
||||
|
|
@ -50,6 +51,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
|
|||
result
|
||||
};
|
||||
|
||||
let db = interner.db;
|
||||
let (parent, own_params) = match (def.try_into(), def) {
|
||||
(Ok(def), _) => (
|
||||
parent_generic_def(db, def),
|
||||
|
|
@ -66,9 +68,12 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
|
|||
}
|
||||
}
|
||||
}
|
||||
(_, SolverDefId::BuiltinDeriveImplId(id)) => {
|
||||
return crate::builtin_derive::generics_of(interner, id);
|
||||
}
|
||||
_ => panic!("No generics for {def:?}"),
|
||||
};
|
||||
let parent_generics = parent.map(|def| Box::new(generics(db, def.into())));
|
||||
let parent_generics = parent.map(|def| Box::new(generics(interner, def.into())));
|
||||
|
||||
Generics {
|
||||
parent,
|
||||
|
|
@ -84,6 +89,13 @@ pub struct Generics {
|
|||
pub own_params: Vec<GenericParamDef>,
|
||||
}
|
||||
|
||||
impl Generics {
|
||||
pub(crate) fn push_param(&mut self, id: GenericParamId) {
|
||||
let index = self.count() as u32;
|
||||
self.own_params.push(GenericParamDef { index, id });
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GenericParamDef {
|
||||
index: u32,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use hir_def::{ImplId, TraitId};
|
||||
use hir_def::TraitId;
|
||||
use macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_type_ir::{
|
||||
Interner,
|
||||
|
|
@ -12,7 +12,7 @@ use rustc_type_ir::{
|
|||
use crate::{
|
||||
db::InternedOpaqueTyId,
|
||||
next_solver::{
|
||||
Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
|
||||
AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
|
||||
infer::{
|
||||
InferCtxt,
|
||||
select::EvaluationResult::*,
|
||||
|
|
@ -249,7 +249,7 @@ impl<'db, N> ImplSource<'db, N> {
|
|||
pub(crate) struct ImplSourceUserDefinedData<'db, N> {
|
||||
#[type_visitable(ignore)]
|
||||
#[type_foldable(identity)]
|
||||
pub(crate) impl_def_id: ImplId,
|
||||
pub(crate) impl_def_id: AnyImplId,
|
||||
pub(crate) args: GenericArgs<'db>,
|
||||
pub(crate) nested: Vec<N>,
|
||||
}
|
||||
|
|
@ -395,7 +395,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>>
|
|||
// FIXME: Remove this in favor of storing this in the tree
|
||||
// For impl candidates, we do the rematch manually to compute the args.
|
||||
ImplSource::UserDefined(ImplSourceUserDefinedData {
|
||||
impl_def_id: impl_def_id.0,
|
||||
impl_def_id,
|
||||
args: cand.instantiate_impl_args(),
|
||||
nested,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ use crate::{
|
|||
lower::GenericPredicates,
|
||||
method_resolution::TraitImpls,
|
||||
next_solver::{
|
||||
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
|
||||
CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, ImplIdWrapper,
|
||||
OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds,
|
||||
TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds,
|
||||
AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
|
||||
CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey,
|
||||
RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper,
|
||||
TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -1020,7 +1020,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
type CoroutineClosureId = CoroutineIdWrapper;
|
||||
type CoroutineId = CoroutineIdWrapper;
|
||||
type AdtId = AdtIdWrapper;
|
||||
type ImplId = ImplIdWrapper;
|
||||
type ImplId = AnyImplId;
|
||||
type UnevaluatedConstId = GeneralConstIdWrapper;
|
||||
type Span = Span;
|
||||
|
||||
|
|
@ -1164,7 +1164,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
}
|
||||
|
||||
fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf {
|
||||
generics(self.db(), def_id)
|
||||
generics(self, def_id)
|
||||
}
|
||||
|
||||
fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
|
||||
|
|
@ -1190,6 +1190,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
| SolverDefId::TraitId(_)
|
||||
| SolverDefId::TypeAliasId(_)
|
||||
| SolverDefId::ImplId(_)
|
||||
| SolverDefId::BuiltinDeriveImplId(_)
|
||||
| SolverDefId::InternedClosureId(_)
|
||||
| SolverDefId::InternedCoroutineId(_) => {
|
||||
return VariancesOf::empty(self);
|
||||
|
|
@ -1327,6 +1328,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
| SolverDefId::AdtId(_)
|
||||
| SolverDefId::TraitId(_)
|
||||
| SolverDefId::ImplId(_)
|
||||
| SolverDefId::BuiltinDeriveImplId(_)
|
||||
| SolverDefId::EnumVariantId(..)
|
||||
| SolverDefId::Ctor(..)
|
||||
| SolverDefId::InternedOpaqueTyId(..) => panic!(),
|
||||
|
|
@ -1445,8 +1447,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
self,
|
||||
def_id: Self::DefId,
|
||||
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
|
||||
GenericPredicates::query_all(self.db, def_id.try_into().unwrap())
|
||||
.map_bound(|it| it.iter().copied())
|
||||
predicates_of(self.db, def_id).all_predicates().map_bound(|it| it.iter().copied())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), ret)]
|
||||
|
|
@ -1454,8 +1455,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
self,
|
||||
def_id: Self::DefId,
|
||||
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
|
||||
GenericPredicates::query_own(self.db, def_id.try_into().unwrap())
|
||||
.map_bound(|it| it.iter().copied())
|
||||
predicates_of(self.db, def_id).own_predicates().map_bound(|it| it.iter().copied())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), ret)]
|
||||
|
|
@ -1500,32 +1500,30 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
GenericPredicates::query_explicit(self.db, def_id.try_into().unwrap()).map_bound(
|
||||
|predicates| {
|
||||
predicates
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|p| match p.kind().skip_binder() {
|
||||
ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
|
||||
ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
|
||||
ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
|
||||
ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
|
||||
// FIXME: Not sure is this correct to allow other clauses but we might replace
|
||||
// `generic_predicates_ns` query here with something closer to rustc's
|
||||
// `implied_bounds_with_filter`, which is more granular lowering than this
|
||||
// "lower at once and then filter" implementation.
|
||||
_ => true,
|
||||
})
|
||||
.map(|p| (p, Span::dummy()))
|
||||
},
|
||||
)
|
||||
predicates_of(self.db, def_id).explicit_predicates().map_bound(|predicates| {
|
||||
predicates
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|p| match p.kind().skip_binder() {
|
||||
ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
|
||||
ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
|
||||
ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
|
||||
ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
|
||||
// FIXME: Not sure is this correct to allow other clauses but we might replace
|
||||
// `generic_predicates_ns` query here with something closer to rustc's
|
||||
// `implied_bounds_with_filter`, which is more granular lowering than this
|
||||
// "lower at once and then filter" implementation.
|
||||
_ => true,
|
||||
})
|
||||
.map(|p| (p, Span::dummy()))
|
||||
})
|
||||
}
|
||||
|
||||
fn impl_super_outlives(
|
||||
self,
|
||||
impl_id: Self::ImplId,
|
||||
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
|
||||
let trait_ref = self.db().impl_trait(impl_id.0).expect("expected an impl of trait");
|
||||
let trait_ref = self.impl_trait_ref(impl_id);
|
||||
trait_ref.map_bound(|trait_ref| {
|
||||
let clause: Clause<'_> = trait_ref.upcast(self);
|
||||
elaborate(self, [clause]).filter(|clause| {
|
||||
|
|
@ -1790,6 +1788,7 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
SolverDefId::ConstId(_)
|
||||
| SolverDefId::FunctionId(_)
|
||||
| SolverDefId::ImplId(_)
|
||||
| SolverDefId::BuiltinDeriveImplId(_)
|
||||
| SolverDefId::StaticId(_)
|
||||
| SolverDefId::InternedClosureId(_)
|
||||
| SolverDefId::InternedCoroutineId(_)
|
||||
|
|
@ -1805,7 +1804,12 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
type_block,
|
||||
trait_block,
|
||||
&mut |impls| {
|
||||
for &impl_ in impls.for_trait_and_self_ty(trait_def_id.0, &simp) {
|
||||
let (regular_impls, builtin_derive_impls) =
|
||||
impls.for_trait_and_self_ty(trait_def_id.0, &simp);
|
||||
for &impl_ in regular_impls {
|
||||
f(impl_.into());
|
||||
}
|
||||
for &impl_ in builtin_derive_impls {
|
||||
f(impl_.into());
|
||||
}
|
||||
},
|
||||
|
|
@ -1927,7 +1931,10 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
}
|
||||
|
||||
fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool {
|
||||
self.db.impl_signature(impl_def_id.0).is_default()
|
||||
match impl_def_id {
|
||||
AnyImplId::ImplId(impl_id) => self.db.impl_signature(impl_id).is_default(),
|
||||
AnyImplId::BuiltinDeriveImplId(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), ret)]
|
||||
|
|
@ -1935,14 +1942,24 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
self,
|
||||
impl_id: Self::ImplId,
|
||||
) -> EarlyBinder<Self, rustc_type_ir::TraitRef<Self>> {
|
||||
let db = self.db();
|
||||
db.impl_trait(impl_id.0)
|
||||
// ImplIds for impls where the trait ref can't be resolved should never reach trait solving
|
||||
.expect("invalid impl passed to trait solver")
|
||||
match impl_id {
|
||||
AnyImplId::ImplId(impl_id) => {
|
||||
let db = self.db();
|
||||
db.impl_trait(impl_id)
|
||||
// ImplIds for impls where the trait ref can't be resolved should never reach trait solving
|
||||
.expect("invalid impl passed to trait solver")
|
||||
}
|
||||
AnyImplId::BuiltinDeriveImplId(impl_id) => {
|
||||
crate::builtin_derive::impl_trait(self, impl_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_polarity(self, impl_id: Self::ImplId) -> rustc_type_ir::ImplPolarity {
|
||||
let impl_data = self.db().impl_signature(impl_id.0);
|
||||
let AnyImplId::ImplId(impl_id) = impl_id else {
|
||||
return ImplPolarity::Positive;
|
||||
};
|
||||
let impl_data = self.db().impl_signature(impl_id);
|
||||
if impl_data.flags.contains(ImplFlags::NEGATIVE) {
|
||||
ImplPolarity::Negative
|
||||
} else {
|
||||
|
|
@ -2230,11 +2247,13 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
specializing_impl_def_id: Self::ImplId,
|
||||
parent_impl_def_id: Self::ImplId,
|
||||
) -> bool {
|
||||
crate::specialization::specializes(
|
||||
self.db,
|
||||
specializing_impl_def_id.0,
|
||||
parent_impl_def_id.0,
|
||||
)
|
||||
let (AnyImplId::ImplId(specializing_impl_def_id), AnyImplId::ImplId(parent_impl_def_id)) =
|
||||
(specializing_impl_def_id, parent_impl_def_id)
|
||||
else {
|
||||
// No builtin derive allow specialization currently.
|
||||
return false;
|
||||
};
|
||||
crate::specialization::specializes(self.db, specializing_impl_def_id, parent_impl_def_id)
|
||||
}
|
||||
|
||||
fn next_trait_solver_globally(self) -> bool {
|
||||
|
|
@ -2349,6 +2368,14 @@ impl<'db> DbInterner<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates {
|
||||
if let SolverDefId::BuiltinDeriveImplId(impl_) = def_id {
|
||||
crate::builtin_derive::predicates(db, impl_)
|
||||
} else {
|
||||
GenericPredicates::query(db, def_id.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! TrivialTypeTraversalImpls {
|
||||
($($ty:ty,)+) => {
|
||||
$(
|
||||
|
|
@ -2396,7 +2423,7 @@ TrivialTypeTraversalImpls! {
|
|||
ClosureIdWrapper,
|
||||
CoroutineIdWrapper,
|
||||
AdtIdWrapper,
|
||||
ImplIdWrapper,
|
||||
AnyImplId,
|
||||
GeneralConstIdWrapper,
|
||||
Safety,
|
||||
FnAbi,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rustc_type_ir::{
|
|||
use tracing::debug;
|
||||
|
||||
use crate::next_solver::{
|
||||
AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
|
||||
AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
|
||||
ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
|
||||
util::sizedness_fast_path,
|
||||
};
|
||||
|
|
@ -174,9 +174,13 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
|||
&self,
|
||||
_goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
|
||||
trait_assoc_def_id: SolverDefId,
|
||||
impl_id: ImplIdWrapper,
|
||||
impl_id: AnyImplId,
|
||||
) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
|
||||
let impl_items = impl_id.0.impl_items(self.0.interner.db());
|
||||
let AnyImplId::ImplId(impl_id) = impl_id else {
|
||||
// Builtin derive traits don't have type/consts assoc items.
|
||||
return Ok(None);
|
||||
};
|
||||
let impl_items = impl_id.impl_items(self.0.interner.db());
|
||||
let id =
|
||||
match trait_assoc_def_id {
|
||||
SolverDefId::TypeAliasId(trait_assoc_id) => {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use rustc_type_ir::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
FnAbi,
|
||||
db::{HirDatabase, InternedCoroutine},
|
||||
lower::GenericPredicates,
|
||||
next_solver::{
|
||||
|
|
@ -495,10 +496,9 @@ impl<'db> Ty<'db> {
|
|||
Some(interner.fn_sig(callable).instantiate(interner, args))
|
||||
}
|
||||
TyKind::FnPtr(sig, hdr) => Some(sig.with(hdr)),
|
||||
TyKind::Closure(_, closure_args) => closure_args
|
||||
.split_closure_args_untupled()
|
||||
.closure_sig_as_fn_ptr_ty
|
||||
.callable_sig(interner),
|
||||
TyKind::Closure(_, closure_args) => {
|
||||
Some(interner.signature_unclosure(closure_args.as_closure().sig(), Safety::Safe))
|
||||
}
|
||||
TyKind::CoroutineClosure(coroutine_id, args) => {
|
||||
Some(args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
|
||||
let unit_ty = Ty::new_unit(interner);
|
||||
|
|
@ -1426,3 +1426,22 @@ impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderTy {
|
|||
Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> DbInterner<'db> {
|
||||
/// Given a closure signature, returns an equivalent fn signature. Detuples
|
||||
/// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then
|
||||
/// you would get a `fn(u32, i32)`.
|
||||
/// `unsafety` determines the unsafety of the fn signature. If you pass
|
||||
/// `Safety::Unsafe` in the previous example, then you would get
|
||||
/// an `unsafe fn (u32, i32)`.
|
||||
/// It cannot convert a closure that requires unsafe.
|
||||
pub fn signature_unclosure(self, sig: PolyFnSig<'db>, safety: Safety) -> PolyFnSig<'db> {
|
||||
sig.map_bound(|s| {
|
||||
let params = match s.inputs()[0].kind() {
|
||||
TyKind::Tuple(params) => params,
|
||||
_ => panic!(),
|
||||
};
|
||||
self.mk_fn_sig(params, s.output(), s.c_variadic, safety, FnAbi::Rust)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ impl Default for TestDB {
|
|||
this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
|
||||
// This needs to be here otherwise `CrateGraphBuilder` panics.
|
||||
this.set_all_crates(Arc::new(Box::new([])));
|
||||
_ = base_db::LibraryRoots::builder(Default::default())
|
||||
.durability(Durability::MEDIUM)
|
||||
.new(&this);
|
||||
_ = base_db::LocalRoots::builder(Default::default())
|
||||
.durability(Durability::MEDIUM)
|
||||
.new(&this);
|
||||
CrateGraphBuilder::default().set_in_db(&mut this);
|
||||
this
|
||||
}
|
||||
|
|
|
|||
|
|
@ -503,3 +503,28 @@ fn main() {
|
|||
expect!["73..149;37..38;103..104 ByValue b Option<Box>"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_needs_to_be_normalized() {
|
||||
check_closure_captures(
|
||||
r#"
|
||||
//- minicore:copy
|
||||
trait Trait {
|
||||
type Associated;
|
||||
}
|
||||
struct A;
|
||||
struct B { x: i32 }
|
||||
impl Trait for A {
|
||||
type Associated = B;
|
||||
}
|
||||
struct C { b: <A as Trait>::Associated }
|
||||
fn main() {
|
||||
let c: C = C { b: B { x: 1 } };
|
||||
let closure = || {
|
||||
let _move = c.b.x;
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect!["220..257;174..175;245..250 ByRef(Shared) c.b.x &'? i32"],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,6 +243,10 @@ $0",
|
|||
"parse_shim",
|
||||
"real_span_map_shim",
|
||||
"TraitImpls::for_crate_",
|
||||
"lang_items",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -279,6 +283,10 @@ pub struct NewStruct {
|
|||
"real_span_map_shim",
|
||||
"crate_local_def_map",
|
||||
"TraitImpls::for_crate_",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -314,6 +322,10 @@ $0",
|
|||
"parse_shim",
|
||||
"real_span_map_shim",
|
||||
"TraitImpls::for_crate_",
|
||||
"lang_items",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -351,6 +363,13 @@ pub enum SomeEnum {
|
|||
"real_span_map_shim",
|
||||
"crate_local_def_map",
|
||||
"TraitImpls::for_crate_",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"EnumVariants::of_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -386,6 +405,10 @@ $0",
|
|||
"parse_shim",
|
||||
"real_span_map_shim",
|
||||
"TraitImpls::for_crate_",
|
||||
"lang_items",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -420,6 +443,9 @@ fn bar() -> f32 {
|
|||
"real_span_map_shim",
|
||||
"crate_local_def_map",
|
||||
"TraitImpls::for_crate_",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -459,6 +485,11 @@ $0",
|
|||
"parse_shim",
|
||||
"real_span_map_shim",
|
||||
"TraitImpls::for_crate_",
|
||||
"lang_items",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -501,17 +532,16 @@ impl SomeStruct {
|
|||
"real_span_map_shim",
|
||||
"crate_local_def_map",
|
||||
"TraitImpls::for_crate_",
|
||||
"AttrFlags::query_",
|
||||
"impl_trait_with_diagnostics_query",
|
||||
"impl_signature_shim",
|
||||
"impl_signature_with_source_map_shim",
|
||||
"lang_items",
|
||||
"crate_lang_items",
|
||||
"AttrFlags::query_",
|
||||
"ImplItems::of_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"AttrFlags::query_",
|
||||
"impl_trait_with_diagnostics_query",
|
||||
"impl_signature_shim",
|
||||
"impl_signature_with_source_map_shim",
|
||||
"impl_self_ty_with_diagnostics_query",
|
||||
"struct_signature_shim",
|
||||
"struct_signature_with_source_map_shim",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use expect_test::expect;
|
||||
|
||||
use crate::tests::check_infer_with_mismatches;
|
||||
|
||||
use super::{check, check_infer, check_no_mismatches, check_types};
|
||||
|
||||
#[test]
|
||||
|
|
@ -3956,3 +3958,24 @@ fn bar() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_coerce_capturing_closure_to_fn_ptr() {
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
fn foo() {
|
||||
let a = 1;
|
||||
let _: fn() -> i32 = || a;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
9..58 '{ ...| a; }': ()
|
||||
19..20 'a': i32
|
||||
23..24 '1': i32
|
||||
34..35 '_': fn() -> i32
|
||||
51..55 '|| a': impl Fn() -> i32
|
||||
54..55 'a': i32
|
||||
51..55: expected fn() -> i32, got impl Fn() -> i32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -851,7 +851,7 @@ struct S;
|
|||
trait Trait<T> {}
|
||||
impl Trait<&str> for S {}
|
||||
|
||||
struct O<T>;
|
||||
struct O<T>(T);
|
||||
impl<U, T: Trait<U>> O<T> {
|
||||
fn foo(&self) -> U { loop {} }
|
||||
}
|
||||
|
|
@ -1492,7 +1492,7 @@ fn dyn_trait_in_impl() {
|
|||
trait Trait<T, U> {
|
||||
fn foo(&self) -> (T, U);
|
||||
}
|
||||
struct S<T, U> {}
|
||||
struct S<T, U>(T, U);
|
||||
impl<T, U> S<T, U> {
|
||||
fn bar(&self) -> &dyn Trait<T, U> { loop {} }
|
||||
}
|
||||
|
|
@ -1506,16 +1506,16 @@ fn test(s: S<u32, i32>) {
|
|||
}"#,
|
||||
expect![[r#"
|
||||
32..36 'self': &'? Self
|
||||
102..106 'self': &'? S<T, U>
|
||||
128..139 '{ loop {} }': &'? (dyn Trait<T, U> + 'static)
|
||||
130..137 'loop {}': !
|
||||
135..137 '{}': ()
|
||||
175..179 'self': &'? Self
|
||||
251..252 's': S<u32, i32>
|
||||
267..289 '{ ...z(); }': ()
|
||||
273..274 's': S<u32, i32>
|
||||
273..280 's.bar()': &'? (dyn Trait<u32, i32> + 'static)
|
||||
273..286 's.bar().baz()': (u32, i32)
|
||||
106..110 'self': &'? S<T, U>
|
||||
132..143 '{ loop {} }': &'? (dyn Trait<T, U> + 'static)
|
||||
134..141 'loop {}': !
|
||||
139..141 '{}': ()
|
||||
179..183 'self': &'? Self
|
||||
255..256 's': S<u32, i32>
|
||||
271..293 '{ ...z(); }': ()
|
||||
277..278 's': S<u32, i32>
|
||||
277..284 's.bar()': &'? (dyn Trait<u32, i32> + 'static)
|
||||
277..290 's.bar().baz()': (u32, i32)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
319
src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs
Normal file
319
src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
//! A simple query to collect tall locals (upvars) a closure use.
|
||||
|
||||
use hir_def::{
|
||||
DefWithBodyId,
|
||||
expr_store::{Body, path::Path},
|
||||
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
|
||||
resolver::{HasResolver, Resolver, ValueNs},
|
||||
};
|
||||
use hir_expand::mod_path::PathKind;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
// Kept sorted.
|
||||
pub struct Upvars(Box<[BindingId]>);
|
||||
|
||||
impl Upvars {
|
||||
fn new(upvars: &FxHashSet<BindingId>) -> Upvars {
|
||||
let mut upvars = upvars.iter().copied().collect::<Box<[_]>>();
|
||||
upvars.sort_unstable();
|
||||
Upvars(upvars)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, local: BindingId) -> bool {
|
||||
self.0.binary_search(&local).is_ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> impl ExactSizeIterator<Item = BindingId> {
|
||||
self.0.iter().copied()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a map from `Expr::Closure` to its upvars.
|
||||
#[salsa::tracked(returns(as_deref))]
|
||||
pub fn upvars_mentioned(
|
||||
db: &dyn HirDatabase,
|
||||
owner: DefWithBodyId,
|
||||
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
|
||||
let body = db.body(owner);
|
||||
let mut resolver = owner.resolver(db);
|
||||
let mut result = FxHashMap::default();
|
||||
handle_expr_outside_closure(db, &mut resolver, owner, &body, body.body_expr, &mut result);
|
||||
return if result.is_empty() {
|
||||
None
|
||||
} else {
|
||||
result.shrink_to_fit();
|
||||
Some(Box::new(result))
|
||||
};
|
||||
|
||||
fn handle_expr_outside_closure<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
resolver: &mut Resolver<'db>,
|
||||
owner: DefWithBodyId,
|
||||
body: &Body,
|
||||
expr: ExprId,
|
||||
closures_map: &mut FxHashMap<ExprId, Upvars>,
|
||||
) {
|
||||
match &body[expr] {
|
||||
&Expr::Closure { body: body_expr, .. } => {
|
||||
let mut upvars = FxHashSet::default();
|
||||
handle_expr_inside_closure(
|
||||
db,
|
||||
resolver,
|
||||
owner,
|
||||
body,
|
||||
expr,
|
||||
body_expr,
|
||||
&mut upvars,
|
||||
closures_map,
|
||||
);
|
||||
if !upvars.is_empty() {
|
||||
closures_map.insert(expr, Upvars::new(&upvars));
|
||||
}
|
||||
}
|
||||
_ => body.walk_child_exprs(expr, |expr| {
|
||||
handle_expr_outside_closure(db, resolver, owner, body, expr, closures_map)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_expr_inside_closure<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
resolver: &mut Resolver<'db>,
|
||||
owner: DefWithBodyId,
|
||||
body: &Body,
|
||||
current_closure: ExprId,
|
||||
expr: ExprId,
|
||||
upvars: &mut FxHashSet<BindingId>,
|
||||
closures_map: &mut FxHashMap<ExprId, Upvars>,
|
||||
) {
|
||||
match &body[expr] {
|
||||
Expr::Path(path) => {
|
||||
resolve_maybe_upvar(
|
||||
db,
|
||||
resolver,
|
||||
owner,
|
||||
body,
|
||||
current_closure,
|
||||
expr,
|
||||
expr.into(),
|
||||
upvars,
|
||||
path,
|
||||
);
|
||||
}
|
||||
&Expr::Assignment { target, .. } => {
|
||||
body.walk_pats(target, &mut |pat| {
|
||||
let Pat::Path(path) = &body[pat] else { return };
|
||||
resolve_maybe_upvar(
|
||||
db,
|
||||
resolver,
|
||||
owner,
|
||||
body,
|
||||
current_closure,
|
||||
expr,
|
||||
pat.into(),
|
||||
upvars,
|
||||
path,
|
||||
);
|
||||
});
|
||||
}
|
||||
&Expr::Closure { body: body_expr, .. } => {
|
||||
let mut closure_upvars = FxHashSet::default();
|
||||
handle_expr_inside_closure(
|
||||
db,
|
||||
resolver,
|
||||
owner,
|
||||
body,
|
||||
expr,
|
||||
body_expr,
|
||||
&mut closure_upvars,
|
||||
closures_map,
|
||||
);
|
||||
if !closure_upvars.is_empty() {
|
||||
closures_map.insert(expr, Upvars::new(&closure_upvars));
|
||||
// All nested closure's upvars are also upvars of the parent closure.
|
||||
upvars.extend(
|
||||
closure_upvars
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|local| body.binding_owner(*local) != Some(current_closure)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
body.walk_child_exprs(expr, |expr| {
|
||||
handle_expr_inside_closure(
|
||||
db,
|
||||
resolver,
|
||||
owner,
|
||||
body,
|
||||
current_closure,
|
||||
expr,
|
||||
upvars,
|
||||
closures_map,
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_maybe_upvar<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
resolver: &mut Resolver<'db>,
|
||||
owner: DefWithBodyId,
|
||||
body: &Body,
|
||||
current_closure: ExprId,
|
||||
expr: ExprId,
|
||||
id: ExprOrPatId,
|
||||
upvars: &mut FxHashSet<BindingId>,
|
||||
path: &Path,
|
||||
) {
|
||||
if let Path::BarePath(mod_path) = path
|
||||
&& matches!(mod_path.kind, PathKind::Plain)
|
||||
&& mod_path.segments().len() == 1
|
||||
{
|
||||
// Could be a variable.
|
||||
let guard = resolver.update_to_inner_scope(db, owner, expr);
|
||||
let resolution =
|
||||
resolver.resolve_path_in_value_ns_fully(db, path, body.expr_or_pat_path_hygiene(id));
|
||||
if let Some(ValueNs::LocalBinding(local)) = resolution
|
||||
&& body.binding_owner(local) != Some(current_closure)
|
||||
{
|
||||
upvars.insert(local);
|
||||
}
|
||||
resolver.reset_to_guard(guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{Expect, expect};
|
||||
use hir_def::{ModuleDefId, db::DefDatabase, nameres::crate_def_map};
|
||||
use itertools::Itertools;
|
||||
use span::Edition;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{test_db::TestDB, upvars::upvars_mentioned};
|
||||
|
||||
#[track_caller]
|
||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
crate::attach_db(&db, || {
|
||||
let def_map = crate_def_map(&db, db.test_crate());
|
||||
let func = def_map
|
||||
.modules()
|
||||
.flat_map(|(_, module)| module.scope.declarations())
|
||||
.filter_map(|decl| match decl {
|
||||
ModuleDefId::FunctionId(func) => Some(func),
|
||||
_ => None,
|
||||
})
|
||||
.exactly_one()
|
||||
.unwrap_or_else(|_| panic!("expected one function"));
|
||||
let (body, source_map) = db.body_with_source_map(func.into());
|
||||
let Some(upvars) = upvars_mentioned(&db, func.into()) else {
|
||||
expectation.assert_eq("");
|
||||
return;
|
||||
};
|
||||
let mut closures = Vec::new();
|
||||
for (&closure, upvars) in upvars {
|
||||
let closure_range = source_map.expr_syntax(closure).unwrap().value.text_range();
|
||||
let upvars = upvars
|
||||
.iter()
|
||||
.map(|local| body[local].name.display(&db, Edition::CURRENT))
|
||||
.join(", ");
|
||||
closures.push((closure_range, upvars));
|
||||
}
|
||||
closures.sort_unstable_by_key(|(range, _)| (range.start(), range.end()));
|
||||
let closures = closures
|
||||
.into_iter()
|
||||
.map(|(range, upvars)| format!("{range:?}: {upvars}"))
|
||||
.join("\n");
|
||||
expectation.assert_eq(&closures);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
check(
|
||||
r#"
|
||||
struct foo;
|
||||
fn foo(param: i32) {
|
||||
let local = "boo";
|
||||
|| { param; foo };
|
||||
|| local;
|
||||
|| { param; local; param; local; };
|
||||
|| 0xDEAFBEAF;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
60..77: param
|
||||
83..91: local
|
||||
97..131: param, local"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
let (a, b);
|
||||
|| {
|
||||
|| a;
|
||||
|| b;
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
31..69: a, b
|
||||
44..48: a
|
||||
58..62: b"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_var() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
let upvar = 1;
|
||||
|closure_param: i32| {
|
||||
let closure_local = closure_param;
|
||||
closure_local + upvar
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect!["34..135: upvar"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_var_nested() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
let a = 1;
|
||||
|b: i32| {
|
||||
|| {
|
||||
let c = 123;
|
||||
a + b + c
|
||||
}
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
30..116: a
|
||||
49..110: a, b"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -36,9 +36,8 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Variances
|
|||
|
||||
#[salsa::tracked(
|
||||
returns(ref),
|
||||
// cycle_fn = crate::variance::variances_of_cycle_fn,
|
||||
// cycle_initial = crate::variance::variances_of_cycle_initial,
|
||||
cycle_result = crate::variance::variances_of_cycle_initial,
|
||||
cycle_fn = crate::variance::variances_of_cycle_fn,
|
||||
cycle_initial = crate::variance::variances_of_cycle_initial,
|
||||
)]
|
||||
fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariancesOf {
|
||||
tracing::debug!("variances_of(def={:?})", def);
|
||||
|
|
@ -64,35 +63,20 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance
|
|||
if count == 0 {
|
||||
return VariancesOf::empty(interner).store();
|
||||
}
|
||||
let mut variances =
|
||||
Context { generics, variances: vec![Variance::Bivariant; count], db }.solve();
|
||||
|
||||
// FIXME(next-solver): This is *not* the correct behavior. I don't know if it has an actual effect,
|
||||
// since bivariance is prohibited in Rust, but rustc definitely does not fallback bivariance.
|
||||
// So why do we do this? Because, with the new solver, the effects of bivariance are catastrophic:
|
||||
// it leads to not relating types properly, and to very, very hard to debug bugs (speaking from experience).
|
||||
// Furthermore, our variance infra is known to not handle cycles properly. Therefore, at least until we fix
|
||||
// cycles, and perhaps forever at least for out tests, not allowing bivariance makes sense.
|
||||
// Why specifically invariance? I don't have a strong reason, mainly that invariance is a stronger relationship
|
||||
// (therefore, less room for mistakes) and that IMO incorrect covariance can be more problematic that incorrect
|
||||
// bivariance, at least while we don't handle lifetimes anyway.
|
||||
for variance in &mut variances {
|
||||
if *variance == Variance::Bivariant {
|
||||
*variance = Variance::Invariant;
|
||||
}
|
||||
}
|
||||
let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve();
|
||||
|
||||
VariancesOf::new_from_slice(&variances).store()
|
||||
}
|
||||
|
||||
// pub(crate) fn variances_of_cycle_fn(
|
||||
// _db: &dyn HirDatabase,
|
||||
// _result: &Option<Arc<[Variance]>>,
|
||||
// _count: u32,
|
||||
// _def: GenericDefId,
|
||||
// ) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
|
||||
// salsa::CycleRecoveryAction::Iterate
|
||||
// }
|
||||
pub(crate) fn variances_of_cycle_fn(
|
||||
_db: &dyn HirDatabase,
|
||||
_: &salsa::Cycle<'_>,
|
||||
_last_provisional_value: &StoredVariancesOf,
|
||||
value: StoredVariancesOf,
|
||||
_def: GenericDefId,
|
||||
) -> StoredVariancesOf {
|
||||
value
|
||||
}
|
||||
|
||||
fn glb(v1: Variance, v2: Variance) -> Variance {
|
||||
// Greatest lower bound of the variance lattice as defined in The Paper:
|
||||
|
|
@ -123,8 +107,7 @@ pub(crate) fn variances_of_cycle_initial(
|
|||
let generics = generics(db, def);
|
||||
let count = generics.len();
|
||||
|
||||
// FIXME(next-solver): Returns `Invariance` and not `Bivariance` here, see the comment in the main query.
|
||||
VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Invariant, count)).store()
|
||||
VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Bivariant, count)).store()
|
||||
}
|
||||
|
||||
struct Context<'db> {
|
||||
|
|
@ -484,8 +467,8 @@ struct Other<'a> {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Hello['a: invariant]
|
||||
Other['a: invariant]
|
||||
Hello['a: bivariant]
|
||||
Other['a: bivariant]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -504,7 +487,7 @@ struct Foo<T: Trait> { //~ ERROR [T: o]
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo[T: invariant]
|
||||
Foo[T: bivariant]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -586,9 +569,9 @@ struct TestBox<U,T:Getter<U>+Setter<U>> { //~ ERROR [U: *, T: +]
|
|||
get[Self: contravariant, T: covariant]
|
||||
get[Self: contravariant, T: contravariant]
|
||||
TestStruct[U: covariant, T: covariant]
|
||||
TestEnum[U: invariant, T: covariant]
|
||||
TestContraStruct[U: invariant, T: covariant]
|
||||
TestBox[U: invariant, T: covariant]
|
||||
TestEnum[U: bivariant, T: covariant]
|
||||
TestContraStruct[U: bivariant, T: covariant]
|
||||
TestBox[U: bivariant, T: covariant]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -708,8 +691,8 @@ enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used
|
|||
trait SomeTrait<'a> { fn foo(&self); } // OK on traits.
|
||||
"#,
|
||||
expect![[r#"
|
||||
SomeStruct['a: invariant]
|
||||
SomeEnum['a: invariant]
|
||||
SomeStruct['a: bivariant]
|
||||
SomeEnum['a: bivariant]
|
||||
foo[Self: contravariant, 'a: invariant]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -737,14 +720,14 @@ struct DoubleNothing<T> {
|
|||
|
||||
"#,
|
||||
expect![[r#"
|
||||
SomeStruct[A: invariant]
|
||||
SomeEnum[A: invariant]
|
||||
ListCell[T: invariant]
|
||||
SelfTyAlias[T: invariant]
|
||||
WithBounds[T: invariant]
|
||||
WithWhereBounds[T: invariant]
|
||||
WithOutlivesBounds[T: invariant]
|
||||
DoubleNothing[T: invariant]
|
||||
SomeStruct[A: bivariant]
|
||||
SomeEnum[A: bivariant]
|
||||
ListCell[T: bivariant]
|
||||
SelfTyAlias[T: bivariant]
|
||||
WithBounds[T: bivariant]
|
||||
WithWhereBounds[T: bivariant]
|
||||
WithOutlivesBounds[T: bivariant]
|
||||
DoubleNothing[T: bivariant]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -855,7 +838,7 @@ struct S3<T>(S<T, T>);
|
|||
"#,
|
||||
expect![[r#"
|
||||
S[T: covariant]
|
||||
S2[T: invariant]
|
||||
S2[T: bivariant]
|
||||
S3[T: covariant]
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -868,7 +851,7 @@ struct S3<T>(S<T, T>);
|
|||
struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
|
||||
"#,
|
||||
expect![[r#"
|
||||
FixedPoint[T: invariant, U: invariant, V: invariant]
|
||||
FixedPoint[T: covariant, U: covariant, V: covariant]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ pub enum AttrsOwner {
|
|||
Field(FieldId),
|
||||
LifetimeParam(LifetimeParamId),
|
||||
TypeOrConstParam(TypeOrConstParamId),
|
||||
/// Things that do not have attributes. Used for builtin derives.
|
||||
Dummy,
|
||||
}
|
||||
|
||||
impl AttrsOwner {
|
||||
|
|
@ -123,7 +125,9 @@ impl AttrsWithOwner {
|
|||
let owner = match self.owner {
|
||||
AttrsOwner::AttrDef(it) => Either::Left(it),
|
||||
AttrsOwner::Field(it) => Either::Right(it),
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return &[],
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
|
||||
return &[];
|
||||
}
|
||||
};
|
||||
self.attrs.doc_aliases(db, owner)
|
||||
}
|
||||
|
|
@ -133,7 +137,9 @@ impl AttrsWithOwner {
|
|||
let owner = match self.owner {
|
||||
AttrsOwner::AttrDef(it) => Either::Left(it),
|
||||
AttrsOwner::Field(it) => Either::Right(it),
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
self.attrs.cfgs(db, owner)
|
||||
}
|
||||
|
|
@ -143,7 +149,9 @@ impl AttrsWithOwner {
|
|||
match self.owner {
|
||||
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
|
||||
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +164,9 @@ pub trait HasAttrs: Sized {
|
|||
AttrsOwner::Field(it) => AttrsWithOwner::new_field(db, it),
|
||||
AttrsOwner::LifetimeParam(it) => AttrsWithOwner::new_lifetime_param(db, it),
|
||||
AttrsOwner::TypeOrConstParam(it) => AttrsWithOwner::new_type_or_const_param(db, it),
|
||||
AttrsOwner::Dummy => {
|
||||
AttrsWithOwner { attrs: AttrFlags::empty(), owner: AttrsOwner::Dummy }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +178,9 @@ pub trait HasAttrs: Sized {
|
|||
match self.attr_id(db) {
|
||||
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
|
||||
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,12 +203,28 @@ impl_has_attrs![
|
|||
(Trait, TraitId),
|
||||
(TypeAlias, TypeAliasId),
|
||||
(Macro, MacroId),
|
||||
(Function, FunctionId),
|
||||
(Adt, AdtId),
|
||||
(Impl, ImplId),
|
||||
(ExternCrateDecl, ExternCrateId),
|
||||
];
|
||||
|
||||
impl HasAttrs for Function {
|
||||
fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
|
||||
match self.id {
|
||||
crate::AnyFunctionId::FunctionId(id) => AttrsOwner::AttrDef(id.into()),
|
||||
crate::AnyFunctionId::BuiltinDeriveImplMethod { .. } => AttrsOwner::Dummy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasAttrs for Impl {
|
||||
fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
|
||||
match self.id {
|
||||
hir_ty::next_solver::AnyImplId::ImplId(id) => AttrsOwner::AttrDef(id.into()),
|
||||
hir_ty::next_solver::AnyImplId::BuiltinDeriveImplId(..) => AttrsOwner::Dummy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_has_attrs_enum {
|
||||
($($variant:ident),* for $enum:ident) => {$(
|
||||
impl HasAttrs for $variant {
|
||||
|
|
@ -294,7 +323,9 @@ fn resolve_doc_path_on_(
|
|||
AttrsOwner::AttrDef(AttrDefId::MacroId(it)) => it.resolver(db),
|
||||
AttrsOwner::AttrDef(AttrDefId::ExternCrateId(it)) => it.resolver(db),
|
||||
AttrsOwner::Field(it) => it.parent.resolver(db),
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
|
||||
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut modpath = doc_modpath_from_str(link)?;
|
||||
|
|
|
|||
|
|
@ -2,19 +2,22 @@
|
|||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
AdtId, GenericDefId,
|
||||
AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId,
|
||||
builtin_derive::BuiltinDeriveImplMethod,
|
||||
expr_store::ExpressionStore,
|
||||
hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
|
||||
item_tree::FieldsShape,
|
||||
signatures::{StaticFlags, TraitFlags},
|
||||
type_ref::{TypeBound, TypeRef, TypeRefId},
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use hir_ty::{
|
||||
GenericPredicates,
|
||||
db::HirDatabase,
|
||||
display::{
|
||||
HirDisplay, HirDisplayWithExpressionStore, HirFormatter, Result, SizedByDefault,
|
||||
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_visibility,
|
||||
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_params_bounds,
|
||||
write_visibility,
|
||||
},
|
||||
next_solver::ClauseKind,
|
||||
};
|
||||
|
|
@ -22,25 +25,78 @@ use itertools::Itertools;
|
|||
use rustc_type_ir::inherent::IntoKind;
|
||||
|
||||
use crate::{
|
||||
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
|
||||
Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
|
||||
ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
|
||||
Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, Type,
|
||||
TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant,
|
||||
};
|
||||
|
||||
fn write_builtin_derive_impl_method<'db>(
|
||||
f: &mut HirFormatter<'_, 'db>,
|
||||
impl_: BuiltinDeriveImplId,
|
||||
method: BuiltinDeriveImplMethod,
|
||||
) -> Result {
|
||||
let db = f.db;
|
||||
let loc = impl_.loc(db);
|
||||
let (adt_params, _adt_params_store) = db.generic_params_and_store(loc.adt.into());
|
||||
|
||||
if f.show_container_bounds() && !adt_params.is_empty() {
|
||||
f.write_str("impl")?;
|
||||
write_generic_params(loc.adt.into(), f)?;
|
||||
f.write_char(' ')?;
|
||||
let trait_id = loc.trait_.get_id(f.lang_items());
|
||||
if let Some(trait_id) = trait_id {
|
||||
f.start_location_link(trait_id.into());
|
||||
}
|
||||
write!(f, "{}", Name::new_symbol_root(loc.trait_.name()).display(db, f.edition()))?;
|
||||
if trait_id.is_some() {
|
||||
f.end_location_link();
|
||||
}
|
||||
f.write_str(" for ")?;
|
||||
f.start_location_link(loc.adt.into());
|
||||
write!(f, "{}", Adt::from(loc.adt).name(db).display(db, f.edition()))?;
|
||||
f.end_location_link();
|
||||
write_generic_args(loc.adt.into(), f)?;
|
||||
f.write_char('\n')?;
|
||||
}
|
||||
|
||||
let Some(trait_method) = method.trait_method(db, impl_) else {
|
||||
return write!(f, "fn {}(…)", method.name());
|
||||
};
|
||||
let has_written_where = write_function(f, trait_method)?;
|
||||
|
||||
if f.show_container_bounds() && !adt_params.is_empty() {
|
||||
if !has_written_where {
|
||||
f.write_str("\nwhere")?
|
||||
}
|
||||
write!(f, "\n // Bounds from impl:")?;
|
||||
|
||||
let predicates =
|
||||
hir_ty::builtin_derive::predicates(db, impl_).explicit_predicates().skip_binder();
|
||||
write_params_bounds(f, predicates)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'db> HirDisplay<'db> for Function {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
let id = match self.id {
|
||||
AnyFunctionId::FunctionId(id) => id,
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
|
||||
return write_builtin_derive_impl_method(f, impl_, method);
|
||||
}
|
||||
};
|
||||
|
||||
let db = f.db;
|
||||
let data = db.function_signature(self.id);
|
||||
let container = self.as_assoc_item(db).map(|it| it.container(db));
|
||||
let mut module = self.module(db);
|
||||
let container = id.loc(db).container;
|
||||
|
||||
// Write container (trait or impl)
|
||||
let container_params = match container {
|
||||
Some(AssocItemContainer::Trait(trait_)) => {
|
||||
let (params, params_store) = f.db.generic_params_and_store(trait_.id.into());
|
||||
ItemContainerId::TraitId(trait_) => {
|
||||
let (params, params_store) = f.db.generic_params_and_store(trait_.into());
|
||||
if f.show_container_bounds() && !params.is_empty() {
|
||||
write_trait_header(&trait_, f)?;
|
||||
write_trait_header(trait_.into(), f)?;
|
||||
f.write_char('\n')?;
|
||||
has_disaplayable_predicates(f.db, ¶ms, ¶ms_store)
|
||||
.then_some((params, params_store))
|
||||
|
|
@ -48,10 +104,10 @@ impl<'db> HirDisplay<'db> for Function {
|
|||
None
|
||||
}
|
||||
}
|
||||
Some(AssocItemContainer::Impl(impl_)) => {
|
||||
let (params, params_store) = f.db.generic_params_and_store(impl_.id.into());
|
||||
ItemContainerId::ImplId(impl_) => {
|
||||
let (params, params_store) = f.db.generic_params_and_store(impl_.into());
|
||||
if f.show_container_bounds() && !params.is_empty() {
|
||||
write_impl_header(&impl_, f)?;
|
||||
write_impl_header(impl_, f)?;
|
||||
f.write_char('\n')?;
|
||||
has_disaplayable_predicates(f.db, ¶ms, ¶ms_store)
|
||||
.then_some((params, params_store))
|
||||
|
|
@ -59,124 +115,20 @@ impl<'db> HirDisplay<'db> for Function {
|
|||
None
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Write signature of the function
|
||||
|
||||
// Block-local impls are "hoisted" to the nearest (non-block) module.
|
||||
if let Some(AssocItemContainer::Impl(_)) = container {
|
||||
module = module.nearest_non_block_module(db);
|
||||
}
|
||||
let module_id = module.id;
|
||||
|
||||
write_visibility(module_id, self.visibility(db), f)?;
|
||||
|
||||
if data.is_default() {
|
||||
f.write_str("default ")?;
|
||||
}
|
||||
if data.is_const() {
|
||||
f.write_str("const ")?;
|
||||
}
|
||||
if data.is_async() {
|
||||
f.write_str("async ")?;
|
||||
}
|
||||
// FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
|
||||
// (they are conditionally unsafe to call). We probably should show something else.
|
||||
if self.is_unsafe_to_call(db, None, f.edition()) {
|
||||
f.write_str("unsafe ")?;
|
||||
}
|
||||
if let Some(abi) = &data.abi {
|
||||
write!(f, "extern \"{}\" ", abi.as_str())?;
|
||||
}
|
||||
write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
|
||||
|
||||
write_generic_params(GenericDefId::FunctionId(self.id), f)?;
|
||||
|
||||
f.write_char('(')?;
|
||||
|
||||
let mut first = true;
|
||||
let mut skip_self = 0;
|
||||
if let Some(self_param) = self.self_param(db) {
|
||||
self_param.hir_fmt(f)?;
|
||||
first = false;
|
||||
skip_self = 1;
|
||||
}
|
||||
|
||||
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
|
||||
let body = db.body(self.id.into());
|
||||
for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
|
||||
if !first {
|
||||
f.write_str(", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
|
||||
let pat_str = body.pretty_print_pat(db, self.id.into(), pat_id, true, f.edition());
|
||||
f.write_str(&pat_str)?;
|
||||
|
||||
f.write_str(": ")?;
|
||||
type_ref.hir_fmt(f, &data.store)?;
|
||||
}
|
||||
|
||||
if data.is_varargs() {
|
||||
if !first {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
f.write_str("...")?;
|
||||
}
|
||||
|
||||
f.write_char(')')?;
|
||||
|
||||
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
|
||||
// Use ugly pattern match to strip the Future trait.
|
||||
// Better way?
|
||||
let ret_type = if !data.is_async() {
|
||||
data.ret_type
|
||||
} else if let Some(ret_type) = data.ret_type {
|
||||
match &data.store[ret_type] {
|
||||
TypeRef::ImplTrait(bounds) => match &bounds[0] {
|
||||
&TypeBound::Path(path, _) => Some(
|
||||
*data.store[path]
|
||||
.segments()
|
||||
.iter()
|
||||
.last()
|
||||
.unwrap()
|
||||
.args_and_bindings
|
||||
.unwrap()
|
||||
.bindings[0]
|
||||
.type_ref
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(ret_type) = ret_type {
|
||||
match &data.store[ret_type] {
|
||||
TypeRef::Tuple(tup) if tup.is_empty() => {}
|
||||
_ => {
|
||||
f.write_str(" -> ")?;
|
||||
ret_type.hir_fmt(f, &data.store)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write where clauses
|
||||
let has_written_where = write_where_clause(GenericDefId::FunctionId(self.id), f)?;
|
||||
let has_written_where = write_function(f, id)?;
|
||||
if let Some((container_params, container_params_store)) = container_params {
|
||||
if !has_written_where {
|
||||
f.write_str("\nwhere")?;
|
||||
}
|
||||
let container_name = match container.unwrap() {
|
||||
AssocItemContainer::Trait(_) => "trait",
|
||||
AssocItemContainer::Impl(_) => "impl",
|
||||
let container_name = match container {
|
||||
ItemContainerId::TraitId(_) => "trait",
|
||||
ItemContainerId::ImplId(_) => "impl",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
write!(f, "\n // Bounds from {container_name}:",)?;
|
||||
write_where_predicates(&container_params, &container_params_store, f)?;
|
||||
|
|
@ -185,14 +137,129 @@ impl<'db> HirDisplay<'db> for Function {
|
|||
}
|
||||
}
|
||||
|
||||
fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Result<bool> {
|
||||
let db = f.db;
|
||||
let func = Function::from(func_id);
|
||||
let data = db.function_signature(func_id);
|
||||
|
||||
let mut module = func.module(db);
|
||||
// Block-local impls are "hoisted" to the nearest (non-block) module.
|
||||
if let ItemContainerId::ImplId(_) = func_id.loc(db).container {
|
||||
module = module.nearest_non_block_module(db);
|
||||
}
|
||||
let module_id = module.id;
|
||||
|
||||
write_visibility(module_id, func.visibility(db), f)?;
|
||||
|
||||
if data.is_default() {
|
||||
f.write_str("default ")?;
|
||||
}
|
||||
if data.is_const() {
|
||||
f.write_str("const ")?;
|
||||
}
|
||||
if data.is_async() {
|
||||
f.write_str("async ")?;
|
||||
}
|
||||
// FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
|
||||
// (they are conditionally unsafe to call). We probably should show something else.
|
||||
if func.is_unsafe_to_call(db, None, f.edition()) {
|
||||
f.write_str("unsafe ")?;
|
||||
}
|
||||
if let Some(abi) = &data.abi {
|
||||
write!(f, "extern \"{}\" ", abi.as_str())?;
|
||||
}
|
||||
write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
|
||||
|
||||
write_generic_params(GenericDefId::FunctionId(func_id), f)?;
|
||||
|
||||
f.write_char('(')?;
|
||||
|
||||
let mut first = true;
|
||||
let mut skip_self = 0;
|
||||
if let Some(self_param) = func.self_param(db) {
|
||||
self_param.hir_fmt(f)?;
|
||||
first = false;
|
||||
skip_self = 1;
|
||||
}
|
||||
|
||||
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
|
||||
let body = db.body(func_id.into());
|
||||
for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) {
|
||||
if !first {
|
||||
f.write_str(", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
|
||||
let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition());
|
||||
f.write_str(&pat_str)?;
|
||||
|
||||
f.write_str(": ")?;
|
||||
type_ref.hir_fmt(f, &data.store)?;
|
||||
}
|
||||
|
||||
if data.is_varargs() {
|
||||
if !first {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
f.write_str("...")?;
|
||||
}
|
||||
|
||||
f.write_char(')')?;
|
||||
|
||||
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
|
||||
// Use ugly pattern match to strip the Future trait.
|
||||
// Better way?
|
||||
let ret_type = if !data.is_async() {
|
||||
data.ret_type
|
||||
} else if let Some(ret_type) = data.ret_type {
|
||||
match &data.store[ret_type] {
|
||||
TypeRef::ImplTrait(bounds) => match &bounds[0] {
|
||||
&TypeBound::Path(path, _) => Some(
|
||||
*data.store[path]
|
||||
.segments()
|
||||
.iter()
|
||||
.last()
|
||||
.unwrap()
|
||||
.args_and_bindings
|
||||
.unwrap()
|
||||
.bindings[0]
|
||||
.type_ref
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(ret_type) = ret_type {
|
||||
match &data.store[ret_type] {
|
||||
TypeRef::Tuple(tup) if tup.is_empty() => {}
|
||||
_ => {
|
||||
f.write_str(" -> ")?;
|
||||
ret_type.hir_fmt(f, &data.store)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write where clauses
|
||||
let has_written_where = write_where_clause(GenericDefId::FunctionId(func_id), f)?;
|
||||
Ok(has_written_where)
|
||||
}
|
||||
|
||||
fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
let db = f.db;
|
||||
|
||||
f.write_str("impl")?;
|
||||
let def_id = GenericDefId::ImplId(impl_.id);
|
||||
let def_id = GenericDefId::ImplId(impl_);
|
||||
write_generic_params(def_id, f)?;
|
||||
|
||||
let impl_data = db.impl_signature(impl_.id);
|
||||
let impl_data = db.impl_signature(impl_);
|
||||
if let Some(target_trait) = &impl_data.target_trait {
|
||||
f.write_char(' ')?;
|
||||
hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?;
|
||||
|
|
@ -200,14 +267,28 @@ fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result
|
|||
}
|
||||
|
||||
f.write_char(' ')?;
|
||||
impl_.self_ty(db).hir_fmt(f)?;
|
||||
Impl::from(impl_).self_ty(db).hir_fmt(f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'db> HirDisplay<'db> for SelfParam {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
let data = f.db.function_signature(self.func);
|
||||
let func = match self.func.id {
|
||||
AnyFunctionId::FunctionId(id) => id,
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method {
|
||||
BuiltinDeriveImplMethod::clone
|
||||
| BuiltinDeriveImplMethod::fmt
|
||||
| BuiltinDeriveImplMethod::hash
|
||||
| BuiltinDeriveImplMethod::cmp
|
||||
| BuiltinDeriveImplMethod::partial_cmp
|
||||
| BuiltinDeriveImplMethod::eq => return f.write_str("&self"),
|
||||
BuiltinDeriveImplMethod::default => {
|
||||
unreachable!("this trait method does not have a self param")
|
||||
}
|
||||
},
|
||||
};
|
||||
let data = f.db.function_signature(func);
|
||||
let param = *data.params.first().unwrap();
|
||||
match &data.store[param] {
|
||||
TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
|
||||
|
|
@ -553,6 +634,18 @@ impl<'db> HirDisplay<'db> for ConstParam {
|
|||
}
|
||||
|
||||
fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
write_generic_params_or_args(def, f, true)
|
||||
}
|
||||
|
||||
fn write_generic_args<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
write_generic_params_or_args(def, f, false)
|
||||
}
|
||||
|
||||
fn write_generic_params_or_args<'db>(
|
||||
def: GenericDefId,
|
||||
f: &mut HirFormatter<'_, 'db>,
|
||||
include_defaults: bool,
|
||||
) -> Result {
|
||||
let (params, store) = f.db.generic_params_and_store(def);
|
||||
if params.iter_lt().next().is_none()
|
||||
&& params.iter_type_or_consts().all(|it| it.1.const_param().is_none())
|
||||
|
|
@ -587,7 +680,7 @@ fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -
|
|||
}
|
||||
delim(f)?;
|
||||
write!(f, "{}", name.display(f.db, f.edition()))?;
|
||||
if let Some(default) = &ty.default {
|
||||
if include_defaults && let Some(default) = &ty.default {
|
||||
f.write_str(" = ")?;
|
||||
default.hir_fmt(f, &store)?;
|
||||
}
|
||||
|
|
@ -597,7 +690,7 @@ fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -
|
|||
write!(f, "const {}: ", name.display(f.db, f.edition()))?;
|
||||
c.ty.hir_fmt(f, &store)?;
|
||||
|
||||
if let Some(default) = &c.default {
|
||||
if include_defaults && let Some(default) = &c.default {
|
||||
f.write_str(" = ")?;
|
||||
default.hir_fmt(f, &store)?;
|
||||
}
|
||||
|
|
@ -746,7 +839,7 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
|
|||
impl<'db> HirDisplay<'db> for Trait {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
// FIXME(trait-alias) needs special handling to print the equal sign
|
||||
write_trait_header(self, f)?;
|
||||
write_trait_header(*self, f)?;
|
||||
let def_id = GenericDefId::TraitId(self.id);
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
|
||||
|
|
@ -783,7 +876,7 @@ impl<'db> HirDisplay<'db> for Trait {
|
|||
}
|
||||
}
|
||||
|
||||
fn write_trait_header<'db>(trait_: &Trait, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
fn write_trait_header<'db>(trait_: Trait, f: &mut HirFormatter<'_, 'db>) -> Result {
|
||||
write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?;
|
||||
let data = f.db.trait_signature(trait_.id);
|
||||
if data.flags.contains(TraitFlags::UNSAFE) {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@
|
|||
//! are splitting the hir.
|
||||
|
||||
use hir_def::{
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
|
||||
ModuleDefId, VariantId,
|
||||
AdtId, AssocItemId, BuiltinDeriveImplId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId,
|
||||
GenericParamId, ModuleDefId, VariantId,
|
||||
hir::{BindingId, LabelId},
|
||||
};
|
||||
use hir_ty::next_solver::AnyImplId;
|
||||
|
||||
use crate::{
|
||||
Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, ItemInNs, Label,
|
||||
Local, ModuleDef, Variant, VariantDef,
|
||||
Adt, AnyFunctionId, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam,
|
||||
ItemInNs, Label, Local, ModuleDef, Variant, VariantDef,
|
||||
};
|
||||
|
||||
macro_rules! from_id {
|
||||
|
|
@ -39,8 +40,8 @@ from_id![
|
|||
(hir_def::TraitId, crate::Trait),
|
||||
(hir_def::StaticId, crate::Static),
|
||||
(hir_def::ConstId, crate::Const),
|
||||
(hir_def::FunctionId, crate::Function),
|
||||
(hir_def::ImplId, crate::Impl),
|
||||
(crate::AnyFunctionId, crate::Function),
|
||||
(hir_ty::next_solver::AnyImplId, crate::Impl),
|
||||
(hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
|
||||
(hir_def::TypeParamId, crate::TypeParam),
|
||||
(hir_def::ConstParamId, crate::ConstParam),
|
||||
|
|
@ -119,11 +120,15 @@ impl From<ModuleDefId> for ModuleDef {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ModuleDef> for ModuleDefId {
|
||||
fn from(id: ModuleDef) -> Self {
|
||||
match id {
|
||||
impl TryFrom<ModuleDef> for ModuleDefId {
|
||||
type Error = ();
|
||||
fn try_from(id: ModuleDef) -> Result<Self, Self::Error> {
|
||||
Ok(match id {
|
||||
ModuleDef::Module(it) => ModuleDefId::ModuleId(it.into()),
|
||||
ModuleDef::Function(it) => ModuleDefId::FunctionId(it.into()),
|
||||
ModuleDef::Function(it) => match it.id {
|
||||
AnyFunctionId::FunctionId(it) => it.into(),
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
|
||||
},
|
||||
ModuleDef::Adt(it) => ModuleDefId::AdtId(it.into()),
|
||||
ModuleDef::Variant(it) => ModuleDefId::EnumVariantId(it.into()),
|
||||
ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()),
|
||||
|
|
@ -132,18 +137,22 @@ impl From<ModuleDef> for ModuleDefId {
|
|||
ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()),
|
||||
ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()),
|
||||
ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefWithBody> for DefWithBodyId {
|
||||
fn from(def: DefWithBody) -> Self {
|
||||
match def {
|
||||
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
|
||||
impl TryFrom<DefWithBody> for DefWithBodyId {
|
||||
type Error = ();
|
||||
fn try_from(def: DefWithBody) -> Result<Self, ()> {
|
||||
Ok(match def {
|
||||
DefWithBody::Function(it) => match it.id {
|
||||
AnyFunctionId::FunctionId(it) => it.into(),
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
|
||||
},
|
||||
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
|
||||
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
|
||||
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,17 +177,11 @@ impl From<AssocItemId> for AssocItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<GenericDef> for GenericDefId {
|
||||
fn from(def: GenericDef) -> Self {
|
||||
match def {
|
||||
GenericDef::Function(it) => GenericDefId::FunctionId(it.id),
|
||||
GenericDef::Adt(it) => GenericDefId::AdtId(it.into()),
|
||||
GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
|
||||
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
|
||||
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
|
||||
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
|
||||
GenericDef::Static(it) => GenericDefId::StaticId(it.id),
|
||||
}
|
||||
impl TryFrom<GenericDef> for GenericDefId {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(def: GenericDef) -> Result<Self, Self::Error> {
|
||||
def.id().ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,13 +241,17 @@ impl From<FieldId> for Field {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<AssocItem> for GenericDefId {
|
||||
fn from(item: AssocItem) -> Self {
|
||||
match item {
|
||||
AssocItem::Function(f) => f.id.into(),
|
||||
impl TryFrom<AssocItem> for GenericDefId {
|
||||
type Error = ();
|
||||
fn try_from(item: AssocItem) -> Result<Self, Self::Error> {
|
||||
Ok(match item {
|
||||
AssocItem::Function(f) => match f.id {
|
||||
AnyFunctionId::FunctionId(it) => it.into(),
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
|
||||
},
|
||||
AssocItem::Const(c) => c.id.into(),
|
||||
AssocItem::TypeAlias(t) => t.id.into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,13 +277,14 @@ impl From<hir_def::item_scope::ItemInNs> for ItemInNs {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ItemInNs> for hir_def::item_scope::ItemInNs {
|
||||
fn from(it: ItemInNs) -> Self {
|
||||
match it {
|
||||
ItemInNs::Types(it) => Self::Types(it.into()),
|
||||
ItemInNs::Values(it) => Self::Values(it.into()),
|
||||
impl TryFrom<ItemInNs> for hir_def::item_scope::ItemInNs {
|
||||
type Error = ();
|
||||
fn try_from(it: ItemInNs) -> Result<Self, Self::Error> {
|
||||
Ok(match it {
|
||||
ItemInNs::Types(it) => Self::Types(it.try_into()?),
|
||||
ItemInNs::Values(it) => Self::Values(it.try_into()?),
|
||||
ItemInNs::Macros(it) => Self::Macros(it.into()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,3 +299,21 @@ impl From<BuiltinType> for hir_def::builtin_type::BuiltinType {
|
|||
it.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hir_def::ImplId> for crate::Impl {
|
||||
fn from(value: hir_def::ImplId) -> Self {
|
||||
crate::Impl { id: AnyImplId::ImplId(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BuiltinDeriveImplId> for crate::Impl {
|
||||
fn from(value: BuiltinDeriveImplId) -> Self {
|
||||
crate::Impl { id: AnyImplId::BuiltinDeriveImplId(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hir_def::FunctionId> for crate::Function {
|
||||
fn from(value: hir_def::FunctionId) -> Self {
|
||||
crate::Function { id: AnyFunctionId::FunctionId(value) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@ use hir_def::{
|
|||
src::{HasChildSource, HasSource as _},
|
||||
};
|
||||
use hir_expand::{EditionedFileId, HirFileId, InFile};
|
||||
use hir_ty::db::InternedClosure;
|
||||
use syntax::ast;
|
||||
use hir_ty::{db::InternedClosure, next_solver::AnyImplId};
|
||||
use syntax::{AstNode, ast};
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
Adt, AnyFunctionId, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
|
||||
Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase,
|
||||
};
|
||||
|
||||
pub trait HasSource {
|
||||
type Ast;
|
||||
pub trait HasSource: Sized {
|
||||
type Ast: AstNode;
|
||||
/// Fetches the definition's source node.
|
||||
/// Using [`crate::SemanticsImpl::source`] is preferred when working with [`crate::Semantics`],
|
||||
/// as that caches the parsed file in the semantics' cache.
|
||||
|
|
@ -27,6 +27,20 @@ pub trait HasSource {
|
|||
/// But we made this method `Option` to support rlib in the future
|
||||
/// by <https://github.com/rust-lang/rust-analyzer/issues/6913>
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
|
||||
|
||||
/// Fetches the source node, along with its full range.
|
||||
///
|
||||
/// The reason for the separate existence of this method is that some things, notably builtin derive impls,
|
||||
/// do not really have a source node, at least not of the correct type. But we still can trace them
|
||||
/// to source code (the derive producing them). So this method will return the range if it is supported,
|
||||
/// and if the node is supported too it will return it as well.
|
||||
fn source_with_range(
|
||||
self,
|
||||
db: &dyn HirDatabase,
|
||||
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
|
||||
let source = self.source(db)?;
|
||||
Some(source.map(|node| (node.syntax().text_range(), Some(node))))
|
||||
}
|
||||
}
|
||||
|
||||
/// NB: Module is !HasSource, because it has two source nodes at the same time:
|
||||
|
|
@ -146,7 +160,30 @@ impl HasSource for Variant {
|
|||
impl HasSource for Function {
|
||||
type Ast = ast::Fn;
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
Some(self.id.lookup(db).source(db))
|
||||
match self.id {
|
||||
AnyFunctionId::FunctionId(id) => Some(id.loc(db).source(db)),
|
||||
// When calling `source()`, we use the trait method source, but when calling `source_with_range()`,
|
||||
// we return `None` as the syntax node source. This is relying on the assumption that if you are calling
|
||||
// `source_with_range()` (e.g. in navigation) you're prepared to deal with no source node, while if
|
||||
// you call `source()` maybe you don't - therefore we fall back to the trait method, to not lose features.
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => method
|
||||
.trait_method(db, impl_)
|
||||
.and_then(|trait_method| Function::from(trait_method).source(db)),
|
||||
}
|
||||
}
|
||||
|
||||
fn source_with_range(
|
||||
self,
|
||||
db: &dyn HirDatabase,
|
||||
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
|
||||
match self.id {
|
||||
AnyFunctionId::FunctionId(id) => Some(
|
||||
id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
|
||||
),
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
|
||||
Some(impl_.loc(db).source(db).map(|range| (range, None)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl HasSource for Const {
|
||||
|
|
@ -190,7 +227,24 @@ impl HasSource for Macro {
|
|||
impl HasSource for Impl {
|
||||
type Ast = ast::Impl;
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
Some(self.id.lookup(db).source(db))
|
||||
match self.id {
|
||||
AnyImplId::ImplId(id) => Some(id.loc(db).source(db)),
|
||||
AnyImplId::BuiltinDeriveImplId(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn source_with_range(
|
||||
self,
|
||||
db: &dyn HirDatabase,
|
||||
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
|
||||
match self.id {
|
||||
AnyImplId::ImplId(id) => Some(
|
||||
id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
|
||||
),
|
||||
AnyImplId::BuiltinDeriveImplId(impl_) => {
|
||||
Some(impl_.loc(db).source(db).map(|range| (range, None)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +278,7 @@ impl HasSource for Param<'_> {
|
|||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
match self.func {
|
||||
Callee::Def(CallableDefId::FunctionId(func)) => {
|
||||
let InFile { file_id, value } = Function { id: func }.source(db)?;
|
||||
let InFile { file_id, value } = Function::from(func).source(db)?;
|
||||
let params = value.param_list()?;
|
||||
if let Some(self_param) = params.self_param() {
|
||||
if let Some(idx) = self.idx.checked_sub(1) {
|
||||
|
|
@ -261,7 +315,7 @@ impl HasSource for SelfParam {
|
|||
type Ast = ast::SelfParam;
|
||||
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
let InFile { file_id, value } = Function::from(self.func).source(db)?;
|
||||
let InFile { file_id, value } = self.func.source(db)?;
|
||||
value
|
||||
.param_list()
|
||||
.and_then(|params| params.self_param())
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -13,7 +13,7 @@ use std::{
|
|||
use base_db::FxIndexSet;
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
|
||||
DefWithBodyId, MacroId, StructId, TraitId, VariantId,
|
||||
attrs::parse_extra_crate_attrs,
|
||||
expr_store::{Body, ExprOrPatSource, HygieneId, path::Path},
|
||||
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
|
||||
|
|
@ -34,7 +34,7 @@ use hir_ty::{
|
|||
diagnostics::{unsafe_operations, unsafe_operations_for_body},
|
||||
infer_query_with_inspect,
|
||||
next_solver::{
|
||||
DbInterner, Span,
|
||||
AnyImplId, DbInterner, Span,
|
||||
format_proof_tree::{ProofTreeData, dump_proof_tree_structured},
|
||||
},
|
||||
};
|
||||
|
|
@ -53,11 +53,11 @@ use syntax::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
|
||||
Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl,
|
||||
InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
|
||||
Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type,
|
||||
TypeAlias, TypeParam, Union, Variant, VariantDef,
|
||||
Adjust, Adjustment, Adt, AnyFunctionId, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
|
||||
ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution,
|
||||
HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
|
||||
Module, ModuleDef, Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait,
|
||||
TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
|
||||
db::HirDatabase,
|
||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{SourceAnalyzer, resolve_hir_path},
|
||||
|
|
@ -106,7 +106,10 @@ impl PathResolution {
|
|||
| PathResolution::DeriveHelper(_)
|
||||
| PathResolution::ConstParam(_) => None,
|
||||
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
|
||||
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
|
||||
PathResolution::SelfType(impl_def) => match impl_def.id {
|
||||
AnyImplId::ImplId(id) => Some(TypeNs::SelfType(id)),
|
||||
AnyImplId::BuiltinDeriveImplId(_) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -345,23 +348,23 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
|
|||
}
|
||||
|
||||
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
|
||||
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
|
||||
self.imp.resolve_await_to_poll(await_expr)
|
||||
}
|
||||
|
||||
pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
|
||||
self.imp.resolve_prefix_expr(prefix_expr).map(Function::from)
|
||||
self.imp.resolve_prefix_expr(prefix_expr)
|
||||
}
|
||||
|
||||
pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
|
||||
self.imp.resolve_index_expr(index_expr).map(Function::from)
|
||||
self.imp.resolve_index_expr(index_expr)
|
||||
}
|
||||
|
||||
pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
|
||||
self.imp.resolve_bin_expr(bin_expr).map(Function::from)
|
||||
self.imp.resolve_bin_expr(bin_expr)
|
||||
}
|
||||
|
||||
pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
|
||||
self.imp.resolve_try_expr(try_expr).map(Function::from)
|
||||
self.imp.resolve_try_expr(try_expr)
|
||||
}
|
||||
|
||||
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
|
||||
|
|
@ -833,7 +836,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
// FIXME: Type the return type
|
||||
/// Returns the range (pre-expansion) in the string literal corresponding to the resolution,
|
||||
/// absolute file range (post-expansion)
|
||||
/// of the part in the format string, the corresponding string token and the resolution if it
|
||||
/// of the part in the format string (post-expansion), the corresponding string token and the resolution if it
|
||||
/// exists.
|
||||
// FIXME: Remove this in favor of `check_for_format_args_template_with_file`
|
||||
pub fn check_for_format_args_template(
|
||||
|
|
@ -1749,6 +1752,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
func: Function,
|
||||
subst: impl IntoIterator<Item = Type<'db>>,
|
||||
) -> Option<Function> {
|
||||
let AnyFunctionId::FunctionId(func) = func.id else { return Some(func) };
|
||||
let interner = DbInterner::new_no_crate(self.db);
|
||||
let mut subst = subst.into_iter();
|
||||
let substs =
|
||||
|
|
@ -1757,7 +1761,12 @@ impl<'db> SemanticsImpl<'db> {
|
|||
subst.next().expect("too few subst").ty.into()
|
||||
});
|
||||
assert!(subst.next().is_none(), "too many subst");
|
||||
Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into())
|
||||
Some(match self.db.lookup_impl_method(env.env, func, substs).0 {
|
||||
Either::Left(it) => it.into(),
|
||||
Either::Right((impl_, method)) => {
|
||||
Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
|
||||
|
|
@ -1768,23 +1777,23 @@ impl<'db> SemanticsImpl<'db> {
|
|||
self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr)
|
||||
}
|
||||
|
||||
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
|
||||
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
|
||||
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
|
||||
}
|
||||
|
||||
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
|
||||
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
|
||||
self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
|
||||
}
|
||||
|
||||
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
|
||||
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
|
||||
self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
|
||||
}
|
||||
|
||||
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
|
||||
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
|
||||
self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
|
||||
}
|
||||
|
||||
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> {
|
||||
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
|
||||
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
|
||||
}
|
||||
|
||||
|
|
@ -1861,7 +1870,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
|
||||
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
|
||||
let def = DefWithBodyId::from(def);
|
||||
let Ok(def) = DefWithBodyId::try_from(def) else {
|
||||
return FxHashSet::default();
|
||||
};
|
||||
let (body, source_map) = self.db.body_with_source_map(def);
|
||||
let infer = InferenceResult::for_body(self.db, def);
|
||||
let mut res = FxHashSet::default();
|
||||
|
|
@ -1877,7 +1888,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||
always!(block.unsafe_token().is_some());
|
||||
let block = self.wrap_node_infile(ast::Expr::from(block));
|
||||
let Some(def) = self.body_for(block.syntax()) else { return Vec::new() };
|
||||
let def = def.into();
|
||||
let Ok(def) = def.try_into() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let (body, source_map) = self.db.body_with_source_map(def);
|
||||
let infer = InferenceResult::for_body(self.db, def);
|
||||
let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else {
|
||||
|
|
@ -2023,16 +2036,22 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
|
||||
/// Search for a definition's source and cache its syntax tree
|
||||
pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
|
||||
where
|
||||
Def::Ast: AstNode,
|
||||
{
|
||||
pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> {
|
||||
// FIXME: source call should go through the parse cache
|
||||
let res = def.source(self.db)?;
|
||||
self.cache(find_root(res.value.syntax()), res.file_id);
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn source_with_range<Def: HasSource>(
|
||||
&self,
|
||||
def: Def,
|
||||
) -> Option<InFile<(TextRange, Option<Def::Ast>)>> {
|
||||
let res = def.source_with_range(self.db)?;
|
||||
self.parse_or_expand(res.file_id);
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
|
||||
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
|
||||
|
||||
|
|
@ -2162,9 +2181,10 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
let def = match &enclosing_item {
|
||||
Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
|
||||
Either::Left(ast::Item::Fn(it)) => {
|
||||
self.to_def(it).map(<_>::into).map(DefWithBodyId::FunctionId)
|
||||
}
|
||||
Either::Left(ast::Item::Fn(it)) => (|| match self.to_def(it)?.id {
|
||||
AnyFunctionId::FunctionId(id) => Some(DefWithBodyId::FunctionId(id)),
|
||||
AnyFunctionId::BuiltinDeriveImplMethod { .. } => None,
|
||||
})(),
|
||||
Either::Left(ast::Item::Const(it)) => {
|
||||
self.to_def(it).map(<_>::into).map(DefWithBodyId::ConstId)
|
||||
}
|
||||
|
|
@ -2201,7 +2221,11 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
|
||||
pub fn impl_generated_from_derive(&self, impl_: Impl) -> Option<Adt> {
|
||||
let source = hir_def::src::HasSource::ast_ptr(&impl_.id.loc(self.db), self.db);
|
||||
let id = match impl_.id {
|
||||
AnyImplId::ImplId(id) => id,
|
||||
AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()),
|
||||
};
|
||||
let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db);
|
||||
let mut file_id = source.file_id;
|
||||
let adt_ast_id = loop {
|
||||
let macro_call = file_id.macro_file()?;
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ use syntax::{
|
|||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field,
|
||||
Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait,
|
||||
TupleField, Type, TypeAlias, Variant,
|
||||
Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const,
|
||||
DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct,
|
||||
ToolModule, Trait, TupleField, Type, TypeAlias, Variant,
|
||||
db::HirDatabase,
|
||||
semantics::{PathResolution, PathResolutionPerNs},
|
||||
};
|
||||
|
|
@ -431,7 +431,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
|
||||
let (f_in_trait, substs) = self.infer()?.method_resolution(expr_id)?;
|
||||
|
||||
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
|
||||
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_method_call_fallback(
|
||||
|
|
@ -446,8 +446,8 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
let (fn_, subst) =
|
||||
self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs);
|
||||
Some((
|
||||
Either::Left(fn_.into()),
|
||||
Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))),
|
||||
Either::Left(fn_),
|
||||
GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)),
|
||||
))
|
||||
}
|
||||
None => {
|
||||
|
|
@ -519,8 +519,8 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
|
||||
let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs);
|
||||
(
|
||||
Either::Right(f.into()),
|
||||
Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))),
|
||||
Either::Right(f),
|
||||
GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)),
|
||||
)
|
||||
}),
|
||||
}
|
||||
|
|
@ -569,7 +569,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
&self,
|
||||
db: &'db dyn HirDatabase,
|
||||
await_expr: &ast::AwaitExpr,
|
||||
) -> Option<FunctionId> {
|
||||
) -> Option<Function> {
|
||||
let mut ty = self.ty_of_expr(await_expr.expr()?)?;
|
||||
|
||||
let into_future_trait = self
|
||||
|
|
@ -605,7 +605,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
&self,
|
||||
db: &'db dyn HirDatabase,
|
||||
prefix_expr: &ast::PrefixExpr,
|
||||
) -> Option<FunctionId> {
|
||||
) -> Option<Function> {
|
||||
let (_op_trait, op_fn) = match prefix_expr.op_kind()? {
|
||||
ast::UnaryOp::Deref => {
|
||||
// This can be either `Deref::deref` or `DerefMut::deref_mut`.
|
||||
|
|
@ -650,7 +650,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
&self,
|
||||
db: &'db dyn HirDatabase,
|
||||
index_expr: &ast::IndexExpr,
|
||||
) -> Option<FunctionId> {
|
||||
) -> Option<Function> {
|
||||
let base_ty = self.ty_of_expr(index_expr.base()?)?;
|
||||
let index_ty = self.ty_of_expr(index_expr.index()?)?;
|
||||
|
||||
|
|
@ -679,7 +679,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
&self,
|
||||
db: &'db dyn HirDatabase,
|
||||
binop_expr: &ast::BinExpr,
|
||||
) -> Option<FunctionId> {
|
||||
) -> Option<Function> {
|
||||
let op = binop_expr.op_kind()?;
|
||||
let lhs = self.ty_of_expr(binop_expr.lhs()?)?;
|
||||
let rhs = self.ty_of_expr(binop_expr.rhs()?)?;
|
||||
|
|
@ -699,7 +699,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
&self,
|
||||
db: &'db dyn HirDatabase,
|
||||
try_expr: &ast::TryExpr,
|
||||
) -> Option<FunctionId> {
|
||||
) -> Option<Function> {
|
||||
let ty = self.ty_of_expr(try_expr.expr()?)?;
|
||||
|
||||
let op_fn = self.lang_items(db).TryTraitBranch?;
|
||||
|
|
@ -905,7 +905,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
subs,
|
||||
self.trait_environment(db),
|
||||
);
|
||||
(AssocItemId::from(f_in_trait), subst)
|
||||
(AssocItem::Function(f_in_trait.into()), Some(subst))
|
||||
}
|
||||
Some(func_ty) => {
|
||||
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind() {
|
||||
|
|
@ -913,19 +913,19 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
.resolve_impl_method_or_trait_def_with_subst(
|
||||
db, f_in_trait, subs,
|
||||
);
|
||||
let subst = GenericSubstitution::new(
|
||||
fn_.into(),
|
||||
let subst = GenericSubstitution::new_from_fn(
|
||||
fn_,
|
||||
subst,
|
||||
self.trait_environment(db),
|
||||
);
|
||||
(fn_.into(), subst)
|
||||
(AssocItem::Function(fn_), subst)
|
||||
} else {
|
||||
let subst = GenericSubstitution::new(
|
||||
f_in_trait.into(),
|
||||
subs,
|
||||
self.trait_environment(db),
|
||||
);
|
||||
(f_in_trait.into(), subst)
|
||||
(AssocItem::Function(f_in_trait.into()), Some(subst))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -938,11 +938,11 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
subst,
|
||||
self.trait_environment(db),
|
||||
);
|
||||
(konst.into(), subst)
|
||||
(AssocItem::Const(konst.into()), Some(subst))
|
||||
}
|
||||
};
|
||||
|
||||
return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
|
||||
return Some((PathResolution::Def(assoc.into()), subst));
|
||||
}
|
||||
if let Some(VariantId::EnumVariantId(variant)) =
|
||||
infer.variant_resolution_for_expr_or_pat(expr_id)
|
||||
|
|
@ -1401,7 +1401,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
db: &'db dyn HirDatabase,
|
||||
func: FunctionId,
|
||||
substs: GenericArgs<'db>,
|
||||
) -> FunctionId {
|
||||
) -> Function {
|
||||
self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0
|
||||
}
|
||||
|
||||
|
|
@ -1410,13 +1410,19 @@ impl<'db> SourceAnalyzer<'db> {
|
|||
db: &'db dyn HirDatabase,
|
||||
func: FunctionId,
|
||||
substs: GenericArgs<'db>,
|
||||
) -> (FunctionId, GenericArgs<'db>) {
|
||||
) -> (Function, GenericArgs<'db>) {
|
||||
let owner = match self.resolver.body_owner() {
|
||||
Some(it) => it,
|
||||
None => return (func, substs),
|
||||
None => return (func.into(), substs),
|
||||
};
|
||||
let env = self.param_and(db.trait_environment_for_body(owner));
|
||||
db.lookup_impl_method(env, func, substs)
|
||||
let (func, args) = db.lookup_impl_method(env, func, substs);
|
||||
match func {
|
||||
Either::Left(func) => (func.into(), args),
|
||||
Either::Right((impl_, method)) => {
|
||||
(Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_impl_const_or_trait_def_with_subst(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use ide_db::{
|
|||
SnippetCap,
|
||||
assists::ExprFillDefaultMode,
|
||||
imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig},
|
||||
rename::RenameConfig,
|
||||
};
|
||||
|
||||
use crate::AssistKind;
|
||||
|
|
@ -27,6 +28,7 @@ pub struct AssistConfig {
|
|||
pub code_action_grouping: bool,
|
||||
pub expr_fill_default: ExprFillDefaultMode,
|
||||
pub prefer_self_ty: bool,
|
||||
pub show_rename_conflicts: bool,
|
||||
}
|
||||
|
||||
impl AssistConfig {
|
||||
|
|
@ -46,4 +48,8 @@ impl AssistConfig {
|
|||
allow_unstable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename_config(&self) -> RenameConfig {
|
||||
RenameConfig { show_conflicts: self.show_rename_conflicts }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ fn let_stmt_to_guarded_return(
|
|||
let let_else_stmt = make::let_else_stmt(
|
||||
happy_pattern,
|
||||
let_stmt.ty(),
|
||||
expr,
|
||||
expr.reset_indent(),
|
||||
ast::make::tail_only_block_expr(early_expression),
|
||||
);
|
||||
let let_else_stmt = let_else_stmt.indent(let_indent_level);
|
||||
|
|
@ -275,11 +275,11 @@ fn flat_let_chain(mut expr: ast::Expr) -> Vec<ast::Expr> {
|
|||
&& bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
|
||||
&& let (Some(lhs), Some(rhs)) = (bin_expr.lhs(), bin_expr.rhs())
|
||||
{
|
||||
reduce_cond(rhs);
|
||||
reduce_cond(rhs.reset_indent());
|
||||
expr = lhs;
|
||||
}
|
||||
|
||||
reduce_cond(expr);
|
||||
reduce_cond(expr.reset_indent());
|
||||
chains.reverse();
|
||||
chains
|
||||
}
|
||||
|
|
@ -1019,6 +1019,63 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indentations() {
|
||||
check_assist(
|
||||
convert_to_guarded_return,
|
||||
r#"
|
||||
mod indent {
|
||||
fn main() {
|
||||
$0if let None = Some(
|
||||
92
|
||||
) {
|
||||
foo(
|
||||
93
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
mod indent {
|
||||
fn main() {
|
||||
let None = Some(
|
||||
92
|
||||
) else { return };
|
||||
foo(
|
||||
93
|
||||
);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_assist(
|
||||
convert_to_guarded_return,
|
||||
r#"
|
||||
//- minicore: option
|
||||
mod indent {
|
||||
fn foo(_: i32) -> Option<i32> { None }
|
||||
fn main() {
|
||||
$0let x = foo(
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
mod indent {
|
||||
fn foo(_: i32) -> Option<i32> { None }
|
||||
fn main() {
|
||||
let Some(x) = foo(
|
||||
2
|
||||
) else { return };
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_already_converted_if() {
|
||||
check_assist_not_applicable(
|
||||
|
|
|
|||
|
|
@ -62,7 +62,9 @@ pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
|
|||
"Remove underscore from a used variable",
|
||||
text_range,
|
||||
|builder| {
|
||||
let changes = def.rename(&ctx.sema, new_name, RenameDefinition::Yes).unwrap();
|
||||
let changes = def
|
||||
.rename(&ctx.sema, new_name, RenameDefinition::Yes, &ctx.config.rename_config())
|
||||
.unwrap();
|
||||
builder.source_change = changes;
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
|
|||
code_action_grouping: true,
|
||||
expr_fill_default: ExprFillDefaultMode::Todo,
|
||||
prefer_self_ty: false,
|
||||
show_rename_conflicts: true,
|
||||
};
|
||||
|
||||
pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
|
||||
|
|
@ -59,6 +60,7 @@ pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
|
|||
code_action_grouping: false,
|
||||
expr_fill_default: ExprFillDefaultMode::Todo,
|
||||
prefer_self_ty: false,
|
||||
show_rename_conflicts: true,
|
||||
};
|
||||
|
||||
pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
|
||||
|
|
@ -80,6 +82,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
|
|||
code_action_grouping: true,
|
||||
expr_fill_default: ExprFillDefaultMode::Todo,
|
||||
prefer_self_ty: false,
|
||||
show_rename_conflicts: true,
|
||||
};
|
||||
|
||||
pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
|
||||
|
|
@ -101,6 +104,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
|
|||
code_action_grouping: true,
|
||||
expr_fill_default: ExprFillDefaultMode::Todo,
|
||||
prefer_self_ty: false,
|
||||
show_rename_conflicts: true,
|
||||
};
|
||||
|
||||
fn assists(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pub(crate) mod format_string;
|
|||
pub(crate) mod item_list;
|
||||
pub(crate) mod keyword;
|
||||
pub(crate) mod lifetime;
|
||||
pub(crate) mod macro_def;
|
||||
pub(crate) mod mod_;
|
||||
pub(crate) mod pattern;
|
||||
pub(crate) mod postfix;
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ fn foo(u: U) { u.$0 }
|
|||
fn test_method_completion_only_fitting_impls() {
|
||||
check_no_kw(
|
||||
r#"
|
||||
struct A<T> {}
|
||||
struct A<T>(T);
|
||||
impl A<u32> {
|
||||
fn the_method(&self) {}
|
||||
}
|
||||
|
|
@ -662,6 +662,7 @@ impl A<i32> {
|
|||
fn foo(a: A<u32>) { a.$0 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
fd 0 u32
|
||||
me the_method() fn(&self)
|
||||
"#]],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
//! Completion for macro meta-variable segments
|
||||
|
||||
use ide_db::SymbolKind;
|
||||
|
||||
use crate::{CompletionItem, Completions, context::CompletionContext};
|
||||
|
||||
pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_>) {
|
||||
for &label in MACRO_SEGMENTS {
|
||||
let item =
|
||||
CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition);
|
||||
item.add_to(acc, ctx.db);
|
||||
}
|
||||
}
|
||||
|
||||
const MACRO_SEGMENTS: &[&str] = &[
|
||||
"ident",
|
||||
"block",
|
||||
"stmt",
|
||||
"expr",
|
||||
"pat",
|
||||
"ty",
|
||||
"lifetime",
|
||||
"literal",
|
||||
"path",
|
||||
"meta",
|
||||
"tt",
|
||||
"item",
|
||||
"vis",
|
||||
"expr_2021",
|
||||
"pat_param",
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue