Auto merge of #140529 - matthiaskrgr:rollup-jpaa2ky, r=matthiaskrgr
Rollup of 10 pull requests Successful merges: - #140385 (Subtree update of `rust-analyzer`) - #140458 (Fix for async drop ice with partly dropped tuple) - #140465 (chore: edit and move tests) - #140467 (Don't FCW assoc consts in patterns) - #140468 (Minor tweaks to make some normalization (adjacent) code less confusing) - #140470 (CI: rfl: move job forward to Linux v6.15-rc4) - #140476 (chore: delete unused ui/auxiliary crates) - #140481 (Require sanitizers be enabled for asan_odr_windows.rs) - #140486 (rustfmt: Also allow bool literals as first item of let chain) - #140494 (Parser: Document restrictions) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
b45dd71d18
896 changed files with 33442 additions and 39725 deletions
|
|
@ -1,6 +1,12 @@
|
|||
// Type resolution: the phase that finds all the types in the AST with
|
||||
// unresolved type variables and replaces "ty_var" types with their
|
||||
// generic parameters.
|
||||
//! During type inference, partially inferred terms are
|
||||
//! represented using inference variables (ty::Infer). These don't appear in
|
||||
//! the final [`ty::TypeckResults`] since all of the types should have been
|
||||
//! inferred once typeck is done.
|
||||
//!
|
||||
//! When type inference is running however, having to update the typeck results
|
||||
//! every time a new type is inferred would be unreasonably slow, so instead all
|
||||
//! of the replacement happens at the end in [`FnCtxt::resolve_type_vars_in_body`],
|
||||
//! which creates a new `TypeckResults` which doesn't contain any inference variables.
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
|
@ -26,15 +32,6 @@ use crate::FnCtxt;
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// Entry point
|
||||
|
||||
// During type inference, partially inferred types are
|
||||
// represented using Type variables (ty::Infer). These don't appear in
|
||||
// the final TypeckResults since all of the types should have been
|
||||
// inferred once typeck is done.
|
||||
// When type inference is running however, having to update the typeck
|
||||
// typeck results every time a new type is inferred would be unreasonably slow,
|
||||
// so instead all of the replacement happens at the end in
|
||||
// resolve_type_vars_in_body, which creates a new TypeTables which
|
||||
// doesn't contain any inference types.
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(crate) fn resolve_type_vars_in_body(
|
||||
&self,
|
||||
|
|
@ -89,14 +86,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The Writeback context. This visitor walks the HIR, checking the
|
||||
// fn-specific typeck results to find references to types or regions. It
|
||||
// resolves those regions to remove inference variables and writes the
|
||||
// final result back into the master typeck results in the tcx. Here and
|
||||
// there, it applies a few ad-hoc checks that were not convenient to
|
||||
// do elsewhere.
|
||||
|
||||
/// The Writeback context. This visitor walks the HIR, checking the
|
||||
/// fn-specific typeck results to find inference variables. It resolves
|
||||
/// those inference variables and writes the final result into the
|
||||
/// `TypeckResults`. It also applies a few ad-hoc checks that were not
|
||||
/// convenient to do elsewhere.
|
||||
struct WritebackCx<'cx, 'tcx> {
|
||||
fcx: &'cx FnCtxt<'cx, 'tcx>,
|
||||
|
||||
|
|
@ -877,7 +871,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
|
|||
let cause = ObligationCause::misc(self.span.to_span(tcx), body_id);
|
||||
let at = self.fcx.at(&cause, self.fcx.param_env);
|
||||
let universes = vec![None; outer_exclusive_binder(value).as_usize()];
|
||||
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_goals(
|
||||
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
|
||||
at, value, universes,
|
||||
) {
|
||||
Ok((value, goals)) => {
|
||||
|
|
|
|||
|
|
@ -115,15 +115,16 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// @lcnr believes that successfully evaluating even though there are
|
||||
// used generic parameters is a bug of evaluation, so checking for it
|
||||
// here does feel somewhat sensible.
|
||||
if !self.features().generic_const_exprs() && ct.args.has_non_region_param() {
|
||||
let def_kind = self.def_kind(instance.def_id());
|
||||
assert!(
|
||||
matches!(
|
||||
def_kind,
|
||||
DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
|
||||
),
|
||||
"{cid:?} is {def_kind:?}",
|
||||
);
|
||||
if !self.features().generic_const_exprs()
|
||||
&& ct.args.has_non_region_param()
|
||||
// We only FCW for anon consts as repeat expr counts with anon consts are the only place
|
||||
// that we have a back compat hack for. We don't need to check this is a const argument
|
||||
// as only anon consts as const args should get evaluated "for the type system".
|
||||
//
|
||||
// If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc
|
||||
// consts in pattern positions. #140447
|
||||
&& self.def_kind(instance.def_id()) == DefKind::AnonConst
|
||||
{
|
||||
let mir_body = self.mir_for_ctfe(instance.def_id());
|
||||
if mir_body.is_polymorphic {
|
||||
let Some(local_def_id) = ct.def.as_local() else { return };
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ where
|
|||
if self.tcx().features().async_drop()
|
||||
&& self.elaborator.body().coroutine.is_some()
|
||||
&& self.elaborator.allow_async_drops()
|
||||
&& !self.elaborator.body()[bb].is_cleanup
|
||||
&& !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup
|
||||
&& drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
|
||||
{
|
||||
self.build_async_drop(
|
||||
|
|
|
|||
|
|
@ -148,11 +148,20 @@ impl<'tcx> MirPatch<'tcx> {
|
|||
self.term_patch_map[bb].is_some()
|
||||
}
|
||||
|
||||
/// Universal getter for block data, either it is in 'old' blocks or in patched ones
|
||||
pub(crate) fn block<'a>(
|
||||
&'a self,
|
||||
body: &'a Body<'tcx>,
|
||||
bb: BasicBlock,
|
||||
) -> &'a BasicBlockData<'tcx> {
|
||||
match bb.index().checked_sub(body.basic_blocks.len()) {
|
||||
Some(new) => &self.new_blocks[new],
|
||||
None => &body[bb],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn terminator_loc(&self, body: &Body<'tcx>, bb: BasicBlock) -> Location {
|
||||
let offset = match bb.index().checked_sub(body.basic_blocks.len()) {
|
||||
Some(index) => self.new_blocks[index].statements.len(),
|
||||
None => body[bb].statements.len(),
|
||||
};
|
||||
let offset = self.block(body, bb).statements.len();
|
||||
Location { block: bb, statement_index: offset }
|
||||
}
|
||||
|
||||
|
|
@ -284,10 +293,7 @@ impl<'tcx> MirPatch<'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn source_info_for_location(&self, body: &Body<'tcx>, loc: Location) -> SourceInfo {
|
||||
let data = match loc.block.index().checked_sub(body.basic_blocks.len()) {
|
||||
Some(new) => &self.new_blocks[new],
|
||||
None => &body[loc.block],
|
||||
};
|
||||
let data = self.block(body, loc.block);
|
||||
Self::source_info_for_index(data, loc)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,13 +58,62 @@ mod tokenstream {
|
|||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Restrictions applied while parsing.
|
||||
///
|
||||
/// The parser maintains a bitset of restrictions it will honor while
|
||||
/// parsing. This is essentially used as a way of tracking state of what
|
||||
/// is being parsed and to change behavior based on that.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Restrictions: u8 {
|
||||
/// Restricts expressions for use in statement position.
|
||||
///
|
||||
/// When expressions are used in various places, like statements or
|
||||
/// match arms, this is used to stop parsing once certain tokens are
|
||||
/// reached.
|
||||
///
|
||||
/// For example, `if true {} & 1` with `STMT_EXPR` in effect is parsed
|
||||
/// as two separate expression statements (`if` and a reference to 1).
|
||||
/// Otherwise it is parsed as a bitwise AND where `if` is on the left
|
||||
/// and 1 is on the right.
|
||||
const STMT_EXPR = 1 << 0;
|
||||
/// Do not allow struct literals.
|
||||
///
|
||||
/// There are several places in the grammar where we don't want to
|
||||
/// allow struct literals because they can require lookahead, or
|
||||
/// otherwise could be ambiguous or cause confusion. For example,
|
||||
/// `if Foo {} {}` isn't clear if it is `Foo{}` struct literal, or
|
||||
/// just `Foo` is the condition, followed by a consequent block,
|
||||
/// followed by an empty block.
|
||||
///
|
||||
/// See [RFC 92](https://rust-lang.github.io/rfcs/0092-struct-grammar.html).
|
||||
const NO_STRUCT_LITERAL = 1 << 1;
|
||||
/// Used to provide better error messages for const generic arguments.
|
||||
///
|
||||
/// An un-braced const generic argument is limited to a very small
|
||||
/// subset of expressions. This is used to detect the situation where
|
||||
/// an expression outside of that subset is used, and to suggest to
|
||||
/// wrap the expression in braces.
|
||||
const CONST_EXPR = 1 << 2;
|
||||
/// Allows `let` expressions.
|
||||
///
|
||||
/// `let pattern = scrutinee` is parsed as an expression, but it is
|
||||
/// only allowed in let chains (`if` and `while` conditions).
|
||||
/// Otherwise it is not an expression (note that `let` in statement
|
||||
/// positions is treated as a `StmtKind::Let` statement, which has a
|
||||
/// slightly different grammar).
|
||||
const ALLOW_LET = 1 << 3;
|
||||
/// Used to detect a missing `=>` in a match guard.
|
||||
///
|
||||
/// This is used for error handling in a match guard to give a better
|
||||
/// error message if the `=>` is missing. It is set when parsing the
|
||||
/// guard expression.
|
||||
const IN_IF_GUARD = 1 << 4;
|
||||
/// Used to detect the incorrect use of expressions in patterns.
|
||||
///
|
||||
/// This is used for error handling while parsing a pattern. During
|
||||
/// error recovery, this will be set to try to parse the pattern as an
|
||||
/// expression, but halts parsing the expression when reaching certain
|
||||
/// tokens like `=`.
|
||||
const IS_PAT = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ pub use fulfill::{FulfillmentCtxt, NextSolverError};
|
|||
pub(crate) use normalize::deeply_normalize_for_diagnostics;
|
||||
pub use normalize::{
|
||||
deeply_normalize, deeply_normalize_with_skipped_universes,
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_goals,
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals,
|
||||
};
|
||||
pub use select::InferCtxtSelectExt;
|
||||
|
|
|
|||
|
|
@ -45,9 +45,11 @@ where
|
|||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
|
||||
{
|
||||
let (value, goals) =
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_goals(at, value, universes)?;
|
||||
assert_eq!(goals, vec![]);
|
||||
let (value, coroutine_goals) =
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
|
||||
at, value, universes,
|
||||
)?;
|
||||
assert_eq!(coroutine_goals, vec![]);
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
|
@ -59,9 +61,9 @@ where
|
|||
/// entered before passing `value` to the function. This is currently needed for
|
||||
/// `normalize_erasing_regions`, which skips binders as it walks through a type.
|
||||
///
|
||||
/// This returns a set of stalled obligations if the typing mode of the underlying infcx
|
||||
/// has any stalled coroutine def ids.
|
||||
pub fn deeply_normalize_with_skipped_universes_and_ambiguous_goals<'tcx, T, E>(
|
||||
/// This returns a set of stalled obligations involving coroutines if the typing mode of
|
||||
/// the underlying infcx has any stalled coroutine def ids.
|
||||
pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'tcx, T, E>(
|
||||
at: At<'_, 'tcx>,
|
||||
value: T,
|
||||
universes: Vec<Option<UniverseIndex>>,
|
||||
|
|
@ -71,11 +73,16 @@ where
|
|||
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
|
||||
{
|
||||
let fulfill_cx = FulfillmentCtxt::new(at.infcx);
|
||||
let mut folder =
|
||||
NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_goals: vec![] };
|
||||
let mut folder = NormalizationFolder {
|
||||
at,
|
||||
fulfill_cx,
|
||||
depth: 0,
|
||||
universes,
|
||||
stalled_coroutine_goals: vec![],
|
||||
};
|
||||
let value = value.try_fold_with(&mut folder)?;
|
||||
let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
|
||||
if errors.is_empty() { Ok((value, folder.stalled_goals)) } else { Err(errors) }
|
||||
if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) }
|
||||
}
|
||||
|
||||
struct NormalizationFolder<'me, 'tcx, E> {
|
||||
|
|
@ -83,7 +90,7 @@ struct NormalizationFolder<'me, 'tcx, E> {
|
|||
fulfill_cx: FulfillmentCtxt<'tcx, E>,
|
||||
depth: usize,
|
||||
universes: Vec<Option<UniverseIndex>>,
|
||||
stalled_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
stalled_coroutine_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'tcx, E> NormalizationFolder<'_, 'tcx, E>
|
||||
|
|
@ -182,7 +189,7 @@ where
|
|||
return Err(errors);
|
||||
}
|
||||
|
||||
self.stalled_goals.extend(
|
||||
self.stalled_coroutine_goals.extend(
|
||||
self.fulfill_cx
|
||||
.drain_stalled_obligations_for_coroutines(self.at.infcx)
|
||||
.into_iter()
|
||||
|
|
@ -298,13 +305,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
|
|||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let infcx = self.at.infcx;
|
||||
let result =
|
||||
infcx.commit_if_ok(|_| {
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_goals::<
|
||||
_,
|
||||
ScrubbedTraitError<'tcx>,
|
||||
>(self.at, ty, vec![None; ty.outer_exclusive_binder().as_usize()])
|
||||
});
|
||||
let result: Result<_, Vec<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| {
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
|
||||
self.at,
|
||||
ty,
|
||||
vec![None; ty.outer_exclusive_binder().as_usize()],
|
||||
)
|
||||
});
|
||||
match result {
|
||||
Ok((ty, _)) => ty,
|
||||
Err(_) => ty.super_fold_with(self),
|
||||
|
|
@ -313,13 +320,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
|
|||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
let infcx = self.at.infcx;
|
||||
let result =
|
||||
infcx.commit_if_ok(|_| {
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_goals::<
|
||||
_,
|
||||
ScrubbedTraitError<'tcx>,
|
||||
>(self.at, ct, vec![None; ct.outer_exclusive_binder().as_usize()])
|
||||
});
|
||||
let result: Result<_, Vec<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| {
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
|
||||
self.at,
|
||||
ct,
|
||||
vec![None; ct.outer_exclusive_binder().as_usize()],
|
||||
)
|
||||
});
|
||||
match result {
|
||||
Ok((ct, _)) => ct,
|
||||
Err(_) => ct.super_fold_with(self),
|
||||
|
|
|
|||
|
|
@ -260,11 +260,14 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
|
|||
}
|
||||
|
||||
ty::Projection if !data.has_escaping_bound_vars() => {
|
||||
// This branch is *mostly* just an optimization: when we don't
|
||||
// have escaping bound vars, we don't need to replace them with
|
||||
// placeholders (see branch below). *Also*, we know that we can
|
||||
// register an obligation to *later* project, since we know
|
||||
// there won't be bound vars there.
|
||||
// When we don't have escaping bound vars we can normalize ambig aliases
|
||||
// to inference variables (done in `normalize_projection_ty`). This would
|
||||
// be wrong if there were escaping bound vars as even if we instantiated
|
||||
// the bound vars with placeholders, we wouldn't be able to map them back
|
||||
// after normalization succeeded.
|
||||
//
|
||||
// Also, as an optimization: when we don't have escaping bound vars, we don't
|
||||
// need to replace them with placeholders (see branch below).
|
||||
let data = data.fold_with(self);
|
||||
let normalized_ty = project::normalize_projection_ty(
|
||||
self.selcx,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
set -euo pipefail
|
||||
|
||||
LINUX_VERSION=v6.14-rc3
|
||||
LINUX_VERSION=v6.15-rc4
|
||||
|
||||
# Build rustc, rustdoc, cargo, clippy-driver and rustfmt
|
||||
../x.py build --stage 2 library rustdoc clippy rustfmt
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ assignees: ''
|
|||
---
|
||||
|
||||
<!--
|
||||
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
|
||||
Troubleshooting guide: https://rust-analyzer.github.io/book/troubleshooting.html
|
||||
Forum for questions: https://users.rust-lang.org/c/ide/14
|
||||
|
||||
Before submitting, please make sure that you're not running into one of these known issues:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ assignees: ''
|
|||
---
|
||||
|
||||
<!--
|
||||
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
|
||||
Troubleshooting guide: https://rust-analyzer.github.io/book/troubleshooting.html
|
||||
|
||||
Please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
|
||||
-->
|
||||
|
|
|
|||
209
src/tools/rust-analyzer/.github/workflows/ci.yaml
vendored
209
src/tools/rust-analyzer/.github/workflows/ci.yaml
vendored
|
|
@ -15,7 +15,6 @@ env:
|
|||
CARGO_NET_RETRY: 10
|
||||
CI: 1
|
||||
RUST_BACKTRACE: short
|
||||
RUSTFLAGS: "-D warnings -D elided_lifetimes_in_paths -D explicit_outlives_requirements -D unsafe_op_in_unsafe_fn -D unused_extern_crates -D unused_lifetimes -D unreachable_pub"
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
|
|
@ -25,7 +24,6 @@ jobs:
|
|||
pull-requests: read
|
||||
outputs:
|
||||
typescript: ${{ steps.filter.outputs.typescript }}
|
||||
proc_macros: ${{ steps.filter.outputs.proc_macros }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@1441771bbfdd59dcd748680ee64ebd8faab1a242
|
||||
|
|
@ -34,52 +32,54 @@ jobs:
|
|||
filters: |
|
||||
typescript:
|
||||
- 'editors/code/**'
|
||||
proc_macros:
|
||||
- 'crates/tt/**'
|
||||
- 'crates/proc-macro-api/**'
|
||||
- 'crates/proc-macro-srv/**'
|
||||
- 'crates/proc-macro-srv-cli/**'
|
||||
|
||||
proc-macro-srv:
|
||||
needs: changes
|
||||
if: github.repository == 'rust-lang/rust-analyzer' && needs.changes.outputs.proc_macros == 'true'
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: proc-macro-srv
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTFLAGS: "-D warnings"
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Install rustup-toolchain-install-master
|
||||
run: cargo install rustup-toolchain-install-master@1.6.0
|
||||
|
||||
# Install a pinned rustc commit to avoid surprises
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update nightly
|
||||
rustup default nightly
|
||||
rustup component add --toolchain nightly rust-src rustfmt
|
||||
RUSTC_VERSION=`cat rust-version`
|
||||
rustup-toolchain-install-master ${RUSTC_VERSION} -c rust-src -c rustfmt
|
||||
rustup default ${RUSTC_VERSION}
|
||||
|
||||
# Emulate a nightly toolchain, because the toolchain installed above does not have "nightly"
|
||||
# in its version string.
|
||||
- name: Emulate a nightly toolchain
|
||||
run: echo "RUSTC_BOOTSTRAP=1" >> $GITHUB_ENV
|
||||
|
||||
# https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
|
||||
- name: Install Rust Problem Matcher
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: echo "::add-matcher::.github/rust.json"
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
|
||||
|
||||
- name: Bump opt-level
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features sysroot-abi -p rust-analyzer -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet
|
||||
run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet
|
||||
|
||||
- name: Check salsa dependency
|
||||
run: "! (cargo tree -p proc-macro-srv-cli | grep -q salsa)"
|
||||
|
||||
rust:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: Rust
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
CC: deny_c
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup default stable
|
||||
rustup component add --toolchain stable rust-src
|
||||
rustup component add --toolchain stable rust-src clippy
|
||||
# We always use a nightly rustfmt, regardless of channel, because we need
|
||||
# --file-lines.
|
||||
rustup toolchain install nightly --profile minimal --component rustfmt
|
||||
|
|
@ -102,63 +102,46 @@ jobs:
|
|||
if: matrix.os == 'ubuntu-latest'
|
||||
run: echo "::add-matcher::.github/rust.json"
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
|
||||
# - name: Cache Dependencies
|
||||
# uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
||||
# with:
|
||||
# workspaces: |
|
||||
# . -> target
|
||||
# ./crates/proc-macro-srv/proc-macro-test/imp -> target
|
||||
|
||||
- name: Bump opt-level
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||
- name: Install nextest
|
||||
uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Codegen checks (rust-analyzer)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cargo codegen --check
|
||||
|
||||
- name: Compile (tests)
|
||||
run: cargo test --no-run --locked
|
||||
- name: Compile tests
|
||||
run: cargo test --no-run
|
||||
|
||||
# It's faster to `test` before `build` ¯\_(ツ)_/¯
|
||||
- name: Compile (rust-analyzer)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cargo build --quiet
|
||||
- name: Run tests
|
||||
run: cargo nextest run --no-fail-fast --hide-progress-bar --status-level fail
|
||||
|
||||
- name: Test
|
||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push'
|
||||
run: cargo test -- --quiet
|
||||
|
||||
- name: Switch to stable toolchain
|
||||
- name: Cancel parallel jobs
|
||||
if: failure()
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup component add --toolchain stable rust-src clippy
|
||||
rustup default stable
|
||||
# https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#cancel-a-workflow-run
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/cancel
|
||||
|
||||
- name: Run analysis-stats on rust-analyzer
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats .
|
||||
|
||||
- name: Run analysis-stats on the rust standard libraries
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps --no-sysroot --no-test $(rustc --print sysroot)/lib/rustlib/src/rust/library/
|
||||
|
||||
- name: clippy
|
||||
if: matrix.os == 'windows-latest'
|
||||
- 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
|
||||
|
||||
- name: rustfmt
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cargo fmt -- --check
|
||||
|
||||
# Weird targets to catch non-portable code
|
||||
rust-cross:
|
||||
analysis-stats:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: Rust Cross
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
targets: "powerpc-unknown-linux-gnu x86_64-unknown-linux-musl"
|
||||
# The rust-analyzer binary is not expected to compile on WASM, but the IDE
|
||||
# crate should
|
||||
targets_ide: "wasm32-unknown-unknown"
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
|
@ -167,19 +150,91 @@ jobs:
|
|||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup target add ${{ env.targets }} ${{ env.targets_ide }}
|
||||
rustup default stable
|
||||
rustup component add rustfmt
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
|
||||
# - name: Cache Dependencies
|
||||
# uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
||||
|
||||
- name: Check
|
||||
- name: Bump opt-level
|
||||
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||
|
||||
- run: cargo build -p rust-analyzer
|
||||
|
||||
- name: ./rust-analyzer
|
||||
run: ./target/debug/rust-analyzer analysis-stats . -q
|
||||
|
||||
- name: sysroot/lib/rustlib/src/rust/library/
|
||||
run: ./target/debug/rust-analyzer analysis-stats --with-deps --no-sysroot --no-test $(rustc --print sysroot)/lib/rustlib/src/rust/library/ -q
|
||||
|
||||
rustfmt:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
for target in ${{ env.targets }}; do
|
||||
cargo check --target=$target --all-targets
|
||||
done
|
||||
for target in ${{ env.targets_ide }}; do
|
||||
cargo check -p ide --target=$target --all-targets
|
||||
done
|
||||
rustup update --no-self-update stable
|
||||
rustup default stable
|
||||
rustup component add rustfmt
|
||||
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
miri:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update nightly
|
||||
rustup default nightly
|
||||
rustup component add miri
|
||||
|
||||
# - name: Cache Dependencies
|
||||
# uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
||||
|
||||
- run: cargo miri test -p intern
|
||||
|
||||
# Weird targets to catch non-portable code
|
||||
rust-cross:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: Rust Cross
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
target: [powerpc-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-unknown]
|
||||
include:
|
||||
# The rust-analyzer binary is not expected to compile on WASM, but the IDE
|
||||
# crate should
|
||||
- target: wasm32-unknown-unknown
|
||||
ide-only: true
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup target add ${{ matrix.target }}
|
||||
|
||||
# - name: Cache Dependencies
|
||||
# uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
||||
|
||||
- run: cargo check --target=${{ matrix.target }} --all-targets -p ide
|
||||
if: ${{ matrix.ide-only }}
|
||||
- run: cargo check --target=${{ matrix.target }} --all-targets
|
||||
if: ${{ !matrix.ide-only }}
|
||||
|
||||
typescript:
|
||||
needs: changes
|
||||
|
|
@ -261,7 +316,7 @@ jobs:
|
|||
run: typos
|
||||
|
||||
conclusion:
|
||||
needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv]
|
||||
needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv, miri, rustfmt, analysis-stats]
|
||||
# We need to ensure this job does *not* get skipped if its dependencies fail,
|
||||
# because a skipped job is considered a success by GitHub. So we have to
|
||||
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
|
||||
|
|
|
|||
|
|
@ -29,19 +29,25 @@ jobs:
|
|||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
code-target: win32-x64
|
||||
pgo: clap-rs/clap@v4.5.36
|
||||
- os: windows-latest
|
||||
target: i686-pc-windows-msvc
|
||||
pgo: clap-rs/clap@v4.5.36
|
||||
- os: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
code-target: win32-arm64
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
zig_target: x86_64-unknown-linux-gnu.2.28
|
||||
# Use a container with glibc 2.28
|
||||
# Zig is not used because it doesn't work with PGO
|
||||
container: quay.io/pypa/manylinux_2_28_x86_64
|
||||
code-target: linux-x64
|
||||
- os: ubuntu-latest
|
||||
pgo: clap-rs/clap@v4.5.36
|
||||
- os: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-gnu
|
||||
zig_target: aarch64-unknown-linux-gnu.2.28
|
||||
container: quay.io/pypa/manylinux_2_28_aarch64
|
||||
code-target: linux-arm64
|
||||
pgo: clap-rs/clap@v4.5.36
|
||||
- os: ubuntu-latest
|
||||
target: arm-unknown-linux-gnueabihf
|
||||
zig_target: arm-unknown-linux-gnueabihf.2.28
|
||||
|
|
@ -49,9 +55,11 @@ jobs:
|
|||
- os: macos-13
|
||||
target: x86_64-apple-darwin
|
||||
code-target: darwin-x64
|
||||
- os: macos-13
|
||||
pgo: clap-rs/clap@v4.5.36
|
||||
- os: macos-14
|
||||
target: aarch64-apple-darwin
|
||||
code-target: darwin-arm64
|
||||
pgo: clap-rs/clap@v4.5.36
|
||||
|
||||
name: dist (${{ matrix.target }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
|
@ -71,10 +79,17 @@ jobs:
|
|||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install rustup
|
||||
if: ${{ matrix.container }}
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup component add rust-src
|
||||
# llvm-tools contain the llvm-profdata tool which is needed for PGO
|
||||
rustup component add rust-src ${{ matrix.pgo && 'llvm-tools' || '' }}
|
||||
rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Install Zig toolchain
|
||||
|
|
@ -87,11 +102,11 @@ jobs:
|
|||
|
||||
- name: Dist (plain)
|
||||
if: ${{ !matrix.zig_target }}
|
||||
run: cargo xtask dist --client-patch-version ${{ github.run_number }}
|
||||
run: cargo xtask dist --client-patch-version ${{ github.run_number }} ${{ matrix.pgo && format('--pgo {0}', matrix.pgo) || ''}}
|
||||
|
||||
- name: Dist (using zigbuild)
|
||||
if: ${{ matrix.zig_target }}
|
||||
run: RA_TARGET=${{ matrix.zig_target}} cargo xtask dist --client-patch-version ${{ github.run_number }} --zig
|
||||
run: RA_TARGET=${{ matrix.zig_target}} cargo xtask dist --client-patch-version ${{ github.run_number }} --zig ${{ matrix.pgo && format('--pgo {0}', matrix.pgo) || ''}}
|
||||
|
||||
- run: npm ci
|
||||
working-directory: editors/code
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,8 +4,8 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.84"
|
||||
edition = "2021"
|
||||
rust-version = "1.86"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["rust-analyzer team"]
|
||||
repository = "https://github.com/rust-lang/rust-analyzer"
|
||||
|
|
@ -46,7 +46,7 @@ debug = 2
|
|||
|
||||
# ungrammar = { path = "../ungrammar" }
|
||||
|
||||
# rust-analyzer-salsa = { path = "../salsa" }
|
||||
# salsa = { path = "../salsa" }
|
||||
|
||||
[workspace.dependencies]
|
||||
# local crates
|
||||
|
|
@ -72,7 +72,7 @@ proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" }
|
|||
proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
|
||||
profile = { path = "./crates/profile", version = "0.0.0" }
|
||||
project-model = { path = "./crates/project-model", version = "0.0.0" }
|
||||
ra-salsa = { path = "./crates/ra-salsa", package = "salsa", version = "0.0.0" }
|
||||
query-group = { package = "query-group-macro", path = "./crates/query-group-macro", version = "0.0.0" }
|
||||
span = { path = "./crates/span", version = "0.0.0" }
|
||||
stdx = { path = "./crates/stdx", version = "0.0.0" }
|
||||
syntax = { path = "./crates/syntax", version = "0.0.0" }
|
||||
|
|
@ -85,72 +85,69 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
|||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
edition = { path = "./crates/edition", version = "0.0.0" }
|
||||
|
||||
ra-ap-rustc_hashes = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.110", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.110", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.110", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.110", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.110", default-features = false }
|
||||
|
||||
# local crates that aren't published to crates.io. These should not have versions.
|
||||
|
||||
# in-tree crates that are published separately and follow semver. See lib/README.md
|
||||
line-index = { version = "0.1.2" }
|
||||
la-arena = { version = "0.3.1" }
|
||||
lsp-server = { version = "0.7.6" }
|
||||
lsp-server = { version = "0.7.8" }
|
||||
|
||||
# non-local crates
|
||||
anyhow = "1.0.75"
|
||||
arrayvec = "0.7.4"
|
||||
bitflags = "2.4.1"
|
||||
cargo_metadata = "0.18.1"
|
||||
camino = "1.1.6"
|
||||
chalk-solve = { version = "0.100.0", default-features = false }
|
||||
chalk-ir = "0.100.0"
|
||||
chalk-recursive = { version = "0.100.0", default-features = false }
|
||||
chalk-derive = "0.100.0"
|
||||
crossbeam-channel = "0.5.8"
|
||||
dissimilar = "1.0.7"
|
||||
anyhow = "1.0.97"
|
||||
arrayvec = "0.7.6"
|
||||
bitflags = "2.9.0"
|
||||
cargo_metadata = "0.19.2"
|
||||
camino = "1.1.9"
|
||||
chalk-solve = { version = "0.102.0", default-features = false }
|
||||
chalk-ir = "0.102.0"
|
||||
chalk-recursive = { version = "0.102.0", default-features = false }
|
||||
chalk-derive = "0.102.0"
|
||||
crossbeam-channel = "0.5.15"
|
||||
dissimilar = "1.0.10"
|
||||
dot = "0.1.4"
|
||||
either = "1.9.0"
|
||||
expect-test = "1.4.0"
|
||||
hashbrown = { version = "0.14", features = [
|
||||
"inline-more",
|
||||
], default-features = false }
|
||||
indexmap = "2.1.0"
|
||||
itertools = "0.12.0"
|
||||
libc = "0.2.150"
|
||||
libloading = "0.8.0"
|
||||
memmap2 = "0.5.4"
|
||||
either = "1.15.0"
|
||||
expect-test = "1.5.1"
|
||||
indexmap = { version = "2.8.0", features = ["serde"] }
|
||||
itertools = "0.14.0"
|
||||
libc = "0.2.171"
|
||||
libloading = "0.8.6"
|
||||
memmap2 = "0.9.5"
|
||||
nohash-hasher = "0.2.0"
|
||||
oorandom = "11.1.3"
|
||||
object = { version = "0.33.0", default-features = false, features = [
|
||||
oorandom = "11.1.5"
|
||||
object = { version = "0.36.7", default-features = false, features = [
|
||||
"std",
|
||||
"read_core",
|
||||
"elf",
|
||||
"macho",
|
||||
"pe",
|
||||
] }
|
||||
process-wrap = { version = "8.0.2", features = ["std"] }
|
||||
process-wrap = { version = "8.2.0", features = ["std"] }
|
||||
pulldown-cmark-to-cmark = "10.0.4"
|
||||
pulldown-cmark = { version = "0.9.0", default-features = false }
|
||||
rayon = "1.8.0"
|
||||
rustc-hash = "2.0.0"
|
||||
pulldown-cmark = { version = "0.9.6", default-features = false }
|
||||
rayon = "1.10.0"
|
||||
salsa = "0.20.0"
|
||||
semver = "1.0.26"
|
||||
serde = { version = "1.0.219" }
|
||||
serde_derive = { version = "1.0.219" }
|
||||
serde_json = "1.0.140"
|
||||
rustc-hash = "2.1.1"
|
||||
rustc-literal-escaper = "0.0.2"
|
||||
semver = "1.0.14"
|
||||
serde = { version = "1.0.192" }
|
||||
serde_derive = { version = "1.0.192" }
|
||||
serde_json = "1.0.108"
|
||||
smallvec = { version = "1.10.0", features = [
|
||||
smallvec = { version = "1.14.0", features = [
|
||||
"const_new",
|
||||
"union",
|
||||
"const_generics",
|
||||
] }
|
||||
smol_str = "0.3.2"
|
||||
text-size = "1.1.1"
|
||||
tracing = "0.1.40"
|
||||
tracing-tree = "0.3.0"
|
||||
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
|
||||
tracing = "0.1.41"
|
||||
tracing-tree = "0.4.0"
|
||||
tracing-subscriber = { version = "0.3.19", default-features = false, features = [
|
||||
"registry",
|
||||
"fmt",
|
||||
"local-time",
|
||||
|
|
@ -159,12 +156,15 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
|
|||
"tracing-log",
|
||||
] }
|
||||
triomphe = { version = "0.1.14", default-features = false, features = ["std"] }
|
||||
url = "2.3.1"
|
||||
xshell = "0.2.5"
|
||||
|
||||
url = "2.5.4"
|
||||
xshell = "0.2.7"
|
||||
|
||||
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
||||
dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
||||
dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] }
|
||||
# We need to freeze the version of the crate, as it needs to match with dashmap
|
||||
hashbrown = { version = "0.14.0", features = [
|
||||
"inline-more",
|
||||
], default-features = false }
|
||||
|
||||
[workspace.lints.rust]
|
||||
# remember to update RUSTFLAGS in ci.yml if you add something here
|
||||
|
|
@ -172,6 +172,7 @@ dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
|||
elided_lifetimes_in_paths = "warn"
|
||||
explicit_outlives_requirements = "warn"
|
||||
unsafe_op_in_unsafe_fn = "warn"
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] }
|
||||
unused_extern_crates = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unreachable_pub = "warn"
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ macro_rules! __ra_macro_fixture526 {($expr : expr )=>{|| -> _ { Some ($expr )}(
|
|||
macro_rules! __ra_macro_fixture527 {($($arg : tt )*)=>($crate :: io :: _print ($crate :: format_args ! ($($arg )*))); }
|
||||
macro_rules! __ra_macro_fixture528 {($fmt : literal , $($tt : tt ),*)=>{ mbe :: ExpandError :: ProcMacroError ( tt :: ExpansionError :: Unknown ( format ! ($fmt , $($tt ),*)))}; ($fmt : literal )=>{ mbe :: ExpandError :: ProcMacroError ( tt :: ExpansionError :: Unknown ($fmt . to_string ()))}}
|
||||
macro_rules! __ra_macro_fixture529 {($($tt : tt )* )=>{$crate :: quote :: IntoTt :: to_subtree ($crate :: __quote ! ($($tt )*))}}
|
||||
macro_rules! __ra_macro_fixture530 {()=>{ Vec ::< tt :: TokenTree >:: new ()}; (@ SUBTREE $delim : ident $($tt : tt )* )=>{{ let children = $crate :: __quote ! ($($tt )*); tt :: Subtree { delimiter : Some ( tt :: Delimiter { kind : tt :: DelimiterKind ::$delim , id : tt :: TokenId :: unspecified (), }), token_trees : $crate :: quote :: IntoTt :: to_tokens ( children ), }}}; (@ PUNCT $first : literal )=>{{ vec ! [ tt :: Leaf :: Punct ( tt :: Punct { char : $first , spacing : tt :: Spacing :: Alone , id : tt :: TokenId :: unspecified (), }). into ()]}}; (@ PUNCT $first : literal , $sec : literal )=>{{ vec ! [ tt :: Leaf :: Punct ( tt :: Punct { char : $first , spacing : tt :: Spacing :: Joint , id : tt :: TokenId :: unspecified (), }). into (), tt :: Leaf :: Punct ( tt :: Punct { char : $sec , spacing : tt :: Spacing :: Alone , id : tt :: TokenId :: unspecified (), }). into ()]}}; (# $first : ident $($tail : tt )* )=>{{ let token = $crate :: quote :: ToTokenTree :: to_token ($first ); let mut tokens = vec ! [ token . into ()]; let mut tail_tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($($tail )*)); tokens . append (& mut tail_tokens ); tokens }}; (## $first : ident $($tail : tt )* )=>{{ let mut tokens = $first . into_iter (). map ($crate :: quote :: ToTokenTree :: to_token ). collect ::< Vec < tt :: TokenTree >> (); let mut tail_tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($($tail )*)); tokens . append (& mut tail_tokens ); tokens }}; ({$($tt : tt )* })=>{$crate :: __quote ! (@ SUBTREE Brace $($tt )*)}; ([$($tt : tt )* ])=>{$crate :: __quote ! (@ SUBTREE Bracket $($tt )*)}; (($($tt : tt )* ))=>{$crate :: __quote ! (@ SUBTREE Parenthesis $($tt )*)}; ($tt : literal )=>{ vec ! [$crate :: quote :: ToTokenTree :: to_token ($tt ). into ()]}; ($tt : ident )=>{ vec ! [{ tt :: Leaf :: Ident ( tt :: Ident { text : stringify ! ($tt ). into (), id : tt :: TokenId :: unspecified (), }). into ()}]}; (-> )=>{$crate :: __quote ! (@ PUNCT '-' , '>' )}; (& )=>{$crate :: __quote ! (@ PUNCT '&' )}; (, )=>{$crate :: __quote ! (@ PUNCT ',' )}; (: )=>{$crate :: __quote ! (@ PUNCT ':' )}; (; )=>{$crate :: __quote ! (@ PUNCT ';' )}; (:: )=>{$crate :: __quote ! (@ PUNCT ':' , ':' )}; (. )=>{$crate :: __quote ! (@ PUNCT '.' )}; (< )=>{$crate :: __quote ! (@ PUNCT '<' )}; (> )=>{$crate :: __quote ! (@ PUNCT '>' )}; ($first : tt $($tail : tt )+ )=>{{ let mut tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($first )); let mut tail_tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($($tail )*)); tokens . append (& mut tail_tokens ); tokens }}; }
|
||||
macro_rules! __ra_macro_fixture530 {()=>{ Vec ::< tt :: TokenTree >:: new ()}; (@ SUBTREE $delim : ident $($tt : tt )* )=>{{ let children = $crate :: __quote ! ($($tt )*); tt :: Subtree { delimiter : Some ( tt :: Delimiter { kind : tt :: DelimiterKind ::$delim , id : tt :: TokenId :: unspecified (), }), token_trees : $crate :: quote :: IntoTt :: to_tokens ( children ), }}}; (@ PUNCT $first : literal )=>{{ vec ! [ tt :: Leaf :: Punct ( tt :: Punct { char : $first , spacing : tt :: Spacing :: Alone , id : tt :: TokenId :: unspecified (), }). into ()]}}; (@ PUNCT $first : literal , $sec : literal )=>{{ vec ! [ tt :: Leaf :: Punct ( tt :: Punct { char : $first , spacing : tt :: Spacing :: Joint , id : tt :: TokenId :: unspecified (), }). into (), tt :: Leaf :: Punct ( tt :: Punct { char : $sec , spacing : tt :: Spacing :: Alone , id : tt :: TokenId :: unspecified (), }). into ()]}}; (# $first : ident $($tail : tt )* )=>{{ let token = $crate :: quote :: ToTokenTree :: to_token ($first ); let mut tokens = vec ! [ token . into ()]; let mut tail_tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($($tail )*)); tokens . append (& mut tail_tokens ); tokens }}; (# # $first : ident $($tail : tt )* )=>{{ let mut tokens = $first . into_iter (). map ($crate :: quote :: ToTokenTree :: to_token ). collect ::< Vec < tt :: TokenTree >> (); let mut tail_tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($($tail )*)); tokens . append (& mut tail_tokens ); tokens }}; ({$($tt : tt )* })=>{$crate :: __quote ! (@ SUBTREE Brace $($tt )*)}; ([$($tt : tt )* ])=>{$crate :: __quote ! (@ SUBTREE Bracket $($tt )*)}; (($($tt : tt )* ))=>{$crate :: __quote ! (@ SUBTREE Parenthesis $($tt )*)}; ($tt : literal )=>{ vec ! [$crate :: quote :: ToTokenTree :: to_token ($tt ). into ()]}; ($tt : ident )=>{ vec ! [{ tt :: Leaf :: Ident ( tt :: Ident { text : stringify ! ($tt ). into (), id : tt :: TokenId :: unspecified (), }). into ()}]}; (-> )=>{$crate :: __quote ! (@ PUNCT '-' , '>' )}; (& )=>{$crate :: __quote ! (@ PUNCT '&' )}; (, )=>{$crate :: __quote ! (@ PUNCT ',' )}; (: )=>{$crate :: __quote ! (@ PUNCT ':' )}; (; )=>{$crate :: __quote ! (@ PUNCT ';' )}; (:: )=>{$crate :: __quote ! (@ PUNCT ':' , ':' )}; (. )=>{$crate :: __quote ! (@ PUNCT '.' )}; (< )=>{$crate :: __quote ! (@ PUNCT '<' )}; (> )=>{$crate :: __quote ! (@ PUNCT '>' )}; ($first : tt $($tail : tt )+ )=>{{ let mut tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($first )); let mut tail_tokens = $crate :: quote :: IntoTt :: to_tokens ($crate :: __quote ! ($($tail )*)); tokens . append (& mut tail_tokens ); tokens }}; }
|
||||
macro_rules! __ra_macro_fixture531 {($($name : ident )*)=>{$(if let Some ( it )= & self .$name { f . field ( stringify ! ($name ), it ); })*}}
|
||||
macro_rules! __ra_macro_fixture532 {($fmt : expr )=>{ RenameError ( format ! ($fmt ))}; ($fmt : expr , $($arg : tt )+)=>{ RenameError ( format ! ($fmt , $($arg )+))}}
|
||||
macro_rules! __ra_macro_fixture533 {($($tokens : tt )*)=>{ return Err ( format_err ! ($($tokens )*))}}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ rust-version.workspace = true
|
|||
[lib]
|
||||
|
||||
[dependencies]
|
||||
lz4_flex = { version = "0.11", default-features = false }
|
||||
|
||||
la-arena.workspace = true
|
||||
ra-salsa.workspace = true
|
||||
dashmap.workspace = true
|
||||
salsa.workspace = true
|
||||
query-group.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
triomphe.workspace = true
|
||||
semver.workspace = true
|
||||
|
|
@ -23,7 +23,6 @@ tracing.workspace = true
|
|||
|
||||
# local deps
|
||||
cfg.workspace = true
|
||||
stdx.workspace = true
|
||||
syntax.workspace = true
|
||||
vfs.workspace = true
|
||||
span.workspace = true
|
||||
|
|
|
|||
|
|
@ -3,23 +3,18 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use ra_salsa::Durability;
|
||||
use rustc_hash::FxHashMap;
|
||||
use salsa::Durability;
|
||||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
|
||||
use crate::{
|
||||
CrateGraph, CrateId, CrateWorkspaceData, SourceDatabaseFileInputExt, SourceRoot,
|
||||
SourceRootDatabase, SourceRootId,
|
||||
};
|
||||
use crate::{CrateGraphBuilder, CratesIdMap, RootQueryDb, SourceRoot, SourceRootId};
|
||||
|
||||
/// Encapsulate a bunch of raw `.set` calls on the database.
|
||||
#[derive(Default)]
|
||||
pub struct FileChange {
|
||||
pub roots: Option<Vec<SourceRoot>>,
|
||||
pub files_changed: Vec<(FileId, Option<String>)>,
|
||||
pub crate_graph: Option<CrateGraph>,
|
||||
pub ws_data: Option<FxHashMap<CrateId, Arc<CrateWorkspaceData>>>,
|
||||
pub crate_graph: Option<CrateGraphBuilder>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FileChange {
|
||||
|
|
@ -39,10 +34,6 @@ impl fmt::Debug for FileChange {
|
|||
}
|
||||
|
||||
impl FileChange {
|
||||
pub fn new() -> Self {
|
||||
FileChange::default()
|
||||
}
|
||||
|
||||
pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
|
||||
self.roots = Some(roots);
|
||||
}
|
||||
|
|
@ -51,48 +42,45 @@ impl FileChange {
|
|||
self.files_changed.push((file_id, new_text))
|
||||
}
|
||||
|
||||
pub fn set_crate_graph(&mut self, graph: CrateGraph) {
|
||||
pub fn set_crate_graph(&mut self, graph: CrateGraphBuilder) {
|
||||
self.crate_graph = Some(graph);
|
||||
}
|
||||
|
||||
pub fn set_ws_data(&mut self, data: FxHashMap<CrateId, Arc<CrateWorkspaceData>>) {
|
||||
self.ws_data = Some(data);
|
||||
}
|
||||
|
||||
pub fn apply(self, db: &mut dyn SourceRootDatabase) {
|
||||
pub fn apply(self, db: &mut dyn RootQueryDb) -> Option<CratesIdMap> {
|
||||
let _p = tracing::info_span!("FileChange::apply").entered();
|
||||
if let Some(roots) = self.roots {
|
||||
for (idx, root) in roots.into_iter().enumerate() {
|
||||
let root_id = SourceRootId(idx as u32);
|
||||
let durability = durability(&root);
|
||||
let durability = source_root_durability(&root);
|
||||
for file_id in root.iter() {
|
||||
db.set_file_source_root_with_durability(file_id, root_id, durability);
|
||||
}
|
||||
|
||||
db.set_source_root_with_durability(root_id, Arc::new(root), durability);
|
||||
}
|
||||
}
|
||||
|
||||
for (file_id, text) in self.files_changed {
|
||||
let source_root_id = db.file_source_root(file_id);
|
||||
let source_root = db.source_root(source_root_id);
|
||||
let durability = durability(&source_root);
|
||||
let source_root = db.source_root(source_root_id.source_root_id(db));
|
||||
|
||||
let durability = file_text_durability(&source_root.source_root(db));
|
||||
// XXX: can't actually remove the file, just reset the text
|
||||
let text = text.unwrap_or_default();
|
||||
db.set_file_text_with_durability(file_id, &text, durability)
|
||||
}
|
||||
|
||||
if let Some(crate_graph) = self.crate_graph {
|
||||
db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
|
||||
}
|
||||
if let Some(data) = self.ws_data {
|
||||
db.set_crate_workspace_data_with_durability(Arc::new(data), Durability::HIGH);
|
||||
return Some(crate_graph.set_in_db(db));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn durability(source_root: &SourceRoot) -> Durability {
|
||||
if source_root.is_library {
|
||||
Durability::HIGH
|
||||
} else {
|
||||
Durability::LOW
|
||||
}
|
||||
fn source_root_durability(source_root: &SourceRoot) -> Durability {
|
||||
if source_root.is_library { Durability::MEDIUM } else { Durability::LOW }
|
||||
}
|
||||
|
||||
fn file_text_durability(source_root: &SourceRoot) -> Durability {
|
||||
if source_root.is_library { Durability::HIGH } else { Durability::LOW }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,23 @@
|
|||
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
|
||||
//! actual IO is done and lowered to input.
|
||||
|
||||
use std::hash::BuildHasherDefault;
|
||||
use std::{fmt, mem, ops};
|
||||
|
||||
use cfg::CfgOptions;
|
||||
use cfg::{CfgOptions, HashableCfgOptions};
|
||||
use dashmap::DashMap;
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use intern::Symbol;
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, EditionedFileId};
|
||||
use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
|
||||
use salsa::{Durability, Setter};
|
||||
use span::Edition;
|
||||
use triomphe::Arc;
|
||||
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
|
||||
use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet};
|
||||
|
||||
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(String, AbsPathBuf), String>>;
|
||||
use crate::{CrateWorkspaceData, EditionedFileId, RootQueryDb};
|
||||
|
||||
pub type ProcMacroPaths = FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), String>>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct SourceRootId(pub u32);
|
||||
|
|
@ -64,30 +70,31 @@ impl SourceRoot {
|
|||
}
|
||||
}
|
||||
|
||||
/// `CrateGraph` is a bit of information which turns a set of text files into a
|
||||
/// number of Rust crates.
|
||||
///
|
||||
/// Each crate is defined by the `FileId` of its root module, the set of enabled
|
||||
/// `cfg` flags and the set of dependencies.
|
||||
///
|
||||
/// Note that, due to cfg's, there might be several crates for a single `FileId`!
|
||||
///
|
||||
/// For the purposes of analysis, a crate does not have a name. Instead, names
|
||||
/// are specified on dependency edges. That is, a crate might be known under
|
||||
/// different names in different dependent crates.
|
||||
///
|
||||
/// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
|
||||
/// language proper, not a concept of the build system. In practice, we get
|
||||
/// `CrateGraph` by lowering `cargo metadata` output.
|
||||
///
|
||||
/// `CrateGraph` is `!Serialize` by design, see
|
||||
/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CrateGraph {
|
||||
arena: Arena<CrateData>,
|
||||
#[derive(Default, Clone)]
|
||||
pub struct CrateGraphBuilder {
|
||||
arena: Arena<CrateBuilder>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for CrateGraph {
|
||||
pub type CrateBuilderId = Idx<CrateBuilder>;
|
||||
|
||||
impl ops::Index<CrateBuilderId> for CrateGraphBuilder {
|
||||
type Output = CrateBuilder;
|
||||
|
||||
fn index(&self, index: CrateBuilderId) -> &Self::Output {
|
||||
&self.arena[index]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CrateBuilder {
|
||||
pub basic: CrateDataBuilder,
|
||||
pub extra: ExtraCrateData,
|
||||
pub cfg_options: CfgOptions,
|
||||
pub env: Env,
|
||||
ws_data: Arc<CrateWorkspaceData>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for CrateGraphBuilder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_map()
|
||||
.entries(self.arena.iter().map(|(id, data)| (u32::from(id.into_raw()), data)))
|
||||
|
|
@ -95,8 +102,6 @@ impl fmt::Debug for CrateGraph {
|
|||
}
|
||||
}
|
||||
|
||||
pub type CrateId = Idx<CrateData>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CrateName(Symbol);
|
||||
|
||||
|
|
@ -105,11 +110,7 @@ impl CrateName {
|
|||
/// Dashes are not allowed in the crate names,
|
||||
/// hence the input string is returned as `Err` for those cases.
|
||||
pub fn new(name: &str) -> Result<CrateName, &str> {
|
||||
if name.contains('-') {
|
||||
Err(name)
|
||||
} else {
|
||||
Ok(Self(Symbol::intern(name)))
|
||||
}
|
||||
if name.contains('-') { Err(name) } else { Ok(Self(Symbol::intern(name))) }
|
||||
}
|
||||
|
||||
/// Creates a crate name, unconditionally replacing the dashes with underscores.
|
||||
|
|
@ -272,10 +273,49 @@ impl ReleaseChannel {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CrateData {
|
||||
/// The crate data from which we derive the `Crate`.
|
||||
///
|
||||
/// We want this to contain as little data as possible, because if it contains dependencies and
|
||||
/// something changes, this crate and all of its dependencies ids are invalidated, which causes
|
||||
/// pretty much everything to be recomputed. If the crate id is not invalidated, only this crate's
|
||||
/// information needs to be recomputed.
|
||||
///
|
||||
/// *Most* different crates have different root files (actually, pretty much all of them).
|
||||
/// Still, it is possible to have crates distinguished by other factors (e.g. dependencies).
|
||||
/// So we store only the root file - unless we find that this crate has the same root file as
|
||||
/// another crate, in which case we store all data for one of them (if one is a dependency of
|
||||
/// the other, we store for it, because it has more dependencies to be invalidated).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct UniqueCrateData {
|
||||
root_file_id: FileId,
|
||||
disambiguator: Option<Box<(BuiltCrateData, HashableCfgOptions)>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CrateData<Id> {
|
||||
pub root_file_id: FileId,
|
||||
pub edition: Edition,
|
||||
/// The dependencies of this crate.
|
||||
///
|
||||
/// Note that this may contain more dependencies than the crate actually uses.
|
||||
/// A common example is the test crate which is included but only actually is active when
|
||||
/// declared in source via `extern crate test`.
|
||||
pub dependencies: Vec<Dependency<Id>>,
|
||||
pub origin: CrateOrigin,
|
||||
pub is_proc_macro: bool,
|
||||
/// The working directory to run proc-macros in invoked in the context of this crate.
|
||||
/// This is the workspace root of the cargo workspace for workspace members, the crate manifest
|
||||
/// dir otherwise.
|
||||
// FIXME: This ought to be a `VfsPath` or something opaque.
|
||||
pub proc_macro_cwd: Arc<AbsPathBuf>,
|
||||
}
|
||||
|
||||
pub type CrateDataBuilder = CrateData<CrateBuilderId>;
|
||||
pub type BuiltCrateData = CrateData<Crate>;
|
||||
|
||||
/// Crate data unrelated to analysis.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtraCrateData {
|
||||
pub version: Option<String>,
|
||||
/// A name used in the package's project declaration: for Cargo projects,
|
||||
/// its `[package].name` can be different for other project types or even
|
||||
|
|
@ -284,21 +324,8 @@ pub struct CrateData {
|
|||
/// For purposes of analysis, crates are anonymous (only names in
|
||||
/// `Dependency` matters), this name should only be used for UI.
|
||||
pub display_name: Option<CrateDisplayName>,
|
||||
pub cfg_options: Arc<CfgOptions>,
|
||||
/// The cfg options that could be used by the crate
|
||||
pub potential_cfg_options: Option<Arc<CfgOptions>>,
|
||||
pub env: Env,
|
||||
/// The dependencies of this crate.
|
||||
///
|
||||
/// Note that this may contain more dependencies than the crate actually uses.
|
||||
/// A common example is the test crate which is included but only actually is active when
|
||||
/// declared in source via `extern crate test`.
|
||||
pub dependencies: Vec<Dependency>,
|
||||
pub origin: CrateOrigin,
|
||||
pub is_proc_macro: bool,
|
||||
/// The working directory to run proc-macros in. This is the workspace root of the cargo workspace
|
||||
/// for workspace members, the crate manifest dir otherwise.
|
||||
pub proc_macro_cwd: Option<AbsPathBuf>,
|
||||
pub potential_cfg_options: Option<CfgOptions>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq)]
|
||||
|
|
@ -326,22 +353,32 @@ impl fmt::Debug for Env {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Dependency {
|
||||
pub crate_id: CrateId,
|
||||
pub struct Dependency<Id> {
|
||||
pub crate_id: Id,
|
||||
pub name: CrateName,
|
||||
prelude: bool,
|
||||
sysroot: bool,
|
||||
}
|
||||
|
||||
impl Dependency {
|
||||
pub fn new(name: CrateName, crate_id: CrateId) -> Self {
|
||||
pub type DependencyBuilder = Dependency<CrateBuilderId>;
|
||||
pub type BuiltDependency = Dependency<Crate>;
|
||||
|
||||
impl DependencyBuilder {
|
||||
pub fn new(name: CrateName, crate_id: CrateBuilderId) -> Self {
|
||||
Self { name, crate_id, prelude: true, sysroot: false }
|
||||
}
|
||||
|
||||
pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool, sysroot: bool) -> Self {
|
||||
pub fn with_prelude(
|
||||
name: CrateName,
|
||||
crate_id: CrateBuilderId,
|
||||
prelude: bool,
|
||||
sysroot: bool,
|
||||
) -> Self {
|
||||
Self { name, crate_id, prelude, sysroot }
|
||||
}
|
||||
}
|
||||
|
||||
impl BuiltDependency {
|
||||
/// Whether this dependency is to be added to the depending crate's extern prelude.
|
||||
pub fn is_prelude(&self) -> bool {
|
||||
self.prelude
|
||||
|
|
@ -353,41 +390,71 @@ impl Dependency {
|
|||
}
|
||||
}
|
||||
|
||||
impl CrateGraph {
|
||||
pub type CratesIdMap = FxHashMap<CrateBuilderId, Crate>;
|
||||
|
||||
#[salsa::input]
|
||||
#[derive(Debug)]
|
||||
pub struct Crate {
|
||||
#[return_ref]
|
||||
pub data: BuiltCrateData,
|
||||
/// Crate data that is not needed for analysis.
|
||||
///
|
||||
/// This is split into a separate field to increase incrementality.
|
||||
#[return_ref]
|
||||
pub extra_data: ExtraCrateData,
|
||||
// This is in `Arc` because it is shared for all crates in a workspace.
|
||||
#[return_ref]
|
||||
pub workspace_data: Arc<CrateWorkspaceData>,
|
||||
#[return_ref]
|
||||
pub cfg_options: CfgOptions,
|
||||
#[return_ref]
|
||||
pub env: Env,
|
||||
}
|
||||
|
||||
/// The mapping from [`UniqueCrateData`] to their [`Crate`] input.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CratesMap(DashMap<UniqueCrateData, Crate, BuildHasherDefault<FxHasher>>);
|
||||
|
||||
impl CrateGraphBuilder {
|
||||
pub fn add_crate_root(
|
||||
&mut self,
|
||||
root_file_id: FileId,
|
||||
edition: Edition,
|
||||
display_name: Option<CrateDisplayName>,
|
||||
version: Option<String>,
|
||||
cfg_options: Arc<CfgOptions>,
|
||||
potential_cfg_options: Option<Arc<CfgOptions>>,
|
||||
mut cfg_options: CfgOptions,
|
||||
mut potential_cfg_options: Option<CfgOptions>,
|
||||
mut env: Env,
|
||||
origin: CrateOrigin,
|
||||
is_proc_macro: bool,
|
||||
proc_macro_cwd: Option<AbsPathBuf>,
|
||||
) -> CrateId {
|
||||
proc_macro_cwd: Arc<AbsPathBuf>,
|
||||
ws_data: Arc<CrateWorkspaceData>,
|
||||
) -> CrateBuilderId {
|
||||
env.entries.shrink_to_fit();
|
||||
let data = CrateData {
|
||||
root_file_id,
|
||||
edition,
|
||||
version,
|
||||
display_name,
|
||||
cfg_options.shrink_to_fit();
|
||||
if let Some(potential_cfg_options) = &mut potential_cfg_options {
|
||||
potential_cfg_options.shrink_to_fit();
|
||||
}
|
||||
self.arena.alloc(CrateBuilder {
|
||||
basic: CrateData {
|
||||
root_file_id,
|
||||
edition,
|
||||
dependencies: Vec::new(),
|
||||
origin,
|
||||
is_proc_macro,
|
||||
proc_macro_cwd,
|
||||
},
|
||||
extra: ExtraCrateData { version, display_name, potential_cfg_options },
|
||||
cfg_options,
|
||||
potential_cfg_options,
|
||||
env,
|
||||
dependencies: Vec::new(),
|
||||
origin,
|
||||
is_proc_macro,
|
||||
proc_macro_cwd,
|
||||
};
|
||||
self.arena.alloc(data)
|
||||
ws_data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_dep(
|
||||
&mut self,
|
||||
from: CrateId,
|
||||
dep: Dependency,
|
||||
from: CrateBuilderId,
|
||||
dep: DependencyBuilder,
|
||||
) -> Result<(), CyclicDependenciesError> {
|
||||
let _p = tracing::info_span!("add_dep").entered();
|
||||
|
||||
|
|
@ -395,37 +462,154 @@ impl CrateGraph {
|
|||
// that out, look for a path in the *opposite* direction, from `to` to
|
||||
// `from`.
|
||||
if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
|
||||
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
|
||||
let path =
|
||||
path.into_iter().map(|it| (it, self[it].extra.display_name.clone())).collect();
|
||||
let err = CyclicDependenciesError { path };
|
||||
assert!(err.from().0 == from && err.to().0 == dep.crate_id);
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
self.arena[from].add_dep(dep);
|
||||
self.arena[from].basic.dependencies.push(dep);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.arena.is_empty()
|
||||
pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap {
|
||||
let mut all_crates = Vec::with_capacity(self.arena.len());
|
||||
let mut visited = FxHashMap::default();
|
||||
let mut visited_root_files = FxHashSet::default();
|
||||
|
||||
let old_all_crates = db.all_crates();
|
||||
|
||||
let crates_map = db.crates_map();
|
||||
// salsa doesn't compare new input to old input to see if they are the same, so here we are doing all the work ourselves.
|
||||
for krate in self.iter() {
|
||||
go(
|
||||
&self,
|
||||
db,
|
||||
&crates_map,
|
||||
&mut visited,
|
||||
&mut visited_root_files,
|
||||
&mut all_crates,
|
||||
krate,
|
||||
);
|
||||
}
|
||||
|
||||
if **old_all_crates != *all_crates {
|
||||
db.set_all_crates_with_durability(
|
||||
Arc::new(all_crates.into_boxed_slice()),
|
||||
Durability::MEDIUM,
|
||||
);
|
||||
}
|
||||
|
||||
return visited;
|
||||
|
||||
fn go(
|
||||
graph: &CrateGraphBuilder,
|
||||
db: &mut dyn RootQueryDb,
|
||||
crates_map: &CratesMap,
|
||||
visited: &mut FxHashMap<CrateBuilderId, Crate>,
|
||||
visited_root_files: &mut FxHashSet<FileId>,
|
||||
all_crates: &mut Vec<Crate>,
|
||||
source: CrateBuilderId,
|
||||
) -> Crate {
|
||||
if let Some(&crate_id) = visited.get(&source) {
|
||||
return crate_id;
|
||||
}
|
||||
let krate = &graph[source];
|
||||
let dependencies = krate
|
||||
.basic
|
||||
.dependencies
|
||||
.iter()
|
||||
.map(|dep| BuiltDependency {
|
||||
crate_id: go(
|
||||
graph,
|
||||
db,
|
||||
crates_map,
|
||||
visited,
|
||||
visited_root_files,
|
||||
all_crates,
|
||||
dep.crate_id,
|
||||
),
|
||||
name: dep.name.clone(),
|
||||
prelude: dep.prelude,
|
||||
sysroot: dep.sysroot,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let crate_data = BuiltCrateData {
|
||||
dependencies,
|
||||
edition: krate.basic.edition,
|
||||
is_proc_macro: krate.basic.is_proc_macro,
|
||||
origin: krate.basic.origin.clone(),
|
||||
root_file_id: krate.basic.root_file_id,
|
||||
proc_macro_cwd: krate.basic.proc_macro_cwd.clone(),
|
||||
};
|
||||
let disambiguator = if visited_root_files.insert(krate.basic.root_file_id) {
|
||||
None
|
||||
} else {
|
||||
Some(Box::new((crate_data.clone(), krate.cfg_options.to_hashable())))
|
||||
};
|
||||
|
||||
let unique_crate_data =
|
||||
UniqueCrateData { root_file_id: krate.basic.root_file_id, disambiguator };
|
||||
let crate_input = match crates_map.0.entry(unique_crate_data) {
|
||||
Entry::Occupied(entry) => {
|
||||
let old_crate = *entry.get();
|
||||
if crate_data != *old_crate.data(db) {
|
||||
old_crate.set_data(db).with_durability(Durability::MEDIUM).to(crate_data);
|
||||
}
|
||||
if krate.extra != *old_crate.extra_data(db) {
|
||||
old_crate
|
||||
.set_extra_data(db)
|
||||
.with_durability(Durability::MEDIUM)
|
||||
.to(krate.extra.clone());
|
||||
}
|
||||
if krate.cfg_options != *old_crate.cfg_options(db) {
|
||||
old_crate
|
||||
.set_cfg_options(db)
|
||||
.with_durability(Durability::MEDIUM)
|
||||
.to(krate.cfg_options.clone());
|
||||
}
|
||||
if krate.env != *old_crate.env(db) {
|
||||
old_crate
|
||||
.set_env(db)
|
||||
.with_durability(Durability::MEDIUM)
|
||||
.to(krate.env.clone());
|
||||
}
|
||||
if krate.ws_data != *old_crate.workspace_data(db) {
|
||||
old_crate
|
||||
.set_workspace_data(db)
|
||||
.with_durability(Durability::MEDIUM)
|
||||
.to(krate.ws_data.clone());
|
||||
}
|
||||
old_crate
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
let input = Crate::builder(
|
||||
crate_data,
|
||||
krate.extra.clone(),
|
||||
krate.ws_data.clone(),
|
||||
krate.cfg_options.clone(),
|
||||
krate.env.clone(),
|
||||
)
|
||||
.durability(Durability::MEDIUM)
|
||||
.new(db);
|
||||
entry.insert(input);
|
||||
input
|
||||
}
|
||||
};
|
||||
all_crates.push(crate_input);
|
||||
visited.insert(source, crate_input);
|
||||
crate_input
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.arena.len()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
|
||||
pub fn iter(&self) -> impl Iterator<Item = CrateBuilderId> + '_ {
|
||||
self.arena.iter().map(|(idx, _)| idx)
|
||||
}
|
||||
|
||||
// FIXME: used for fixing up the toolchain sysroot, should be removed and done differently
|
||||
#[doc(hidden)]
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ {
|
||||
self.arena.iter_mut()
|
||||
}
|
||||
|
||||
/// Returns an iterator over all transitive dependencies of the given crate,
|
||||
/// including the crate itself.
|
||||
pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
|
||||
pub fn transitive_deps(&self, of: CrateBuilderId) -> impl Iterator<Item = CrateBuilderId> {
|
||||
let mut worklist = vec![of];
|
||||
let mut deps = FxHashSet::default();
|
||||
|
||||
|
|
@ -434,42 +618,15 @@ impl CrateGraph {
|
|||
continue;
|
||||
}
|
||||
|
||||
worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id));
|
||||
worklist.extend(self[krate].basic.dependencies.iter().map(|dep| dep.crate_id));
|
||||
}
|
||||
|
||||
deps.into_iter()
|
||||
}
|
||||
|
||||
/// Returns all transitive reverse dependencies of the given crate,
|
||||
/// including the crate itself.
|
||||
pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
|
||||
let mut worklist = vec![of];
|
||||
let mut rev_deps = FxHashSet::default();
|
||||
rev_deps.insert(of);
|
||||
|
||||
let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
|
||||
self.arena.iter().for_each(|(krate, data)| {
|
||||
data.dependencies
|
||||
.iter()
|
||||
.for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
|
||||
});
|
||||
|
||||
while let Some(krate) = worklist.pop() {
|
||||
if let Some(krate_rev_deps) = inverted_graph.get(&krate) {
|
||||
krate_rev_deps
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&rev_dep| rev_deps.insert(rev_dep))
|
||||
.for_each(|rev_dep| worklist.push(rev_dep));
|
||||
}
|
||||
}
|
||||
|
||||
rev_deps.into_iter()
|
||||
}
|
||||
|
||||
/// Returns all crates in the graph, sorted in topological order (ie. dependencies of a crate
|
||||
/// come before the crate itself).
|
||||
pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
|
||||
fn crates_in_topological_order(&self) -> Vec<CrateBuilderId> {
|
||||
let mut res = Vec::new();
|
||||
let mut visited = FxHashSet::default();
|
||||
|
||||
|
|
@ -480,15 +637,15 @@ impl CrateGraph {
|
|||
return res;
|
||||
|
||||
fn go(
|
||||
graph: &CrateGraph,
|
||||
visited: &mut FxHashSet<CrateId>,
|
||||
res: &mut Vec<CrateId>,
|
||||
source: CrateId,
|
||||
graph: &CrateGraphBuilder,
|
||||
visited: &mut FxHashSet<CrateBuilderId>,
|
||||
res: &mut Vec<CrateBuilderId>,
|
||||
source: CrateBuilderId,
|
||||
) {
|
||||
if !visited.insert(source) {
|
||||
return;
|
||||
}
|
||||
for dep in graph[source].dependencies.iter() {
|
||||
for dep in graph[source].basic.dependencies.iter() {
|
||||
go(graph, visited, res, dep.crate_id)
|
||||
}
|
||||
res.push(source)
|
||||
|
|
@ -504,23 +661,27 @@ impl CrateGraph {
|
|||
/// Returns a map mapping `other`'s IDs to the new IDs in `self`.
|
||||
pub fn extend(
|
||||
&mut self,
|
||||
mut other: CrateGraph,
|
||||
mut other: CrateGraphBuilder,
|
||||
proc_macros: &mut ProcMacroPaths,
|
||||
) -> FxHashMap<CrateId, CrateId> {
|
||||
) -> FxHashMap<CrateBuilderId, CrateBuilderId> {
|
||||
// Sorting here is a bit pointless because the input is likely already sorted.
|
||||
// However, the overhead is small and it makes the `extend` method harder to misuse.
|
||||
self.arena
|
||||
.iter_mut()
|
||||
.for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
|
||||
.for_each(|(_, data)| data.basic.dependencies.sort_by_key(|dep| dep.crate_id));
|
||||
|
||||
let m = self.len();
|
||||
let m = self.arena.len();
|
||||
let topo = other.crates_in_topological_order();
|
||||
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
|
||||
let mut id_map: FxHashMap<CrateBuilderId, CrateBuilderId> = FxHashMap::default();
|
||||
for topo in topo {
|
||||
let crate_data = &mut other.arena[topo];
|
||||
|
||||
crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
|
||||
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
|
||||
crate_data
|
||||
.basic
|
||||
.dependencies
|
||||
.iter_mut()
|
||||
.for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
|
||||
crate_data.basic.dependencies.sort_by_key(|dep| dep.crate_id);
|
||||
|
||||
let find = self.arena.iter().take(m).find_map(|(k, v)| (v == crate_data).then_some(k));
|
||||
let new_id = find.unwrap_or_else(|| self.arena.alloc(crate_data.clone()));
|
||||
|
|
@ -534,10 +695,10 @@ impl CrateGraph {
|
|||
|
||||
fn find_path(
|
||||
&self,
|
||||
visited: &mut FxHashSet<CrateId>,
|
||||
from: CrateId,
|
||||
to: CrateId,
|
||||
) -> Option<Vec<CrateId>> {
|
||||
visited: &mut FxHashSet<CrateBuilderId>,
|
||||
from: CrateBuilderId,
|
||||
to: CrateBuilderId,
|
||||
) -> Option<Vec<CrateBuilderId>> {
|
||||
if !visited.insert(from) {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -546,7 +707,7 @@ impl CrateGraph {
|
|||
return Some(vec![to]);
|
||||
}
|
||||
|
||||
for dep in &self[from].dependencies {
|
||||
for dep in &self[from].basic.dependencies {
|
||||
let crate_id = dep.crate_id;
|
||||
if let Some(mut path) = self.find_path(visited, crate_id, to) {
|
||||
path.push(from);
|
||||
|
|
@ -559,7 +720,10 @@ impl CrateGraph {
|
|||
|
||||
/// Removes all crates from this crate graph except for the ones in `to_keep` and fixes up the dependencies.
|
||||
/// Returns a mapping from old crate ids to new crate ids.
|
||||
pub fn remove_crates_except(&mut self, to_keep: &[CrateId]) -> Vec<Option<CrateId>> {
|
||||
pub fn remove_crates_except(
|
||||
&mut self,
|
||||
to_keep: &[CrateBuilderId],
|
||||
) -> Vec<Option<CrateBuilderId>> {
|
||||
let mut id_map = vec![None; self.arena.len()];
|
||||
self.arena = std::mem::take(&mut self.arena)
|
||||
.into_iter()
|
||||
|
|
@ -567,12 +731,12 @@ impl CrateGraph {
|
|||
.enumerate()
|
||||
.map(|(new_id, (id, data))| {
|
||||
id_map[id.into_raw().into_u32() as usize] =
|
||||
Some(CrateId::from_raw(RawIdx::from_u32(new_id as u32)));
|
||||
Some(CrateBuilderId::from_raw(RawIdx::from_u32(new_id as u32)));
|
||||
data
|
||||
})
|
||||
.collect();
|
||||
for (_, data) in self.arena.iter_mut() {
|
||||
data.dependencies.iter_mut().for_each(|dep| {
|
||||
data.basic.dependencies.iter_mut().for_each(|dep| {
|
||||
dep.crate_id =
|
||||
id_map[dep.crate_id.into_raw().into_u32() as usize].expect("crate was filtered")
|
||||
});
|
||||
|
|
@ -585,22 +749,36 @@ impl CrateGraph {
|
|||
}
|
||||
}
|
||||
|
||||
impl ops::Index<CrateId> for CrateGraph {
|
||||
type Output = CrateData;
|
||||
fn index(&self, crate_id: CrateId) -> &CrateData {
|
||||
&self.arena[crate_id]
|
||||
pub(crate) fn transitive_rev_deps(db: &dyn RootQueryDb, of: Crate) -> FxHashSet<Crate> {
|
||||
let mut worklist = vec![of];
|
||||
let mut rev_deps = FxHashSet::default();
|
||||
rev_deps.insert(of);
|
||||
|
||||
let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
|
||||
db.all_crates().iter().for_each(|&krate| {
|
||||
krate
|
||||
.data(db)
|
||||
.dependencies
|
||||
.iter()
|
||||
.for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
|
||||
});
|
||||
|
||||
while let Some(krate) = worklist.pop() {
|
||||
if let Some(crate_rev_deps) = inverted_graph.get(&krate) {
|
||||
crate_rev_deps
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&rev_dep| rev_deps.insert(rev_dep))
|
||||
.for_each(|rev_dep| worklist.push(rev_dep));
|
||||
}
|
||||
}
|
||||
|
||||
rev_deps
|
||||
}
|
||||
|
||||
impl CrateData {
|
||||
/// Add a dependency to `self` without checking if the dependency
|
||||
// is existent among `self.dependencies`.
|
||||
fn add_dep(&mut self, dep: Dependency) {
|
||||
self.dependencies.push(dep)
|
||||
}
|
||||
|
||||
pub fn root_file_id(&self) -> EditionedFileId {
|
||||
EditionedFileId::new(self.root_file_id, self.edition)
|
||||
impl BuiltCrateData {
|
||||
pub fn root_file_id(&self, db: &dyn salsa::Database) -> EditionedFileId {
|
||||
EditionedFileId::new(db, self.root_file_id, self.edition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -657,21 +835,21 @@ impl<'a> IntoIterator for &'a Env {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct CyclicDependenciesError {
|
||||
path: Vec<(CrateId, Option<CrateDisplayName>)>,
|
||||
path: Vec<(CrateBuilderId, Option<CrateDisplayName>)>,
|
||||
}
|
||||
|
||||
impl CyclicDependenciesError {
|
||||
fn from(&self) -> &(CrateId, Option<CrateDisplayName>) {
|
||||
fn from(&self) -> &(CrateBuilderId, Option<CrateDisplayName>) {
|
||||
self.path.first().unwrap()
|
||||
}
|
||||
fn to(&self) -> &(CrateId, Option<CrateDisplayName>) {
|
||||
fn to(&self) -> &(CrateBuilderId, Option<CrateDisplayName>) {
|
||||
self.path.last().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CyclicDependenciesError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name {
|
||||
let render = |(id, name): &(CrateBuilderId, Option<CrateDisplayName>)| match name {
|
||||
Some(it) => format!("{it}({id:?})"),
|
||||
None => format!("{id:?}"),
|
||||
};
|
||||
|
|
@ -688,13 +866,20 @@ impl fmt::Display for CyclicDependenciesError {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::CrateOrigin;
|
||||
use triomphe::Arc;
|
||||
use vfs::AbsPathBuf;
|
||||
|
||||
use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
|
||||
use crate::{CrateWorkspaceData, DependencyBuilder};
|
||||
|
||||
use super::{CrateGraphBuilder, CrateName, CrateOrigin, Edition::Edition2018, Env, FileId};
|
||||
|
||||
fn empty_ws_data() -> Arc<CrateWorkspaceData> {
|
||||
Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None })
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_cyclic_dependency_indirect() {
|
||||
let mut graph = CrateGraph::default();
|
||||
let mut graph = CrateGraphBuilder::default();
|
||||
let crate1 = graph.add_crate_root(
|
||||
FileId::from_raw(1u32),
|
||||
Edition2018,
|
||||
|
|
@ -705,7 +890,8 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId::from_raw(2u32),
|
||||
|
|
@ -717,7 +903,8 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId::from_raw(3u32),
|
||||
|
|
@ -729,22 +916,29 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate2, DependencyBuilder::new(CrateName::new("crate3").unwrap(), crate3,))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate3, DependencyBuilder::new(CrateName::new("crate1").unwrap(), crate1,))
|
||||
.is_err()
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_ok());
|
||||
assert!(graph
|
||||
.add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3,))
|
||||
.is_ok());
|
||||
assert!(graph
|
||||
.add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1,))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_cyclic_dependency_direct() {
|
||||
let mut graph = CrateGraph::default();
|
||||
let mut graph = CrateGraphBuilder::default();
|
||||
let crate1 = graph.add_crate_root(
|
||||
FileId::from_raw(1u32),
|
||||
Edition2018,
|
||||
|
|
@ -755,7 +949,8 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId::from_raw(2u32),
|
||||
|
|
@ -767,19 +962,24 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate2, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_err()
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_ok());
|
||||
assert!(graph
|
||||
.add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let mut graph = CrateGraph::default();
|
||||
let mut graph = CrateGraphBuilder::default();
|
||||
let crate1 = graph.add_crate_root(
|
||||
FileId::from_raw(1u32),
|
||||
Edition2018,
|
||||
|
|
@ -790,7 +990,8 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId::from_raw(2u32),
|
||||
|
|
@ -802,7 +1003,8 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId::from_raw(3u32),
|
||||
|
|
@ -814,19 +1016,24 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(crate2, DependencyBuilder::new(CrateName::new("crate3").unwrap(), crate3,))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
|
||||
.is_ok());
|
||||
assert!(graph
|
||||
.add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3,))
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dashes_are_normalized() {
|
||||
let mut graph = CrateGraph::default();
|
||||
let mut graph = CrateGraphBuilder::default();
|
||||
let crate1 = graph.add_crate_root(
|
||||
FileId::from_raw(1u32),
|
||||
Edition2018,
|
||||
|
|
@ -837,7 +1044,8 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId::from_raw(2u32),
|
||||
|
|
@ -849,17 +1057,25 @@ mod tests {
|
|||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
false,
|
||||
None,
|
||||
Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
|
||||
empty_ws_data(),
|
||||
);
|
||||
assert!(
|
||||
graph
|
||||
.add_dep(
|
||||
crate1,
|
||||
DependencyBuilder::new(
|
||||
CrateName::normalize_dashes("crate-name-with-dashes"),
|
||||
crate2,
|
||||
)
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(
|
||||
crate1,
|
||||
Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2,)
|
||||
)
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
graph[crate1].dependencies,
|
||||
vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2,)]
|
||||
graph.arena[crate1].basic.dependencies,
|
||||
vec![
|
||||
DependencyBuilder::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2,)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,83 +3,311 @@
|
|||
mod change;
|
||||
mod input;
|
||||
|
||||
use std::panic;
|
||||
|
||||
use ra_salsa::Durability;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::EditionedFileId;
|
||||
use syntax::{ast, Parse, SourceFile, SyntaxError};
|
||||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
pub use crate::{
|
||||
change::FileChange,
|
||||
input::{
|
||||
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env,
|
||||
LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId,
|
||||
TargetLayoutLoadResult,
|
||||
BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder,
|
||||
CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap,
|
||||
DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroPaths, ReleaseChannel,
|
||||
SourceRoot, SourceRootId, TargetLayoutLoadResult, UniqueCrateData,
|
||||
},
|
||||
};
|
||||
pub use ra_salsa::{self, Cancelled};
|
||||
pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, VfsPath};
|
||||
|
||||
use dashmap::{DashMap, mapref::entry::Entry};
|
||||
pub use query_group::{self};
|
||||
use rustc_hash::{FxHashSet, FxHasher};
|
||||
pub use salsa::{self};
|
||||
use salsa::{Durability, Setter};
|
||||
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
|
||||
use span::Edition;
|
||||
use syntax::{Parse, SyntaxError, ast};
|
||||
use triomphe::Arc;
|
||||
pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_intern_key {
|
||||
($name:ident) => {
|
||||
impl $crate::ra_salsa::InternKey for $name {
|
||||
fn from_intern_id(v: $crate::ra_salsa::InternId) -> Self {
|
||||
$name(v)
|
||||
}
|
||||
fn as_intern_id(&self) -> $crate::ra_salsa::InternId {
|
||||
self.0
|
||||
($id:ident, $loc:ident) => {
|
||||
#[salsa::interned(no_lifetime)]
|
||||
pub struct $id {
|
||||
pub loc: $loc,
|
||||
}
|
||||
|
||||
// If we derive this salsa prints the values recursively, and this causes us to blow.
|
||||
impl ::std::fmt::Debug for $id {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
f.debug_tuple(stringify!($id))
|
||||
.field(&format_args!("{:04x}", self.0.as_u32()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Upcast<T: ?Sized> {
|
||||
fn upcast(&self) -> &T;
|
||||
}
|
||||
|
||||
pub const DEFAULT_FILE_TEXT_LRU_CAP: u16 = 16;
|
||||
pub const DEFAULT_PARSE_LRU_CAP: u16 = 128;
|
||||
pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024;
|
||||
|
||||
pub trait FileLoader {
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
|
||||
/// Crates whose root's source root is the same as the source root of `file_id`
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Files {
|
||||
files: Arc<DashMap<vfs::FileId, FileText, BuildHasherDefault<FxHasher>>>,
|
||||
source_roots: Arc<DashMap<SourceRootId, SourceRootInput, BuildHasherDefault<FxHasher>>>,
|
||||
file_source_roots: Arc<DashMap<vfs::FileId, FileSourceRootInput, BuildHasherDefault<FxHasher>>>,
|
||||
}
|
||||
|
||||
impl Files {
|
||||
pub fn file_text(&self, file_id: vfs::FileId) -> FileText {
|
||||
*self.files.get(&file_id).expect("Unable to fetch file; this is a bug")
|
||||
}
|
||||
|
||||
pub fn set_file_text(&self, db: &mut dyn SourceDatabase, file_id: vfs::FileId, text: &str) {
|
||||
match self.files.entry(file_id) {
|
||||
Entry::Occupied(mut occupied) => {
|
||||
occupied.get_mut().set_text(db).to(Arc::from(text));
|
||||
}
|
||||
Entry::Vacant(vacant) => {
|
||||
let text = FileText::new(db, Arc::from(text), file_id);
|
||||
vacant.insert(text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_file_text_with_durability(
|
||||
&self,
|
||||
db: &mut dyn SourceDatabase,
|
||||
file_id: vfs::FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
) {
|
||||
match self.files.entry(file_id) {
|
||||
Entry::Occupied(mut occupied) => {
|
||||
occupied.get_mut().set_text(db).with_durability(durability).to(Arc::from(text));
|
||||
}
|
||||
Entry::Vacant(vacant) => {
|
||||
let text =
|
||||
FileText::builder(Arc::from(text), file_id).durability(durability).new(db);
|
||||
vacant.insert(text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Source root of the file.
|
||||
pub fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
|
||||
let source_root = self
|
||||
.source_roots
|
||||
.get(&source_root_id)
|
||||
.expect("Unable to fetch source root id; this is a bug");
|
||||
|
||||
*source_root
|
||||
}
|
||||
|
||||
pub fn set_source_root_with_durability(
|
||||
&self,
|
||||
db: &mut dyn SourceDatabase,
|
||||
source_root_id: SourceRootId,
|
||||
source_root: Arc<SourceRoot>,
|
||||
durability: Durability,
|
||||
) {
|
||||
match self.source_roots.entry(source_root_id) {
|
||||
Entry::Occupied(mut occupied) => {
|
||||
occupied.get_mut().set_source_root(db).with_durability(durability).to(source_root);
|
||||
}
|
||||
Entry::Vacant(vacant) => {
|
||||
let source_root =
|
||||
SourceRootInput::builder(source_root).durability(durability).new(db);
|
||||
vacant.insert(source_root);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn file_source_root(&self, id: vfs::FileId) -> FileSourceRootInput {
|
||||
let file_source_root = self
|
||||
.file_source_roots
|
||||
.get(&id)
|
||||
.expect("Unable to fetch FileSourceRootInput; this is a bug");
|
||||
*file_source_root
|
||||
}
|
||||
|
||||
pub fn set_file_source_root_with_durability(
|
||||
&self,
|
||||
db: &mut dyn SourceDatabase,
|
||||
id: vfs::FileId,
|
||||
source_root_id: SourceRootId,
|
||||
durability: Durability,
|
||||
) {
|
||||
match self.file_source_roots.entry(id) {
|
||||
Entry::Occupied(mut occupied) => {
|
||||
occupied
|
||||
.get_mut()
|
||||
.set_source_root_id(db)
|
||||
.with_durability(durability)
|
||||
.to(source_root_id);
|
||||
}
|
||||
Entry::Vacant(vacant) => {
|
||||
let file_source_root =
|
||||
FileSourceRootInput::builder(source_root_id).durability(durability).new(db);
|
||||
vacant.insert(file_source_root);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::interned(no_lifetime, debug, constructor=from_span)]
|
||||
pub struct EditionedFileId {
|
||||
pub editioned_file_id: span::EditionedFileId,
|
||||
}
|
||||
|
||||
impl EditionedFileId {
|
||||
// Salsa already uses the name `new`...
|
||||
#[inline]
|
||||
pub fn new(db: &dyn salsa::Database, file_id: FileId, edition: Edition) -> Self {
|
||||
EditionedFileId::from_span(db, span::EditionedFileId::new(file_id, edition))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_edition(db: &dyn salsa::Database, file_id: FileId) -> Self {
|
||||
EditionedFileId::new(db, file_id, Edition::CURRENT)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn file_id(self, db: &dyn salsa::Database) -> vfs::FileId {
|
||||
let id = self.editioned_file_id(db);
|
||||
id.file_id()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unpack(self, db: &dyn salsa::Database) -> (vfs::FileId, span::Edition) {
|
||||
let id = self.editioned_file_id(db);
|
||||
(id.file_id(), id.edition())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn edition(self, db: &dyn SourceDatabase) -> Edition {
|
||||
self.editioned_file_id(db).edition()
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::input(debug)]
|
||||
pub struct FileText {
|
||||
pub text: Arc<str>,
|
||||
pub file_id: vfs::FileId,
|
||||
}
|
||||
|
||||
#[salsa::input(debug)]
|
||||
pub struct FileSourceRootInput {
|
||||
pub source_root_id: SourceRootId,
|
||||
}
|
||||
|
||||
#[salsa::input(debug)]
|
||||
pub struct SourceRootInput {
|
||||
pub source_root: Arc<SourceRoot>,
|
||||
}
|
||||
|
||||
/// Database which stores all significant input facts: source code and project
|
||||
/// model. Everything else in rust-analyzer is derived from these queries.
|
||||
#[ra_salsa::query_group(SourceDatabaseStorage)]
|
||||
pub trait SourceDatabase: FileLoader + std::fmt::Debug {
|
||||
#[ra_salsa::input]
|
||||
fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
|
||||
|
||||
/// Text of the file.
|
||||
#[ra_salsa::lru]
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
|
||||
#[query_group::query_group]
|
||||
pub trait RootQueryDb: SourceDatabase + salsa::Database {
|
||||
/// Parses the file into the syntax tree.
|
||||
#[ra_salsa::lru]
|
||||
#[salsa::invoke(parse)]
|
||||
#[salsa::lru(128)]
|
||||
fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>;
|
||||
|
||||
/// Returns the set of errors obtained from parsing the file including validation errors.
|
||||
fn parse_errors(&self, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>>;
|
||||
#[salsa::transparent]
|
||||
fn parse_errors(&self, file_id: EditionedFileId) -> Option<&[SyntaxError]>;
|
||||
|
||||
/// The crate graph.
|
||||
#[ra_salsa::input]
|
||||
fn crate_graph(&self) -> Arc<CrateGraph>;
|
||||
#[salsa::transparent]
|
||||
fn toolchain_channel(&self, krate: Crate) -> Option<ReleaseChannel>;
|
||||
|
||||
#[ra_salsa::input]
|
||||
fn crate_workspace_data(&self) -> Arc<FxHashMap<CrateId, Arc<CrateWorkspaceData>>>;
|
||||
/// Crates whose root file is in `id`.
|
||||
#[salsa::invoke_interned(source_root_crates)]
|
||||
fn source_root_crates(&self, id: SourceRootId) -> Arc<[Crate]>;
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
fn toolchain_channel(&self, krate: CrateId) -> Option<ReleaseChannel>;
|
||||
#[salsa::transparent]
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<[Crate]>;
|
||||
|
||||
/// Returns the crates in topological order.
|
||||
///
|
||||
/// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications.
|
||||
#[salsa::input]
|
||||
fn all_crates(&self) -> Arc<Box<[Crate]>>;
|
||||
|
||||
/// Returns an iterator over all transitive dependencies of the given crate,
|
||||
/// including the crate itself.
|
||||
///
|
||||
/// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications.
|
||||
#[salsa::transparent]
|
||||
fn transitive_deps(&self, crate_id: Crate) -> FxHashSet<Crate>;
|
||||
|
||||
/// Returns all transitive reverse dependencies of the given crate,
|
||||
/// including the crate itself.
|
||||
///
|
||||
/// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications.
|
||||
#[salsa::invoke(input::transitive_rev_deps)]
|
||||
#[salsa::transparent]
|
||||
fn transitive_rev_deps(&self, of: Crate) -> FxHashSet<Crate>;
|
||||
}
|
||||
|
||||
pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
|
||||
// There is a bit of duplication here and in `CrateGraphBuilder` in the same method, but it's not terrible
|
||||
// and removing that is a bit difficult.
|
||||
let mut worklist = vec![crate_id];
|
||||
let mut deps = FxHashSet::default();
|
||||
|
||||
while let Some(krate) = worklist.pop() {
|
||||
if !deps.insert(krate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
worklist.extend(krate.data(db).dependencies.iter().map(|dep| dep.crate_id));
|
||||
}
|
||||
|
||||
deps
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
pub trait SourceDatabase: salsa::Database {
|
||||
/// Text of the file.
|
||||
fn file_text(&self, file_id: vfs::FileId) -> FileText;
|
||||
|
||||
fn set_file_text(&mut self, file_id: vfs::FileId, text: &str);
|
||||
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: vfs::FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
);
|
||||
|
||||
/// Contents of the source root.
|
||||
fn source_root(&self, id: SourceRootId) -> SourceRootInput;
|
||||
|
||||
fn file_source_root(&self, id: vfs::FileId) -> FileSourceRootInput;
|
||||
|
||||
fn set_file_source_root_with_durability(
|
||||
&mut self,
|
||||
id: vfs::FileId,
|
||||
source_root_id: SourceRootId,
|
||||
durability: Durability,
|
||||
);
|
||||
|
||||
/// Source root of the file.
|
||||
fn set_source_root_with_durability(
|
||||
&mut self,
|
||||
source_root_id: SourceRootId,
|
||||
source_root: Arc<SourceRoot>,
|
||||
durability: Durability,
|
||||
);
|
||||
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
// FIXME: this *somehow* should be platform agnostic...
|
||||
let source_root = self.file_source_root(path.anchor);
|
||||
let source_root = self.source_root(source_root.source_root_id(self));
|
||||
source_root.source_root(self).resolve_path(path)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn crates_map(&self) -> Arc<CratesMap>;
|
||||
}
|
||||
|
||||
/// Crate related data shared by the whole workspace.
|
||||
|
|
@ -91,113 +319,57 @@ pub struct CrateWorkspaceData {
|
|||
pub toolchain: Option<Version>,
|
||||
}
|
||||
|
||||
fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option<ReleaseChannel> {
|
||||
db.crate_workspace_data()
|
||||
.get(&krate)?
|
||||
.toolchain
|
||||
.as_ref()
|
||||
.and_then(|v| ReleaseChannel::from_str(&v.pre))
|
||||
impl CrateWorkspaceData {
|
||||
pub fn is_atleast_187(&self) -> bool {
|
||||
const VERSION_187: Version = Version {
|
||||
major: 1,
|
||||
minor: 87,
|
||||
patch: 0,
|
||||
pre: Prerelease::EMPTY,
|
||||
build: BuildMetadata::EMPTY,
|
||||
};
|
||||
self.toolchain.as_ref().map_or(false, |v| *v >= VERSION_187)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Parse<ast::SourceFile> {
|
||||
fn toolchain_channel(db: &dyn RootQueryDb, krate: Crate) -> Option<ReleaseChannel> {
|
||||
krate.workspace_data(db).toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre))
|
||||
}
|
||||
|
||||
fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFile> {
|
||||
let _p = tracing::info_span!("parse", ?file_id).entered();
|
||||
let (file_id, edition) = file_id.unpack();
|
||||
let text = db.file_text(file_id);
|
||||
SourceFile::parse(&text, edition)
|
||||
let (file_id, edition) = file_id.unpack(db.as_dyn_database());
|
||||
let text = db.file_text(file_id).text(db);
|
||||
ast::SourceFile::parse(&text, edition)
|
||||
}
|
||||
|
||||
fn parse_errors(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>> {
|
||||
let errors = db.parse(file_id).errors();
|
||||
match &*errors {
|
||||
[] => None,
|
||||
[..] => Some(errors.into()),
|
||||
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Box<[SyntaxError]>> {
|
||||
let errors = db.parse(file_id).errors();
|
||||
match &*errors {
|
||||
[] => None,
|
||||
[..] => Some(errors.into()),
|
||||
}
|
||||
}
|
||||
parse_errors(db, file_id).as_ref().map(|it| &**it)
|
||||
}
|
||||
|
||||
fn file_text(db: &dyn SourceDatabase, file_id: FileId) -> Arc<str> {
|
||||
let bytes = db.compressed_file_text(file_id);
|
||||
let bytes =
|
||||
lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail");
|
||||
let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8");
|
||||
Arc::from(text)
|
||||
}
|
||||
|
||||
/// We don't want to give HIR knowledge of source roots, hence we extract these
|
||||
/// methods into a separate DB.
|
||||
#[ra_salsa::query_group(SourceRootDatabaseStorage)]
|
||||
pub trait SourceRootDatabase: SourceDatabase {
|
||||
/// Path to a file, relative to the root of its source root.
|
||||
/// Source root of the file.
|
||||
#[ra_salsa::input]
|
||||
fn file_source_root(&self, file_id: FileId) -> SourceRootId;
|
||||
/// Contents of the source root.
|
||||
#[ra_salsa::input]
|
||||
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
|
||||
|
||||
/// Crates whose root file is in `id`.
|
||||
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
|
||||
}
|
||||
|
||||
pub trait SourceDatabaseFileInputExt {
|
||||
fn set_file_text(&mut self, file_id: FileId, text: &str) {
|
||||
self.set_file_text_with_durability(file_id, text, Durability::LOW);
|
||||
}
|
||||
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
);
|
||||
}
|
||||
|
||||
impl<Db: ?Sized + SourceRootDatabase> SourceDatabaseFileInputExt for Db {
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
) {
|
||||
let bytes = text.as_bytes();
|
||||
let compressed = lz4_flex::compress_prepend_size(bytes);
|
||||
self.set_compressed_file_text_with_durability(
|
||||
file_id,
|
||||
Arc::from(compressed.as_slice()),
|
||||
durability,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn source_root_crates(db: &dyn SourceRootDatabase, id: SourceRootId) -> Arc<[CrateId]> {
|
||||
let graph = db.crate_graph();
|
||||
let mut crates = graph
|
||||
fn source_root_crates(db: &dyn RootQueryDb, id: SourceRootId) -> Arc<[Crate]> {
|
||||
let crates = db.all_crates();
|
||||
crates
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&krate| {
|
||||
let root_file = graph[krate].root_file_id;
|
||||
db.file_source_root(root_file) == id
|
||||
let root_file = krate.data(db).root_file_id;
|
||||
db.file_source_root(root_file).source_root_id(db) == id
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
crates.sort();
|
||||
crates.dedup();
|
||||
crates.into_iter().collect()
|
||||
.collect()
|
||||
}
|
||||
|
||||
// FIXME: Would be nice to get rid of this somehow
|
||||
/// Silly workaround for cyclic deps due to the SourceRootDatabase and SourceDatabase split
|
||||
/// regarding FileLoader
|
||||
pub struct FileLoaderDelegate<T>(pub T);
|
||||
fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> {
|
||||
let _p = tracing::info_span!("relevant_crates").entered();
|
||||
|
||||
impl<T: SourceRootDatabase> FileLoader for FileLoaderDelegate<&'_ T> {
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
// FIXME: this *somehow* should be platform agnostic...
|
||||
let source_root = self.0.file_source_root(path.anchor);
|
||||
let source_root = self.0.source_root(source_root);
|
||||
source_root.resolve_path(path)
|
||||
}
|
||||
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> {
|
||||
let _p = tracing::info_span!("relevant_crates").entered();
|
||||
let source_root = self.0.file_source_root(file_id);
|
||||
self.0.source_root_crates(source_root)
|
||||
}
|
||||
let source_root = db.file_source_root(file_id);
|
||||
db.source_root_crates(source_root.source_root_id(db))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ tt = { workspace = true, optional = true }
|
|||
intern.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.1"
|
||||
oorandom = "11.1.3"
|
||||
expect-test = "1.5.1"
|
||||
oorandom = "11.1.5"
|
||||
# We depend on both individually instead of using `features = ["derive"]` to microoptimize the
|
||||
# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr`
|
||||
# supports `arbitrary`. This way, we avoid feature unification.
|
||||
arbitrary = "1.3.2"
|
||||
derive_arbitrary = "1.3.2"
|
||||
arbitrary = "1.4.1"
|
||||
derive_arbitrary = "1.4.1"
|
||||
|
||||
# local deps
|
||||
syntax-bridge.workspace = true
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use std::fmt;
|
|||
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use intern::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
|
||||
pub use cfg_expr::{CfgAtom, CfgExpr};
|
||||
pub use dnf::DnfExpr;
|
||||
|
|
@ -31,7 +31,7 @@ pub struct CfgOptions {
|
|||
|
||||
impl Default for CfgOptions {
|
||||
fn default() -> Self {
|
||||
Self { enabled: FxHashSet::from_iter([CfgAtom::Flag(sym::true_.clone())]) }
|
||||
Self { enabled: FxHashSet::from_iter([CfgAtom::Flag(sym::true_)]) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +104,17 @@ impl CfgOptions {
|
|||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_hashable(&self) -> HashableCfgOptions {
|
||||
let mut enabled = self.enabled.iter().cloned().collect::<Box<[_]>>();
|
||||
enabled.sort_unstable();
|
||||
HashableCfgOptions { _enabled: enabled }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shrink_to_fit(&mut self) {
|
||||
self.enabled.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<CfgAtom> for CfgOptions {
|
||||
|
|
@ -256,3 +267,9 @@ impl fmt::Display for InactiveReason {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A `CfgOptions` that implements `Hash`, for the sake of hashing only.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct HashableCfgOptions {
|
||||
_enabled: Box<[CfgAtom]>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use expect_test::{expect, Expect};
|
||||
use expect_test::{Expect, expect};
|
||||
use intern::Symbol;
|
||||
use syntax::{ast, AstNode, Edition};
|
||||
use syntax::{AstNode, Edition, ast};
|
||||
use syntax_bridge::{
|
||||
dummy_test_span_utils::{DummyTestSpanMap, DUMMY},
|
||||
syntax_node_to_token_tree, DocCommentDesugarMode,
|
||||
DocCommentDesugarMode,
|
||||
dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
|
||||
syntax_node_to_token_tree,
|
||||
};
|
||||
|
||||
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
|
||||
|
|
|
|||
|
|
@ -15,9 +15,19 @@ pub enum Edition {
|
|||
impl Edition {
|
||||
pub const DEFAULT: Edition = Edition::Edition2015;
|
||||
pub const LATEST: Edition = Edition::Edition2024;
|
||||
pub const CURRENT: Edition = Edition::Edition2021;
|
||||
pub const CURRENT: Edition = Edition::Edition2024;
|
||||
/// The current latest stable edition, note this is usually not the right choice in code.
|
||||
pub const CURRENT_FIXME: Edition = Edition::Edition2021;
|
||||
pub const CURRENT_FIXME: Edition = Edition::Edition2024;
|
||||
|
||||
pub fn from_u32(u32: u32) -> Edition {
|
||||
match u32 {
|
||||
0 => Edition::Edition2015,
|
||||
1 => Edition::Edition2018,
|
||||
2 => Edition::Edition2021,
|
||||
3 => Edition::Edition2024,
|
||||
_ => panic!("invalid edition"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at_least_2024(self) -> bool {
|
||||
self >= Edition::Edition2024
|
||||
|
|
@ -31,6 +41,15 @@ impl Edition {
|
|||
self >= Edition::Edition2018
|
||||
}
|
||||
|
||||
pub fn number(&self) -> usize {
|
||||
match self {
|
||||
Edition::Edition2015 => 2015,
|
||||
Edition::Edition2018 => 2018,
|
||||
Edition::Edition2021 => 2021,
|
||||
Edition::Edition2024 => 2024,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter() -> impl Iterator<Item = Edition> {
|
||||
[Edition::Edition2015, Edition::Edition2018, Edition::Edition2021, Edition::Edition2024]
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ rust-version.workspace = true
|
|||
[dependencies]
|
||||
arrayvec.workspace = true
|
||||
bitflags.workspace = true
|
||||
cov-mark = "2.0.0-pre.1"
|
||||
dashmap.workspace = true
|
||||
cov-mark = "2.0.0"
|
||||
drop_bomb = "0.1.5"
|
||||
either.workspace = true
|
||||
fst = { version = "0.4.7", default-features = false }
|
||||
|
|
@ -25,12 +24,12 @@ la-arena.workspace = true
|
|||
rustc-hash.workspace = true
|
||||
tracing.workspace = true
|
||||
smallvec.workspace = true
|
||||
hashbrown.workspace = true
|
||||
triomphe.workspace = true
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_apfloat = "0.2.2"
|
||||
text-size.workspace = true
|
||||
salsa.workspace = true
|
||||
query-group.workspace = true
|
||||
|
||||
ra-ap-rustc_hashes.workspace = true
|
||||
ra-ap-rustc_parse_format.workspace = true
|
||||
ra-ap-rustc_abi.workspace = true
|
||||
|
||||
|
|
@ -44,7 +43,7 @@ mbe.workspace = true
|
|||
cfg.workspace = true
|
||||
tt.workspace = true
|
||||
span.workspace = true
|
||||
|
||||
thin-vec = "0.2.14"
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test.workspace = true
|
||||
|
|
@ -53,6 +52,7 @@ expect-test.workspace = true
|
|||
test-utils.workspace = true
|
||||
test-fixture.workspace = true
|
||||
syntax-bridge.workspace = true
|
||||
|
||||
[features]
|
||||
in-rust-tree = ["hir-expand/in-rust-tree"]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,31 +2,32 @@
|
|||
|
||||
use std::{borrow::Cow, hash::Hash, ops};
|
||||
|
||||
use base_db::CrateId;
|
||||
use base_db::Crate;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
|
||||
HirFileId, InFile,
|
||||
attrs::{Attr, AttrId, RawAttrs, collect_attrs},
|
||||
};
|
||||
use intern::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
use la_arena::{ArenaMap, Idx, RawIdx};
|
||||
use mbe::DelimiterKind;
|
||||
use rustc_abi::ReprOptions;
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs},
|
||||
AstPtr,
|
||||
ast::{self, HasAttrs},
|
||||
};
|
||||
use triomphe::Arc;
|
||||
use tt::iter::{TtElement, TtIter};
|
||||
|
||||
use crate::{
|
||||
AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
|
||||
VariantId,
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, FieldParent, ItemTreeNode},
|
||||
lang_item::LangItem,
|
||||
nameres::{ModuleOrigin, ModuleSource},
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
|
||||
VariantId,
|
||||
};
|
||||
|
||||
/// Desugared attributes of an item post `cfg_attr` expansion.
|
||||
|
|
@ -44,8 +45,8 @@ impl Attrs {
|
|||
(**self).iter().find(|attr| attr.id == id)
|
||||
}
|
||||
|
||||
pub(crate) fn filter(db: &dyn DefDatabase, krate: CrateId, raw_attrs: RawAttrs) -> Attrs {
|
||||
Attrs(raw_attrs.filter(db.upcast(), krate))
|
||||
pub(crate) fn filter(db: &dyn DefDatabase, krate: Crate, raw_attrs: RawAttrs) -> Attrs {
|
||||
Attrs(raw_attrs.filter(db, krate))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +76,6 @@ impl Attrs {
|
|||
let _p = tracing::info_span!("fields_attrs_query").entered();
|
||||
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
|
||||
let mut res = ArenaMap::default();
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let item_tree;
|
||||
let (parent, fields, krate) = match v {
|
||||
VariantId::EnumVariantId(it) => {
|
||||
|
|
@ -84,7 +83,7 @@ impl Attrs {
|
|||
let krate = loc.parent.lookup(db).container.krate;
|
||||
item_tree = loc.id.item_tree(db);
|
||||
let variant = &item_tree[loc.id.value];
|
||||
(FieldParent::Variant(loc.id.value), &variant.fields, krate)
|
||||
(FieldParent::EnumVariant(loc.id.value), &variant.fields, krate)
|
||||
}
|
||||
VariantId::StructId(it) => {
|
||||
let loc = it.lookup(db);
|
||||
|
|
@ -102,7 +101,7 @@ impl Attrs {
|
|||
}
|
||||
};
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg_options = krate.cfg_options(db);
|
||||
|
||||
let mut idx = 0;
|
||||
for (id, _field) in fields.iter().enumerate() {
|
||||
|
|
@ -118,17 +117,20 @@ impl Attrs {
|
|||
}
|
||||
|
||||
impl Attrs {
|
||||
pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'attrs> {
|
||||
#[inline]
|
||||
pub fn by_key(&self, key: Symbol) -> AttrQuery<'_> {
|
||||
AttrQuery { attrs: self, key }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rust_analyzer_tool(&self) -> impl Iterator<Item = &Attr> {
|
||||
self.iter()
|
||||
.filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg(&self) -> Option<CfgExpr> {
|
||||
let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
|
||||
let mut cfgs = self.by_key(sym::cfg).tt_values().map(CfgExpr::parse);
|
||||
let first = cfgs.next()?;
|
||||
match cfgs.next() {
|
||||
Some(second) => {
|
||||
|
|
@ -139,10 +141,12 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfgs(&self) -> impl Iterator<Item = CfgExpr> + '_ {
|
||||
self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse)
|
||||
self.by_key(sym::cfg).tt_values().map(CfgExpr::parse)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
|
||||
match self.cfg() {
|
||||
None => true,
|
||||
|
|
@ -150,78 +154,225 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lang(&self) -> Option<&Symbol> {
|
||||
self.by_key(&sym::lang).string_value()
|
||||
self.by_key(sym::lang).string_value()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lang_item(&self) -> Option<LangItem> {
|
||||
self.by_key(&sym::lang).string_value().and_then(LangItem::from_symbol)
|
||||
self.by_key(sym::lang).string_value().and_then(LangItem::from_symbol)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_doc_hidden(&self) -> bool {
|
||||
self.by_key(&sym::doc).tt_values().any(|tt| {
|
||||
self.by_key(sym::doc).tt_values().any(|tt| {
|
||||
tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis &&
|
||||
matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_doc_notable_trait(&self) -> bool {
|
||||
self.by_key(&sym::doc).tt_values().any(|tt| {
|
||||
self.by_key(sym::doc).tt_values().any(|tt| {
|
||||
tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis &&
|
||||
matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
|
||||
self.by_key(&sym::doc).tt_values().map(DocExpr::parse)
|
||||
self.by_key(sym::doc).tt_values().map(DocExpr::parse)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_aliases(&self) -> impl Iterator<Item = Symbol> + '_ {
|
||||
self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn export_name(&self) -> Option<&Symbol> {
|
||||
self.by_key(&sym::export_name).string_value()
|
||||
self.by_key(sym::export_name).string_value()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_proc_macro(&self) -> bool {
|
||||
self.by_key(&sym::proc_macro).exists()
|
||||
self.by_key(sym::proc_macro).exists()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_proc_macro_attribute(&self) -> bool {
|
||||
self.by_key(&sym::proc_macro_attribute).exists()
|
||||
self.by_key(sym::proc_macro_attribute).exists()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_proc_macro_derive(&self) -> bool {
|
||||
self.by_key(&sym::proc_macro_derive).exists()
|
||||
self.by_key(sym::proc_macro_derive).exists()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_test(&self) -> bool {
|
||||
self.iter().any(|it| {
|
||||
it.path()
|
||||
.segments()
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(
|
||||
[sym::core.clone(), sym::prelude.clone(), sym::v1.clone(), sym::test.clone()]
|
||||
.iter()
|
||||
.rev(),
|
||||
)
|
||||
.zip([sym::core, sym::prelude, sym::v1, sym::test].iter().rev())
|
||||
.all(|it| it.0 == it.1)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_ignore(&self) -> bool {
|
||||
self.by_key(&sym::ignore).exists()
|
||||
self.by_key(sym::ignore).exists()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_bench(&self) -> bool {
|
||||
self.by_key(&sym::bench).exists()
|
||||
self.by_key(sym::bench).exists()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_unstable(&self) -> bool {
|
||||
self.by_key(&sym::unstable).exists()
|
||||
self.by_key(sym::unstable).exists()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rustc_legacy_const_generics(&self) -> Option<Box<Box<[u32]>>> {
|
||||
self.by_key(sym::rustc_legacy_const_generics)
|
||||
.tt_values()
|
||||
.next()
|
||||
.map(parse_rustc_legacy_const_generics)
|
||||
.filter(|it| !it.is_empty())
|
||||
.map(Box::new)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn repr(&self) -> Option<ReprOptions> {
|
||||
self.by_key(sym::repr).tt_values().filter_map(parse_repr_tt).fold(None, |acc, repr| {
|
||||
acc.map_or(Some(repr), |mut acc| {
|
||||
merge_repr(&mut acc, repr);
|
||||
Some(acc)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
|
||||
let mut indices = Vec::new();
|
||||
let mut iter = tt.iter();
|
||||
while let (Some(first), second) = (iter.next(), iter.next()) {
|
||||
match first {
|
||||
TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() {
|
||||
Ok(index) => indices.push(index),
|
||||
Err(_) => break,
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
|
||||
if let Some(comma) = second {
|
||||
match comma {
|
||||
TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indices.into_boxed_slice()
|
||||
}
|
||||
|
||||
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
|
||||
let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
|
||||
flags.insert(other.flags);
|
||||
*align = (*align).max(other.align);
|
||||
*pack = match (*pack, other.pack) {
|
||||
(Some(pack), None) | (None, Some(pack)) => Some(pack),
|
||||
_ => (*pack).min(other.pack),
|
||||
};
|
||||
if other.int.is_some() {
|
||||
*int = other.int;
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option<ReprOptions> {
|
||||
use crate::builtin_type::{BuiltinInt, BuiltinUint};
|
||||
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
|
||||
|
||||
match tt.top_subtree().delimiter {
|
||||
tt::Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
let mut acc = ReprOptions::default();
|
||||
let mut tts = tt.iter();
|
||||
while let Some(tt) = tts.next() {
|
||||
let TtElement::Leaf(tt::Leaf::Ident(ident)) = tt else {
|
||||
continue;
|
||||
};
|
||||
let repr = match &ident.sym {
|
||||
s if *s == sym::packed => {
|
||||
let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
|
||||
tts.next();
|
||||
if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
|
||||
lit.symbol.as_str().parse().unwrap_or_default()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE));
|
||||
ReprOptions { pack, ..Default::default() }
|
||||
}
|
||||
s if *s == sym::align => {
|
||||
let mut align = None;
|
||||
if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
|
||||
tts.next();
|
||||
if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
|
||||
if let Ok(a) = lit.symbol.as_str().parse() {
|
||||
align = Align::from_bytes(a).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
ReprOptions { align, ..Default::default() }
|
||||
}
|
||||
s if *s == sym::C => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() },
|
||||
s if *s == sym::transparent => {
|
||||
ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() }
|
||||
}
|
||||
s if *s == sym::simd => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() },
|
||||
repr => {
|
||||
let mut int = None;
|
||||
if let Some(builtin) = BuiltinInt::from_suffix_sym(repr)
|
||||
.map(Either::Left)
|
||||
.or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right))
|
||||
{
|
||||
int = Some(match builtin {
|
||||
Either::Left(bi) => match bi {
|
||||
BuiltinInt::Isize => IntegerType::Pointer(true),
|
||||
BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
|
||||
BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
|
||||
BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
|
||||
BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
|
||||
BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
|
||||
},
|
||||
Either::Right(bu) => match bu {
|
||||
BuiltinUint::Usize => IntegerType::Pointer(false),
|
||||
BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
|
||||
BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
|
||||
BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
|
||||
BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
|
||||
BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
|
||||
},
|
||||
});
|
||||
}
|
||||
ReprOptions { int, ..Default::default() }
|
||||
}
|
||||
};
|
||||
merge_repr(&mut acc, repr);
|
||||
}
|
||||
|
||||
Some(acc)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -373,7 +524,7 @@ impl AttrsWithOwner {
|
|||
// FIXME: We should be never getting `None` here.
|
||||
match src.value.get(it.local_id()) {
|
||||
Some(val) => RawAttrs::from_attrs_owner(
|
||||
db.upcast(),
|
||||
db,
|
||||
src.with_value(val),
|
||||
db.span_map(src.file_id).as_ref(),
|
||||
),
|
||||
|
|
@ -385,7 +536,7 @@ impl AttrsWithOwner {
|
|||
// FIXME: We should be never getting `None` here.
|
||||
match src.value.get(it.local_id()) {
|
||||
Some(val) => RawAttrs::from_attrs_owner(
|
||||
db.upcast(),
|
||||
db,
|
||||
src.with_value(val),
|
||||
db.span_map(src.file_id).as_ref(),
|
||||
),
|
||||
|
|
@ -397,7 +548,7 @@ impl AttrsWithOwner {
|
|||
// FIXME: We should be never getting `None` here.
|
||||
match src.value.get(it.local_id) {
|
||||
Some(val) => RawAttrs::from_attrs_owner(
|
||||
db.upcast(),
|
||||
db,
|
||||
src.with_value(val),
|
||||
db.span_map(src.file_id).as_ref(),
|
||||
),
|
||||
|
|
@ -410,7 +561,7 @@ impl AttrsWithOwner {
|
|||
AttrDefId::UseId(it) => attrs_from_item_tree_loc(db, it),
|
||||
};
|
||||
|
||||
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
|
||||
let attrs = raw_attrs.filter(db, def.krate(db));
|
||||
Attrs(attrs)
|
||||
}
|
||||
|
||||
|
|
@ -547,36 +698,42 @@ impl AttrSourceMap {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AttrQuery<'attr> {
|
||||
attrs: &'attr Attrs,
|
||||
key: &'attr Symbol,
|
||||
key: Symbol,
|
||||
}
|
||||
|
||||
impl<'attr> AttrQuery<'attr> {
|
||||
#[inline]
|
||||
pub fn tt_values(self) -> impl Iterator<Item = &'attr crate::tt::TopSubtree> {
|
||||
self.attrs().filter_map(|attr| attr.token_tree_value())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn string_value(self) -> Option<&'attr Symbol> {
|
||||
self.attrs().find_map(|attr| attr.string_value())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
|
||||
self.attrs().find_map(|attr| attr.string_value_with_span())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
|
||||
self.attrs().find_map(|attr| attr.string_value_unescape())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn exists(self) -> bool {
|
||||
self.attrs().next().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
|
||||
let key = self.key;
|
||||
self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == *key))
|
||||
self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key))
|
||||
}
|
||||
|
||||
/// Find string value for a specific key inside token tree
|
||||
|
|
@ -585,10 +742,11 @@ impl<'attr> AttrQuery<'attr> {
|
|||
/// #[doc(html_root_url = "url")]
|
||||
/// ^^^^^^^^^^^^^ key
|
||||
/// ```
|
||||
pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&'attr str> {
|
||||
#[inline]
|
||||
pub fn find_string_value_in_tt(self, key: Symbol) -> Option<&'attr str> {
|
||||
self.tt_values().find_map(|tt| {
|
||||
let name = tt.iter()
|
||||
.skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key))
|
||||
.skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == key))
|
||||
.nth(2);
|
||||
|
||||
match name {
|
||||
|
|
@ -601,17 +759,14 @@ impl<'attr> AttrQuery<'attr> {
|
|||
|
||||
fn any_has_attrs<'db>(
|
||||
db: &(dyn DefDatabase + 'db),
|
||||
id: impl Lookup<
|
||||
Database<'db> = dyn DefDatabase + 'db,
|
||||
Data = impl HasSource<Value = impl ast::HasAttrs>,
|
||||
>,
|
||||
id: impl Lookup<Database = dyn DefDatabase, Data = impl HasSource<Value = impl ast::HasAttrs>>,
|
||||
) -> InFile<ast::AnyHasAttrs> {
|
||||
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
|
||||
}
|
||||
|
||||
fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>(
|
||||
db: &(dyn DefDatabase + 'db),
|
||||
lookup: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = impl ItemTreeLoc<Id = N>>,
|
||||
lookup: impl Lookup<Database = dyn DefDatabase, Data = impl ItemTreeLoc<Id = N>>,
|
||||
) -> RawAttrs {
|
||||
let id = lookup.lookup(db).item_tree_id();
|
||||
let tree = id.item_tree(db);
|
||||
|
|
@ -649,8 +804,8 @@ mod tests {
|
|||
|
||||
use hir_expand::span_map::{RealSpanMap, SpanMap};
|
||||
use span::FileId;
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use syntax::{AstNode, TextRange, ast};
|
||||
use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
|
||||
|
||||
use crate::attr::{DocAtom, DocExpr};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
use std::fmt;
|
||||
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use intern::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
/// Different signed int types.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum BuiltinInt {
|
||||
|
|
@ -51,28 +51,28 @@ impl BuiltinType {
|
|||
#[rustfmt::skip]
|
||||
pub fn all_builtin_types() -> [(Name, BuiltinType); 19] {
|
||||
[
|
||||
(Name::new_symbol_root(sym::char.clone()), BuiltinType::Char),
|
||||
(Name::new_symbol_root(sym::bool.clone()), BuiltinType::Bool),
|
||||
(Name::new_symbol_root(sym::str.clone()), BuiltinType::Str),
|
||||
(Name::new_symbol_root(sym::char), BuiltinType::Char),
|
||||
(Name::new_symbol_root(sym::bool), BuiltinType::Bool),
|
||||
(Name::new_symbol_root(sym::str), BuiltinType::Str),
|
||||
|
||||
(Name::new_symbol_root(sym::isize.clone()), BuiltinType::Int(BuiltinInt::Isize)),
|
||||
(Name::new_symbol_root(sym::i8.clone()), BuiltinType::Int(BuiltinInt::I8)),
|
||||
(Name::new_symbol_root(sym::i16.clone()), BuiltinType::Int(BuiltinInt::I16)),
|
||||
(Name::new_symbol_root(sym::i32.clone()), BuiltinType::Int(BuiltinInt::I32)),
|
||||
(Name::new_symbol_root(sym::i64.clone()), BuiltinType::Int(BuiltinInt::I64)),
|
||||
(Name::new_symbol_root(sym::i128.clone()), BuiltinType::Int(BuiltinInt::I128)),
|
||||
(Name::new_symbol_root(sym::isize), BuiltinType::Int(BuiltinInt::Isize)),
|
||||
(Name::new_symbol_root(sym::i8), BuiltinType::Int(BuiltinInt::I8)),
|
||||
(Name::new_symbol_root(sym::i16), BuiltinType::Int(BuiltinInt::I16)),
|
||||
(Name::new_symbol_root(sym::i32), BuiltinType::Int(BuiltinInt::I32)),
|
||||
(Name::new_symbol_root(sym::i64), BuiltinType::Int(BuiltinInt::I64)),
|
||||
(Name::new_symbol_root(sym::i128), BuiltinType::Int(BuiltinInt::I128)),
|
||||
|
||||
(Name::new_symbol_root(sym::usize.clone()), BuiltinType::Uint(BuiltinUint::Usize)),
|
||||
(Name::new_symbol_root(sym::u8.clone()), BuiltinType::Uint(BuiltinUint::U8)),
|
||||
(Name::new_symbol_root(sym::u16.clone()), BuiltinType::Uint(BuiltinUint::U16)),
|
||||
(Name::new_symbol_root(sym::u32.clone()), BuiltinType::Uint(BuiltinUint::U32)),
|
||||
(Name::new_symbol_root(sym::u64.clone()), BuiltinType::Uint(BuiltinUint::U64)),
|
||||
(Name::new_symbol_root(sym::u128.clone()), BuiltinType::Uint(BuiltinUint::U128)),
|
||||
(Name::new_symbol_root(sym::usize), BuiltinType::Uint(BuiltinUint::Usize)),
|
||||
(Name::new_symbol_root(sym::u8), BuiltinType::Uint(BuiltinUint::U8)),
|
||||
(Name::new_symbol_root(sym::u16), BuiltinType::Uint(BuiltinUint::U16)),
|
||||
(Name::new_symbol_root(sym::u32), BuiltinType::Uint(BuiltinUint::U32)),
|
||||
(Name::new_symbol_root(sym::u64), BuiltinType::Uint(BuiltinUint::U64)),
|
||||
(Name::new_symbol_root(sym::u128), BuiltinType::Uint(BuiltinUint::U128)),
|
||||
|
||||
(Name::new_symbol_root(sym::f16.clone()), BuiltinType::Float(BuiltinFloat::F16)),
|
||||
(Name::new_symbol_root(sym::f32.clone()), BuiltinType::Float(BuiltinFloat::F32)),
|
||||
(Name::new_symbol_root(sym::f64.clone()), BuiltinType::Float(BuiltinFloat::F64)),
|
||||
(Name::new_symbol_root(sym::f128.clone()), BuiltinType::Float(BuiltinFloat::F128)),
|
||||
(Name::new_symbol_root(sym::f16), BuiltinType::Float(BuiltinFloat::F16)),
|
||||
(Name::new_symbol_root(sym::f32), BuiltinType::Float(BuiltinFloat::F32)),
|
||||
(Name::new_symbol_root(sym::f64), BuiltinType::Float(BuiltinFloat::F64)),
|
||||
(Name::new_symbol_root(sym::f128), BuiltinType::Float(BuiltinFloat::F128)),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -86,30 +86,30 @@ impl BuiltinType {
|
|||
impl AsName for BuiltinType {
|
||||
fn as_name(&self) -> Name {
|
||||
match self {
|
||||
BuiltinType::Char => Name::new_symbol_root(sym::char.clone()),
|
||||
BuiltinType::Bool => Name::new_symbol_root(sym::bool.clone()),
|
||||
BuiltinType::Str => Name::new_symbol_root(sym::str.clone()),
|
||||
BuiltinType::Char => Name::new_symbol_root(sym::char),
|
||||
BuiltinType::Bool => Name::new_symbol_root(sym::bool),
|
||||
BuiltinType::Str => Name::new_symbol_root(sym::str),
|
||||
BuiltinType::Int(it) => match it {
|
||||
BuiltinInt::Isize => Name::new_symbol_root(sym::isize.clone()),
|
||||
BuiltinInt::I8 => Name::new_symbol_root(sym::i8.clone()),
|
||||
BuiltinInt::I16 => Name::new_symbol_root(sym::i16.clone()),
|
||||
BuiltinInt::I32 => Name::new_symbol_root(sym::i32.clone()),
|
||||
BuiltinInt::I64 => Name::new_symbol_root(sym::i64.clone()),
|
||||
BuiltinInt::I128 => Name::new_symbol_root(sym::i128.clone()),
|
||||
BuiltinInt::Isize => Name::new_symbol_root(sym::isize),
|
||||
BuiltinInt::I8 => Name::new_symbol_root(sym::i8),
|
||||
BuiltinInt::I16 => Name::new_symbol_root(sym::i16),
|
||||
BuiltinInt::I32 => Name::new_symbol_root(sym::i32),
|
||||
BuiltinInt::I64 => Name::new_symbol_root(sym::i64),
|
||||
BuiltinInt::I128 => Name::new_symbol_root(sym::i128),
|
||||
},
|
||||
BuiltinType::Uint(it) => match it {
|
||||
BuiltinUint::Usize => Name::new_symbol_root(sym::usize.clone()),
|
||||
BuiltinUint::U8 => Name::new_symbol_root(sym::u8.clone()),
|
||||
BuiltinUint::U16 => Name::new_symbol_root(sym::u16.clone()),
|
||||
BuiltinUint::U32 => Name::new_symbol_root(sym::u32.clone()),
|
||||
BuiltinUint::U64 => Name::new_symbol_root(sym::u64.clone()),
|
||||
BuiltinUint::U128 => Name::new_symbol_root(sym::u128.clone()),
|
||||
BuiltinUint::Usize => Name::new_symbol_root(sym::usize),
|
||||
BuiltinUint::U8 => Name::new_symbol_root(sym::u8),
|
||||
BuiltinUint::U16 => Name::new_symbol_root(sym::u16),
|
||||
BuiltinUint::U32 => Name::new_symbol_root(sym::u32),
|
||||
BuiltinUint::U64 => Name::new_symbol_root(sym::u64),
|
||||
BuiltinUint::U128 => Name::new_symbol_root(sym::u128),
|
||||
},
|
||||
BuiltinType::Float(it) => match it {
|
||||
BuiltinFloat::F16 => Name::new_symbol_root(sym::f16.clone()),
|
||||
BuiltinFloat::F32 => Name::new_symbol_root(sym::f32.clone()),
|
||||
BuiltinFloat::F64 => Name::new_symbol_root(sym::f64.clone()),
|
||||
BuiltinFloat::F128 => Name::new_symbol_root(sym::f128.clone()),
|
||||
BuiltinFloat::F16 => Name::new_symbol_root(sym::f16),
|
||||
BuiltinFloat::F32 => Name::new_symbol_root(sym::f32),
|
||||
BuiltinFloat::F64 => Name::new_symbol_root(sym::f64),
|
||||
BuiltinFloat::F128 => Name::new_symbol_root(sym::f128),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,843 +0,0 @@
|
|||
//! Contains basic data about various HIR declarations.
|
||||
|
||||
pub mod adt;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{
|
||||
name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
|
||||
};
|
||||
use intern::{sym, Symbol};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{ast, Parse};
|
||||
use triomphe::Arc;
|
||||
use tt::iter::TtElement;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
expander::{Expander, Mark},
|
||||
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId},
|
||||
macro_call_as_call_id,
|
||||
nameres::{
|
||||
attr_resolution::ResolvedAttr,
|
||||
diagnostics::{DefDiagnostic, DefDiagnostics},
|
||||
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
|
||||
DefMap, MacroSubNs,
|
||||
},
|
||||
path::ImportAlias,
|
||||
type_ref::{TraitRef, TypeBound, TypeRefId, TypesMap},
|
||||
visibility::RawVisibility,
|
||||
AssocItemId, AstIdWithPath, ConstId, ConstLoc, ExternCrateId, FunctionId, FunctionLoc,
|
||||
HasModule, ImplId, Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId,
|
||||
ProcMacroId, StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionData {
|
||||
pub name: Name,
|
||||
pub params: Box<[TypeRefId]>,
|
||||
pub ret_type: TypeRefId,
|
||||
pub visibility: RawVisibility,
|
||||
pub abi: Option<Symbol>,
|
||||
pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>,
|
||||
pub rustc_allow_incoherent_impl: bool,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
flags: FnFlags,
|
||||
}
|
||||
|
||||
impl FunctionData {
|
||||
pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
|
||||
let loc = func.lookup(db);
|
||||
let krate = loc.container.module(db).krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let func = &item_tree[loc.id.value];
|
||||
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
|
||||
trait_vis(db, trait_id)
|
||||
} else {
|
||||
item_tree[func.visibility].clone()
|
||||
};
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let attr_owner = |idx| {
|
||||
item_tree::AttrOwner::Param(loc.id.value, Idx::from_raw(RawIdx::from(idx as u32)))
|
||||
};
|
||||
|
||||
let mut flags = func.flags;
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
// If there's a self param in the syntax, but it is cfg'd out, remove the flag.
|
||||
let is_cfgd_out =
|
||||
!item_tree.attrs(db, krate, attr_owner(0usize)).is_cfg_enabled(cfg_options);
|
||||
if is_cfgd_out {
|
||||
cov_mark::hit!(cfgd_out_self_param);
|
||||
flags.remove(FnFlags::HAS_SELF_PARAM);
|
||||
}
|
||||
}
|
||||
if flags.contains(FnFlags::IS_VARARGS) {
|
||||
if let Some((_, param)) = func.params.iter().enumerate().rev().find(|&(idx, _)| {
|
||||
item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options)
|
||||
}) {
|
||||
if param.type_ref.is_some() {
|
||||
flags.remove(FnFlags::IS_VARARGS);
|
||||
}
|
||||
} else {
|
||||
flags.remove(FnFlags::IS_VARARGS);
|
||||
}
|
||||
}
|
||||
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
let legacy_const_generics_indices = attrs
|
||||
.by_key(&sym::rustc_legacy_const_generics)
|
||||
.tt_values()
|
||||
.next()
|
||||
.map(parse_rustc_legacy_const_generics)
|
||||
.filter(|it| !it.is_empty())
|
||||
.map(Box::new);
|
||||
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
||||
if flags.contains(FnFlags::HAS_UNSAFE_KW)
|
||||
&& attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
|
||||
{
|
||||
flags.remove(FnFlags::HAS_UNSAFE_KW);
|
||||
flags.insert(FnFlags::DEPRECATED_SAFE_2024);
|
||||
}
|
||||
|
||||
if attrs.by_key(&sym::target_feature).exists() {
|
||||
flags.insert(FnFlags::HAS_TARGET_FEATURE);
|
||||
}
|
||||
|
||||
Arc::new(FunctionData {
|
||||
name: func.name.clone(),
|
||||
params: func
|
||||
.params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(idx, _)| {
|
||||
item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options)
|
||||
})
|
||||
.filter_map(|(_, param)| param.type_ref)
|
||||
.collect(),
|
||||
ret_type: func.ret_type,
|
||||
visibility,
|
||||
abi: func.abi.clone(),
|
||||
legacy_const_generics_indices,
|
||||
types_map: func.types_map.clone(),
|
||||
flags,
|
||||
rustc_allow_incoherent_impl,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_body(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_BODY)
|
||||
}
|
||||
|
||||
/// True if the first param is `self`. This is relevant to decide whether this
|
||||
/// can be called as a method.
|
||||
pub fn has_self_param(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_SELF_PARAM)
|
||||
}
|
||||
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_DEFAULT_KW)
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_CONST_KW)
|
||||
}
|
||||
|
||||
pub fn is_async(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_ASYNC_KW)
|
||||
}
|
||||
|
||||
pub fn is_unsafe(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
|
||||
}
|
||||
|
||||
pub fn is_deprecated_safe_2024(&self) -> bool {
|
||||
self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
|
||||
}
|
||||
|
||||
pub fn is_safe(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_SAFE_KW)
|
||||
}
|
||||
|
||||
pub fn is_varargs(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_VARARGS)
|
||||
}
|
||||
|
||||
pub fn has_target_feature(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
|
||||
let mut indices = Vec::new();
|
||||
let mut iter = tt.iter();
|
||||
while let (Some(first), second) = (iter.next(), iter.next()) {
|
||||
match first {
|
||||
TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() {
|
||||
Ok(index) => indices.push(index),
|
||||
Err(_) => break,
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
|
||||
if let Some(comma) = second {
|
||||
match comma {
|
||||
TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indices.into_boxed_slice()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeAliasData {
|
||||
pub name: Name,
|
||||
pub type_ref: Option<TypeRefId>,
|
||||
pub visibility: RawVisibility,
|
||||
pub is_extern: bool,
|
||||
pub rustc_has_incoherent_inherent_impls: bool,
|
||||
pub rustc_allow_incoherent_impl: bool,
|
||||
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
|
||||
pub bounds: Box<[TypeBound]>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
impl TypeAliasData {
|
||||
pub(crate) fn type_alias_data_query(
|
||||
db: &dyn DefDatabase,
|
||||
typ: TypeAliasId,
|
||||
) -> Arc<TypeAliasData> {
|
||||
let loc = typ.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let typ = &item_tree[loc.id.value];
|
||||
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
|
||||
trait_vis(db, trait_id)
|
||||
} else {
|
||||
item_tree[typ.visibility].clone()
|
||||
};
|
||||
|
||||
let attrs = item_tree.attrs(
|
||||
db,
|
||||
loc.container.module(db).krate(),
|
||||
ModItem::from(loc.id.value).into(),
|
||||
);
|
||||
let rustc_has_incoherent_inherent_impls =
|
||||
attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists();
|
||||
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
||||
|
||||
Arc::new(TypeAliasData {
|
||||
name: typ.name.clone(),
|
||||
type_ref: typ.type_ref,
|
||||
visibility,
|
||||
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
|
||||
rustc_has_incoherent_inherent_impls,
|
||||
rustc_allow_incoherent_impl,
|
||||
bounds: typ.bounds.clone(),
|
||||
types_map: typ.types_map.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct TraitFlags: u8 {
|
||||
const IS_AUTO = 1 << 0;
|
||||
const IS_UNSAFE = 1 << 1;
|
||||
const IS_FUNDAMENTAL = 1 << 2;
|
||||
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3;
|
||||
const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4;
|
||||
const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5;
|
||||
const RUSTC_PAREN_SUGAR = 1 << 6;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TraitData {
|
||||
pub name: Name,
|
||||
pub items: Box<[(Name, AssocItemId)]>,
|
||||
pub flags: TraitFlags,
|
||||
pub visibility: RawVisibility,
|
||||
// box it as the vec is usually empty anyways
|
||||
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
|
||||
}
|
||||
|
||||
impl TraitData {
|
||||
#[inline]
|
||||
pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
|
||||
db.trait_data_with_diagnostics(tr).0
|
||||
}
|
||||
|
||||
pub(crate) fn trait_data_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
tr: TraitId,
|
||||
) -> (Arc<TraitData>, DefDiagnostics) {
|
||||
let ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let tr_def = &item_tree[tree_id.value];
|
||||
let name = tr_def.name.clone();
|
||||
let visibility = item_tree[tr_def.visibility].clone();
|
||||
let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
|
||||
|
||||
let mut flags = TraitFlags::empty();
|
||||
|
||||
if tr_def.is_auto {
|
||||
flags |= TraitFlags::IS_AUTO;
|
||||
}
|
||||
if tr_def.is_unsafe {
|
||||
flags |= TraitFlags::IS_UNSAFE;
|
||||
}
|
||||
if attrs.by_key(&sym::fundamental).exists() {
|
||||
flags |= TraitFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
|
||||
}
|
||||
if attrs.by_key(&sym::rustc_paren_sugar).exists() {
|
||||
flags |= TraitFlags::RUSTC_PAREN_SUGAR;
|
||||
}
|
||||
|
||||
let mut skip_array_during_method_dispatch =
|
||||
attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();
|
||||
let mut skip_boxed_slice_during_method_dispatch = false;
|
||||
for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() {
|
||||
for tt in tt.iter() {
|
||||
if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt {
|
||||
skip_array_during_method_dispatch |= ident.sym == sym::array;
|
||||
skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if skip_array_during_method_dispatch {
|
||||
flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH;
|
||||
}
|
||||
if skip_boxed_slice_during_method_dispatch {
|
||||
flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
|
||||
}
|
||||
|
||||
let mut collector =
|
||||
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
|
||||
collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
|
||||
let (items, macro_calls, diagnostics) = collector.finish();
|
||||
|
||||
(
|
||||
Arc::new(TraitData { name, macro_calls, items, visibility, flags }),
|
||||
DefDiagnostics::new(diagnostics),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
|
||||
self.items.iter().filter_map(|(_name, item)| match item {
|
||||
AssocItemId::TypeAliasId(t) => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
|
||||
self.items.iter().find_map(|(item_name, item)| match item {
|
||||
AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn method_by_name(&self, name: &Name) -> Option<FunctionId> {
|
||||
self.items.iter().find_map(|(item_name, item)| match item {
|
||||
AssocItemId::FunctionId(t) if item_name == name => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
|
||||
self.macro_calls.iter().flat_map(|it| it.iter()).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TraitAliasData {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibility,
|
||||
}
|
||||
|
||||
impl TraitAliasData {
|
||||
pub(crate) fn trait_alias_query(db: &dyn DefDatabase, id: TraitAliasId) -> Arc<TraitAliasData> {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let alias = &item_tree[loc.id.value];
|
||||
let visibility = item_tree[alias.visibility].clone();
|
||||
|
||||
Arc::new(TraitAliasData { name: alias.name.clone(), visibility })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ImplData {
|
||||
pub target_trait: Option<TraitRef>,
|
||||
pub self_ty: TypeRefId,
|
||||
pub items: Box<[(Name, AssocItemId)]>,
|
||||
pub is_negative: bool,
|
||||
pub is_unsafe: bool,
|
||||
// box it as the vec is usually empty anyways
|
||||
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
impl ImplData {
|
||||
#[inline]
|
||||
pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
|
||||
db.impl_data_with_diagnostics(id).0
|
||||
}
|
||||
|
||||
pub(crate) fn impl_data_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
id: ImplId,
|
||||
) -> (Arc<ImplData>, DefDiagnostics) {
|
||||
let _p = tracing::info_span!("impl_data_with_diagnostics_query").entered();
|
||||
let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
|
||||
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let impl_def = &item_tree[tree_id.value];
|
||||
let target_trait = impl_def.target_trait;
|
||||
let self_ty = impl_def.self_ty;
|
||||
let is_negative = impl_def.is_negative;
|
||||
let is_unsafe = impl_def.is_unsafe;
|
||||
|
||||
let mut collector =
|
||||
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id));
|
||||
collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
|
||||
|
||||
let (items, macro_calls, diagnostics) = collector.finish();
|
||||
|
||||
(
|
||||
Arc::new(ImplData {
|
||||
target_trait,
|
||||
self_ty,
|
||||
items,
|
||||
is_negative,
|
||||
is_unsafe,
|
||||
macro_calls,
|
||||
types_map: impl_def.types_map.clone(),
|
||||
}),
|
||||
DefDiagnostics::new(diagnostics),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
|
||||
self.macro_calls.iter().flat_map(|it| it.iter()).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Macro2Data {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibility,
|
||||
// It's a bit wasteful as currently this is only for builtin `Default` derive macro, but macro2
|
||||
// are rarely used in practice so I think it's okay for now.
|
||||
/// Derive helpers, if this is a derive rustc_builtin_macro
|
||||
pub helpers: Option<Box<[Name]>>,
|
||||
}
|
||||
|
||||
impl Macro2Data {
|
||||
pub(crate) fn macro2_data_query(db: &dyn DefDatabase, makro: Macro2Id) -> Arc<Macro2Data> {
|
||||
let loc = makro.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let makro = &item_tree[loc.id.value];
|
||||
|
||||
let helpers = item_tree
|
||||
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
|
||||
.by_key(&sym::rustc_builtin_macro)
|
||||
.tt_values()
|
||||
.next()
|
||||
.and_then(parse_macro_name_and_helper_attrs)
|
||||
.map(|(_, helpers)| helpers);
|
||||
|
||||
Arc::new(Macro2Data {
|
||||
name: makro.name.clone(),
|
||||
visibility: item_tree[makro.visibility].clone(),
|
||||
helpers,
|
||||
})
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MacroRulesData {
|
||||
pub name: Name,
|
||||
pub macro_export: bool,
|
||||
}
|
||||
|
||||
impl MacroRulesData {
|
||||
pub(crate) fn macro_rules_data_query(
|
||||
db: &dyn DefDatabase,
|
||||
makro: MacroRulesId,
|
||||
) -> Arc<MacroRulesData> {
|
||||
let loc = makro.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let makro = &item_tree[loc.id.value];
|
||||
|
||||
let macro_export = item_tree
|
||||
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
|
||||
.by_key(&sym::macro_export)
|
||||
.exists();
|
||||
|
||||
Arc::new(MacroRulesData { name: makro.name.clone(), macro_export })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ProcMacroData {
|
||||
pub name: Name,
|
||||
/// Derive helpers, if this is a derive
|
||||
pub helpers: Option<Box<[Name]>>,
|
||||
}
|
||||
|
||||
impl ProcMacroData {
|
||||
pub(crate) fn proc_macro_data_query(
|
||||
db: &dyn DefDatabase,
|
||||
makro: ProcMacroId,
|
||||
) -> Arc<ProcMacroData> {
|
||||
let loc = makro.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let makro = &item_tree[loc.id.value];
|
||||
|
||||
let (name, helpers) = if let Some(def) = item_tree
|
||||
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
|
||||
.parse_proc_macro_decl(&makro.name)
|
||||
{
|
||||
(
|
||||
def.name,
|
||||
match def.kind {
|
||||
ProcMacroKind::Derive { helpers } => Some(helpers),
|
||||
ProcMacroKind::Bang | ProcMacroKind::Attr => None,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
// eeeh...
|
||||
stdx::never!("proc macro declaration is not a proc macro");
|
||||
(makro.name.clone(), None)
|
||||
};
|
||||
Arc::new(ProcMacroData { name, helpers })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExternCrateDeclData {
|
||||
pub name: Name,
|
||||
pub alias: Option<ImportAlias>,
|
||||
pub visibility: RawVisibility,
|
||||
pub crate_id: Option<CrateId>,
|
||||
}
|
||||
|
||||
impl ExternCrateDeclData {
|
||||
pub(crate) fn extern_crate_decl_data_query(
|
||||
db: &dyn DefDatabase,
|
||||
extern_crate: ExternCrateId,
|
||||
) -> Arc<ExternCrateDeclData> {
|
||||
let loc = extern_crate.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let extern_crate = &item_tree[loc.id.value];
|
||||
|
||||
let name = extern_crate.name.clone();
|
||||
let krate = loc.container.krate();
|
||||
let crate_id = if name == sym::self_.clone() {
|
||||
Some(krate)
|
||||
} else {
|
||||
db.crate_graph()[krate].dependencies.iter().find_map(|dep| {
|
||||
if dep.name.symbol() == name.symbol() {
|
||||
Some(dep.crate_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
Arc::new(Self {
|
||||
name,
|
||||
visibility: item_tree[extern_crate.visibility].clone(),
|
||||
alias: extern_crate.alias.clone(),
|
||||
crate_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ConstData {
|
||||
/// `None` for `const _: () = ();`
|
||||
pub name: Option<Name>,
|
||||
pub type_ref: TypeRefId,
|
||||
pub visibility: RawVisibility,
|
||||
pub rustc_allow_incoherent_impl: bool,
|
||||
pub has_body: bool,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
impl ConstData {
|
||||
pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
|
||||
let loc = konst.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let konst = &item_tree[loc.id.value];
|
||||
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
|
||||
trait_vis(db, trait_id)
|
||||
} else {
|
||||
item_tree[konst.visibility].clone()
|
||||
};
|
||||
|
||||
let rustc_allow_incoherent_impl = item_tree
|
||||
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
|
||||
.by_key(&sym::rustc_allow_incoherent_impl)
|
||||
.exists();
|
||||
|
||||
Arc::new(ConstData {
|
||||
name: konst.name.clone(),
|
||||
type_ref: konst.type_ref,
|
||||
visibility,
|
||||
rustc_allow_incoherent_impl,
|
||||
has_body: konst.has_body,
|
||||
types_map: konst.types_map.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StaticData {
|
||||
pub name: Name,
|
||||
pub type_ref: TypeRefId,
|
||||
pub visibility: RawVisibility,
|
||||
pub mutable: bool,
|
||||
pub is_extern: bool,
|
||||
pub has_safe_kw: bool,
|
||||
pub has_unsafe_kw: bool,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
impl StaticData {
|
||||
pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
|
||||
let loc = konst.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let statik = &item_tree[loc.id.value];
|
||||
|
||||
Arc::new(StaticData {
|
||||
name: statik.name.clone(),
|
||||
type_ref: statik.type_ref,
|
||||
visibility: item_tree[statik.visibility].clone(),
|
||||
mutable: statik.mutable,
|
||||
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
|
||||
has_safe_kw: statik.has_safe_kw,
|
||||
has_unsafe_kw: statik.has_unsafe_kw,
|
||||
types_map: statik.types_map.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct AssocItemCollector<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
module_id: ModuleId,
|
||||
def_map: Arc<DefMap>,
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
container: ItemContainerId,
|
||||
expander: Expander,
|
||||
|
||||
items: Vec<(Name, AssocItemId)>,
|
||||
macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
|
||||
}
|
||||
|
||||
impl<'a> AssocItemCollector<'a> {
|
||||
fn new(
|
||||
db: &'a dyn DefDatabase,
|
||||
module_id: ModuleId,
|
||||
file_id: HirFileId,
|
||||
container: ItemContainerId,
|
||||
) -> Self {
|
||||
Self {
|
||||
db,
|
||||
module_id,
|
||||
def_map: module_id.def_map(db),
|
||||
container,
|
||||
expander: Expander::new(db, file_id, module_id),
|
||||
items: Vec::new(),
|
||||
macro_calls: Vec::new(),
|
||||
diagnostics: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(
|
||||
self,
|
||||
) -> (
|
||||
Box<[(Name, AssocItemId)]>,
|
||||
Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
|
||||
Vec<DefDiagnostic>,
|
||||
) {
|
||||
(
|
||||
self.items.into_boxed_slice(),
|
||||
if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
|
||||
self.diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
|
||||
let container = self.container;
|
||||
self.items.reserve(assoc_items.len());
|
||||
|
||||
'items: for &item in assoc_items {
|
||||
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
|
||||
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
|
||||
self.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id.local_id,
|
||||
tree_id,
|
||||
ModItem::from(item).into(),
|
||||
attrs.cfg().unwrap(),
|
||||
self.expander.cfg_options().clone(),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
'attrs: for attr in &*attrs {
|
||||
let ast_id =
|
||||
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
|
||||
let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
|
||||
|
||||
match self.def_map.resolve_attr_macro(
|
||||
self.db,
|
||||
self.module_id.local_id,
|
||||
ast_id_with_path,
|
||||
attr,
|
||||
) {
|
||||
Ok(ResolvedAttr::Macro(call_id)) => {
|
||||
let loc = self.db.lookup_intern_macro_call(call_id);
|
||||
if let MacroDefKind::ProcMacro(_, exp, _) = loc.def.kind {
|
||||
// If there's no expander for the proc macro (e.g. the
|
||||
// proc macro is ignored, or building the proc macro
|
||||
// crate failed), skip expansion like we would if it was
|
||||
// disabled. This is analogous to the handling in
|
||||
// `DefCollector::collect_macros`.
|
||||
if let Some(err) = exp.as_expand_error(self.module_id.krate) {
|
||||
self.diagnostics.push(DefDiagnostic::macro_error(
|
||||
self.module_id.local_id,
|
||||
ast_id,
|
||||
(*attr.path).clone(),
|
||||
err,
|
||||
));
|
||||
continue 'attrs;
|
||||
}
|
||||
}
|
||||
|
||||
self.macro_calls.push((ast_id, call_id));
|
||||
let res =
|
||||
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||
self.collect_macro_items(res);
|
||||
continue 'items;
|
||||
}
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
self.module_id.local_id,
|
||||
MacroCallKind::Attr {
|
||||
ast_id,
|
||||
attr_args: None,
|
||||
invoc_attr_index: attr.id,
|
||||
},
|
||||
attr.path().clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.collect_item(item_tree, tree_id, container, item);
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_item(
|
||||
&mut self,
|
||||
item_tree: &ItemTree,
|
||||
tree_id: TreeId,
|
||||
container: ItemContainerId,
|
||||
item: AssocItem,
|
||||
) {
|
||||
match item {
|
||||
AssocItem::Function(id) => {
|
||||
let item = &item_tree[id];
|
||||
let def =
|
||||
FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
|
||||
self.items.push((item.name.clone(), def.into()));
|
||||
}
|
||||
AssocItem::TypeAlias(id) => {
|
||||
let item = &item_tree[id];
|
||||
let def =
|
||||
TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
|
||||
self.items.push((item.name.clone(), def.into()));
|
||||
}
|
||||
AssocItem::Const(id) => {
|
||||
let item = &item_tree[id];
|
||||
let Some(name) = item.name.clone() else { return };
|
||||
let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
|
||||
self.items.push((name, def.into()));
|
||||
}
|
||||
AssocItem::MacroCall(call) => {
|
||||
let file_id = self.expander.current_file_id();
|
||||
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
|
||||
let module = self.expander.module.local_id;
|
||||
|
||||
let resolver = |path: &_| {
|
||||
self.def_map
|
||||
.resolve_path(
|
||||
self.db,
|
||||
module,
|
||||
path,
|
||||
crate::item_scope::BuiltinShadowMode::Other,
|
||||
Some(MacroSubNs::Bang),
|
||||
)
|
||||
.0
|
||||
.take_macros()
|
||||
.map(|it| self.db.macro_def(it))
|
||||
};
|
||||
match macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
&AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
|
||||
ctxt,
|
||||
expand_to,
|
||||
self.expander.krate(),
|
||||
resolver,
|
||||
) {
|
||||
Ok(Some(call_id)) => {
|
||||
let res =
|
||||
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||
self.macro_calls.push((InFile::new(file_id, ast_id.upcast()), call_id));
|
||||
self.collect_macro_items(res);
|
||||
}
|
||||
Ok(None) => (),
|
||||
Err(_) => {
|
||||
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
self.module_id.local_id,
|
||||
MacroCallKind::FnLike {
|
||||
ast_id: InFile::new(file_id, ast_id),
|
||||
expand_to,
|
||||
eager: None,
|
||||
},
|
||||
Clone::clone(path),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_macro_items(&mut self, res: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>) {
|
||||
let Some((mark, _parse)) = res.value else { return };
|
||||
|
||||
let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
|
||||
let item_tree = tree_id.item_tree(self.db);
|
||||
let iter: SmallVec<[_; 2]> =
|
||||
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item).collect();
|
||||
|
||||
self.collect(&item_tree, tree_id, &iter);
|
||||
|
||||
self.expander.exit(mark);
|
||||
}
|
||||
}
|
||||
|
||||
fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility {
|
||||
let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let tr_def = &item_tree[tree_id.value];
|
||||
item_tree[tr_def.visibility].clone()
|
||||
}
|
||||
|
|
@ -1,489 +0,0 @@
|
|||
//! Defines hir-level representation of structs, enums and unions
|
||||
|
||||
use base_db::CrateId;
|
||||
use bitflags::bitflags;
|
||||
use cfg::CfgOptions;
|
||||
use either::Either;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use la_arena::Arena;
|
||||
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
|
||||
use rustc_hashes::Hash64;
|
||||
use triomphe::Arc;
|
||||
use tt::iter::TtElement;
|
||||
|
||||
use crate::{
|
||||
builtin_type::{BuiltinInt, BuiltinUint},
|
||||
db::DefDatabase,
|
||||
hir::Expr,
|
||||
item_tree::{
|
||||
AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
|
||||
tt::{Delimiter, DelimiterKind, Leaf, TopSubtree},
|
||||
type_ref::{TypeRefId, TypesMap},
|
||||
visibility::RawVisibility,
|
||||
EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
|
||||
};
|
||||
|
||||
/// Note that we use `StructData` for unions as well!
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StructData {
|
||||
pub name: Name,
|
||||
pub variant_data: Arc<VariantData>,
|
||||
pub repr: Option<ReprOptions>,
|
||||
pub visibility: RawVisibility,
|
||||
pub flags: StructFlags,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct StructFlags: u8 {
|
||||
const NO_FLAGS = 0;
|
||||
/// Indicates whether the struct is `PhantomData`.
|
||||
const IS_PHANTOM_DATA = 1 << 2;
|
||||
/// Indicates whether the struct has a `#[fundamental]` attribute.
|
||||
const IS_FUNDAMENTAL = 1 << 3;
|
||||
// FIXME: should this be a flag?
|
||||
/// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
|
||||
const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4;
|
||||
/// Indicates whether this struct is `Box`.
|
||||
const IS_BOX = 1 << 5;
|
||||
/// Indicates whether this struct is `ManuallyDrop`.
|
||||
const IS_MANUALLY_DROP = 1 << 6;
|
||||
/// Indicates whether this struct is `UnsafeCell`.
|
||||
const IS_UNSAFE_CELL = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumData {
|
||||
pub name: Name,
|
||||
pub variants: Box<[(EnumVariantId, Name)]>,
|
||||
pub repr: Option<ReprOptions>,
|
||||
pub visibility: RawVisibility,
|
||||
pub rustc_has_incoherent_inherent_impls: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumVariantData {
|
||||
pub name: Name,
|
||||
pub variant_data: Arc<VariantData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum VariantData {
|
||||
Record { fields: Arena<FieldData>, types_map: Arc<TypesMap> },
|
||||
Tuple { fields: Arena<FieldData>, types_map: Arc<TypesMap> },
|
||||
Unit,
|
||||
}
|
||||
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FieldData {
|
||||
pub name: Name,
|
||||
pub type_ref: TypeRefId,
|
||||
pub visibility: RawVisibility,
|
||||
}
|
||||
|
||||
fn repr_from_value(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
item_tree: &ItemTree,
|
||||
of: AttrOwner,
|
||||
) -> Option<ReprOptions> {
|
||||
item_tree.attrs(db, krate, of).by_key(&sym::repr).tt_values().find_map(parse_repr_tt)
|
||||
}
|
||||
|
||||
fn parse_repr_tt(tt: &TopSubtree) -> Option<ReprOptions> {
|
||||
match tt.top_subtree().delimiter {
|
||||
Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
let mut flags = ReprFlags::empty();
|
||||
let mut int = None;
|
||||
let mut max_align: Option<Align> = None;
|
||||
let mut min_pack: Option<Align> = None;
|
||||
|
||||
let mut tts = tt.iter();
|
||||
while let Some(tt) = tts.next() {
|
||||
if let TtElement::Leaf(Leaf::Ident(ident)) = tt {
|
||||
flags.insert(match &ident.sym {
|
||||
s if *s == sym::packed => {
|
||||
let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
|
||||
tts.next();
|
||||
if let Some(TtElement::Leaf(Leaf::Literal(lit))) = tt_iter.next() {
|
||||
lit.symbol.as_str().parse().unwrap_or_default()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let pack = Align::from_bytes(pack).unwrap_or(Align::ONE);
|
||||
min_pack =
|
||||
Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
|
||||
ReprFlags::empty()
|
||||
}
|
||||
s if *s == sym::align => {
|
||||
if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
|
||||
tts.next();
|
||||
if let Some(TtElement::Leaf(Leaf::Literal(lit))) = tt_iter.next() {
|
||||
if let Ok(align) = lit.symbol.as_str().parse() {
|
||||
let align = Align::from_bytes(align).ok();
|
||||
max_align = max_align.max(align);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReprFlags::empty()
|
||||
}
|
||||
s if *s == sym::C => ReprFlags::IS_C,
|
||||
s if *s == sym::transparent => ReprFlags::IS_TRANSPARENT,
|
||||
s if *s == sym::simd => ReprFlags::IS_SIMD,
|
||||
repr => {
|
||||
if let Some(builtin) = BuiltinInt::from_suffix_sym(repr)
|
||||
.map(Either::Left)
|
||||
.or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right))
|
||||
{
|
||||
int = Some(match builtin {
|
||||
Either::Left(bi) => match bi {
|
||||
BuiltinInt::Isize => IntegerType::Pointer(true),
|
||||
BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
|
||||
BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
|
||||
BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
|
||||
BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
|
||||
BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
|
||||
},
|
||||
Either::Right(bu) => match bu {
|
||||
BuiltinUint::Usize => IntegerType::Pointer(false),
|
||||
BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
|
||||
BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
|
||||
BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
|
||||
BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
|
||||
BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
|
||||
},
|
||||
});
|
||||
}
|
||||
ReprFlags::empty()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Some(ReprOptions {
|
||||
int,
|
||||
align: max_align,
|
||||
pack: min_pack,
|
||||
flags,
|
||||
field_shuffle_seed: Hash64::ZERO,
|
||||
})
|
||||
}
|
||||
|
||||
impl StructData {
|
||||
#[inline]
|
||||
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
|
||||
db.struct_data_with_diagnostics(id).0
|
||||
}
|
||||
|
||||
pub(crate) fn struct_data_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
id: StructId,
|
||||
) -> (Arc<StructData>, DefDiagnostics) {
|
||||
let loc = id.lookup(db);
|
||||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
}
|
||||
if attrs.by_key(&sym::fundamental).exists() {
|
||||
flags |= StructFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
if let Some(lang) = attrs.lang_item() {
|
||||
match lang {
|
||||
LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
|
||||
LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
|
||||
LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
|
||||
LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let strukt = &item_tree[loc.id.value];
|
||||
let (fields, diagnostics) = lower_fields(
|
||||
db,
|
||||
krate,
|
||||
loc.container.local_id,
|
||||
loc.id.tree_id(),
|
||||
&item_tree,
|
||||
&db.crate_graph()[krate].cfg_options,
|
||||
FieldParent::Struct(loc.id.value),
|
||||
&strukt.fields,
|
||||
None,
|
||||
);
|
||||
let types_map = strukt.types_map.clone();
|
||||
(
|
||||
Arc::new(StructData {
|
||||
name: strukt.name.clone(),
|
||||
variant_data: Arc::new(match strukt.shape {
|
||||
FieldsShape::Record => VariantData::Record { fields, types_map },
|
||||
FieldsShape::Tuple => VariantData::Tuple { fields, types_map },
|
||||
FieldsShape::Unit => VariantData::Unit,
|
||||
}),
|
||||
repr,
|
||||
visibility: item_tree[strukt.visibility].clone(),
|
||||
flags,
|
||||
}),
|
||||
DefDiagnostics::new(diagnostics),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
|
||||
db.union_data_with_diagnostics(id).0
|
||||
}
|
||||
|
||||
pub(crate) fn union_data_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
id: UnionId,
|
||||
) -> (Arc<StructData>, DefDiagnostics) {
|
||||
let loc = id.lookup(db);
|
||||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
}
|
||||
if attrs.by_key(&sym::fundamental).exists() {
|
||||
flags |= StructFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
|
||||
let union = &item_tree[loc.id.value];
|
||||
let (fields, diagnostics) = lower_fields(
|
||||
db,
|
||||
krate,
|
||||
loc.container.local_id,
|
||||
loc.id.tree_id(),
|
||||
&item_tree,
|
||||
&db.crate_graph()[krate].cfg_options,
|
||||
FieldParent::Union(loc.id.value),
|
||||
&union.fields,
|
||||
None,
|
||||
);
|
||||
let types_map = union.types_map.clone();
|
||||
(
|
||||
Arc::new(StructData {
|
||||
name: union.name.clone(),
|
||||
variant_data: Arc::new(VariantData::Record { fields, types_map }),
|
||||
repr,
|
||||
visibility: item_tree[union.visibility].clone(),
|
||||
flags,
|
||||
}),
|
||||
DefDiagnostics::new(diagnostics),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumData {
|
||||
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
|
||||
let loc = e.lookup(db);
|
||||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let rustc_has_incoherent_inherent_impls = item_tree
|
||||
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
|
||||
.by_key(&sym::rustc_has_incoherent_inherent_impls)
|
||||
.exists();
|
||||
|
||||
let enum_ = &item_tree[loc.id.value];
|
||||
|
||||
Arc::new(EnumData {
|
||||
name: enum_.name.clone(),
|
||||
variants: loc.container.def_map(db).enum_definitions[&e]
|
||||
.iter()
|
||||
.map(|&id| (id, item_tree[id.lookup(db).id.value].name.clone()))
|
||||
.collect(),
|
||||
repr,
|
||||
visibility: item_tree[enum_.visibility].clone(),
|
||||
rustc_has_incoherent_inherent_impls,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
|
||||
let &(id, _) = self.variants.iter().find(|(_id, n)| n == name)?;
|
||||
Some(id)
|
||||
}
|
||||
|
||||
pub fn variant_body_type(&self) -> IntegerType {
|
||||
match self.repr {
|
||||
Some(ReprOptions { int: Some(builtin), .. }) => builtin,
|
||||
_ => IntegerType::Pointer(true),
|
||||
}
|
||||
}
|
||||
|
||||
// [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
|
||||
pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
|
||||
self.variants.iter().all(|(v, _)| {
|
||||
// The condition check order is slightly modified from rustc
|
||||
// to improve performance by early returning with relatively fast checks
|
||||
let variant = &db.enum_variant_data(*v).variant_data;
|
||||
if !variant.fields().is_empty() {
|
||||
return false;
|
||||
}
|
||||
// The outer if condition is whether this variant has const ctor or not
|
||||
if !matches!(variant.kind(), StructKind::Unit) {
|
||||
let body = db.body((*v).into());
|
||||
// A variant with explicit discriminant
|
||||
if body.exprs[body.body_expr] != Expr::Missing {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumVariantData {
|
||||
#[inline]
|
||||
pub(crate) fn enum_variant_data_query(
|
||||
db: &dyn DefDatabase,
|
||||
e: EnumVariantId,
|
||||
) -> Arc<EnumVariantData> {
|
||||
db.enum_variant_data_with_diagnostics(e).0
|
||||
}
|
||||
|
||||
pub(crate) fn enum_variant_data_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
e: EnumVariantId,
|
||||
) -> (Arc<EnumVariantData>, DefDiagnostics) {
|
||||
let loc = e.lookup(db);
|
||||
let container = loc.parent.lookup(db).container;
|
||||
let krate = container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let variant = &item_tree[loc.id.value];
|
||||
|
||||
let (fields, diagnostics) = lower_fields(
|
||||
db,
|
||||
krate,
|
||||
container.local_id,
|
||||
loc.id.tree_id(),
|
||||
&item_tree,
|
||||
&db.crate_graph()[krate].cfg_options,
|
||||
FieldParent::Variant(loc.id.value),
|
||||
&variant.fields,
|
||||
Some(item_tree[loc.parent.lookup(db).id.value].visibility),
|
||||
);
|
||||
let types_map = variant.types_map.clone();
|
||||
|
||||
(
|
||||
Arc::new(EnumVariantData {
|
||||
name: variant.name.clone(),
|
||||
variant_data: Arc::new(match variant.shape {
|
||||
FieldsShape::Record => VariantData::Record { fields, types_map },
|
||||
FieldsShape::Tuple => VariantData::Tuple { fields, types_map },
|
||||
FieldsShape::Unit => VariantData::Unit,
|
||||
}),
|
||||
}),
|
||||
DefDiagnostics::new(diagnostics),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl VariantData {
|
||||
pub fn fields(&self) -> &Arena<FieldData> {
|
||||
const EMPTY: &Arena<FieldData> = &Arena::new();
|
||||
match &self {
|
||||
VariantData::Record { fields, .. } | VariantData::Tuple { fields, .. } => fields,
|
||||
_ => EMPTY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn types_map(&self) -> &TypesMap {
|
||||
match &self {
|
||||
VariantData::Record { types_map, .. } | VariantData::Tuple { types_map, .. } => {
|
||||
types_map
|
||||
}
|
||||
VariantData::Unit => TypesMap::EMPTY,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Linear lookup
|
||||
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
|
||||
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> StructKind {
|
||||
match self {
|
||||
VariantData::Record { .. } => StructKind::Record,
|
||||
VariantData::Tuple { .. } => StructKind::Tuple,
|
||||
VariantData::Unit => StructKind::Unit,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::self_named_constructors)]
|
||||
pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc<VariantData> {
|
||||
match id {
|
||||
VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
|
||||
VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(),
|
||||
VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum StructKind {
|
||||
Tuple,
|
||||
Record,
|
||||
Unit,
|
||||
}
|
||||
|
||||
fn lower_fields(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
container: LocalModuleId,
|
||||
tree_id: TreeId,
|
||||
item_tree: &ItemTree,
|
||||
cfg_options: &CfgOptions,
|
||||
parent: FieldParent,
|
||||
fields: &[Field],
|
||||
override_visibility: Option<RawVisibilityId>,
|
||||
) -> (Arena<FieldData>, Vec<DefDiagnostic>) {
|
||||
let mut diagnostics = Vec::new();
|
||||
let mut arena = Arena::new();
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
let attr_owner = AttrOwner::make_field_indexed(parent, idx);
|
||||
let attrs = item_tree.attrs(db, krate, attr_owner);
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
arena.alloc(lower_field(item_tree, field, override_visibility));
|
||||
} else {
|
||||
diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
container,
|
||||
tree_id,
|
||||
attr_owner,
|
||||
attrs.cfg().unwrap(),
|
||||
cfg_options.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
(arena, diagnostics)
|
||||
}
|
||||
|
||||
fn lower_field(
|
||||
item_tree: &ItemTree,
|
||||
field: &Field,
|
||||
override_visibility: Option<RawVisibilityId>,
|
||||
) -> FieldData {
|
||||
FieldData {
|
||||
name: field.name.clone(),
|
||||
type_ref: field.type_ref,
|
||||
visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,273 +1,373 @@
|
|||
//! Defines database & queries for name resolution.
|
||||
use base_db::{ra_salsa, CrateId, SourceDatabase, Upcast};
|
||||
use base_db::{Crate, RootQueryDb, SourceDatabase};
|
||||
use either::Either;
|
||||
use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId};
|
||||
use hir_expand::{EditionedFileId, HirFileId, MacroCallId, MacroDefId, db::ExpandDatabase};
|
||||
use intern::sym;
|
||||
use la_arena::ArenaMap;
|
||||
use span::{EditionedFileId, MacroCallId};
|
||||
use syntax::{ast, AstPtr};
|
||||
use syntax::{AstPtr, ast};
|
||||
use thin_vec::ThinVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, EnumVariantId,
|
||||
EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId,
|
||||
FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroId,
|
||||
MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId,
|
||||
StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId,
|
||||
TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId,
|
||||
attr::{Attrs, AttrsWithOwner},
|
||||
data::{
|
||||
adt::{EnumData, EnumVariantData, StructData, VariantData},
|
||||
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
|
||||
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
|
||||
expr_store::{
|
||||
Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes,
|
||||
},
|
||||
expr_store::{scope::ExprScopes, Body, BodySourceMap},
|
||||
generics::GenericParams,
|
||||
hir::generics::GenericParams,
|
||||
import_map::ImportMap,
|
||||
item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
|
||||
item_tree::{AttrOwner, ItemTree},
|
||||
lang_item::{self, LangItem, LangItemTarget, LangItems},
|
||||
nameres::{diagnostics::DefDiagnostics, DefMap},
|
||||
nameres::{
|
||||
DefMap, LocalDefMap,
|
||||
assoc::{ImplItems, TraitItems},
|
||||
diagnostics::DefDiagnostics,
|
||||
},
|
||||
signatures::{
|
||||
ConstSignature, EnumSignature, EnumVariants, FunctionSignature, ImplSignature,
|
||||
InactiveEnumVariantCode, StaticSignature, StructSignature, TraitAliasSignature,
|
||||
TraitSignature, TypeAliasSignature, UnionSignature, VariantFields,
|
||||
},
|
||||
tt,
|
||||
type_ref::TypesSourceMap,
|
||||
visibility::{self, Visibility},
|
||||
AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
|
||||
EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId,
|
||||
ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, InTypeConstId,
|
||||
InTypeConstLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroId, MacroRulesId, MacroRulesLoc,
|
||||
MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc,
|
||||
TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
||||
UseId, UseLoc, VariantId,
|
||||
};
|
||||
|
||||
#[ra_salsa::query_group(InternDatabaseStorage)]
|
||||
pub trait InternDatabase: SourceDatabase {
|
||||
// region: items
|
||||
#[ra_salsa::interned]
|
||||
fn intern_use(&self, loc: UseLoc) -> UseId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_struct(&self, loc: StructLoc) -> StructId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_union(&self, loc: UnionLoc) -> UnionId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_enum(&self, loc: EnumLoc) -> EnumId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_const(&self, loc: ConstLoc) -> ConstId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_static(&self, loc: StaticLoc) -> StaticId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_trait(&self, loc: TraitLoc) -> TraitId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_impl(&self, loc: ImplLoc) -> ImplId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
|
||||
// endregion: items
|
||||
use salsa::plumbing::AsId;
|
||||
|
||||
#[ra_salsa::interned]
|
||||
#[query_group::query_group(InternDatabaseStorage)]
|
||||
pub trait InternDatabase: RootQueryDb {
|
||||
// region: items
|
||||
#[salsa::interned]
|
||||
fn intern_use(&self, loc: UseLoc) -> UseId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_struct(&self, loc: StructLoc) -> StructId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_union(&self, loc: UnionLoc) -> UnionId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_enum(&self, loc: EnumLoc) -> EnumId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_const(&self, loc: ConstLoc) -> ConstId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_static(&self, loc: StaticLoc) -> StaticId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_trait(&self, loc: TraitLoc) -> TraitId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_impl(&self, loc: ImplLoc) -> ImplId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
|
||||
// // endregion: items
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_block(&self, loc: BlockLoc) -> BlockId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId;
|
||||
}
|
||||
|
||||
#[ra_salsa::query_group(DefDatabaseStorage)]
|
||||
pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
|
||||
#[query_group::query_group]
|
||||
pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
|
||||
/// Whether to expand procedural macros during name resolution.
|
||||
#[ra_salsa::input]
|
||||
#[salsa::input]
|
||||
fn expand_proc_attr_macros(&self) -> bool;
|
||||
|
||||
/// Computes an [`ItemTree`] for the given file or macro expansion.
|
||||
#[ra_salsa::invoke(ItemTree::file_item_tree_query)]
|
||||
#[salsa::invoke(ItemTree::file_item_tree_query)]
|
||||
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
|
||||
|
||||
#[ra_salsa::invoke(ItemTree::block_item_tree_query)]
|
||||
#[salsa::invoke(ItemTree::block_item_tree_query)]
|
||||
fn block_item_tree(&self, block_id: BlockId) -> Arc<ItemTree>;
|
||||
|
||||
#[ra_salsa::invoke(ItemTree::file_item_tree_with_source_map_query)]
|
||||
fn file_item_tree_with_source_map(
|
||||
&self,
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>);
|
||||
#[salsa::invoke(DefMap::crate_local_def_map_query)]
|
||||
fn crate_local_def_map(&self, krate: Crate) -> (Arc<DefMap>, Arc<LocalDefMap>);
|
||||
|
||||
#[ra_salsa::invoke(ItemTree::block_item_tree_with_source_map_query)]
|
||||
fn block_item_tree_with_source_map(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>);
|
||||
|
||||
#[ra_salsa::invoke(DefMap::crate_def_map_query)]
|
||||
fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
|
||||
#[salsa::invoke(DefMap::crate_def_map_query)]
|
||||
fn crate_def_map(&self, krate: Crate) -> Arc<DefMap>;
|
||||
|
||||
/// Computes the block-level `DefMap`.
|
||||
#[ra_salsa::invoke(DefMap::block_def_map_query)]
|
||||
#[salsa::invoke(DefMap::block_def_map_query)]
|
||||
fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
|
||||
|
||||
/// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution.
|
||||
#[salsa::invoke(macro_def)]
|
||||
fn macro_def(&self, m: MacroId) -> MacroDefId;
|
||||
|
||||
// region:data
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(StructData::struct_data_query)]
|
||||
fn struct_data(&self, id: StructId) -> Arc<StructData>;
|
||||
|
||||
#[ra_salsa::invoke(StructData::struct_data_with_diagnostics_query)]
|
||||
fn struct_data_with_diagnostics(&self, id: StructId) -> (Arc<StructData>, DefDiagnostics);
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(StructData::union_data_query)]
|
||||
fn union_data(&self, id: UnionId) -> Arc<StructData>;
|
||||
|
||||
#[ra_salsa::invoke(StructData::union_data_with_diagnostics_query)]
|
||||
fn union_data_with_diagnostics(&self, id: UnionId) -> (Arc<StructData>, DefDiagnostics);
|
||||
|
||||
#[ra_salsa::invoke(EnumData::enum_data_query)]
|
||||
fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(EnumVariantData::enum_variant_data_query)]
|
||||
fn enum_variant_data(&self, id: EnumVariantId) -> Arc<EnumVariantData>;
|
||||
|
||||
#[ra_salsa::invoke(EnumVariantData::enum_variant_data_with_diagnostics_query)]
|
||||
fn enum_variant_data_with_diagnostics(
|
||||
#[salsa::invoke(VariantFields::query)]
|
||||
fn variant_fields_with_source_map(
|
||||
&self,
|
||||
id: EnumVariantId,
|
||||
) -> (Arc<EnumVariantData>, DefDiagnostics);
|
||||
id: VariantId,
|
||||
) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(VariantData::variant_data)]
|
||||
fn variant_data(&self, id: VariantId) -> Arc<VariantData>;
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(ImplData::impl_data_query)]
|
||||
fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
|
||||
#[salsa::tracked]
|
||||
fn enum_variants(&self, id: EnumId) -> Arc<EnumVariants> {
|
||||
self.enum_variants_with_diagnostics(id).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(ImplData::impl_data_with_diagnostics_query)]
|
||||
fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, DefDiagnostics);
|
||||
#[salsa::invoke(EnumVariants::enum_variants_query)]
|
||||
fn enum_variants_with_diagnostics(
|
||||
&self,
|
||||
id: EnumId,
|
||||
) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>);
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(TraitData::trait_data_query)]
|
||||
fn trait_data(&self, e: TraitId) -> Arc<TraitData>;
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(ImplItems::impl_items_query)]
|
||||
fn impl_items(&self, e: ImplId) -> Arc<ImplItems>;
|
||||
|
||||
#[ra_salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
|
||||
fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitData>, DefDiagnostics);
|
||||
#[salsa::invoke(ImplItems::impl_items_with_diagnostics_query)]
|
||||
fn impl_items_with_diagnostics(&self, e: ImplId) -> (Arc<ImplItems>, DefDiagnostics);
|
||||
|
||||
#[ra_salsa::invoke(TraitAliasData::trait_alias_query)]
|
||||
fn trait_alias_data(&self, e: TraitAliasId) -> Arc<TraitAliasData>;
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(TraitItems::trait_items_query)]
|
||||
fn trait_items(&self, e: TraitId) -> Arc<TraitItems>;
|
||||
|
||||
#[ra_salsa::invoke(TypeAliasData::type_alias_data_query)]
|
||||
fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
|
||||
#[salsa::invoke(TraitItems::trait_items_with_diagnostics_query)]
|
||||
fn trait_items_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitItems>, DefDiagnostics);
|
||||
|
||||
#[ra_salsa::invoke(FunctionData::fn_data_query)]
|
||||
fn function_data(&self, func: FunctionId) -> Arc<FunctionData>;
|
||||
#[salsa::tracked]
|
||||
fn variant_fields(&self, id: VariantId) -> Arc<VariantFields> {
|
||||
self.variant_fields_with_source_map(id).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(ConstData::const_data_query)]
|
||||
fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
|
||||
#[salsa::tracked]
|
||||
fn trait_signature(&self, trait_: TraitId) -> Arc<TraitSignature> {
|
||||
self.trait_signature_with_source_map(trait_).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(StaticData::static_data_query)]
|
||||
fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
|
||||
#[salsa::tracked]
|
||||
fn impl_signature(&self, impl_: ImplId) -> Arc<ImplSignature> {
|
||||
self.impl_signature_with_source_map(impl_).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(Macro2Data::macro2_data_query)]
|
||||
fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;
|
||||
#[salsa::tracked]
|
||||
fn struct_signature(&self, struct_: StructId) -> Arc<StructSignature> {
|
||||
self.struct_signature_with_source_map(struct_).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(MacroRulesData::macro_rules_data_query)]
|
||||
fn macro_rules_data(&self, makro: MacroRulesId) -> Arc<MacroRulesData>;
|
||||
#[salsa::tracked]
|
||||
fn union_signature(&self, union_: UnionId) -> Arc<UnionSignature> {
|
||||
self.union_signature_with_source_map(union_).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(ProcMacroData::proc_macro_data_query)]
|
||||
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
|
||||
#[salsa::tracked]
|
||||
fn enum_signature(&self, e: EnumId) -> Arc<EnumSignature> {
|
||||
self.enum_signature_with_source_map(e).0
|
||||
}
|
||||
|
||||
#[ra_salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)]
|
||||
fn extern_crate_decl_data(&self, extern_crate: ExternCrateId) -> Arc<ExternCrateDeclData>;
|
||||
#[salsa::tracked]
|
||||
fn const_signature(&self, e: ConstId) -> Arc<ConstSignature> {
|
||||
self.const_signature_with_source_map(e).0
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn static_signature(&self, e: StaticId) -> Arc<StaticSignature> {
|
||||
self.static_signature_with_source_map(e).0
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn function_signature(&self, e: FunctionId) -> Arc<FunctionSignature> {
|
||||
self.function_signature_with_source_map(e).0
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn trait_alias_signature(&self, e: TraitAliasId) -> Arc<TraitAliasSignature> {
|
||||
self.trait_alias_signature_with_source_map(e).0
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn type_alias_signature(&self, e: TypeAliasId) -> Arc<TypeAliasSignature> {
|
||||
self.type_alias_signature_with_source_map(e).0
|
||||
}
|
||||
|
||||
#[salsa::invoke(TraitSignature::query)]
|
||||
fn trait_signature_with_source_map(
|
||||
&self,
|
||||
trait_: TraitId,
|
||||
) -> (Arc<TraitSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(ImplSignature::query)]
|
||||
fn impl_signature_with_source_map(
|
||||
&self,
|
||||
impl_: ImplId,
|
||||
) -> (Arc<ImplSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(StructSignature::query)]
|
||||
fn struct_signature_with_source_map(
|
||||
&self,
|
||||
struct_: StructId,
|
||||
) -> (Arc<StructSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(UnionSignature::query)]
|
||||
fn union_signature_with_source_map(
|
||||
&self,
|
||||
union_: UnionId,
|
||||
) -> (Arc<UnionSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(EnumSignature::query)]
|
||||
fn enum_signature_with_source_map(
|
||||
&self,
|
||||
e: EnumId,
|
||||
) -> (Arc<EnumSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(ConstSignature::query)]
|
||||
fn const_signature_with_source_map(
|
||||
&self,
|
||||
e: ConstId,
|
||||
) -> (Arc<ConstSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(StaticSignature::query)]
|
||||
fn static_signature_with_source_map(
|
||||
&self,
|
||||
e: StaticId,
|
||||
) -> (Arc<StaticSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(FunctionSignature::query)]
|
||||
fn function_signature_with_source_map(
|
||||
&self,
|
||||
e: FunctionId,
|
||||
) -> (Arc<FunctionSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(TraitAliasSignature::query)]
|
||||
fn trait_alias_signature_with_source_map(
|
||||
&self,
|
||||
e: TraitAliasId,
|
||||
) -> (Arc<TraitAliasSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
#[salsa::invoke(TypeAliasSignature::query)]
|
||||
fn type_alias_signature_with_source_map(
|
||||
&self,
|
||||
e: TypeAliasId,
|
||||
) -> (Arc<TypeAliasSignature>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
// endregion:data
|
||||
|
||||
#[ra_salsa::invoke(Body::body_with_source_map_query)]
|
||||
#[ra_salsa::lru]
|
||||
#[salsa::invoke(Body::body_with_source_map_query)]
|
||||
#[salsa::lru(512)]
|
||||
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
|
||||
|
||||
#[ra_salsa::invoke(Body::body_query)]
|
||||
#[salsa::invoke(Body::body_query)]
|
||||
fn body(&self, def: DefWithBodyId) -> Arc<Body>;
|
||||
|
||||
#[ra_salsa::invoke(ExprScopes::expr_scopes_query)]
|
||||
#[salsa::invoke(ExprScopes::expr_scopes_query)]
|
||||
fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>;
|
||||
|
||||
#[ra_salsa::invoke(GenericParams::generic_params_query)]
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(GenericParams::new)]
|
||||
fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>;
|
||||
|
||||
/// If this returns `None` for the source map, that means it is the same as with the item tree.
|
||||
#[ra_salsa::invoke(GenericParams::generic_params_with_source_map_query)]
|
||||
fn generic_params_with_source_map(
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(GenericParams::generic_params_and_store)]
|
||||
fn generic_params_and_store(
|
||||
&self,
|
||||
def: GenericDefId,
|
||||
) -> (Arc<GenericParams>, Option<Arc<TypesSourceMap>>);
|
||||
) -> (Arc<GenericParams>, Arc<ExpressionStore>);
|
||||
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(GenericParams::generic_params_and_store_and_source_map)]
|
||||
fn generic_params_and_store_and_source_map(
|
||||
&self,
|
||||
def: GenericDefId,
|
||||
) -> (Arc<GenericParams>, Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>);
|
||||
|
||||
// region:attrs
|
||||
|
||||
#[ra_salsa::invoke(Attrs::fields_attrs_query)]
|
||||
#[salsa::invoke(Attrs::fields_attrs_query)]
|
||||
fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>;
|
||||
|
||||
// should this really be a query?
|
||||
#[ra_salsa::invoke(crate::attr::fields_attrs_source_map)]
|
||||
#[salsa::invoke(crate::attr::fields_attrs_source_map)]
|
||||
fn fields_attrs_source_map(
|
||||
&self,
|
||||
def: VariantId,
|
||||
) -> Arc<ArenaMap<LocalFieldId, AstPtr<Either<ast::TupleField, ast::RecordField>>>>;
|
||||
|
||||
#[ra_salsa::invoke(AttrsWithOwner::attrs_query)]
|
||||
// FIXME: Make this a non-interned query.
|
||||
#[salsa::invoke_interned(AttrsWithOwner::attrs_query)]
|
||||
fn attrs(&self, def: AttrDefId) -> Attrs;
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(lang_item::lang_attr)]
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(lang_item::lang_attr)]
|
||||
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
|
||||
|
||||
// endregion:attrs
|
||||
|
||||
#[ra_salsa::invoke(LangItems::lang_item_query)]
|
||||
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
|
||||
#[salsa::invoke(LangItems::lang_item_query)]
|
||||
fn lang_item(&self, start_crate: Crate, item: LangItem) -> Option<LangItemTarget>;
|
||||
|
||||
#[ra_salsa::invoke(ImportMap::import_map_query)]
|
||||
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
|
||||
#[salsa::invoke(ImportMap::import_map_query)]
|
||||
fn import_map(&self, krate: Crate) -> Arc<ImportMap>;
|
||||
|
||||
// region:visibilities
|
||||
|
||||
#[ra_salsa::invoke(visibility::field_visibilities_query)]
|
||||
#[salsa::invoke(visibility::field_visibilities_query)]
|
||||
fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
|
||||
|
||||
// FIXME: unify function_visibility and const_visibility?
|
||||
#[ra_salsa::invoke(visibility::function_visibility_query)]
|
||||
|
||||
#[salsa::invoke(visibility::function_visibility_query)]
|
||||
fn function_visibility(&self, def: FunctionId) -> Visibility;
|
||||
|
||||
#[ra_salsa::invoke(visibility::const_visibility_query)]
|
||||
#[salsa::invoke(visibility::const_visibility_query)]
|
||||
fn const_visibility(&self, def: ConstId) -> Visibility;
|
||||
|
||||
#[salsa::invoke(visibility::type_alias_visibility_query)]
|
||||
fn type_alias_visibility(&self, def: TypeAliasId) -> Visibility;
|
||||
|
||||
// endregion:visibilities
|
||||
|
||||
#[ra_salsa::invoke(LangItems::crate_lang_items_query)]
|
||||
fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>;
|
||||
#[salsa::invoke(LangItems::crate_lang_items_query)]
|
||||
fn crate_lang_items(&self, krate: Crate) -> Option<Arc<LangItems>>;
|
||||
|
||||
#[ra_salsa::invoke(crate::lang_item::notable_traits_in_deps)]
|
||||
fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>;
|
||||
#[ra_salsa::invoke(crate::lang_item::crate_notable_traits)]
|
||||
fn crate_notable_traits(&self, krate: CrateId) -> Option<Arc<[TraitId]>>;
|
||||
#[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
|
||||
fn notable_traits_in_deps(&self, krate: Crate) -> Arc<[Arc<[TraitId]>]>;
|
||||
#[salsa::invoke(crate::lang_item::crate_notable_traits)]
|
||||
fn crate_notable_traits(&self, krate: Crate) -> Option<Arc<[TraitId]>>;
|
||||
|
||||
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
||||
#[salsa::invoke(crate_supports_no_std)]
|
||||
fn crate_supports_no_std(&self, crate_id: Crate) -> bool;
|
||||
|
||||
fn include_macro_invoc(&self, crate_id: CrateId) -> Arc<[(MacroCallId, EditionedFileId)]>;
|
||||
#[salsa::invoke(include_macro_invoc)]
|
||||
fn include_macro_invoc(&self, crate_id: Crate) -> Arc<[(MacroCallId, EditionedFileId)]>;
|
||||
}
|
||||
|
||||
// return: macro call id and include file id
|
||||
fn include_macro_invoc(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
) -> Arc<[(MacroCallId, EditionedFileId)]> {
|
||||
db.crate_def_map(krate)
|
||||
.modules
|
||||
|
|
@ -275,20 +375,20 @@ fn include_macro_invoc(
|
|||
.flat_map(|m| m.scope.iter_macro_invoc())
|
||||
.filter_map(|invoc| {
|
||||
db.lookup_intern_macro_call(*invoc.1)
|
||||
.include_file_id(db.upcast(), *invoc.1)
|
||||
.include_file_id(db, *invoc.1)
|
||||
.map(|x| (*invoc.1, x))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
|
||||
let file = db.crate_graph()[crate_id].root_file_id();
|
||||
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: Crate) -> bool {
|
||||
let file = crate_id.data(db).root_file_id(db);
|
||||
let item_tree = db.file_item_tree(file.into());
|
||||
let attrs = item_tree.raw_attrs(AttrOwner::TopLevel);
|
||||
for attr in &**attrs {
|
||||
match attr.path().as_ident() {
|
||||
Some(ident) if *ident == sym::no_std.clone() => return true,
|
||||
Some(ident) if *ident == sym::cfg_attr.clone() => {}
|
||||
Some(ident) if *ident == sym::no_std => return true,
|
||||
Some(ident) if *ident == sym::cfg_attr => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
|
|
@ -304,7 +404,7 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
|
|||
for output in segments.skip(1) {
|
||||
match output.flat_tokens() {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,15 +27,15 @@
|
|||
pub mod keys {
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use hir_expand::{attrs::AttrId, MacroCallId};
|
||||
use hir_expand::{MacroCallId, attrs::AttrId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
use syntax::{AstNode, AstPtr, ast};
|
||||
|
||||
use crate::{
|
||||
dyn_map::{DynMap, Policy},
|
||||
BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId,
|
||||
ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId,
|
||||
TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
|
||||
dyn_map::{DynMap, Policy},
|
||||
};
|
||||
|
||||
pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>;
|
||||
|
|
@ -112,6 +112,10 @@ pub struct Key<K, V, P = (K, V)> {
|
|||
}
|
||||
|
||||
impl<K, V, P> Key<K, V, P> {
|
||||
#[allow(
|
||||
clippy::new_without_default,
|
||||
reason = "this a const fn, so it can't be default yet. See <https://github.com/rust-lang/rust/issues/63065>"
|
||||
)]
|
||||
pub(crate) const fn new() -> Key<K, V, P> {
|
||||
Key { _phantom: PhantomData }
|
||||
}
|
||||
|
|
@ -148,16 +152,11 @@ impl<K: Hash + Eq + 'static, V: 'static> Policy for (K, V) {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DynMap {
|
||||
pub(crate) map: Map,
|
||||
}
|
||||
|
||||
impl Default for DynMap {
|
||||
fn default() -> Self {
|
||||
DynMap { map: Map::new() }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct KeyMap<KEY> {
|
||||
map: DynMap,
|
||||
|
|
|
|||
|
|
@ -1,243 +0,0 @@
|
|||
//! Macro expansion utilities.
|
||||
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgOptions;
|
||||
use drop_bomb::DropBomb;
|
||||
use hir_expand::{
|
||||
attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandErrorKind,
|
||||
ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
|
||||
};
|
||||
use span::{Edition, SyntaxContextId};
|
||||
use syntax::{ast, Parse};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::type_ref::{TypesMap, TypesSourceMap};
|
||||
use crate::{
|
||||
attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId,
|
||||
UnresolvedMacro,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Expander {
|
||||
cfg_options: Arc<CfgOptions>,
|
||||
span_map: OnceCell<SpanMap>,
|
||||
current_file_id: HirFileId,
|
||||
pub(crate) module: ModuleId,
|
||||
/// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
|
||||
recursion_depth: u32,
|
||||
recursion_limit: usize,
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
|
||||
let recursion_limit = module.def_map(db).recursion_limit() as usize;
|
||||
let recursion_limit = if cfg!(test) {
|
||||
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
|
||||
std::cmp::min(32, recursion_limit)
|
||||
} else {
|
||||
recursion_limit
|
||||
};
|
||||
Expander {
|
||||
current_file_id,
|
||||
module,
|
||||
recursion_depth: 0,
|
||||
recursion_limit,
|
||||
cfg_options: db.crate_graph()[module.krate].cfg_options.clone(),
|
||||
span_map: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn span_map(&self, db: &dyn DefDatabase) -> &SpanMap {
|
||||
self.span_map.get_or_init(|| db.span_map(self.current_file_id))
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> CrateId {
|
||||
self.module.krate
|
||||
}
|
||||
|
||||
pub fn syntax_context(&self) -> SyntaxContextId {
|
||||
// FIXME:
|
||||
SyntaxContextId::root(Edition::CURRENT)
|
||||
}
|
||||
|
||||
pub fn enter_expand<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
macro_call: ast::MacroCall,
|
||||
resolver: impl Fn(&ModPath) -> Option<MacroId>,
|
||||
) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
|
||||
// FIXME: within_limit should support this, instead of us having to extract the error
|
||||
let mut unresolved_macro_err = None;
|
||||
|
||||
let result = self.within_limit(db, |this| {
|
||||
let macro_call = this.in_file(¯o_call);
|
||||
match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| {
|
||||
resolver(path).map(|it| db.macro_def(it))
|
||||
}) {
|
||||
Ok(call_id) => call_id,
|
||||
Err(resolve_err) => {
|
||||
unresolved_macro_err = Some(resolve_err);
|
||||
ExpandResult { value: None, err: None }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(err) = unresolved_macro_err {
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_expand_id<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
) -> ExpandResult<Option<(Mark, Parse<T>)>> {
|
||||
self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, mut mark: Mark) {
|
||||
self.span_map = mark.span_map;
|
||||
self.current_file_id = mark.file_id;
|
||||
if self.recursion_depth == u32::MAX {
|
||||
// Recursion limit has been reached somewhere in the macro expansion tree. Reset the
|
||||
// depth only when we get out of the tree.
|
||||
if !self.current_file_id.is_macro() {
|
||||
self.recursion_depth = 0;
|
||||
}
|
||||
} else {
|
||||
self.recursion_depth -= 1;
|
||||
}
|
||||
mark.bomb.defuse();
|
||||
}
|
||||
|
||||
pub fn ctx<'a>(
|
||||
&self,
|
||||
db: &'a dyn DefDatabase,
|
||||
types_map: &'a mut TypesMap,
|
||||
types_source_map: &'a mut TypesSourceMap,
|
||||
) -> LowerCtx<'a> {
|
||||
LowerCtx::with_span_map_cell(
|
||||
db,
|
||||
self.current_file_id,
|
||||
self.span_map.clone(),
|
||||
types_map,
|
||||
types_source_map,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn in_file<T>(&self, value: T) -> InFile<T> {
|
||||
InFile { file_id: self.current_file_id, value }
|
||||
}
|
||||
|
||||
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
|
||||
Attrs::filter(
|
||||
db,
|
||||
self.krate(),
|
||||
RawAttrs::new(
|
||||
db.upcast(),
|
||||
owner,
|
||||
self.span_map.get_or_init(|| db.span_map(self.current_file_id)).as_ref(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn cfg_options(&self) -> &CfgOptions {
|
||||
&self.cfg_options
|
||||
}
|
||||
|
||||
pub fn current_file_id(&self) -> HirFileId {
|
||||
self.current_file_id
|
||||
}
|
||||
|
||||
pub(crate) fn parse_path(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
path: ast::Path,
|
||||
types_map: &mut TypesMap,
|
||||
types_source_map: &mut TypesSourceMap,
|
||||
) -> Option<Path> {
|
||||
let mut ctx = LowerCtx::with_span_map_cell(
|
||||
db,
|
||||
self.current_file_id,
|
||||
self.span_map.clone(),
|
||||
types_map,
|
||||
types_source_map,
|
||||
);
|
||||
Path::from_src(&mut ctx, path)
|
||||
}
|
||||
|
||||
fn within_limit<F, T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
op: F,
|
||||
) -> ExpandResult<Option<(Mark, Parse<T>)>>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
|
||||
{
|
||||
if self.recursion_depth == u32::MAX {
|
||||
// Recursion limit has been reached somewhere in the macro expansion tree. We should
|
||||
// stop expanding other macro calls in this tree, or else this may result in
|
||||
// exponential number of macro expansions, leading to a hang.
|
||||
//
|
||||
// The overflow error should have been reported when it occurred (see the next branch),
|
||||
// so don't return overflow error here to avoid diagnostics duplication.
|
||||
cov_mark::hit!(overflow_but_not_me);
|
||||
return ExpandResult::ok(None);
|
||||
}
|
||||
|
||||
let ExpandResult { value, err } = op(self);
|
||||
let Some(call_id) = value else {
|
||||
return ExpandResult { value: None, err };
|
||||
};
|
||||
if self.recursion_depth as usize > self.recursion_limit {
|
||||
self.recursion_depth = u32::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
return ExpandResult::only_err(ExpandError::new(
|
||||
db.macro_arg_considering_derives(call_id, &call_id.lookup(db.upcast()).kind).2,
|
||||
ExpandErrorKind::RecursionOverflow,
|
||||
));
|
||||
}
|
||||
|
||||
let macro_file = call_id.as_macro_file();
|
||||
let res = db.parse_macro_expansion(macro_file);
|
||||
|
||||
let err = err.or(res.err);
|
||||
ExpandResult {
|
||||
value: match &err {
|
||||
// If proc-macro is disabled or unresolved, we want to expand to a missing expression
|
||||
// instead of an empty tree which might end up in an empty block.
|
||||
Some(e) if matches!(e.kind(), ExpandErrorKind::MissingProcMacroExpander(_)) => None,
|
||||
_ => (|| {
|
||||
let parse = res.value.0.cast::<T>()?;
|
||||
|
||||
self.recursion_depth += 1;
|
||||
let old_span_map = OnceCell::new();
|
||||
if let Some(prev) = self.span_map.take() {
|
||||
_ = old_span_map.set(prev);
|
||||
};
|
||||
_ = self.span_map.set(SpanMap::ExpansionSpanMap(res.value.1));
|
||||
let old_file_id =
|
||||
std::mem::replace(&mut self.current_file_id, macro_file.into());
|
||||
let mark = Mark {
|
||||
file_id: old_file_id,
|
||||
span_map: old_span_map,
|
||||
bomb: DropBomb::new("expansion mark dropped"),
|
||||
};
|
||||
Some((mark, parse))
|
||||
})(),
|
||||
},
|
||||
err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mark {
|
||||
file_id: HirFileId,
|
||||
span_map: OnceCell<SpanMap>,
|
||||
bomb: DropBomb,
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
//! Defines `ExpressionStore`: a lowered representation of functions, statics and
|
||||
//! consts.
|
||||
mod body;
|
||||
mod lower;
|
||||
mod pretty;
|
||||
pub mod body;
|
||||
mod expander;
|
||||
pub mod lower;
|
||||
pub mod path;
|
||||
pub mod pretty;
|
||||
pub mod scope;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
|
|
@ -12,45 +13,49 @@ use std::ops::{Deref, Index};
|
|||
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{name::Name, ExpandError, InFile};
|
||||
use hir_expand::{ExpandError, InFile, MacroCallId, mod_path::ModPath, name::Name};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::{Edition, MacroFileId, SyntaxContextData};
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||
use span::{Edition, SyntaxContext};
|
||||
use syntax::{AstPtr, SyntaxNodePtr, ast};
|
||||
use triomphe::Arc;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
BlockId, SyntheticSyntax,
|
||||
db::DefDatabase,
|
||||
expr_store::path::Path,
|
||||
hir::{
|
||||
Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
|
||||
PatId, RecordFieldPat, Statement,
|
||||
},
|
||||
nameres::DefMap,
|
||||
path::{ModPath, Path},
|
||||
type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
||||
BlockId, DefWithBodyId, Lookup, SyntheticSyntax,
|
||||
type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId},
|
||||
};
|
||||
|
||||
pub use self::body::{Body, BodySourceMap};
|
||||
pub use self::lower::{
|
||||
hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment,
|
||||
};
|
||||
|
||||
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct HygieneId(span::SyntaxContextId);
|
||||
pub struct HygieneId(span::SyntaxContext);
|
||||
|
||||
impl HygieneId {
|
||||
// The edition doesn't matter here, we only use this for comparisons and to lookup the macro.
|
||||
pub const ROOT: Self = Self(span::SyntaxContextId::root(Edition::Edition2015));
|
||||
pub const ROOT: Self = Self(span::SyntaxContext::root(Edition::Edition2015));
|
||||
|
||||
pub fn new(mut ctx: span::SyntaxContextId) -> Self {
|
||||
pub fn new(mut ctx: span::SyntaxContext) -> Self {
|
||||
// See `Name` for why we're doing that.
|
||||
ctx.remove_root_edition();
|
||||
Self(ctx)
|
||||
}
|
||||
|
||||
pub(crate) fn lookup(self, db: &dyn DefDatabase) -> SyntaxContextData {
|
||||
db.lookup_intern_syntax_context(self.0)
|
||||
// FIXME: Inline this
|
||||
pub(crate) fn lookup(self) -> SyntaxContext {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub(crate) fn is_root(self) -> bool {
|
||||
|
|
@ -79,27 +84,26 @@ pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
|
|||
pub type SelfParamPtr = AstPtr<ast::SelfParam>;
|
||||
pub type MacroCallPtr = AstPtr<ast::MacroCall>;
|
||||
|
||||
pub type TypePtr = AstPtr<ast::Type>;
|
||||
pub type TypeSource = InFile<TypePtr>;
|
||||
|
||||
pub type LifetimePtr = AstPtr<ast::Lifetime>;
|
||||
pub type LifetimeSource = InFile<LifetimePtr>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ExpressionStore {
|
||||
pub exprs: Arena<Expr>,
|
||||
pub pats: Arena<Pat>,
|
||||
pub bindings: Arena<Binding>,
|
||||
pub labels: Arena<Label>,
|
||||
pub types: Arena<TypeRef>,
|
||||
pub lifetimes: Arena<LifetimeRef>,
|
||||
/// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
|
||||
/// top level expression, it will not be listed in here.
|
||||
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
||||
pub types: TypesMap,
|
||||
/// Block expressions in this store that may contain inner items.
|
||||
block_scopes: Box<[BlockId]>,
|
||||
|
||||
/// A map from binding to its hygiene ID.
|
||||
///
|
||||
/// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here.
|
||||
/// If a binding does not appear here it has `SyntaxContextId::ROOT`.
|
||||
///
|
||||
/// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
|
||||
/// expansions are attributed to their parent expansion (recursively).
|
||||
binding_hygiene: FxHashMap<BindingId, HygieneId>,
|
||||
/// A map from an variable usages to their hygiene ID.
|
||||
///
|
||||
/// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer
|
||||
|
|
@ -127,15 +131,19 @@ pub struct ExpressionStoreSourceMap {
|
|||
field_map_back: FxHashMap<ExprId, FieldSource>,
|
||||
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
|
||||
|
||||
pub types: TypesSourceMap,
|
||||
types_map_back: ArenaMap<TypeRefId, TypeSource>,
|
||||
types_map: FxHashMap<TypeSource, TypeRefId>,
|
||||
|
||||
lifetime_map_back: ArenaMap<LifetimeRefId, LifetimeSource>,
|
||||
lifetime_map: FxHashMap<LifetimeSource, LifetimeRefId>,
|
||||
|
||||
template_map: Option<Box<FormatTemplate>>,
|
||||
|
||||
expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>,
|
||||
pub expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
|
||||
|
||||
/// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
|
||||
/// the source map (since they're just as volatile).
|
||||
diagnostics: Vec<ExpressionStoreDiagnostics>,
|
||||
pub diagnostics: Vec<ExpressionStoreDiagnostics>,
|
||||
}
|
||||
|
||||
/// The body of an item (function, const etc.).
|
||||
|
|
@ -145,10 +153,10 @@ pub struct ExpressionStoreBuilder {
|
|||
pub pats: Arena<Pat>,
|
||||
pub bindings: Arena<Binding>,
|
||||
pub labels: Arena<Label>,
|
||||
pub lifetimes: Arena<LifetimeRef>,
|
||||
pub binding_owners: FxHashMap<BindingId, ExprId>,
|
||||
pub types: TypesMap,
|
||||
pub types: Arena<TypeRef>,
|
||||
block_scopes: Vec<BlockId>,
|
||||
binding_hygiene: FxHashMap<BindingId, HygieneId>,
|
||||
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +185,7 @@ pub enum ExpressionStoreDiagnostics {
|
|||
}
|
||||
|
||||
impl ExpressionStoreBuilder {
|
||||
fn finish(self) -> ExpressionStore {
|
||||
pub fn finish(self) -> ExpressionStore {
|
||||
let Self {
|
||||
block_scopes,
|
||||
mut exprs,
|
||||
|
|
@ -185,18 +193,18 @@ impl ExpressionStoreBuilder {
|
|||
mut pats,
|
||||
mut bindings,
|
||||
mut binding_owners,
|
||||
mut binding_hygiene,
|
||||
mut ident_hygiene,
|
||||
mut types,
|
||||
mut lifetimes,
|
||||
} = self;
|
||||
exprs.shrink_to_fit();
|
||||
labels.shrink_to_fit();
|
||||
pats.shrink_to_fit();
|
||||
bindings.shrink_to_fit();
|
||||
binding_owners.shrink_to_fit();
|
||||
binding_hygiene.shrink_to_fit();
|
||||
ident_hygiene.shrink_to_fit();
|
||||
types.shrink_to_fit();
|
||||
lifetimes.shrink_to_fit();
|
||||
|
||||
ExpressionStore {
|
||||
exprs,
|
||||
|
|
@ -205,8 +213,8 @@ impl ExpressionStoreBuilder {
|
|||
labels,
|
||||
binding_owners,
|
||||
types,
|
||||
lifetimes,
|
||||
block_scopes: block_scopes.into_boxed_slice(),
|
||||
binding_hygiene,
|
||||
ident_hygiene,
|
||||
}
|
||||
}
|
||||
|
|
@ -275,6 +283,9 @@ impl ExpressionStore {
|
|||
}
|
||||
}
|
||||
|
||||
/// Walks the immediate children expressions and calls `f` for each child expression.
|
||||
///
|
||||
/// Note that this does not walk const blocks.
|
||||
pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
|
||||
let expr = &self[expr_id];
|
||||
match expr {
|
||||
|
|
@ -408,6 +419,10 @@ impl ExpressionStore {
|
|||
}
|
||||
}
|
||||
|
||||
/// Walks the immediate children expressions and calls `f` for each child expression but does
|
||||
/// not walk expressions within patterns.
|
||||
///
|
||||
/// Note that this does not walk const blocks.
|
||||
pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
|
||||
let expr = &self[expr_id];
|
||||
match expr {
|
||||
|
|
@ -542,7 +557,7 @@ impl ExpressionStore {
|
|||
}
|
||||
|
||||
fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
|
||||
self.binding_hygiene.get(&binding).copied().unwrap_or(HygieneId::ROOT)
|
||||
self.bindings[binding].hygiene
|
||||
}
|
||||
|
||||
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
|
||||
|
|
@ -601,6 +616,26 @@ impl Index<TypeRefId> for ExpressionStore {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<LifetimeRefId> for ExpressionStore {
|
||||
type Output = LifetimeRef;
|
||||
|
||||
fn index(&self, b: LifetimeRefId) -> &LifetimeRef {
|
||||
&self.lifetimes[b]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<PathId> for ExpressionStore {
|
||||
type Output = Path;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: PathId) -> &Self::Output {
|
||||
let TypeRef::Path(path) = &self[index.type_ref()] else {
|
||||
unreachable!("`PathId` always points to `TypeRef::Path`");
|
||||
};
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Change `node_` prefix to something more reasonable.
|
||||
// Perhaps `expr_syntax` and `expr_id`?
|
||||
impl ExpressionStoreSourceMap {
|
||||
|
|
@ -620,12 +655,12 @@ impl ExpressionStoreSourceMap {
|
|||
self.expr_map.get(&src).cloned()
|
||||
}
|
||||
|
||||
pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroFileId> {
|
||||
pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
|
||||
let src = node.map(AstPtr::new);
|
||||
self.expansions.get(&src).cloned()
|
||||
}
|
||||
|
||||
pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroFileId)> + '_ {
|
||||
pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroCallId)> + '_ {
|
||||
self.expansions.iter().map(|(&a, &b)| (a, b))
|
||||
}
|
||||
|
||||
|
|
@ -637,6 +672,14 @@ impl ExpressionStoreSourceMap {
|
|||
self.pat_map.get(&node.map(AstPtr::new)).cloned()
|
||||
}
|
||||
|
||||
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
|
||||
self.types_map_back.get(id).cloned().ok_or(SyntheticSyntax)
|
||||
}
|
||||
|
||||
pub fn node_type(&self, node: InFile<&ast::Type>) -> Option<TypeRefId> {
|
||||
self.types_map.get(&node.map(AstPtr::new)).cloned()
|
||||
}
|
||||
|
||||
pub fn label_syntax(&self, label: LabelId) -> LabelSource {
|
||||
self.label_map_back[label]
|
||||
}
|
||||
|
|
@ -663,10 +706,14 @@ impl ExpressionStoreSourceMap {
|
|||
self.expr_map.get(&src).copied()
|
||||
}
|
||||
|
||||
pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroFileId)> {
|
||||
pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroCallId)> {
|
||||
self.expansions.iter()
|
||||
}
|
||||
|
||||
pub fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
|
||||
self.expansions.get(&node.map(AstPtr::new)).copied()
|
||||
}
|
||||
|
||||
pub fn implicit_format_args(
|
||||
&self,
|
||||
node: InFile<&ast::FormatArgsExpr>,
|
||||
|
|
@ -716,7 +763,10 @@ impl ExpressionStoreSourceMap {
|
|||
template_map,
|
||||
diagnostics,
|
||||
binding_definitions,
|
||||
types,
|
||||
types_map,
|
||||
types_map_back,
|
||||
lifetime_map_back,
|
||||
lifetime_map,
|
||||
} = self;
|
||||
if let Some(template_map) = template_map {
|
||||
let FormatTemplate {
|
||||
|
|
@ -739,6 +789,9 @@ impl ExpressionStoreSourceMap {
|
|||
expansions.shrink_to_fit();
|
||||
diagnostics.shrink_to_fit();
|
||||
binding_definitions.shrink_to_fit();
|
||||
types.shrink_to_fit();
|
||||
types_map.shrink_to_fit();
|
||||
types_map_back.shrink_to_fit();
|
||||
lifetime_map.shrink_to_fit();
|
||||
lifetime_map_back.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,18 @@
|
|||
use std::ops;
|
||||
|
||||
use hir_expand::{InFile, Lookup};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use span::Edition;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
expander::Expander,
|
||||
expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr},
|
||||
hir::{BindingId, ExprId, PatId},
|
||||
item_tree::AttrOwner,
|
||||
src::HasSource,
|
||||
DefWithBodyId, HasModule,
|
||||
db::DefDatabase,
|
||||
expr_store::{
|
||||
ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr, lower::lower_body, pretty,
|
||||
},
|
||||
hir::{BindingId, ExprId, PatId},
|
||||
src::HasSource,
|
||||
};
|
||||
|
||||
/// The body of an item (function, const etc.).
|
||||
|
|
@ -79,31 +78,10 @@ impl Body {
|
|||
let InFile { file_id, value: body } = {
|
||||
match def {
|
||||
DefWithBodyId::FunctionId(f) => {
|
||||
let data = db.function_data(f);
|
||||
let f = f.lookup(db);
|
||||
let src = f.source(db);
|
||||
params = src.value.param_list().map(move |param_list| {
|
||||
let item_tree = f.id.item_tree(db);
|
||||
let func = &item_tree[f.id.value];
|
||||
let krate = f.container.module(db).krate;
|
||||
let crate_graph = db.crate_graph();
|
||||
(
|
||||
param_list,
|
||||
(0..func.params.len()).map(move |idx| {
|
||||
item_tree
|
||||
.attrs(
|
||||
db,
|
||||
krate,
|
||||
AttrOwner::Param(
|
||||
f.id.value,
|
||||
Idx::from_raw(RawIdx::from(idx as u32)),
|
||||
),
|
||||
)
|
||||
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
||||
}),
|
||||
)
|
||||
});
|
||||
is_async_fn = data.is_async();
|
||||
params = src.value.param_list();
|
||||
is_async_fn = src.value.async_token().is_some();
|
||||
src.map(|it| it.body().map(ast::Expr::from))
|
||||
}
|
||||
DefWithBodyId::ConstId(c) => {
|
||||
|
|
@ -121,13 +99,11 @@ impl Body {
|
|||
let src = s.source(db);
|
||||
src.map(|it| it.expr())
|
||||
}
|
||||
DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
|
||||
}
|
||||
};
|
||||
let module = def.module(db);
|
||||
let expander = Expander::new(db, file_id, module);
|
||||
let (body, mut source_map) =
|
||||
lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn);
|
||||
lower_body(db, def, file_id, module, params, body, is_async_fn);
|
||||
source_map.store.shrink_to_fit();
|
||||
|
||||
(Arc::new(body), Arc::new(source_map))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,220 @@
|
|||
//! Macro expansion utilities.
|
||||
|
||||
use std::mem;
|
||||
|
||||
use base_db::Crate;
|
||||
use drop_bomb::DropBomb;
|
||||
use hir_expand::{
|
||||
ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
|
||||
attrs::RawAttrs, eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
|
||||
};
|
||||
use span::{AstIdMap, Edition, SyntaxContext};
|
||||
use syntax::ast::HasAttrs;
|
||||
use syntax::{Parse, ast};
|
||||
use triomphe::Arc;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::attr::Attrs;
|
||||
use crate::expr_store::HygieneId;
|
||||
use crate::nameres::DefMap;
|
||||
use crate::{AsMacroCall, MacroId, UnresolvedMacro, db::DefDatabase};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Expander {
|
||||
span_map: SpanMap,
|
||||
current_file_id: HirFileId,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
/// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
|
||||
recursion_depth: u32,
|
||||
recursion_limit: usize,
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub(super) fn new(
|
||||
db: &dyn DefDatabase,
|
||||
current_file_id: HirFileId,
|
||||
def_map: &DefMap,
|
||||
) -> Expander {
|
||||
let recursion_limit = def_map.recursion_limit() as usize;
|
||||
let recursion_limit = if cfg!(test) {
|
||||
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
|
||||
std::cmp::min(32, recursion_limit)
|
||||
} else {
|
||||
recursion_limit
|
||||
};
|
||||
Expander {
|
||||
current_file_id,
|
||||
recursion_depth: 0,
|
||||
recursion_limit,
|
||||
span_map: db.span_map(current_file_id),
|
||||
ast_id_map: db.ast_id_map(current_file_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ctx_for_range(&self, range: TextRange) -> SyntaxContext {
|
||||
self.span_map.span_for_range(range).ctx
|
||||
}
|
||||
|
||||
pub(super) fn hygiene_for_range(&self, db: &dyn DefDatabase, range: TextRange) -> HygieneId {
|
||||
match self.span_map.as_ref() {
|
||||
hir_expand::span_map::SpanMapRef::ExpansionSpanMap(span_map) => {
|
||||
HygieneId::new(span_map.span_at(range.start()).ctx.opaque_and_semitransparent(db))
|
||||
}
|
||||
hir_expand::span_map::SpanMapRef::RealSpanMap(_) => HygieneId::ROOT,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn attrs(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
krate: Crate,
|
||||
has_attrs: &dyn HasAttrs,
|
||||
) -> Attrs {
|
||||
Attrs::filter(db, krate, RawAttrs::new(db, has_attrs, self.span_map.as_ref()))
|
||||
}
|
||||
|
||||
pub(super) fn is_cfg_enabled(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
krate: Crate,
|
||||
has_attrs: &dyn HasAttrs,
|
||||
) -> bool {
|
||||
self.attrs(db, krate, has_attrs).is_cfg_enabled(krate.cfg_options(db))
|
||||
}
|
||||
|
||||
pub(super) fn call_syntax_ctx(&self) -> SyntaxContext {
|
||||
// FIXME:
|
||||
SyntaxContext::root(Edition::CURRENT_FIXME)
|
||||
}
|
||||
|
||||
pub(super) fn enter_expand<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
macro_call: ast::MacroCall,
|
||||
krate: Crate,
|
||||
resolver: impl Fn(&ModPath) -> Option<MacroId>,
|
||||
eager_callback: EagerCallBackFn<'_>,
|
||||
) -> Result<ExpandResult<Option<(Mark, Option<Parse<T>>)>>, UnresolvedMacro> {
|
||||
// FIXME: within_limit should support this, instead of us having to extract the error
|
||||
let mut unresolved_macro_err = None;
|
||||
|
||||
let result = self.within_limit(db, |this| {
|
||||
let macro_call = this.in_file(¯o_call);
|
||||
match macro_call.as_call_id_with_errors(
|
||||
db,
|
||||
krate,
|
||||
|path| resolver(path).map(|it| db.macro_def(it)),
|
||||
eager_callback,
|
||||
) {
|
||||
Ok(call_id) => call_id,
|
||||
Err(resolve_err) => {
|
||||
unresolved_macro_err = Some(resolve_err);
|
||||
ExpandResult { value: None, err: None }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(err) = unresolved_macro_err { Err(err) } else { Ok(result) }
|
||||
}
|
||||
|
||||
pub(super) fn enter_expand_id<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
) -> ExpandResult<Option<(Mark, Option<Parse<T>>)>> {
|
||||
self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
|
||||
}
|
||||
|
||||
pub(super) fn exit(&mut self, Mark { file_id, span_map, ast_id_map, mut bomb }: Mark) {
|
||||
self.span_map = span_map;
|
||||
self.current_file_id = file_id;
|
||||
self.ast_id_map = ast_id_map;
|
||||
if self.recursion_depth == u32::MAX {
|
||||
// Recursion limit has been reached somewhere in the macro expansion tree. Reset the
|
||||
// depth only when we get out of the tree.
|
||||
if !self.current_file_id.is_macro() {
|
||||
self.recursion_depth = 0;
|
||||
}
|
||||
} else {
|
||||
self.recursion_depth -= 1;
|
||||
}
|
||||
bomb.defuse();
|
||||
}
|
||||
|
||||
pub(super) fn in_file<T>(&self, value: T) -> InFile<T> {
|
||||
InFile { file_id: self.current_file_id, value }
|
||||
}
|
||||
|
||||
pub(super) fn current_file_id(&self) -> HirFileId {
|
||||
self.current_file_id
|
||||
}
|
||||
|
||||
fn within_limit<F, T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
op: F,
|
||||
) -> ExpandResult<Option<(Mark, Option<Parse<T>>)>>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
|
||||
{
|
||||
if self.recursion_depth == u32::MAX {
|
||||
// Recursion limit has been reached somewhere in the macro expansion tree. We should
|
||||
// stop expanding other macro calls in this tree, or else this may result in
|
||||
// exponential number of macro expansions, leading to a hang.
|
||||
//
|
||||
// The overflow error should have been reported when it occurred (see the next branch),
|
||||
// so don't return overflow error here to avoid diagnostics duplication.
|
||||
cov_mark::hit!(overflow_but_not_me);
|
||||
return ExpandResult::ok(None);
|
||||
}
|
||||
|
||||
let ExpandResult { value, err } = op(self);
|
||||
let Some(call_id) = value else {
|
||||
return ExpandResult { value: None, err };
|
||||
};
|
||||
if self.recursion_depth as usize > self.recursion_limit {
|
||||
self.recursion_depth = u32::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
return ExpandResult::only_err(ExpandError::new(
|
||||
db.macro_arg_considering_derives(call_id, &call_id.lookup(db).kind).2,
|
||||
ExpandErrorKind::RecursionOverflow,
|
||||
));
|
||||
}
|
||||
|
||||
let res = db.parse_macro_expansion(call_id);
|
||||
|
||||
let err = err.or(res.err);
|
||||
ExpandResult {
|
||||
value: {
|
||||
let parse = res.value.0.cast::<T>();
|
||||
|
||||
self.recursion_depth += 1;
|
||||
let old_file_id = std::mem::replace(&mut self.current_file_id, call_id.into());
|
||||
let old_span_map =
|
||||
std::mem::replace(&mut self.span_map, db.span_map(self.current_file_id));
|
||||
let prev_ast_id_map =
|
||||
mem::replace(&mut self.ast_id_map, db.ast_id_map(self.current_file_id));
|
||||
let mark = Mark {
|
||||
file_id: old_file_id,
|
||||
span_map: old_span_map,
|
||||
ast_id_map: prev_ast_id_map,
|
||||
bomb: DropBomb::new("expansion mark dropped"),
|
||||
};
|
||||
Some((mark, parse))
|
||||
},
|
||||
err,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ast_id_map(&self) -> &AstIdMap {
|
||||
&self.ast_id_map
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Mark {
|
||||
file_id: HirFileId,
|
||||
span_map: SpanMap,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
bomb: DropBomb,
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,8 +3,8 @@ use hir_expand::name::Name;
|
|||
use intern::Symbol;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syntax::{
|
||||
ast::{self, HasName, IsString},
|
||||
AstNode, AstPtr, AstToken, T,
|
||||
ast::{self, HasName, IsString},
|
||||
};
|
||||
use tt::TextRange;
|
||||
|
||||
|
|
@ -158,7 +158,12 @@ impl ExprCollector<'_> {
|
|||
AsmOperand::Const(self.collect_expr_opt(c.expr()))
|
||||
}
|
||||
ast::AsmOperand::AsmSym(s) => {
|
||||
let Some(path) = s.path().and_then(|p| self.parse_path(p)) else {
|
||||
let Some(path) = s.path().and_then(|p| {
|
||||
self.lower_path(
|
||||
p,
|
||||
&mut ExprCollector::impl_trait_error_allocator,
|
||||
)
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
AsmOperand::Sym(path)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,289 @@
|
|||
//! Many kinds of items or constructs can have generic parameters: functions,
|
||||
//! structs, impls, traits, etc. This module provides a common HIR for these
|
||||
//! generic parameters. See also the `Generics` type and the `generics_of` query
|
||||
//! in rustc.
|
||||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use intern::sym;
|
||||
use la_arena::Arena;
|
||||
use syntax::ast::{self, HasName, HasTypeBounds};
|
||||
use thin_vec::ThinVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
GenericDefId, TypeOrConstParamId, TypeParamId,
|
||||
expr_store::{TypePtr, lower::ExprCollector},
|
||||
hir::generics::{
|
||||
ConstParamData, GenericParams, LifetimeParamData, TypeOrConstParamData, TypeParamData,
|
||||
TypeParamProvenance, WherePredicate,
|
||||
},
|
||||
type_ref::{LifetimeRef, LifetimeRefId, TypeBound, TypeRef, TypeRefId},
|
||||
};
|
||||
|
||||
pub(crate) type ImplTraitLowerFn<'l> = &'l mut dyn for<'ec, 'db> FnMut(
|
||||
&'ec mut ExprCollector<'db>,
|
||||
TypePtr,
|
||||
ThinVec<TypeBound>,
|
||||
) -> TypeRefId;
|
||||
|
||||
pub(crate) struct GenericParamsCollector {
|
||||
type_or_consts: Arena<TypeOrConstParamData>,
|
||||
lifetimes: Arena<LifetimeParamData>,
|
||||
where_predicates: Vec<WherePredicate>,
|
||||
parent: GenericDefId,
|
||||
}
|
||||
|
||||
impl GenericParamsCollector {
|
||||
pub(crate) fn new(parent: GenericDefId) -> Self {
|
||||
Self {
|
||||
type_or_consts: Default::default(),
|
||||
lifetimes: Default::default(),
|
||||
where_predicates: Default::default(),
|
||||
parent,
|
||||
}
|
||||
}
|
||||
pub(crate) fn with_self_param(
|
||||
ec: &mut ExprCollector<'_>,
|
||||
parent: GenericDefId,
|
||||
bounds: Option<ast::TypeBoundList>,
|
||||
) -> Self {
|
||||
let mut this = Self::new(parent);
|
||||
this.fill_self_param(ec, bounds);
|
||||
this
|
||||
}
|
||||
|
||||
pub(crate) fn lower(
|
||||
&mut self,
|
||||
ec: &mut ExprCollector<'_>,
|
||||
generic_param_list: Option<ast::GenericParamList>,
|
||||
where_clause: Option<ast::WhereClause>,
|
||||
) {
|
||||
if let Some(params) = generic_param_list {
|
||||
self.lower_param_list(ec, params)
|
||||
}
|
||||
if let Some(where_clause) = where_clause {
|
||||
self.lower_where_predicates(ec, where_clause);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn collect_impl_trait<R>(
|
||||
&mut self,
|
||||
ec: &mut ExprCollector<'_>,
|
||||
cb: impl FnOnce(&mut ExprCollector<'_>, ImplTraitLowerFn<'_>) -> R,
|
||||
) -> R {
|
||||
cb(
|
||||
ec,
|
||||
&mut Self::lower_argument_impl_trait(
|
||||
&mut self.type_or_consts,
|
||||
&mut self.where_predicates,
|
||||
self.parent,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn finish(self) -> Arc<GenericParams> {
|
||||
let Self { mut lifetimes, mut type_or_consts, mut where_predicates, parent: _ } = self;
|
||||
|
||||
if lifetimes.is_empty() && type_or_consts.is_empty() && where_predicates.is_empty() {
|
||||
static EMPTY: LazyLock<Arc<GenericParams>> = LazyLock::new(|| {
|
||||
Arc::new(GenericParams {
|
||||
lifetimes: Arena::new(),
|
||||
type_or_consts: Arena::new(),
|
||||
where_predicates: Box::default(),
|
||||
})
|
||||
});
|
||||
return Arc::clone(&EMPTY);
|
||||
}
|
||||
|
||||
lifetimes.shrink_to_fit();
|
||||
type_or_consts.shrink_to_fit();
|
||||
where_predicates.shrink_to_fit();
|
||||
Arc::new(GenericParams {
|
||||
type_or_consts,
|
||||
lifetimes,
|
||||
where_predicates: where_predicates.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_param_list(&mut self, ec: &mut ExprCollector<'_>, params: ast::GenericParamList) {
|
||||
for generic_param in params.generic_params() {
|
||||
let enabled = ec.expander.is_cfg_enabled(ec.db, ec.module.krate(), &generic_param);
|
||||
if !enabled {
|
||||
continue;
|
||||
}
|
||||
|
||||
match generic_param {
|
||||
ast::GenericParam::TypeParam(type_param) => {
|
||||
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
let default = type_param.default_type().map(|it| {
|
||||
ec.lower_type_ref(it, &mut ExprCollector::impl_trait_error_allocator)
|
||||
});
|
||||
let param = TypeParamData {
|
||||
name: Some(name.clone()),
|
||||
default,
|
||||
provenance: TypeParamProvenance::TypeParamList,
|
||||
};
|
||||
let idx = self.type_or_consts.alloc(param.into());
|
||||
let type_ref =
|
||||
TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent: self.parent,
|
||||
local_id: idx,
|
||||
}));
|
||||
let type_ref = ec.alloc_type_ref_desugared(type_ref);
|
||||
self.lower_bounds(ec, type_param.type_bound_list(), Either::Left(type_ref));
|
||||
}
|
||||
ast::GenericParam::ConstParam(const_param) => {
|
||||
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
let ty = ec.lower_type_ref_opt(
|
||||
const_param.ty(),
|
||||
&mut ExprCollector::impl_trait_error_allocator,
|
||||
);
|
||||
let param = ConstParamData {
|
||||
name,
|
||||
ty,
|
||||
default: const_param.default_val().map(|it| ec.lower_const_arg(it)),
|
||||
};
|
||||
let _idx = self.type_or_consts.alloc(param.into());
|
||||
}
|
||||
ast::GenericParam::LifetimeParam(lifetime_param) => {
|
||||
let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime());
|
||||
if let LifetimeRef::Named(name) = &ec.store.lifetimes[lifetime] {
|
||||
let param = LifetimeParamData { name: name.clone() };
|
||||
let _idx = self.lifetimes.alloc(param);
|
||||
self.lower_bounds(
|
||||
ec,
|
||||
lifetime_param.type_bound_list(),
|
||||
Either::Right(lifetime),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_where_predicates(
|
||||
&mut self,
|
||||
ec: &mut ExprCollector<'_>,
|
||||
where_clause: ast::WhereClause,
|
||||
) {
|
||||
for pred in where_clause.predicates() {
|
||||
let target = if let Some(type_ref) = pred.ty() {
|
||||
Either::Left(
|
||||
ec.lower_type_ref(type_ref, &mut ExprCollector::impl_trait_error_allocator),
|
||||
)
|
||||
} else if let Some(lifetime) = pred.lifetime() {
|
||||
Either::Right(ec.lower_lifetime_ref(lifetime))
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
|
||||
// Higher-Ranked Trait Bounds
|
||||
param_list
|
||||
.lifetime_params()
|
||||
.map(|lifetime_param| {
|
||||
lifetime_param
|
||||
.lifetime()
|
||||
.map_or_else(Name::missing, |lt| Name::new_lifetime(<.text()))
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
|
||||
self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_bounds(
|
||||
&mut self,
|
||||
ec: &mut ExprCollector<'_>,
|
||||
type_bounds: Option<ast::TypeBoundList>,
|
||||
target: Either<TypeRefId, LifetimeRefId>,
|
||||
) {
|
||||
for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
|
||||
self.lower_type_bound_as_predicate(ec, bound, None, target);
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_type_bound_as_predicate(
|
||||
&mut self,
|
||||
ec: &mut ExprCollector<'_>,
|
||||
bound: ast::TypeBound,
|
||||
hrtb_lifetimes: Option<&[Name]>,
|
||||
target: Either<TypeRefId, LifetimeRefId>,
|
||||
) {
|
||||
let bound = ec.lower_type_bound(
|
||||
bound,
|
||||
&mut Self::lower_argument_impl_trait(
|
||||
&mut self.type_or_consts,
|
||||
&mut self.where_predicates,
|
||||
self.parent,
|
||||
),
|
||||
);
|
||||
let predicate = match (target, bound) {
|
||||
(_, TypeBound::Error | TypeBound::Use(_)) => return,
|
||||
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
|
||||
Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
|
||||
lifetimes: ThinVec::from_iter(hrtb_lifetimes.iter().cloned()),
|
||||
target: type_ref,
|
||||
bound,
|
||||
},
|
||||
None => WherePredicate::TypeBound { target: type_ref, bound },
|
||||
},
|
||||
(Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
|
||||
WherePredicate::Lifetime { target: lifetime, bound }
|
||||
}
|
||||
(Either::Right(_), TypeBound::ForLifetime(..) | TypeBound::Path(..)) => return,
|
||||
};
|
||||
self.where_predicates.push(predicate);
|
||||
}
|
||||
|
||||
fn lower_argument_impl_trait(
|
||||
type_or_consts: &mut Arena<TypeOrConstParamData>,
|
||||
where_predicates: &mut Vec<WherePredicate>,
|
||||
parent: GenericDefId,
|
||||
) -> impl for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>, TypePtr, ThinVec<TypeBound>) -> TypeRefId
|
||||
{
|
||||
move |ec, ptr, impl_trait_bounds| {
|
||||
let param = TypeParamData {
|
||||
name: None,
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::ArgumentImplTrait,
|
||||
};
|
||||
let param_id = TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent,
|
||||
local_id: type_or_consts.alloc(param.into()),
|
||||
}));
|
||||
let type_ref = ec.alloc_type_ref(param_id, ptr);
|
||||
for bound in impl_trait_bounds {
|
||||
where_predicates
|
||||
.push(WherePredicate::TypeBound { target: type_ref, bound: bound.clone() });
|
||||
}
|
||||
type_ref
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_self_param(&mut self, ec: &mut ExprCollector<'_>, bounds: Option<ast::TypeBoundList>) {
|
||||
let self_ = Name::new_symbol_root(sym::Self_);
|
||||
let idx = self.type_or_consts.alloc(
|
||||
TypeParamData {
|
||||
name: Some(self_.clone()),
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
debug_assert_eq!(idx, GenericParams::SELF_PARAM_ID_IN_SELF);
|
||||
let type_ref = TypeRef::TypeParam(TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent: self.parent,
|
||||
local_id: idx,
|
||||
}));
|
||||
let self_ = ec.alloc_type_ref_desugared(type_ref);
|
||||
if let Some(bounds) = bounds {
|
||||
self.lower_bounds(ec, Some(bounds), Either::Left(self_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,28 @@
|
|||
//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::iter;
|
||||
|
||||
use crate::{lower::LowerCtx, path::NormalPath, type_ref::ConstRef};
|
||||
use crate::expr_store::{
|
||||
lower::{ExprCollector, generics::ImplTraitLowerFn},
|
||||
path::NormalPath,
|
||||
};
|
||||
|
||||
use hir_expand::{
|
||||
mod_path::resolve_crate_root,
|
||||
mod_path::{ModPath, PathKind, resolve_crate_root},
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use intern::{sym, Interned};
|
||||
use stdx::thin_vec::EmptyOptimizedThinVec;
|
||||
use syntax::ast::{self, AstNode, HasGenericArgs, HasTypeBounds};
|
||||
use intern::{Interned, sym};
|
||||
use syntax::{
|
||||
AstPtr,
|
||||
ast::{self, AstNode, HasGenericArgs},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
expr_store::path::{GenericArg, GenericArgs, Path},
|
||||
type_ref::TypeRef,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -27,7 +35,11 @@ thread_local! {
|
|||
/// It correctly handles `$crate` based path from macro call.
|
||||
// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
|
||||
// also needs an update.
|
||||
pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<Path> {
|
||||
pub(super) fn lower_path(
|
||||
collector: &mut ExprCollector<'_>,
|
||||
mut path: ast::Path,
|
||||
impl_trait_lower_fn: ImplTraitLowerFn<'_>,
|
||||
) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut type_anchor = None;
|
||||
let mut segments = Vec::new();
|
||||
|
|
@ -43,9 +55,20 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
segments.push(name);
|
||||
};
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
let Some(segment) = path.segment() else {
|
||||
segments.push(Name::missing());
|
||||
// We can end up here if for `path::`
|
||||
match qualifier(&path) {
|
||||
Some(it) => {
|
||||
path = it;
|
||||
continue;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
};
|
||||
|
||||
if segment.coloncolon_token().is_some() {
|
||||
debug_assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
kind = PathKind::Abs;
|
||||
}
|
||||
|
||||
|
|
@ -57,8 +80,8 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
return None;
|
||||
}
|
||||
break kind = resolve_crate_root(
|
||||
ctx.db.upcast(),
|
||||
ctx.span_map().span_for_range(name_ref.syntax().text_range()).ctx,
|
||||
collector.db,
|
||||
collector.expander.ctx_for_range(name_ref.syntax().text_range()),
|
||||
)
|
||||
.map(PathKind::DollarCrate)
|
||||
.unwrap_or(PathKind::Crate);
|
||||
|
|
@ -66,13 +89,16 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
let name = name_ref.as_name();
|
||||
let args = segment
|
||||
.generic_arg_list()
|
||||
.and_then(|it| lower_generic_args(ctx, it))
|
||||
.and_then(|it| collector.lower_generic_args(it, impl_trait_lower_fn))
|
||||
.or_else(|| {
|
||||
lower_generic_args_from_fn_path(
|
||||
ctx,
|
||||
collector.lower_generic_args_from_fn_path(
|
||||
segment.parenthesized_arg_list(),
|
||||
segment.ret_type(),
|
||||
impl_trait_lower_fn,
|
||||
)
|
||||
})
|
||||
.or_else(|| {
|
||||
segment.return_type_syntax().map(|_| GenericArgs::return_type_notation())
|
||||
});
|
||||
if args.is_some() {
|
||||
generic_args.resize(segments.len(), None);
|
||||
|
|
@ -81,12 +107,12 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
push_segment(&segment, &mut segments, name);
|
||||
}
|
||||
ast::PathSegmentKind::SelfTypeKw => {
|
||||
push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_.clone()));
|
||||
push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_));
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
debug_assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
||||
let self_type = TypeRef::from_ast(ctx, type_ref?);
|
||||
let self_type = collector.lower_type_ref(type_ref?, impl_trait_lower_fn);
|
||||
|
||||
match trait_ref {
|
||||
// <T>::foo
|
||||
|
|
@ -96,7 +122,12 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let path = Path::from_src(ctx, trait_ref.path()?)?;
|
||||
let path = collector.lower_path(trait_ref.path()?, impl_trait_lower_fn)?;
|
||||
// FIXME: Unnecessary clone
|
||||
collector.alloc_type_ref(
|
||||
TypeRef::Path(path.clone()),
|
||||
AstPtr::new(&trait_ref).upcast(),
|
||||
);
|
||||
let mod_path = path.mod_path()?;
|
||||
let path_generic_args = path.generic_args();
|
||||
let num_segments = mod_path.segments().len();
|
||||
|
|
@ -123,10 +154,8 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
args: iter::once(self_type)
|
||||
.chain(it.args.iter().cloned())
|
||||
.collect(),
|
||||
|
||||
has_self_type: true,
|
||||
bindings: it.bindings.clone(),
|
||||
desugared_from_fn: it.desugared_from_fn,
|
||||
..it
|
||||
},
|
||||
None => GenericArgs {
|
||||
args: Box::new([self_type]),
|
||||
|
|
@ -184,10 +213,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
// We follow what it did anyway :)
|
||||
if segments.len() == 1 && kind == PathKind::Plain {
|
||||
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||
let syn_ctxt = ctx.span_map().span_for_range(path.segment()?.syntax().text_range()).ctx;
|
||||
if let Some(macro_call_id) = ctx.db.lookup_intern_syntax_context(syn_ctxt).outer_expn {
|
||||
if ctx.db.lookup_intern_macro_call(macro_call_id).def.local_inner {
|
||||
kind = match resolve_crate_root(ctx.db.upcast(), syn_ctxt) {
|
||||
let syn_ctxt = collector.expander.ctx_for_range(path.segment()?.syntax().text_range());
|
||||
if let Some(macro_call_id) = syn_ctxt.outer_expn(collector.db) {
|
||||
if collector.db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner {
|
||||
kind = match resolve_crate_root(collector.db, syn_ctxt) {
|
||||
Some(crate_root) => PathKind::DollarCrate(crate_root),
|
||||
None => PathKind::Crate,
|
||||
}
|
||||
|
|
@ -207,7 +236,11 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
|
|||
if type_anchor.is_none() && generic_args.is_empty() {
|
||||
return Some(Path::BarePath(mod_path));
|
||||
} else {
|
||||
return Some(Path::Normal(NormalPath::new(type_anchor, mod_path, generic_args)));
|
||||
return Some(Path::Normal(Box::new(NormalPath {
|
||||
type_anchor,
|
||||
mod_path,
|
||||
generic_args: generic_args.into_boxed_slice(),
|
||||
})));
|
||||
}
|
||||
|
||||
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
|
||||
|
|
@ -256,102 +289,3 @@ pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<
|
|||
.nth(segment_idx as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_generic_args(
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
node: ast::GenericArgList,
|
||||
) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
for generic_arg in node.generic_args() {
|
||||
match generic_arg {
|
||||
ast::GenericArg::TypeArg(type_arg) => {
|
||||
let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
|
||||
lower_ctx.update_impl_traits_bounds_from_type_ref(type_ref);
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
|
||||
if assoc_type_arg.param_list().is_some() {
|
||||
// We currently ignore associated return type bounds.
|
||||
continue;
|
||||
}
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
// Nested impl traits like `impl Foo<Assoc = impl Bar>` are allowed
|
||||
lower_ctx.with_outer_impl_trait_scope(false, |lower_ctx| {
|
||||
let name = name_ref.as_name();
|
||||
let args = assoc_type_arg
|
||||
.generic_arg_list()
|
||||
.and_then(|args| lower_generic_args(lower_ctx, args));
|
||||
let type_ref =
|
||||
assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
|
||||
let type_ref = type_ref
|
||||
.inspect(|&tr| lower_ctx.update_impl_traits_bounds_from_type_ref(tr));
|
||||
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
|
||||
l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
|
||||
} else {
|
||||
Box::default()
|
||||
};
|
||||
bindings.push(AssociatedTypeBinding { name, args, type_ref, bounds });
|
||||
});
|
||||
}
|
||||
}
|
||||
ast::GenericArg::LifetimeArg(lifetime_arg) => {
|
||||
if let Some(lifetime) = lifetime_arg.lifetime() {
|
||||
let lifetime_ref = LifetimeRef::new(&lifetime);
|
||||
args.push(GenericArg::Lifetime(lifetime_ref))
|
||||
}
|
||||
}
|
||||
ast::GenericArg::ConstArg(arg) => {
|
||||
let arg = ConstRef::from_const_arg(lower_ctx, Some(arg));
|
||||
args.push(GenericArg::Const(arg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(GenericArgs {
|
||||
args: args.into_boxed_slice(),
|
||||
has_self_type: false,
|
||||
bindings: bindings.into_boxed_slice(),
|
||||
desugared_from_fn: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
|
||||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
fn lower_generic_args_from_fn_path(
|
||||
ctx: &mut LowerCtx<'_>,
|
||||
args: Option<ast::ParenthesizedArgList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
let params = args?;
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.type_args() {
|
||||
let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
let args = Box::new([GenericArg::Type(
|
||||
ctx.alloc_type_ref_desugared(TypeRef::Tuple(EmptyOptimizedThinVec::from_iter(param_types))),
|
||||
)]);
|
||||
let bindings = if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
|
||||
Box::new([AssociatedTypeBinding {
|
||||
name: Name::new_symbol_root(sym::Output.clone()),
|
||||
args: None,
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Box::default(),
|
||||
}])
|
||||
} else {
|
||||
// -> ()
|
||||
let type_ref = ctx.alloc_type_ref_desugared(TypeRef::unit());
|
||||
Box::new([AssociatedTypeBinding {
|
||||
name: Name::new_symbol_root(sym::Output.clone()),
|
||||
args: None,
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Box::default(),
|
||||
}])
|
||||
};
|
||||
Some(GenericArgs { args, has_self_type: false, bindings, desugared_from_fn: true })
|
||||
}
|
||||
|
|
@ -1,26 +1,29 @@
|
|||
use expect_test::{expect, Expect};
|
||||
use expect_test::{Expect, expect};
|
||||
use span::Edition;
|
||||
use syntax::ast::{self, make};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{
|
||||
lower::LowerCtx,
|
||||
path::{
|
||||
lower::{hir_segment_to_ast_segment, SEGMENT_LOWERING_MAP},
|
||||
Path,
|
||||
db::DefDatabase,
|
||||
expr_store::{
|
||||
ExpressionStore,
|
||||
lower::{
|
||||
ExprCollector,
|
||||
path::{SEGMENT_LOWERING_MAP, hir_segment_to_ast_segment},
|
||||
},
|
||||
path::Path,
|
||||
pretty,
|
||||
},
|
||||
pretty,
|
||||
test_db::TestDB,
|
||||
type_ref::{TypesMap, TypesSourceMap},
|
||||
};
|
||||
|
||||
fn lower_path(path: ast::Path) -> (TestDB, TypesMap, Option<Path>) {
|
||||
fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) {
|
||||
let (db, file_id) = TestDB::with_single_file("");
|
||||
let mut types_map = TypesMap::default();
|
||||
let mut types_source_map = TypesSourceMap::default();
|
||||
let mut ctx = LowerCtx::new(&db, file_id.into(), &mut types_map, &mut types_source_map);
|
||||
let lowered_path = ctx.lower_path(path);
|
||||
(db, types_map, lowered_path)
|
||||
let krate = db.fetch_test_crate();
|
||||
let mut ctx = ExprCollector::new(&db, db.crate_def_map(krate).root_module_id(), file_id.into());
|
||||
let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
|
||||
let store = ctx.store.finish();
|
||||
(db, store, lowered_path)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -111,11 +114,9 @@ fn keywords_in_middle_fail_lowering3() {
|
|||
|
||||
#[track_caller]
|
||||
fn check_path_lowering(path: &str, expected: Expect) {
|
||||
let (db, types_map, lowered_path) = lower_path(make::path_from_text(path));
|
||||
let (db, store, lowered_path) = lower_path(make::path_from_text(path));
|
||||
let lowered_path = lowered_path.expect("failed to lower path");
|
||||
let mut buf = String::new();
|
||||
pretty::print_path(&db, &lowered_path, &types_map, &mut buf, Edition::CURRENT)
|
||||
.expect("failed to pretty-print path");
|
||||
let buf = pretty::print_path(&db, &store, &lowered_path, Edition::CURRENT);
|
||||
expected.assert_eq(&buf);
|
||||
}
|
||||
|
||||
|
|
@ -1,54 +1,16 @@
|
|||
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
|
||||
mod lower;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
iter,
|
||||
};
|
||||
use std::iter;
|
||||
|
||||
use crate::{
|
||||
lang_item::LangItemTarget,
|
||||
lower::LowerCtx,
|
||||
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRefId},
|
||||
type_ref::{ConstRef, LifetimeRefId, TypeBound, TypeRefId},
|
||||
};
|
||||
use hir_expand::{
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::Name,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use span::Edition;
|
||||
use stdx::thin_vec::thin_vec_with_header_struct;
|
||||
use syntax::ast;
|
||||
|
||||
pub use hir_expand::mod_path::{path, ModPath, PathKind};
|
||||
|
||||
pub use lower::hir_segment_to_ast_segment;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ImportAlias {
|
||||
/// Unnamed alias, as in `use Foo as _;`
|
||||
Underscore,
|
||||
/// Named alias
|
||||
Alias(Name),
|
||||
}
|
||||
|
||||
impl ImportAlias {
|
||||
pub fn display(&self, edition: Edition) -> impl Display + '_ {
|
||||
ImportAliasDisplay { value: self, edition }
|
||||
}
|
||||
}
|
||||
|
||||
struct ImportAliasDisplay<'a> {
|
||||
value: &'a ImportAlias,
|
||||
edition: Edition,
|
||||
}
|
||||
impl Display for ImportAliasDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.value {
|
||||
ImportAlias::Underscore => f.write_str("_"),
|
||||
ImportAlias::Alias(name) => Display::fmt(&name.display_no_db(self.edition), f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Path {
|
||||
|
|
@ -58,7 +20,7 @@ pub enum Path {
|
|||
/// this is not a problem since many more paths have generics than a type anchor).
|
||||
BarePath(Interned<ModPath>),
|
||||
/// `Path::Normal` will always have either generics or type anchor.
|
||||
Normal(NormalPath),
|
||||
Normal(Box<NormalPath>),
|
||||
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
|
||||
/// links via a normal path since they might be private and not accessible in the usage place.
|
||||
LangItem(LangItemTarget, Option<Name>),
|
||||
|
|
@ -71,12 +33,24 @@ const _: () = {
|
|||
assert!(size_of::<Option<Path>>() == 16);
|
||||
};
|
||||
|
||||
thin_vec_with_header_struct! {
|
||||
pub new(pub(crate)) struct NormalPath, NormalPathHeader {
|
||||
pub generic_args: [Option<GenericArgs>],
|
||||
pub type_anchor: Option<TypeRefId>,
|
||||
pub mod_path: Interned<ModPath>; ref,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct NormalPath {
|
||||
pub generic_args: Box<[Option<GenericArgs>]>,
|
||||
pub type_anchor: Option<TypeRefId>,
|
||||
pub mod_path: Interned<ModPath>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum GenericArgsParentheses {
|
||||
No,
|
||||
/// Bounds of the form `Type::method(..): Send` or `impl Trait<method(..): Send>`,
|
||||
/// aka. Return Type Notation or RTN.
|
||||
ReturnTypeNotation,
|
||||
/// `Fn`-family parenthesized traits, e.g. `impl Fn(u32) -> String`.
|
||||
///
|
||||
/// This is desugared into one generic argument containing a tuple of all arguments,
|
||||
/// and an associated type binding for `Output` for the return type.
|
||||
ParenSugar,
|
||||
}
|
||||
|
||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||
|
|
@ -92,9 +66,8 @@ pub struct GenericArgs {
|
|||
pub has_self_type: bool,
|
||||
/// Associated type bindings like in `Iterator<Item = T>`.
|
||||
pub bindings: Box<[AssociatedTypeBinding]>,
|
||||
/// Whether these generic args were desugared from `Trait(Arg) -> Output`
|
||||
/// parenthesis notation typically used for the `Fn` traits.
|
||||
pub desugared_from_fn: bool,
|
||||
/// Whether these generic args were written with parentheses and how.
|
||||
pub parenthesized: GenericArgsParentheses,
|
||||
}
|
||||
|
||||
/// An associated type binding like in `Iterator<Item = T>`.
|
||||
|
|
@ -118,20 +91,18 @@ pub struct AssociatedTypeBinding {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum GenericArg {
|
||||
Type(TypeRefId),
|
||||
Lifetime(LifetimeRef),
|
||||
Lifetime(LifetimeRefId),
|
||||
Const(ConstRef),
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// It correctly handles `$crate` based path from macro call.
|
||||
pub fn from_src(ctx: &mut LowerCtx<'_>, path: ast::Path) -> Option<Path> {
|
||||
lower::lower_path(ctx, path)
|
||||
}
|
||||
|
||||
/// Converts a known mod path to `Path`.
|
||||
pub fn from_known_path(path: ModPath, generic_args: Vec<Option<GenericArgs>>) -> Path {
|
||||
Path::Normal(NormalPath::new(None, Interned::new(path), generic_args))
|
||||
Path::Normal(Box::new(NormalPath {
|
||||
generic_args: generic_args.into_boxed_slice(),
|
||||
type_anchor: None,
|
||||
mod_path: Interned::new(path),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Converts a known mod path to `Path`.
|
||||
|
|
@ -143,7 +114,7 @@ impl Path {
|
|||
pub fn kind(&self) -> &PathKind {
|
||||
match self {
|
||||
Path::BarePath(mod_path) => &mod_path.kind,
|
||||
Path::Normal(path) => &path.mod_path().kind,
|
||||
Path::Normal(path) => &path.mod_path.kind,
|
||||
Path::LangItem(..) => &PathKind::Abs,
|
||||
}
|
||||
}
|
||||
|
|
@ -151,7 +122,7 @@ impl Path {
|
|||
#[inline]
|
||||
pub fn type_anchor(&self) -> Option<TypeRefId> {
|
||||
match self {
|
||||
Path::Normal(path) => path.type_anchor(),
|
||||
Path::Normal(path) => path.type_anchor,
|
||||
Path::LangItem(..) | Path::BarePath(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +130,7 @@ impl Path {
|
|||
#[inline]
|
||||
pub fn generic_args(&self) -> Option<&[Option<GenericArgs>]> {
|
||||
match self {
|
||||
Path::Normal(path) => Some(path.generic_args()),
|
||||
Path::Normal(path) => Some(&path.generic_args),
|
||||
Path::LangItem(..) | Path::BarePath(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -170,8 +141,8 @@ impl Path {
|
|||
PathSegments { segments: mod_path.segments(), generic_args: None }
|
||||
}
|
||||
Path::Normal(path) => PathSegments {
|
||||
segments: path.mod_path().segments(),
|
||||
generic_args: Some(path.generic_args()),
|
||||
segments: path.mod_path.segments(),
|
||||
generic_args: Some(&path.generic_args),
|
||||
},
|
||||
Path::LangItem(_, seg) => PathSegments { segments: seg.as_slice(), generic_args: None },
|
||||
}
|
||||
|
|
@ -180,7 +151,7 @@ impl Path {
|
|||
pub fn mod_path(&self) -> Option<&ModPath> {
|
||||
match self {
|
||||
Path::BarePath(mod_path) => Some(mod_path),
|
||||
Path::Normal(path) => Some(path.mod_path()),
|
||||
Path::Normal(path) => Some(&path.mod_path),
|
||||
Path::LangItem(..) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -197,12 +168,12 @@ impl Path {
|
|||
))))
|
||||
}
|
||||
Path::Normal(path) => {
|
||||
let mod_path = path.mod_path();
|
||||
let mod_path = &path.mod_path;
|
||||
if mod_path.is_ident() {
|
||||
return None;
|
||||
}
|
||||
let type_anchor = path.type_anchor();
|
||||
let generic_args = path.generic_args();
|
||||
let type_anchor = path.type_anchor;
|
||||
let generic_args = &path.generic_args;
|
||||
let qualifier_mod_path = Interned::new(ModPath::from_segments(
|
||||
mod_path.kind,
|
||||
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
|
||||
|
|
@ -211,11 +182,11 @@ impl Path {
|
|||
if type_anchor.is_none() && qualifier_generic_args.iter().all(|it| it.is_none()) {
|
||||
Some(Path::BarePath(qualifier_mod_path))
|
||||
} else {
|
||||
Some(Path::Normal(NormalPath::new(
|
||||
Some(Path::Normal(Box::new(NormalPath {
|
||||
type_anchor,
|
||||
qualifier_mod_path,
|
||||
qualifier_generic_args.iter().cloned(),
|
||||
)))
|
||||
mod_path: qualifier_mod_path,
|
||||
generic_args: qualifier_generic_args.iter().cloned().collect(),
|
||||
})))
|
||||
}
|
||||
}
|
||||
Path::LangItem(..) => None,
|
||||
|
|
@ -226,9 +197,9 @@ impl Path {
|
|||
match self {
|
||||
Path::BarePath(mod_path) => mod_path.is_Self(),
|
||||
Path::Normal(path) => {
|
||||
path.type_anchor().is_none()
|
||||
&& path.mod_path().is_Self()
|
||||
&& path.generic_args().iter().all(|args| args.is_none())
|
||||
path.type_anchor.is_none()
|
||||
&& path.mod_path.is_Self()
|
||||
&& path.generic_args.iter().all(|args| args.is_none())
|
||||
}
|
||||
Path::LangItem(..) => false,
|
||||
}
|
||||
|
|
@ -314,19 +285,21 @@ impl<'a> PathSegments<'a> {
|
|||
}
|
||||
|
||||
impl GenericArgs {
|
||||
pub(crate) fn from_ast(
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
node: ast::GenericArgList,
|
||||
) -> Option<GenericArgs> {
|
||||
lower::lower_generic_args(lower_ctx, node)
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> GenericArgs {
|
||||
GenericArgs {
|
||||
args: Box::default(),
|
||||
has_self_type: false,
|
||||
bindings: Box::default(),
|
||||
desugared_from_fn: false,
|
||||
parenthesized: GenericArgsParentheses::No,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn return_type_notation() -> GenericArgs {
|
||||
GenericArgs {
|
||||
args: Box::default(),
|
||||
has_self_type: false,
|
||||
bindings: Box::default(),
|
||||
parenthesized: GenericArgsParentheses::ReturnTypeNotation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +1,83 @@
|
|||
//! A pretty-printer for HIR.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt::{self, Write};
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
mem,
|
||||
};
|
||||
|
||||
use hir_expand::{Lookup, mod_path::PathKind};
|
||||
use itertools::Itertools;
|
||||
use span::Edition;
|
||||
|
||||
use crate::{
|
||||
hir::{Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement},
|
||||
pretty::{print_generic_args, print_path, print_type_ref},
|
||||
AdtId, DefWithBodyId, GenericDefId, ItemTreeLoc, TypeParamId, VariantId,
|
||||
expr_store::path::{GenericArg, GenericArgs},
|
||||
hir::{
|
||||
Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
|
||||
generics::{GenericParams, WherePredicate},
|
||||
},
|
||||
lang_item::LangItemTarget,
|
||||
signatures::{FnFlags, FunctionSignature, StructSignature},
|
||||
type_ref::{ConstRef, LifetimeRef, Mutability, TraitBoundModifier, TypeBound, UseArgRef},
|
||||
};
|
||||
use crate::{LifetimeParamId, signatures::StructFlags};
|
||||
use crate::{item_tree::FieldsShape, signatures::FieldData};
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! w {
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = write!($dst, $($arg)*); }
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! wln {
|
||||
($dst:expr) => {
|
||||
{ $dst.newline(); }
|
||||
};
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = w!($dst, $($arg)*); $dst.newline(); }
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(super) enum LineFormat {
|
||||
pub enum LineFormat {
|
||||
Oneline,
|
||||
Newline,
|
||||
Indentation,
|
||||
}
|
||||
|
||||
pub(super) fn print_body_hir(
|
||||
pub fn print_body_hir(
|
||||
db: &dyn DefDatabase,
|
||||
body: &Body,
|
||||
owner: DefWithBodyId,
|
||||
edition: Edition,
|
||||
) -> String {
|
||||
let header = match owner {
|
||||
DefWithBodyId::FunctionId(it) => it
|
||||
.lookup(db)
|
||||
.id
|
||||
.resolved(db, |it| format!("fn {}", it.name.display(db.upcast(), edition))),
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
it.lookup(db).id.resolved(db, |it| format!("fn {}", it.name.display(db, edition)))
|
||||
}
|
||||
DefWithBodyId::StaticId(it) => it
|
||||
.lookup(db)
|
||||
.id
|
||||
.resolved(db, |it| format!("static {} = ", it.name.display(db.upcast(), edition))),
|
||||
.resolved(db, |it| format!("static {} = ", it.name.display(db, edition))),
|
||||
DefWithBodyId::ConstId(it) => it.lookup(db).id.resolved(db, |it| {
|
||||
format!(
|
||||
"const {} = ",
|
||||
match &it.name {
|
||||
Some(name) => name.display(db.upcast(), edition).to_string(),
|
||||
Some(name) => name.display(db, edition).to_string(),
|
||||
None => "_".to_owned(),
|
||||
}
|
||||
)
|
||||
}),
|
||||
DefWithBodyId::InTypeConstId(_) => "In type const = ".to_owned(),
|
||||
DefWithBodyId::VariantId(it) => {
|
||||
let loc = it.lookup(db);
|
||||
let enum_loc = loc.parent.lookup(db);
|
||||
format!(
|
||||
"enum {}::{}",
|
||||
enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast(), edition),
|
||||
loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
|
||||
enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition),
|
||||
loc.id.item_tree(db)[loc.id.value].name.display(db, edition),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
@ -63,32 +90,21 @@ pub(super) fn print_body_hir(
|
|||
line_format: LineFormat::Newline,
|
||||
edition,
|
||||
};
|
||||
if let DefWithBodyId::FunctionId(it) = owner {
|
||||
if let DefWithBodyId::FunctionId(_) = owner {
|
||||
p.buf.push('(');
|
||||
let function_data = db.function_data(it);
|
||||
let (mut params, ret_type) = (function_data.params.iter(), &function_data.ret_type);
|
||||
if let Some(self_param) = body.self_param {
|
||||
p.print_binding(self_param);
|
||||
p.buf.push_str(": ");
|
||||
if let Some(ty) = params.next() {
|
||||
p.print_type_ref(*ty, &function_data.types_map);
|
||||
p.buf.push_str(", ");
|
||||
}
|
||||
p.buf.push_str(", ");
|
||||
}
|
||||
body.params.iter().zip(params).for_each(|(¶m, ty)| {
|
||||
p.print_pat(param);
|
||||
p.buf.push_str(": ");
|
||||
p.print_type_ref(*ty, &function_data.types_map);
|
||||
body.params.iter().for_each(|param| {
|
||||
p.print_pat(*param);
|
||||
p.buf.push_str(", ");
|
||||
});
|
||||
// remove the last ", " in param list
|
||||
if body.params.len() > 0 {
|
||||
if !body.params.is_empty() {
|
||||
p.buf.truncate(p.buf.len() - 2);
|
||||
}
|
||||
p.buf.push(')');
|
||||
// return type
|
||||
p.buf.push_str(" -> ");
|
||||
p.print_type_ref(*ret_type, &function_data.types_map);
|
||||
p.buf.push(' ');
|
||||
}
|
||||
p.print_expr(body.body_expr);
|
||||
|
|
@ -98,7 +114,298 @@ pub(super) fn print_body_hir(
|
|||
p.buf
|
||||
}
|
||||
|
||||
pub(super) fn print_expr_hir(
|
||||
pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: Edition) -> String {
|
||||
let header = match owner {
|
||||
VariantId::StructId(it) => {
|
||||
it.lookup(db).id.resolved(db, |it| format!("struct {}", it.name.display(db, edition)))
|
||||
}
|
||||
VariantId::EnumVariantId(enum_variant_id) => {
|
||||
let loc = enum_variant_id.lookup(db);
|
||||
let enum_loc = loc.parent.lookup(db);
|
||||
format!(
|
||||
"enum {}::{}",
|
||||
enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition),
|
||||
loc.id.item_tree(db)[loc.id.value].name.display(db, edition),
|
||||
)
|
||||
}
|
||||
VariantId::UnionId(union_id) => union_id
|
||||
.lookup(db)
|
||||
.id
|
||||
.resolved(db, |it| format!("union {}", it.name.display(db, edition))),
|
||||
};
|
||||
|
||||
let fields = db.variant_fields(owner);
|
||||
|
||||
let mut p = Printer {
|
||||
db,
|
||||
store: &fields.store,
|
||||
buf: header,
|
||||
indent_level: 0,
|
||||
line_format: LineFormat::Newline,
|
||||
edition,
|
||||
};
|
||||
match fields.shape {
|
||||
FieldsShape::Record => wln!(p, " {{"),
|
||||
FieldsShape::Tuple => wln!(p, "("),
|
||||
FieldsShape::Unit => (),
|
||||
}
|
||||
|
||||
for (_, data) in fields.fields().iter() {
|
||||
let FieldData { name, type_ref, visibility, is_unsafe } = data;
|
||||
match visibility {
|
||||
crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => {
|
||||
w!(p, "{}", interned.display(db, p.edition))
|
||||
}
|
||||
crate::item_tree::RawVisibility::Public => w!(p, "pub "),
|
||||
}
|
||||
if *is_unsafe {
|
||||
w!(p, "unsafe ");
|
||||
}
|
||||
w!(p, "{}: ", name.display(db, p.edition));
|
||||
p.print_type_ref(*type_ref);
|
||||
}
|
||||
|
||||
match fields.shape {
|
||||
FieldsShape::Record => wln!(p, "}}"),
|
||||
FieldsShape::Tuple => wln!(p, ");"),
|
||||
FieldsShape::Unit => wln!(p, ";"),
|
||||
}
|
||||
p.buf
|
||||
}
|
||||
|
||||
pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Edition) -> String {
|
||||
match owner {
|
||||
GenericDefId::AdtId(id) => match id {
|
||||
AdtId::StructId(id) => {
|
||||
let signature = db.struct_signature(id);
|
||||
print_struct(db, &signature, edition)
|
||||
}
|
||||
AdtId::UnionId(id) => {
|
||||
format!("unimplemented {id:?}")
|
||||
}
|
||||
AdtId::EnumId(id) => {
|
||||
format!("unimplemented {id:?}")
|
||||
}
|
||||
},
|
||||
GenericDefId::ConstId(id) => format!("unimplemented {id:?}"),
|
||||
GenericDefId::FunctionId(id) => {
|
||||
let signature = db.function_signature(id);
|
||||
print_function(db, &signature, edition)
|
||||
}
|
||||
GenericDefId::ImplId(id) => format!("unimplemented {id:?}"),
|
||||
GenericDefId::StaticId(id) => format!("unimplemented {id:?}"),
|
||||
GenericDefId::TraitAliasId(id) => format!("unimplemented {id:?}"),
|
||||
GenericDefId::TraitId(id) => format!("unimplemented {id:?}"),
|
||||
GenericDefId::TypeAliasId(id) => format!("unimplemented {id:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_path(
|
||||
db: &dyn DefDatabase,
|
||||
store: &ExpressionStore,
|
||||
path: &Path,
|
||||
edition: Edition,
|
||||
) -> String {
|
||||
let mut p = Printer {
|
||||
db,
|
||||
store,
|
||||
buf: String::new(),
|
||||
indent_level: 0,
|
||||
line_format: LineFormat::Newline,
|
||||
edition,
|
||||
};
|
||||
p.print_path(path);
|
||||
p.buf
|
||||
}
|
||||
|
||||
pub fn print_struct(
|
||||
db: &dyn DefDatabase,
|
||||
StructSignature { name, generic_params, store, flags, shape, repr }: &StructSignature,
|
||||
edition: Edition,
|
||||
) -> String {
|
||||
let mut p = Printer {
|
||||
db,
|
||||
store,
|
||||
buf: String::new(),
|
||||
indent_level: 0,
|
||||
line_format: LineFormat::Newline,
|
||||
edition,
|
||||
};
|
||||
if let Some(repr) = repr {
|
||||
if repr.c() {
|
||||
wln!(p, "#[repr(C)]");
|
||||
}
|
||||
if let Some(align) = repr.align {
|
||||
wln!(p, "#[repr(align({}))]", align.bytes());
|
||||
}
|
||||
if let Some(pack) = repr.pack {
|
||||
wln!(p, "#[repr(pack({}))]", pack.bytes());
|
||||
}
|
||||
}
|
||||
if flags.contains(StructFlags::FUNDAMENTAL) {
|
||||
wln!(p, "#[fundamental]");
|
||||
}
|
||||
w!(p, "struct ");
|
||||
w!(p, "{}", name.display(db, edition));
|
||||
print_generic_params(db, generic_params, &mut p);
|
||||
match shape {
|
||||
FieldsShape::Record => wln!(p, " {{...}}"),
|
||||
FieldsShape::Tuple => wln!(p, "(...)"),
|
||||
FieldsShape::Unit => (),
|
||||
}
|
||||
|
||||
print_where_clauses(db, generic_params, &mut p);
|
||||
|
||||
match shape {
|
||||
FieldsShape::Record => wln!(p),
|
||||
FieldsShape::Tuple => wln!(p, ";"),
|
||||
FieldsShape::Unit => wln!(p, ";"),
|
||||
}
|
||||
|
||||
p.buf
|
||||
}
|
||||
|
||||
pub fn print_function(
|
||||
db: &dyn DefDatabase,
|
||||
FunctionSignature {
|
||||
name,
|
||||
generic_params,
|
||||
store,
|
||||
params,
|
||||
ret_type,
|
||||
abi,
|
||||
flags,
|
||||
legacy_const_generics_indices,
|
||||
}: &FunctionSignature,
|
||||
edition: Edition,
|
||||
) -> String {
|
||||
let mut p = Printer {
|
||||
db,
|
||||
store,
|
||||
buf: String::new(),
|
||||
indent_level: 0,
|
||||
line_format: LineFormat::Newline,
|
||||
edition,
|
||||
};
|
||||
if flags.contains(FnFlags::CONST) {
|
||||
w!(p, "const ");
|
||||
}
|
||||
if flags.contains(FnFlags::ASYNC) {
|
||||
w!(p, "async ");
|
||||
}
|
||||
if flags.contains(FnFlags::UNSAFE) {
|
||||
w!(p, "unsafe ");
|
||||
}
|
||||
if flags.contains(FnFlags::EXPLICIT_SAFE) {
|
||||
w!(p, "safe ");
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
w!(p, "extern \"{}\" ", abi.as_str());
|
||||
}
|
||||
w!(p, "fn ");
|
||||
w!(p, "{}", name.display(db, edition));
|
||||
print_generic_params(db, generic_params, &mut p);
|
||||
w!(p, "(");
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(p, ", ");
|
||||
}
|
||||
if legacy_const_generics_indices.as_ref().is_some_and(|idx| idx.contains(&(i as u32))) {
|
||||
w!(p, "const: ");
|
||||
}
|
||||
p.print_type_ref(*param);
|
||||
}
|
||||
w!(p, ")");
|
||||
if let Some(ret_type) = ret_type {
|
||||
w!(p, " -> ");
|
||||
p.print_type_ref(*ret_type);
|
||||
}
|
||||
|
||||
print_where_clauses(db, generic_params, &mut p);
|
||||
wln!(p, " {{...}}");
|
||||
|
||||
p.buf
|
||||
}
|
||||
|
||||
fn print_where_clauses(db: &dyn DefDatabase, generic_params: &GenericParams, p: &mut Printer<'_>) {
|
||||
if !generic_params.where_predicates.is_empty() {
|
||||
w!(p, "\nwhere\n");
|
||||
p.indented(|p| {
|
||||
for (i, pred) in generic_params.where_predicates.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(p, ",\n");
|
||||
}
|
||||
match pred {
|
||||
WherePredicate::TypeBound { target, bound } => {
|
||||
p.print_type_ref(*target);
|
||||
w!(p, ": ");
|
||||
p.print_type_bounds(std::slice::from_ref(bound));
|
||||
}
|
||||
WherePredicate::Lifetime { target, bound } => {
|
||||
p.print_lifetime_ref(*target);
|
||||
w!(p, ": ");
|
||||
p.print_lifetime_ref(*bound);
|
||||
}
|
||||
WherePredicate::ForLifetime { lifetimes, target, bound } => {
|
||||
w!(p, "for<");
|
||||
for (i, lifetime) in lifetimes.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(p, ", ");
|
||||
}
|
||||
w!(p, "{}", lifetime.display(db, p.edition));
|
||||
}
|
||||
w!(p, "> ");
|
||||
p.print_type_ref(*target);
|
||||
w!(p, ": ");
|
||||
p.print_type_bounds(std::slice::from_ref(bound));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
wln!(p);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_generic_params(db: &dyn DefDatabase, generic_params: &GenericParams, p: &mut Printer<'_>) {
|
||||
if !generic_params.is_empty() {
|
||||
w!(p, "<");
|
||||
let mut first = true;
|
||||
for (_i, param) in generic_params.iter_lt() {
|
||||
if !first {
|
||||
w!(p, ", ");
|
||||
}
|
||||
first = false;
|
||||
w!(p, "{}", param.name.display(db, p.edition));
|
||||
}
|
||||
for (i, param) in generic_params.iter_type_or_consts() {
|
||||
if !first {
|
||||
w!(p, ", ");
|
||||
}
|
||||
first = false;
|
||||
if let Some(const_param) = param.const_param() {
|
||||
w!(p, "const {}: ", const_param.name.display(db, p.edition));
|
||||
p.print_type_ref(const_param.ty);
|
||||
if let Some(default) = const_param.default {
|
||||
w!(p, " = ");
|
||||
p.print_expr(default.expr);
|
||||
}
|
||||
}
|
||||
if let Some(type_param) = param.type_param() {
|
||||
match &type_param.name {
|
||||
Some(name) => w!(p, "{}", name.display(db, p.edition)),
|
||||
None => w!(p, "Param[{}]", i.into_raw()),
|
||||
}
|
||||
if let Some(default) = type_param.default {
|
||||
w!(p, " = ");
|
||||
p.print_type_ref(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
w!(p, ">");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_expr_hir(
|
||||
db: &dyn DefDatabase,
|
||||
store: &ExpressionStore,
|
||||
_owner: DefWithBodyId,
|
||||
|
|
@ -117,7 +424,7 @@ pub(super) fn print_expr_hir(
|
|||
p.buf
|
||||
}
|
||||
|
||||
pub(super) fn print_pat_hir(
|
||||
pub fn print_pat_hir(
|
||||
db: &dyn DefDatabase,
|
||||
store: &ExpressionStore,
|
||||
_owner: DefWithBodyId,
|
||||
|
|
@ -137,21 +444,6 @@ pub(super) fn print_pat_hir(
|
|||
p.buf
|
||||
}
|
||||
|
||||
macro_rules! w {
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = write!($dst, $($arg)*); }
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! wln {
|
||||
($dst:expr) => {
|
||||
{ $dst.newline(); }
|
||||
};
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = w!($dst, $($arg)*); $dst.newline(); }
|
||||
};
|
||||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
store: &'a ExpressionStore,
|
||||
|
|
@ -238,7 +530,7 @@ impl Printer<'_> {
|
|||
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
|
||||
Expr::OffsetOf(offset_of) => {
|
||||
w!(self, "builtin#offset_of(");
|
||||
self.print_type_ref(offset_of.container, &self.store.types);
|
||||
self.print_type_ref(offset_of.container);
|
||||
let edition = self.edition;
|
||||
w!(
|
||||
self,
|
||||
|
|
@ -246,7 +538,7 @@ impl Printer<'_> {
|
|||
offset_of
|
||||
.fields
|
||||
.iter()
|
||||
.format_with(".", |field, f| f(&field.display(self.db.upcast(), edition)))
|
||||
.format_with(".", |field, f| f(&field.display(self.db, edition)))
|
||||
);
|
||||
}
|
||||
Expr::Path(path) => self.print_path(path),
|
||||
|
|
@ -268,7 +560,7 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::Loop { body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition));
|
||||
w!(self, "{}: ", self.store[*lbl].name.display(self.db, self.edition));
|
||||
}
|
||||
w!(self, "loop ");
|
||||
self.print_expr(*body);
|
||||
|
|
@ -288,11 +580,10 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::MethodCall { receiver, method_name, args, generic_args } => {
|
||||
self.print_expr(*receiver);
|
||||
w!(self, ".{}", method_name.display(self.db.upcast(), self.edition));
|
||||
w!(self, ".{}", method_name.display(self.db, self.edition));
|
||||
if let Some(args) = generic_args {
|
||||
w!(self, "::<");
|
||||
let edition = self.edition;
|
||||
print_generic_args(self.db, args, &self.store.types, self, edition).unwrap();
|
||||
self.print_generic_args(args);
|
||||
w!(self, ">");
|
||||
}
|
||||
w!(self, "(");
|
||||
|
|
@ -327,13 +618,13 @@ impl Printer<'_> {
|
|||
Expr::Continue { label } => {
|
||||
w!(self, "continue");
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
|
||||
w!(self, " {}", self.store[*lbl].name.display(self.db, self.edition));
|
||||
}
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
w!(self, "break");
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition));
|
||||
w!(self, " {}", self.store[*lbl].name.display(self.db, self.edition));
|
||||
}
|
||||
if let Some(expr) = expr {
|
||||
self.whitespace();
|
||||
|
|
@ -378,7 +669,7 @@ impl Printer<'_> {
|
|||
let edition = self.edition;
|
||||
self.indented(|p| {
|
||||
for field in &**fields {
|
||||
w!(p, "{}: ", field.name.display(self.db.upcast(), edition));
|
||||
w!(p, "{}: ", field.name.display(self.db, edition));
|
||||
p.print_expr(field.expr);
|
||||
wln!(p, ",");
|
||||
}
|
||||
|
|
@ -392,7 +683,7 @@ impl Printer<'_> {
|
|||
}
|
||||
Expr::Field { expr, name } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ".{}", name.display(self.db.upcast(), self.edition));
|
||||
w!(self, ".{}", name.display(self.db, self.edition));
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
self.print_expr(*expr);
|
||||
|
|
@ -401,7 +692,7 @@ impl Printer<'_> {
|
|||
Expr::Cast { expr, type_ref } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, " as ");
|
||||
self.print_type_ref(*type_ref, &self.store.types);
|
||||
self.print_type_ref(*type_ref);
|
||||
}
|
||||
Expr::Ref { expr, rawness, mutability } => {
|
||||
w!(self, "&");
|
||||
|
|
@ -489,13 +780,13 @@ impl Printer<'_> {
|
|||
self.print_pat(*pat);
|
||||
if let Some(ty) = ty {
|
||||
w!(self, ": ");
|
||||
self.print_type_ref(*ty, &self.store.types);
|
||||
self.print_type_ref(*ty);
|
||||
}
|
||||
}
|
||||
w!(self, "|");
|
||||
if let Some(ret_ty) = ret_type {
|
||||
w!(self, " -> ");
|
||||
self.print_type_ref(*ret_ty, &self.store.types);
|
||||
self.print_type_ref(*ret_ty);
|
||||
}
|
||||
self.whitespace();
|
||||
self.print_expr(*body);
|
||||
|
|
@ -531,7 +822,7 @@ impl Printer<'_> {
|
|||
Expr::Literal(lit) => self.print_literal(lit),
|
||||
Expr::Block { id: _, statements, tail, label } => {
|
||||
let label = label.map(|lbl| {
|
||||
format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition))
|
||||
format!("{}: ", self.store[lbl].name.display(self.db, self.edition))
|
||||
});
|
||||
self.print_block(label.as_deref(), statements, tail);
|
||||
}
|
||||
|
|
@ -617,7 +908,7 @@ impl Printer<'_> {
|
|||
let oneline = matches!(self.line_format, LineFormat::Oneline);
|
||||
self.indented(|p| {
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
let field_name = arg.name.display(self.db.upcast(), edition).to_string();
|
||||
let field_name = arg.name.display(self.db, edition).to_string();
|
||||
|
||||
let mut same_name = false;
|
||||
if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
|
||||
|
|
@ -731,7 +1022,7 @@ impl Printer<'_> {
|
|||
self.print_pat(*pat);
|
||||
if let Some(ty) = type_ref {
|
||||
w!(self, ": ");
|
||||
self.print_type_ref(*ty, &self.store.types);
|
||||
self.print_type_ref(*ty);
|
||||
}
|
||||
if let Some(init) = initializer {
|
||||
w!(self, " = ");
|
||||
|
|
@ -782,16 +1073,6 @@ impl Printer<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_type_ref(&mut self, ty: TypeRefId, map: &TypesMap) {
|
||||
let edition = self.edition;
|
||||
print_type_ref(self.db, ty, map, self, edition).unwrap();
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path) {
|
||||
let edition = self.edition;
|
||||
print_path(self.db, path, &self.store.types, self, edition).unwrap();
|
||||
}
|
||||
|
||||
fn print_binding(&mut self, id: BindingId) {
|
||||
let Binding { name, mode, .. } = &self.store.bindings[id];
|
||||
let mode = match mode {
|
||||
|
|
@ -800,6 +1081,288 @@ impl Printer<'_> {
|
|||
BindingAnnotation::Ref => "ref ",
|
||||
BindingAnnotation::RefMut => "ref mut ",
|
||||
};
|
||||
w!(self, "{}{}", mode, name.display(self.db.upcast(), self.edition));
|
||||
w!(self, "{}{}", mode, name.display(self.db, self.edition));
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path) {
|
||||
if let Path::LangItem(it, s) = path {
|
||||
w!(self, "builtin#lang(");
|
||||
macro_rules! write_name {
|
||||
($it:ident) => {{
|
||||
let loc = $it.lookup(self.db);
|
||||
let tree = loc.item_tree_id().item_tree(self.db);
|
||||
let name = &tree[loc.id.value].name;
|
||||
w!(self, "{}", name.display(self.db, self.edition));
|
||||
}};
|
||||
}
|
||||
match *it {
|
||||
LangItemTarget::ImplDef(it) => w!(self, "{it:?}"),
|
||||
LangItemTarget::EnumId(it) => write_name!(it),
|
||||
LangItemTarget::Function(it) => write_name!(it),
|
||||
LangItemTarget::Static(it) => write_name!(it),
|
||||
LangItemTarget::Struct(it) => write_name!(it),
|
||||
LangItemTarget::Union(it) => write_name!(it),
|
||||
LangItemTarget::TypeAlias(it) => write_name!(it),
|
||||
LangItemTarget::Trait(it) => write_name!(it),
|
||||
LangItemTarget::EnumVariant(it) => write_name!(it),
|
||||
}
|
||||
|
||||
if let Some(s) = s {
|
||||
w!(self, "::{}", s.display(self.db, self.edition));
|
||||
}
|
||||
return w!(self, ")");
|
||||
}
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
w!(self, "<");
|
||||
self.print_type_ref(anchor);
|
||||
w!(self, ">::");
|
||||
}
|
||||
None => match path.kind() {
|
||||
PathKind::Plain => {}
|
||||
&PathKind::SELF => w!(self, "self"),
|
||||
PathKind::Super(n) => {
|
||||
for i in 0..*n {
|
||||
if i == 0 {
|
||||
w!(self, "super");
|
||||
} else {
|
||||
w!(self, "::super");
|
||||
}
|
||||
}
|
||||
}
|
||||
PathKind::Crate => w!(self, "crate"),
|
||||
PathKind::Abs => {}
|
||||
PathKind::DollarCrate(krate) => w!(
|
||||
self,
|
||||
"{}",
|
||||
krate
|
||||
.extra_data(self.db)
|
||||
.display_name
|
||||
.as_ref()
|
||||
.map(|it| it.crate_name().symbol().as_str())
|
||||
.unwrap_or("$crate")
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for (i, segment) in path.segments().iter().enumerate() {
|
||||
if i != 0 || !matches!(path.kind(), PathKind::Plain) {
|
||||
w!(self, "::");
|
||||
}
|
||||
|
||||
w!(self, "{}", segment.name.display(self.db, self.edition));
|
||||
if let Some(generics) = segment.args_and_bindings {
|
||||
w!(self, "::<");
|
||||
self.print_generic_args(generics);
|
||||
|
||||
w!(self, ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_args(&mut self, generics: &GenericArgs) {
|
||||
let mut first = true;
|
||||
let args = if generics.has_self_type {
|
||||
let (self_ty, args) = generics.args.split_first().unwrap();
|
||||
w!(self, "Self=");
|
||||
self.print_generic_arg(self_ty);
|
||||
first = false;
|
||||
args
|
||||
} else {
|
||||
&generics.args
|
||||
};
|
||||
for arg in args {
|
||||
if !first {
|
||||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
self.print_generic_arg(arg);
|
||||
}
|
||||
for binding in generics.bindings.iter() {
|
||||
if !first {
|
||||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
w!(self, "{}", binding.name.display(self.db, self.edition));
|
||||
if !binding.bounds.is_empty() {
|
||||
w!(self, ": ");
|
||||
self.print_type_bounds(&binding.bounds);
|
||||
}
|
||||
if let Some(ty) = binding.type_ref {
|
||||
w!(self, " = ");
|
||||
self.print_type_ref(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_arg(&mut self, arg: &GenericArg) {
|
||||
match arg {
|
||||
GenericArg::Type(ty) => self.print_type_ref(*ty),
|
||||
GenericArg::Const(ConstRef { expr }) => self.print_expr(*expr),
|
||||
GenericArg::Lifetime(lt) => self.print_lifetime_ref(*lt),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_param(&mut self, param: TypeParamId) {
|
||||
let generic_params = self.db.generic_params(param.parent());
|
||||
|
||||
match generic_params[param.local_id()].name() {
|
||||
Some(name) => w!(self, "{}", name.display(self.db, self.edition)),
|
||||
None => w!(self, "Param[{}]", param.local_id().into_raw()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_lifetime_param(&mut self, param: LifetimeParamId) {
|
||||
let generic_params = self.db.generic_params(param.parent);
|
||||
w!(self, "{}", generic_params[param.local_id].name.display(self.db, self.edition))
|
||||
}
|
||||
|
||||
pub(crate) fn print_lifetime_ref(&mut self, lt_ref: LifetimeRefId) {
|
||||
match &self.store[lt_ref] {
|
||||
LifetimeRef::Static => w!(self, "'static"),
|
||||
LifetimeRef::Named(lt) => {
|
||||
w!(self, "{}", lt.display(self.db, self.edition))
|
||||
}
|
||||
LifetimeRef::Placeholder => w!(self, "'_"),
|
||||
LifetimeRef::Error => w!(self, "'{{error}}"),
|
||||
&LifetimeRef::Param(p) => self.print_lifetime_param(p),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_ref(&mut self, type_ref: TypeRefId) {
|
||||
// FIXME: deduplicate with `HirDisplay` impl
|
||||
match &self.store[type_ref] {
|
||||
TypeRef::Never => w!(self, "!"),
|
||||
&TypeRef::TypeParam(p) => self.print_type_param(p),
|
||||
TypeRef::Placeholder => w!(self, "_"),
|
||||
TypeRef::Tuple(fields) => {
|
||||
w!(self, "(");
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
self.print_type_ref(*field);
|
||||
}
|
||||
w!(self, ")");
|
||||
}
|
||||
TypeRef::Path(path) => self.print_path(path),
|
||||
TypeRef::RawPtr(pointee, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "*const",
|
||||
Mutability::Mut => "*mut",
|
||||
};
|
||||
w!(self, "{mtbl} ");
|
||||
self.print_type_ref(*pointee);
|
||||
}
|
||||
TypeRef::Reference(ref_) => {
|
||||
let mtbl = match ref_.mutability {
|
||||
Mutability::Shared => "",
|
||||
Mutability::Mut => "mut ",
|
||||
};
|
||||
w!(self, "&");
|
||||
if let Some(lt) = &ref_.lifetime {
|
||||
self.print_lifetime_ref(*lt);
|
||||
w!(self, " ");
|
||||
}
|
||||
w!(self, "{mtbl}");
|
||||
self.print_type_ref(ref_.ty);
|
||||
}
|
||||
TypeRef::Array(array) => {
|
||||
w!(self, "[");
|
||||
self.print_type_ref(array.ty);
|
||||
w!(self, "; ");
|
||||
self.print_generic_arg(&GenericArg::Const(array.len));
|
||||
w!(self, "]");
|
||||
}
|
||||
TypeRef::Slice(elem) => {
|
||||
w!(self, "[");
|
||||
self.print_type_ref(*elem);
|
||||
w!(self, "]");
|
||||
}
|
||||
TypeRef::Fn(fn_) => {
|
||||
let ((_, return_type), args) =
|
||||
fn_.params.split_last().expect("TypeRef::Fn is missing return type");
|
||||
if fn_.is_unsafe {
|
||||
w!(self, "unsafe ");
|
||||
}
|
||||
if let Some(abi) = &fn_.abi {
|
||||
w!(self, "extern ");
|
||||
w!(self, "{}", abi.as_str());
|
||||
w!(self, " ");
|
||||
}
|
||||
w!(self, "fn(");
|
||||
for (i, (_, typeref)) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
self.print_type_ref(*typeref);
|
||||
}
|
||||
if fn_.is_varargs {
|
||||
if !args.is_empty() {
|
||||
w!(self, ", ");
|
||||
}
|
||||
w!(self, "...");
|
||||
}
|
||||
w!(self, ") -> ");
|
||||
self.print_type_ref(*return_type);
|
||||
}
|
||||
TypeRef::Error => w!(self, "{{error}}"),
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
w!(self, "impl ");
|
||||
self.print_type_bounds(bounds);
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
w!(self, "dyn ");
|
||||
self.print_type_bounds(bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_bounds(&mut self, bounds: &[TypeBound]) {
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(self, " + ");
|
||||
}
|
||||
|
||||
match bound {
|
||||
TypeBound::Path(path, modifier) => {
|
||||
match modifier {
|
||||
TraitBoundModifier::None => (),
|
||||
TraitBoundModifier::Maybe => w!(self, "?"),
|
||||
}
|
||||
self.print_path(&self.store[*path]);
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
w!(
|
||||
self,
|
||||
"for<{}> ",
|
||||
lifetimes
|
||||
.iter()
|
||||
.map(|it| it.display(self.db, self.edition))
|
||||
.format(", ")
|
||||
.to_string()
|
||||
);
|
||||
self.print_path(&self.store[*path]);
|
||||
}
|
||||
TypeBound::Lifetime(lt) => self.print_lifetime_ref(*lt),
|
||||
TypeBound::Use(args) => {
|
||||
w!(self, "use<");
|
||||
let mut first = true;
|
||||
for arg in args {
|
||||
if !mem::take(&mut first) {
|
||||
w!(self, ", ");
|
||||
}
|
||||
match arg {
|
||||
UseArgRef::Name(it) => {
|
||||
w!(self, "{}", it.display(self.db, self.edition))
|
||||
}
|
||||
UseArgRef::Lifetime(it) => self.print_lifetime_ref(*it),
|
||||
}
|
||||
}
|
||||
w!(self, ">")
|
||||
}
|
||||
TypeBound::Error => w!(self, "{{unknown}}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
//! Name resolution for expressions.
|
||||
use hir_expand::{name::Name, MacroDefId};
|
||||
use hir_expand::{MacroDefId, name::Name};
|
||||
use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
BlockId, DefWithBodyId,
|
||||
db::DefDatabase,
|
||||
expr_store::{Body, ExpressionStore, HygieneId},
|
||||
hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
|
||||
BlockId, ConstBlockId, DefWithBodyId,
|
||||
};
|
||||
|
||||
pub type ScopeId = Idx<ScopeData>;
|
||||
|
|
@ -53,9 +53,7 @@ pub struct ScopeData {
|
|||
impl ExprScopes {
|
||||
pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
|
||||
let body = db.body(def);
|
||||
let mut scopes = ExprScopes::new_body(&body, |const_block| {
|
||||
db.lookup_intern_anonymous_const(const_block).root
|
||||
});
|
||||
let mut scopes = ExprScopes::new_body(&body);
|
||||
scopes.shrink_to_fit();
|
||||
Arc::new(scopes)
|
||||
}
|
||||
|
|
@ -104,10 +102,7 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
|
|||
}
|
||||
|
||||
impl ExprScopes {
|
||||
fn new_body(
|
||||
body: &Body,
|
||||
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
||||
) -> ExprScopes {
|
||||
fn new_body(body: &Body) -> ExprScopes {
|
||||
let mut scopes = ExprScopes {
|
||||
scopes: Arena::default(),
|
||||
scope_entries: Arena::default(),
|
||||
|
|
@ -118,7 +113,7 @@ impl ExprScopes {
|
|||
scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param));
|
||||
}
|
||||
scopes.add_params_bindings(body, root, &body.params);
|
||||
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block);
|
||||
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
|
||||
scopes
|
||||
}
|
||||
|
||||
|
|
@ -221,23 +216,22 @@ fn compute_block_scopes(
|
|||
store: &ExpressionStore,
|
||||
scopes: &mut ExprScopes,
|
||||
scope: &mut ScopeId,
|
||||
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
||||
) {
|
||||
for stmt in statements {
|
||||
match stmt {
|
||||
Statement::Let { pat, initializer, else_branch, .. } => {
|
||||
if let Some(expr) = initializer {
|
||||
compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
|
||||
compute_expr_scopes(*expr, store, scopes, scope);
|
||||
}
|
||||
if let Some(expr) = else_branch {
|
||||
compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
|
||||
compute_expr_scopes(*expr, store, scopes, scope);
|
||||
}
|
||||
|
||||
*scope = scopes.new_scope(*scope);
|
||||
scopes.add_pat_bindings(store, *scope, *pat);
|
||||
}
|
||||
Statement::Expr { expr, .. } => {
|
||||
compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block);
|
||||
compute_expr_scopes(*expr, store, scopes, scope);
|
||||
}
|
||||
Statement::Item(Item::MacroDef(macro_id)) => {
|
||||
*scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
|
||||
|
|
@ -246,7 +240,7 @@ fn compute_block_scopes(
|
|||
}
|
||||
}
|
||||
if let Some(expr) = tail {
|
||||
compute_expr_scopes(expr, store, scopes, scope, resolve_const_block);
|
||||
compute_expr_scopes(expr, store, scopes, scope);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,13 +249,12 @@ fn compute_expr_scopes(
|
|||
store: &ExpressionStore,
|
||||
scopes: &mut ExprScopes,
|
||||
scope: &mut ScopeId,
|
||||
resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy,
|
||||
) {
|
||||
let make_label =
|
||||
|label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone()));
|
||||
|
||||
let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
|
||||
compute_expr_scopes(expr, store, scopes, scope, resolve_const_block)
|
||||
compute_expr_scopes(expr, store, scopes, scope)
|
||||
};
|
||||
|
||||
scopes.set_scope(expr, *scope);
|
||||
|
|
@ -271,18 +264,18 @@ fn compute_expr_scopes(
|
|||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||
// via the block itself (important for blocks that only contain items, no expressions).
|
||||
scopes.set_scope(expr, scope);
|
||||
compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
|
||||
compute_block_scopes(statements, *tail, store, scopes, &mut scope);
|
||||
}
|
||||
Expr::Const(id) => {
|
||||
let mut scope = scopes.root_scope();
|
||||
compute_expr_scopes(scopes, resolve_const_block(*id), &mut scope);
|
||||
compute_expr_scopes(scopes, *id, &mut scope);
|
||||
}
|
||||
Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
|
||||
let mut scope = scopes.new_block_scope(*scope, *id, None);
|
||||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||
// via the block itself (important for blocks that only contain items, no expressions).
|
||||
scopes.set_scope(expr, scope);
|
||||
compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block);
|
||||
compute_block_scopes(statements, *tail, store, scopes, &mut scope);
|
||||
}
|
||||
Expr::Loop { body: body_expr, label } => {
|
||||
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
|
||||
|
|
@ -324,20 +317,20 @@ fn compute_expr_scopes(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base_db::SourceDatabase;
|
||||
use hir_expand::{name::AsName, InFile};
|
||||
use base_db::RootQueryDb;
|
||||
use hir_expand::{InFile, name::AsName};
|
||||
use span::FileId;
|
||||
use syntax::{algo::find_node_at_offset, ast, AstNode};
|
||||
use syntax::{AstNode, algo::find_node_at_offset, ast};
|
||||
use test_fixture::WithFixture;
|
||||
use test_utils::{assert_eq_text, extract_offset};
|
||||
|
||||
use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
|
||||
use crate::{FunctionId, ModuleDefId, db::DefDatabase, test_db::TestDB};
|
||||
|
||||
fn find_function(db: &TestDB, file_id: FileId) -> FunctionId {
|
||||
let krate = db.test_crate();
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
|
||||
let module = crate_def_map.modules_for_file(file_id).next().unwrap();
|
||||
let module = crate_def_map.modules_for_file(db, file_id).next().unwrap();
|
||||
let (_, def) = crate_def_map[module].scope.entries().next().unwrap();
|
||||
match def.take_values().unwrap() {
|
||||
ModuleDefId::FunctionId(it) => it,
|
||||
|
|
@ -357,18 +350,20 @@ mod tests {
|
|||
};
|
||||
|
||||
let (db, position) = TestDB::with_position(&code);
|
||||
let file_id = position.file_id;
|
||||
let editioned_file_id = position.file_id;
|
||||
let offset = position.offset;
|
||||
|
||||
let file_syntax = db.parse(file_id).syntax_node();
|
||||
let (file_id, _) = editioned_file_id.unpack(&db);
|
||||
|
||||
let file_syntax = db.parse(editioned_file_id).syntax_node();
|
||||
let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
|
||||
let function = find_function(&db, file_id.file_id());
|
||||
let function = find_function(&db, file_id);
|
||||
|
||||
let scopes = db.expr_scopes(function.into());
|
||||
let (_body, source_map) = db.body_with_source_map(function.into());
|
||||
|
||||
let expr_id = source_map
|
||||
.node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
|
||||
.node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() })
|
||||
.unwrap()
|
||||
.as_expr()
|
||||
.unwrap();
|
||||
|
|
@ -511,15 +506,17 @@ fn foo() {
|
|||
|
||||
fn do_check_local_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_offset: u32) {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
let file_id = position.file_id;
|
||||
let editioned_file_id = position.file_id;
|
||||
let offset = position.offset;
|
||||
|
||||
let file = db.parse(file_id).ok().unwrap();
|
||||
let (file_id, _) = editioned_file_id.unpack(&db);
|
||||
|
||||
let file = db.parse(editioned_file_id).ok().unwrap();
|
||||
let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
|
||||
.expect("failed to find a name at the target offset");
|
||||
let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();
|
||||
|
||||
let function = find_function(&db, file_id.file_id());
|
||||
let function = find_function(&db, file_id);
|
||||
|
||||
let scopes = db.expr_scopes(function.into());
|
||||
let (_, source_map) = db.body_with_source_map(function.into());
|
||||
|
|
@ -527,7 +524,7 @@ fn foo() {
|
|||
let expr_scope = {
|
||||
let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
|
||||
let expr_id = source_map
|
||||
.node_expr(InFile { file_id: file_id.into(), value: &expr_ast })
|
||||
.node_expr(InFile { file_id: editioned_file_id.into(), value: &expr_ast })
|
||||
.unwrap()
|
||||
.as_expr()
|
||||
.unwrap();
|
||||
|
|
|
|||
|
|
@ -1,503 +1,2 @@
|
|||
mod block;
|
||||
|
||||
use crate::{hir::MatchArm, test_db::TestDB, ModuleDefId};
|
||||
use expect_test::{expect, Expect};
|
||||
use la_arena::RawIdx;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
|
||||
let krate = db.fetch_test_crate();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut fn_def = None;
|
||||
'outer: for (_, module) in def_map.modules() {
|
||||
for decl in module.scope.declarations() {
|
||||
if let ModuleDefId::FunctionId(it) = decl {
|
||||
fn_def = Some(it);
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
let fn_def = fn_def.unwrap().into();
|
||||
|
||||
let body = db.body(fn_def);
|
||||
(db, body, fn_def)
|
||||
}
|
||||
|
||||
fn def_map_at(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
|
||||
let module = db.module_at_position(position);
|
||||
module.def_map(&db).dump(&db)
|
||||
}
|
||||
|
||||
fn check_block_scopes_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
|
||||
let module = db.module_at_position(position);
|
||||
let actual = module.def_map(&db).dump_block_scopes(&db);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
fn check_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
let actual = def_map_at(ra_fixture);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn your_stack_belongs_to_me() {
|
||||
cov_mark::check!(your_stack_belongs_to_me);
|
||||
lower(
|
||||
r#"
|
||||
#![recursion_limit = "32"]
|
||||
macro_rules! n_nuple {
|
||||
($e:tt) => ();
|
||||
($($rest:tt)*) => {{
|
||||
(n_nuple!($($rest)*)None,)
|
||||
}};
|
||||
}
|
||||
fn main() { n_nuple!(1,2,3); }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn your_stack_belongs_to_me2() {
|
||||
cov_mark::check!(overflow_but_not_me);
|
||||
lower(
|
||||
r#"
|
||||
#![recursion_limit = "32"]
|
||||
macro_rules! foo {
|
||||
() => {{ foo!(); foo!(); }}
|
||||
}
|
||||
fn main() { foo!(); }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursion_limit() {
|
||||
lower(
|
||||
r#"
|
||||
#![recursion_limit = "2"]
|
||||
macro_rules! n_nuple {
|
||||
($e:tt) => ();
|
||||
($first:tt $($rest:tt)*) => {{
|
||||
n_nuple!($($rest)*)
|
||||
}};
|
||||
}
|
||||
fn main() { n_nuple!(1,2,3); }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3642_bad_macro_stackover() {
|
||||
lower(
|
||||
r#"
|
||||
#[macro_export]
|
||||
macro_rules! match_ast {
|
||||
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
|
||||
|
||||
(match ($node:expr) {
|
||||
$( ast::$ast:ident($it:ident) => $res:expr, )*
|
||||
_ => $catch_all:expr $(,)?
|
||||
}) => {{
|
||||
$( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
|
||||
{ $catch_all }
|
||||
}};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let anchor = match_ast! {
|
||||
match parent {
|
||||
as => {},
|
||||
_ => return None
|
||||
}
|
||||
};
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_resolve() {
|
||||
// Regression test for a path resolution bug introduced with inner item handling.
|
||||
lower(
|
||||
r#"
|
||||
macro_rules! vec {
|
||||
() => { () };
|
||||
($elem:expr; $n:expr) => { () };
|
||||
($($x:expr),+ $(,)?) => { () };
|
||||
}
|
||||
mod m {
|
||||
fn outer() {
|
||||
let _ = vec![FileSet::default(); self.len()];
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_for_loop() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: iterator
|
||||
fn main() {
|
||||
for ident in 0..10 {
|
||||
foo();
|
||||
bar()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() -> () {
|
||||
match builtin#lang(into_iter)(
|
||||
(0) ..(10) ,
|
||||
) {
|
||||
mut <ra@gennew>11 => loop {
|
||||
match builtin#lang(next)(
|
||||
&mut <ra@gennew>11,
|
||||
) {
|
||||
builtin#lang(None) => break,
|
||||
builtin#lang(Some)(ident) => {
|
||||
foo();
|
||||
bar()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() -> () {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"\u{1b}hello ", " ", " friends, we ", " ", "",
|
||||
],
|
||||
&[
|
||||
builtin#lang(Argument::new_display)(
|
||||
&count,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
&"fancy",
|
||||
), builtin#lang(Argument::new_debug)(
|
||||
&are,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
&"!",
|
||||
),
|
||||
],
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
8u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Is)(
|
||||
2usize,
|
||||
),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
2usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
3usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
);
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_hygiene() {
|
||||
let (db, body, def) = lower(
|
||||
r##"
|
||||
//- minicore: fmt, from
|
||||
//- /main.rs
|
||||
mod error;
|
||||
|
||||
use crate::error::error;
|
||||
|
||||
fn main() {
|
||||
// _ = forces body expansion instead of block def map expansion
|
||||
_ = error!("Failed to resolve path `{}`", node.text());
|
||||
}
|
||||
//- /error.rs
|
||||
macro_rules! _error {
|
||||
($fmt:expr, $($arg:tt)+) => {$crate::error::intermediate!(format_args!($fmt, $($arg)+))}
|
||||
}
|
||||
pub(crate) use _error as error;
|
||||
macro_rules! _intermediate {
|
||||
($arg:expr) => {$crate::error::SsrError::new($arg)}
|
||||
}
|
||||
pub(crate) use _intermediate as intermediate;
|
||||
|
||||
pub struct SsrError(pub(crate) core::fmt::Arguments);
|
||||
|
||||
impl SsrError {
|
||||
pub(crate) fn new(message: impl Into<core::fmt::Arguments>) -> SsrError {
|
||||
SsrError(message.into())
|
||||
}
|
||||
}
|
||||
"##,
|
||||
);
|
||||
|
||||
assert_eq!(db.body_with_source_map(def).1.diagnostics(), &[]);
|
||||
expect![[r#"
|
||||
fn main() -> () {
|
||||
_ = $crate::error::SsrError::new(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"Failed to resolve path `", "`",
|
||||
],
|
||||
&[
|
||||
builtin#lang(Argument::new_display)(
|
||||
&node.text(),
|
||||
),
|
||||
],
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
),
|
||||
);
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_10300() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: concat, panic
|
||||
mod private {
|
||||
pub use core::concat;
|
||||
}
|
||||
|
||||
macro_rules! m {
|
||||
() => {
|
||||
panic!(concat!($crate::private::concat!("cc")));
|
||||
};
|
||||
}
|
||||
|
||||
fn f(a: i32, b: u32) -> String {
|
||||
m!();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let (_, source_map) = db.body_with_source_map(def);
|
||||
assert_eq!(source_map.diagnostics(), &[]);
|
||||
|
||||
for (_, def_map) in body.blocks(&db) {
|
||||
assert_eq!(def_map.diagnostics(), &[]);
|
||||
}
|
||||
|
||||
expect![[r#"
|
||||
fn f(a: i32, b: u32) -> String {
|
||||
{
|
||||
$crate::panicking::panic_fmt(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"cc",
|
||||
],
|
||||
&[],
|
||||
&[],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
),
|
||||
);
|
||||
};
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_tuple_macro() {
|
||||
// This is a funny one. `let m!()() = Bar()` is an error in rustc, because `m!()()` isn't a valid pattern,
|
||||
// but in destructuring assignment it is valid, because `m!()()` is a valid expression, and destructuring
|
||||
// assignments start their lives as expressions. So we have to do the same.
|
||||
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
struct Bar();
|
||||
|
||||
macro_rules! m {
|
||||
() => { Bar };
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
m!()() = Bar();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let (_, source_map) = db.body_with_source_map(def);
|
||||
assert_eq!(source_map.diagnostics(), &[]);
|
||||
|
||||
for (_, def_map) in body.blocks(&db) {
|
||||
assert_eq!(def_map.diagnostics(), &[]);
|
||||
}
|
||||
|
||||
expect![[r#"
|
||||
fn foo() -> () {
|
||||
Bar() = Bar();
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing_record_variant() {
|
||||
let (_, body, _) = lower(
|
||||
r#"
|
||||
enum A {
|
||||
B { field: i32 },
|
||||
}
|
||||
fn f() {
|
||||
use A::*;
|
||||
match () {
|
||||
B => {}
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
|
||||
assert_eq!(
|
||||
body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
|
||||
"B",
|
||||
"should have a binding for `B`",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_pretty_print_bind_pat() {
|
||||
let (db, body, owner) = lower(
|
||||
r#"
|
||||
fn foo() {
|
||||
let v @ u = 123;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let printed = body.pretty_print(&db, owner, Edition::CURRENT);
|
||||
assert_eq!(
|
||||
printed,
|
||||
r#"fn foo() -> () {
|
||||
let v @ u = 123;
|
||||
}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_skips_body() {
|
||||
let (db, body, owner) = lower(
|
||||
r#"
|
||||
#[rust_analyzer::skip]
|
||||
async fn foo(a: (), b: i32) -> u32 {
|
||||
0 + 1 + b()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let printed = body.pretty_print(&db, owner, Edition::CURRENT);
|
||||
expect!["fn foo(<28>: (), <20>: i32) -> impl ::core::future::Future::<Output = u32> <20>"]
|
||||
.assert_eq(&printed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range_bounds_are_hir_exprs() {
|
||||
let (_, body, _) = lower(
|
||||
r#"
|
||||
pub const L: i32 = 6;
|
||||
mod x {
|
||||
pub const R: i32 = 100;
|
||||
}
|
||||
const fn f(x: i32) -> i32 {
|
||||
match x {
|
||||
-1..=5 => x * 10,
|
||||
L..=x::R => x * 100,
|
||||
_ => x,
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let mtch_arms = body
|
||||
.exprs
|
||||
.iter()
|
||||
.find_map(|(_, expr)| {
|
||||
if let Expr::Match { arms, .. } = expr {
|
||||
return Some(arms);
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let MatchArm { pat, .. } = mtch_arms[1];
|
||||
match body.pats[pat] {
|
||||
Pat::Range { start, end } => {
|
||||
let hir_start = &body.exprs[start.unwrap()];
|
||||
let hir_end = &body.exprs[end.unwrap()];
|
||||
|
||||
assert!(matches!(hir_start, Expr::Path { .. }));
|
||||
assert!(matches!(hir_end, Expr::Path { .. }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
mod body;
|
||||
mod signatures;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,502 @@
|
|||
mod block;
|
||||
|
||||
use crate::{DefWithBodyId, ModuleDefId, hir::MatchArm, test_db::TestDB};
|
||||
use expect_test::{Expect, expect};
|
||||
use la_arena::RawIdx;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use super::super::*;
|
||||
|
||||
fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
|
||||
let krate = db.fetch_test_crate();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut fn_def = None;
|
||||
'outer: for (_, module) in def_map.modules() {
|
||||
for decl in module.scope.declarations() {
|
||||
if let ModuleDefId::FunctionId(it) = decl {
|
||||
fn_def = Some(it);
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
let fn_def = fn_def.unwrap().into();
|
||||
|
||||
let body = db.body(fn_def);
|
||||
(db, body, fn_def)
|
||||
}
|
||||
|
||||
fn def_map_at(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
|
||||
let module = db.module_at_position(position);
|
||||
module.def_map(&db).dump(&db)
|
||||
}
|
||||
|
||||
fn check_block_scopes_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
|
||||
let module = db.module_at_position(position);
|
||||
let actual = module.def_map(&db).dump_block_scopes(&db);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
fn check_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
let actual = def_map_at(ra_fixture);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn your_stack_belongs_to_me() {
|
||||
cov_mark::check!(your_stack_belongs_to_me);
|
||||
lower(
|
||||
r#"
|
||||
#![recursion_limit = "32"]
|
||||
macro_rules! n_nuple {
|
||||
($e:tt) => ();
|
||||
($($rest:tt)*) => {{
|
||||
(n_nuple!($($rest)*)None,)
|
||||
}};
|
||||
}
|
||||
fn main() { n_nuple!(1,2,3); }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn your_stack_belongs_to_me2() {
|
||||
cov_mark::check!(overflow_but_not_me);
|
||||
lower(
|
||||
r#"
|
||||
#![recursion_limit = "32"]
|
||||
macro_rules! foo {
|
||||
() => {{ foo!(); foo!(); }}
|
||||
}
|
||||
fn main() { foo!(); }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursion_limit() {
|
||||
lower(
|
||||
r#"
|
||||
#![recursion_limit = "2"]
|
||||
macro_rules! n_nuple {
|
||||
($e:tt) => ();
|
||||
($first:tt $($rest:tt)*) => {{
|
||||
n_nuple!($($rest)*)
|
||||
}};
|
||||
}
|
||||
fn main() { n_nuple!(1,2,3); }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3642_bad_macro_stackover() {
|
||||
lower(
|
||||
r#"
|
||||
#[macro_export]
|
||||
macro_rules! match_ast {
|
||||
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
|
||||
|
||||
(match ($node:expr) {
|
||||
$( ast::$ast:ident($it:ident) => $res:expr, )*
|
||||
_ => $catch_all:expr $(,)?
|
||||
}) => {{
|
||||
$( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
|
||||
{ $catch_all }
|
||||
}};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let anchor = match_ast! {
|
||||
match parent {
|
||||
as => {},
|
||||
_ => return None
|
||||
}
|
||||
};
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_resolve() {
|
||||
// Regression test for a path resolution bug introduced with inner item handling.
|
||||
lower(
|
||||
r#"
|
||||
macro_rules! vec {
|
||||
() => { () };
|
||||
($elem:expr; $n:expr) => { () };
|
||||
($($x:expr),+ $(,)?) => { () };
|
||||
}
|
||||
mod m {
|
||||
fn outer() {
|
||||
let _ = vec![FileSet::default(); self.len()];
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_for_loop() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: iterator
|
||||
fn main() {
|
||||
for ident in 0..10 {
|
||||
foo();
|
||||
bar()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
match builtin#lang(into_iter)(
|
||||
(0) ..(10) ,
|
||||
) {
|
||||
mut <ra@gennew>11 => loop {
|
||||
match builtin#lang(next)(
|
||||
&mut <ra@gennew>11,
|
||||
) {
|
||||
builtin#lang(None) => break,
|
||||
builtin#lang(Some)(ident) => {
|
||||
foo();
|
||||
bar()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"\u{1b}hello ", " ", " friends, we ", " ", "",
|
||||
],
|
||||
&[
|
||||
builtin#lang(Argument::new_display)(
|
||||
&count,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
&"fancy",
|
||||
), builtin#lang(Argument::new_debug)(
|
||||
&are,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
&"!",
|
||||
),
|
||||
],
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
8u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Is)(
|
||||
2,
|
||||
),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
2usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
3usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
);
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_hygiene() {
|
||||
let (db, body, def) = lower(
|
||||
r##"
|
||||
//- minicore: fmt, from
|
||||
//- /main.rs
|
||||
mod error;
|
||||
|
||||
use crate::error::error;
|
||||
|
||||
fn main() {
|
||||
// _ = forces body expansion instead of block def map expansion
|
||||
_ = error!("Failed to resolve path `{}`", node.text());
|
||||
}
|
||||
//- /error.rs
|
||||
macro_rules! _error {
|
||||
($fmt:expr, $($arg:tt)+) => {$crate::error::intermediate!(format_args!($fmt, $($arg)+))}
|
||||
}
|
||||
pub(crate) use _error as error;
|
||||
macro_rules! _intermediate {
|
||||
($arg:expr) => {$crate::error::SsrError::new($arg)}
|
||||
}
|
||||
pub(crate) use _intermediate as intermediate;
|
||||
|
||||
pub struct SsrError(pub(crate) core::fmt::Arguments);
|
||||
|
||||
impl SsrError {
|
||||
pub(crate) fn new(message: impl Into<core::fmt::Arguments>) -> SsrError {
|
||||
SsrError(message.into())
|
||||
}
|
||||
}
|
||||
"##,
|
||||
);
|
||||
|
||||
assert_eq!(db.body_with_source_map(def).1.diagnostics(), &[]);
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
_ = ra_test_fixture::error::SsrError::new(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"Failed to resolve path `", "`",
|
||||
],
|
||||
&[
|
||||
builtin#lang(Argument::new_display)(
|
||||
&node.text(),
|
||||
),
|
||||
],
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
),
|
||||
);
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_10300() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: concat, panic
|
||||
mod private {
|
||||
pub use core::concat;
|
||||
}
|
||||
|
||||
macro_rules! m {
|
||||
() => {
|
||||
panic!(concat!($crate::private::concat!("cc")));
|
||||
};
|
||||
}
|
||||
|
||||
fn f(a: i32, b: u32) -> String {
|
||||
m!();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let (_, source_map) = db.body_with_source_map(def);
|
||||
assert_eq!(source_map.diagnostics(), &[]);
|
||||
|
||||
for (_, def_map) in body.blocks(&db) {
|
||||
assert_eq!(def_map.diagnostics(), &[]);
|
||||
}
|
||||
|
||||
expect![[r#"
|
||||
fn f(a, b) {
|
||||
{
|
||||
core::panicking::panic_fmt(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"cc",
|
||||
],
|
||||
&[],
|
||||
&[],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
),
|
||||
);
|
||||
};
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_tuple_macro() {
|
||||
// This is a funny one. `let m!()() = Bar()` is an error in rustc, because `m!()()` isn't a valid pattern,
|
||||
// but in destructuring assignment it is valid, because `m!()()` is a valid expression, and destructuring
|
||||
// assignments start their lives as expressions. So we have to do the same.
|
||||
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
struct Bar();
|
||||
|
||||
macro_rules! m {
|
||||
() => { Bar };
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
m!()() = Bar();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let (_, source_map) = db.body_with_source_map(def);
|
||||
assert_eq!(source_map.diagnostics(), &[]);
|
||||
|
||||
for (_, def_map) in body.blocks(&db) {
|
||||
assert_eq!(def_map.diagnostics(), &[]);
|
||||
}
|
||||
|
||||
expect![[r#"
|
||||
fn foo() {
|
||||
Bar() = Bar();
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing_record_variant() {
|
||||
let (_, body, _) = lower(
|
||||
r#"
|
||||
enum A {
|
||||
B { field: i32 },
|
||||
}
|
||||
fn f() {
|
||||
use A::*;
|
||||
match () {
|
||||
B => {}
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
|
||||
assert_eq!(
|
||||
body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
|
||||
"B",
|
||||
"should have a binding for `B`",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_pretty_print_bind_pat() {
|
||||
let (db, body, owner) = lower(
|
||||
r#"
|
||||
fn foo() {
|
||||
let v @ u = 123;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let printed = body.pretty_print(&db, owner, Edition::CURRENT);
|
||||
|
||||
expect![[r#"
|
||||
fn foo() {
|
||||
let v @ u = 123;
|
||||
}"#]]
|
||||
.assert_eq(&printed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_skips_body() {
|
||||
let (db, body, owner) = lower(
|
||||
r#"
|
||||
#[rust_analyzer::skip]
|
||||
async fn foo(a: (), b: i32) -> u32 {
|
||||
0 + 1 + b()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let printed = body.pretty_print(&db, owner, Edition::CURRENT);
|
||||
expect!["fn foo(<28>, <20>) <20>"].assert_eq(&printed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range_bounds_are_hir_exprs() {
|
||||
let (_, body, _) = lower(
|
||||
r#"
|
||||
pub const L: i32 = 6;
|
||||
mod x {
|
||||
pub const R: i32 = 100;
|
||||
}
|
||||
const fn f(x: i32) -> i32 {
|
||||
match x {
|
||||
-1..=5 => x * 10,
|
||||
L..=x::R => x * 100,
|
||||
_ => x,
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let mtch_arms = body
|
||||
.exprs
|
||||
.iter()
|
||||
.find_map(|(_, expr)| {
|
||||
if let Expr::Match { arms, .. } = expr {
|
||||
return Some(arms);
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let MatchArm { pat, .. } = mtch_arms[1];
|
||||
match body.pats[pat] {
|
||||
Pat::Range { start, end } => {
|
||||
let hir_start = &body.exprs[start.unwrap()];
|
||||
let hir_end = &body.exprs[end.unwrap()];
|
||||
|
||||
assert!(matches!(hir_start, Expr::Path { .. }));
|
||||
assert!(matches!(hir_end, Expr::Path { .. }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -189,8 +189,8 @@ fn f() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
|
||||
BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
|
||||
BlockId(3801) in BlockRelativeModuleId { block: Some(BlockId(3800)), local_id: Idx::<ModuleData>(1) }
|
||||
BlockId(3800) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
|
||||
crate scope
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
use crate::{
|
||||
GenericDefId, ModuleDefId,
|
||||
expr_store::pretty::{print_function, print_struct},
|
||||
test_db::TestDB,
|
||||
};
|
||||
use expect_test::{Expect, expect};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use super::super::*;
|
||||
|
||||
fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
|
||||
let krate = db.fetch_test_crate();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut defs = vec![];
|
||||
for (_, module) in def_map.modules() {
|
||||
for decl in module.scope.declarations() {
|
||||
let def: GenericDefId = match decl {
|
||||
ModuleDefId::ModuleId(_) => continue,
|
||||
ModuleDefId::FunctionId(id) => id.into(),
|
||||
ModuleDefId::AdtId(id) => id.into(),
|
||||
ModuleDefId::ConstId(id) => id.into(),
|
||||
ModuleDefId::StaticId(id) => id.into(),
|
||||
ModuleDefId::TraitId(id) => id.into(),
|
||||
ModuleDefId::TraitAliasId(id) => id.into(),
|
||||
ModuleDefId::TypeAliasId(id) => id.into(),
|
||||
ModuleDefId::EnumVariantId(_) => continue,
|
||||
ModuleDefId::BuiltinType(_) => continue,
|
||||
ModuleDefId::MacroId(_) => continue,
|
||||
};
|
||||
defs.push(def);
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = String::new();
|
||||
for def in defs {
|
||||
match def {
|
||||
GenericDefId::AdtId(adt_id) => match adt_id {
|
||||
crate::AdtId::StructId(struct_id) => {
|
||||
out += &print_struct(&db, &db.struct_signature(struct_id), Edition::CURRENT);
|
||||
}
|
||||
crate::AdtId::UnionId(_id) => (),
|
||||
crate::AdtId::EnumId(_id) => (),
|
||||
},
|
||||
GenericDefId::ConstId(_id) => (),
|
||||
GenericDefId::FunctionId(function_id) => {
|
||||
out += &print_function(&db, &db.function_signature(function_id), Edition::CURRENT)
|
||||
}
|
||||
|
||||
GenericDefId::ImplId(_id) => (),
|
||||
GenericDefId::StaticId(_id) => (),
|
||||
GenericDefId::TraitAliasId(_id) => (),
|
||||
GenericDefId::TraitId(_id) => (),
|
||||
GenericDefId::TypeAliasId(_id) => (),
|
||||
}
|
||||
}
|
||||
|
||||
expect.assert_eq(&out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structs() {
|
||||
lower_and_print(
|
||||
r"
|
||||
struct S { field: foo, }
|
||||
struct S(i32, u32, &'static str);
|
||||
#[repr(Rust)]
|
||||
struct S;
|
||||
|
||||
struct S<'a, 'b, T: Clone, const C: usize = 3, X = ()> where X: Default, for<'a, 'c> fn() -> i32: for<'b> Trait<'a, Item = Boo>;
|
||||
#[repr(C, packed)]
|
||||
struct S {}
|
||||
",
|
||||
expect![[r#"
|
||||
struct S {...}
|
||||
struct S(...)
|
||||
;
|
||||
struct S;
|
||||
struct S<'a, 'b, T, const C: usize = 3, X = ()>
|
||||
where
|
||||
T: Clone,
|
||||
X: Default,
|
||||
for<'a, 'c> fn() -> i32: for<'b> Trait::<'a, Item = Boo>
|
||||
;
|
||||
#[repr(C)]
|
||||
#[repr(pack(1))]
|
||||
struct S {...}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn functions() {
|
||||
lower_and_print(
|
||||
r#"
|
||||
fn foo<'a, const C: usize = 314235, T: Trait<Item = A> = B>(Struct { foo: bar }: &Struct, _: (), a: u32) -> &'a dyn Fn() -> i32 where (): Default {}
|
||||
const async unsafe extern "C" fn a() {}
|
||||
fn ret_impl_trait() -> impl Trait {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo<'a, const C: usize = 314235, T = B>(&Struct, (), u32) -> &'a dyn Fn::<(), Output = i32>
|
||||
where
|
||||
T: Trait::<Item = A>,
|
||||
(): Default
|
||||
{...}
|
||||
const async unsafe extern "C" fn a() -> impl ::core::future::Future::<Output = ()> {...}
|
||||
fn ret_impl_trait() -> impl Trait {...}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_position_impl_trait_functions() {
|
||||
lower_and_print(
|
||||
r"
|
||||
fn impl_trait_args<T>(_: impl Trait) {}
|
||||
fn impl_trait_args2<T>(_: impl Trait<impl Trait>) {}
|
||||
|
||||
fn impl_trait_ret<T>() -> impl Trait {}
|
||||
fn impl_trait_ret2<T>() -> impl Trait<impl Trait> {}
|
||||
|
||||
fn not_allowed1(f: impl Fn(impl Foo)) {
|
||||
let foo = S;
|
||||
f(foo);
|
||||
}
|
||||
|
||||
// This caused stack overflow in #17498
|
||||
fn not_allowed2(f: impl Fn(&impl Foo)) {
|
||||
let foo = S;
|
||||
f(&foo);
|
||||
}
|
||||
|
||||
fn not_allowed3(bar: impl Bar<impl Foo>) {}
|
||||
|
||||
// This also caused stack overflow
|
||||
fn not_allowed4(bar: impl Bar<&impl Foo>) {}
|
||||
|
||||
fn allowed1(baz: impl Baz<Assoc = impl Foo>) {}
|
||||
|
||||
fn allowed2<'a>(baz: impl Baz<Assoc = &'a (impl Foo + 'a)>) {}
|
||||
|
||||
fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
|
||||
",
|
||||
expect![[r#"
|
||||
fn impl_trait_args<T, Param[1]>(Param[1])
|
||||
where
|
||||
Param[1]: Trait
|
||||
{...}
|
||||
fn impl_trait_args2<T, Param[1]>(Param[1])
|
||||
where
|
||||
Param[1]: Trait::<{error}>
|
||||
{...}
|
||||
fn impl_trait_ret<T>() -> impl Trait {...}
|
||||
fn impl_trait_ret2<T>() -> impl Trait::<{error}> {...}
|
||||
fn not_allowed1<Param[0]>(Param[0])
|
||||
where
|
||||
Param[0]: Fn::<({error}), Output = ()>
|
||||
{...}
|
||||
fn not_allowed2<Param[0]>(Param[0])
|
||||
where
|
||||
Param[0]: Fn::<(&{error}), Output = ()>
|
||||
{...}
|
||||
fn not_allowed3<Param[0]>(Param[0])
|
||||
where
|
||||
Param[0]: Bar::<{error}>
|
||||
{...}
|
||||
fn not_allowed4<Param[0]>(Param[0])
|
||||
where
|
||||
Param[0]: Bar::<&{error}>
|
||||
{...}
|
||||
fn allowed1<Param[0], Param[1]>(Param[1])
|
||||
where
|
||||
Param[0]: Foo,
|
||||
Param[1]: Baz::<Assoc = Param[0]>
|
||||
{...}
|
||||
fn allowed2<'a, Param[0], Param[1]>(Param[1])
|
||||
where
|
||||
Param[0]: Foo,
|
||||
Param[0]: 'a,
|
||||
Param[1]: Baz::<Assoc = &'a Param[0]>
|
||||
{...}
|
||||
fn allowed3<Param[0], Param[1]>(Param[1])
|
||||
where
|
||||
Param[0]: Foo,
|
||||
Param[1]: Baz::<Assoc = Qux::<Param[0]>>
|
||||
{...}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -2,21 +2,21 @@
|
|||
|
||||
use std::{cell::Cell, cmp::Ordering, iter};
|
||||
|
||||
use base_db::{CrateId, CrateOrigin, LangCrateOrigin};
|
||||
use base_db::{Crate, CrateOrigin, LangCrateOrigin};
|
||||
use hir_expand::{
|
||||
name::{AsName, Name},
|
||||
Lookup,
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
ImportPathConfig, ModuleDefId, ModuleId,
|
||||
db::DefDatabase,
|
||||
item_scope::ItemInNs,
|
||||
nameres::DefMap,
|
||||
path::{ModPath, PathKind},
|
||||
visibility::{Visibility, VisibilityExplicitness},
|
||||
ImportPathConfig, ModuleDefId, ModuleId,
|
||||
};
|
||||
|
||||
/// Find a path that can be used to refer to a certain item. This can depend on
|
||||
|
|
@ -50,7 +50,7 @@ pub fn find_path(
|
|||
prefix: prefix_kind,
|
||||
cfg,
|
||||
ignore_local_imports,
|
||||
is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
|
||||
is_std_item: item_module.krate().data(db).origin.is_lang(),
|
||||
from,
|
||||
from_def_map: &from.def_map(db),
|
||||
fuel: Cell::new(FIND_PATH_FUEL),
|
||||
|
|
@ -134,10 +134,11 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
|
|||
|
||||
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
|
||||
// - if the item is an enum variant, refer to it via the enum
|
||||
if let Some(mut path) =
|
||||
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
|
||||
{
|
||||
path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
|
||||
let loc = variant.lookup(ctx.db);
|
||||
if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) {
|
||||
path.push_segment(
|
||||
ctx.db.enum_variants(loc.parent).variants[loc.index as usize].1.clone(),
|
||||
);
|
||||
return Some(path);
|
||||
}
|
||||
// If this doesn't work, it seems we have no way of referring to the
|
||||
|
|
@ -174,9 +175,9 @@ fn find_path_for_module(
|
|||
}
|
||||
// - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
|
||||
|
||||
let root_def_map = ctx.from.derive_crate_root().def_map(ctx.db);
|
||||
let root_local_def_map = ctx.from.derive_crate_root().local_def_map(ctx.db).1;
|
||||
// rev here so we prefer looking at renamed extern decls first
|
||||
for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude().rev() {
|
||||
for (name, (def_id, _extern_crate)) in root_local_def_map.extern_prelude().rev() {
|
||||
if crate_root != def_id {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -360,7 +361,7 @@ fn calculate_best_path(
|
|||
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
|
||||
// that wants to import it here, but we always prefer to use the external path here.
|
||||
|
||||
ctx.db.crate_graph()[ctx.from.krate].dependencies.iter().for_each(|dep| {
|
||||
ctx.from.krate.data(ctx.db).dependencies.iter().for_each(|dep| {
|
||||
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id)
|
||||
});
|
||||
}
|
||||
|
|
@ -373,11 +374,10 @@ fn find_in_sysroot(
|
|||
max_len: usize,
|
||||
best_choice: &mut Option<Choice>,
|
||||
) {
|
||||
let crate_graph = ctx.db.crate_graph();
|
||||
let dependencies = &crate_graph[ctx.from.krate].dependencies;
|
||||
let dependencies = &ctx.from.krate.data(ctx.db).dependencies;
|
||||
let mut search = |lang, best_choice: &mut _| {
|
||||
if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| {
|
||||
match crate_graph[dep.crate_id].origin {
|
||||
match dep.crate_id.data(ctx.db).origin {
|
||||
CrateOrigin::Lang(l) => l == lang,
|
||||
_ => false,
|
||||
}
|
||||
|
|
@ -419,7 +419,7 @@ fn find_in_dep(
|
|||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
best_choice: &mut Option<Choice>,
|
||||
dep: CrateId,
|
||||
dep: Crate,
|
||||
) {
|
||||
let import_map = ctx.db.import_map(dep);
|
||||
let Some(import_info_for) = import_map.import_info_for(item) else {
|
||||
|
|
@ -652,7 +652,7 @@ fn find_local_import_locations(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
use expect_test::{Expect, expect};
|
||||
use hir_expand::db::ExpandDatabase;
|
||||
use itertools::Itertools;
|
||||
use span::Edition;
|
||||
|
|
@ -688,9 +688,10 @@ mod tests {
|
|||
})
|
||||
.unwrap();
|
||||
|
||||
let def_map = module.def_map(&db);
|
||||
let (def_map, local_def_map) = module.local_def_map(&db);
|
||||
let resolved = def_map
|
||||
.resolve_path(
|
||||
&local_def_map,
|
||||
&db,
|
||||
module.local_id,
|
||||
&mod_path,
|
||||
|
|
|
|||
|
|
@ -1,900 +0,0 @@
|
|||
//! Many kinds of items or constructs can have generic parameters: functions,
|
||||
//! structs, impls, traits, etc. This module provides a common HIR for these
|
||||
//! generic parameters. See also the `Generics` type and the `generics_of` query
|
||||
//! in rustc.
|
||||
|
||||
use std::{ops, sync::LazyLock};
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
name::{AsName, Name},
|
||||
ExpandResult,
|
||||
};
|
||||
use la_arena::{Arena, RawIdx};
|
||||
use stdx::{
|
||||
impl_from,
|
||||
thin_vec::{EmptyOptimizedThinVec, ThinVec},
|
||||
};
|
||||
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
expander::Expander,
|
||||
item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree},
|
||||
lower::LowerCtx,
|
||||
nameres::{DefMap, MacroSubNs},
|
||||
path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
|
||||
type_ref::{
|
||||
ArrayType, ConstRef, FnType, LifetimeRef, PathId, RefType, TypeBound, TypeRef, TypeRefId,
|
||||
TypesMap, TypesSourceMap,
|
||||
},
|
||||
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
|
||||
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
||||
};
|
||||
|
||||
/// The index of the self param in the generic of the non-parent definition.
|
||||
const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> =
|
||||
LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0));
|
||||
|
||||
/// Data about a generic type parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TypeParamData {
|
||||
/// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just
|
||||
/// make it always be a value, giving impl trait a special name.
|
||||
pub name: Option<Name>,
|
||||
pub default: Option<TypeRefId>,
|
||||
pub provenance: TypeParamProvenance,
|
||||
}
|
||||
|
||||
/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct LifetimeParamData {
|
||||
pub name: Name,
|
||||
}
|
||||
|
||||
/// Data about a generic const parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ConstParamData {
|
||||
pub name: Name,
|
||||
pub ty: TypeRefId,
|
||||
pub default: Option<ConstRef>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum TypeParamProvenance {
|
||||
TypeParamList,
|
||||
TraitSelf,
|
||||
ArgumentImplTrait,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum TypeOrConstParamData {
|
||||
TypeParamData(TypeParamData),
|
||||
ConstParamData(ConstParamData),
|
||||
}
|
||||
|
||||
impl TypeOrConstParamData {
|
||||
pub fn name(&self) -> Option<&Name> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(),
|
||||
TypeOrConstParamData::ConstParamData(it) => Some(&it.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_default(&self) -> bool {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
|
||||
TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_param(&self) -> Option<&TypeParamData> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => Some(it),
|
||||
TypeOrConstParamData::ConstParamData(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_param(&self) -> Option<&ConstParamData> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(_) => None,
|
||||
TypeOrConstParamData::ConstParamData(it) => Some(it),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_trait_self(&self) -> bool {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => {
|
||||
it.provenance == TypeParamProvenance::TraitSelf
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum GenericParamData {
|
||||
TypeParamData(TypeParamData),
|
||||
ConstParamData(ConstParamData),
|
||||
LifetimeParamData(LifetimeParamData),
|
||||
}
|
||||
|
||||
impl GenericParamData {
|
||||
pub fn name(&self) -> Option<&Name> {
|
||||
match self {
|
||||
GenericParamData::TypeParamData(it) => it.name.as_ref(),
|
||||
GenericParamData::ConstParamData(it) => Some(&it.name),
|
||||
GenericParamData::LifetimeParamData(it) => Some(&it.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_param(&self) -> Option<&TypeParamData> {
|
||||
match self {
|
||||
GenericParamData::TypeParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_param(&self) -> Option<&ConstParamData> {
|
||||
match self {
|
||||
GenericParamData::ConstParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lifetime_param(&self) -> Option<&LifetimeParamData> {
|
||||
match self {
|
||||
GenericParamData::LifetimeParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
|
||||
|
||||
pub enum GenericParamDataRef<'a> {
|
||||
TypeParamData(&'a TypeParamData),
|
||||
ConstParamData(&'a ConstParamData),
|
||||
LifetimeParamData(&'a LifetimeParamData),
|
||||
}
|
||||
|
||||
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct GenericParams {
|
||||
type_or_consts: Arena<TypeOrConstParamData>,
|
||||
lifetimes: Arena<LifetimeParamData>,
|
||||
where_predicates: Box<[WherePredicate]>,
|
||||
pub types_map: TypesMap,
|
||||
}
|
||||
|
||||
impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
|
||||
type Output = TypeOrConstParamData;
|
||||
fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData {
|
||||
&self.type_or_consts[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<LocalLifetimeParamId> for GenericParams {
|
||||
type Output = LifetimeParamData;
|
||||
fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData {
|
||||
&self.lifetimes[index]
|
||||
}
|
||||
}
|
||||
|
||||
/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
|
||||
/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
|
||||
/// It might still result in multiple actual predicates though, because of
|
||||
/// associated type bindings like `Iterator<Item = u32>`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum WherePredicate {
|
||||
TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
|
||||
Lifetime { target: LifetimeRef, bound: LifetimeRef },
|
||||
ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum WherePredicateTypeTarget {
|
||||
TypeRef(TypeRefId),
|
||||
/// For desugared where predicates that can directly refer to a type param.
|
||||
TypeOrConstParam(LocalTypeOrConstParamId),
|
||||
}
|
||||
|
||||
impl GenericParams {
|
||||
/// Number of Generic parameters (type_or_consts + lifetimes)
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.type_or_consts.len() + self.lifetimes.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len_lifetimes(&self) -> usize {
|
||||
self.lifetimes.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len_type_or_consts(&self) -> usize {
|
||||
self.type_or_consts.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn no_predicates(&self) -> bool {
|
||||
self.where_predicates.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> {
|
||||
self.where_predicates.iter()
|
||||
}
|
||||
|
||||
/// Iterator of type_or_consts field
|
||||
#[inline]
|
||||
pub fn iter_type_or_consts(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
|
||||
self.type_or_consts.iter()
|
||||
}
|
||||
|
||||
/// Iterator of lifetimes field
|
||||
#[inline]
|
||||
pub fn iter_lt(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
|
||||
self.lifetimes.iter()
|
||||
}
|
||||
|
||||
pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> {
|
||||
self.type_or_consts.iter().find_map(|(id, p)| {
|
||||
if p.name().as_ref() == Some(&name) && p.type_param().is_some() {
|
||||
Some(TypeParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId> {
|
||||
self.type_or_consts.iter().find_map(|(id, p)| {
|
||||
if p.name().as_ref() == Some(&name) && p.const_param().is_some() {
|
||||
Some(ConstParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
|
||||
if self.type_or_consts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
matches!(
|
||||
self.type_or_consts[SELF_PARAM_ID_IN_SELF],
|
||||
TypeOrConstParamData::TypeParamData(TypeParamData {
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
..
|
||||
})
|
||||
)
|
||||
.then(|| SELF_PARAM_ID_IN_SELF)
|
||||
}
|
||||
|
||||
pub fn find_lifetime_by_name(
|
||||
&self,
|
||||
name: &Name,
|
||||
parent: GenericDefId,
|
||||
) -> Option<LifetimeParamId> {
|
||||
self.lifetimes.iter().find_map(|(id, p)| {
|
||||
if &p.name == name {
|
||||
Some(LifetimeParamId { local_id: id, parent })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn generic_params_query(
|
||||
db: &dyn DefDatabase,
|
||||
def: GenericDefId,
|
||||
) -> Arc<GenericParams> {
|
||||
db.generic_params_with_source_map(def).0
|
||||
}
|
||||
|
||||
pub(crate) fn generic_params_with_source_map_query(
|
||||
db: &dyn DefDatabase,
|
||||
def: GenericDefId,
|
||||
) -> (Arc<GenericParams>, Option<Arc<TypesSourceMap>>) {
|
||||
let _p = tracing::info_span!("generic_params_query").entered();
|
||||
|
||||
let krate = def.krate(db);
|
||||
let cfg_options = db.crate_graph();
|
||||
let cfg_options = &cfg_options[krate].cfg_options;
|
||||
|
||||
// Returns the generic parameters that are enabled under the current `#[cfg]` options
|
||||
let enabled_params =
|
||||
|params: &Arc<GenericParams>, item_tree: &ItemTree, parent: GenericModItem| {
|
||||
let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
|
||||
let attr_owner_ct = |param| AttrOwner::TypeOrConstParamData(parent, param);
|
||||
let attr_owner_lt = |param| AttrOwner::LifetimeParamData(parent, param);
|
||||
|
||||
// In the common case, no parameters will by disabled by `#[cfg]` attributes.
|
||||
// Therefore, make a first pass to check if all parameters are enabled and, if so,
|
||||
// clone the `Interned<GenericParams>` instead of recreating an identical copy.
|
||||
let all_type_or_consts_enabled =
|
||||
params.type_or_consts.iter().all(|(idx, _)| enabled(attr_owner_ct(idx)));
|
||||
let all_lifetimes_enabled =
|
||||
params.lifetimes.iter().all(|(idx, _)| enabled(attr_owner_lt(idx)));
|
||||
|
||||
if all_type_or_consts_enabled && all_lifetimes_enabled {
|
||||
params.clone()
|
||||
} else {
|
||||
Arc::new(GenericParams {
|
||||
type_or_consts: all_type_or_consts_enabled
|
||||
.then(|| params.type_or_consts.clone())
|
||||
.unwrap_or_else(|| {
|
||||
params
|
||||
.type_or_consts
|
||||
.iter()
|
||||
.filter(|&(idx, _)| enabled(attr_owner_ct(idx)))
|
||||
.map(|(_, param)| param.clone())
|
||||
.collect()
|
||||
}),
|
||||
lifetimes: all_lifetimes_enabled
|
||||
.then(|| params.lifetimes.clone())
|
||||
.unwrap_or_else(|| {
|
||||
params
|
||||
.lifetimes
|
||||
.iter()
|
||||
.filter(|&(idx, _)| enabled(attr_owner_lt(idx)))
|
||||
.map(|(_, param)| param.clone())
|
||||
.collect()
|
||||
}),
|
||||
where_predicates: params.where_predicates.clone(),
|
||||
types_map: params.types_map.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
fn id_to_generics<Id: GenericsItemTreeNode>(
|
||||
db: &dyn DefDatabase,
|
||||
id: impl for<'db> Lookup<
|
||||
Database<'db> = dyn DefDatabase + 'db,
|
||||
Data = impl ItemTreeLoc<Id = Id>,
|
||||
>,
|
||||
enabled_params: impl Fn(
|
||||
&Arc<GenericParams>,
|
||||
&ItemTree,
|
||||
GenericModItem,
|
||||
) -> Arc<GenericParams>,
|
||||
) -> (Arc<GenericParams>, Option<Arc<TypesSourceMap>>)
|
||||
where
|
||||
FileItemTreeId<Id>: Into<GenericModItem>,
|
||||
{
|
||||
let id = id.lookup(db).item_tree_id();
|
||||
let tree = id.item_tree(db);
|
||||
let item = &tree[id.value];
|
||||
(enabled_params(item.generic_params(), &tree, id.value.into()), None)
|
||||
}
|
||||
|
||||
match def {
|
||||
GenericDefId::FunctionId(id) => {
|
||||
let loc = id.lookup(db);
|
||||
let tree = loc.id.item_tree(db);
|
||||
let item = &tree[loc.id.value];
|
||||
|
||||
let enabled_params =
|
||||
enabled_params(&item.explicit_generic_params, &tree, loc.id.value.into());
|
||||
|
||||
let module = loc.container.module(db);
|
||||
let func_data = db.function_data(id);
|
||||
if func_data.params.is_empty() {
|
||||
(enabled_params, None)
|
||||
} else {
|
||||
let source_maps = loc.id.item_tree_with_source_map(db).1;
|
||||
let item_source_maps = source_maps.function(loc.id.value);
|
||||
let mut generic_params = GenericParamsCollector {
|
||||
type_or_consts: enabled_params.type_or_consts.clone(),
|
||||
lifetimes: enabled_params.lifetimes.clone(),
|
||||
where_predicates: enabled_params.where_predicates.clone().into(),
|
||||
};
|
||||
|
||||
let (mut types_map, mut types_source_maps) =
|
||||
(enabled_params.types_map.clone(), item_source_maps.generics().clone());
|
||||
// Don't create an `Expander` if not needed since this
|
||||
// could cause a reparse after the `ItemTree` has been created due to the spanmap.
|
||||
let mut expander = None;
|
||||
for ¶m in func_data.params.iter() {
|
||||
generic_params.fill_implicit_impl_trait_args(
|
||||
db,
|
||||
&mut types_map,
|
||||
&mut types_source_maps,
|
||||
&mut expander,
|
||||
&mut || {
|
||||
(module.def_map(db), Expander::new(db, loc.id.file_id(), module))
|
||||
},
|
||||
param,
|
||||
&item.types_map,
|
||||
item_source_maps.item(),
|
||||
);
|
||||
}
|
||||
let generics = generic_params.finish(types_map, &mut types_source_maps);
|
||||
(generics, Some(Arc::new(types_source_maps)))
|
||||
}
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::TraitId(id) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
|
||||
GenericDefId::ConstId(_) | GenericDefId::StaticId(_) => (
|
||||
Arc::new(GenericParams {
|
||||
type_or_consts: Default::default(),
|
||||
lifetimes: Default::default(),
|
||||
where_predicates: Default::default(),
|
||||
types_map: Default::default(),
|
||||
}),
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct GenericParamsCollector {
|
||||
pub(crate) type_or_consts: Arena<TypeOrConstParamData>,
|
||||
lifetimes: Arena<LifetimeParamData>,
|
||||
where_predicates: Vec<WherePredicate>,
|
||||
}
|
||||
|
||||
impl GenericParamsCollector {
|
||||
pub(crate) fn fill(
|
||||
&mut self,
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
node: &dyn HasGenericParams,
|
||||
add_param_attrs: impl FnMut(
|
||||
Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
|
||||
ast::GenericParam,
|
||||
),
|
||||
) {
|
||||
if let Some(params) = node.generic_param_list() {
|
||||
self.fill_params(lower_ctx, params, add_param_attrs)
|
||||
}
|
||||
if let Some(where_clause) = node.where_clause() {
|
||||
self.fill_where_predicates(lower_ctx, where_clause);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fill_bounds(
|
||||
&mut self,
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
type_bounds: Option<ast::TypeBoundList>,
|
||||
target: Either<TypeRefId, LifetimeRef>,
|
||||
) {
|
||||
for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
|
||||
self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_params(
|
||||
&mut self,
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
params: ast::GenericParamList,
|
||||
mut add_param_attrs: impl FnMut(
|
||||
Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
|
||||
ast::GenericParam,
|
||||
),
|
||||
) {
|
||||
for type_or_const_param in params.type_or_const_params() {
|
||||
match type_or_const_param {
|
||||
ast::TypeOrConstParam::Type(type_param) => {
|
||||
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
// FIXME: Use `Path::from_src`
|
||||
let default =
|
||||
type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it));
|
||||
let param = TypeParamData {
|
||||
name: Some(name.clone()),
|
||||
default,
|
||||
provenance: TypeParamProvenance::TypeParamList,
|
||||
};
|
||||
let idx = self.type_or_consts.alloc(param.into());
|
||||
let type_ref = lower_ctx.alloc_type_ref_desugared(TypeRef::Path(name.into()));
|
||||
self.fill_bounds(
|
||||
lower_ctx,
|
||||
type_param.type_bound_list(),
|
||||
Either::Left(type_ref),
|
||||
);
|
||||
add_param_attrs(Either::Left(idx), ast::GenericParam::TypeParam(type_param));
|
||||
}
|
||||
ast::TypeOrConstParam::Const(const_param) => {
|
||||
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
let ty = TypeRef::from_ast_opt(lower_ctx, const_param.ty());
|
||||
let param = ConstParamData {
|
||||
name,
|
||||
ty,
|
||||
default: ConstRef::from_const_param(lower_ctx, &const_param),
|
||||
};
|
||||
let idx = self.type_or_consts.alloc(param.into());
|
||||
add_param_attrs(Either::Left(idx), ast::GenericParam::ConstParam(const_param));
|
||||
}
|
||||
}
|
||||
}
|
||||
for lifetime_param in params.lifetime_params() {
|
||||
let name =
|
||||
lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(<));
|
||||
let param = LifetimeParamData { name: name.clone() };
|
||||
let idx = self.lifetimes.alloc(param);
|
||||
let lifetime_ref = LifetimeRef::new_name(name);
|
||||
self.fill_bounds(
|
||||
lower_ctx,
|
||||
lifetime_param.type_bound_list(),
|
||||
Either::Right(lifetime_ref),
|
||||
);
|
||||
add_param_attrs(Either::Right(idx), ast::GenericParam::LifetimeParam(lifetime_param));
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_where_predicates(
|
||||
&mut self,
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
where_clause: ast::WhereClause,
|
||||
) {
|
||||
for pred in where_clause.predicates() {
|
||||
let target = if let Some(type_ref) = pred.ty() {
|
||||
Either::Left(TypeRef::from_ast(lower_ctx, type_ref))
|
||||
} else if let Some(lifetime) = pred.lifetime() {
|
||||
Either::Right(LifetimeRef::new(&lifetime))
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
|
||||
// Higher-Ranked Trait Bounds
|
||||
param_list
|
||||
.lifetime_params()
|
||||
.map(|lifetime_param| {
|
||||
lifetime_param
|
||||
.lifetime()
|
||||
.map_or_else(Name::missing, |lt| Name::new_lifetime(<))
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
|
||||
self.add_where_predicate_from_bound(
|
||||
lower_ctx,
|
||||
bound,
|
||||
lifetimes.as_deref(),
|
||||
target.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_where_predicate_from_bound(
|
||||
&mut self,
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
bound: ast::TypeBound,
|
||||
hrtb_lifetimes: Option<&[Name]>,
|
||||
target: Either<TypeRefId, LifetimeRef>,
|
||||
) {
|
||||
let bound = TypeBound::from_ast(lower_ctx, bound);
|
||||
self.fill_impl_trait_bounds(lower_ctx.take_impl_traits_bounds());
|
||||
let predicate = match (target, bound) {
|
||||
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
|
||||
Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
|
||||
lifetimes: hrtb_lifetimes.to_vec().into_boxed_slice(),
|
||||
target: WherePredicateTypeTarget::TypeRef(type_ref),
|
||||
bound,
|
||||
},
|
||||
None => WherePredicate::TypeBound {
|
||||
target: WherePredicateTypeTarget::TypeRef(type_ref),
|
||||
bound,
|
||||
},
|
||||
},
|
||||
(Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
|
||||
WherePredicate::Lifetime { target: lifetime, bound }
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
self.where_predicates.push(predicate);
|
||||
}
|
||||
|
||||
fn fill_impl_trait_bounds(&mut self, impl_bounds: Vec<ThinVec<TypeBound>>) {
|
||||
for bounds in impl_bounds {
|
||||
let param = TypeParamData {
|
||||
name: None,
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::ArgumentImplTrait,
|
||||
};
|
||||
let param_id = self.type_or_consts.alloc(param.into());
|
||||
for bound in &bounds {
|
||||
self.where_predicates.push(WherePredicate::TypeBound {
|
||||
target: WherePredicateTypeTarget::TypeOrConstParam(param_id),
|
||||
bound: bound.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_implicit_impl_trait_args(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
generics_types_map: &mut TypesMap,
|
||||
generics_types_source_map: &mut TypesSourceMap,
|
||||
// FIXME: Change this back to `LazyCell` if https://github.com/rust-lang/libs-team/issues/429 is accepted.
|
||||
exp: &mut Option<(Arc<DefMap>, Expander)>,
|
||||
exp_fill: &mut dyn FnMut() -> (Arc<DefMap>, Expander),
|
||||
type_ref: TypeRefId,
|
||||
types_map: &TypesMap,
|
||||
types_source_map: &TypesSourceMap,
|
||||
) {
|
||||
TypeRef::walk(type_ref, types_map, &mut |type_ref| {
|
||||
if let TypeRef::ImplTrait(bounds) = type_ref {
|
||||
let param = TypeParamData {
|
||||
name: None,
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::ArgumentImplTrait,
|
||||
};
|
||||
let param_id = self.type_or_consts.alloc(param.into());
|
||||
for bound in bounds {
|
||||
let bound = copy_type_bound(
|
||||
bound,
|
||||
types_map,
|
||||
types_source_map,
|
||||
generics_types_map,
|
||||
generics_types_source_map,
|
||||
);
|
||||
self.where_predicates.push(WherePredicate::TypeBound {
|
||||
target: WherePredicateTypeTarget::TypeOrConstParam(param_id),
|
||||
bound,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeRef::Macro(mc) = type_ref {
|
||||
let macro_call = mc.to_node(db.upcast());
|
||||
let (def_map, expander) = exp.get_or_insert_with(&mut *exp_fill);
|
||||
|
||||
let module = expander.module.local_id;
|
||||
let resolver = |path: &_| {
|
||||
def_map
|
||||
.resolve_path(
|
||||
db,
|
||||
module,
|
||||
path,
|
||||
crate::item_scope::BuiltinShadowMode::Other,
|
||||
Some(MacroSubNs::Bang),
|
||||
)
|
||||
.0
|
||||
.take_macros()
|
||||
};
|
||||
if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
|
||||
expander.enter_expand(db, macro_call, resolver)
|
||||
{
|
||||
let (mut macro_types_map, mut macro_types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut ctx =
|
||||
expander.ctx(db, &mut macro_types_map, &mut macro_types_source_map);
|
||||
let type_ref = TypeRef::from_ast(&mut ctx, expanded.tree());
|
||||
self.fill_implicit_impl_trait_args(
|
||||
db,
|
||||
generics_types_map,
|
||||
generics_types_source_map,
|
||||
&mut *exp,
|
||||
exp_fill,
|
||||
type_ref,
|
||||
¯o_types_map,
|
||||
¯o_types_source_map,
|
||||
);
|
||||
exp.get_or_insert_with(&mut *exp_fill).1.exit(mark);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn finish(
|
||||
self,
|
||||
mut generics_types_map: TypesMap,
|
||||
generics_types_source_map: &mut TypesSourceMap,
|
||||
) -> Arc<GenericParams> {
|
||||
let Self { mut lifetimes, mut type_or_consts, mut where_predicates } = self;
|
||||
|
||||
if lifetimes.is_empty() && type_or_consts.is_empty() && where_predicates.is_empty() {
|
||||
static EMPTY: LazyLock<Arc<GenericParams>> = LazyLock::new(|| {
|
||||
Arc::new(GenericParams {
|
||||
lifetimes: Arena::new(),
|
||||
type_or_consts: Arena::new(),
|
||||
where_predicates: Box::default(),
|
||||
types_map: TypesMap::default(),
|
||||
})
|
||||
});
|
||||
return Arc::clone(&EMPTY);
|
||||
}
|
||||
|
||||
lifetimes.shrink_to_fit();
|
||||
type_or_consts.shrink_to_fit();
|
||||
where_predicates.shrink_to_fit();
|
||||
generics_types_map.shrink_to_fit();
|
||||
generics_types_source_map.shrink_to_fit();
|
||||
Arc::new(GenericParams {
|
||||
type_or_consts,
|
||||
lifetimes,
|
||||
where_predicates: where_predicates.into_boxed_slice(),
|
||||
types_map: generics_types_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies a `TypeRef` from a `TypesMap` (accompanied with `TypesSourceMap`) into another `TypesMap`
|
||||
/// (and `TypesSourceMap`).
|
||||
fn copy_type_ref(
|
||||
type_ref: TypeRefId,
|
||||
from: &TypesMap,
|
||||
from_source_map: &TypesSourceMap,
|
||||
to: &mut TypesMap,
|
||||
to_source_map: &mut TypesSourceMap,
|
||||
) -> TypeRefId {
|
||||
let result = match &from[type_ref] {
|
||||
TypeRef::Fn(fn_) => {
|
||||
let params = fn_.params().iter().map(|(name, param_type)| {
|
||||
(name.clone(), copy_type_ref(*param_type, from, from_source_map, to, to_source_map))
|
||||
});
|
||||
TypeRef::Fn(FnType::new(fn_.is_varargs(), fn_.is_unsafe(), fn_.abi().clone(), params))
|
||||
}
|
||||
TypeRef::Tuple(types) => TypeRef::Tuple(EmptyOptimizedThinVec::from_iter(
|
||||
types.iter().map(|&t| copy_type_ref(t, from, from_source_map, to, to_source_map)),
|
||||
)),
|
||||
&TypeRef::RawPtr(type_ref, mutbl) => TypeRef::RawPtr(
|
||||
copy_type_ref(type_ref, from, from_source_map, to, to_source_map),
|
||||
mutbl,
|
||||
),
|
||||
TypeRef::Reference(ref_) => TypeRef::Reference(Box::new(RefType {
|
||||
ty: copy_type_ref(ref_.ty, from, from_source_map, to, to_source_map),
|
||||
lifetime: ref_.lifetime.clone(),
|
||||
mutability: ref_.mutability,
|
||||
})),
|
||||
TypeRef::Array(array) => TypeRef::Array(Box::new(ArrayType {
|
||||
ty: copy_type_ref(array.ty, from, from_source_map, to, to_source_map),
|
||||
len: array.len.clone(),
|
||||
})),
|
||||
&TypeRef::Slice(type_ref) => {
|
||||
TypeRef::Slice(copy_type_ref(type_ref, from, from_source_map, to, to_source_map))
|
||||
}
|
||||
TypeRef::ImplTrait(bounds) => TypeRef::ImplTrait(ThinVec::from_iter(copy_type_bounds(
|
||||
bounds,
|
||||
from,
|
||||
from_source_map,
|
||||
to,
|
||||
to_source_map,
|
||||
))),
|
||||
TypeRef::DynTrait(bounds) => TypeRef::DynTrait(ThinVec::from_iter(copy_type_bounds(
|
||||
bounds,
|
||||
from,
|
||||
from_source_map,
|
||||
to,
|
||||
to_source_map,
|
||||
))),
|
||||
TypeRef::Path(path) => {
|
||||
TypeRef::Path(copy_path(path, from, from_source_map, to, to_source_map))
|
||||
}
|
||||
TypeRef::Never => TypeRef::Never,
|
||||
TypeRef::Placeholder => TypeRef::Placeholder,
|
||||
TypeRef::Macro(macro_call) => TypeRef::Macro(*macro_call),
|
||||
TypeRef::Error => TypeRef::Error,
|
||||
};
|
||||
let id = to.types.alloc(result);
|
||||
if let Some(&ptr) = from_source_map.types_map_back.get(id) {
|
||||
to_source_map.types_map_back.insert(id, ptr);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
fn copy_path(
|
||||
path: &Path,
|
||||
from: &TypesMap,
|
||||
from_source_map: &TypesSourceMap,
|
||||
to: &mut TypesMap,
|
||||
to_source_map: &mut TypesSourceMap,
|
||||
) -> Path {
|
||||
match path {
|
||||
Path::BarePath(mod_path) => Path::BarePath(mod_path.clone()),
|
||||
Path::Normal(path) => {
|
||||
let type_anchor = path
|
||||
.type_anchor()
|
||||
.map(|type_ref| copy_type_ref(type_ref, from, from_source_map, to, to_source_map));
|
||||
let mod_path = path.mod_path().clone();
|
||||
let generic_args = path.generic_args().iter().map(|generic_args| {
|
||||
copy_generic_args(generic_args, from, from_source_map, to, to_source_map)
|
||||
});
|
||||
Path::Normal(NormalPath::new(type_anchor, mod_path, generic_args))
|
||||
}
|
||||
Path::LangItem(lang_item, name) => Path::LangItem(*lang_item, name.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_generic_args(
|
||||
generic_args: &Option<GenericArgs>,
|
||||
from: &TypesMap,
|
||||
from_source_map: &TypesSourceMap,
|
||||
to: &mut TypesMap,
|
||||
to_source_map: &mut TypesSourceMap,
|
||||
) -> Option<GenericArgs> {
|
||||
generic_args.as_ref().map(|generic_args| {
|
||||
let args = generic_args
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
&GenericArg::Type(ty) => {
|
||||
GenericArg::Type(copy_type_ref(ty, from, from_source_map, to, to_source_map))
|
||||
}
|
||||
GenericArg::Lifetime(lifetime) => GenericArg::Lifetime(lifetime.clone()),
|
||||
GenericArg::Const(konst) => GenericArg::Const(konst.clone()),
|
||||
})
|
||||
.collect();
|
||||
let bindings = generic_args
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|binding| {
|
||||
let name = binding.name.clone();
|
||||
let args =
|
||||
copy_generic_args(&binding.args, from, from_source_map, to, to_source_map);
|
||||
let type_ref = binding.type_ref.map(|type_ref| {
|
||||
copy_type_ref(type_ref, from, from_source_map, to, to_source_map)
|
||||
});
|
||||
let bounds =
|
||||
copy_type_bounds(&binding.bounds, from, from_source_map, to, to_source_map)
|
||||
.collect();
|
||||
AssociatedTypeBinding { name, args, type_ref, bounds }
|
||||
})
|
||||
.collect();
|
||||
GenericArgs {
|
||||
args,
|
||||
has_self_type: generic_args.has_self_type,
|
||||
bindings,
|
||||
desugared_from_fn: generic_args.desugared_from_fn,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn copy_type_bounds<'a>(
|
||||
bounds: &'a [TypeBound],
|
||||
from: &'a TypesMap,
|
||||
from_source_map: &'a TypesSourceMap,
|
||||
to: &'a mut TypesMap,
|
||||
to_source_map: &'a mut TypesSourceMap,
|
||||
) -> impl stdx::thin_vec::TrustedLen<Item = TypeBound> + 'a {
|
||||
bounds.iter().map(|bound| copy_type_bound(bound, from, from_source_map, to, to_source_map))
|
||||
}
|
||||
|
||||
fn copy_type_bound(
|
||||
bound: &TypeBound,
|
||||
from: &TypesMap,
|
||||
from_source_map: &TypesSourceMap,
|
||||
to: &mut TypesMap,
|
||||
to_source_map: &mut TypesSourceMap,
|
||||
) -> TypeBound {
|
||||
let mut copy_path_id = |path: PathId| {
|
||||
let new_path = copy_path(&from[path], from, from_source_map, to, to_source_map);
|
||||
let new_path_id = to.types.alloc(TypeRef::Path(new_path));
|
||||
if let Some(&ptr) = from_source_map.types_map_back.get(path.type_ref()) {
|
||||
to_source_map.types_map_back.insert(new_path_id, ptr);
|
||||
}
|
||||
PathId::from_type_ref_unchecked(new_path_id)
|
||||
};
|
||||
|
||||
match bound {
|
||||
&TypeBound::Path(path, modifier) => TypeBound::Path(copy_path_id(path), modifier),
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
TypeBound::ForLifetime(lifetimes.clone(), copy_path_id(*path))
|
||||
}
|
||||
TypeBound::Lifetime(lifetime) => TypeBound::Lifetime(lifetime.clone()),
|
||||
TypeBound::Use(use_args) => TypeBound::Use(use_args.clone()),
|
||||
TypeBound::Error => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,12 @@
|
|||
//! See also a neighboring `body` module.
|
||||
|
||||
pub mod format_args;
|
||||
pub mod generics;
|
||||
pub mod type_ref;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::{name::Name, MacroDefId};
|
||||
use hir_expand::{MacroDefId, name::Name};
|
||||
use intern::Symbol;
|
||||
use la_arena::Idx;
|
||||
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
|
||||
|
|
@ -25,10 +26,13 @@ use syntax::ast;
|
|||
use type_ref::TypeRefId;
|
||||
|
||||
use crate::{
|
||||
BlockId,
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||
path::{GenericArgs, Path},
|
||||
expr_store::{
|
||||
HygieneId,
|
||||
path::{GenericArgs, Path},
|
||||
},
|
||||
type_ref::{Mutability, Rawness},
|
||||
BlockId, ConstBlockId,
|
||||
};
|
||||
|
||||
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
|
||||
|
|
@ -137,11 +141,7 @@ pub enum LiteralOrConst {
|
|||
|
||||
impl Literal {
|
||||
pub fn negate(self) -> Option<Self> {
|
||||
if let Literal::Int(i, k) = self {
|
||||
Some(Literal::Int(-i, k))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if let Literal::Int(i, k) = self { Some(Literal::Int(-i, k)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ pub enum Expr {
|
|||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Const(ConstBlockId),
|
||||
Const(ExprId),
|
||||
// FIXME: Fold this into Block with an unsafe flag?
|
||||
Unsafe {
|
||||
id: Option<BlockId>,
|
||||
|
|
@ -555,6 +555,9 @@ pub struct Binding {
|
|||
pub name: Name,
|
||||
pub mode: BindingAnnotation,
|
||||
pub problems: Option<BindingProblems>,
|
||||
/// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
|
||||
/// expansions are attributed to their parent expansion (recursively).
|
||||
pub hygiene: HygieneId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ use either::Either;
|
|||
use hir_expand::name::Name;
|
||||
use intern::Symbol;
|
||||
use rustc_parse_format as parse;
|
||||
use span::SyntaxContextId;
|
||||
use span::SyntaxContext;
|
||||
use stdx::TupleExt;
|
||||
use syntax::{
|
||||
ast::{self, IsString},
|
||||
TextRange,
|
||||
ast::{self, IsString},
|
||||
};
|
||||
|
||||
use crate::hir::ExprId;
|
||||
|
|
@ -176,7 +176,7 @@ pub(crate) fn parse(
|
|||
is_direct_literal: bool,
|
||||
mut synth: impl FnMut(Name, Option<TextRange>) -> ExprId,
|
||||
mut record_usage: impl FnMut(Name, Option<TextRange>),
|
||||
call_ctx: SyntaxContextId,
|
||||
call_ctx: SyntaxContext,
|
||||
) -> FormatArgs {
|
||||
let Ok(text) = s.value() else {
|
||||
return FormatArgs {
|
||||
|
|
@ -460,10 +460,6 @@ impl FormatArgumentsCollector {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, arg: FormatArgument) -> usize {
|
||||
let index = self.arguments.len();
|
||||
if let Some(name) = arg.kind.ident() {
|
||||
|
|
|
|||
403
src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs
Normal file
403
src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
//! Pre-type IR item generics
|
||||
use std::{ops, sync::LazyLock};
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use stdx::impl_from;
|
||||
use thin_vec::ThinVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, ConstParamId, GenericDefId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
|
||||
db::DefDatabase,
|
||||
expr_store::{ExpressionStore, ExpressionStoreSourceMap},
|
||||
type_ref::{ConstRef, LifetimeRefId, TypeBound, TypeRefId},
|
||||
};
|
||||
|
||||
pub type LocalTypeOrConstParamId = Idx<TypeOrConstParamData>;
|
||||
pub type LocalLifetimeParamId = Idx<LifetimeParamData>;
|
||||
|
||||
/// Data about a generic type parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TypeParamData {
|
||||
/// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just
|
||||
/// make it always be a value, giving impl trait a special name.
|
||||
pub name: Option<Name>,
|
||||
pub default: Option<TypeRefId>,
|
||||
pub provenance: TypeParamProvenance,
|
||||
}
|
||||
|
||||
/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct LifetimeParamData {
|
||||
pub name: Name,
|
||||
}
|
||||
|
||||
/// Data about a generic const parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ConstParamData {
|
||||
pub name: Name,
|
||||
pub ty: TypeRefId,
|
||||
pub default: Option<ConstRef>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum TypeParamProvenance {
|
||||
TypeParamList,
|
||||
TraitSelf,
|
||||
ArgumentImplTrait,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum TypeOrConstParamData {
|
||||
TypeParamData(TypeParamData),
|
||||
ConstParamData(ConstParamData),
|
||||
}
|
||||
|
||||
impl TypeOrConstParamData {
|
||||
pub fn name(&self) -> Option<&Name> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(),
|
||||
TypeOrConstParamData::ConstParamData(it) => Some(&it.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_default(&self) -> bool {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
|
||||
TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_param(&self) -> Option<&TypeParamData> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => Some(it),
|
||||
TypeOrConstParamData::ConstParamData(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_param(&self) -> Option<&ConstParamData> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(_) => None,
|
||||
TypeOrConstParamData::ConstParamData(it) => Some(it),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_trait_self(&self) -> bool {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(it) => {
|
||||
it.provenance == TypeParamProvenance::TraitSelf
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum GenericParamData {
|
||||
TypeParamData(TypeParamData),
|
||||
ConstParamData(ConstParamData),
|
||||
LifetimeParamData(LifetimeParamData),
|
||||
}
|
||||
|
||||
impl GenericParamData {
|
||||
pub fn name(&self) -> Option<&Name> {
|
||||
match self {
|
||||
GenericParamData::TypeParamData(it) => it.name.as_ref(),
|
||||
GenericParamData::ConstParamData(it) => Some(&it.name),
|
||||
GenericParamData::LifetimeParamData(it) => Some(&it.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_param(&self) -> Option<&TypeParamData> {
|
||||
match self {
|
||||
GenericParamData::TypeParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_param(&self) -> Option<&ConstParamData> {
|
||||
match self {
|
||||
GenericParamData::ConstParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lifetime_param(&self) -> Option<&LifetimeParamData> {
|
||||
match self {
|
||||
GenericParamData::LifetimeParamData(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GenericParamDataRef<'a> {
|
||||
TypeParamData(&'a TypeParamData),
|
||||
ConstParamData(&'a ConstParamData),
|
||||
LifetimeParamData(&'a LifetimeParamData),
|
||||
}
|
||||
|
||||
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct GenericParams {
|
||||
pub(crate) type_or_consts: Arena<TypeOrConstParamData>,
|
||||
pub(crate) lifetimes: Arena<LifetimeParamData>,
|
||||
pub(crate) where_predicates: Box<[WherePredicate]>,
|
||||
}
|
||||
|
||||
impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
|
||||
type Output = TypeOrConstParamData;
|
||||
fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData {
|
||||
&self.type_or_consts[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<LocalLifetimeParamId> for GenericParams {
|
||||
type Output = LifetimeParamData;
|
||||
fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData {
|
||||
&self.lifetimes[index]
|
||||
}
|
||||
}
|
||||
|
||||
/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
|
||||
/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
|
||||
/// It might still result in multiple actual predicates though, because of
|
||||
/// associated type bindings like `Iterator<Item = u32>`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum WherePredicate {
|
||||
TypeBound { target: TypeRefId, bound: TypeBound },
|
||||
Lifetime { target: LifetimeRefId, bound: LifetimeRefId },
|
||||
ForLifetime { lifetimes: ThinVec<Name>, target: TypeRefId, bound: TypeBound },
|
||||
}
|
||||
|
||||
static EMPTY: LazyLock<Arc<GenericParams>> = LazyLock::new(|| {
|
||||
Arc::new(GenericParams {
|
||||
type_or_consts: Arena::default(),
|
||||
lifetimes: Arena::default(),
|
||||
where_predicates: Box::default(),
|
||||
})
|
||||
});
|
||||
|
||||
impl GenericParams {
|
||||
/// The index of the self param in the generic of the non-parent definition.
|
||||
pub(crate) const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> =
|
||||
LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0));
|
||||
|
||||
pub fn new(db: &dyn DefDatabase, def: GenericDefId) -> Arc<GenericParams> {
|
||||
match def {
|
||||
GenericDefId::AdtId(AdtId::EnumId(it)) => db.enum_signature(it).generic_params.clone(),
|
||||
GenericDefId::AdtId(AdtId::StructId(it)) => {
|
||||
db.struct_signature(it).generic_params.clone()
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::UnionId(it)) => {
|
||||
db.union_signature(it).generic_params.clone()
|
||||
}
|
||||
GenericDefId::ConstId(_) => EMPTY.clone(),
|
||||
GenericDefId::FunctionId(function_id) => {
|
||||
db.function_signature(function_id).generic_params.clone()
|
||||
}
|
||||
GenericDefId::ImplId(impl_id) => db.impl_signature(impl_id).generic_params.clone(),
|
||||
GenericDefId::StaticId(_) => EMPTY.clone(),
|
||||
GenericDefId::TraitAliasId(trait_alias_id) => {
|
||||
db.trait_alias_signature(trait_alias_id).generic_params.clone()
|
||||
}
|
||||
GenericDefId::TraitId(trait_id) => db.trait_signature(trait_id).generic_params.clone(),
|
||||
GenericDefId::TypeAliasId(type_alias_id) => {
|
||||
db.type_alias_signature(type_alias_id).generic_params.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generic_params_and_store(
|
||||
db: &dyn DefDatabase,
|
||||
def: GenericDefId,
|
||||
) -> (Arc<GenericParams>, Arc<ExpressionStore>) {
|
||||
match def {
|
||||
GenericDefId::AdtId(AdtId::EnumId(id)) => {
|
||||
let sig = db.enum_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::StructId(id)) => {
|
||||
let sig = db.struct_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::UnionId(id)) => {
|
||||
let sig = db.union_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::ConstId(id) => {
|
||||
let sig = db.const_signature(id);
|
||||
(EMPTY.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::FunctionId(id) => {
|
||||
let sig = db.function_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::ImplId(id) => {
|
||||
let sig = db.impl_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::StaticId(id) => {
|
||||
let sig = db.static_signature(id);
|
||||
(EMPTY.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::TraitAliasId(id) => {
|
||||
let sig = db.trait_alias_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::TraitId(id) => {
|
||||
let sig = db.trait_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
GenericDefId::TypeAliasId(id) => {
|
||||
let sig = db.type_alias_signature(id);
|
||||
(sig.generic_params.clone(), sig.store.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generic_params_and_store_and_source_map(
|
||||
db: &dyn DefDatabase,
|
||||
def: GenericDefId,
|
||||
) -> (Arc<GenericParams>, Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>) {
|
||||
match def {
|
||||
GenericDefId::AdtId(AdtId::EnumId(id)) => {
|
||||
let (sig, sm) = db.enum_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::StructId(id)) => {
|
||||
let (sig, sm) = db.struct_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::UnionId(id)) => {
|
||||
let (sig, sm) = db.union_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::ConstId(id) => {
|
||||
let (sig, sm) = db.const_signature_with_source_map(id);
|
||||
(EMPTY.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::FunctionId(id) => {
|
||||
let (sig, sm) = db.function_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::ImplId(id) => {
|
||||
let (sig, sm) = db.impl_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::StaticId(id) => {
|
||||
let (sig, sm) = db.static_signature_with_source_map(id);
|
||||
(EMPTY.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::TraitAliasId(id) => {
|
||||
let (sig, sm) = db.trait_alias_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::TraitId(id) => {
|
||||
let (sig, sm) = db.trait_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
GenericDefId::TypeAliasId(id) => {
|
||||
let (sig, sm) = db.type_alias_signature_with_source_map(id);
|
||||
(sig.generic_params.clone(), sig.store.clone(), sm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of Generic parameters (type_or_consts + lifetimes)
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.type_or_consts.len() + self.lifetimes.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len_lifetimes(&self) -> usize {
|
||||
self.lifetimes.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len_type_or_consts(&self) -> usize {
|
||||
self.type_or_consts.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn no_predicates(&self) -> bool {
|
||||
self.where_predicates.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> {
|
||||
self.where_predicates.iter()
|
||||
}
|
||||
|
||||
/// Iterator of type_or_consts field
|
||||
#[inline]
|
||||
pub fn iter_type_or_consts(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
|
||||
self.type_or_consts.iter()
|
||||
}
|
||||
|
||||
/// Iterator of lifetimes field
|
||||
#[inline]
|
||||
pub fn iter_lt(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
|
||||
self.lifetimes.iter()
|
||||
}
|
||||
|
||||
pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> {
|
||||
self.type_or_consts.iter().find_map(|(id, p)| {
|
||||
if p.name().as_ref() == Some(&name) && p.type_param().is_some() {
|
||||
Some(TypeParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId> {
|
||||
self.type_or_consts.iter().find_map(|(id, p)| {
|
||||
if p.name().as_ref() == Some(&name) && p.const_param().is_some() {
|
||||
Some(ConstParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
|
||||
if self.type_or_consts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
matches!(
|
||||
self.type_or_consts[Self::SELF_PARAM_ID_IN_SELF],
|
||||
TypeOrConstParamData::TypeParamData(TypeParamData {
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
..
|
||||
})
|
||||
)
|
||||
.then(|| Self::SELF_PARAM_ID_IN_SELF)
|
||||
}
|
||||
|
||||
pub fn find_lifetime_by_name(
|
||||
&self,
|
||||
name: &Name,
|
||||
parent: GenericDefId,
|
||||
) -> Option<LifetimeParamId> {
|
||||
self.lifetimes.iter().find_map(|(id, p)| {
|
||||
if &p.name == name { Some(LifetimeParamId { local_id: id, parent }) } else { None }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,21 @@
|
|||
//! HIR for references to types. Paths in these are not yet resolved. They can
|
||||
//! be directly created from an ast::TypeRef, without further queries.
|
||||
|
||||
use core::fmt;
|
||||
use std::{fmt::Write, ops::Index};
|
||||
use std::fmt::Write;
|
||||
|
||||
use hir_expand::{
|
||||
db::ExpandDatabase,
|
||||
name::{AsName, Name},
|
||||
AstId, InFile,
|
||||
};
|
||||
use intern::{sym, Symbol};
|
||||
use la_arena::{Arena, ArenaMap, Idx};
|
||||
use span::Edition;
|
||||
use stdx::thin_vec::{thin_vec_with_header_struct, EmptyOptimizedThinVec, ThinVec};
|
||||
use syntax::{
|
||||
ast::{self, HasGenericArgs, HasName, IsString},
|
||||
AstPtr,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Symbol;
|
||||
use la_arena::Idx;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::{
|
||||
LifetimeParamId, TypeParamId,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
hir::Literal,
|
||||
lower::LowerCtx,
|
||||
path::{GenericArg, Path},
|
||||
SyntheticSyntax,
|
||||
expr_store::{
|
||||
ExpressionStore,
|
||||
path::{GenericArg, Path},
|
||||
},
|
||||
hir::{ExprId, Literal},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
|
@ -34,11 +26,7 @@ pub enum Mutability {
|
|||
|
||||
impl Mutability {
|
||||
pub fn from_mutable(mutable: bool) -> Mutability {
|
||||
if mutable {
|
||||
Mutability::Mut
|
||||
} else {
|
||||
Mutability::Shared
|
||||
}
|
||||
if mutable { Mutability::Mut } else { Mutability::Shared }
|
||||
}
|
||||
|
||||
pub fn as_keyword_for_ref(self) -> &'static str {
|
||||
|
|
@ -80,11 +68,7 @@ pub enum Rawness {
|
|||
|
||||
impl Rawness {
|
||||
pub fn from_raw(is_raw: bool) -> Rawness {
|
||||
if is_raw {
|
||||
Rawness::RawPtr
|
||||
} else {
|
||||
Rawness::Ref
|
||||
}
|
||||
if is_raw { Rawness::RawPtr } else { Rawness::Ref }
|
||||
}
|
||||
|
||||
pub fn is_raw(&self) -> bool {
|
||||
|
|
@ -114,40 +98,32 @@ pub struct TraitRef {
|
|||
pub path: PathId,
|
||||
}
|
||||
|
||||
impl TraitRef {
|
||||
/// Converts an `ast::PathType` to a `hir::TraitRef`.
|
||||
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> Option<Self> {
|
||||
// FIXME: Use `Path::from_src`
|
||||
match &node {
|
||||
ast::Type::PathType(path) => path
|
||||
.path()
|
||||
.and_then(|it| ctx.lower_path(it))
|
||||
.map(|path| TraitRef { path: ctx.alloc_path(path, AstPtr::new(&node)) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct FnType {
|
||||
pub params: Box<[(Option<Name>, TypeRefId)]>,
|
||||
pub is_varargs: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub abi: Option<Symbol>,
|
||||
}
|
||||
|
||||
thin_vec_with_header_struct! {
|
||||
pub new(pub(crate)) struct FnType, FnTypeHeader {
|
||||
pub params: [(Option<Name>, TypeRefId)],
|
||||
pub is_varargs: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub abi: Option<Symbol>; ref,
|
||||
impl FnType {
|
||||
#[inline]
|
||||
pub fn split_params_and_ret(&self) -> (&[(Option<Name>, TypeRefId)], TypeRefId) {
|
||||
let (ret, params) = self.params.split_last().expect("should have at least return type");
|
||||
(params, ret.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ArrayType {
|
||||
pub ty: TypeRefId,
|
||||
// FIXME: This should be Ast<ConstArg>
|
||||
pub len: ConstRef,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct RefType {
|
||||
pub ty: TypeRefId,
|
||||
pub lifetime: Option<LifetimeRef>,
|
||||
pub lifetime: Option<LifetimeRefId>,
|
||||
pub mutability: Mutability,
|
||||
}
|
||||
|
||||
|
|
@ -156,17 +132,19 @@ pub struct RefType {
|
|||
pub enum TypeRef {
|
||||
Never,
|
||||
Placeholder,
|
||||
Tuple(EmptyOptimizedThinVec<TypeRefId>),
|
||||
Tuple(ThinVec<TypeRefId>),
|
||||
Path(Path),
|
||||
RawPtr(TypeRefId, Mutability),
|
||||
// FIXME: Unbox this once `Idx` has a niche,
|
||||
// as `RefType` should shrink by 4 bytes then
|
||||
Reference(Box<RefType>),
|
||||
Array(Box<ArrayType>),
|
||||
Array(ArrayType),
|
||||
Slice(TypeRefId),
|
||||
/// A fn pointer. Last element of the vector is the return type.
|
||||
Fn(FnType),
|
||||
Fn(Box<FnType>),
|
||||
ImplTrait(ThinVec<TypeBound>),
|
||||
DynTrait(ThinVec<TypeBound>),
|
||||
Macro(AstId<ast::MacroCall>),
|
||||
TypeParam(TypeParamId),
|
||||
Error,
|
||||
}
|
||||
|
||||
|
|
@ -175,97 +153,33 @@ const _: () = assert!(size_of::<TypeRef>() == 16);
|
|||
|
||||
pub type TypeRefId = Idx<TypeRef>;
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TypesMap {
|
||||
pub(crate) types: Arena<TypeRef>,
|
||||
}
|
||||
|
||||
impl TypesMap {
|
||||
pub const EMPTY: &TypesMap = &TypesMap { types: Arena::new() };
|
||||
|
||||
pub(crate) fn shrink_to_fit(&mut self) {
|
||||
let TypesMap { types } = self;
|
||||
types.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<TypeRefId> for TypesMap {
|
||||
type Output = TypeRef;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: TypeRefId) -> &Self::Output {
|
||||
&self.types[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<PathId> for TypesMap {
|
||||
type Output = Path;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: PathId) -> &Self::Output {
|
||||
let TypeRef::Path(path) = &self[index.type_ref()] else {
|
||||
unreachable!("`PathId` always points to `TypeRef::Path`");
|
||||
};
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
pub type TypePtr = AstPtr<ast::Type>;
|
||||
pub type TypeSource = InFile<TypePtr>;
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TypesSourceMap {
|
||||
pub(crate) types_map_back: ArenaMap<TypeRefId, TypeSource>,
|
||||
}
|
||||
|
||||
impl TypesSourceMap {
|
||||
pub const EMPTY: Self = Self { types_map_back: ArenaMap::new() };
|
||||
|
||||
pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
|
||||
self.types_map_back.get(id).cloned().ok_or(SyntheticSyntax)
|
||||
}
|
||||
|
||||
pub(crate) fn shrink_to_fit(&mut self) {
|
||||
let TypesSourceMap { types_map_back } = self;
|
||||
types_map_back.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
pub type LifetimeRefId = Idx<LifetimeRef>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct LifetimeRef {
|
||||
pub name: Name,
|
||||
}
|
||||
|
||||
impl LifetimeRef {
|
||||
pub(crate) fn new_name(name: Name) -> Self {
|
||||
LifetimeRef { name }
|
||||
}
|
||||
|
||||
pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
|
||||
LifetimeRef { name: Name::new_lifetime(lifetime) }
|
||||
}
|
||||
|
||||
pub fn missing() -> LifetimeRef {
|
||||
LifetimeRef { name: Name::missing() }
|
||||
}
|
||||
pub enum LifetimeRef {
|
||||
Named(Name),
|
||||
Static,
|
||||
Placeholder,
|
||||
Param(LifetimeParamId),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeBound {
|
||||
Path(PathId, TraitBoundModifier),
|
||||
ForLifetime(Box<[Name]>, PathId),
|
||||
Lifetime(LifetimeRef),
|
||||
Use(Box<[UseArgRef]>),
|
||||
ForLifetime(ThinVec<Name>, PathId),
|
||||
Lifetime(LifetimeRefId),
|
||||
Use(ThinVec<UseArgRef>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const _: [(); 24] = [(); size_of::<TypeBound>()];
|
||||
const _: [(); 16] = [(); size_of::<TypeBound>()];
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum UseArgRef {
|
||||
Name(Name),
|
||||
Lifetime(LifetimeRef),
|
||||
Lifetime(LifetimeRefId),
|
||||
}
|
||||
|
||||
/// A modifier on a bound, currently this is only used for `?Sized`, where the
|
||||
|
|
@ -277,124 +191,19 @@ pub enum TraitBoundModifier {
|
|||
}
|
||||
|
||||
impl TypeRef {
|
||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||
pub fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> TypeRefId {
|
||||
let ty = match &node {
|
||||
ast::Type::ParenType(inner) => return TypeRef::from_ast_opt(ctx, inner.ty()),
|
||||
ast::Type::TupleType(inner) => TypeRef::Tuple(EmptyOptimizedThinVec::from_iter(
|
||||
Vec::from_iter(inner.fields().map(|it| TypeRef::from_ast(ctx, it))),
|
||||
)),
|
||||
ast::Type::NeverType(..) => TypeRef::Never,
|
||||
ast::Type::PathType(inner) => {
|
||||
// FIXME: Use `Path::from_src`
|
||||
inner
|
||||
.path()
|
||||
.and_then(|it| ctx.lower_path(it))
|
||||
.map(TypeRef::Path)
|
||||
.unwrap_or(TypeRef::Error)
|
||||
}
|
||||
ast::Type::PtrType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
|
||||
let mutability = Mutability::from_mutable(inner.mut_token().is_some());
|
||||
TypeRef::RawPtr(inner_ty, mutability)
|
||||
}
|
||||
ast::Type::ArrayType(inner) => {
|
||||
let len = ConstRef::from_const_arg(ctx, inner.const_arg());
|
||||
TypeRef::Array(Box::new(ArrayType {
|
||||
ty: TypeRef::from_ast_opt(ctx, inner.ty()),
|
||||
len,
|
||||
}))
|
||||
}
|
||||
ast::Type::SliceType(inner) => TypeRef::Slice(TypeRef::from_ast_opt(ctx, inner.ty())),
|
||||
ast::Type::RefType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
|
||||
let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(<));
|
||||
let mutability = Mutability::from_mutable(inner.mut_token().is_some());
|
||||
TypeRef::Reference(Box::new(RefType { ty: inner_ty, lifetime, mutability }))
|
||||
}
|
||||
ast::Type::InferType(_inner) => TypeRef::Placeholder,
|
||||
ast::Type::FnPtrType(inner) => {
|
||||
let ret_ty = inner
|
||||
.ret_type()
|
||||
.and_then(|rt| rt.ty())
|
||||
.map(|it| TypeRef::from_ast(ctx, it))
|
||||
.unwrap_or_else(|| ctx.alloc_type_ref_desugared(TypeRef::unit()));
|
||||
let mut is_varargs = false;
|
||||
let mut params = if let Some(pl) = inner.param_list() {
|
||||
if let Some(param) = pl.params().last() {
|
||||
is_varargs = param.dotdotdot_token().is_some();
|
||||
}
|
||||
|
||||
pl.params()
|
||||
.map(|it| {
|
||||
let type_ref = TypeRef::from_ast_opt(ctx, it.ty());
|
||||
let name = match it.pat() {
|
||||
Some(ast::Pat::IdentPat(it)) => Some(
|
||||
it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing),
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
(name, type_ref)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::with_capacity(1)
|
||||
};
|
||||
fn lower_abi(abi: ast::Abi) -> Symbol {
|
||||
match abi.abi_string() {
|
||||
Some(tok) => Symbol::intern(tok.text_without_quotes()),
|
||||
// `extern` default to be `extern "C"`.
|
||||
_ => sym::C.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
let abi = inner.abi().map(lower_abi);
|
||||
params.push((None, ret_ty));
|
||||
TypeRef::Fn(FnType::new(is_varargs, inner.unsafe_token().is_some(), abi, params))
|
||||
}
|
||||
// for types are close enough for our purposes to the inner type for now...
|
||||
ast::Type::ForType(inner) => return TypeRef::from_ast_opt(ctx, inner.ty()),
|
||||
ast::Type::ImplTraitType(inner) => {
|
||||
if ctx.outer_impl_trait() {
|
||||
// Disallow nested impl traits
|
||||
TypeRef::Error
|
||||
} else {
|
||||
ctx.with_outer_impl_trait_scope(true, |ctx| {
|
||||
TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
|
||||
})
|
||||
}
|
||||
}
|
||||
ast::Type::DynTraitType(inner) => {
|
||||
TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
|
||||
}
|
||||
ast::Type::MacroType(mt) => match mt.macro_call() {
|
||||
Some(mc) => TypeRef::Macro(ctx.ast_id(&mc)),
|
||||
None => TypeRef::Error,
|
||||
},
|
||||
};
|
||||
ctx.alloc_type_ref(ty, AstPtr::new(&node))
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_opt(ctx: &mut LowerCtx<'_>, node: Option<ast::Type>) -> TypeRefId {
|
||||
match node {
|
||||
Some(node) => TypeRef::from_ast(ctx, node),
|
||||
None => ctx.alloc_error_type(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unit() -> TypeRef {
|
||||
TypeRef::Tuple(EmptyOptimizedThinVec::empty())
|
||||
TypeRef::Tuple(ThinVec::new())
|
||||
}
|
||||
|
||||
pub fn walk(this: TypeRefId, map: &TypesMap, f: &mut impl FnMut(&TypeRef)) {
|
||||
pub fn walk(this: TypeRefId, map: &ExpressionStore, f: &mut impl FnMut(&TypeRef)) {
|
||||
go(this, f, map);
|
||||
|
||||
fn go(type_ref: TypeRefId, f: &mut impl FnMut(&TypeRef), map: &TypesMap) {
|
||||
fn go(type_ref: TypeRefId, f: &mut impl FnMut(&TypeRef), map: &ExpressionStore) {
|
||||
let type_ref = &map[type_ref];
|
||||
f(type_ref);
|
||||
match type_ref {
|
||||
TypeRef::Fn(fn_) => {
|
||||
fn_.params().iter().for_each(|&(_, param_type)| go(param_type, f, map))
|
||||
fn_.params.iter().for_each(|&(_, param_type)| go(param_type, f, map))
|
||||
}
|
||||
TypeRef::Tuple(types) => types.iter().for_each(|&t| go(t, f, map)),
|
||||
TypeRef::RawPtr(type_ref, _) | TypeRef::Slice(type_ref) => go(*type_ref, f, map),
|
||||
|
|
@ -411,11 +220,11 @@ impl TypeRef {
|
|||
}
|
||||
}
|
||||
TypeRef::Path(path) => go_path(path, f, map),
|
||||
TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
|
||||
TypeRef::Never | TypeRef::Placeholder | TypeRef::Error | TypeRef::TypeParam(_) => {}
|
||||
};
|
||||
}
|
||||
|
||||
fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef), map: &TypesMap) {
|
||||
fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef), map: &ExpressionStore) {
|
||||
if let Some(type_ref) = path.type_anchor() {
|
||||
go(type_ref, f, map);
|
||||
}
|
||||
|
|
@ -448,71 +257,8 @@ impl TypeRef {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_bounds_from_ast(
|
||||
lower_ctx: &mut LowerCtx<'_>,
|
||||
type_bounds_opt: Option<ast::TypeBoundList>,
|
||||
) -> ThinVec<TypeBound> {
|
||||
if let Some(type_bounds) = type_bounds_opt {
|
||||
ThinVec::from_iter(Vec::from_iter(
|
||||
type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)),
|
||||
))
|
||||
} else {
|
||||
ThinVec::from_iter([])
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeBound {
|
||||
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::TypeBound) -> Self {
|
||||
let mut lower_path_type = |path_type: &ast::PathType| ctx.lower_path(path_type.path()?);
|
||||
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
let m = match node.question_mark_token() {
|
||||
Some(_) => TraitBoundModifier::Maybe,
|
||||
None => TraitBoundModifier::None,
|
||||
};
|
||||
lower_path_type(&path_type)
|
||||
.map(|p| {
|
||||
TypeBound::Path(ctx.alloc_path(p, AstPtr::new(&path_type).upcast()), m)
|
||||
})
|
||||
.unwrap_or(TypeBound::Error)
|
||||
}
|
||||
ast::TypeBoundKind::ForType(for_type) => {
|
||||
let lt_refs = match for_type.generic_param_list() {
|
||||
Some(gpl) => gpl
|
||||
.lifetime_params()
|
||||
.flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<)))
|
||||
.collect(),
|
||||
None => Box::default(),
|
||||
};
|
||||
let path = for_type.ty().and_then(|ty| match &ty {
|
||||
ast::Type::PathType(path_type) => lower_path_type(path_type).map(|p| (p, ty)),
|
||||
_ => None,
|
||||
});
|
||||
match path {
|
||||
Some((p, ty)) => {
|
||||
TypeBound::ForLifetime(lt_refs, ctx.alloc_path(p, AstPtr::new(&ty)))
|
||||
}
|
||||
None => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
ast::TypeBoundKind::Use(gal) => TypeBound::Use(
|
||||
gal.use_bound_generic_args()
|
||||
.map(|p| match p {
|
||||
ast::UseBoundGenericArg::Lifetime(l) => {
|
||||
UseArgRef::Lifetime(LifetimeRef::new(&l))
|
||||
}
|
||||
ast::UseBoundGenericArg::NameRef(n) => UseArgRef::Name(n.as_name()),
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
ast::TypeBoundKind::Lifetime(lifetime) => {
|
||||
TypeBound::Lifetime(LifetimeRef::new(&lifetime))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path<'a>(&self, map: &'a TypesMap) -> Option<(&'a Path, TraitBoundModifier)> {
|
||||
pub fn as_path<'a>(&self, map: &'a ExpressionStore) -> Option<(&'a Path, TraitBoundModifier)> {
|
||||
match self {
|
||||
&TypeBound::Path(p, m) => Some((&map[p], m)),
|
||||
&TypeBound::ForLifetime(_, p) => Some((&map[p], TraitBoundModifier::None)),
|
||||
|
|
@ -521,90 +267,9 @@ impl TypeBound {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ConstRef {
|
||||
Scalar(Box<LiteralConstRef>),
|
||||
Path(Name),
|
||||
Complex(AstId<ast::ConstArg>),
|
||||
}
|
||||
|
||||
impl ConstRef {
|
||||
pub(crate) fn from_const_arg(lower_ctx: &LowerCtx<'_>, arg: Option<ast::ConstArg>) -> Self {
|
||||
if let Some(arg) = arg {
|
||||
if let Some(expr) = arg.expr() {
|
||||
return Self::from_expr(expr, Some(lower_ctx.ast_id(&arg)));
|
||||
}
|
||||
}
|
||||
Self::Scalar(Box::new(LiteralConstRef::Unknown))
|
||||
}
|
||||
|
||||
pub(crate) fn from_const_param(
|
||||
lower_ctx: &LowerCtx<'_>,
|
||||
param: &ast::ConstParam,
|
||||
) -> Option<Self> {
|
||||
param.default_val().map(|default| Self::from_const_arg(lower_ctx, Some(default)))
|
||||
}
|
||||
|
||||
pub fn display<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn ExpandDatabase,
|
||||
edition: Edition,
|
||||
) -> impl fmt::Display + 'a {
|
||||
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef, Edition);
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.1 {
|
||||
ConstRef::Scalar(s) => s.fmt(f),
|
||||
ConstRef::Path(n) => n.display(self.0, self.2).fmt(f),
|
||||
ConstRef::Complex(_) => f.write_str("{const}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Display(db, self, edition)
|
||||
}
|
||||
|
||||
// We special case literals and single identifiers, to speed up things.
|
||||
fn from_expr(expr: ast::Expr, ast_id: Option<AstId<ast::ConstArg>>) -> Self {
|
||||
fn is_path_ident(p: &ast::PathExpr) -> bool {
|
||||
let Some(path) = p.path() else {
|
||||
return false;
|
||||
};
|
||||
if path.coloncolon_token().is_some() {
|
||||
return false;
|
||||
}
|
||||
if let Some(s) = path.segment() {
|
||||
if s.coloncolon_token().is_some() || s.generic_arg_list().is_some() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
match expr {
|
||||
ast::Expr::PathExpr(p) if is_path_ident(&p) => {
|
||||
match p.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
|
||||
Some(it) => Self::Path(it.as_name()),
|
||||
None => Self::Scalar(Box::new(LiteralConstRef::Unknown)),
|
||||
}
|
||||
}
|
||||
ast::Expr::Literal(literal) => Self::Scalar(Box::new(match literal.kind() {
|
||||
ast::LiteralKind::IntNumber(num) => {
|
||||
num.value().map(LiteralConstRef::UInt).unwrap_or(LiteralConstRef::Unknown)
|
||||
}
|
||||
ast::LiteralKind::Char(c) => {
|
||||
c.value().map(LiteralConstRef::Char).unwrap_or(LiteralConstRef::Unknown)
|
||||
}
|
||||
ast::LiteralKind::Bool(f) => LiteralConstRef::Bool(f),
|
||||
_ => LiteralConstRef::Unknown,
|
||||
})),
|
||||
_ => {
|
||||
if let Some(ast_id) = ast_id {
|
||||
Self::Complex(ast_id)
|
||||
} else {
|
||||
Self::Scalar(Box::new(LiteralConstRef::Unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ConstRef {
|
||||
pub expr: ExprId,
|
||||
}
|
||||
|
||||
/// A literal constant value
|
||||
|
|
|
|||
|
|
@ -2,22 +2,22 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use base_db::CrateId;
|
||||
use fst::{raw::IndexedValue, Automaton, Streamer};
|
||||
use base_db::Crate;
|
||||
use fst::{Automaton, Streamer, raw::IndexedValue};
|
||||
use hir_expand::name::Name;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::SmallVec;
|
||||
use span::Edition;
|
||||
use stdx::{format_to, TupleExt};
|
||||
use stdx::format_to;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId,
|
||||
db::DefDatabase,
|
||||
item_scope::{ImportOrExternCrate, ItemInNs},
|
||||
nameres::DefMap,
|
||||
visibility::Visibility,
|
||||
AssocItemId, FxIndexMap, ModuleDefId, ModuleId, TraitId,
|
||||
};
|
||||
|
||||
/// Item import details stored in the `ImportMap`.
|
||||
|
|
@ -31,6 +31,8 @@ pub struct ImportInfo {
|
|||
pub is_doc_hidden: bool,
|
||||
/// Whether this item is annotated with `#[unstable(..)]`.
|
||||
pub is_unstable: bool,
|
||||
/// The value of `#[rust_analyzer::completions(...)]`, if exists.
|
||||
pub complete: Complete,
|
||||
}
|
||||
|
||||
/// A map from publicly exported items to its name.
|
||||
|
|
@ -66,19 +68,14 @@ impl ImportMap {
|
|||
for (k, v) in self.item_to_info_map.iter() {
|
||||
format_to!(out, "{:?} ({:?}) -> ", k, v.1);
|
||||
for v in &v.0 {
|
||||
format_to!(
|
||||
out,
|
||||
"{}:{:?}, ",
|
||||
v.name.display(db.upcast(), Edition::CURRENT),
|
||||
v.container
|
||||
);
|
||||
format_to!(out, "{}:{:?}, ", v.name.display(db, Edition::CURRENT), v.container);
|
||||
}
|
||||
format_to!(out, "\n");
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
|
||||
pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: Crate) -> Arc<Self> {
|
||||
let _p = tracing::info_span!("import_map_query").entered();
|
||||
|
||||
let map = Self::collect_import_map(db, krate);
|
||||
|
|
@ -129,7 +126,7 @@ impl ImportMap {
|
|||
self.item_to_info_map.get(&item).map(|(info, _)| &**info)
|
||||
}
|
||||
|
||||
fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMapIndex {
|
||||
fn collect_import_map(db: &dyn DefDatabase, krate: Crate) -> ImportMapIndex {
|
||||
let _p = tracing::info_span!("collect_import_map").entered();
|
||||
|
||||
let def_map = db.crate_def_map(krate);
|
||||
|
|
@ -155,11 +152,7 @@ impl ImportMap {
|
|||
|
||||
let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
|
||||
let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
|
||||
if per_ns.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some((name, per_ns))
|
||||
}
|
||||
if per_ns.is_none() { None } else { Some((name, per_ns)) }
|
||||
});
|
||||
|
||||
for (name, per_ns) in visible_items {
|
||||
|
|
@ -176,16 +169,22 @@ impl ImportMap {
|
|||
ItemInNs::Macros(id) => Some(id.into()),
|
||||
}
|
||||
};
|
||||
let (is_doc_hidden, is_unstable) = attr_id.map_or((false, false), |attr_id| {
|
||||
let attrs = db.attrs(attr_id);
|
||||
(attrs.has_doc_hidden(), attrs.is_unstable())
|
||||
});
|
||||
let (is_doc_hidden, is_unstable, do_not_complete) = match attr_id {
|
||||
None => (false, false, Complete::Yes),
|
||||
Some(attr_id) => {
|
||||
let attrs = db.attrs(attr_id);
|
||||
let do_not_complete =
|
||||
Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), &attrs);
|
||||
(attrs.has_doc_hidden(), attrs.is_unstable(), do_not_complete)
|
||||
}
|
||||
};
|
||||
|
||||
let import_info = ImportInfo {
|
||||
name: name.clone(),
|
||||
container: module,
|
||||
is_doc_hidden,
|
||||
is_unstable,
|
||||
complete: do_not_complete,
|
||||
};
|
||||
|
||||
if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
|
||||
|
|
@ -222,7 +221,7 @@ impl ImportMap {
|
|||
trait_import_info: &ImportInfo,
|
||||
) {
|
||||
let _p = tracing::info_span!("collect_trait_assoc_items").entered();
|
||||
for &(ref assoc_item_name, item) in &db.trait_data(tr).items {
|
||||
for &(ref assoc_item_name, item) in &db.trait_items(tr).items {
|
||||
let module_def_id = match item {
|
||||
AssocItemId::FunctionId(f) => ModuleDefId::from(f),
|
||||
AssocItemId::ConstId(c) => ModuleDefId::from(c),
|
||||
|
|
@ -239,12 +238,17 @@ impl ImportMap {
|
|||
ItemInNs::Values(module_def_id)
|
||||
};
|
||||
|
||||
let attrs = &db.attrs(item.into());
|
||||
let attr_id = item.into();
|
||||
let attrs = &db.attrs(attr_id);
|
||||
let item_do_not_complete = Complete::extract(false, attrs);
|
||||
let do_not_complete =
|
||||
Complete::for_trait_item(trait_import_info.complete, item_do_not_complete);
|
||||
let assoc_item_info = ImportInfo {
|
||||
container: trait_import_info.container,
|
||||
name: assoc_item_name.clone(),
|
||||
is_doc_hidden: attrs.has_doc_hidden(),
|
||||
is_unstable: attrs.is_unstable(),
|
||||
complete: do_not_complete,
|
||||
};
|
||||
|
||||
let (infos, _) =
|
||||
|
|
@ -400,15 +404,13 @@ impl Query {
|
|||
/// This returns a list of items that could be imported from dependencies of `krate`.
|
||||
pub fn search_dependencies(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
query: &Query,
|
||||
) -> FxHashSet<ItemInNs> {
|
||||
) -> FxHashSet<(ItemInNs, Complete)> {
|
||||
let _p = tracing::info_span!("search_dependencies", ?query).entered();
|
||||
|
||||
let graph = db.crate_graph();
|
||||
|
||||
let import_maps: Vec<_> =
|
||||
graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
|
||||
krate.data(db).dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
|
||||
|
||||
let mut op = fst::map::OpBuilder::new();
|
||||
|
||||
|
|
@ -445,7 +447,7 @@ fn search_maps(
|
|||
import_maps: &[Arc<ImportMap>],
|
||||
mut stream: fst::map::Union<'_>,
|
||||
query: &Query,
|
||||
) -> FxHashSet<ItemInNs> {
|
||||
) -> FxHashSet<(ItemInNs, Complete)> {
|
||||
let mut res = FxHashSet::default();
|
||||
while let Some((_, indexed_values)) = stream.next() {
|
||||
for &IndexedValue { index: import_map_idx, value } in indexed_values {
|
||||
|
|
@ -465,8 +467,9 @@ fn search_maps(
|
|||
})
|
||||
.filter(|&(_, info)| {
|
||||
query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str())
|
||||
});
|
||||
res.extend(iter.map(TupleExt::head));
|
||||
})
|
||||
.map(|(item, import_info)| (item, import_info.complete));
|
||||
res.extend(iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -475,11 +478,11 @@ fn search_maps(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base_db::{SourceDatabase, Upcast};
|
||||
use expect_test::{expect, Expect};
|
||||
use base_db::RootQueryDb;
|
||||
use expect_test::{Expect, expect};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{test_db::TestDB, ItemContainerId, Lookup};
|
||||
use crate::{ItemContainerId, Lookup, test_db::TestDB};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -512,21 +515,23 @@ mod tests {
|
|||
expect: Expect,
|
||||
) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let crate_graph = db.crate_graph();
|
||||
let krate = crate_graph
|
||||
let all_crates = db.all_crates();
|
||||
let krate = all_crates
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|&krate| {
|
||||
crate_graph[krate]
|
||||
krate
|
||||
.extra_data(&db)
|
||||
.display_name
|
||||
.as_ref()
|
||||
.is_some_and(|it| it.crate_name().as_str() == crate_name)
|
||||
})
|
||||
.expect("could not find crate");
|
||||
|
||||
let actual = search_dependencies(db.upcast(), krate, &query)
|
||||
let actual = search_dependencies(&db, krate, &query)
|
||||
.into_iter()
|
||||
.filter_map(|dependency| {
|
||||
let dependency_krate = dependency.krate(db.upcast())?;
|
||||
.filter_map(|(dependency, _)| {
|
||||
let dependency_krate = dependency.krate(&db)?;
|
||||
let dependency_imports = db.import_map(dependency_krate);
|
||||
|
||||
let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
|
||||
|
|
@ -545,7 +550,7 @@ mod tests {
|
|||
|
||||
Some(format!(
|
||||
"{}::{} ({})\n",
|
||||
crate_graph[dependency_krate].display_name.as_ref()?,
|
||||
dependency_krate.extra_data(&db).display_name.as_ref()?,
|
||||
path,
|
||||
mark
|
||||
))
|
||||
|
|
@ -575,8 +580,8 @@ mod tests {
|
|||
|
||||
let trait_info = dependency_imports.import_info_for(ItemInNs::Types(trait_id.into()))?;
|
||||
|
||||
let trait_data = db.trait_data(trait_id);
|
||||
let (assoc_item_name, _) = trait_data
|
||||
let trait_items = db.trait_items(trait_id);
|
||||
let (assoc_item_name, _) = trait_items
|
||||
.items
|
||||
.iter()
|
||||
.find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?;
|
||||
|
|
@ -584,23 +589,24 @@ mod tests {
|
|||
Some(format!(
|
||||
"{}::{}",
|
||||
render_path(db, &trait_info[0]),
|
||||
assoc_item_name.display(db.upcast(), Edition::CURRENT)
|
||||
assoc_item_name.display(db, Edition::CURRENT)
|
||||
))
|
||||
}
|
||||
|
||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let crate_graph = db.crate_graph();
|
||||
let all_crates = db.all_crates();
|
||||
|
||||
let actual = crate_graph
|
||||
let actual = all_crates
|
||||
.iter()
|
||||
.copied()
|
||||
.filter_map(|krate| {
|
||||
let cdata = &crate_graph[krate];
|
||||
let cdata = &krate.extra_data(&db);
|
||||
let name = cdata.display_name.as_ref()?;
|
||||
|
||||
let map = db.import_map(krate);
|
||||
|
||||
Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
|
||||
Some(format!("{name}:\n{}\n", map.fmt_for_test(&db)))
|
||||
})
|
||||
.sorted()
|
||||
.collect::<String>();
|
||||
|
|
@ -623,7 +629,7 @@ mod tests {
|
|||
module = parent;
|
||||
}
|
||||
|
||||
segments.iter().rev().map(|it| it.display(db.upcast(), Edition::CURRENT)).join("::")
|
||||
segments.iter().rev().map(|it| it.display(db, Edition::CURRENT)).join("::")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -3,23 +3,23 @@
|
|||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
|
||||
use base_db::Crate;
|
||||
use hir_expand::{AstId, MacroCallId, attrs::AttrId, db::ExpandDatabase, name::Name};
|
||||
use indexmap::map::Entry;
|
||||
use itertools::Itertools;
|
||||
use la_arena::Idx;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use span::Edition;
|
||||
use stdx::format_to;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
|
||||
LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
|
||||
db::DefDatabase,
|
||||
per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
|
||||
visibility::{Visibility, VisibilityExplicitness},
|
||||
AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
|
||||
LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -358,7 +358,7 @@ impl ItemScope {
|
|||
}
|
||||
|
||||
/// Get a name from current module scope, legacy macros are not included
|
||||
pub(crate) fn get(&self, name: &Name) -> PerNs {
|
||||
pub fn get(&self, name: &Name) -> PerNs {
|
||||
PerNs {
|
||||
types: self.types.get(name).copied(),
|
||||
values: self.values.get(name).copied(),
|
||||
|
|
@ -453,7 +453,7 @@ impl ItemScope {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn macro_invoc(&self, call: AstId<ast::MacroCall>) -> Option<MacroCallId> {
|
||||
pub fn macro_invoc(&self, call: AstId<ast::MacroCall>) -> Option<MacroCallId> {
|
||||
self.macro_invocations.get(&call).copied()
|
||||
}
|
||||
|
||||
|
|
@ -916,7 +916,7 @@ impl ItemInNs {
|
|||
}
|
||||
|
||||
/// Returns the crate defining this item (or `None` if `self` is built-in).
|
||||
pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
|
||||
pub fn krate(&self, db: &dyn DefDatabase) -> Option<Crate> {
|
||||
match self {
|
||||
ItemInNs::Types(id) | ItemInNs::Values(id) => id.module(db).map(|m| m.krate),
|
||||
ItemInNs::Macros(id) => Some(id.module(db).krate),
|
||||
|
|
|
|||
|
|
@ -44,27 +44,23 @@ use std::{
|
|||
};
|
||||
|
||||
use ast::{AstNode, StructKind};
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{attrs::RawAttrs, name::Name, ExpandTo, HirFileId, InFile};
|
||||
use base_db::Crate;
|
||||
use hir_expand::{
|
||||
ExpandTo, HirFileId, InFile,
|
||||
attrs::RawAttrs,
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::Name,
|
||||
};
|
||||
use intern::{Interned, Symbol};
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::{AstIdNode, Edition, FileAstId, SyntaxContextId};
|
||||
use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
|
||||
use stdx::never;
|
||||
use syntax::{ast, match_ast, SyntaxKind};
|
||||
use syntax::{SyntaxKind, ast, match_ast};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
generics::GenericParams,
|
||||
path::{GenericArgs, ImportAlias, ModPath, Path, PathKind},
|
||||
type_ref::{Mutability, TraitRef, TypeBound, TypeRefId, TypesMap, TypesSourceMap},
|
||||
visibility::{RawVisibility, VisibilityExplicitness},
|
||||
BlockId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
|
||||
};
|
||||
use crate::{BlockId, Lookup, attr::Attrs, db::DefDatabase};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct RawVisibilityId(u32);
|
||||
|
|
@ -100,23 +96,16 @@ pub struct ItemTree {
|
|||
|
||||
impl ItemTree {
|
||||
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
|
||||
db.file_item_tree_with_source_map(file_id).0
|
||||
}
|
||||
|
||||
pub(crate) fn file_item_tree_with_source_map_query(
|
||||
db: &dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
|
||||
let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
|
||||
static EMPTY: OnceLock<(Arc<ItemTree>, Arc<ItemTreeSourceMaps>)> = OnceLock::new();
|
||||
static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
|
||||
|
||||
let ctx = lower::Ctx::new(db, file_id);
|
||||
let syntax = db.parse_or_expand(file_id);
|
||||
let mut top_attrs = None;
|
||||
let (mut item_tree, source_maps) = match_ast! {
|
||||
let mut item_tree = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(file) => {
|
||||
top_attrs = Some(RawAttrs::new(db.upcast(), &file, ctx.span_map()));
|
||||
top_attrs = Some(RawAttrs::new(db, &file, ctx.span_map()));
|
||||
ctx.lower_module_items(&file)
|
||||
},
|
||||
ast::MacroItems(items) => {
|
||||
|
|
@ -143,55 +132,42 @@ impl ItemTree {
|
|||
{
|
||||
EMPTY
|
||||
.get_or_init(|| {
|
||||
(
|
||||
Arc::new(ItemTree {
|
||||
top_level: SmallVec::new_const(),
|
||||
attrs: FxHashMap::default(),
|
||||
data: None,
|
||||
}),
|
||||
Arc::default(),
|
||||
)
|
||||
Arc::new(ItemTree {
|
||||
top_level: SmallVec::new_const(),
|
||||
attrs: FxHashMap::default(),
|
||||
data: None,
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
} else {
|
||||
item_tree.shrink_to_fit();
|
||||
(Arc::new(item_tree), Arc::new(source_maps))
|
||||
Arc::new(item_tree)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
|
||||
db.block_item_tree_with_source_map(block).0
|
||||
}
|
||||
|
||||
pub(crate) fn block_item_tree_with_source_map_query(
|
||||
db: &dyn DefDatabase,
|
||||
block: BlockId,
|
||||
) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
|
||||
let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
|
||||
static EMPTY: OnceLock<(Arc<ItemTree>, Arc<ItemTreeSourceMaps>)> = OnceLock::new();
|
||||
static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
|
||||
|
||||
let loc = block.lookup(db);
|
||||
let block = loc.ast_id.to_node(db.upcast());
|
||||
let block = loc.ast_id.to_node(db);
|
||||
|
||||
let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
|
||||
let (mut item_tree, source_maps) = ctx.lower_block(&block);
|
||||
let mut item_tree = ctx.lower_block(&block);
|
||||
if item_tree.data.is_none() && item_tree.top_level.is_empty() && item_tree.attrs.is_empty()
|
||||
{
|
||||
EMPTY
|
||||
.get_or_init(|| {
|
||||
(
|
||||
Arc::new(ItemTree {
|
||||
top_level: SmallVec::new_const(),
|
||||
attrs: FxHashMap::default(),
|
||||
data: None,
|
||||
}),
|
||||
Arc::default(),
|
||||
)
|
||||
Arc::new(ItemTree {
|
||||
top_level: SmallVec::new_const(),
|
||||
attrs: FxHashMap::default(),
|
||||
data: None,
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
} else {
|
||||
item_tree.shrink_to_fit();
|
||||
(Arc::new(item_tree), Arc::new(source_maps))
|
||||
Arc::new(item_tree)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +178,7 @@ impl ItemTree {
|
|||
}
|
||||
|
||||
/// Returns the inner attributes of the source file.
|
||||
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
|
||||
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
|
||||
Attrs::filter(
|
||||
db,
|
||||
krate,
|
||||
|
|
@ -214,10 +190,26 @@ impl ItemTree {
|
|||
self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
|
||||
}
|
||||
|
||||
pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
|
||||
pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: Crate, of: AttrOwner) -> Attrs {
|
||||
Attrs::filter(db, krate, self.raw_attrs(of).clone())
|
||||
}
|
||||
|
||||
/// Returns a count of a few, expensive items.
|
||||
///
|
||||
/// For more detail, see [`ItemTreeDataStats`].
|
||||
pub fn item_tree_stats(&self) -> ItemTreeDataStats {
|
||||
match self.data {
|
||||
Some(ref data) => ItemTreeDataStats {
|
||||
traits: data.traits.len(),
|
||||
impls: data.impls.len(),
|
||||
mods: data.mods.len(),
|
||||
macro_calls: data.macro_calls.len(),
|
||||
macro_rules: data.macro_rules.len(),
|
||||
},
|
||||
None => ItemTreeDataStats::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self, db: &dyn DefDatabase, edition: Edition) -> String {
|
||||
pretty::print_item_tree(db, self, edition)
|
||||
}
|
||||
|
|
@ -231,7 +223,10 @@ impl ItemTree {
|
|||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
if let Some(data) = &mut self.data {
|
||||
let ItemTree { top_level, attrs, data } = self;
|
||||
top_level.shrink_to_fit();
|
||||
attrs.shrink_to_fit();
|
||||
if let Some(data) = data {
|
||||
let ItemTreeData {
|
||||
uses,
|
||||
extern_crates,
|
||||
|
|
@ -329,157 +324,12 @@ struct ItemTreeData {
|
|||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
pub struct ItemTreeSourceMaps {
|
||||
all_concatenated: Box<[TypesSourceMap]>,
|
||||
structs_offset: u32,
|
||||
unions_offset: u32,
|
||||
enum_generics_offset: u32,
|
||||
variants_offset: u32,
|
||||
consts_offset: u32,
|
||||
statics_offset: u32,
|
||||
trait_generics_offset: u32,
|
||||
trait_alias_generics_offset: u32,
|
||||
impls_offset: u32,
|
||||
type_aliases_offset: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GenericItemSourceMap<'a>(&'a [TypesSourceMap; 2]);
|
||||
|
||||
impl<'a> GenericItemSourceMap<'a> {
|
||||
#[inline]
|
||||
pub fn item(self) -> &'a TypesSourceMap {
|
||||
&self.0[0]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn generics(self) -> &'a TypesSourceMap {
|
||||
&self.0[1]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
pub struct GenericItemSourceMapBuilder {
|
||||
pub item: TypesSourceMap,
|
||||
pub generics: TypesSourceMap,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
struct ItemTreeSourceMapsBuilder {
|
||||
functions: Vec<GenericItemSourceMapBuilder>,
|
||||
structs: Vec<GenericItemSourceMapBuilder>,
|
||||
unions: Vec<GenericItemSourceMapBuilder>,
|
||||
enum_generics: Vec<TypesSourceMap>,
|
||||
variants: Vec<TypesSourceMap>,
|
||||
consts: Vec<TypesSourceMap>,
|
||||
statics: Vec<TypesSourceMap>,
|
||||
trait_generics: Vec<TypesSourceMap>,
|
||||
trait_alias_generics: Vec<TypesSourceMap>,
|
||||
impls: Vec<GenericItemSourceMapBuilder>,
|
||||
type_aliases: Vec<GenericItemSourceMapBuilder>,
|
||||
}
|
||||
|
||||
impl ItemTreeSourceMapsBuilder {
|
||||
fn build(self) -> ItemTreeSourceMaps {
|
||||
let ItemTreeSourceMapsBuilder {
|
||||
functions,
|
||||
structs,
|
||||
unions,
|
||||
enum_generics,
|
||||
variants,
|
||||
consts,
|
||||
statics,
|
||||
trait_generics,
|
||||
trait_alias_generics,
|
||||
impls,
|
||||
type_aliases,
|
||||
} = self;
|
||||
let structs_offset = functions.len() as u32 * 2;
|
||||
let unions_offset = structs_offset + (structs.len() as u32 * 2);
|
||||
let enum_generics_offset = unions_offset + (unions.len() as u32 * 2);
|
||||
let variants_offset = enum_generics_offset + (enum_generics.len() as u32);
|
||||
let consts_offset = variants_offset + (variants.len() as u32);
|
||||
let statics_offset = consts_offset + (consts.len() as u32);
|
||||
let trait_generics_offset = statics_offset + (statics.len() as u32);
|
||||
let trait_alias_generics_offset = trait_generics_offset + (trait_generics.len() as u32);
|
||||
let impls_offset = trait_alias_generics_offset + (trait_alias_generics.len() as u32);
|
||||
let type_aliases_offset = impls_offset + (impls.len() as u32 * 2);
|
||||
let all_concatenated = generics_concat(functions)
|
||||
.chain(generics_concat(structs))
|
||||
.chain(generics_concat(unions))
|
||||
.chain(enum_generics)
|
||||
.chain(variants)
|
||||
.chain(consts)
|
||||
.chain(statics)
|
||||
.chain(trait_generics)
|
||||
.chain(trait_alias_generics)
|
||||
.chain(generics_concat(impls))
|
||||
.chain(generics_concat(type_aliases))
|
||||
.collect();
|
||||
return ItemTreeSourceMaps {
|
||||
all_concatenated,
|
||||
structs_offset,
|
||||
unions_offset,
|
||||
enum_generics_offset,
|
||||
variants_offset,
|
||||
consts_offset,
|
||||
statics_offset,
|
||||
trait_generics_offset,
|
||||
trait_alias_generics_offset,
|
||||
impls_offset,
|
||||
type_aliases_offset,
|
||||
};
|
||||
|
||||
fn generics_concat(
|
||||
source_maps: Vec<GenericItemSourceMapBuilder>,
|
||||
) -> impl Iterator<Item = TypesSourceMap> {
|
||||
source_maps.into_iter().flat_map(|it| [it.item, it.generics])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemTreeSourceMaps {
|
||||
#[inline]
|
||||
fn generic_item(&self, offset: u32, index: u32) -> GenericItemSourceMap<'_> {
|
||||
GenericItemSourceMap(
|
||||
self.all_concatenated[(offset + (index * 2)) as usize..][..2].try_into().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn non_generic_item(&self, offset: u32, index: u32) -> &TypesSourceMap {
|
||||
&self.all_concatenated[(offset + index) as usize]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn function(&self, index: FileItemTreeId<Function>) -> GenericItemSourceMap<'_> {
|
||||
self.generic_item(0, index.0.into_raw().into_u32())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! index_item_source_maps {
|
||||
( $( $name:ident; $field:ident[$tree_id:ident]; $fn:ident; $ret:ty, )* ) => {
|
||||
impl ItemTreeSourceMaps {
|
||||
$(
|
||||
#[inline]
|
||||
pub fn $name(&self, index: FileItemTreeId<$tree_id>) -> $ret {
|
||||
self.$fn(self.$field, index.0.into_raw().into_u32())
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
index_item_source_maps! {
|
||||
strukt; structs_offset[Struct]; generic_item; GenericItemSourceMap<'_>,
|
||||
union; unions_offset[Union]; generic_item; GenericItemSourceMap<'_>,
|
||||
enum_generic; enum_generics_offset[Enum]; non_generic_item; &TypesSourceMap,
|
||||
variant; variants_offset[Variant]; non_generic_item; &TypesSourceMap,
|
||||
konst; consts_offset[Const]; non_generic_item; &TypesSourceMap,
|
||||
statik; statics_offset[Static]; non_generic_item; &TypesSourceMap,
|
||||
trait_generic; trait_generics_offset[Trait]; non_generic_item; &TypesSourceMap,
|
||||
trait_alias_generic; trait_alias_generics_offset[TraitAlias]; non_generic_item; &TypesSourceMap,
|
||||
impl_; impls_offset[Impl]; generic_item; GenericItemSourceMap<'_>,
|
||||
type_alias; type_aliases_offset[TypeAlias]; generic_item; GenericItemSourceMap<'_>,
|
||||
pub struct ItemTreeDataStats {
|
||||
pub traits: usize,
|
||||
pub impls: usize,
|
||||
pub mods: usize,
|
||||
pub macro_calls: usize,
|
||||
pub macro_rules: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
|
|
@ -490,10 +340,8 @@ pub enum AttrOwner {
|
|||
TopLevel,
|
||||
|
||||
Variant(FileItemTreeId<Variant>),
|
||||
// while not relevant to early name resolution, fields can contain visibility
|
||||
Field(FieldParent, ItemTreeFieldId),
|
||||
Param(FileItemTreeId<Function>, ItemTreeParamId),
|
||||
TypeOrConstParamData(GenericModItem, LocalTypeOrConstParamId),
|
||||
LifetimeParamData(GenericModItem, LocalLifetimeParamId),
|
||||
}
|
||||
|
||||
impl AttrOwner {
|
||||
|
|
@ -506,10 +354,9 @@ impl AttrOwner {
|
|||
pub enum FieldParent {
|
||||
Struct(FileItemTreeId<Struct>),
|
||||
Union(FileItemTreeId<Union>),
|
||||
Variant(FileItemTreeId<Variant>),
|
||||
EnumVariant(FileItemTreeId<Variant>),
|
||||
}
|
||||
|
||||
pub type ItemTreeParamId = Idx<Param>;
|
||||
pub type ItemTreeFieldId = Idx<Field>;
|
||||
|
||||
macro_rules! from_attrs {
|
||||
|
|
@ -536,9 +383,6 @@ pub trait ItemTreeNode: Clone {
|
|||
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
|
||||
fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner;
|
||||
}
|
||||
pub trait GenericsItemTreeNode: ItemTreeNode {
|
||||
fn generic_params(&self) -> &Arc<GenericParams>;
|
||||
}
|
||||
|
||||
pub struct FileItemTreeId<N>(Idx<N>);
|
||||
|
||||
|
|
@ -591,7 +435,7 @@ pub struct TreeId {
|
|||
}
|
||||
|
||||
impl TreeId {
|
||||
pub(crate) fn new(file: HirFileId, block: Option<BlockId>) -> Self {
|
||||
pub fn new(file: HirFileId, block: Option<BlockId>) -> Self {
|
||||
Self { file, block }
|
||||
}
|
||||
|
||||
|
|
@ -602,16 +446,6 @@ impl TreeId {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn item_tree_with_source_map(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
|
||||
match self.block {
|
||||
Some(block) => db.block_item_tree_with_source_map(block),
|
||||
None => db.file_item_tree_with_source_map(self.file),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_id(self) -> HirFileId {
|
||||
self.file
|
||||
}
|
||||
|
|
@ -644,13 +478,6 @@ impl<N> ItemTreeId<N> {
|
|||
self.tree.item_tree(db)
|
||||
}
|
||||
|
||||
pub fn item_tree_with_source_map(
|
||||
self,
|
||||
db: &dyn DefDatabase,
|
||||
) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
|
||||
self.tree.item_tree_with_source_map(db)
|
||||
}
|
||||
|
||||
pub fn resolved<R>(self, db: &dyn DefDatabase, cb: impl FnOnce(&N) -> R) -> R
|
||||
where
|
||||
ItemTree: Index<FileItemTreeId<N>, Output = N>,
|
||||
|
|
@ -682,7 +509,7 @@ impl<N> Hash for ItemTreeId<N> {
|
|||
}
|
||||
|
||||
macro_rules! mod_items {
|
||||
( $( $typ:ident $(<$generic_params:ident>)? in $fld:ident -> $ast:ty ),+ $(,)? ) => {
|
||||
( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ModItem {
|
||||
$(
|
||||
|
|
@ -690,16 +517,6 @@ macro_rules! mod_items {
|
|||
)+
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum GenericModItem {
|
||||
$(
|
||||
$(
|
||||
#[cfg_attr(ignore_fragment, $generic_params)]
|
||||
$typ(FileItemTreeId<$typ>),
|
||||
)?
|
||||
)+
|
||||
}
|
||||
|
||||
impl ModItem {
|
||||
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
|
||||
match self {
|
||||
|
|
@ -708,52 +525,12 @@ macro_rules! mod_items {
|
|||
}
|
||||
}
|
||||
|
||||
impl GenericModItem {
|
||||
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::AnyHasGenericParams> {
|
||||
match self {
|
||||
$(
|
||||
$(
|
||||
#[cfg_attr(ignore_fragment, $generic_params)]
|
||||
GenericModItem::$typ(it) => tree[it.index()].ast_id().upcast(),
|
||||
)?
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenericModItem> for ModItem {
|
||||
fn from(id: GenericModItem) -> ModItem {
|
||||
match id {
|
||||
$(
|
||||
$(
|
||||
#[cfg_attr(ignore_fragment, $generic_params)]
|
||||
GenericModItem::$typ(id) => ModItem::$typ(id),
|
||||
)?
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenericModItem> for AttrOwner {
|
||||
fn from(t: GenericModItem) -> AttrOwner {
|
||||
AttrOwner::ModItem(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<FileItemTreeId<$typ>> for ModItem {
|
||||
fn from(id: FileItemTreeId<$typ>) -> ModItem {
|
||||
ModItem::$typ(id)
|
||||
}
|
||||
}
|
||||
$(
|
||||
#[cfg_attr(ignore_fragment, $generic_params)]
|
||||
impl From<FileItemTreeId<$typ>> for GenericModItem {
|
||||
fn from(id: FileItemTreeId<$typ>) -> GenericModItem {
|
||||
GenericModItem::$typ(id)
|
||||
}
|
||||
}
|
||||
)?
|
||||
)+
|
||||
|
||||
$(
|
||||
|
|
@ -780,14 +557,6 @@ macro_rules! mod_items {
|
|||
&self.data().$fld[index]
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl GenericsItemTreeNode for $typ {
|
||||
fn generic_params(&self) -> &Arc<GenericParams> {
|
||||
&self.$generic_params
|
||||
}
|
||||
}
|
||||
)?
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
|
@ -796,16 +565,16 @@ mod_items! {
|
|||
Use in uses -> ast::Use,
|
||||
ExternCrate in extern_crates -> ast::ExternCrate,
|
||||
ExternBlock in extern_blocks -> ast::ExternBlock,
|
||||
Function<explicit_generic_params> in functions -> ast::Fn,
|
||||
Struct<generic_params> in structs -> ast::Struct,
|
||||
Union<generic_params> in unions -> ast::Union,
|
||||
Enum<generic_params> in enums -> ast::Enum,
|
||||
Function in functions -> ast::Fn,
|
||||
Struct in structs -> ast::Struct,
|
||||
Union in unions -> ast::Union,
|
||||
Enum in enums -> ast::Enum,
|
||||
Const in consts -> ast::Const,
|
||||
Static in statics -> ast::Static,
|
||||
Trait<generic_params> in traits -> ast::Trait,
|
||||
TraitAlias<generic_params> in trait_aliases -> ast::TraitAlias,
|
||||
Impl<generic_params> in impls -> ast::Impl,
|
||||
TypeAlias<generic_params> in type_aliases -> ast::TypeAlias,
|
||||
Trait in traits -> ast::Trait,
|
||||
TraitAlias in trait_aliases -> ast::TraitAlias,
|
||||
Impl in impls -> ast::Impl,
|
||||
TypeAlias in type_aliases -> ast::TypeAlias,
|
||||
Mod in mods -> ast::Module,
|
||||
MacroCall in macro_calls -> ast::MacroCall,
|
||||
MacroRules in macro_rules -> ast::MacroRules,
|
||||
|
|
@ -881,6 +650,34 @@ pub struct UseTree {
|
|||
kind: UseTreeKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ImportAlias {
|
||||
/// Unnamed alias, as in `use Foo as _;`
|
||||
Underscore,
|
||||
/// Named alias
|
||||
Alias(Name),
|
||||
}
|
||||
|
||||
impl ImportAlias {
|
||||
pub fn display(&self, edition: Edition) -> impl fmt::Display + '_ {
|
||||
ImportAliasDisplay { value: self, edition }
|
||||
}
|
||||
}
|
||||
|
||||
struct ImportAliasDisplay<'a> {
|
||||
value: &'a ImportAlias,
|
||||
edition: Edition,
|
||||
}
|
||||
|
||||
impl fmt::Display for ImportAliasDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.value {
|
||||
ImportAlias::Underscore => f.write_str("_"),
|
||||
ImportAlias::Alias(name) => fmt::Display::fmt(&name.display_no_db(self.edition), f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum UseTreeKind {
|
||||
/// ```ignore
|
||||
|
|
@ -921,66 +718,30 @@ pub struct ExternBlock {
|
|||
pub struct Function {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub explicit_generic_params: Arc<GenericParams>,
|
||||
pub abi: Option<Symbol>,
|
||||
pub params: Box<[Param]>,
|
||||
pub ret_type: TypeRefId,
|
||||
pub ast_id: FileAstId<ast::Fn>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
pub(crate) flags: FnFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Param {
|
||||
pub type_ref: Option<TypeRefId>,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub(crate) struct FnFlags: u16 {
|
||||
const HAS_SELF_PARAM = 1 << 0;
|
||||
const HAS_BODY = 1 << 1;
|
||||
const HAS_DEFAULT_KW = 1 << 2;
|
||||
const HAS_CONST_KW = 1 << 3;
|
||||
const HAS_ASYNC_KW = 1 << 4;
|
||||
const HAS_UNSAFE_KW = 1 << 5;
|
||||
const IS_VARARGS = 1 << 6;
|
||||
const HAS_SAFE_KW = 1 << 7;
|
||||
/// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
|
||||
/// but keeping it for all functions will consume a lot of memory when there are
|
||||
/// only very few functions with it. So we only encode its existence here, and lookup
|
||||
/// it if needed.
|
||||
const HAS_TARGET_FEATURE = 1 << 8;
|
||||
const DEPRECATED_SAFE_2024 = 1 << 9;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Struct {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub fields: Box<[Field]>,
|
||||
pub shape: FieldsShape,
|
||||
pub ast_id: FileAstId<ast::Struct>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Union {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub fields: Box<[Field]>,
|
||||
pub ast_id: FileAstId<ast::Union>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Enum {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub variants: Range<FileItemTreeId<Variant>>,
|
||||
pub ast_id: FileAstId<ast::Enum>,
|
||||
}
|
||||
|
|
@ -991,7 +752,6 @@ pub struct Variant {
|
|||
pub fields: Box<[Field]>,
|
||||
pub shape: FieldsShape,
|
||||
pub ast_id: FileAstId<ast::Variant>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
|
|
@ -1001,12 +761,38 @@ pub enum FieldsShape {
|
|||
Unit,
|
||||
}
|
||||
|
||||
/// Visibility of an item, not yet resolved.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum RawVisibility {
|
||||
/// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
|
||||
/// equivalent to `pub(self)`.
|
||||
Module(Interned<ModPath>, VisibilityExplicitness),
|
||||
/// `pub`.
|
||||
Public,
|
||||
}
|
||||
|
||||
/// Whether the item was imported through an explicit `pub(crate) use` or just a `use` without
|
||||
/// visibility.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum VisibilityExplicitness {
|
||||
Explicit,
|
||||
Implicit,
|
||||
}
|
||||
|
||||
impl VisibilityExplicitness {
|
||||
pub fn is_explicit(&self) -> bool {
|
||||
matches!(self, Self::Explicit)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove this from item tree?
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Field {
|
||||
pub name: Name,
|
||||
pub type_ref: TypeRefId,
|
||||
pub visibility: RawVisibilityId,
|
||||
// FIXME: Not an item tree property
|
||||
pub is_unsafe: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
@ -1014,32 +800,20 @@ pub struct Const {
|
|||
/// `None` for `const _: () = ();`
|
||||
pub name: Option<Name>,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub type_ref: TypeRefId,
|
||||
pub ast_id: FileAstId<ast::Const>,
|
||||
pub has_body: bool,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Static {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
// TODO: use bitflags when we have more flags
|
||||
pub mutable: bool,
|
||||
pub has_safe_kw: bool,
|
||||
pub has_unsafe_kw: bool,
|
||||
pub type_ref: TypeRefId,
|
||||
pub ast_id: FileAstId<ast::Static>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Trait {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub is_auto: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub items: Box<[AssocItem]>,
|
||||
pub ast_id: FileAstId<ast::Trait>,
|
||||
}
|
||||
|
|
@ -1048,32 +822,20 @@ pub struct Trait {
|
|||
pub struct TraitAlias {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub ast_id: FileAstId<ast::TraitAlias>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Impl {
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub target_trait: Option<TraitRef>,
|
||||
pub self_ty: TypeRefId,
|
||||
pub is_negative: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub items: Box<[AssocItem]>,
|
||||
pub ast_id: FileAstId<ast::Impl>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeAlias {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
/// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
|
||||
pub bounds: Box<[TypeBound]>,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub type_ref: Option<TypeRefId>,
|
||||
pub ast_id: FileAstId<ast::TypeAlias>,
|
||||
pub types_map: Arc<TypesMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
@ -1098,7 +860,7 @@ pub struct MacroCall {
|
|||
pub path: Interned<ModPath>,
|
||||
pub ast_id: FileAstId<ast::MacroCall>,
|
||||
pub expand_to: ExpandTo,
|
||||
pub ctxt: SyntaxContextId,
|
||||
pub ctxt: SyntaxContext,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
@ -1126,7 +888,7 @@ impl Use {
|
|||
) -> ast::UseTree {
|
||||
// Re-lower the AST item and get the source map.
|
||||
// Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
|
||||
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
||||
let ast = InFile::new(file_id, self.ast_id).to_node(db);
|
||||
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
||||
let (_, source_map) = lower::lower_use_tree(db, ast_use_tree, &mut |range| {
|
||||
db.span_map(file_id).span_for_range(range).ctx
|
||||
|
|
@ -1143,7 +905,7 @@ impl Use {
|
|||
) -> Arena<ast::UseTree> {
|
||||
// Re-lower the AST item and get the source map.
|
||||
// Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
|
||||
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
||||
let ast = InFile::new(file_id, self.ast_id).to_node(db);
|
||||
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
||||
lower::lower_use_tree(db, ast_use_tree, &mut |range| {
|
||||
db.span_map(file_id).span_for_range(range).ctx
|
||||
|
|
|
|||
|
|
@ -3,42 +3,29 @@
|
|||
use std::{cell::OnceCell, collections::hash_map::Entry};
|
||||
|
||||
use hir_expand::{
|
||||
mod_path::path,
|
||||
HirFileId,
|
||||
mod_path::PathKind,
|
||||
name::AsName,
|
||||
span_map::{SpanMap, SpanMapRef},
|
||||
HirFileId,
|
||||
};
|
||||
use intern::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::{AstIdMap, SyntaxContextId};
|
||||
use stdx::thin_vec::ThinVec;
|
||||
use span::{AstIdMap, SyntaxContext};
|
||||
use syntax::{
|
||||
ast::{self, HasModuleItem, HasName, HasTypeBounds, IsString},
|
||||
AstNode,
|
||||
ast::{self, HasModuleItem, HasName, IsString},
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance},
|
||||
item_tree::{
|
||||
AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldParent,
|
||||
FieldsShape, FileItemTreeId, FnFlags, Function, GenericArgs, GenericItemSourceMapBuilder,
|
||||
GenericModItem, Idx, Impl, ImportAlias, Interned, ItemTree, ItemTreeData,
|
||||
ItemTreeSourceMaps, ItemTreeSourceMapsBuilder, Macro2, MacroCall, MacroRules, Mod, ModItem,
|
||||
ModKind, ModPath, Mutability, Name, Param, Path, Range, RawAttrs, RawIdx, RawVisibilityId,
|
||||
Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind,
|
||||
Variant,
|
||||
AssocItem, AttrOwner, Const, Enum, ExternBlock, ExternCrate, Field, FieldParent,
|
||||
FieldsShape, FileItemTreeId, Function, Idx, Impl, ImportAlias, Interned, ItemTree,
|
||||
ItemTreeData, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, ModPath, Name, Range,
|
||||
RawAttrs, RawIdx, RawVisibility, RawVisibilityId, Static, Struct, StructKind, Trait,
|
||||
TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, Variant, VisibilityExplicitness,
|
||||
},
|
||||
lower::LowerCtx,
|
||||
path::AssociatedTypeBinding,
|
||||
type_ref::{
|
||||
LifetimeRef, PathId, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
|
||||
TypesMap, TypesSourceMap,
|
||||
},
|
||||
visibility::RawVisibility,
|
||||
LocalLifetimeParamId, LocalTypeOrConstParamId,
|
||||
};
|
||||
|
||||
fn id<N>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||
|
|
@ -49,11 +36,8 @@ pub(super) struct Ctx<'a> {
|
|||
db: &'a dyn DefDatabase,
|
||||
tree: ItemTree,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
generic_param_attr_buffer:
|
||||
FxHashMap<Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, RawAttrs>,
|
||||
span_map: OnceCell<SpanMap>,
|
||||
file: HirFileId,
|
||||
source_maps: ItemTreeSourceMapsBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Ctx<'a> {
|
||||
|
|
@ -61,11 +45,9 @@ impl<'a> Ctx<'a> {
|
|||
Self {
|
||||
db,
|
||||
tree: ItemTree::default(),
|
||||
generic_param_attr_buffer: FxHashMap::default(),
|
||||
source_ast_id_map: db.ast_id_map(file),
|
||||
file,
|
||||
span_map: OnceCell::new(),
|
||||
source_maps: ItemTreeSourceMapsBuilder::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,39 +55,13 @@ impl<'a> Ctx<'a> {
|
|||
self.span_map.get_or_init(|| self.db.span_map(self.file)).as_ref()
|
||||
}
|
||||
|
||||
fn body_ctx<'b, 'c>(
|
||||
&self,
|
||||
types_map: &'b mut TypesMap,
|
||||
types_source_map: &'b mut TypesSourceMap,
|
||||
) -> LowerCtx<'c>
|
||||
where
|
||||
'a: 'c,
|
||||
'b: 'c,
|
||||
{
|
||||
// FIXME: This seems a bit wasteful that if `LowerCtx` will initialize the span map we won't benefit.
|
||||
LowerCtx::with_span_map_cell(
|
||||
self.db,
|
||||
self.file,
|
||||
self.span_map.clone(),
|
||||
types_map,
|
||||
types_source_map,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn lower_module_items(
|
||||
mut self,
|
||||
item_owner: &dyn HasModuleItem,
|
||||
) -> (ItemTree, ItemTreeSourceMaps) {
|
||||
pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
|
||||
self.tree.top_level =
|
||||
item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
|
||||
assert!(self.generic_param_attr_buffer.is_empty());
|
||||
(self.tree, self.source_maps.build())
|
||||
self.tree
|
||||
}
|
||||
|
||||
pub(super) fn lower_macro_stmts(
|
||||
mut self,
|
||||
stmts: ast::MacroStmts,
|
||||
) -> (ItemTree, ItemTreeSourceMaps) {
|
||||
pub(super) fn lower_macro_stmts(mut self, stmts: ast::MacroStmts) -> ItemTree {
|
||||
self.tree.top_level = stmts
|
||||
.statements()
|
||||
.filter_map(|stmt| {
|
||||
|
|
@ -135,14 +91,11 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
assert!(self.generic_param_attr_buffer.is_empty());
|
||||
(self.tree, self.source_maps.build())
|
||||
self.tree
|
||||
}
|
||||
|
||||
pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> (ItemTree, ItemTreeSourceMaps) {
|
||||
self.tree
|
||||
.attrs
|
||||
.insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.span_map()));
|
||||
pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
|
||||
self.tree.attrs.insert(AttrOwner::TopLevel, RawAttrs::new(self.db, block, self.span_map()));
|
||||
self.tree.top_level = block
|
||||
.statements()
|
||||
.filter_map(|stmt| match stmt {
|
||||
|
|
@ -164,8 +117,7 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
assert!(self.generic_param_attr_buffer.is_empty());
|
||||
(self.tree, self.source_maps.build())
|
||||
self.tree
|
||||
}
|
||||
|
||||
fn data(&mut self) -> &mut ItemTreeData {
|
||||
|
|
@ -192,7 +144,7 @@ impl<'a> Ctx<'a> {
|
|||
ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
|
||||
ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
|
||||
};
|
||||
let attrs = RawAttrs::new(self.db.upcast(), item, self.span_map());
|
||||
let attrs = RawAttrs::new(self.db, item, self.span_map());
|
||||
self.add_attrs(mod_item.into(), attrs);
|
||||
|
||||
Some(mod_item)
|
||||
|
|
@ -218,7 +170,7 @@ impl<'a> Ctx<'a> {
|
|||
ast::AssocItem::Const(ast) => Some(self.lower_const(ast).into()),
|
||||
ast::AssocItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
|
||||
}?;
|
||||
let attrs = RawAttrs::new(self.db.upcast(), item_node, self.span_map());
|
||||
let attrs = RawAttrs::new(self.db, item_node, self.span_map());
|
||||
self.add_attrs(
|
||||
match item {
|
||||
AssocItem::Function(it) => AttrOwner::ModItem(ModItem::Function(it)),
|
||||
|
|
@ -232,31 +184,13 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
|
||||
fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
let visibility = self.lower_visibility(strukt);
|
||||
let name = strukt.name()?.as_name();
|
||||
let ast_id = self.source_ast_id_map.ast_id(strukt);
|
||||
let (fields, kind, attrs) = self.lower_fields(&strukt.kind(), &mut body_ctx);
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::No, strukt);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = Struct {
|
||||
name,
|
||||
visibility,
|
||||
generic_params,
|
||||
fields,
|
||||
shape: kind,
|
||||
ast_id,
|
||||
types_map: Arc::new(types_map),
|
||||
};
|
||||
let (fields, kind, attrs) = self.lower_fields(&strukt.kind());
|
||||
let res = Struct { name, visibility, fields, shape: kind, ast_id };
|
||||
let id = id(self.data().structs.alloc(res));
|
||||
self.source_maps.structs.push(GenericItemSourceMapBuilder {
|
||||
item: types_source_map,
|
||||
generics: generics_source_map,
|
||||
});
|
||||
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(
|
||||
AttrOwner::Field(
|
||||
|
|
@ -266,14 +200,12 @@ impl<'a> Ctx<'a> {
|
|||
attr,
|
||||
);
|
||||
}
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
fn lower_fields(
|
||||
&mut self,
|
||||
strukt_kind: &ast::StructKind,
|
||||
body_ctx: &mut LowerCtx<'_>,
|
||||
) -> (Box<[Field]>, FieldsShape, Vec<(usize, RawAttrs)>) {
|
||||
match strukt_kind {
|
||||
ast::StructKind::Record(it) => {
|
||||
|
|
@ -281,9 +213,9 @@ impl<'a> Ctx<'a> {
|
|||
let mut attrs = vec![];
|
||||
|
||||
for (i, field) in it.fields().enumerate() {
|
||||
let data = self.lower_record_field(&field, body_ctx);
|
||||
let data = self.lower_record_field(&field);
|
||||
fields.push(data);
|
||||
let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
|
||||
let attr = RawAttrs::new(self.db, &field, self.span_map());
|
||||
if !attr.is_empty() {
|
||||
attrs.push((i, attr))
|
||||
}
|
||||
|
|
@ -295,9 +227,9 @@ impl<'a> Ctx<'a> {
|
|||
let mut attrs = vec![];
|
||||
|
||||
for (i, field) in it.fields().enumerate() {
|
||||
let data = self.lower_tuple_field(i, &field, body_ctx);
|
||||
let data = self.lower_tuple_field(i, &field);
|
||||
fields.push(data);
|
||||
let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
|
||||
let attr = RawAttrs::new(self.db, &field, self.span_map());
|
||||
if !attr.is_empty() {
|
||||
attrs.push((i, attr))
|
||||
}
|
||||
|
|
@ -308,63 +240,32 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_record_field(
|
||||
&mut self,
|
||||
field: &ast::RecordField,
|
||||
body_ctx: &mut LowerCtx<'_>,
|
||||
) -> Field {
|
||||
fn lower_record_field(&mut self, field: &ast::RecordField) -> Field {
|
||||
let name = match field.name() {
|
||||
Some(name) => name.as_name(),
|
||||
None => Name::missing(),
|
||||
};
|
||||
let visibility = self.lower_visibility(field);
|
||||
let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty());
|
||||
|
||||
Field { name, type_ref, visibility }
|
||||
Field { name, visibility, is_unsafe: field.unsafe_token().is_some() }
|
||||
}
|
||||
|
||||
fn lower_tuple_field(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
field: &ast::TupleField,
|
||||
body_ctx: &mut LowerCtx<'_>,
|
||||
) -> Field {
|
||||
fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleField) -> Field {
|
||||
let name = Name::new_tuple_field(idx);
|
||||
let visibility = self.lower_visibility(field);
|
||||
let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty());
|
||||
Field { name, type_ref, visibility }
|
||||
Field { name, visibility, is_unsafe: false }
|
||||
}
|
||||
|
||||
fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
let visibility = self.lower_visibility(union);
|
||||
let name = union.name()?.as_name();
|
||||
let ast_id = self.source_ast_id_map.ast_id(union);
|
||||
let (fields, _, attrs) = match union.record_field_list() {
|
||||
Some(record_field_list) => {
|
||||
self.lower_fields(&StructKind::Record(record_field_list), &mut body_ctx)
|
||||
}
|
||||
Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
|
||||
None => (Box::default(), FieldsShape::Record, Vec::default()),
|
||||
};
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::No, union);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = Union {
|
||||
name,
|
||||
visibility,
|
||||
generic_params,
|
||||
fields,
|
||||
ast_id,
|
||||
types_map: Arc::new(types_map),
|
||||
};
|
||||
let res = Union { name, visibility, fields, ast_id };
|
||||
let id = id(self.data().unions.alloc(res));
|
||||
self.source_maps.unions.push(GenericItemSourceMapBuilder {
|
||||
item: types_source_map,
|
||||
generics: generics_source_map,
|
||||
});
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(
|
||||
AttrOwner::Field(
|
||||
|
|
@ -374,7 +275,6 @@ impl<'a> Ctx<'a> {
|
|||
attr,
|
||||
);
|
||||
}
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
|
|
@ -388,12 +288,8 @@ impl<'a> Ctx<'a> {
|
|||
FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx())
|
||||
}
|
||||
};
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::No, enum_);
|
||||
let res = Enum { name, visibility, generic_params, variants, ast_id };
|
||||
let res = Enum { name, visibility, variants, ast_id };
|
||||
let id = id(self.data().enums.alloc(res));
|
||||
self.source_maps.enum_generics.push(generics_source_map);
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
|
|
@ -401,34 +297,25 @@ impl<'a> Ctx<'a> {
|
|||
let start = self.next_variant_idx();
|
||||
for variant in variants.variants() {
|
||||
let idx = self.lower_variant(&variant);
|
||||
self.add_attrs(
|
||||
id(idx).into(),
|
||||
RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
|
||||
);
|
||||
self.add_attrs(id(idx).into(), RawAttrs::new(self.db, &variant, self.span_map()));
|
||||
}
|
||||
let end = self.next_variant_idx();
|
||||
FileItemTreeId(start)..FileItemTreeId(end)
|
||||
}
|
||||
|
||||
fn lower_variant(&mut self, variant: &ast::Variant) -> Idx<Variant> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
let name = match variant.name() {
|
||||
Some(name) => name.as_name(),
|
||||
None => Name::missing(),
|
||||
};
|
||||
let (fields, kind, attrs) = self.lower_fields(&variant.kind(), &mut body_ctx);
|
||||
let (fields, kind, attrs) = self.lower_fields(&variant.kind());
|
||||
let ast_id = self.source_ast_id_map.ast_id(variant);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = Variant { name, fields, shape: kind, ast_id, types_map: Arc::new(types_map) };
|
||||
let res = Variant { name, fields, shape: kind, ast_id };
|
||||
let id = self.data().variants.alloc(res);
|
||||
self.source_maps.variants.push(types_source_map);
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(
|
||||
AttrOwner::Field(
|
||||
FieldParent::Variant(FileItemTreeId(id)),
|
||||
FieldParent::EnumVariant(FileItemTreeId(id)),
|
||||
Idx::from_raw(RawIdx::from_u32(idx as u32)),
|
||||
),
|
||||
attr,
|
||||
|
|
@ -438,144 +325,14 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
|
||||
fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
|
||||
let visibility = self.lower_visibility(func);
|
||||
let name = func.name()?.as_name();
|
||||
|
||||
let mut has_self_param = false;
|
||||
let mut has_var_args = false;
|
||||
let mut params = vec![];
|
||||
let mut attrs = vec![];
|
||||
let mut push_attr = |idx, attr: RawAttrs| {
|
||||
if !attr.is_empty() {
|
||||
attrs.push((idx, attr))
|
||||
}
|
||||
};
|
||||
if let Some(param_list) = func.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
push_attr(
|
||||
params.len(),
|
||||
RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
|
||||
);
|
||||
let self_type = match self_param.ty() {
|
||||
Some(type_ref) => TypeRef::from_ast(&mut body_ctx, type_ref),
|
||||
None => {
|
||||
let self_type = body_ctx.alloc_type_ref_desugared(TypeRef::Path(
|
||||
Name::new_symbol_root(sym::Self_.clone()).into(),
|
||||
));
|
||||
match self_param.kind() {
|
||||
ast::SelfParamKind::Owned => self_type,
|
||||
ast::SelfParamKind::Ref => body_ctx.alloc_type_ref_desugared(
|
||||
TypeRef::Reference(Box::new(RefType {
|
||||
ty: self_type,
|
||||
lifetime: self_param.lifetime().as_ref().map(LifetimeRef::new),
|
||||
mutability: Mutability::Shared,
|
||||
})),
|
||||
),
|
||||
ast::SelfParamKind::MutRef => body_ctx.alloc_type_ref_desugared(
|
||||
TypeRef::Reference(Box::new(RefType {
|
||||
ty: self_type,
|
||||
lifetime: self_param.lifetime().as_ref().map(LifetimeRef::new),
|
||||
mutability: Mutability::Mut,
|
||||
})),
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
params.push(Param { type_ref: Some(self_type) });
|
||||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
push_attr(params.len(), RawAttrs::new(self.db.upcast(), ¶m, self.span_map()));
|
||||
let param = match param.dotdotdot_token() {
|
||||
Some(_) => {
|
||||
has_var_args = true;
|
||||
Param { type_ref: None }
|
||||
}
|
||||
None => {
|
||||
let type_ref = TypeRef::from_ast_opt(&mut body_ctx, param.ty());
|
||||
Param { type_ref: Some(type_ref) }
|
||||
}
|
||||
};
|
||||
params.push(param);
|
||||
}
|
||||
}
|
||||
|
||||
let ret_type = match func.ret_type() {
|
||||
Some(rt) => match rt.ty() {
|
||||
Some(type_ref) => TypeRef::from_ast(&mut body_ctx, type_ref),
|
||||
None if rt.thin_arrow_token().is_some() => body_ctx.alloc_error_type(),
|
||||
None => body_ctx.alloc_type_ref_desugared(TypeRef::unit()),
|
||||
},
|
||||
None => body_ctx.alloc_type_ref_desugared(TypeRef::unit()),
|
||||
};
|
||||
|
||||
let ret_type = if func.async_token().is_some() {
|
||||
let future_impl = desugar_future_path(&mut body_ctx, ret_type);
|
||||
let ty_bound = TypeBound::Path(future_impl, TraitBoundModifier::None);
|
||||
body_ctx.alloc_type_ref_desugared(TypeRef::ImplTrait(ThinVec::from_iter([ty_bound])))
|
||||
} else {
|
||||
ret_type
|
||||
};
|
||||
|
||||
let abi = func.abi().map(lower_abi);
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(func);
|
||||
|
||||
let mut flags = FnFlags::default();
|
||||
if func.body().is_some() {
|
||||
flags |= FnFlags::HAS_BODY;
|
||||
}
|
||||
if has_self_param {
|
||||
flags |= FnFlags::HAS_SELF_PARAM;
|
||||
}
|
||||
if func.default_token().is_some() {
|
||||
flags |= FnFlags::HAS_DEFAULT_KW;
|
||||
}
|
||||
if func.const_token().is_some() {
|
||||
flags |= FnFlags::HAS_CONST_KW;
|
||||
}
|
||||
if func.async_token().is_some() {
|
||||
flags |= FnFlags::HAS_ASYNC_KW;
|
||||
}
|
||||
if func.unsafe_token().is_some() {
|
||||
flags |= FnFlags::HAS_UNSAFE_KW;
|
||||
}
|
||||
if func.safe_token().is_some() {
|
||||
flags |= FnFlags::HAS_SAFE_KW;
|
||||
}
|
||||
if has_var_args {
|
||||
flags |= FnFlags::IS_VARARGS;
|
||||
}
|
||||
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::No, func);
|
||||
let res = Function {
|
||||
name,
|
||||
visibility,
|
||||
explicit_generic_params: generic_params,
|
||||
abi,
|
||||
params: params.into_boxed_slice(),
|
||||
ret_type,
|
||||
ast_id,
|
||||
types_map: Arc::new(types_map),
|
||||
flags,
|
||||
};
|
||||
let res = Function { name, visibility, ast_id };
|
||||
|
||||
let id = id(self.data().functions.alloc(res));
|
||||
self.source_maps.functions.push(GenericItemSourceMapBuilder {
|
||||
item: types_source_map,
|
||||
generics: generics_source_map,
|
||||
});
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(AttrOwner::Param(id, Idx::from_raw(RawIdx::from_u32(idx as u32))), attr);
|
||||
}
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
|
|
@ -583,82 +340,27 @@ impl<'a> Ctx<'a> {
|
|||
&mut self,
|
||||
type_alias: &ast::TypeAlias,
|
||||
) -> Option<FileItemTreeId<TypeAlias>> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
let name = type_alias.name()?.as_name();
|
||||
let type_ref = type_alias.ty().map(|it| TypeRef::from_ast(&mut body_ctx, it));
|
||||
let visibility = self.lower_visibility(type_alias);
|
||||
let bounds = self.lower_type_bounds(type_alias, &mut body_ctx);
|
||||
let ast_id = self.source_ast_id_map.ast_id(type_alias);
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::No, type_alias);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = TypeAlias {
|
||||
name,
|
||||
visibility,
|
||||
bounds,
|
||||
generic_params,
|
||||
type_ref,
|
||||
ast_id,
|
||||
types_map: Arc::new(types_map),
|
||||
};
|
||||
let res = TypeAlias { name, visibility, ast_id };
|
||||
let id = id(self.data().type_aliases.alloc(res));
|
||||
self.source_maps.type_aliases.push(GenericItemSourceMapBuilder {
|
||||
item: types_source_map,
|
||||
generics: generics_source_map,
|
||||
});
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
let name = static_.name()?.as_name();
|
||||
let type_ref = TypeRef::from_ast_opt(&mut body_ctx, static_.ty());
|
||||
let visibility = self.lower_visibility(static_);
|
||||
let mutable = static_.mut_token().is_some();
|
||||
let has_safe_kw = static_.safe_token().is_some();
|
||||
let has_unsafe_kw = static_.unsafe_token().is_some();
|
||||
let ast_id = self.source_ast_id_map.ast_id(static_);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = Static {
|
||||
name,
|
||||
visibility,
|
||||
mutable,
|
||||
type_ref,
|
||||
ast_id,
|
||||
has_safe_kw,
|
||||
has_unsafe_kw,
|
||||
types_map: Arc::new(types_map),
|
||||
};
|
||||
self.source_maps.statics.push(types_source_map);
|
||||
let res = Static { name, visibility, ast_id };
|
||||
Some(id(self.data().statics.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
let name = konst.name().map(|it| it.as_name());
|
||||
let type_ref = TypeRef::from_ast_opt(&mut body_ctx, konst.ty());
|
||||
let visibility = self.lower_visibility(konst);
|
||||
let ast_id = self.source_ast_id_map.ast_id(konst);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = Const {
|
||||
name,
|
||||
visibility,
|
||||
type_ref,
|
||||
ast_id,
|
||||
has_body: konst.body().is_some(),
|
||||
types_map: Arc::new(types_map),
|
||||
};
|
||||
self.source_maps.consts.push(types_source_map);
|
||||
let res = Const { name, visibility, ast_id };
|
||||
id(self.data().consts.alloc(res))
|
||||
}
|
||||
|
||||
|
|
@ -687,8 +389,6 @@ impl<'a> Ctx<'a> {
|
|||
let name = trait_def.name()?.as_name();
|
||||
let visibility = self.lower_visibility(trait_def);
|
||||
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
||||
let is_auto = trait_def.auto_token().is_some();
|
||||
let is_unsafe = trait_def.unsafe_token().is_some();
|
||||
|
||||
let items = trait_def
|
||||
.assoc_item_list()
|
||||
|
|
@ -697,12 +397,8 @@ impl<'a> Ctx<'a> {
|
|||
.filter_map(|item_node| self.lower_assoc_item(&item_node))
|
||||
.collect();
|
||||
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
|
||||
let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
|
||||
let def = Trait { name, visibility, items, ast_id };
|
||||
let id = id(self.data().traits.alloc(def));
|
||||
self.source_maps.trait_generics.push(generics_source_map);
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
|
|
@ -713,32 +409,14 @@ impl<'a> Ctx<'a> {
|
|||
let name = trait_alias_def.name()?.as_name();
|
||||
let visibility = self.lower_visibility(trait_alias_def);
|
||||
let ast_id = self.source_ast_id_map.ast_id(trait_alias_def);
|
||||
let (generic_params, generics_source_map) = self.lower_generic_params(
|
||||
HasImplicitSelf::Yes(trait_alias_def.type_bound_list()),
|
||||
trait_alias_def,
|
||||
);
|
||||
|
||||
let alias = TraitAlias { name, visibility, generic_params, ast_id };
|
||||
let alias = TraitAlias { name, visibility, ast_id };
|
||||
let id = id(self.data().trait_aliases.alloc(alias));
|
||||
self.source_maps.trait_alias_generics.push(generics_source_map);
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
fn lower_impl(&mut self, impl_def: &ast::Impl) -> FileItemTreeId<Impl> {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(impl_def);
|
||||
// FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
|
||||
// as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
|
||||
// equals itself.
|
||||
let self_ty = TypeRef::from_ast_opt(&mut body_ctx, impl_def.self_ty());
|
||||
let target_trait = impl_def.trait_().and_then(|tr| TraitRef::from_ast(&mut body_ctx, tr));
|
||||
let is_negative = impl_def.excl_token().is_some();
|
||||
let is_unsafe = impl_def.unsafe_token().is_some();
|
||||
|
||||
// We cannot use `assoc_items()` here as that does not include macro calls.
|
||||
let items = impl_def
|
||||
.assoc_item_list()
|
||||
|
|
@ -748,27 +426,8 @@ impl<'a> Ctx<'a> {
|
|||
.collect();
|
||||
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
|
||||
// type alias rather than a type parameter, so this is handled by the resolver.
|
||||
let (generic_params, generics_source_map) =
|
||||
self.lower_generic_params(HasImplicitSelf::No, impl_def);
|
||||
types_map.shrink_to_fit();
|
||||
types_source_map.shrink_to_fit();
|
||||
let res = Impl {
|
||||
generic_params,
|
||||
target_trait,
|
||||
self_ty,
|
||||
is_negative,
|
||||
is_unsafe,
|
||||
items,
|
||||
ast_id,
|
||||
types_map: Arc::new(types_map),
|
||||
};
|
||||
let id = id(self.data().impls.alloc(res));
|
||||
self.source_maps.impls.push(GenericItemSourceMapBuilder {
|
||||
item: types_source_map,
|
||||
generics: generics_source_map,
|
||||
});
|
||||
self.write_generic_params_attributes(id.into());
|
||||
id
|
||||
let res = Impl { items, ast_id };
|
||||
id(self.data().impls.alloc(res))
|
||||
}
|
||||
|
||||
fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Use>> {
|
||||
|
|
@ -801,7 +460,7 @@ impl<'a> Ctx<'a> {
|
|||
let span_map = self.span_map();
|
||||
let path = m.path()?;
|
||||
let range = path.syntax().text_range();
|
||||
let path = Interned::new(ModPath::from_src(self.db.upcast(), path, &mut |range| {
|
||||
let path = Interned::new(ModPath::from_src(self.db, path, &mut |range| {
|
||||
span_map.span_for_range(range).ctx
|
||||
})?);
|
||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
||||
|
|
@ -844,7 +503,7 @@ impl<'a> Ctx<'a> {
|
|||
ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
|
||||
ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
|
||||
};
|
||||
let attrs = RawAttrs::new(self.db.upcast(), &item, self.span_map());
|
||||
let attrs = RawAttrs::new(self.db, &item, self.span_map());
|
||||
self.add_attrs(mod_item.into(), attrs);
|
||||
Some(mod_item)
|
||||
})
|
||||
|
|
@ -855,75 +514,8 @@ impl<'a> Ctx<'a> {
|
|||
id(self.data().extern_blocks.alloc(res))
|
||||
}
|
||||
|
||||
fn write_generic_params_attributes(&mut self, parent: GenericModItem) {
|
||||
self.generic_param_attr_buffer.drain().for_each(|(idx, attrs)| {
|
||||
self.tree.attrs.insert(
|
||||
match idx {
|
||||
Either::Left(id) => AttrOwner::TypeOrConstParamData(parent, id),
|
||||
Either::Right(id) => AttrOwner::LifetimeParamData(parent, id),
|
||||
},
|
||||
attrs,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_generic_params(
|
||||
&mut self,
|
||||
has_implicit_self: HasImplicitSelf,
|
||||
node: &dyn ast::HasGenericParams,
|
||||
) -> (Arc<GenericParams>, TypesSourceMap) {
|
||||
let (mut types_map, mut types_source_map) =
|
||||
(TypesMap::default(), TypesSourceMap::default());
|
||||
let mut body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
|
||||
debug_assert!(self.generic_param_attr_buffer.is_empty(),);
|
||||
body_ctx.take_impl_traits_bounds();
|
||||
let mut generics = GenericParamsCollector::default();
|
||||
|
||||
if let HasImplicitSelf::Yes(bounds) = has_implicit_self {
|
||||
// Traits and trait aliases get the Self type as an implicit first type parameter.
|
||||
generics.type_or_consts.alloc(
|
||||
TypeParamData {
|
||||
name: Some(Name::new_symbol_root(sym::Self_.clone())),
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
// add super traits as bounds on Self
|
||||
// i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar`
|
||||
let bound_target = Either::Left(body_ctx.alloc_type_ref_desugared(TypeRef::Path(
|
||||
Name::new_symbol_root(sym::Self_.clone()).into(),
|
||||
)));
|
||||
generics.fill_bounds(&mut body_ctx, bounds, bound_target);
|
||||
}
|
||||
|
||||
let span_map = body_ctx.span_map().clone();
|
||||
let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
|
||||
param| {
|
||||
let attrs = RawAttrs::new(self.db.upcast(), ¶m, span_map.as_ref());
|
||||
debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none());
|
||||
};
|
||||
generics.fill(&mut body_ctx, node, add_param_attrs);
|
||||
|
||||
let generics = generics.finish(types_map, &mut types_source_map);
|
||||
(generics, types_source_map)
|
||||
}
|
||||
|
||||
fn lower_type_bounds(
|
||||
&mut self,
|
||||
node: &dyn ast::HasTypeBounds,
|
||||
body_ctx: &mut LowerCtx<'_>,
|
||||
) -> Box<[TypeBound]> {
|
||||
match node.type_bound_list() {
|
||||
Some(bound_list) => {
|
||||
bound_list.bounds().map(|it| TypeBound::from_ast(body_ctx, it)).collect()
|
||||
}
|
||||
None => Box::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
|
||||
let vis = RawVisibility::from_ast(self.db, item.visibility(), &mut |range| {
|
||||
let vis = visibility_from_ast(self.db, item.visibility(), &mut |range| {
|
||||
self.span_map().span_for_range(range).ctx
|
||||
});
|
||||
self.data().vis.alloc(vis)
|
||||
|
|
@ -936,33 +528,11 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn desugar_future_path(ctx: &mut LowerCtx<'_>, orig: TypeRefId) -> PathId {
|
||||
let path = path![core::future::Future];
|
||||
let mut generic_args: Vec<_> =
|
||||
std::iter::repeat(None).take(path.segments().len() - 1).collect();
|
||||
let binding = AssociatedTypeBinding {
|
||||
name: Name::new_symbol_root(sym::Output.clone()),
|
||||
args: None,
|
||||
type_ref: Some(orig),
|
||||
bounds: Box::default(),
|
||||
};
|
||||
generic_args.push(Some(GenericArgs { bindings: Box::new([binding]), ..GenericArgs::empty() }));
|
||||
|
||||
let path = Path::from_known_path(path, generic_args);
|
||||
PathId::from_type_ref_unchecked(ctx.alloc_type_ref_desugared(TypeRef::Path(path)))
|
||||
}
|
||||
|
||||
enum HasImplicitSelf {
|
||||
/// Inner list is a type bound list for the implicit `Self`.
|
||||
Yes(Option<ast::TypeBoundList>),
|
||||
No,
|
||||
}
|
||||
|
||||
fn lower_abi(abi: ast::Abi) -> Symbol {
|
||||
match abi.abi_string() {
|
||||
Some(tok) => Symbol::intern(tok.text_without_quotes()),
|
||||
// `extern` default to be `extern "C"`.
|
||||
_ => sym::C.clone(),
|
||||
_ => sym::C,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -975,7 +545,7 @@ impl UseTreeLowering<'_> {
|
|||
fn lower_use_tree(
|
||||
&mut self,
|
||||
tree: ast::UseTree,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContextId,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
|
||||
) -> Option<UseTree> {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
|
|
@ -984,7 +554,7 @@ impl UseTreeLowering<'_> {
|
|||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||
Some(path) => {
|
||||
match ModPath::from_src(self.db.upcast(), path, span_for_range) {
|
||||
match ModPath::from_src(self.db, path, span_for_range) {
|
||||
Some(it) => Some(it),
|
||||
None => return None, // FIXME: report errors somewhere
|
||||
}
|
||||
|
|
@ -1005,7 +575,7 @@ impl UseTreeLowering<'_> {
|
|||
} else {
|
||||
let is_glob = tree.star_token().is_some();
|
||||
let path = match tree.path() {
|
||||
Some(path) => Some(ModPath::from_src(self.db.upcast(), path, span_for_range)?),
|
||||
Some(path) => Some(ModPath::from_src(self.db, path, span_for_range)?),
|
||||
None => None,
|
||||
};
|
||||
let alias = tree.rename().map(|a| {
|
||||
|
|
@ -1042,9 +612,38 @@ impl UseTreeLowering<'_> {
|
|||
pub(crate) fn lower_use_tree(
|
||||
db: &dyn DefDatabase,
|
||||
tree: ast::UseTree,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContextId,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
|
||||
) -> Option<(UseTree, Arena<ast::UseTree>)> {
|
||||
let mut lowering = UseTreeLowering { db, mapping: Arena::new() };
|
||||
let tree = lowering.lower_use_tree(tree, span_for_range)?;
|
||||
Some((tree, lowering.mapping))
|
||||
}
|
||||
|
||||
fn private_vis() -> RawVisibility {
|
||||
RawVisibility::Module(
|
||||
Interned::new(ModPath::from_kind(PathKind::SELF)),
|
||||
VisibilityExplicitness::Implicit,
|
||||
)
|
||||
}
|
||||
|
||||
fn visibility_from_ast(
|
||||
db: &dyn DefDatabase,
|
||||
node: Option<ast::Visibility>,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
|
||||
) -> RawVisibility {
|
||||
let Some(node) = node else { return private_vis() };
|
||||
let path = match node.kind() {
|
||||
ast::VisibilityKind::In(path) => {
|
||||
let path = ModPath::from_src(db, path, span_for_range);
|
||||
match path {
|
||||
None => return private_vis(),
|
||||
Some(path) => path,
|
||||
}
|
||||
}
|
||||
ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate),
|
||||
ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
|
||||
ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::SELF),
|
||||
ast::VisibilityKind::Pub => return RawVisibility::Public,
|
||||
};
|
||||
RawVisibility::Module(Interned::new(path), VisibilityExplicitness::Explicit)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,12 @@ use la_arena::{Idx, RawIdx};
|
|||
use span::{Edition, ErasedFileAstId};
|
||||
|
||||
use crate::{
|
||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||
item_tree::{
|
||||
AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldParent,
|
||||
FieldsShape, FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl,
|
||||
ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, Path, RawAttrs,
|
||||
RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, Union, Use,
|
||||
UseTree, UseTreeKind, Variant,
|
||||
FieldsShape, FileItemTreeId, Function, Impl, ItemTree, Macro2, MacroCall, MacroRules, Mod,
|
||||
ModItem, ModKind, RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias,
|
||||
Union, Use, UseTree, UseTreeKind, Variant,
|
||||
},
|
||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||
type_ref::{TypeRefId, TypesMap},
|
||||
visibility::RawVisibility,
|
||||
};
|
||||
|
||||
|
|
@ -100,7 +96,7 @@ impl Printer<'_> {
|
|||
self,
|
||||
"#{}[{}{}]{}",
|
||||
inner,
|
||||
attr.path.display(self.db.upcast(), self.edition),
|
||||
attr.path.display(self.db, self.edition),
|
||||
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
||||
separated_by,
|
||||
);
|
||||
|
|
@ -116,34 +112,30 @@ impl Printer<'_> {
|
|||
fn print_visibility(&mut self, vis: RawVisibilityId) {
|
||||
match &self.tree[vis] {
|
||||
RawVisibility::Module(path, _expl) => {
|
||||
w!(self, "pub({}) ", path.display(self.db.upcast(), self.edition))
|
||||
w!(self, "pub({}) ", path.display(self.db, self.edition))
|
||||
}
|
||||
RawVisibility::Public => w!(self, "pub "),
|
||||
};
|
||||
}
|
||||
|
||||
fn print_fields(
|
||||
&mut self,
|
||||
parent: FieldParent,
|
||||
kind: FieldsShape,
|
||||
fields: &[Field],
|
||||
map: &TypesMap,
|
||||
) {
|
||||
fn print_fields(&mut self, parent: FieldParent, kind: FieldsShape, fields: &[Field]) {
|
||||
let edition = self.edition;
|
||||
match kind {
|
||||
FieldsShape::Record => {
|
||||
self.whitespace();
|
||||
w!(self, "{{");
|
||||
self.indented(|this| {
|
||||
for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() {
|
||||
for (idx, Field { name, visibility, is_unsafe }) in fields.iter().enumerate() {
|
||||
this.print_attrs_of(
|
||||
AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
|
||||
"\n",
|
||||
);
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name.display(self.db.upcast(), edition));
|
||||
this.print_type_ref(*type_ref, map);
|
||||
wln!(this, ",");
|
||||
if *is_unsafe {
|
||||
w!(this, "unsafe ");
|
||||
}
|
||||
|
||||
wln!(this, "{},", name.display(self.db, edition));
|
||||
}
|
||||
});
|
||||
w!(self, "}}");
|
||||
|
|
@ -151,15 +143,16 @@ impl Printer<'_> {
|
|||
FieldsShape::Tuple => {
|
||||
w!(self, "(");
|
||||
self.indented(|this| {
|
||||
for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() {
|
||||
for (idx, Field { name, visibility, is_unsafe }) in fields.iter().enumerate() {
|
||||
this.print_attrs_of(
|
||||
AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
|
||||
"\n",
|
||||
);
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name.display(self.db.upcast(), edition));
|
||||
this.print_type_ref(*type_ref, map);
|
||||
wln!(this, ",");
|
||||
if *is_unsafe {
|
||||
w!(this, "unsafe ");
|
||||
}
|
||||
wln!(this, "{},", name.display(self.db, edition));
|
||||
}
|
||||
});
|
||||
w!(self, ")");
|
||||
|
|
@ -168,49 +161,23 @@ impl Printer<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_fields_and_where_clause(
|
||||
&mut self,
|
||||
parent: FieldParent,
|
||||
kind: FieldsShape,
|
||||
fields: &[Field],
|
||||
params: &GenericParams,
|
||||
map: &TypesMap,
|
||||
) {
|
||||
match kind {
|
||||
FieldsShape::Record => {
|
||||
if self.print_where_clause(params) {
|
||||
wln!(self);
|
||||
}
|
||||
self.print_fields(parent, kind, fields, map);
|
||||
}
|
||||
FieldsShape::Unit => {
|
||||
self.print_where_clause(params);
|
||||
self.print_fields(parent, kind, fields, map);
|
||||
}
|
||||
FieldsShape::Tuple => {
|
||||
self.print_fields(parent, kind, fields, map);
|
||||
self.print_where_clause(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
||||
match &use_tree.kind {
|
||||
UseTreeKind::Single { path, alias } => {
|
||||
w!(self, "{}", path.display(self.db.upcast(), self.edition));
|
||||
w!(self, "{}", path.display(self.db, self.edition));
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias.display(self.edition));
|
||||
}
|
||||
}
|
||||
UseTreeKind::Glob { path } => {
|
||||
if let Some(path) = path {
|
||||
w!(self, "{}::", path.display(self.db.upcast(), self.edition));
|
||||
w!(self, "{}::", path.display(self.db, self.edition));
|
||||
}
|
||||
w!(self, "*");
|
||||
}
|
||||
UseTreeKind::Prefixed { prefix, list } => {
|
||||
if let Some(prefix) = prefix {
|
||||
w!(self, "{}::", prefix.display(self.db.upcast(), self.edition));
|
||||
w!(self, "{}::", prefix.display(self.db, self.edition));
|
||||
}
|
||||
w!(self, "{{");
|
||||
for (i, tree) in list.iter().enumerate() {
|
||||
|
|
@ -240,7 +207,7 @@ impl Printer<'_> {
|
|||
let ExternCrate { name, alias, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "extern crate {}", name.display(self.db.upcast(), self.edition));
|
||||
w!(self, "extern crate {}", name.display(self.db, self.edition));
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias.display(self.edition));
|
||||
}
|
||||
|
|
@ -262,89 +229,17 @@ impl Printer<'_> {
|
|||
wln!(self, "}}");
|
||||
}
|
||||
ModItem::Function(it) => {
|
||||
let Function {
|
||||
name,
|
||||
visibility,
|
||||
explicit_generic_params,
|
||||
abi,
|
||||
params,
|
||||
ret_type,
|
||||
ast_id,
|
||||
types_map,
|
||||
flags,
|
||||
} = &self.tree[it];
|
||||
let Function { name, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
if flags.contains(FnFlags::HAS_DEFAULT_KW) {
|
||||
w!(self, "default ");
|
||||
}
|
||||
if flags.contains(FnFlags::HAS_CONST_KW) {
|
||||
w!(self, "const ");
|
||||
}
|
||||
if flags.contains(FnFlags::HAS_ASYNC_KW) {
|
||||
w!(self, "async ");
|
||||
}
|
||||
if flags.contains(FnFlags::HAS_UNSAFE_KW) {
|
||||
w!(self, "unsafe ");
|
||||
}
|
||||
if flags.contains(FnFlags::HAS_SAFE_KW) {
|
||||
w!(self, "safe ");
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
w!(self, "extern \"{}\" ", abi);
|
||||
}
|
||||
w!(self, "fn {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(explicit_generic_params, it.into());
|
||||
w!(self, "(");
|
||||
if !params.is_empty() {
|
||||
self.indented(|this| {
|
||||
for (idx, Param { type_ref }) in params.iter().enumerate() {
|
||||
this.print_attrs_of(
|
||||
AttrOwner::Param(it, Idx::from_raw(RawIdx::from(idx as u32))),
|
||||
"\n",
|
||||
);
|
||||
if idx == 0 && flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
w!(this, "self: ");
|
||||
}
|
||||
if let Some(type_ref) = type_ref {
|
||||
this.print_type_ref(*type_ref, types_map);
|
||||
} else {
|
||||
wln!(this, "...");
|
||||
}
|
||||
wln!(this, ",");
|
||||
}
|
||||
});
|
||||
}
|
||||
w!(self, ") -> ");
|
||||
self.print_type_ref(*ret_type, types_map);
|
||||
self.print_where_clause(explicit_generic_params);
|
||||
if flags.contains(FnFlags::HAS_BODY) {
|
||||
wln!(self, " {{ ... }}");
|
||||
} else {
|
||||
wln!(self, ";");
|
||||
}
|
||||
wln!(self, "fn {};", name.display(self.db, self.edition));
|
||||
}
|
||||
ModItem::Struct(it) => {
|
||||
let Struct {
|
||||
visibility,
|
||||
name,
|
||||
fields,
|
||||
shape: kind,
|
||||
generic_params,
|
||||
ast_id,
|
||||
types_map,
|
||||
} = &self.tree[it];
|
||||
let Struct { visibility, name, fields, shape: kind, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "struct {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
self.print_fields_and_where_clause(
|
||||
FieldParent::Struct(it),
|
||||
*kind,
|
||||
fields,
|
||||
generic_params,
|
||||
types_map,
|
||||
);
|
||||
w!(self, "struct {}", name.display(self.db, self.edition));
|
||||
self.print_fields(FieldParent::Struct(it), *kind, fields);
|
||||
if matches!(kind, FieldsShape::Record) {
|
||||
wln!(self);
|
||||
} else {
|
||||
|
|
@ -352,98 +247,56 @@ impl Printer<'_> {
|
|||
}
|
||||
}
|
||||
ModItem::Union(it) => {
|
||||
let Union { name, visibility, fields, generic_params, ast_id, types_map } =
|
||||
&self.tree[it];
|
||||
let Union { name, visibility, fields, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "union {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
self.print_fields_and_where_clause(
|
||||
FieldParent::Union(it),
|
||||
FieldsShape::Record,
|
||||
fields,
|
||||
generic_params,
|
||||
types_map,
|
||||
);
|
||||
w!(self, "union {}", name.display(self.db, self.edition));
|
||||
self.print_fields(FieldParent::Union(it), FieldsShape::Record, fields);
|
||||
wln!(self);
|
||||
}
|
||||
ModItem::Enum(it) => {
|
||||
let Enum { name, visibility, variants, generic_params, ast_id } = &self.tree[it];
|
||||
let Enum { name, visibility, variants, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "enum {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
w!(self, "enum {}", name.display(self.db, self.edition));
|
||||
let edition = self.edition;
|
||||
self.indented(|this| {
|
||||
for variant in FileItemTreeId::range_iter(variants.clone()) {
|
||||
let Variant { name, fields, shape: kind, ast_id, types_map } =
|
||||
&this.tree[variant];
|
||||
let Variant { name, fields, shape: kind, ast_id } = &this.tree[variant];
|
||||
this.print_ast_id(ast_id.erase());
|
||||
this.print_attrs_of(variant, "\n");
|
||||
w!(this, "{}", name.display(self.db.upcast(), edition));
|
||||
this.print_fields(FieldParent::Variant(variant), *kind, fields, types_map);
|
||||
w!(this, "{}", name.display(self.db, edition));
|
||||
this.print_fields(FieldParent::EnumVariant(variant), *kind, fields);
|
||||
wln!(this, ",");
|
||||
}
|
||||
});
|
||||
wln!(self, "}}");
|
||||
}
|
||||
ModItem::Const(it) => {
|
||||
let Const { name, visibility, type_ref, ast_id, has_body: _, types_map } =
|
||||
&self.tree[it];
|
||||
let Const { name, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "const ");
|
||||
match name {
|
||||
Some(name) => w!(self, "{}", name.display(self.db.upcast(), self.edition)),
|
||||
Some(name) => w!(self, "{}", name.display(self.db, self.edition)),
|
||||
None => w!(self, "_"),
|
||||
}
|
||||
w!(self, ": ");
|
||||
self.print_type_ref(*type_ref, types_map);
|
||||
wln!(self, " = _;");
|
||||
}
|
||||
ModItem::Static(it) => {
|
||||
let Static {
|
||||
name,
|
||||
visibility,
|
||||
mutable,
|
||||
type_ref,
|
||||
ast_id,
|
||||
has_safe_kw,
|
||||
has_unsafe_kw,
|
||||
types_map,
|
||||
} = &self.tree[it];
|
||||
let Static { name, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
if *has_safe_kw {
|
||||
w!(self, "safe ");
|
||||
}
|
||||
if *has_unsafe_kw {
|
||||
w!(self, "unsafe ");
|
||||
}
|
||||
w!(self, "static ");
|
||||
if *mutable {
|
||||
w!(self, "mut ");
|
||||
}
|
||||
w!(self, "{}: ", name.display(self.db.upcast(), self.edition));
|
||||
self.print_type_ref(*type_ref, types_map);
|
||||
w!(self, "{}", name.display(self.db, self.edition));
|
||||
w!(self, " = _;");
|
||||
wln!(self);
|
||||
}
|
||||
ModItem::Trait(it) => {
|
||||
let Trait { name, visibility, is_auto, is_unsafe, items, generic_params, ast_id } =
|
||||
&self.tree[it];
|
||||
let Trait { name, visibility, items, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
if *is_unsafe {
|
||||
w!(self, "unsafe ");
|
||||
}
|
||||
if *is_auto {
|
||||
w!(self, "auto ");
|
||||
}
|
||||
w!(self, "trait {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
w!(self, "trait {} {{", name.display(self.db, self.edition));
|
||||
self.indented(|this| {
|
||||
for item in &**items {
|
||||
this.print_mod_item((*item).into());
|
||||
|
|
@ -452,43 +305,15 @@ impl Printer<'_> {
|
|||
wln!(self, "}}");
|
||||
}
|
||||
ModItem::TraitAlias(it) => {
|
||||
let TraitAlias { name, visibility, generic_params, ast_id } = &self.tree[it];
|
||||
let TraitAlias { name, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "trait {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
w!(self, " = ");
|
||||
self.print_where_clause(generic_params);
|
||||
w!(self, ";");
|
||||
wln!(self);
|
||||
wln!(self, "trait {} = ..;", name.display(self.db, self.edition));
|
||||
}
|
||||
ModItem::Impl(it) => {
|
||||
let Impl {
|
||||
target_trait,
|
||||
self_ty,
|
||||
is_negative,
|
||||
is_unsafe,
|
||||
items,
|
||||
generic_params,
|
||||
ast_id,
|
||||
types_map,
|
||||
} = &self.tree[it];
|
||||
let Impl { items, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
if *is_unsafe {
|
||||
w!(self, "unsafe");
|
||||
}
|
||||
w!(self, "impl");
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
w!(self, " ");
|
||||
if *is_negative {
|
||||
w!(self, "!");
|
||||
}
|
||||
if let Some(tr) = target_trait {
|
||||
self.print_path(&types_map[tr.path], types_map);
|
||||
w!(self, " for ");
|
||||
}
|
||||
self.print_type_ref(*self_ty, types_map);
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
w!(self, "impl {{");
|
||||
self.indented(|this| {
|
||||
for item in &**items {
|
||||
this.print_mod_item((*item).into());
|
||||
|
|
@ -497,28 +322,10 @@ impl Printer<'_> {
|
|||
wln!(self, "}}");
|
||||
}
|
||||
ModItem::TypeAlias(it) => {
|
||||
let TypeAlias {
|
||||
name,
|
||||
visibility,
|
||||
bounds,
|
||||
type_ref,
|
||||
generic_params,
|
||||
ast_id,
|
||||
types_map,
|
||||
} = &self.tree[it];
|
||||
let TypeAlias { name, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "type {}", name.display(self.db.upcast(), self.edition));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
if !bounds.is_empty() {
|
||||
w!(self, ": ");
|
||||
self.print_type_bounds(bounds, types_map);
|
||||
}
|
||||
if let Some(ty) = type_ref {
|
||||
w!(self, " = ");
|
||||
self.print_type_ref(*ty, types_map);
|
||||
}
|
||||
self.print_where_clause(generic_params);
|
||||
w!(self, "type {}", name.display(self.db, self.edition));
|
||||
w!(self, ";");
|
||||
wln!(self);
|
||||
}
|
||||
|
|
@ -526,7 +333,7 @@ impl Printer<'_> {
|
|||
let Mod { name, visibility, kind, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "mod {}", name.display(self.db.upcast(), self.edition));
|
||||
w!(self, "mod {}", name.display(self.db, self.edition));
|
||||
match kind {
|
||||
ModKind::Inline { items } => {
|
||||
w!(self, " {{");
|
||||
|
|
@ -546,145 +353,29 @@ impl Printer<'_> {
|
|||
let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it];
|
||||
let _ = writeln!(
|
||||
self,
|
||||
"// AstId: {:?}, SyntaxContext: {}, ExpandTo: {:?}",
|
||||
"// AstId: {:?}, SyntaxContextId: {}, ExpandTo: {:?}",
|
||||
ast_id.erase().into_raw(),
|
||||
ctxt,
|
||||
expand_to
|
||||
);
|
||||
wln!(self, "{}!(...);", path.display(self.db.upcast(), self.edition));
|
||||
wln!(self, "{}!(...);", path.display(self.db, self.edition));
|
||||
}
|
||||
ModItem::MacroRules(it) => {
|
||||
let MacroRules { name, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
wln!(
|
||||
self,
|
||||
"macro_rules! {} {{ ... }}",
|
||||
name.display(self.db.upcast(), self.edition)
|
||||
);
|
||||
wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db, self.edition));
|
||||
}
|
||||
ModItem::Macro2(it) => {
|
||||
let Macro2 { name, visibility, ast_id } = &self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast(), self.edition));
|
||||
wln!(self, "macro {} {{ ... }}", name.display(self.db, self.edition));
|
||||
}
|
||||
}
|
||||
|
||||
self.blank();
|
||||
}
|
||||
|
||||
fn print_type_ref(&mut self, type_ref: TypeRefId, map: &TypesMap) {
|
||||
let edition = self.edition;
|
||||
print_type_ref(self.db, type_ref, map, self, edition).unwrap();
|
||||
}
|
||||
|
||||
fn print_type_bounds(&mut self, bounds: &[TypeBound], map: &TypesMap) {
|
||||
let edition = self.edition;
|
||||
print_type_bounds(self.db, bounds, map, self, edition).unwrap();
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path, map: &TypesMap) {
|
||||
let edition = self.edition;
|
||||
print_path(self.db, path, map, self, edition).unwrap();
|
||||
}
|
||||
|
||||
fn print_generic_params(&mut self, params: &GenericParams, parent: GenericModItem) {
|
||||
if params.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
w!(self, "<");
|
||||
let mut first = true;
|
||||
for (idx, lt) in params.iter_lt() {
|
||||
if !first {
|
||||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
self.print_attrs_of(AttrOwner::LifetimeParamData(parent, idx), " ");
|
||||
w!(self, "{}", lt.name.display(self.db.upcast(), self.edition));
|
||||
}
|
||||
for (idx, x) in params.iter_type_or_consts() {
|
||||
if !first {
|
||||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
self.print_attrs_of(AttrOwner::TypeOrConstParamData(parent, idx), " ");
|
||||
match x {
|
||||
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
|
||||
Some(name) => w!(self, "{}", name.display(self.db.upcast(), self.edition)),
|
||||
None => w!(self, "_anon_{}", idx.into_raw()),
|
||||
},
|
||||
TypeOrConstParamData::ConstParamData(konst) => {
|
||||
w!(self, "const {}: ", konst.name.display(self.db.upcast(), self.edition));
|
||||
self.print_type_ref(konst.ty, ¶ms.types_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
w!(self, ">");
|
||||
}
|
||||
|
||||
fn print_where_clause_and_opening_brace(&mut self, params: &GenericParams) {
|
||||
if self.print_where_clause(params) {
|
||||
w!(self, "\n{{");
|
||||
} else {
|
||||
self.whitespace();
|
||||
w!(self, "{{");
|
||||
}
|
||||
}
|
||||
|
||||
fn print_where_clause(&mut self, params: &GenericParams) -> bool {
|
||||
if params.where_predicates().next().is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
w!(self, "\nwhere");
|
||||
let edition = self.edition;
|
||||
self.indented(|this| {
|
||||
for (i, pred) in params.where_predicates().enumerate() {
|
||||
if i != 0 {
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
||||
let (target, bound) = match pred {
|
||||
WherePredicate::TypeBound { target, bound } => (target, bound),
|
||||
WherePredicate::Lifetime { target, bound } => {
|
||||
w!(
|
||||
this,
|
||||
"{}: {}",
|
||||
target.name.display(self.db.upcast(), edition),
|
||||
bound.name.display(self.db.upcast(), edition)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
WherePredicate::ForLifetime { lifetimes, target, bound } => {
|
||||
w!(this, "for<");
|
||||
for (i, lt) in lifetimes.iter().enumerate() {
|
||||
if i != 0 {
|
||||
w!(this, ", ");
|
||||
}
|
||||
w!(this, "{}", lt.display(self.db.upcast(), edition));
|
||||
}
|
||||
w!(this, "> ");
|
||||
(target, bound)
|
||||
}
|
||||
};
|
||||
|
||||
match target {
|
||||
WherePredicateTypeTarget::TypeRef(ty) => {
|
||||
this.print_type_ref(*ty, ¶ms.types_map)
|
||||
}
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) => match params[*id].name() {
|
||||
Some(name) => w!(this, "{}", name.display(self.db.upcast(), edition)),
|
||||
None => w!(this, "_anon_{}", id.into_raw()),
|
||||
},
|
||||
}
|
||||
w!(this, ": ");
|
||||
this.print_type_bounds(std::slice::from_ref(bound), ¶ms.types_map);
|
||||
}
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
fn print_ast_id(&mut self, ast_id: ErasedFileAstId) {
|
||||
wln!(self, "// AstId: {:?}", ast_id.into_raw());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use expect_test::{expect, Expect};
|
||||
use expect_test::{Expect, expect};
|
||||
use span::Edition;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
|
|
@ -83,11 +83,11 @@ extern "C" {
|
|||
|
||||
#[on_extern_static]
|
||||
// AstId: 3
|
||||
pub(self) static EX_STATIC: u8 = _;
|
||||
pub(self) static EX_STATIC = _;
|
||||
|
||||
#[on_extern_fn]
|
||||
// AstId: 4
|
||||
pub(self) fn ex_fn() -> ();
|
||||
pub(self) fn ex_fn;
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
|
@ -131,35 +131,35 @@ enum E {
|
|||
// AstId: 2
|
||||
pub(self) struct Struct {
|
||||
#[doc = " fld docs"]
|
||||
pub(self) fld: (),
|
||||
pub(self) fld,
|
||||
}
|
||||
|
||||
// AstId: 3
|
||||
pub(self) struct Tuple(
|
||||
#[attr]
|
||||
pub(self) 0: u8,
|
||||
pub(self) 0,
|
||||
);
|
||||
|
||||
// AstId: 4
|
||||
pub(self) union Ize {
|
||||
pub(self) a: (),
|
||||
pub(self) b: (),
|
||||
pub(self) a,
|
||||
pub(self) b,
|
||||
}
|
||||
|
||||
// AstId: 5
|
||||
pub(self) enum E {
|
||||
pub(self) enum E
|
||||
// AstId: 6
|
||||
#[doc = " comment on Unit"]
|
||||
Unit,
|
||||
// AstId: 7
|
||||
#[doc = " comment on Tuple"]
|
||||
Tuple(
|
||||
pub(self) 0: u8,
|
||||
pub(self) 0,
|
||||
),
|
||||
// AstId: 8
|
||||
Struct {
|
||||
#[doc = " comment on a: u8"]
|
||||
pub(self) a: u8,
|
||||
pub(self) a,
|
||||
},
|
||||
}
|
||||
"#]],
|
||||
|
|
@ -186,33 +186,23 @@ trait Tr: SuperTrait + 'lifetime {
|
|||
"#,
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub static mut ST: () = _;
|
||||
pub static ST = _;
|
||||
|
||||
// AstId: 2
|
||||
pub(self) const _: Anon = _;
|
||||
pub(self) const _ = _;
|
||||
|
||||
#[attr]
|
||||
#[inner_attr_in_fn]
|
||||
// AstId: 3
|
||||
pub(self) fn f(
|
||||
#[attr]
|
||||
u8,
|
||||
(),
|
||||
) -> () { ... }
|
||||
pub(self) fn f;
|
||||
|
||||
// AstId: 4
|
||||
pub(self) trait Tr<Self>
|
||||
where
|
||||
Self: SuperTrait,
|
||||
Self: 'lifetime
|
||||
{
|
||||
pub(self) trait Tr {
|
||||
// AstId: 6
|
||||
pub(self) type Assoc: AssocBound = Default;
|
||||
pub(self) type Assoc;
|
||||
|
||||
// AstId: 7
|
||||
pub(self) fn method(
|
||||
self: &Self,
|
||||
) -> ();
|
||||
pub(self) fn method;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -242,7 +232,7 @@ mod outline;
|
|||
pub(self) use super::*;
|
||||
|
||||
// AstId: 4
|
||||
pub(self) fn fn_in_module() -> () { ... }
|
||||
pub(self) fn fn_in_module;
|
||||
}
|
||||
|
||||
// AstId: 2
|
||||
|
|
@ -270,159 +260,12 @@ m!();
|
|||
// AstId: 2
|
||||
pub macro m2 { ... }
|
||||
|
||||
// AstId: 3, SyntaxContext: 2, ExpandTo: Items
|
||||
// AstId: 3, SyntaxContextId: ROOT2024, ExpandTo: Items
|
||||
m!(...);
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mod_paths() {
|
||||
check(
|
||||
r#"
|
||||
struct S {
|
||||
a: self::Ty,
|
||||
b: super::SuperTy,
|
||||
c: super::super::SuperSuperTy,
|
||||
d: ::abs::Path,
|
||||
e: crate::Crate,
|
||||
f: plain::path::Ty,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub(self) struct S {
|
||||
pub(self) a: self::Ty,
|
||||
pub(self) b: super::SuperTy,
|
||||
pub(self) c: super::super::SuperSuperTy,
|
||||
pub(self) d: ::abs::Path,
|
||||
pub(self) e: crate::Crate,
|
||||
pub(self) f: plain::path::Ty,
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn types() {
|
||||
check(
|
||||
r#"
|
||||
struct S {
|
||||
a: Mixed<'a, T, Item=(), OtherItem=u8>,
|
||||
b: <Fully as Qualified>::Syntax,
|
||||
c: <TypeAnchored>::Path::<'a>,
|
||||
d: dyn for<'a> Trait<'a>,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub(self) struct S {
|
||||
pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>,
|
||||
pub(self) b: Qualified::<Self=Fully>::Syntax,
|
||||
pub(self) c: <TypeAnchored>::Path::<'a>,
|
||||
pub(self) d: dyn for<'a> Trait::<'a>,
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generics() {
|
||||
check(
|
||||
r#"
|
||||
struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> {
|
||||
field: &'a &'b T,
|
||||
}
|
||||
|
||||
struct Tuple<T: Copy, U: ?Sized>(T, U);
|
||||
|
||||
impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> {
|
||||
fn f<G: 'a>(arg: impl Copy) -> impl Copy {}
|
||||
}
|
||||
|
||||
enum Enum<'a, T, const U: u8> {}
|
||||
union Union<'a, T, const U: u8> {}
|
||||
|
||||
trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub(self) struct S<'a, 'b, T, const K: u8>
|
||||
where
|
||||
T: Copy,
|
||||
T: 'a,
|
||||
T: 'b,
|
||||
'b: 'a
|
||||
{
|
||||
pub(self) field: &'a &'b T,
|
||||
}
|
||||
|
||||
// AstId: 2
|
||||
pub(self) struct Tuple<T, U>(
|
||||
pub(self) 0: T,
|
||||
pub(self) 1: U,
|
||||
)
|
||||
where
|
||||
T: Copy,
|
||||
U: ?Sized;
|
||||
|
||||
// AstId: 3
|
||||
impl<'a, 'b, T, const K: u8> S::<'a, 'b, T, K>
|
||||
where
|
||||
T: Copy,
|
||||
T: 'a,
|
||||
T: 'b,
|
||||
'b: 'a
|
||||
{
|
||||
// AstId: 9
|
||||
pub(self) fn f<G>(
|
||||
impl Copy,
|
||||
) -> impl Copy
|
||||
where
|
||||
G: 'a { ... }
|
||||
}
|
||||
|
||||
// AstId: 4
|
||||
pub(self) enum Enum<'a, T, const U: u8> {
|
||||
}
|
||||
|
||||
// AstId: 5
|
||||
pub(self) union Union<'a, T, const U: u8> {
|
||||
}
|
||||
|
||||
// AstId: 6
|
||||
pub(self) trait Tr<'a, Self, T>
|
||||
where
|
||||
Self: Super,
|
||||
T: 'a,
|
||||
Self: for<'a> Tr::<'a, T>
|
||||
{
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generics_with_attributes() {
|
||||
check(
|
||||
r#"
|
||||
struct S<#[cfg(never)] T>;
|
||||
struct S<A, B, #[cfg(never)] C>;
|
||||
struct S<A, #[cfg(never)] B, C>;
|
||||
"#,
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub(self) struct S<#[cfg(never)] T>;
|
||||
|
||||
// AstId: 2
|
||||
pub(self) struct S<A, B, #[cfg(never)] C>;
|
||||
|
||||
// AstId: 3
|
||||
pub(self) struct S<A, #[cfg(never)] B, C>;
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pub_self() {
|
||||
check(
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
//! 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::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
use rustc_hash::FxHashMap;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
|
||||
FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId,
|
||||
StaticId, StructId, TraitId, TypeAliasId, UnionId, db::DefDatabase, expr_store::path::Path,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -96,7 +96,7 @@ impl LangItems {
|
|||
/// Salsa query. This will look for lang items in a specific crate.
|
||||
pub(crate) fn crate_lang_items_query(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
) -> Option<Arc<LangItems>> {
|
||||
let _p = tracing::info_span!("crate_lang_items_query").entered();
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ impl LangItems {
|
|||
for (_, module_data) in crate_def_map.modules() {
|
||||
for impl_def in module_data.scope.impls() {
|
||||
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
|
||||
for &(_, assoc) in db.impl_data(impl_def).items.iter() {
|
||||
for &(_, assoc) in db.impl_items(impl_def).items.iter() {
|
||||
match assoc {
|
||||
AssocItemId::FunctionId(f) => {
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
|
||||
|
|
@ -124,7 +124,7 @@ impl LangItems {
|
|||
match def {
|
||||
ModuleDefId::TraitId(trait_) => {
|
||||
lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
|
||||
db.trait_data(trait_).items.iter().for_each(
|
||||
db.trait_items(trait_).items.iter().for_each(
|
||||
|&(_, assoc_id)| match assoc_id {
|
||||
AssocItemId::FunctionId(f) => {
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
|
||||
|
|
@ -140,7 +140,7 @@ impl LangItems {
|
|||
}
|
||||
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
|
||||
lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
|
||||
crate_def_map.enum_definitions[&e].iter().for_each(|&id| {
|
||||
db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
|
||||
lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
|
||||
});
|
||||
}
|
||||
|
|
@ -164,18 +164,14 @@ impl LangItems {
|
|||
}
|
||||
}
|
||||
|
||||
if lang_items.items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(lang_items))
|
||||
}
|
||||
if lang_items.items.is_empty() { None } else { Some(Arc::new(lang_items)) }
|
||||
}
|
||||
|
||||
/// Salsa query. Look for a lang item, starting from the specified crate and recursively
|
||||
/// traversing its dependencies.
|
||||
pub(crate) fn lang_item_query(
|
||||
db: &dyn DefDatabase,
|
||||
start_crate: CrateId,
|
||||
start_crate: Crate,
|
||||
item: LangItem,
|
||||
) -> Option<LangItemTarget> {
|
||||
let _p = tracing::info_span!("lang_item_query").entered();
|
||||
|
|
@ -184,10 +180,7 @@ impl LangItems {
|
|||
{
|
||||
return Some(target);
|
||||
}
|
||||
db.crate_graph()[start_crate]
|
||||
.dependencies
|
||||
.iter()
|
||||
.find_map(|dep| db.lang_item(dep.crate_id, item))
|
||||
start_crate.data(db).dependencies.iter().find_map(|dep| db.lang_item(dep.crate_id, item))
|
||||
}
|
||||
|
||||
fn collect_lang_item<T>(
|
||||
|
|
@ -209,19 +202,14 @@ pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangIte
|
|||
db.attrs(item).lang_item()
|
||||
}
|
||||
|
||||
pub(crate) fn notable_traits_in_deps(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
) -> Arc<[Arc<[TraitId]>]> {
|
||||
pub(crate) fn notable_traits_in_deps(db: &dyn DefDatabase, krate: Crate) -> Arc<[Arc<[TraitId]>]> {
|
||||
let _p = tracing::info_span!("notable_traits_in_deps", ?krate).entered();
|
||||
let crate_graph = db.crate_graph();
|
||||
|
||||
Arc::from_iter(
|
||||
crate_graph.transitive_deps(krate).filter_map(|krate| db.crate_notable_traits(krate)),
|
||||
db.transitive_deps(krate).into_iter().filter_map(|krate| db.crate_notable_traits(krate)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Option<Arc<[TraitId]>> {
|
||||
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Arc<[TraitId]>> {
|
||||
let _p = tracing::info_span!("crate_notable_traits", ?krate).entered();
|
||||
|
||||
let mut traits = Vec::new();
|
||||
|
|
@ -238,11 +226,7 @@ pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Opti
|
|||
}
|
||||
}
|
||||
|
||||
if traits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(traits.into_iter().collect())
|
||||
}
|
||||
if traits.is_empty() { None } else { Some(traits.into_iter().collect()) }
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
|
|
@ -290,17 +274,12 @@ impl LangItem {
|
|||
Self::from_symbol(name.symbol())
|
||||
}
|
||||
|
||||
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
||||
pub fn path(&self, db: &dyn DefDatabase, start_crate: Crate) -> Option<Path> {
|
||||
let t = db.lang_item(start_crate, *self)?;
|
||||
Some(Path::LangItem(t, None))
|
||||
}
|
||||
|
||||
pub fn ty_rel_path(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
start_crate: CrateId,
|
||||
seg: Name,
|
||||
) -> Option<Path> {
|
||||
pub fn ty_rel_path(&self, db: &dyn DefDatabase, start_crate: Crate, seg: Name) -> Option<Path> {
|
||||
let t = db.lang_item(start_crate, *self)?;
|
||||
Some(Path::LangItem(t, Some(seg)))
|
||||
}
|
||||
|
|
@ -366,6 +345,7 @@ language_item_table! {
|
|||
IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
|
||||
UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None;
|
||||
VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
|
|
|||
|
|
@ -18,32 +18,22 @@ extern crate ra_ap_rustc_parse_format as rustc_parse_format;
|
|||
#[cfg(feature = "in-rust-tree")]
|
||||
extern crate rustc_abi;
|
||||
|
||||
#[cfg(feature = "in-rust-tree")]
|
||||
extern crate rustc_hashes;
|
||||
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_abi as rustc_abi;
|
||||
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_hashes as rustc_hashes;
|
||||
|
||||
pub mod db;
|
||||
|
||||
pub mod attr;
|
||||
pub mod builtin_type;
|
||||
pub mod item_scope;
|
||||
pub mod path;
|
||||
pub mod per_ns;
|
||||
|
||||
pub mod expander;
|
||||
pub mod lower;
|
||||
pub mod signatures;
|
||||
|
||||
pub mod dyn_map;
|
||||
|
||||
pub mod item_tree;
|
||||
|
||||
pub mod data;
|
||||
pub mod generics;
|
||||
pub mod lang_item;
|
||||
|
||||
pub mod hir;
|
||||
|
|
@ -59,57 +49,54 @@ pub mod find_path;
|
|||
pub mod import_map;
|
||||
pub mod visibility;
|
||||
|
||||
use intern::Interned;
|
||||
use intern::{Interned, sym};
|
||||
pub use rustc_abi as layout;
|
||||
use triomphe::Arc;
|
||||
|
||||
pub use crate::signatures::LocalFieldId;
|
||||
|
||||
#[cfg(test)]
|
||||
mod macro_expansion_tests;
|
||||
mod pretty;
|
||||
#[cfg(test)]
|
||||
mod test_db;
|
||||
|
||||
use std::{
|
||||
hash::{Hash, Hasher},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use base_db::{
|
||||
impl_intern_key,
|
||||
ra_salsa::{self, InternValueTrivial},
|
||||
CrateId,
|
||||
};
|
||||
use base_db::{Crate, impl_intern_key};
|
||||
use hir_expand::{
|
||||
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
|
||||
MacroDefId, MacroDefKind,
|
||||
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
|
||||
db::ExpandDatabase,
|
||||
eager::expand_eager_macro_input,
|
||||
impl_intern_lookup,
|
||||
mod_path::ModPath,
|
||||
name::Name,
|
||||
proc_macro::{CustomProcMacroExpander, ProcMacroKind},
|
||||
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
|
||||
MacroDefId, MacroDefKind,
|
||||
};
|
||||
use item_tree::ExternBlock;
|
||||
use la_arena::Idx;
|
||||
use nameres::DefMap;
|
||||
use span::{AstIdNode, Edition, FileAstId, SyntaxContextId};
|
||||
use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
|
||||
use stdx::impl_from;
|
||||
use syntax::{ast, AstNode};
|
||||
use syntax::{AstNode, ast};
|
||||
|
||||
pub use hir_expand::{tt, Intern, Lookup};
|
||||
pub use hir_expand::{Intern, Lookup, tt};
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::VariantData,
|
||||
db::DefDatabase,
|
||||
hir::generics::{LocalLifetimeParamId, LocalTypeOrConstParamId},
|
||||
item_tree::{
|
||||
Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules,
|
||||
Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant,
|
||||
},
|
||||
nameres::LocalDefMap,
|
||||
signatures::VariantFields,
|
||||
};
|
||||
|
||||
type FxIndexMap<K, V> =
|
||||
indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
||||
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>;
|
||||
/// A wrapper around three booleans
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||
pub struct ImportPathConfig {
|
||||
|
|
@ -192,8 +179,7 @@ pub trait ItemTreeLoc {
|
|||
|
||||
macro_rules! impl_intern {
|
||||
($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
|
||||
impl_intern_key!($id);
|
||||
impl InternValueTrivial for $loc {}
|
||||
impl_intern_key!($id, $loc);
|
||||
impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup);
|
||||
};
|
||||
}
|
||||
|
|
@ -213,87 +199,58 @@ macro_rules! impl_loc {
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionId(ra_salsa::InternId);
|
||||
type FunctionLoc = AssocItemLoc<Function>;
|
||||
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
||||
impl_loc!(FunctionLoc, id: Function, container: ItemContainerId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct StructId(ra_salsa::InternId);
|
||||
type StructLoc = ItemLoc<Struct>;
|
||||
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
||||
impl_loc!(StructLoc, id: Struct, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct UnionId(ra_salsa::InternId);
|
||||
pub type UnionLoc = ItemLoc<Union>;
|
||||
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
|
||||
impl_loc!(UnionLoc, id: Union, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct EnumId(ra_salsa::InternId);
|
||||
pub type EnumLoc = ItemLoc<Enum>;
|
||||
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
|
||||
impl_loc!(EnumLoc, id: Enum, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(ra_salsa::InternId);
|
||||
type ConstLoc = AssocItemLoc<Const>;
|
||||
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
||||
impl_loc!(ConstLoc, id: Const, container: ItemContainerId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StaticId(ra_salsa::InternId);
|
||||
pub type StaticLoc = AssocItemLoc<Static>;
|
||||
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
|
||||
impl_loc!(StaticLoc, id: Static, container: ItemContainerId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct TraitId(ra_salsa::InternId);
|
||||
pub type TraitLoc = ItemLoc<Trait>;
|
||||
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
|
||||
impl_loc!(TraitLoc, id: Trait, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TraitAliasId(ra_salsa::InternId);
|
||||
pub type TraitAliasLoc = ItemLoc<TraitAlias>;
|
||||
impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
|
||||
impl_loc!(TraitAliasLoc, id: TraitAlias, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TypeAliasId(ra_salsa::InternId);
|
||||
type TypeAliasLoc = AssocItemLoc<TypeAlias>;
|
||||
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
|
||||
impl_loc!(TypeAliasLoc, id: TypeAlias, container: ItemContainerId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ImplId(ra_salsa::InternId);
|
||||
type ImplLoc = ItemLoc<Impl>;
|
||||
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
||||
impl_loc!(ImplLoc, id: Impl, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct UseId(ra_salsa::InternId);
|
||||
type UseLoc = ItemLoc<Use>;
|
||||
impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
|
||||
impl_loc!(UseLoc, id: Use, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ExternCrateId(ra_salsa::InternId);
|
||||
type ExternCrateLoc = ItemLoc<ExternCrate>;
|
||||
impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
|
||||
impl_loc!(ExternCrateLoc, id: ExternCrate, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ExternBlockId(ra_salsa::InternId);
|
||||
type ExternBlockLoc = ItemLoc<ExternBlock>;
|
||||
impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block);
|
||||
impl_loc!(ExternBlockLoc, id: ExternBlock, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumVariantId(ra_salsa::InternId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumVariantLoc {
|
||||
pub id: ItemTreeId<Variant>,
|
||||
|
|
@ -302,9 +259,6 @@ pub struct EnumVariantLoc {
|
|||
}
|
||||
impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant);
|
||||
impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct Macro2Id(ra_salsa::InternId);
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Macro2Loc {
|
||||
pub container: ModuleId,
|
||||
|
|
@ -316,8 +270,6 @@ pub struct Macro2Loc {
|
|||
impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
|
||||
impl_loc!(Macro2Loc, id: Macro2, container: ModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct MacroRulesId(ra_salsa::InternId);
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroRulesLoc {
|
||||
pub container: ModuleId,
|
||||
|
|
@ -345,8 +297,7 @@ pub enum MacroExpander {
|
|||
BuiltInDerive(BuiltinDeriveExpander),
|
||||
BuiltInEager(EagerExpander),
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ProcMacroId(ra_salsa::InternId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ProcMacroLoc {
|
||||
pub container: CrateRootModuleId,
|
||||
|
|
@ -358,8 +309,6 @@ pub struct ProcMacroLoc {
|
|||
impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro);
|
||||
impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct BlockId(ra_salsa::InternId);
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||
pub struct BlockLoc {
|
||||
pub ast_id: AstId<ast::BlockExpr>,
|
||||
|
|
@ -368,24 +317,10 @@ pub struct BlockLoc {
|
|||
}
|
||||
impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
|
||||
|
||||
/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
|
||||
/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct ConstBlockId(ra_salsa::InternId);
|
||||
impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||
pub struct ConstBlockLoc {
|
||||
/// The parent of the anonymous const block.
|
||||
pub parent: DefWithBodyId,
|
||||
/// The root expression of this const block in the parent body.
|
||||
pub root: hir::ExprId,
|
||||
}
|
||||
|
||||
/// A `ModuleId` that is always a crate's root module.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct CrateRootModuleId {
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
}
|
||||
|
||||
impl CrateRootModuleId {
|
||||
|
|
@ -393,7 +328,11 @@ impl CrateRootModuleId {
|
|||
db.crate_def_map(self.krate)
|
||||
}
|
||||
|
||||
pub fn krate(self) -> CrateId {
|
||||
pub(crate) fn local_def_map(&self, db: &dyn DefDatabase) -> (Arc<DefMap>, Arc<LocalDefMap>) {
|
||||
db.crate_local_def_map(self.krate)
|
||||
}
|
||||
|
||||
pub fn krate(self) -> Crate {
|
||||
self.krate
|
||||
}
|
||||
}
|
||||
|
|
@ -421,8 +360,8 @@ impl From<CrateRootModuleId> for ModuleDefId {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<CrateId> for CrateRootModuleId {
|
||||
fn from(krate: CrateId) -> Self {
|
||||
impl From<Crate> for CrateRootModuleId {
|
||||
fn from(krate: Crate) -> Self {
|
||||
CrateRootModuleId { krate }
|
||||
}
|
||||
}
|
||||
|
|
@ -441,7 +380,7 @@ impl TryFrom<ModuleId> for CrateRootModuleId {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct ModuleId {
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
/// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the
|
||||
/// `BlockId` of that block expression. If `None`, this module is part of the crate-level
|
||||
/// `DefMap` of `krate`.
|
||||
|
|
@ -458,11 +397,22 @@ impl ModuleId {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (Arc<DefMap>, Arc<LocalDefMap>) {
|
||||
match self.block {
|
||||
Some(block) => (db.block_def_map(block), self.only_local_def_map(db)),
|
||||
None => db.crate_local_def_map(self.krate),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> Arc<LocalDefMap> {
|
||||
db.crate_local_def_map(self.krate).1
|
||||
}
|
||||
|
||||
pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc<DefMap> {
|
||||
db.crate_def_map(self.krate)
|
||||
}
|
||||
|
||||
pub fn krate(self) -> CrateId {
|
||||
pub fn krate(self) -> Crate {
|
||||
self.krate
|
||||
}
|
||||
|
||||
|
|
@ -470,11 +420,7 @@ impl ModuleId {
|
|||
let def_map = self.def_map(db);
|
||||
let parent = def_map[self.local_id].parent?;
|
||||
def_map[parent].children.iter().find_map(|(name, module_id)| {
|
||||
if *module_id == self.local_id {
|
||||
Some(name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if *module_id == self.local_id { Some(name.clone()) } else { None }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -525,8 +471,6 @@ pub struct FieldId {
|
|||
pub local_id: LocalFieldId,
|
||||
}
|
||||
|
||||
pub type LocalFieldId = Idx<data::adt::FieldData>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TupleId(pub u32);
|
||||
|
||||
|
|
@ -536,12 +480,11 @@ pub struct TupleFieldId {
|
|||
pub index: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct TypeOrConstParamId {
|
||||
pub parent: GenericDefId,
|
||||
pub local_id: LocalTypeOrConstParamId,
|
||||
}
|
||||
impl InternValueTrivial for TypeOrConstParamId {}
|
||||
|
||||
/// A TypeOrConstParamId with an invariant that it actually belongs to a type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -595,15 +538,11 @@ impl From<ConstParamId> for TypeOrConstParamId {
|
|||
}
|
||||
}
|
||||
|
||||
pub type LocalTypeOrConstParamId = Idx<generics::TypeOrConstParamData>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct LifetimeParamId {
|
||||
pub parent: GenericDefId,
|
||||
pub local_id: LocalLifetimeParamId,
|
||||
}
|
||||
pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
|
||||
impl InternValueTrivial for LifetimeParamId {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ItemContainerId {
|
||||
|
|
@ -615,7 +554,7 @@ pub enum ItemContainerId {
|
|||
impl_from!(ModuleId for ItemContainerId);
|
||||
|
||||
/// A Data Type
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum AdtId {
|
||||
StructId(StructId),
|
||||
UnionId(UnionId),
|
||||
|
|
@ -624,7 +563,7 @@ pub enum AdtId {
|
|||
impl_from!(StructId, UnionId, EnumId for AdtId);
|
||||
|
||||
/// A macro
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum MacroId {
|
||||
Macro2Id(Macro2Id),
|
||||
MacroRulesId(MacroRulesId),
|
||||
|
|
@ -678,222 +617,59 @@ impl_from!(
|
|||
for ModuleDefId
|
||||
);
|
||||
|
||||
/// Something that holds types, required for the current const arg lowering implementation as they
|
||||
/// need to be able to query where they are defined.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum TypeOwnerId {
|
||||
FunctionId(FunctionId),
|
||||
StaticId(StaticId),
|
||||
ConstId(ConstId),
|
||||
InTypeConstId(InTypeConstId),
|
||||
AdtId(AdtId),
|
||||
TraitId(TraitId),
|
||||
TraitAliasId(TraitAliasId),
|
||||
TypeAliasId(TypeAliasId),
|
||||
ImplId(ImplId),
|
||||
EnumVariantId(EnumVariantId),
|
||||
}
|
||||
|
||||
impl TypeOwnerId {
|
||||
fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
||||
Some(match self {
|
||||
TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it),
|
||||
TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it),
|
||||
TypeOwnerId::StaticId(it) => GenericDefId::StaticId(it),
|
||||
TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it),
|
||||
TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it),
|
||||
TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it),
|
||||
TypeOwnerId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
|
||||
TypeOwnerId::ImplId(it) => GenericDefId::ImplId(it),
|
||||
TypeOwnerId::EnumVariantId(it) => {
|
||||
GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
|
||||
}
|
||||
TypeOwnerId::InTypeConstId(_) => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl_from!(
|
||||
FunctionId,
|
||||
StaticId,
|
||||
ConstId,
|
||||
InTypeConstId,
|
||||
AdtId,
|
||||
TraitId,
|
||||
TraitAliasId,
|
||||
TypeAliasId,
|
||||
ImplId,
|
||||
EnumVariantId
|
||||
for TypeOwnerId
|
||||
);
|
||||
|
||||
// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let it: Type = _; }`)
|
||||
impl From<DefWithBodyId> for TypeOwnerId {
|
||||
fn from(value: DefWithBodyId) -> Self {
|
||||
match value {
|
||||
DefWithBodyId::FunctionId(it) => it.into(),
|
||||
DefWithBodyId::StaticId(it) => it.into(),
|
||||
DefWithBodyId::ConstId(it) => it.into(),
|
||||
DefWithBodyId::InTypeConstId(it) => it.into(),
|
||||
DefWithBodyId::VariantId(it) => it.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenericDefId> for TypeOwnerId {
|
||||
fn from(value: GenericDefId) -> Self {
|
||||
match value {
|
||||
GenericDefId::FunctionId(it) => it.into(),
|
||||
GenericDefId::AdtId(it) => it.into(),
|
||||
GenericDefId::TraitId(it) => it.into(),
|
||||
GenericDefId::TraitAliasId(it) => it.into(),
|
||||
GenericDefId::TypeAliasId(it) => it.into(),
|
||||
GenericDefId::ImplId(it) => it.into(),
|
||||
GenericDefId::ConstId(it) => it.into(),
|
||||
GenericDefId::StaticId(it) => it.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This should not be a thing
|
||||
/// A thing that we want to store in interned ids, but we don't know its type in `hir-def`. This is
|
||||
/// currently only used in `InTypeConstId` for storing the type (which has type `Ty` defined in
|
||||
/// the `hir-ty` crate) of the constant in its id, which is a temporary hack so we may want
|
||||
/// to remove this after removing that.
|
||||
pub trait OpaqueInternableThing:
|
||||
std::any::Any + std::fmt::Debug + Sync + Send + UnwindSafe + RefUnwindSafe
|
||||
{
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
fn box_any(&self) -> Box<dyn std::any::Any>;
|
||||
fn dyn_hash(&self, state: &mut dyn Hasher);
|
||||
fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool;
|
||||
fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing>;
|
||||
}
|
||||
|
||||
impl Hash for dyn OpaqueInternableThing {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.dyn_hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for dyn OpaqueInternableThing {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.dyn_eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for dyn OpaqueInternableThing {}
|
||||
|
||||
impl Clone for Box<dyn OpaqueInternableThing> {
|
||||
fn clone(&self) -> Self {
|
||||
self.dyn_clone()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(const-generic-body): Use an stable id for in type consts.
|
||||
//
|
||||
// The current id uses `AstId<ast::ConstArg>` which will be changed by every change in the code. Ideally
|
||||
// we should use an id which is relative to the type owner, so that every change will only invalidate the
|
||||
// id if it happens inside of the type owner.
|
||||
//
|
||||
// The solution probably is to have some query on `TypeOwnerId` to traverse its constant children and store
|
||||
// their `AstId` in a list (vector or arena), and use the index of that list in the id here. That query probably
|
||||
// needs name resolution, and might go far and handles the whole path lowering or type lowering for a `TypeOwnerId`.
|
||||
//
|
||||
// Whatever path the solution takes, it should answer 3 questions at the same time:
|
||||
// * Is the id stable enough?
|
||||
// * How to find a constant id using an ast node / position in the source code? This is needed when we want to
|
||||
// provide ide functionalities inside an in type const (which we currently don't support) e.g. go to definition
|
||||
// for a local defined there. A complex id might have some trouble in this reverse mapping.
|
||||
// * How to find the return type of a constant using its id? We have this data when we are doing type lowering
|
||||
// and the name of the struct that contains this constant is resolved, so a query that only traverses the
|
||||
// type owner by its syntax tree might have a hard time here.
|
||||
|
||||
/// A constant in a type as a substitution for const generics (like `Foo<{ 2 + 2 }>`) or as an array
|
||||
/// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These
|
||||
/// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`].
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct InTypeConstId(ra_salsa::InternId);
|
||||
impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
|
||||
|
||||
// We would like to set `derive(PartialEq)`
|
||||
// but the compiler complains about that `.expected_ty` does not implement the `Copy` trait.
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
#[derive(Debug, Hash, Eq, Clone)]
|
||||
pub struct InTypeConstLoc {
|
||||
pub id: AstId<ast::ConstArg>,
|
||||
/// The thing this const arg appears in
|
||||
pub owner: TypeOwnerId,
|
||||
// FIXME(const-generic-body): The expected type should not be
|
||||
pub expected_ty: Box<dyn OpaqueInternableThing>,
|
||||
}
|
||||
|
||||
impl PartialEq for InTypeConstLoc {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id && self.owner == other.owner && *self.expected_ty == *other.expected_ty
|
||||
}
|
||||
}
|
||||
|
||||
impl InTypeConstId {
|
||||
pub fn source(&self, db: &dyn DefDatabase) -> ast::ConstArg {
|
||||
let src = self.lookup(db).id;
|
||||
let file_id = src.file_id;
|
||||
let root = &db.parse_or_expand(file_id);
|
||||
db.ast_id_map(file_id).get(src.value).to_node(root)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum GeneralConstId {
|
||||
ConstId(ConstId),
|
||||
StaticId(StaticId),
|
||||
ConstBlockId(ConstBlockId),
|
||||
InTypeConstId(InTypeConstId),
|
||||
}
|
||||
|
||||
impl_from!(ConstId, StaticId, ConstBlockId, InTypeConstId for GeneralConstId);
|
||||
impl_from!(ConstId, StaticId for GeneralConstId);
|
||||
|
||||
impl GeneralConstId {
|
||||
pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
||||
pub fn generic_def(self, _db: &dyn DefDatabase) -> Option<GenericDefId> {
|
||||
match self {
|
||||
GeneralConstId::ConstId(it) => Some(it.into()),
|
||||
GeneralConstId::StaticId(it) => Some(it.into()),
|
||||
GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
|
||||
GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(self, db: &dyn DefDatabase) -> String {
|
||||
match self {
|
||||
GeneralConstId::StaticId(it) => {
|
||||
db.static_data(it).name.display(db.upcast(), Edition::CURRENT).to_string()
|
||||
let loc = it.lookup(db);
|
||||
let tree = loc.item_tree_id().item_tree(db);
|
||||
let name = tree[loc.id.value].name.display(db, Edition::CURRENT);
|
||||
name.to_string()
|
||||
}
|
||||
GeneralConstId::ConstId(const_id) => {
|
||||
let loc = const_id.lookup(db);
|
||||
let tree = loc.item_tree_id().item_tree(db);
|
||||
tree[loc.id.value].name.as_ref().map_or_else(
|
||||
|| "_".to_owned(),
|
||||
|name| name.display(db, Edition::CURRENT).to_string(),
|
||||
)
|
||||
}
|
||||
GeneralConstId::ConstId(const_id) => db
|
||||
.const_data(const_id)
|
||||
.name
|
||||
.as_ref()
|
||||
.map(|it| it.as_str())
|
||||
.unwrap_or("_")
|
||||
.to_owned(),
|
||||
GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
|
||||
GeneralConstId::InTypeConstId(id) => format!("{{in type const {id:?}}}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The defs which have a body.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
/// The defs which have a body (have root expressions for type inference).
|
||||
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum DefWithBodyId {
|
||||
FunctionId(FunctionId),
|
||||
StaticId(StaticId),
|
||||
ConstId(ConstId),
|
||||
InTypeConstId(InTypeConstId),
|
||||
VariantId(EnumVariantId),
|
||||
// /// All fields of a variant are inference roots
|
||||
// VariantId(VariantId),
|
||||
// /// The signature can contain inference roots in a bunch of places
|
||||
// /// like const parameters or const arguments in paths
|
||||
// This should likely be kept on its own with a separate query
|
||||
// GenericDefId(GenericDefId),
|
||||
}
|
||||
|
||||
impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId);
|
||||
impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
|
||||
|
||||
impl From<EnumVariantId> for DefWithBodyId {
|
||||
fn from(id: EnumVariantId) -> Self {
|
||||
|
|
@ -908,9 +684,6 @@ impl DefWithBodyId {
|
|||
DefWithBodyId::StaticId(s) => Some(s.into()),
|
||||
DefWithBodyId::ConstId(c) => Some(c.into()),
|
||||
DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()),
|
||||
// FIXME: stable rust doesn't allow generics in constants, but we should
|
||||
// use `TypeOwnerId::as_generic_def_id` when it does.
|
||||
DefWithBodyId::InTypeConstId(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -928,7 +701,7 @@ pub enum AssocItemId {
|
|||
// casting them, and somehow making the constructors private, which would be annoying.
|
||||
impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum GenericDefId {
|
||||
AdtId(AdtId),
|
||||
// consts can have type parameters from their parents (i.e. associated consts of traits)
|
||||
|
|
@ -962,7 +735,7 @@ impl GenericDefId {
|
|||
) -> (HirFileId, Option<ast::GenericParamList>) {
|
||||
fn file_id_and_params_of_item_loc<Loc>(
|
||||
db: &dyn DefDatabase,
|
||||
def: impl for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = Loc>,
|
||||
def: impl Lookup<Database = dyn DefDatabase, Data = Loc>,
|
||||
) -> (HirFileId, Option<ast::GenericParamList>)
|
||||
where
|
||||
Loc: src::HasSource,
|
||||
|
|
@ -1017,15 +790,13 @@ impl From<AssocItemId> for GenericDefId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum CallableDefId {
|
||||
FunctionId(FunctionId),
|
||||
StructId(StructId),
|
||||
EnumVariantId(EnumVariantId),
|
||||
}
|
||||
|
||||
impl InternValueTrivial for CallableDefId {}
|
||||
|
||||
impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
|
||||
impl From<CallableDefId> for ModuleDefId {
|
||||
fn from(def: CallableDefId) -> ModuleDefId {
|
||||
|
|
@ -1038,7 +809,7 @@ impl From<CallableDefId> for ModuleDefId {
|
|||
}
|
||||
|
||||
impl CallableDefId {
|
||||
pub fn krate(self, db: &dyn DefDatabase) -> CrateId {
|
||||
pub fn krate(self, db: &dyn DefDatabase) -> Crate {
|
||||
match self {
|
||||
CallableDefId::FunctionId(f) => f.krate(db),
|
||||
CallableDefId::StructId(s) => s.krate(db),
|
||||
|
|
@ -1135,7 +906,7 @@ impl From<VariantId> for AttrDefId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
|
||||
pub enum VariantId {
|
||||
EnumVariantId(EnumVariantId),
|
||||
StructId(StructId),
|
||||
|
|
@ -1144,12 +915,8 @@ pub enum VariantId {
|
|||
impl_from!(EnumVariantId, StructId, UnionId for VariantId);
|
||||
|
||||
impl VariantId {
|
||||
pub fn variant_data(self, db: &dyn DefDatabase) -> Arc<VariantData> {
|
||||
match self {
|
||||
VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
|
||||
VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
|
||||
VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(),
|
||||
}
|
||||
pub fn variant_data(self, db: &dyn DefDatabase) -> Arc<VariantFields> {
|
||||
db.variant_fields(self)
|
||||
}
|
||||
|
||||
pub fn file_id(self, db: &dyn DefDatabase) -> HirFileId {
|
||||
|
|
@ -1175,7 +942,7 @@ pub trait HasModule {
|
|||
/// Returns the crate this thing is defined within.
|
||||
#[inline]
|
||||
#[doc(alias = "crate")]
|
||||
fn krate(&self, db: &dyn DefDatabase) -> CrateId {
|
||||
fn krate(&self, db: &dyn DefDatabase) -> Crate {
|
||||
self.module(db).krate
|
||||
}
|
||||
}
|
||||
|
|
@ -1197,7 +964,7 @@ pub trait HasModule {
|
|||
impl<N, ItemId> HasModule for ItemId
|
||||
where
|
||||
N: ItemTreeNode,
|
||||
ItemId: for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = ItemLoc<N>> + Copy,
|
||||
ItemId: Lookup<Database = dyn DefDatabase, Data = ItemLoc<N>> + Copy,
|
||||
{
|
||||
#[inline]
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
|
|
@ -1222,7 +989,7 @@ where
|
|||
#[inline]
|
||||
fn module_for_assoc_item_loc<'db>(
|
||||
db: &(dyn 'db + DefDatabase),
|
||||
id: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<impl ItemTreeNode>>,
|
||||
id: impl Lookup<Database = dyn DefDatabase, Data = AssocItemLoc<impl ItemTreeNode>>,
|
||||
) -> ModuleId {
|
||||
id.lookup(db).container.module(db)
|
||||
}
|
||||
|
|
@ -1325,23 +1092,6 @@ impl HasModule for MacroId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasModule for TypeOwnerId {
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
match *self {
|
||||
TypeOwnerId::FunctionId(it) => it.module(db),
|
||||
TypeOwnerId::StaticId(it) => it.module(db),
|
||||
TypeOwnerId::ConstId(it) => it.module(db),
|
||||
TypeOwnerId::AdtId(it) => it.module(db),
|
||||
TypeOwnerId::TraitId(it) => it.module(db),
|
||||
TypeOwnerId::TraitAliasId(it) => it.module(db),
|
||||
TypeOwnerId::TypeAliasId(it) => it.module(db),
|
||||
TypeOwnerId::ImplId(it) => it.module(db),
|
||||
TypeOwnerId::EnumVariantId(it) => it.module(db),
|
||||
TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for DefWithBodyId {
|
||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
|
|
@ -1349,7 +1099,6 @@ impl HasModule for DefWithBodyId {
|
|||
DefWithBodyId::StaticId(it) => it.module(db),
|
||||
DefWithBodyId::ConstId(it) => it.module(db),
|
||||
DefWithBodyId::VariantId(it) => it.module(db),
|
||||
DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1418,22 +1167,18 @@ impl ModuleDefId {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: Replace this with a plain function, it only has one impl
|
||||
/// A helper trait for converting to MacroCallId
|
||||
pub trait AsMacroCall {
|
||||
fn as_call_id(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
) -> Option<MacroCallId> {
|
||||
self.as_call_id_with_errors(db, krate, resolver).ok()?.value
|
||||
}
|
||||
|
||||
trait AsMacroCall {
|
||||
fn as_call_id_with_errors(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
krate: Crate,
|
||||
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
|
||||
eager_callback: &mut dyn FnMut(
|
||||
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
|
||||
MacroCallId,
|
||||
),
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
|
||||
}
|
||||
|
||||
|
|
@ -1441,15 +1186,19 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||
fn as_call_id_with_errors(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
krate: Crate,
|
||||
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
|
||||
eager_callback: &mut dyn FnMut(
|
||||
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
|
||||
MacroCallId,
|
||||
),
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
|
||||
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||
let span_map = db.span_map(self.file_id);
|
||||
let path = self.value.path().and_then(|path| {
|
||||
let range = path.syntax().text_range();
|
||||
let mod_path = path::ModPath::from_src(db, path, &mut |range| {
|
||||
let mod_path = ModPath::from_src(db, path, &mut |range| {
|
||||
span_map.as_ref().span_for_range(range).ctx
|
||||
})?;
|
||||
let call_site = span_map.span_for_range(range);
|
||||
|
|
@ -1472,6 +1221,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||
krate,
|
||||
resolver,
|
||||
resolver,
|
||||
eager_callback,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1480,15 +1230,11 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct AstIdWithPath<T: AstIdNode> {
|
||||
ast_id: AstId<T>,
|
||||
path: Interned<path::ModPath>,
|
||||
path: Interned<ModPath>,
|
||||
}
|
||||
|
||||
impl<T: AstIdNode> AstIdWithPath<T> {
|
||||
fn new(
|
||||
file_id: HirFileId,
|
||||
ast_id: FileAstId<T>,
|
||||
path: Interned<path::ModPath>,
|
||||
) -> AstIdWithPath<T> {
|
||||
fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: Interned<ModPath>) -> AstIdWithPath<T> {
|
||||
AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path }
|
||||
}
|
||||
}
|
||||
|
|
@ -1496,10 +1242,14 @@ impl<T: AstIdNode> AstIdWithPath<T> {
|
|||
fn macro_call_as_call_id(
|
||||
db: &dyn ExpandDatabase,
|
||||
call: &AstIdWithPath<ast::MacroCall>,
|
||||
call_site: SyntaxContextId,
|
||||
call_site: SyntaxContext,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
krate: Crate,
|
||||
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
|
||||
eager_callback: &mut dyn FnMut(
|
||||
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
|
||||
MacroCallId,
|
||||
),
|
||||
) -> Result<Option<MacroCallId>, UnresolvedMacro> {
|
||||
macro_call_as_call_id_with_eager(
|
||||
db,
|
||||
|
|
@ -1510,6 +1260,7 @@ fn macro_call_as_call_id(
|
|||
krate,
|
||||
resolver,
|
||||
resolver,
|
||||
eager_callback,
|
||||
)
|
||||
.map(|res| res.value)
|
||||
}
|
||||
|
|
@ -1517,12 +1268,16 @@ fn macro_call_as_call_id(
|
|||
fn macro_call_as_call_id_with_eager(
|
||||
db: &dyn ExpandDatabase,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
path: &path::ModPath,
|
||||
call_site: SyntaxContextId,
|
||||
path: &ModPath,
|
||||
call_site: SyntaxContext,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl FnOnce(&path::ModPath) -> Option<MacroDefId>,
|
||||
eager_resolver: impl Fn(&path::ModPath) -> Option<MacroDefId>,
|
||||
krate: Crate,
|
||||
resolver: impl FnOnce(&ModPath) -> Option<MacroDefId>,
|
||||
eager_resolver: impl Fn(&ModPath) -> Option<MacroDefId>,
|
||||
eager_callback: &mut dyn FnMut(
|
||||
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
|
||||
MacroCallId,
|
||||
),
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
let def = resolver(path).ok_or_else(|| UnresolvedMacro { path: path.clone() })?;
|
||||
|
||||
|
|
@ -1535,6 +1290,7 @@ fn macro_call_as_call_id_with_eager(
|
|||
def,
|
||||
call_site,
|
||||
&|path| eager_resolver(path).filter(MacroDefId::is_fn_like),
|
||||
eager_callback,
|
||||
),
|
||||
_ if def.is_fn_like() => ExpandResult {
|
||||
value: Some(def.make_call(
|
||||
|
|
@ -1552,8 +1308,86 @@ fn macro_call_as_call_id_with_eager(
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedMacro {
|
||||
pub path: hir_expand::mod_path::ModPath,
|
||||
pub path: ModPath,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub struct SyntheticSyntax;
|
||||
|
||||
// Feature: Completions Attribute
|
||||
// Crate authors can opt their type out of completions in some cases.
|
||||
// This is done with the `#[rust_analyzer::completions(...)]` attribute.
|
||||
//
|
||||
// All completeable things support `#[rust_analyzer::completions(ignore_flyimport)]`,
|
||||
// which causes the thing to get excluded from flyimport completion. It will still
|
||||
// be completed when in scope. This is analogous to the setting `rust-analyzer.completion.autoimport.exclude`
|
||||
// with `"type": "always"`.
|
||||
//
|
||||
// In addition, traits support two more modes: `#[rust_analyzer::completions(ignore_flyimport_methods)]`,
|
||||
// which means the trait itself may still be flyimported but its methods won't, and
|
||||
// `#[rust_analyzer::completions(ignore_methods)]`, which means the methods won't be completed even when
|
||||
// the trait is in scope (but the trait itself may still be completed). The methods will still be completed
|
||||
// on `dyn Trait`, `impl Trait` or where the trait is specified in bounds. These modes correspond to
|
||||
// the settings `rust-analyzer.completion.autoimport.exclude` with `"type": "methods"` and
|
||||
// `rust-analyzer.completion.excludeTraits`, respectively.
|
||||
//
|
||||
// Malformed attributes will be ignored without warnings.
|
||||
//
|
||||
// Note that users have no way to override this attribute, so be careful and only include things
|
||||
// users definitely do not want to be completed!
|
||||
|
||||
/// `#[rust_analyzer::completions(...)]` options.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Complete {
|
||||
/// No `#[rust_analyzer::completions(...)]`.
|
||||
Yes,
|
||||
/// `#[rust_analyzer::completions(ignore_flyimport)]`.
|
||||
IgnoreFlyimport,
|
||||
/// `#[rust_analyzer::completions(ignore_flyimport_methods)]` (on a trait only).
|
||||
IgnoreFlyimportMethods,
|
||||
/// `#[rust_analyzer::completions(ignore_methods)]` (on a trait only).
|
||||
IgnoreMethods,
|
||||
}
|
||||
|
||||
impl Complete {
|
||||
pub fn extract(is_trait: bool, attrs: &Attrs) -> Complete {
|
||||
let mut do_not_complete = Complete::Yes;
|
||||
for ra_attr in attrs.rust_analyzer_tool() {
|
||||
let segments = ra_attr.path.segments();
|
||||
if segments.len() != 2 {
|
||||
continue;
|
||||
}
|
||||
let action = segments[1].symbol();
|
||||
if *action == sym::completions {
|
||||
match ra_attr.token_tree_value().map(|tt| tt.token_trees().flat_tokens()) {
|
||||
Some([tt::TokenTree::Leaf(tt::Leaf::Ident(ident))]) => {
|
||||
if ident.sym == sym::ignore_flyimport {
|
||||
do_not_complete = Complete::IgnoreFlyimport;
|
||||
} else if is_trait {
|
||||
if ident.sym == sym::ignore_methods {
|
||||
do_not_complete = Complete::IgnoreMethods;
|
||||
} else if ident.sym == sym::ignore_flyimport_methods {
|
||||
do_not_complete = Complete::IgnoreFlyimportMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
do_not_complete
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn for_trait_item(trait_attr: Complete, item_attr: Complete) -> Complete {
|
||||
match (trait_attr, item_attr) {
|
||||
(
|
||||
Complete::IgnoreFlyimportMethods
|
||||
| Complete::IgnoreFlyimport
|
||||
| Complete::IgnoreMethods,
|
||||
_,
|
||||
) => Complete::IgnoreFlyimport,
|
||||
_ => item_attr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,149 +0,0 @@
|
|||
//! Context for lowering paths.
|
||||
use std::{cell::OnceCell, mem};
|
||||
|
||||
use hir_expand::{span_map::SpanMap, AstId, HirFileId, InFile};
|
||||
use span::{AstIdMap, AstIdNode, Edition, EditionedFileId, FileId, RealSpanMap};
|
||||
use stdx::thin_vec::ThinVec;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
path::Path,
|
||||
type_ref::{PathId, TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
|
||||
};
|
||||
|
||||
pub struct LowerCtx<'a> {
|
||||
pub db: &'a dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
span_map: OnceCell<SpanMap>,
|
||||
ast_id_map: OnceCell<Arc<AstIdMap>>,
|
||||
impl_trait_bounds: Vec<ThinVec<TypeBound>>,
|
||||
// Prevent nested impl traits like `impl Foo<impl Bar>`.
|
||||
outer_impl_trait: bool,
|
||||
types_map: &'a mut TypesMap,
|
||||
types_source_map: &'a mut TypesSourceMap,
|
||||
}
|
||||
|
||||
impl<'a> LowerCtx<'a> {
|
||||
pub fn new(
|
||||
db: &'a dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
types_map: &'a mut TypesMap,
|
||||
types_source_map: &'a mut TypesSourceMap,
|
||||
) -> Self {
|
||||
LowerCtx {
|
||||
db,
|
||||
file_id,
|
||||
span_map: OnceCell::new(),
|
||||
ast_id_map: OnceCell::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
outer_impl_trait: false,
|
||||
types_map,
|
||||
types_source_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_span_map_cell(
|
||||
db: &'a dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
span_map: OnceCell<SpanMap>,
|
||||
types_map: &'a mut TypesMap,
|
||||
types_source_map: &'a mut TypesSourceMap,
|
||||
) -> Self {
|
||||
LowerCtx {
|
||||
db,
|
||||
file_id,
|
||||
span_map,
|
||||
ast_id_map: OnceCell::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
outer_impl_trait: false,
|
||||
types_map,
|
||||
types_source_map,
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepares a `LowerCtx` for synthetic AST that needs to be lowered. This is intended for IDE things.
|
||||
pub fn for_synthetic_ast(
|
||||
db: &'a dyn DefDatabase,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
types_map: &'a mut TypesMap,
|
||||
types_source_map: &'a mut TypesSourceMap,
|
||||
) -> Self {
|
||||
let file_id = EditionedFileId::new(
|
||||
FileId::from_raw(EditionedFileId::MAX_FILE_ID),
|
||||
Edition::Edition2015,
|
||||
);
|
||||
LowerCtx {
|
||||
db,
|
||||
// Make up an invalid file id, so that if we will try to actually access it salsa will panic.
|
||||
file_id: file_id.into(),
|
||||
span_map: SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(file_id))).into(),
|
||||
ast_id_map: ast_id_map.into(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
outer_impl_trait: false,
|
||||
types_map,
|
||||
types_source_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn span_map(&self) -> &SpanMap {
|
||||
self.span_map.get_or_init(|| self.db.span_map(self.file_id))
|
||||
}
|
||||
|
||||
pub(crate) fn lower_path(&mut self, ast: ast::Path) -> Option<Path> {
|
||||
Path::from_src(self, ast)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: AstIdNode>(&self, item: &N) -> AstId<N> {
|
||||
InFile::new(
|
||||
self.file_id,
|
||||
self.ast_id_map.get_or_init(|| self.db.ast_id_map(self.file_id)).ast_id(item),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn update_impl_traits_bounds_from_type_ref(&mut self, type_ref: TypeRefId) {
|
||||
TypeRef::walk(type_ref, self.types_map, &mut |tr| {
|
||||
if let TypeRef::ImplTrait(bounds) = tr {
|
||||
self.impl_trait_bounds.push(bounds.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn take_impl_traits_bounds(&mut self) -> Vec<ThinVec<TypeBound>> {
|
||||
mem::take(&mut self.impl_trait_bounds)
|
||||
}
|
||||
|
||||
pub(crate) fn outer_impl_trait(&self) -> bool {
|
||||
self.outer_impl_trait
|
||||
}
|
||||
|
||||
pub(crate) fn with_outer_impl_trait_scope<R>(
|
||||
&mut self,
|
||||
impl_trait: bool,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
let old = mem::replace(&mut self.outer_impl_trait, impl_trait);
|
||||
let result = f(self);
|
||||
self.outer_impl_trait = old;
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_type_ref(&mut self, type_ref: TypeRef, node: TypePtr) -> TypeRefId {
|
||||
let id = self.types_map.types.alloc(type_ref);
|
||||
self.types_source_map.types_map_back.insert(id, InFile::new(self.file_id, node));
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_type_ref_desugared(&mut self, type_ref: TypeRef) -> TypeRefId {
|
||||
self.types_map.types.alloc(type_ref)
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_error_type(&mut self) -> TypeRefId {
|
||||
self.types_map.types.alloc(TypeRef::Error)
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_path(&mut self, path: Path, node: TypePtr) -> PathId {
|
||||
PathId::from_type_ref_unchecked(self.alloc_type_ref(TypeRef::Path(path), node))
|
||||
}
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ enum Command {
|
|||
}
|
||||
|
||||
impl <> $crate::cmp::PartialOrd for Command< > where {
|
||||
fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> {
|
||||
fn partial_cmp(&self , other: &Self ) -> $crate::option::Option<$crate::cmp::Ordering> {
|
||||
match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) {
|
||||
$crate::option::Option::Some($crate::cmp::Ordering::Equal)=> {
|
||||
match (self , other) {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ macro_rules! f {
|
|||
};
|
||||
}
|
||||
|
||||
struct#0:1@58..64#4# MyTraitMap2#0:2@31..42#2# {#0:1@72..73#4#
|
||||
map#0:1@86..89#4#:#0:1@89..90#4# #0:1@89..90#4#::#0:1@91..93#4#std#0:1@93..96#4#::#0:1@96..98#4#collections#0:1@98..109#4#::#0:1@109..111#4#HashSet#0:1@111..118#4#<#0:1@118..119#4#(#0:1@119..120#4#)#0:1@120..121#4#>#0:1@121..122#4#,#0:1@122..123#4#
|
||||
}#0:1@132..133#4#
|
||||
struct#0:1@58..64#14336# MyTraitMap2#0:2@31..42#ROOT2024# {#0:1@72..73#14336#
|
||||
map#0:1@86..89#14336#:#0:1@89..90#14336# #0:1@89..90#14336#::#0:1@91..93#14336#std#0:1@93..96#14336#::#0:1@96..98#14336#collections#0:1@98..109#14336#::#0:1@109..111#14336#HashSet#0:1@111..118#14336#<#0:1@118..119#14336#(#0:1@119..120#14336#)#0:1@120..121#14336#>#0:1@121..122#14336#,#0:1@122..123#14336#
|
||||
}#0:1@132..133#14336#
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -75,12 +75,12 @@ macro_rules! f {
|
|||
};
|
||||
}
|
||||
|
||||
fn#0:2@30..32#2# main#0:2@33..37#2#(#0:2@37..38#2#)#0:2@38..39#2# {#0:2@40..41#2#
|
||||
1#0:2@50..51#2#;#0:2@51..52#2#
|
||||
1.0#0:2@61..64#2#;#0:2@64..65#2#
|
||||
(#0:2@74..75#2#(#0:2@75..76#2#1#0:2@76..77#2#,#0:2@77..78#2# )#0:2@78..79#2#,#0:2@79..80#2# )#0:2@80..81#2#.#0:2@81..82#2#0#0:2@82..85#2#.#0:2@82..85#2#0#0:2@82..85#2#;#0:2@85..86#2#
|
||||
let#0:2@95..98#2# x#0:2@99..100#2# =#0:2@101..102#2# 1#0:2@103..104#2#;#0:2@104..105#2#
|
||||
}#0:2@110..111#2#
|
||||
fn#0:2@30..32#ROOT2024# main#0:2@33..37#ROOT2024#(#0:2@37..38#ROOT2024#)#0:2@38..39#ROOT2024# {#0:2@40..41#ROOT2024#
|
||||
1#0:2@50..51#ROOT2024#;#0:2@51..52#ROOT2024#
|
||||
1.0#0:2@61..64#ROOT2024#;#0:2@64..65#ROOT2024#
|
||||
(#0:2@74..75#ROOT2024#(#0:2@75..76#ROOT2024#1#0:2@76..77#ROOT2024#,#0:2@77..78#ROOT2024# )#0:2@78..79#ROOT2024#,#0:2@79..80#ROOT2024# )#0:2@80..81#ROOT2024#.#0:2@81..82#ROOT2024#0#0:2@82..85#ROOT2024#.#0:2@82..85#ROOT2024#0#0:2@82..85#ROOT2024#;#0:2@85..86#ROOT2024#
|
||||
let#0:2@95..98#ROOT2024# x#0:2@99..100#ROOT2024# =#0:2@101..102#ROOT2024# 1#0:2@103..104#ROOT2024#;#0:2@104..105#ROOT2024#
|
||||
}#0:2@110..111#ROOT2024#
|
||||
|
||||
|
||||
"#]],
|
||||
|
|
@ -171,7 +171,7 @@ fn main(foo: ()) {
|
|||
}
|
||||
|
||||
fn main(foo: ()) {
|
||||
/* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#2#;
|
||||
/* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#ROOT2024#;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ macro_rules! mk_struct {
|
|||
#[macro_use]
|
||||
mod foo;
|
||||
|
||||
struct#1:1@59..65#4# Foo#0:2@32..35#2#(#1:1@70..71#4#u32#0:2@41..44#2#)#1:1@74..75#4#;#1:1@75..76#4#
|
||||
struct#1:1@59..65#14336# Foo#0:2@32..35#ROOT2024#(#1:1@70..71#14336#u32#0:2@41..44#ROOT2024#)#1:1@74..75#14336#;#1:1@75..76#14336#
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -423,10 +423,10 @@ m! { foo, bar }
|
|||
macro_rules! m {
|
||||
($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
|
||||
}
|
||||
impl#\4# Bar#\4# {#\4#
|
||||
fn#\4# foo#\2#(#\4#)#\4# {#\4#}#\4#
|
||||
fn#\4# bar#\2#(#\4#)#\4# {#\4#}#\4#
|
||||
}#\4#
|
||||
impl#\14336# Bar#\14336# {#\14336#
|
||||
fn#\14336# foo#\ROOT2024#(#\14336#)#\14336# {#\14336#}#\14336#
|
||||
fn#\14336# bar#\ROOT2024#(#\14336#)#\14336# {#\14336#}#\14336#
|
||||
}#\14336#
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -1408,7 +1408,7 @@ ok!();
|
|||
macro_rules! m2 {
|
||||
($($a:expr => $b:ident)* _ => $c:expr) => { ok!(); }
|
||||
}
|
||||
ok!();
|
||||
/* error: unexpected token in input */ok!();
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -1979,3 +1979,51 @@ fn f() {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn semicolon_does_not_glue() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! bug {
|
||||
($id: expr) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*; $norm: expr) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*;; $print: expr) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => {
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
let _ = bug!(a;;;test);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! bug {
|
||||
($id: expr) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*; $norm: expr) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*;; $print: expr) => {
|
||||
true
|
||||
};
|
||||
($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => {
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
let _ = true;
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,9 +162,10 @@ fn test() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn expr_dont_match_inline_const() {
|
||||
fn expr_inline_const() {
|
||||
check(
|
||||
r#"
|
||||
//- /lib.rs edition:2021
|
||||
macro_rules! foo {
|
||||
($e:expr) => { $e }
|
||||
}
|
||||
|
|
@ -181,6 +182,30 @@ macro_rules! foo {
|
|||
fn test() {
|
||||
/* error: no rule matches input tokens */missing;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
//- /lib.rs edition:2024
|
||||
macro_rules! foo {
|
||||
($e:expr) => { $e }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
foo!(const { 3 });
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! foo {
|
||||
($e:expr) => { $e }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
(const {
|
||||
3
|
||||
}
|
||||
);
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -582,8 +582,8 @@ macro_rules! arbitrary {
|
|||
}
|
||||
|
||||
impl <A: Arbitrary> $crate::arbitrary::Arbitrary for Vec<A> {
|
||||
type Parameters = RangedParams1<A::Parameters>;
|
||||
type Strategy = VecStrategy<A::Strategy>;
|
||||
type Parameters = RangedParams1<A::Parameters> ;
|
||||
type Strategy = VecStrategy<A::Strategy> ;
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { {
|
||||
let product_unpack![range, a] = args;
|
||||
vec(any_with::<A>(a), range)
|
||||
|
|
|
|||
|
|
@ -16,34 +16,34 @@ mod proc_macros;
|
|||
|
||||
use std::{iter, ops::Range, sync};
|
||||
|
||||
use base_db::SourceDatabase;
|
||||
use base_db::RootQueryDb;
|
||||
use expect_test::Expect;
|
||||
use hir_expand::{
|
||||
InFile, MacroCallKind, MacroKind,
|
||||
db::ExpandDatabase,
|
||||
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
|
||||
span_map::SpanMapRef,
|
||||
InFile, MacroCallKind, MacroFileId, MacroFileIdExt, MacroKind,
|
||||
};
|
||||
use intern::Symbol;
|
||||
use itertools::Itertools;
|
||||
use span::{Edition, Span};
|
||||
use stdx::{format_to, format_to_acc};
|
||||
use syntax::{
|
||||
ast::{self, edit::IndentLevel},
|
||||
AstNode,
|
||||
SyntaxKind::{COMMENT, EOF, IDENT, LIFETIME_IDENT},
|
||||
SyntaxNode, T,
|
||||
ast::{self, edit::IndentLevel},
|
||||
};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{
|
||||
AdtId, AsMacroCall, Lookup, ModuleDefId,
|
||||
db::DefDatabase,
|
||||
nameres::{DefMap, MacroSubNs, ModuleSource},
|
||||
resolver::HasResolver,
|
||||
src::HasSource,
|
||||
test_db::TestDB,
|
||||
tt::TopSubtree,
|
||||
AdtId, AsMacroCall, Lookup, ModuleDefId,
|
||||
};
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -63,9 +63,11 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
|
|||
MacroCallKind::Derive { ast_id, .. } => ast_id.map(|it| it.erase()),
|
||||
MacroCallKind::Attr { ast_id, .. } => ast_id.map(|it| it.erase()),
|
||||
};
|
||||
let ast = db
|
||||
.parse(ast_id.file_id.file_id().expect("macros inside macros are not supported"))
|
||||
.syntax_node();
|
||||
|
||||
let editioned_file_id =
|
||||
ast_id.file_id.file_id().expect("macros inside macros are not supported");
|
||||
|
||||
let ast = db.parse(editioned_file_id).syntax_node();
|
||||
let ast_id_map = db.ast_id_map(ast_id.file_id);
|
||||
let node = ast_id_map.get_erased(ast_id.value).to_node(&ast);
|
||||
Some((node.text_range(), errors))
|
||||
|
|
@ -126,15 +128,19 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
|
||||
let macro_call = InFile::new(source.file_id, ¯o_call);
|
||||
let res = macro_call
|
||||
.as_call_id_with_errors(&db, krate, |path| {
|
||||
resolver
|
||||
.resolve_path_as_macro(&db, path, Some(MacroSubNs::Bang))
|
||||
.map(|(it, _)| db.macro_def(it))
|
||||
})
|
||||
.as_call_id_with_errors(
|
||||
&db,
|
||||
krate,
|
||||
|path| {
|
||||
resolver
|
||||
.resolve_path_as_macro(&db, path, Some(MacroSubNs::Bang))
|
||||
.map(|(it, _)| db.macro_def(it))
|
||||
},
|
||||
&mut |_, _| (),
|
||||
)
|
||||
.unwrap();
|
||||
let macro_call_id = res.value.unwrap();
|
||||
let macro_file = MacroFileId { macro_call_id };
|
||||
let mut expansion_result = db.parse_macro_expansion(macro_file);
|
||||
let mut expansion_result = db.parse_macro_expansion(macro_call_id);
|
||||
expansion_result.err = expansion_result.err.or(res.err);
|
||||
expansions.push((macro_call.value.clone(), expansion_result));
|
||||
}
|
||||
|
|
@ -357,7 +363,7 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
|
|||
_: Span,
|
||||
_: Span,
|
||||
_: Span,
|
||||
_: Option<String>,
|
||||
_: String,
|
||||
) -> Result<TopSubtree, ProcMacroExpansionError> {
|
||||
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
|
||||
subtree,
|
||||
|
|
@ -371,4 +377,8 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
|
|||
panic!("got invalid macro input: {:?}", parse.errors());
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
|
||||
other.as_any().type_id() == std::any::TypeId::of::<Self>()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,9 +181,9 @@ fn foo(&self) {
|
|||
self.0. 1;
|
||||
}
|
||||
|
||||
fn#0:1@45..47#2# foo#0:1@48..51#2#(#0:1@51..52#2#�:1@52..53#2#self#0:1@53..57#2# )#0:1@57..58#2# {#0:1@59..60#2#
|
||||
self#0:1@65..69#2# .#0:1@69..70#2#0#0:1@70..71#2#.#0:1@71..72#2#1#0:1@73..74#2#;#0:1@74..75#2#
|
||||
}#0:1@76..77#2#"#]],
|
||||
fn#0:1@45..47#ROOT2024# foo#0:1@48..51#ROOT2024#(#0:1@51..52#ROOT2024#�:1@52..53#ROOT2024#self#0:1@53..57#ROOT2024# )#0:1@57..58#ROOT2024# {#0:1@59..60#ROOT2024#
|
||||
self#0:1@65..69#ROOT2024# .#0:1@69..70#ROOT2024#0#0:1@70..71#ROOT2024#.#0:1@71..72#ROOT2024#1#0:1@73..74#ROOT2024#;#0:1@74..75#ROOT2024#
|
||||
}#0:1@76..77#ROOT2024#"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
//! path and, upon success, we run macro expansion and "collect module" phase on
|
||||
//! the result
|
||||
|
||||
pub mod assoc;
|
||||
pub mod attr_resolution;
|
||||
mod collector;
|
||||
pub mod diagnostics;
|
||||
|
|
@ -59,30 +60,30 @@ mod tests;
|
|||
|
||||
use std::ops::Deref;
|
||||
|
||||
use base_db::CrateId;
|
||||
use base_db::Crate;
|
||||
use hir_expand::{
|
||||
name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, mod_path::ModPath,
|
||||
name::Name, proc_macro::ProcMacroKind,
|
||||
};
|
||||
use intern::Symbol;
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, EditionedFileId, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID};
|
||||
use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, AstNode, SmolStr, SyntaxNode};
|
||||
use syntax::{AstNode, SmolStr, SyntaxNode, ToSmolStr, ast};
|
||||
use triomphe::Arc;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, FxIndexMap,
|
||||
LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
|
||||
db::DefDatabase,
|
||||
item_scope::{BuiltinShadowMode, ItemScope},
|
||||
item_tree::{ItemTreeId, Mod, TreeId},
|
||||
nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode},
|
||||
path::ModPath,
|
||||
per_ns::PerNs,
|
||||
visibility::{Visibility, VisibilityExplicitness},
|
||||
AstId, BlockId, BlockLoc, CrateRootModuleId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
|
||||
FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
|
||||
};
|
||||
|
||||
pub use self::path_resolution::ResolvePathResultPrefixInfo;
|
||||
|
|
@ -95,6 +96,39 @@ const PREDEFINED_TOOLS: &[SmolStr] = &[
|
|||
SmolStr::new_static("rust_analyzer"),
|
||||
];
|
||||
|
||||
/// Parts of the def map that are only needed when analyzing code in the same crate.
|
||||
///
|
||||
/// There are some data in the def map (e.g. extern prelude) that is only needed when analyzing
|
||||
/// things in the same crate (and maybe in the IDE layer), e.g. the extern prelude. If we put
|
||||
/// it in the DefMap dependant DefMaps will be invalidated when they change (e.g. when we add
|
||||
/// a dependency to the crate). Instead we split them out of the DefMap into a LocalDefMap struct.
|
||||
/// `crate_local_def_map()` returns both, and `crate_def_map()` returns only the external-relevant
|
||||
/// DefMap.
|
||||
#[derive(Debug, PartialEq, Eq, Default)]
|
||||
pub struct LocalDefMap {
|
||||
// FIXME: There are probably some other things that could be here, but this is less severe and you
|
||||
// need to be careful with things that block def maps also have.
|
||||
/// The extern prelude which contains all root modules of external crates that are in scope.
|
||||
extern_prelude: FxIndexMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
|
||||
}
|
||||
|
||||
impl LocalDefMap {
|
||||
pub(crate) const EMPTY: &Self =
|
||||
&Self { extern_prelude: FxIndexMap::with_hasher(rustc_hash::FxBuildHasher) };
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
let Self { extern_prelude } = self;
|
||||
extern_prelude.shrink_to_fit();
|
||||
}
|
||||
|
||||
pub(crate) fn extern_prelude(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_
|
||||
{
|
||||
self.extern_prelude.iter().map(|(name, &def)| (name, def))
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the results of (early) name resolution.
|
||||
///
|
||||
/// A `DefMap` stores the module tree and the definitions that are in scope in every module after
|
||||
|
|
@ -107,7 +141,7 @@ const PREDEFINED_TOOLS: &[SmolStr] = &[
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct DefMap {
|
||||
/// The crate this `DefMap` belongs to.
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
/// When this is a block def map, this will hold the block id of the block and module that
|
||||
/// contains this block.
|
||||
block: Option<BlockInfo>,
|
||||
|
|
@ -124,12 +158,15 @@ pub struct DefMap {
|
|||
/// this contains all kinds of macro, not just `macro_rules!` macro.
|
||||
/// ExternCrateId being None implies it being imported from the general prelude import.
|
||||
macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>,
|
||||
pub(crate) enum_definitions: FxHashMap<EnumId, Box<[EnumVariantId]>>,
|
||||
|
||||
// FIXME: AstId's are fairly unstable
|
||||
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
|
||||
/// attributes.
|
||||
// FIXME: Figure out a better way for the IDE layer to resolve these?
|
||||
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
|
||||
// FIXME: AstId's are fairly unstable
|
||||
/// A mapping from [`hir_expand::MacroDefId`] to [`crate::MacroId`].
|
||||
pub macro_def_to_macro_id: FxHashMap<ErasedAstId, MacroId>,
|
||||
|
||||
/// The diagnostics that need to be emitted for this crate.
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
|
|
@ -141,9 +178,6 @@ pub struct DefMap {
|
|||
/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct DefMapCrateData {
|
||||
/// The extern prelude which contains all root modules of external crates that are in scope.
|
||||
extern_prelude: FxIndexMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
|
||||
|
||||
/// Side table for resolving derive helpers.
|
||||
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
|
||||
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
|
||||
|
|
@ -166,7 +200,6 @@ struct DefMapCrateData {
|
|||
impl DefMapCrateData {
|
||||
fn new(edition: Edition) -> Self {
|
||||
Self {
|
||||
extern_prelude: FxIndexMap::default(),
|
||||
exported_derives: FxHashMap::default(),
|
||||
fn_proc_macro_mapping: FxHashMap::default(),
|
||||
registered_attrs: Vec::new(),
|
||||
|
|
@ -182,7 +215,6 @@ impl DefMapCrateData {
|
|||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
let Self {
|
||||
extern_prelude,
|
||||
exported_derives,
|
||||
fn_proc_macro_mapping,
|
||||
registered_attrs,
|
||||
|
|
@ -194,7 +226,6 @@ impl DefMapCrateData {
|
|||
edition: _,
|
||||
recursion_limit: _,
|
||||
} = self;
|
||||
extern_prelude.shrink_to_fit();
|
||||
exported_derives.shrink_to_fit();
|
||||
fn_proc_macro_mapping.shrink_to_fit();
|
||||
registered_attrs.shrink_to_fit();
|
||||
|
|
@ -219,11 +250,11 @@ struct BlockRelativeModuleId {
|
|||
}
|
||||
|
||||
impl BlockRelativeModuleId {
|
||||
fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
|
||||
fn def_map(self, db: &dyn DefDatabase, krate: Crate) -> Arc<DefMap> {
|
||||
self.into_module(krate).def_map(db)
|
||||
}
|
||||
|
||||
fn into_module(self, krate: CrateId) -> ModuleId {
|
||||
fn into_module(self, krate: Crate) -> ModuleId {
|
||||
ModuleId { krate, block: self.block, local_id: self.local_id }
|
||||
}
|
||||
|
||||
|
|
@ -295,18 +326,19 @@ impl ModuleOrigin {
|
|||
/// That is, a file or a `mod foo {}` with items.
|
||||
pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
|
||||
match self {
|
||||
&ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
|
||||
let sf = db.parse(definition).tree();
|
||||
InFile::new(definition.into(), ModuleSource::SourceFile(sf))
|
||||
&ModuleOrigin::File { definition: editioned_file_id, .. }
|
||||
| &ModuleOrigin::CrateRoot { definition: editioned_file_id } => {
|
||||
let sf = db.parse(editioned_file_id).tree();
|
||||
InFile::new(editioned_file_id.into(), ModuleSource::SourceFile(sf))
|
||||
}
|
||||
&ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
|
||||
definition_tree_id.file_id(),
|
||||
ModuleSource::Module(
|
||||
AstId::new(definition_tree_id.file_id(), definition).to_node(db.upcast()),
|
||||
AstId::new(definition_tree_id.file_id(), definition).to_node(db),
|
||||
),
|
||||
),
|
||||
ModuleOrigin::BlockExpr { block, .. } => {
|
||||
InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast())))
|
||||
InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -334,14 +366,28 @@ impl DefMap {
|
|||
self.data.edition
|
||||
}
|
||||
|
||||
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> {
|
||||
let crate_graph = db.crate_graph();
|
||||
let krate = &crate_graph[crate_id];
|
||||
let name = krate.display_name.as_deref().map(Symbol::as_str).unwrap_or_default();
|
||||
let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
|
||||
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: Crate) -> Arc<DefMap> {
|
||||
db.crate_local_def_map(crate_id).0
|
||||
}
|
||||
|
||||
pub(crate) fn crate_local_def_map_query(
|
||||
db: &dyn DefDatabase,
|
||||
crate_id: Crate,
|
||||
) -> (Arc<DefMap>, Arc<LocalDefMap>) {
|
||||
let krate = crate_id.data(db);
|
||||
let _p = tracing::info_span!(
|
||||
"crate_def_map_query",
|
||||
name=?crate_id
|
||||
.extra_data(db)
|
||||
.display_name
|
||||
.as_ref()
|
||||
.map(|it| it.crate_name().to_smolstr())
|
||||
.unwrap_or_default()
|
||||
)
|
||||
.entered();
|
||||
|
||||
let module_data = ModuleData::new(
|
||||
ModuleOrigin::CrateRoot { definition: krate.root_file_id() },
|
||||
ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) },
|
||||
Visibility::Public,
|
||||
);
|
||||
|
||||
|
|
@ -351,10 +397,14 @@ impl DefMap {
|
|||
module_data,
|
||||
None,
|
||||
);
|
||||
let def_map =
|
||||
collector::collect_defs(db, def_map, TreeId::new(krate.root_file_id().into(), None));
|
||||
let (def_map, local_def_map) = collector::collect_defs(
|
||||
db,
|
||||
def_map,
|
||||
TreeId::new(krate.root_file_id(db).into(), None),
|
||||
None,
|
||||
);
|
||||
|
||||
Arc::new(def_map)
|
||||
(Arc::new(def_map), Arc::new(local_def_map))
|
||||
}
|
||||
|
||||
pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
|
||||
|
|
@ -367,10 +417,10 @@ impl DefMap {
|
|||
let module_data =
|
||||
ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility);
|
||||
|
||||
let parent_map = module.def_map(db);
|
||||
let (crate_map, crate_local_map) = db.crate_local_def_map(module.krate);
|
||||
let def_map = DefMap::empty(
|
||||
module.krate,
|
||||
parent_map.data.clone(),
|
||||
crate_map.data.clone(),
|
||||
module_data,
|
||||
Some(BlockInfo {
|
||||
block: block_id,
|
||||
|
|
@ -378,13 +428,17 @@ impl DefMap {
|
|||
}),
|
||||
);
|
||||
|
||||
let def_map =
|
||||
collector::collect_defs(db, def_map, TreeId::new(ast_id.file_id, Some(block_id)));
|
||||
let (def_map, _) = collector::collect_defs(
|
||||
db,
|
||||
def_map,
|
||||
TreeId::new(ast_id.file_id, Some(block_id)),
|
||||
Some(crate_local_map),
|
||||
);
|
||||
Arc::new(def_map)
|
||||
}
|
||||
|
||||
fn empty(
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
crate_data: Arc<DefMapCrateData>,
|
||||
module_data: ModuleData,
|
||||
block: Option<BlockInfo>,
|
||||
|
|
@ -401,8 +455,8 @@ impl DefMap {
|
|||
macro_use_prelude: FxHashMap::default(),
|
||||
derive_helpers_in_scope: FxHashMap::default(),
|
||||
diagnostics: Vec::new(),
|
||||
enum_definitions: FxHashMap::default(),
|
||||
data: crate_data,
|
||||
macro_def_to_macro_id: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
fn shrink_to_fit(&mut self) {
|
||||
|
|
@ -416,14 +470,14 @@ impl DefMap {
|
|||
krate: _,
|
||||
prelude: _,
|
||||
data: _,
|
||||
enum_definitions,
|
||||
macro_def_to_macro_id,
|
||||
} = self;
|
||||
|
||||
macro_def_to_macro_id.shrink_to_fit();
|
||||
macro_use_prelude.shrink_to_fit();
|
||||
diagnostics.shrink_to_fit();
|
||||
modules.shrink_to_fit();
|
||||
derive_helpers_in_scope.shrink_to_fit();
|
||||
enum_definitions.shrink_to_fit();
|
||||
for (_, module) in modules.iter_mut() {
|
||||
module.children.shrink_to_fit();
|
||||
module.scope.shrink_to_fit();
|
||||
|
|
@ -432,11 +486,15 @@ impl DefMap {
|
|||
}
|
||||
|
||||
impl DefMap {
|
||||
pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
|
||||
pub fn modules_for_file<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn DefDatabase,
|
||||
file_id: FileId,
|
||||
) -> impl Iterator<Item = LocalModuleId> + 'a {
|
||||
self.modules
|
||||
.iter()
|
||||
.filter(move |(_id, data)| {
|
||||
data.origin.file_id().map(EditionedFileId::file_id) == Some(file_id)
|
||||
data.origin.file_id().map(|file_id| file_id.file_id(db)) == Some(file_id)
|
||||
})
|
||||
.map(|(id, _data)| id)
|
||||
}
|
||||
|
|
@ -476,7 +534,7 @@ impl DefMap {
|
|||
self.data.fn_proc_macro_mapping.get(&id).copied()
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> CrateId {
|
||||
pub fn krate(&self) -> Crate {
|
||||
self.krate
|
||||
}
|
||||
|
||||
|
|
@ -551,12 +609,12 @@ impl DefMap {
|
|||
) {
|
||||
format_to!(buf, "{}\n", path);
|
||||
|
||||
map.modules[module].scope.dump(db.upcast(), buf);
|
||||
map.modules[module].scope.dump(db, buf);
|
||||
|
||||
for (name, child) in
|
||||
map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
|
||||
{
|
||||
let path = format!("{path}::{}", name.display(db.upcast(), Edition::LATEST));
|
||||
let path = format!("{path}::{}", name.display(db, Edition::LATEST));
|
||||
buf.push('\n');
|
||||
go(buf, db, map, &path, *child);
|
||||
}
|
||||
|
|
@ -587,19 +645,13 @@ impl DefMap {
|
|||
self.prelude
|
||||
}
|
||||
|
||||
pub(crate) fn extern_prelude(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_
|
||||
{
|
||||
self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_use_prelude(&self) -> &FxHashMap<Name, (MacroId, Option<ExternCrateId>)> {
|
||||
&self.macro_use_prelude
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
|
|
@ -607,6 +659,7 @@ impl DefMap {
|
|||
expected_macro_subns: Option<MacroSubNs>,
|
||||
) -> (PerNs, Option<usize>) {
|
||||
let res = self.resolve_path_fp_with_macro(
|
||||
local_def_map,
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
original_module,
|
||||
|
|
@ -621,12 +674,14 @@ impl DefMap {
|
|||
/// points at the unresolved segments.
|
||||
pub(crate) fn resolve_path_locally(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> (PerNs, Option<usize>, ResolvePathResultPrefixInfo) {
|
||||
let res = self.resolve_path_fp_with_macro_single(
|
||||
local_def_map,
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
original_module,
|
||||
|
|
@ -695,17 +750,14 @@ impl ModuleData {
|
|||
&ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
|
||||
InFile::new(
|
||||
definition.into(),
|
||||
ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID)
|
||||
.to_range(db.upcast()),
|
||||
ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID).to_range(db),
|
||||
)
|
||||
}
|
||||
&ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
|
||||
definition_tree_id.file_id(),
|
||||
AstId::new(definition_tree_id.file_id(), definition).to_range(db.upcast()),
|
||||
AstId::new(definition_tree_id.file_id(), definition).to_range(db),
|
||||
),
|
||||
ModuleOrigin::BlockExpr { block, .. } => {
|
||||
InFile::new(block.file_id, block.to_range(db.upcast()))
|
||||
}
|
||||
ModuleOrigin::BlockExpr { block, .. } => InFile::new(block.file_id, block.to_range(db)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -713,7 +765,7 @@ impl ModuleData {
|
|||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Module>> {
|
||||
let decl = self.origin.declaration()?;
|
||||
let value = decl.to_node(db.upcast());
|
||||
let value = decl.to_node(db);
|
||||
Some(InFile { file_id: decl.file_id, value })
|
||||
}
|
||||
|
||||
|
|
@ -721,7 +773,7 @@ impl ModuleData {
|
|||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source_range(&self, db: &dyn DefDatabase) -> Option<InFile<TextRange>> {
|
||||
let decl = self.origin.declaration()?;
|
||||
Some(InFile { file_id: decl.file_id, value: decl.to_range(db.upcast()) })
|
||||
Some(InFile { file_id: decl.file_id, value: decl.to_range(db) })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
307
src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs
Normal file
307
src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
//! Expansion of associated items
|
||||
|
||||
use hir_expand::{AstId, InFile, Intern, Lookup, MacroCallKind, MacroDefKind, name::Name};
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AssocItemId, AstIdWithPath, ConstLoc, FunctionId, FunctionLoc, ImplId, ItemContainerId,
|
||||
ItemLoc, MacroCallId, ModuleId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
db::DefDatabase,
|
||||
item_tree::{AssocItem, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId},
|
||||
macro_call_as_call_id,
|
||||
nameres::{
|
||||
DefMap, LocalDefMap, MacroSubNs,
|
||||
attr_resolution::ResolvedAttr,
|
||||
diagnostics::{DefDiagnostic, DefDiagnostics},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TraitItems {
|
||||
pub items: Box<[(Name, AssocItemId)]>,
|
||||
// box it as the vec is usually empty anyways
|
||||
// FIXME: AstIds are rather unstable...
|
||||
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
|
||||
}
|
||||
|
||||
impl TraitItems {
|
||||
#[inline]
|
||||
pub(crate) fn trait_items_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitItems> {
|
||||
db.trait_items_with_diagnostics(tr).0
|
||||
}
|
||||
|
||||
pub(crate) fn trait_items_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
tr: TraitId,
|
||||
) -> (Arc<TraitItems>, DefDiagnostics) {
|
||||
let ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
|
||||
|
||||
let collector = AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr));
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let (items, macro_calls, diagnostics) =
|
||||
collector.collect(&item_tree, tree_id.tree_id(), &item_tree[tree_id.value].items);
|
||||
|
||||
(Arc::new(TraitItems { macro_calls, items }), DefDiagnostics::new(diagnostics))
|
||||
}
|
||||
|
||||
pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
|
||||
self.items.iter().filter_map(|(_name, item)| match item {
|
||||
AssocItemId::TypeAliasId(t) => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
|
||||
self.items.iter().find_map(|(item_name, item)| match item {
|
||||
AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn method_by_name(&self, name: &Name) -> Option<FunctionId> {
|
||||
self.items.iter().find_map(|(item_name, item)| match item {
|
||||
AssocItemId::FunctionId(t) if item_name == name => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
|
||||
self.macro_calls.iter().flat_map(|it| it.iter()).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ImplItems {
|
||||
pub items: Box<[(Name, AssocItemId)]>,
|
||||
// box it as the vec is usually empty anyways
|
||||
// FIXME: AstIds are rather unstable...
|
||||
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
|
||||
}
|
||||
|
||||
impl ImplItems {
|
||||
#[inline]
|
||||
pub(crate) fn impl_items_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplItems> {
|
||||
db.impl_items_with_diagnostics(id).0
|
||||
}
|
||||
|
||||
pub(crate) fn impl_items_with_diagnostics_query(
|
||||
db: &dyn DefDatabase,
|
||||
id: ImplId,
|
||||
) -> (Arc<ImplItems>, DefDiagnostics) {
|
||||
let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered();
|
||||
let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
|
||||
|
||||
let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id));
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let (items, macro_calls, diagnostics) =
|
||||
collector.collect(&item_tree, tree_id.tree_id(), &item_tree[tree_id.value].items);
|
||||
|
||||
(Arc::new(ImplItems { items, macro_calls }), DefDiagnostics::new(diagnostics))
|
||||
}
|
||||
|
||||
pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
|
||||
self.macro_calls.iter().flat_map(|it| it.iter()).copied()
|
||||
}
|
||||
}
|
||||
|
||||
struct AssocItemCollector<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
module_id: ModuleId,
|
||||
def_map: Arc<DefMap>,
|
||||
local_def_map: Arc<LocalDefMap>,
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
container: ItemContainerId,
|
||||
|
||||
depth: usize,
|
||||
items: Vec<(Name, AssocItemId)>,
|
||||
macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
|
||||
}
|
||||
|
||||
impl<'a> AssocItemCollector<'a> {
|
||||
fn new(db: &'a dyn DefDatabase, module_id: ModuleId, container: ItemContainerId) -> Self {
|
||||
let (def_map, local_def_map) = module_id.local_def_map(db);
|
||||
Self {
|
||||
db,
|
||||
module_id,
|
||||
def_map,
|
||||
local_def_map,
|
||||
container,
|
||||
items: Vec::new(),
|
||||
|
||||
depth: 0,
|
||||
macro_calls: Vec::new(),
|
||||
diagnostics: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect(
|
||||
mut self,
|
||||
item_tree: &ItemTree,
|
||||
tree_id: TreeId,
|
||||
assoc_items: &[AssocItem],
|
||||
) -> (
|
||||
Box<[(Name, AssocItemId)]>,
|
||||
Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
|
||||
Vec<DefDiagnostic>,
|
||||
) {
|
||||
self.items.reserve(assoc_items.len());
|
||||
for &item in assoc_items {
|
||||
self.collect_item(item_tree, tree_id, item);
|
||||
}
|
||||
(
|
||||
self.items.into_boxed_slice(),
|
||||
if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
|
||||
self.diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
fn collect_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem) {
|
||||
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
|
||||
if !attrs.is_cfg_enabled(self.module_id.krate.cfg_options(self.db)) {
|
||||
self.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id.local_id,
|
||||
tree_id,
|
||||
ModItem::from(item).into(),
|
||||
attrs.cfg().unwrap(),
|
||||
self.module_id.krate.cfg_options(self.db).clone(),
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
'attrs: for attr in &*attrs {
|
||||
let ast_id = AstId::new(tree_id.file_id(), item.ast_id(item_tree).upcast());
|
||||
let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
|
||||
|
||||
match self.def_map.resolve_attr_macro(
|
||||
&self.local_def_map,
|
||||
self.db,
|
||||
self.module_id.local_id,
|
||||
ast_id_with_path,
|
||||
attr,
|
||||
) {
|
||||
Ok(ResolvedAttr::Macro(call_id)) => {
|
||||
let loc = self.db.lookup_intern_macro_call(call_id);
|
||||
if let MacroDefKind::ProcMacro(_, exp, _) = loc.def.kind {
|
||||
// If there's no expander for the proc macro (e.g. the
|
||||
// proc macro is ignored, or building the proc macro
|
||||
// crate failed), skip expansion like we would if it was
|
||||
// disabled. This is analogous to the handling in
|
||||
// `DefCollector::collect_macros`.
|
||||
if let Some(err) = exp.as_expand_error(self.module_id.krate) {
|
||||
self.diagnostics.push(DefDiagnostic::macro_error(
|
||||
self.module_id.local_id,
|
||||
ast_id,
|
||||
(*attr.path).clone(),
|
||||
err,
|
||||
));
|
||||
continue 'attrs;
|
||||
}
|
||||
}
|
||||
|
||||
self.macro_calls.push((ast_id, call_id));
|
||||
self.collect_macro_items(call_id);
|
||||
return;
|
||||
}
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
self.module_id.local_id,
|
||||
MacroCallKind::Attr { ast_id, attr_args: None, invoc_attr_index: attr.id },
|
||||
attr.path().clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.record_item(item_tree, tree_id, item);
|
||||
}
|
||||
|
||||
fn record_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem) {
|
||||
match item {
|
||||
AssocItem::Function(id) => {
|
||||
let item = &item_tree[id];
|
||||
let def =
|
||||
FunctionLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
|
||||
.intern(self.db);
|
||||
self.items.push((item.name.clone(), def.into()));
|
||||
}
|
||||
AssocItem::TypeAlias(id) => {
|
||||
let item = &item_tree[id];
|
||||
let def =
|
||||
TypeAliasLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
|
||||
.intern(self.db);
|
||||
self.items.push((item.name.clone(), def.into()));
|
||||
}
|
||||
AssocItem::Const(id) => {
|
||||
let item = &item_tree[id];
|
||||
let Some(name) = item.name.clone() else { return };
|
||||
let def = ConstLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
|
||||
.intern(self.db);
|
||||
self.items.push((name, def.into()));
|
||||
}
|
||||
AssocItem::MacroCall(call) => {
|
||||
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
|
||||
|
||||
let resolver = |path: &_| {
|
||||
self.def_map
|
||||
.resolve_path(
|
||||
&self.local_def_map,
|
||||
self.db,
|
||||
self.module_id.local_id,
|
||||
path,
|
||||
crate::item_scope::BuiltinShadowMode::Other,
|
||||
Some(MacroSubNs::Bang),
|
||||
)
|
||||
.0
|
||||
.take_macros()
|
||||
.map(|it| self.db.macro_def(it))
|
||||
};
|
||||
match macro_call_as_call_id(
|
||||
self.db,
|
||||
&AstIdWithPath::new(tree_id.file_id(), ast_id, Clone::clone(path)),
|
||||
ctxt,
|
||||
expand_to,
|
||||
self.module_id.krate(),
|
||||
resolver,
|
||||
&mut |ptr, call_id| {
|
||||
self.macro_calls.push((ptr.map(|(_, it)| it.upcast()), call_id))
|
||||
},
|
||||
) {
|
||||
Ok(Some(call_id)) => {
|
||||
self.macro_calls
|
||||
.push((InFile::new(tree_id.file_id(), ast_id.upcast()), call_id));
|
||||
self.collect_macro_items(call_id);
|
||||
}
|
||||
Ok(None) => (),
|
||||
Err(_) => {
|
||||
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
self.module_id.local_id,
|
||||
MacroCallKind::FnLike {
|
||||
ast_id: InFile::new(tree_id.file_id(), ast_id),
|
||||
expand_to,
|
||||
eager: None,
|
||||
},
|
||||
Clone::clone(path),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_macro_items(&mut self, macro_call_id: MacroCallId) {
|
||||
if self.depth > self.def_map.recursion_limit() as usize {
|
||||
tracing::warn!("macro expansion is too deep");
|
||||
return;
|
||||
}
|
||||
let tree_id = TreeId::new(macro_call_id.into(), None);
|
||||
let item_tree = self.db.file_item_tree(macro_call_id.into());
|
||||
|
||||
self.depth += 1;
|
||||
for item in item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item) {
|
||||
self.collect_item(&item_tree, tree_id, item);
|
||||
}
|
||||
self.depth -= 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
//! Post-nameres attribute resolution.
|
||||
|
||||
use base_db::CrateId;
|
||||
use base_db::Crate;
|
||||
use hir_expand::{
|
||||
MacroCallId, MacroCallKind, MacroDefId,
|
||||
attrs::{Attr, AttrId, AttrInput},
|
||||
inert_attr_macro::find_builtin_attr_idx,
|
||||
MacroCallId, MacroCallKind, MacroDefId,
|
||||
mod_path::{ModPath, PathKind},
|
||||
};
|
||||
use span::SyntaxContextId;
|
||||
use span::SyntaxContext;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AstIdWithPath, LocalModuleId, MacroId, UnresolvedMacro,
|
||||
db::DefDatabase,
|
||||
item_scope::BuiltinShadowMode,
|
||||
nameres::path_resolution::ResolveMode,
|
||||
path::{self, ModPath, PathKind},
|
||||
AstIdWithPath, LocalModuleId, MacroId, UnresolvedMacro,
|
||||
nameres::{LocalDefMap, path_resolution::ResolveMode},
|
||||
};
|
||||
|
||||
use super::{DefMap, MacroSubNs};
|
||||
|
|
@ -30,6 +30,7 @@ pub enum ResolvedAttr {
|
|||
impl DefMap {
|
||||
pub(crate) fn resolve_attr_macro(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
original_module: LocalModuleId,
|
||||
ast_id: AstIdWithPath<ast::Item>,
|
||||
|
|
@ -42,6 +43,7 @@ impl DefMap {
|
|||
}
|
||||
|
||||
let resolved_res = self.resolve_path_fp_with_macro(
|
||||
local_def_map,
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
original_module,
|
||||
|
|
@ -105,7 +107,7 @@ pub(super) fn attr_macro_as_call_id(
|
|||
db: &dyn DefDatabase,
|
||||
item_attr: &AstIdWithPath<ast::Item>,
|
||||
macro_attr: &Attr,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
def: MacroDefId,
|
||||
) -> MacroCallId {
|
||||
let arg = match macro_attr.input.as_deref() {
|
||||
|
|
@ -119,7 +121,7 @@ pub(super) fn attr_macro_as_call_id(
|
|||
};
|
||||
|
||||
def.make_call(
|
||||
db.upcast(),
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::Attr {
|
||||
ast_id: item_attr.ast_id,
|
||||
|
|
@ -135,16 +137,16 @@ pub(super) fn derive_macro_as_call_id(
|
|||
item_attr: &AstIdWithPath<ast::Adt>,
|
||||
derive_attr_index: AttrId,
|
||||
derive_pos: u32,
|
||||
call_site: SyntaxContextId,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
call_site: SyntaxContext,
|
||||
krate: Crate,
|
||||
resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
derive_macro_id: MacroCallId,
|
||||
) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
|
||||
let (macro_id, def_id) = resolver(&item_attr.path)
|
||||
.filter(|(_, def_id)| def_id.is_derive())
|
||||
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.as_ref().clone() })?;
|
||||
let call_id = def_id.make_call(
|
||||
db.upcast(),
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::Derive {
|
||||
ast_id: item_attr.ast_id,
|
||||
|
|
|
|||
|
|
@ -5,62 +5,66 @@
|
|||
|
||||
use std::{cmp::Ordering, iter, mem, ops::Not};
|
||||
|
||||
use base_db::{CrateId, CrateOrigin, Dependency, LangCrateOrigin};
|
||||
use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin};
|
||||
use cfg::{CfgAtom, CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
EditionedFileId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
|
||||
MacroDefKind,
|
||||
attrs::{Attr, AttrId},
|
||||
builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::{AsName, Name},
|
||||
proc_macro::CustomProcMacroExpander,
|
||||
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
||||
MacroFileIdExt,
|
||||
};
|
||||
use intern::{sym, Interned};
|
||||
use itertools::{izip, Itertools};
|
||||
use intern::{Interned, sym};
|
||||
use itertools::{Itertools, izip};
|
||||
use la_arena::Idx;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, EditionedFileId, FileAstId, SyntaxContextId};
|
||||
use span::{Edition, FileAstId, SyntaxContext};
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc,
|
||||
ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
|
||||
LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId,
|
||||
MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc,
|
||||
StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc,
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
|
||||
item_tree::{
|
||||
self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
|
||||
ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind,
|
||||
self, AttrOwner, FieldsShape, FileItemTreeId, ImportAlias, ImportKind, ItemTree,
|
||||
ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||
UseTreeKind,
|
||||
},
|
||||
macro_call_as_call_id, macro_call_as_call_id_with_eager,
|
||||
nameres::{
|
||||
BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode,
|
||||
attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
|
||||
diagnostics::DefDiagnostic,
|
||||
mod_resolution::ModDir,
|
||||
path_resolution::ReachedFixedPoint,
|
||||
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
|
||||
sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
|
||||
ResolveMode,
|
||||
proc_macro::{ProcMacroDef, ProcMacroKind, parse_macro_name_and_helper_attrs},
|
||||
sub_namespace_match,
|
||||
},
|
||||
path::{ImportAlias, ModPath, PathKind},
|
||||
per_ns::{Item, PerNs},
|
||||
tt,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantLoc,
|
||||
ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern,
|
||||
ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
|
||||
MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
|
||||
ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
||||
UnresolvedMacro, UseId, UseLoc,
|
||||
};
|
||||
|
||||
const GLOB_RECURSION_LIMIT: usize = 100;
|
||||
const FIXED_POINT_LIMIT: usize = 8192;
|
||||
|
||||
pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
|
||||
let crate_graph = db.crate_graph();
|
||||
|
||||
let krate = &crate_graph[def_map.krate];
|
||||
pub(super) fn collect_defs(
|
||||
db: &dyn DefDatabase,
|
||||
def_map: DefMap,
|
||||
tree_id: TreeId,
|
||||
crate_local_def_map: Option<Arc<LocalDefMap>>,
|
||||
) -> (DefMap, LocalDefMap) {
|
||||
let krate = &def_map.krate.data(db);
|
||||
let cfg_options = def_map.krate.cfg_options(db);
|
||||
|
||||
// populate external prelude and dependency list
|
||||
let mut deps =
|
||||
|
|
@ -72,8 +76,10 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
|||
}
|
||||
|
||||
let proc_macros = if krate.is_proc_macro {
|
||||
db.proc_macros()
|
||||
.for_crate(def_map.krate, db.syntax_context(tree_id.file_id(), krate.edition))
|
||||
db.proc_macros_for_crate(def_map.krate)
|
||||
.and_then(|proc_macros| {
|
||||
proc_macros.list(db.syntax_context(tree_id.file_id(), krate.edition))
|
||||
})
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
|
|
@ -82,13 +88,15 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
|||
let mut collector = DefCollector {
|
||||
db,
|
||||
def_map,
|
||||
local_def_map: LocalDefMap::default(),
|
||||
crate_local_def_map,
|
||||
deps,
|
||||
glob_imports: FxHashMap::default(),
|
||||
unresolved_imports: Vec::new(),
|
||||
indeterminate_imports: Vec::new(),
|
||||
unresolved_macros: Vec::new(),
|
||||
mod_dirs: FxHashMap::default(),
|
||||
cfg_options: &krate.cfg_options,
|
||||
cfg_options,
|
||||
proc_macros,
|
||||
from_glob_import: Default::default(),
|
||||
skip_attrs: Default::default(),
|
||||
|
|
@ -101,9 +109,10 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
|||
collector.seed_with_top_level();
|
||||
}
|
||||
collector.collect();
|
||||
let mut def_map = collector.finish();
|
||||
let (mut def_map, mut local_def_map) = collector.finish();
|
||||
def_map.shrink_to_fit();
|
||||
def_map
|
||||
local_def_map.shrink_to_fit();
|
||||
(def_map, local_def_map)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -183,13 +192,13 @@ enum MacroDirectiveKind {
|
|||
FnLike {
|
||||
ast_id: AstIdWithPath<ast::MacroCall>,
|
||||
expand_to: ExpandTo,
|
||||
ctxt: SyntaxContextId,
|
||||
ctxt: SyntaxContext,
|
||||
},
|
||||
Derive {
|
||||
ast_id: AstIdWithPath<ast::Adt>,
|
||||
derive_attr: AttrId,
|
||||
derive_pos: usize,
|
||||
ctxt: SyntaxContextId,
|
||||
ctxt: SyntaxContext,
|
||||
/// The "parent" macro it is resolved to.
|
||||
derive_macro_id: MacroCallId,
|
||||
},
|
||||
|
|
@ -205,8 +214,11 @@ enum MacroDirectiveKind {
|
|||
struct DefCollector<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
def_map: DefMap,
|
||||
local_def_map: LocalDefMap,
|
||||
/// Set only in case of blocks.
|
||||
crate_local_def_map: Option<Arc<LocalDefMap>>,
|
||||
// The dependencies of the current crate, including optional deps like `test`.
|
||||
deps: FxHashMap<Name, Dependency>,
|
||||
deps: FxHashMap<Name, BuiltDependency>,
|
||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
|
||||
unresolved_imports: Vec<ImportDirective>,
|
||||
indeterminate_imports: Vec<(ImportDirective, PerNs)>,
|
||||
|
|
@ -238,8 +250,7 @@ impl DefCollector<'_> {
|
|||
fn seed_with_top_level(&mut self) {
|
||||
let _p = tracing::info_span!("seed_with_top_level").entered();
|
||||
|
||||
let crate_graph = self.db.crate_graph();
|
||||
let file_id = crate_graph[self.def_map.krate].root_file_id();
|
||||
let file_id = self.def_map.krate.data(self.db).root_file_id(self.db);
|
||||
let item_tree = self.db.file_item_tree(file_id.into());
|
||||
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
|
||||
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
|
||||
|
|
@ -257,41 +268,40 @@ impl DefCollector<'_> {
|
|||
let Some(attr_name) = attr.path.as_ident() else { continue };
|
||||
|
||||
match () {
|
||||
() if *attr_name == sym::recursion_limit.clone() => {
|
||||
() if *attr_name == sym::recursion_limit => {
|
||||
if let Some(limit) = attr.string_value() {
|
||||
if let Ok(limit) = limit.as_str().parse() {
|
||||
crate_data.recursion_limit = Some(limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
() if *attr_name == sym::crate_type.clone() => {
|
||||
() if *attr_name == sym::crate_type => {
|
||||
if attr.string_value() == Some(&sym::proc_dash_macro) {
|
||||
self.is_proc_macro = true;
|
||||
}
|
||||
}
|
||||
() if *attr_name == sym::no_core.clone() => crate_data.no_core = true,
|
||||
() if *attr_name == sym::no_std.clone() => crate_data.no_std = true,
|
||||
() if *attr_name == sym::rustc_coherence_is_core.clone() => {
|
||||
() if *attr_name == sym::no_core => crate_data.no_core = true,
|
||||
() if *attr_name == sym::no_std => crate_data.no_std = true,
|
||||
() if *attr_name == sym::rustc_coherence_is_core => {
|
||||
crate_data.rustc_coherence_is_core = true;
|
||||
}
|
||||
() if *attr_name == sym::feature.clone() => {
|
||||
let features = attr
|
||||
.parse_path_comma_token_tree(self.db.upcast())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|(feat, _)| match feat.segments() {
|
||||
[name] => Some(name.symbol().clone()),
|
||||
_ => None,
|
||||
});
|
||||
() if *attr_name == sym::feature => {
|
||||
let features =
|
||||
attr.parse_path_comma_token_tree(self.db).into_iter().flatten().filter_map(
|
||||
|(feat, _)| match feat.segments() {
|
||||
[name] => Some(name.symbol().clone()),
|
||||
_ => None,
|
||||
},
|
||||
);
|
||||
crate_data.unstable_features.extend(features);
|
||||
}
|
||||
() if *attr_name == sym::register_attr.clone() => {
|
||||
() if *attr_name == sym::register_attr => {
|
||||
if let Some(ident) = attr.single_ident_value() {
|
||||
crate_data.registered_attrs.push(ident.sym.clone());
|
||||
cov_mark::hit!(register_attr);
|
||||
}
|
||||
}
|
||||
() if *attr_name == sym::register_tool.clone() => {
|
||||
() if *attr_name == sym::register_tool => {
|
||||
if let Some(ident) = attr.single_ident_value() {
|
||||
crate_data.registered_tools.push(ident.sym.clone());
|
||||
cov_mark::hit!(register_tool);
|
||||
|
|
@ -310,20 +320,24 @@ impl DefCollector<'_> {
|
|||
// don't do pre-configured attribute resolution yet.
|
||||
// So here check if we are no_core / no_std and we are trying to add the
|
||||
// corresponding dep from the sysroot
|
||||
let skip = match crate_graph[dep.crate_id].origin {
|
||||
CrateOrigin::Lang(LangCrateOrigin::Core) => {
|
||||
crate_data.no_core && dep.is_sysroot()
|
||||
}
|
||||
CrateOrigin::Lang(LangCrateOrigin::Std) => {
|
||||
crate_data.no_std && dep.is_sysroot()
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Depending on the crate data of a dependency seems bad for incrementality, but
|
||||
// we only do that for sysroot crates (this is why the order of the `&&` is important)
|
||||
// - which are normally standard library crate, which realistically aren't going
|
||||
// to have their crate ID invalidated, because they stay on the same root file and
|
||||
// they're dependencies of everything else, so if some collision miraculously occurs
|
||||
// we will resolve it by disambiguating the other crate.
|
||||
let skip = dep.is_sysroot()
|
||||
&& match dep.crate_id.data(self.db).origin {
|
||||
CrateOrigin::Lang(LangCrateOrigin::Core) => crate_data.no_core,
|
||||
CrateOrigin::Lang(LangCrateOrigin::Std) => crate_data.no_std,
|
||||
_ => false,
|
||||
};
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
crate_data
|
||||
self.local_def_map
|
||||
.extern_prelude
|
||||
.insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
|
||||
}
|
||||
|
|
@ -376,7 +390,7 @@ impl DefCollector<'_> {
|
|||
'resolve_attr: loop {
|
||||
let _p = tracing::info_span!("resolve_macros loop").entered();
|
||||
'resolve_macros: loop {
|
||||
self.db.unwind_if_cancelled();
|
||||
self.db.unwind_if_revision_cancelled();
|
||||
|
||||
{
|
||||
let _p = tracing::info_span!("resolve_imports loop").entered();
|
||||
|
|
@ -493,20 +507,20 @@ impl DefCollector<'_> {
|
|||
}
|
||||
|
||||
let krate = if self.def_map.data.no_std {
|
||||
Name::new_symbol_root(sym::core.clone())
|
||||
} else if self.def_map.extern_prelude().any(|(name, _)| *name == sym::std.clone()) {
|
||||
Name::new_symbol_root(sym::std.clone())
|
||||
Name::new_symbol_root(sym::core)
|
||||
} else if self.local_def_map().extern_prelude().any(|(name, _)| *name == sym::std) {
|
||||
Name::new_symbol_root(sym::std)
|
||||
} else {
|
||||
// If `std` does not exist for some reason, fall back to core. This mostly helps
|
||||
// keep r-a's own tests minimal.
|
||||
Name::new_symbol_root(sym::core.clone())
|
||||
Name::new_symbol_root(sym::core)
|
||||
};
|
||||
|
||||
let edition = match self.def_map.data.edition {
|
||||
Edition::Edition2015 => Name::new_symbol_root(sym::rust_2015.clone()),
|
||||
Edition::Edition2018 => Name::new_symbol_root(sym::rust_2018.clone()),
|
||||
Edition::Edition2021 => Name::new_symbol_root(sym::rust_2021.clone()),
|
||||
Edition::Edition2024 => Name::new_symbol_root(sym::rust_2024.clone()),
|
||||
Edition::Edition2015 => Name::new_symbol_root(sym::rust_2015),
|
||||
Edition::Edition2018 => Name::new_symbol_root(sym::rust_2018),
|
||||
Edition::Edition2021 => Name::new_symbol_root(sym::rust_2021),
|
||||
Edition::Edition2024 => Name::new_symbol_root(sym::rust_2024),
|
||||
};
|
||||
|
||||
let path_kind = match self.def_map.data.edition {
|
||||
|
|
@ -515,11 +529,17 @@ impl DefCollector<'_> {
|
|||
};
|
||||
let path = ModPath::from_segments(
|
||||
path_kind,
|
||||
[krate, Name::new_symbol_root(sym::prelude.clone()), edition],
|
||||
[krate, Name::new_symbol_root(sym::prelude), edition],
|
||||
);
|
||||
|
||||
let (per_ns, _) =
|
||||
self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
|
||||
let (per_ns, _) = self.def_map.resolve_path(
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
DefMap::ROOT,
|
||||
&path,
|
||||
BuiltinShadowMode::Other,
|
||||
None,
|
||||
);
|
||||
|
||||
match per_ns.types {
|
||||
Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => {
|
||||
|
|
@ -528,13 +548,17 @@ impl DefCollector<'_> {
|
|||
types => {
|
||||
tracing::debug!(
|
||||
"could not resolve prelude path `{}` to module (resolved to {:?})",
|
||||
path.display(self.db.upcast(), Edition::LATEST),
|
||||
path.display(self.db, Edition::LATEST),
|
||||
types
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn local_def_map(&mut self) -> &LocalDefMap {
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map)
|
||||
}
|
||||
|
||||
/// Adds a definition of procedural macro `name` to the root module.
|
||||
///
|
||||
/// # Notes on procedural macro resolution
|
||||
|
|
@ -555,6 +579,7 @@ impl DefCollector<'_> {
|
|||
&mut self,
|
||||
def: ProcMacroDef,
|
||||
id: ItemTreeId<item_tree::Function>,
|
||||
ast_id: AstId<ast::Fn>,
|
||||
fn_id: FunctionId,
|
||||
) {
|
||||
let kind = def.kind.to_basedb_kind();
|
||||
|
|
@ -578,6 +603,8 @@ impl DefCollector<'_> {
|
|||
edition: self.def_map.data.edition,
|
||||
}
|
||||
.intern(self.db);
|
||||
|
||||
self.def_map.macro_def_to_macro_id.insert(ast_id.erase(), proc_macro_id.into());
|
||||
self.define_proc_macro(def.name.clone(), proc_macro_id);
|
||||
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
|
||||
if let ProcMacroKind::Derive { helpers } = def.kind {
|
||||
|
|
@ -660,7 +687,13 @@ impl DefCollector<'_> {
|
|||
) {
|
||||
let vis = self
|
||||
.def_map
|
||||
.resolve_visibility(self.db, module_id, vis, false)
|
||||
.resolve_visibility(
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
module_id,
|
||||
vis,
|
||||
false,
|
||||
)
|
||||
.unwrap_or(Visibility::Public);
|
||||
self.def_map.modules[module_id].scope.declare(macro_.into());
|
||||
self.update(
|
||||
|
|
@ -694,7 +727,7 @@ impl DefCollector<'_> {
|
|||
/// created by `use` in the root module, ignoring the visibility of `use`.
|
||||
fn import_macros_from_extern_crate(
|
||||
&mut self,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
names: Option<Vec<Name>>,
|
||||
extern_crate: Option<ExternCrateId>,
|
||||
) {
|
||||
|
|
@ -775,10 +808,11 @@ impl DefCollector<'_> {
|
|||
}
|
||||
|
||||
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
|
||||
let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db.upcast(), Edition::LATEST))
|
||||
let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db, Edition::LATEST))
|
||||
.entered();
|
||||
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
|
||||
let res = self.def_map.resolve_path_fp_with_macro(
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
ResolveMode::Import,
|
||||
module_id,
|
||||
|
|
@ -814,7 +848,13 @@ impl DefCollector<'_> {
|
|||
let mut def = directive.status.namespaces();
|
||||
let vis = self
|
||||
.def_map
|
||||
.resolve_visibility(self.db, module_id, &directive.import.visibility, false)
|
||||
.resolve_visibility(
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
module_id,
|
||||
&directive.import.visibility,
|
||||
false,
|
||||
)
|
||||
.unwrap_or(Visibility::Public);
|
||||
|
||||
match import.source {
|
||||
|
|
@ -929,27 +969,16 @@ impl DefCollector<'_> {
|
|||
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
|
||||
cov_mark::hit!(glob_enum);
|
||||
// glob import from enum => just import all the variants
|
||||
|
||||
// We need to check if the def map the enum is from is us, if it is we can't
|
||||
// call the def-map query since we are currently constructing it!
|
||||
let loc = e.lookup(self.db);
|
||||
let tree = loc.id.item_tree(self.db);
|
||||
let current_def_map = self.def_map.krate == loc.container.krate
|
||||
&& self.def_map.block_id() == loc.container.block;
|
||||
let def_map;
|
||||
let resolutions = if current_def_map {
|
||||
&self.def_map.enum_definitions[&e]
|
||||
} else {
|
||||
def_map = loc.container.def_map(self.db);
|
||||
&def_map.enum_definitions[&e]
|
||||
}
|
||||
.iter()
|
||||
.map(|&variant| {
|
||||
let name = tree[variant.lookup(self.db).id.value].name.clone();
|
||||
let res = PerNs::both(variant.into(), variant.into(), vis, None);
|
||||
(Some(name), res)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let resolutions = self
|
||||
.db
|
||||
.enum_variants(e)
|
||||
.variants
|
||||
.iter()
|
||||
.map(|&(variant, ref name)| {
|
||||
let res = PerNs::both(variant.into(), variant.into(), vis, None);
|
||||
(Some(name.clone()), res)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.update(
|
||||
module_id,
|
||||
&resolutions,
|
||||
|
|
@ -977,7 +1006,7 @@ impl DefCollector<'_> {
|
|||
vis: Visibility,
|
||||
import: Option<ImportOrExternCrate>,
|
||||
) {
|
||||
self.db.unwind_if_cancelled();
|
||||
self.db.unwind_if_revision_cancelled();
|
||||
self.update_recursive(module_id, resolutions, vis, import, 0)
|
||||
}
|
||||
|
||||
|
|
@ -1199,6 +1228,7 @@ impl DefCollector<'_> {
|
|||
No,
|
||||
}
|
||||
|
||||
let mut eager_callback_buffer = vec![];
|
||||
let mut res = ReachedFixedPoint::Yes;
|
||||
// Retain unresolved macros after this round of resolution.
|
||||
let mut retain = |directive: &MacroDirective| {
|
||||
|
|
@ -1210,6 +1240,7 @@ impl DefCollector<'_> {
|
|||
};
|
||||
let resolver = |path: &_| {
|
||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
ResolveMode::Other,
|
||||
directive.module_id,
|
||||
|
|
@ -1224,12 +1255,15 @@ impl DefCollector<'_> {
|
|||
match &directive.kind {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
|
||||
let call_id = macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
self.db,
|
||||
ast_id,
|
||||
*call_site,
|
||||
*expand_to,
|
||||
self.def_map.krate,
|
||||
resolver_def_id,
|
||||
&mut |ptr, call_id| {
|
||||
eager_callback_buffer.push((directive.module_id, ptr, call_id));
|
||||
},
|
||||
);
|
||||
if let Ok(Some(call_id)) = call_id {
|
||||
self.def_map.modules[directive.module_id]
|
||||
|
|
@ -1339,8 +1373,7 @@ impl DefCollector<'_> {
|
|||
MacroDefKind::BuiltInAttr(_, expander)
|
||||
if expander.is_test() || expander.is_bench() || expander.is_test_case()
|
||||
) {
|
||||
let test_is_active =
|
||||
self.cfg_options.check_atom(&CfgAtom::Flag(sym::test.clone()));
|
||||
let test_is_active = self.cfg_options.check_atom(&CfgAtom::Flag(sym::test));
|
||||
if test_is_active {
|
||||
return recollect_without(self);
|
||||
}
|
||||
|
|
@ -1375,7 +1408,7 @@ impl DefCollector<'_> {
|
|||
|
||||
let ast_id = ast_id.with_value(ast_adt_id);
|
||||
|
||||
match attr.parse_path_comma_token_tree(self.db.upcast()) {
|
||||
match attr.parse_path_comma_token_tree(self.db) {
|
||||
Some(derive_macros) => {
|
||||
let call_id = call_id();
|
||||
let mut len = 0;
|
||||
|
|
@ -1455,6 +1488,10 @@ impl DefCollector<'_> {
|
|||
macros.extend(mem::take(&mut self.unresolved_macros));
|
||||
self.unresolved_macros = macros;
|
||||
|
||||
for (module_id, ptr, call_id) in eager_callback_buffer {
|
||||
self.def_map.modules[module_id].scope.add_macro_invoc(ptr.map(|(_, it)| it), call_id);
|
||||
}
|
||||
|
||||
for (module_id, depth, container, macro_call_id) in resolved {
|
||||
self.collect_macro_expansion(module_id, macro_call_id, depth, container);
|
||||
}
|
||||
|
|
@ -1474,11 +1511,11 @@ impl DefCollector<'_> {
|
|||
tracing::warn!("macro expansion is too deep");
|
||||
return;
|
||||
}
|
||||
let file_id = macro_call_id.as_file();
|
||||
let file_id = macro_call_id.into();
|
||||
|
||||
let item_tree = self.db.file_item_tree(file_id);
|
||||
|
||||
let mod_dir = if macro_call_id.as_macro_file().is_include_macro(self.db.upcast()) {
|
||||
let mod_dir = if macro_call_id.is_include_macro(self.db) {
|
||||
ModDir::root()
|
||||
} else {
|
||||
self.mod_dirs[&module_id].clone()
|
||||
|
|
@ -1495,7 +1532,7 @@ impl DefCollector<'_> {
|
|||
.collect(item_tree.top_level_items(), container);
|
||||
}
|
||||
|
||||
fn finish(mut self) -> DefMap {
|
||||
fn finish(mut self) -> (DefMap, LocalDefMap) {
|
||||
// Emit diagnostics for all remaining unexpanded macros.
|
||||
let _p = tracing::info_span!("DefCollector::finish").entered();
|
||||
|
||||
|
|
@ -1504,13 +1541,14 @@ impl DefCollector<'_> {
|
|||
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
|
||||
// FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
|
||||
let macro_call_as_call_id = macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
self.db,
|
||||
ast_id,
|
||||
*call_site,
|
||||
*expand_to,
|
||||
self.def_map.krate,
|
||||
|path| {
|
||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
|
||||
self.db,
|
||||
ResolveMode::Other,
|
||||
directive.module_id,
|
||||
|
|
@ -1520,6 +1558,7 @@ impl DefCollector<'_> {
|
|||
);
|
||||
resolved_res.resolved_def.take_macros().map(|it| self.db.macro_def(it))
|
||||
},
|
||||
&mut |_, _| (),
|
||||
);
|
||||
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
|
|
@ -1582,7 +1621,7 @@ impl DefCollector<'_> {
|
|||
));
|
||||
}
|
||||
|
||||
self.def_map
|
||||
(self.def_map, self.local_def_map)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1635,9 +1674,9 @@ impl ModCollector<'_, '_> {
|
|||
None,
|
||||
)
|
||||
};
|
||||
let resolve_vis = |def_map: &DefMap, visibility| {
|
||||
let resolve_vis = |def_map: &DefMap, local_def_map: &LocalDefMap, visibility| {
|
||||
def_map
|
||||
.resolve_visibility(db, module_id, visibility, false)
|
||||
.resolve_visibility(local_def_map, db, module_id, visibility, false)
|
||||
.unwrap_or(Visibility::Public)
|
||||
};
|
||||
|
||||
|
|
@ -1658,6 +1697,11 @@ impl ModCollector<'_, '_> {
|
|||
|
||||
let module = self.def_collector.def_map.module_id(module_id);
|
||||
let def_map = &mut self.def_collector.def_map;
|
||||
let local_def_map = self
|
||||
.def_collector
|
||||
.crate_local_def_map
|
||||
.as_deref()
|
||||
.unwrap_or(&self.def_collector.local_def_map);
|
||||
|
||||
match item {
|
||||
ModItem::Mod(m) => self.collect_module(m, &attrs),
|
||||
|
|
@ -1667,7 +1711,7 @@ impl ModCollector<'_, '_> {
|
|||
id: ItemTreeId::new(self.tree_id, item_tree_id),
|
||||
}
|
||||
.intern(db);
|
||||
let is_prelude = attrs.by_key(&sym::prelude_import).exists();
|
||||
let is_prelude = attrs.by_key(sym::prelude_import).exists();
|
||||
Import::from_use(
|
||||
self.item_tree,
|
||||
ItemTreeId::new(self.tree_id, item_tree_id),
|
||||
|
|
@ -1711,13 +1755,13 @@ impl ModCollector<'_, '_> {
|
|||
};
|
||||
|
||||
if let Some(resolved) = resolved {
|
||||
let vis = resolve_vis(def_map, &self.item_tree[*visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[*visibility]);
|
||||
|
||||
if is_crate_root {
|
||||
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
|
||||
if let Some(name) = name {
|
||||
Arc::get_mut(&mut def_map.data)
|
||||
.unwrap()
|
||||
self.def_collector
|
||||
.local_def_map
|
||||
.extern_prelude
|
||||
.insert(name.clone(), (resolved, Some(id)));
|
||||
}
|
||||
|
|
@ -1725,7 +1769,7 @@ impl ModCollector<'_, '_> {
|
|||
if !is_self {
|
||||
self.process_macro_use_extern_crate(
|
||||
id,
|
||||
attrs.by_key(&sym::macro_use).attrs(),
|
||||
attrs.by_key(sym::macro_use).attrs(),
|
||||
resolved.krate,
|
||||
);
|
||||
}
|
||||
|
|
@ -1784,7 +1828,7 @@ impl ModCollector<'_, '_> {
|
|||
let fn_id =
|
||||
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
|
||||
if self.def_collector.def_map.block.is_none()
|
||||
&& self.def_collector.is_proc_macro
|
||||
|
|
@ -1794,6 +1838,7 @@ impl ModCollector<'_, '_> {
|
|||
self.def_collector.export_proc_macro(
|
||||
proc_macro,
|
||||
ItemTreeId::new(self.tree_id, id),
|
||||
InFile::new(self.file_id(), self.item_tree[id].ast_id()),
|
||||
fn_id,
|
||||
);
|
||||
}
|
||||
|
|
@ -1804,7 +1849,7 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::Struct(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
StructLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
|
||||
|
|
@ -1818,7 +1863,7 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::Union(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
UnionLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
|
||||
|
|
@ -1835,41 +1880,8 @@ impl ModCollector<'_, '_> {
|
|||
EnumLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
|
||||
.intern(db);
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
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);
|
||||
|
||||
let mut index = 0;
|
||||
let variants = FileItemTreeId::range_iter(it.variants.clone())
|
||||
.filter_map(|variant| {
|
||||
let is_enabled = self
|
||||
.item_tree
|
||||
.attrs(db, krate, variant.into())
|
||||
.cfg()
|
||||
.and_then(|cfg| self.is_cfg_enabled(&cfg).not().then_some(cfg))
|
||||
.map_or(Ok(()), Err);
|
||||
match is_enabled {
|
||||
Err(cfg) => {
|
||||
self.emit_unconfigured_diagnostic(
|
||||
self.tree_id,
|
||||
variant.into(),
|
||||
&cfg,
|
||||
);
|
||||
None
|
||||
}
|
||||
Ok(()) => Some({
|
||||
let loc = EnumVariantLoc {
|
||||
id: ItemTreeId::new(self.tree_id, variant),
|
||||
parent: enum_,
|
||||
index,
|
||||
}
|
||||
.intern(db);
|
||||
index += 1;
|
||||
loc
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.def_collector.def_map.enum_definitions.insert(enum_, variants);
|
||||
}
|
||||
ModItem::Const(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
|
@ -1878,7 +1890,8 @@ impl ModCollector<'_, '_> {
|
|||
|
||||
match &it.name {
|
||||
Some(name) => {
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis =
|
||||
resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(self.def_collector, const_id.into(), name, vis, false);
|
||||
}
|
||||
None => {
|
||||
|
|
@ -1892,7 +1905,7 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::Static(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
StaticLoc { container, id: ItemTreeId::new(self.tree_id, id) }
|
||||
|
|
@ -1906,7 +1919,7 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::Trait(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
TraitLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
|
||||
|
|
@ -1920,7 +1933,7 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::TraitAlias(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
TraitAliasLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
|
||||
|
|
@ -1934,7 +1947,7 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::TypeAlias(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
|
||||
update_def(
|
||||
self.def_collector,
|
||||
TypeAliasLoc { container, id: ItemTreeId::new(self.tree_id, id) }
|
||||
|
|
@ -1971,13 +1984,12 @@ impl ModCollector<'_, '_> {
|
|||
&mut self,
|
||||
extern_crate_id: ExternCrateId,
|
||||
macro_use_attrs: impl Iterator<Item = &'a Attr>,
|
||||
target_crate: CrateId,
|
||||
target_crate: Crate,
|
||||
) {
|
||||
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||
let mut single_imports = Vec::new();
|
||||
for attr in macro_use_attrs {
|
||||
let Some(paths) = attr.parse_path_comma_token_tree(self.def_collector.db.upcast())
|
||||
else {
|
||||
let Some(paths) = attr.parse_path_comma_token_tree(self.def_collector.db) else {
|
||||
// `#[macro_use]` (without any paths) found, forget collected names and just import
|
||||
// all visible macros.
|
||||
self.def_collector.import_macros_from_extern_crate(
|
||||
|
|
@ -2002,8 +2014,8 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
|
||||
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
|
||||
let path_attr = attrs.by_key(&sym::path).string_value_unescape();
|
||||
let is_macro_use = attrs.by_key(&sym::macro_use).exists();
|
||||
let path_attr = attrs.by_key(sym::path).string_value_unescape();
|
||||
let is_macro_use = attrs.by_key(sym::macro_use).exists();
|
||||
let module = &self.item_tree[module_id];
|
||||
match &module.kind {
|
||||
// inline module, just recurse
|
||||
|
|
@ -2080,7 +2092,7 @@ impl ModCollector<'_, '_> {
|
|||
let is_macro_use = is_macro_use
|
||||
|| item_tree
|
||||
.top_level_attrs(db, krate)
|
||||
.by_key(&sym::macro_use)
|
||||
.by_key(sym::macro_use)
|
||||
.exists();
|
||||
if is_macro_use {
|
||||
self.import_all_legacy_macros(module_id);
|
||||
|
|
@ -2115,7 +2127,16 @@ impl ModCollector<'_, '_> {
|
|||
) -> LocalModuleId {
|
||||
let def_map = &mut self.def_collector.def_map;
|
||||
let vis = def_map
|
||||
.resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
|
||||
.resolve_visibility(
|
||||
self.def_collector
|
||||
.crate_local_def_map
|
||||
.as_deref()
|
||||
.unwrap_or(&self.def_collector.local_def_map),
|
||||
self.def_collector.db,
|
||||
self.module_id,
|
||||
visibility,
|
||||
false,
|
||||
)
|
||||
.unwrap_or(Visibility::Public);
|
||||
let origin = match definition {
|
||||
None => ModuleOrigin::Inline {
|
||||
|
|
@ -2198,7 +2219,7 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
tracing::debug!(
|
||||
"non-builtin attribute {}",
|
||||
attr.path.display(self.def_collector.db.upcast(), Edition::LATEST)
|
||||
attr.path.display(self.def_collector.db, Edition::LATEST)
|
||||
);
|
||||
|
||||
let ast_id = AstIdWithPath::new(
|
||||
|
|
@ -2230,11 +2251,11 @@ impl ModCollector<'_, '_> {
|
|||
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
|
||||
let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
|
||||
|
||||
let export_attr = attrs.by_key(&sym::macro_export);
|
||||
let export_attr = || attrs.by_key(sym::macro_export);
|
||||
|
||||
let is_export = export_attr.exists();
|
||||
let is_export = export_attr().exists();
|
||||
let local_inner = if is_export {
|
||||
export_attr.tt_values().flat_map(|it| it.iter()).any(|it| match it {
|
||||
export_attr().tt_values().flat_map(|it| it.iter()).any(|it| match it {
|
||||
tt::TtElement::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros,
|
||||
_ => false,
|
||||
})
|
||||
|
|
@ -2243,17 +2264,17 @@ impl ModCollector<'_, '_> {
|
|||
};
|
||||
|
||||
// Case 1: builtin macros
|
||||
let expander = if attrs.by_key(&sym::rustc_builtin_macro).exists() {
|
||||
let expander = if attrs.by_key(sym::rustc_builtin_macro).exists() {
|
||||
// `#[rustc_builtin_macro = "builtin_name"]` overrides the `macro_rules!` name.
|
||||
let name;
|
||||
let name = match attrs.by_key(&sym::rustc_builtin_macro).string_value_with_span() {
|
||||
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
|
||||
}
|
||||
None => {
|
||||
let explicit_name =
|
||||
attrs.by_key(&sym::rustc_builtin_macro).tt_values().next().and_then(|tt| {
|
||||
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),
|
||||
_ => None,
|
||||
|
|
@ -2283,7 +2304,7 @@ impl ModCollector<'_, '_> {
|
|||
// Case 2: normal `macro_rules!` macro
|
||||
MacroExpander::Declarative
|
||||
};
|
||||
let allow_internal_unsafe = attrs.by_key(&sym::allow_internal_unsafe).exists();
|
||||
let allow_internal_unsafe = attrs.by_key(sym::allow_internal_unsafe).exists();
|
||||
|
||||
let mut flags = MacroRulesLocFlags::empty();
|
||||
flags.set(MacroRulesLocFlags::LOCAL_INNER, local_inner);
|
||||
|
|
@ -2297,6 +2318,10 @@ impl ModCollector<'_, '_> {
|
|||
edition: self.def_collector.def_map.data.edition,
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
self.def_collector.def_map.macro_def_to_macro_id.insert(
|
||||
InFile::new(self.file_id(), self.item_tree[id].ast_id()).erase(),
|
||||
macro_id.into(),
|
||||
);
|
||||
self.def_collector.define_macro_rules(
|
||||
self.module_id,
|
||||
mac.name.clone(),
|
||||
|
|
@ -2313,14 +2338,14 @@ impl ModCollector<'_, '_> {
|
|||
// Case 1: builtin macros
|
||||
let mut helpers_opt = None;
|
||||
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
|
||||
let expander = if attrs.by_key(&sym::rustc_builtin_macro).exists() {
|
||||
let expander = if attrs.by_key(sym::rustc_builtin_macro).exists() {
|
||||
if let Some(expander) = find_builtin_macro(&mac.name) {
|
||||
match expander {
|
||||
Either::Left(it) => MacroExpander::BuiltIn(it),
|
||||
Either::Right(it) => MacroExpander::BuiltInEager(it),
|
||||
}
|
||||
} else if let Some(expander) = find_builtin_derive(&mac.name) {
|
||||
if let Some(attr) = attrs.by_key(&sym::rustc_builtin_macro).tt_values().next() {
|
||||
if let Some(attr) = attrs.by_key(sym::rustc_builtin_macro).tt_values().next() {
|
||||
// NOTE: The item *may* have both `#[rustc_builtin_macro]` and `#[proc_macro_derive]`,
|
||||
// in which case rustc ignores the helper attributes from the latter, but it
|
||||
// "doesn't make sense in practice" (see rust-lang/rust#87027).
|
||||
|
|
@ -2331,8 +2356,8 @@ impl ModCollector<'_, '_> {
|
|||
stdx::always!(
|
||||
name == mac.name,
|
||||
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
|
||||
mac.name.display(self.def_collector.db.upcast(), Edition::LATEST),
|
||||
name.display(self.def_collector.db.upcast(), Edition::LATEST),
|
||||
mac.name.display(self.def_collector.db, Edition::LATEST),
|
||||
name.display(self.def_collector.db, Edition::LATEST),
|
||||
);
|
||||
helpers_opt = Some(helpers);
|
||||
}
|
||||
|
|
@ -2351,7 +2376,7 @@ impl ModCollector<'_, '_> {
|
|||
// Case 2: normal `macro`
|
||||
MacroExpander::Declarative
|
||||
};
|
||||
let allow_internal_unsafe = attrs.by_key(&sym::allow_internal_unsafe).exists();
|
||||
let allow_internal_unsafe = attrs.by_key(sym::allow_internal_unsafe).exists();
|
||||
|
||||
let macro_id = Macro2Loc {
|
||||
container: module,
|
||||
|
|
@ -2361,6 +2386,10 @@ impl ModCollector<'_, '_> {
|
|||
edition: self.def_collector.def_map.data.edition,
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
self.def_collector.def_map.macro_def_to_macro_id.insert(
|
||||
InFile::new(self.file_id(), self.item_tree[id].ast_id()).erase(),
|
||||
macro_id.into(),
|
||||
);
|
||||
self.def_collector.define_macro_def(
|
||||
self.module_id,
|
||||
mac.name.clone(),
|
||||
|
|
@ -2389,9 +2418,10 @@ impl ModCollector<'_, '_> {
|
|||
// new legacy macros that create textual scopes. We need a way to resolve names in textual
|
||||
// scopes without eager expansion.
|
||||
|
||||
let mut eager_callback_buffer = vec![];
|
||||
// Case 1: try to resolve macro calls with single-segment name and expand macro_rules
|
||||
if let Ok(res) = macro_call_as_call_id_with_eager(
|
||||
db.upcast(),
|
||||
db,
|
||||
ast_id.ast_id,
|
||||
&ast_id.path,
|
||||
ctxt,
|
||||
|
|
@ -2417,6 +2447,10 @@ impl ModCollector<'_, '_> {
|
|||
},
|
||||
|path| {
|
||||
let resolved_res = self.def_collector.def_map.resolve_path_fp_with_macro(
|
||||
self.def_collector
|
||||
.crate_local_def_map
|
||||
.as_deref()
|
||||
.unwrap_or(&self.def_collector.local_def_map),
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
self.module_id,
|
||||
|
|
@ -2426,7 +2460,13 @@ impl ModCollector<'_, '_> {
|
|||
);
|
||||
resolved_res.resolved_def.take_macros().map(|it| db.macro_def(it))
|
||||
},
|
||||
&mut |ptr, call_id| eager_callback_buffer.push((ptr, call_id)),
|
||||
) {
|
||||
for (ptr, call_id) in eager_callback_buffer {
|
||||
self.def_collector.def_map.modules[self.module_id]
|
||||
.scope
|
||||
.add_macro_invoc(ptr.map(|(_, it)| it), call_id);
|
||||
}
|
||||
// FIXME: if there were errors, this might've been in the eager expansion from an
|
||||
// unresolved macro, so we need to push this into late macro resolution. see fixme above
|
||||
if res.err.is_none() {
|
||||
|
|
@ -2517,7 +2557,6 @@ impl ModCollector<'_, '_> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base_db::SourceDatabase;
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::{nameres::DefMapCrateData, test_db::TestDB};
|
||||
|
|
@ -2528,6 +2567,8 @@ mod tests {
|
|||
let mut collector = DefCollector {
|
||||
db,
|
||||
def_map,
|
||||
local_def_map: LocalDefMap::default(),
|
||||
crate_local_def_map: None,
|
||||
deps: FxHashMap::default(),
|
||||
glob_imports: FxHashMap::default(),
|
||||
unresolved_imports: Vec::new(),
|
||||
|
|
@ -2550,7 +2591,7 @@ mod tests {
|
|||
let (db, file_id) = TestDB::with_single_file(not_ra_fixture);
|
||||
let krate = db.test_crate();
|
||||
|
||||
let edition = db.crate_graph()[krate].edition;
|
||||
let edition = krate.data(&db).edition;
|
||||
let module_origin = ModuleOrigin::CrateRoot { definition: file_id };
|
||||
let def_map = DefMap::empty(
|
||||
krate,
|
||||
|
|
@ -2588,7 +2629,7 @@ foo!(KABOOM);
|
|||
// the release mode. That's why the argument is not an ra_fixture --
|
||||
// otherwise injection highlighting gets stuck.
|
||||
//
|
||||
// We need to find a way to fail this faster.
|
||||
// We need to find a way to fail this faster!
|
||||
do_resolve(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
|
|
|
|||
|
|
@ -3,15 +3,14 @@
|
|||
use std::ops::Not;
|
||||
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{attrs::AttrId, ExpandErrorKind, MacroCallKind};
|
||||
use hir_expand::{ExpandErrorKind, MacroCallKind, attrs::AttrId, mod_path::ModPath};
|
||||
use la_arena::Idx;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
AstId,
|
||||
item_tree::{self, AttrOwner, ItemTreeId, TreeId},
|
||||
nameres::LocalModuleId,
|
||||
path::ModPath,
|
||||
AstId,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
//! This module resolves `mod foo;` declaration to file.
|
||||
use arrayvec::ArrayVec;
|
||||
use base_db::AnchoredPath;
|
||||
use hir_expand::{name::Name, HirFileIdExt};
|
||||
use span::EditionedFileId;
|
||||
use hir_expand::{EditionedFileId, name::Name};
|
||||
|
||||
use crate::{db::DefDatabase, HirFileId};
|
||||
use crate::{HirFileId, db::DefDatabase};
|
||||
|
||||
const MOD_DEPTH_LIMIT: usize = 32;
|
||||
|
||||
|
|
@ -77,9 +76,9 @@ impl ModDir {
|
|||
}
|
||||
};
|
||||
|
||||
let orig_file_id = file_id.original_file_respecting_includes(db.upcast());
|
||||
let orig_file_id = file_id.original_file_respecting_includes(db);
|
||||
for candidate in candidate_files.iter() {
|
||||
let path = AnchoredPath { anchor: orig_file_id.file_id(), path: candidate.as_str() };
|
||||
let path = AnchoredPath { anchor: orig_file_id.file_id(db), path: candidate.as_str() };
|
||||
if let Some(file_id) = db.resolve_path(path) {
|
||||
let is_mod_rs = candidate.ends_with("/mod.rs");
|
||||
|
||||
|
|
@ -92,7 +91,7 @@ impl ModDir {
|
|||
if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) {
|
||||
return Ok((
|
||||
// FIXME: Edition, is this rightr?
|
||||
EditionedFileId::new(file_id, orig_file_id.edition()),
|
||||
EditionedFileId::new(db, file_id, orig_file_id.edition(db)),
|
||||
is_mod_rs,
|
||||
mod_dir,
|
||||
));
|
||||
|
|
|
|||
|
|
@ -11,19 +11,22 @@
|
|||
//! `ReachedFixedPoint` signals about this.
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{name::Name, Lookup};
|
||||
use hir_expand::{
|
||||
Lookup,
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::Name,
|
||||
};
|
||||
use span::Edition;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, LocalModuleId, ModuleDefId,
|
||||
db::DefDatabase,
|
||||
item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
|
||||
item_scope::{BUILTIN_SCOPE, ImportOrExternCrate},
|
||||
item_tree::FieldsShape,
|
||||
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
|
||||
path::{ModPath, PathKind},
|
||||
nameres::{BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, sub_namespace_match},
|
||||
per_ns::PerNs,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, LocalModuleId, ModuleDefId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
@ -91,6 +94,7 @@ impl PerNs {
|
|||
impl DefMap {
|
||||
pub(crate) fn resolve_visibility(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
// module to import to
|
||||
original_module: LocalModuleId,
|
||||
|
|
@ -101,8 +105,14 @@ impl DefMap {
|
|||
) -> Option<Visibility> {
|
||||
let mut vis = match visibility {
|
||||
RawVisibility::Module(path, explicitness) => {
|
||||
let (result, remaining) =
|
||||
self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
|
||||
let (result, remaining) = self.resolve_path(
|
||||
local_def_map,
|
||||
db,
|
||||
original_module,
|
||||
path,
|
||||
BuiltinShadowMode::Module,
|
||||
None,
|
||||
);
|
||||
if remaining.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -137,6 +147,7 @@ impl DefMap {
|
|||
// the result.
|
||||
pub(super) fn resolve_path_fp_with_macro(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
mode: ResolveMode,
|
||||
// module to import to
|
||||
|
|
@ -148,6 +159,7 @@ impl DefMap {
|
|||
expected_macro_subns: Option<MacroSubNs>,
|
||||
) -> ResolvePathResult {
|
||||
let mut result = self.resolve_path_fp_with_macro_single(
|
||||
local_def_map,
|
||||
db,
|
||||
mode,
|
||||
original_module,
|
||||
|
|
@ -196,6 +208,7 @@ impl DefMap {
|
|||
current_map = &arc;
|
||||
|
||||
let new = current_map.resolve_path_fp_in_all_preludes(
|
||||
local_def_map,
|
||||
db,
|
||||
mode,
|
||||
original_module,
|
||||
|
|
@ -210,6 +223,7 @@ impl DefMap {
|
|||
}
|
||||
|
||||
let new = current_map.resolve_path_fp_with_macro_single(
|
||||
local_def_map,
|
||||
db,
|
||||
mode,
|
||||
original_module,
|
||||
|
|
@ -224,6 +238,7 @@ impl DefMap {
|
|||
|
||||
pub(super) fn resolve_path_fp_with_macro_single(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
mode: ResolveMode,
|
||||
original_module: LocalModuleId,
|
||||
|
|
@ -258,7 +273,12 @@ impl DefMap {
|
|||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
||||
self.resolve_name_in_crate_root_or_extern_prelude(db, original_module, segment)
|
||||
self.resolve_name_in_crate_root_or_extern_prelude(
|
||||
local_def_map,
|
||||
db,
|
||||
original_module,
|
||||
segment,
|
||||
)
|
||||
}
|
||||
PathKind::Plain => {
|
||||
let (_, segment) = match segments.next() {
|
||||
|
|
@ -276,6 +296,7 @@ impl DefMap {
|
|||
|
||||
tracing::debug!("resolving {:?} in module", segment);
|
||||
self.resolve_name_in_module(
|
||||
local_def_map,
|
||||
db,
|
||||
original_module,
|
||||
segment,
|
||||
|
|
@ -321,7 +342,9 @@ impl DefMap {
|
|||
// with), resolve the remaining path segments in that `DefMap`.
|
||||
let path =
|
||||
ModPath::from_segments(PathKind::SELF, path.segments().iter().cloned());
|
||||
// This is the same crate, so the local def map is the same.
|
||||
return def_map.resolve_path_fp_with_macro(
|
||||
local_def_map,
|
||||
db,
|
||||
mode,
|
||||
local_id,
|
||||
|
|
@ -333,10 +356,10 @@ impl DefMap {
|
|||
|
||||
PerNs::types(module.into(), Visibility::Public, None)
|
||||
}
|
||||
PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
|
||||
PathKind::Abs => match self.resolve_path_abs(local_def_map, &mut segments, path) {
|
||||
Either::Left(it) => it,
|
||||
Either::Right(reached_fixed_point) => {
|
||||
return ResolvePathResult::empty(reached_fixed_point)
|
||||
return ResolvePathResult::empty(reached_fixed_point);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -347,6 +370,7 @@ impl DefMap {
|
|||
/// Resolves a path only in the preludes, without accounting for item scopes.
|
||||
pub(super) fn resolve_path_fp_in_all_preludes(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
mode: ResolveMode,
|
||||
original_module: LocalModuleId,
|
||||
|
|
@ -368,7 +392,7 @@ impl DefMap {
|
|||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
||||
self.resolve_name_in_extern_prelude(segment)
|
||||
self.resolve_name_in_extern_prelude(local_def_map, segment)
|
||||
}
|
||||
PathKind::Plain => {
|
||||
let (_, segment) = match segments.next() {
|
||||
|
|
@ -376,16 +400,16 @@ impl DefMap {
|
|||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
tracing::debug!("resolving {:?} in module", segment);
|
||||
self.resolve_name_in_all_preludes(db, segment)
|
||||
self.resolve_name_in_all_preludes(local_def_map, db, segment)
|
||||
}
|
||||
PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
|
||||
PathKind::Abs => match self.resolve_path_abs(local_def_map, &mut segments, path) {
|
||||
Either::Left(it) => it,
|
||||
Either::Right(reached_fixed_point) => {
|
||||
return ResolvePathResult::empty(reached_fixed_point)
|
||||
return ResolvePathResult::empty(reached_fixed_point);
|
||||
}
|
||||
},
|
||||
PathKind::DollarCrate(_) | PathKind::Crate | PathKind::Super(_) => {
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes)
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -395,6 +419,7 @@ impl DefMap {
|
|||
/// 2018-style absolute path -- only extern prelude
|
||||
fn resolve_path_abs<'a>(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
segments: &mut impl Iterator<Item = (usize, &'a Name)>,
|
||||
path: &ModPath,
|
||||
) -> Either<PerNs, ReachedFixedPoint> {
|
||||
|
|
@ -402,7 +427,7 @@ impl DefMap {
|
|||
Some((_, segment)) => segment,
|
||||
None => return Either::Right(ReachedFixedPoint::Yes),
|
||||
};
|
||||
if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
|
||||
if let Some(&(def, extern_crate)) = local_def_map.extern_prelude.get(segment) {
|
||||
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||
Either::Left(PerNs::types(
|
||||
def.into(),
|
||||
|
|
@ -451,6 +476,7 @@ impl DefMap {
|
|||
// this point, we know we're resolving a multi-segment path so macro kind
|
||||
// expectation is discarded.
|
||||
let resolution = defp_map.resolve_path_fp_with_macro(
|
||||
LocalDefMap::EMPTY,
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
module.local_id,
|
||||
|
|
@ -483,33 +509,24 @@ impl DefMap {
|
|||
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
|
||||
// enum variant
|
||||
cov_mark::hit!(can_import_enum_variant);
|
||||
let def_map;
|
||||
|
||||
let loc = e.lookup(db);
|
||||
let tree = loc.id.item_tree(db);
|
||||
let current_def_map =
|
||||
self.krate == loc.container.krate && self.block_id() == loc.container.block;
|
||||
let res = if current_def_map {
|
||||
&self.enum_definitions[&e]
|
||||
} else {
|
||||
def_map = loc.container.def_map(db);
|
||||
&def_map.enum_definitions[&e]
|
||||
}
|
||||
.iter()
|
||||
.find_map(|&variant| {
|
||||
let variant_data = &tree[variant.lookup(db).id.value];
|
||||
(variant_data.name == *segment).then(|| match variant_data.shape {
|
||||
FieldsShape::Record => {
|
||||
PerNs::types(variant.into(), Visibility::Public, None)
|
||||
}
|
||||
FieldsShape::Tuple | FieldsShape::Unit => PerNs::both(
|
||||
variant.into(),
|
||||
variant.into(),
|
||||
Visibility::Public,
|
||||
None,
|
||||
),
|
||||
})
|
||||
});
|
||||
let res =
|
||||
db.enum_variants(e).variants.iter().find(|(_, name)| name == segment).map(
|
||||
|&(variant, _)| {
|
||||
let item_tree_id = variant.lookup(db).id;
|
||||
match item_tree_id.item_tree(db)[item_tree_id.value].shape {
|
||||
FieldsShape::Record => {
|
||||
PerNs::types(variant.into(), Visibility::Public, None)
|
||||
}
|
||||
FieldsShape::Tuple | FieldsShape::Unit => PerNs::both(
|
||||
variant.into(),
|
||||
variant.into(),
|
||||
Visibility::Public,
|
||||
None,
|
||||
),
|
||||
}
|
||||
},
|
||||
);
|
||||
// FIXME: Need to filter visibility here and below? Not sure.
|
||||
return match res {
|
||||
Some(res) => {
|
||||
|
|
@ -568,6 +585,7 @@ impl DefMap {
|
|||
|
||||
fn resolve_name_in_module(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
module: LocalModuleId,
|
||||
name: &Name,
|
||||
|
|
@ -611,7 +629,7 @@ impl DefMap {
|
|||
// they might been shadowed by local names.
|
||||
return PerNs::none();
|
||||
}
|
||||
self.resolve_name_in_extern_prelude(name)
|
||||
self.resolve_name_in_extern_prelude(local_def_map, name)
|
||||
};
|
||||
let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
|
||||
let prelude = || {
|
||||
|
|
@ -628,19 +646,24 @@ impl DefMap {
|
|||
.or_else(prelude)
|
||||
}
|
||||
|
||||
fn resolve_name_in_all_preludes(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
|
||||
fn resolve_name_in_all_preludes(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
name: &Name,
|
||||
) -> PerNs {
|
||||
// Resolve in:
|
||||
// - extern prelude / macro_use prelude
|
||||
// - std prelude
|
||||
let extern_prelude = self.resolve_name_in_extern_prelude(name);
|
||||
let extern_prelude = self.resolve_name_in_extern_prelude(local_def_map, name);
|
||||
let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
|
||||
let prelude = || self.resolve_in_prelude(db, name);
|
||||
|
||||
extern_prelude.or_else(macro_use_prelude).or_else(prelude)
|
||||
}
|
||||
|
||||
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
|
||||
self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
|
||||
fn resolve_name_in_extern_prelude(&self, local_def_map: &LocalDefMap, name: &Name) -> PerNs {
|
||||
local_def_map.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
|
||||
PerNs::types(
|
||||
it.into(),
|
||||
Visibility::Public,
|
||||
|
|
@ -662,6 +685,7 @@ impl DefMap {
|
|||
|
||||
fn resolve_name_in_crate_root_or_extern_prelude(
|
||||
&self,
|
||||
local_def_map: &LocalDefMap,
|
||||
db: &dyn DefDatabase,
|
||||
module: LocalModuleId,
|
||||
name: &Name,
|
||||
|
|
@ -678,7 +702,7 @@ impl DefMap {
|
|||
// Don't resolve extern prelude in pseudo-module of a block.
|
||||
return PerNs::none();
|
||||
}
|
||||
self.resolve_name_in_extern_prelude(name)
|
||||
self.resolve_name_in_extern_prelude(local_def_map, name)
|
||||
};
|
||||
|
||||
from_crate_root.or_else(from_extern_prelude)
|
||||
|
|
|
|||
|
|
@ -30,26 +30,36 @@ impl ProcMacroKind {
|
|||
}
|
||||
|
||||
impl Attrs {
|
||||
#[rustfmt::skip]
|
||||
pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
|
||||
if self.is_proc_macro() {
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang })
|
||||
} else if self.is_proc_macro_attribute() {
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
|
||||
} else if self.by_key(&sym::proc_macro_derive).exists() {
|
||||
let derive = self.by_key(&sym::proc_macro_derive).tt_values().next()?;
|
||||
let def = parse_macro_name_and_helper_attrs(derive)
|
||||
.map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } });
|
||||
|
||||
if def.is_none() {
|
||||
tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
|
||||
}
|
||||
|
||||
def
|
||||
} else if self.by_key(sym::proc_macro_derive).exists() {
|
||||
let derive = self.parse_proc_macro_derive();
|
||||
Some(match derive {
|
||||
Some((name, helpers)) => {
|
||||
ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } }
|
||||
}
|
||||
None => ProcMacroDef {
|
||||
name: func_name.clone(),
|
||||
kind: ProcMacroKind::Derive { helpers: Box::default() },
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_proc_macro_derive(&self) -> Option<(Name, Box<[Name]>)> {
|
||||
let derive = self.by_key(sym::proc_macro_derive).tt_values().next()?;
|
||||
parse_macro_name_and_helper_attrs(derive)
|
||||
}
|
||||
|
||||
pub fn parse_rustc_builtin_macro(&self) -> Option<(Name, Box<[Name]>)> {
|
||||
let derive = self.by_key(sym::rustc_builtin_macro).tt_values().next()?;
|
||||
parse_macro_name_and_helper_attrs(derive)
|
||||
}
|
||||
}
|
||||
|
||||
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ mod macros;
|
|||
mod mod_resolution;
|
||||
mod primitives;
|
||||
|
||||
use base_db::SourceDatabase;
|
||||
use expect_test::{expect, Expect};
|
||||
use base_db::RootQueryDb;
|
||||
use expect_test::{Expect, expect};
|
||||
use test_fixture::WithFixture;
|
||||
use triomphe::Arc;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
use base_db::SourceDatabaseFileInputExt as _;
|
||||
use base_db::{
|
||||
CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
|
||||
DependencyBuilder, Env, RootQueryDb, SourceDatabase,
|
||||
};
|
||||
use intern::Symbol;
|
||||
use span::Edition;
|
||||
use test_fixture::WithFixture;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};
|
||||
use crate::{AdtId, ModuleDefId, db::DefDatabase, nameres::tests::TestDB};
|
||||
|
||||
fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) {
|
||||
let (mut db, pos) = TestDB::with_position(ra_fixture_initial);
|
||||
|
|
@ -12,7 +18,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
|
|||
});
|
||||
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
|
||||
}
|
||||
db.set_file_text(pos.file_id.file_id(), ra_fixture_change);
|
||||
db.set_file_text(pos.file_id.file_id(&db), ra_fixture_change);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
|
@ -22,6 +28,80 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crate_metadata_changes_should_not_invalidate_unrelated_def_maps() {
|
||||
let (mut db, files) = TestDB::with_many_files(
|
||||
r#"
|
||||
//- /a.rs crate:a
|
||||
pub fn foo() {}
|
||||
|
||||
//- /b.rs crate:b
|
||||
pub struct Bar;
|
||||
|
||||
//- /c.rs crate:c deps:b
|
||||
pub const BAZ: u32 = 0;
|
||||
"#,
|
||||
);
|
||||
|
||||
for &krate in db.all_crates().iter() {
|
||||
db.crate_def_map(krate);
|
||||
}
|
||||
|
||||
let all_crates_before = db.all_crates();
|
||||
|
||||
{
|
||||
// Add a dependency a -> b.
|
||||
let mut new_crate_graph = CrateGraphBuilder::default();
|
||||
|
||||
let mut add_crate = |crate_name, root_file_idx: usize| {
|
||||
new_crate_graph.add_crate_root(
|
||||
files[root_file_idx].file_id(&db),
|
||||
Edition::CURRENT,
|
||||
Some(CrateDisplayName::from_canonical_name(crate_name)),
|
||||
None,
|
||||
Default::default(),
|
||||
None,
|
||||
Env::default(),
|
||||
CrateOrigin::Local { repo: None, name: Some(Symbol::intern(crate_name)) },
|
||||
false,
|
||||
Arc::new(
|
||||
// FIXME: This is less than ideal
|
||||
TryFrom::try_from(
|
||||
&*std::env::current_dir().unwrap().as_path().to_string_lossy(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }),
|
||||
)
|
||||
};
|
||||
let a = add_crate("a", 0);
|
||||
let b = add_crate("b", 1);
|
||||
let c = add_crate("c", 2);
|
||||
new_crate_graph
|
||||
.add_dep(c, DependencyBuilder::new(CrateName::new("b").unwrap(), b))
|
||||
.unwrap();
|
||||
new_crate_graph
|
||||
.add_dep(b, DependencyBuilder::new(CrateName::new("a").unwrap(), a))
|
||||
.unwrap();
|
||||
new_crate_graph.set_in_db(&mut db);
|
||||
}
|
||||
|
||||
let all_crates_after = db.all_crates();
|
||||
assert!(
|
||||
Arc::ptr_eq(&all_crates_before, &all_crates_after),
|
||||
"the all_crates list should not have been invalidated"
|
||||
);
|
||||
|
||||
let events = db.log_executed(|| {
|
||||
for &krate in db.all_crates().iter() {
|
||||
db.crate_def_map(krate);
|
||||
}
|
||||
});
|
||||
let invalidated_def_maps =
|
||||
events.iter().filter(|event| event.contains("crate_def_map")).count();
|
||||
assert_eq!(invalidated_def_maps, 1, "{events:#?}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_function_should_not_invalidate_def_map() {
|
||||
check_def_map_is_not_recomputed(
|
||||
|
|
@ -255,10 +335,10 @@ m!(Z);
|
|||
assert_eq!(module_data.scope.resolutions().count(), 4);
|
||||
});
|
||||
let n_recalculated_item_trees =
|
||||
events.iter().filter(|it| it.contains("item_tree(")).count();
|
||||
events.iter().filter(|it| it.contains("file_item_tree_shim")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 6);
|
||||
let n_reparsed_macros =
|
||||
events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
|
||||
events.iter().filter(|it| it.contains("parse_macro_expansion_shim")).count();
|
||||
assert_eq!(n_reparsed_macros, 3);
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +348,7 @@ fn quux() { 92 }
|
|||
m!(Y);
|
||||
m!(Z);
|
||||
"#;
|
||||
db.set_file_text(pos.file_id.file_id(), new_text);
|
||||
db.set_file_text(pos.file_id.file_id(&db), new_text);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
|
@ -276,10 +356,11 @@ m!(Z);
|
|||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||
assert_eq!(module_data.scope.resolutions().count(), 4);
|
||||
});
|
||||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 1);
|
||||
let n_recalculated_item_trees =
|
||||
events.iter().filter(|it| it.contains("file_item_tree_shim")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 1, "{events:#?}");
|
||||
let n_reparsed_macros =
|
||||
events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
|
||||
events.iter().filter(|it| it.contains("parse_macro_expansion_shim")).count();
|
||||
assert_eq!(n_reparsed_macros, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -310,14 +391,15 @@ pub type Ty = ();
|
|||
let events = db.log_executed(|| {
|
||||
db.file_item_tree(pos.file_id.into());
|
||||
});
|
||||
let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree(")).count();
|
||||
let n_calculated_item_trees =
|
||||
events.iter().filter(|it| it.contains("file_item_tree_shim")).count();
|
||||
assert_eq!(n_calculated_item_trees, 1);
|
||||
let n_parsed_files = events.iter().filter(|it| it.contains("parse(")).count();
|
||||
let n_parsed_files = events.iter().filter(|it| it.contains("parse")).count();
|
||||
assert_eq!(n_parsed_files, 1);
|
||||
}
|
||||
|
||||
// Delete the parse tree.
|
||||
base_db::ParseQuery.in_db(&db).purge();
|
||||
// FIXME(salsa-transition): bring this back
|
||||
// base_db::ParseQuery.in_db(&db).purge();
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
|
@ -327,22 +409,22 @@ pub type Ty = ();
|
|||
assert_eq!(module_data.scope.impls().count(), 1);
|
||||
|
||||
for imp in module_data.scope.impls() {
|
||||
db.impl_data(imp);
|
||||
db.impl_signature(imp);
|
||||
}
|
||||
|
||||
for (_, res) in module_data.scope.resolutions() {
|
||||
match res.values.map(|it| it.def).or(res.types.map(|it| it.def)).unwrap() {
|
||||
ModuleDefId::FunctionId(f) => _ = db.function_data(f),
|
||||
ModuleDefId::FunctionId(f) => _ = db.function_signature(f),
|
||||
ModuleDefId::AdtId(adt) => match adt {
|
||||
AdtId::StructId(it) => _ = db.struct_data(it),
|
||||
AdtId::UnionId(it) => _ = db.union_data(it),
|
||||
AdtId::EnumId(it) => _ = db.enum_data(it),
|
||||
AdtId::StructId(it) => _ = db.struct_signature(it),
|
||||
AdtId::UnionId(it) => _ = db.union_signature(it),
|
||||
AdtId::EnumId(it) => _ = db.enum_signature(it),
|
||||
},
|
||||
ModuleDefId::ConstId(it) => _ = db.const_data(it),
|
||||
ModuleDefId::StaticId(it) => _ = db.static_data(it),
|
||||
ModuleDefId::TraitId(it) => _ = db.trait_data(it),
|
||||
ModuleDefId::TraitAliasId(it) => _ = db.trait_alias_data(it),
|
||||
ModuleDefId::TypeAliasId(it) => _ = db.type_alias_data(it),
|
||||
ModuleDefId::ConstId(it) => _ = db.const_signature(it),
|
||||
ModuleDefId::StaticId(it) => _ = db.static_signature(it),
|
||||
ModuleDefId::TraitId(it) => _ = db.trait_signature(it),
|
||||
ModuleDefId::TraitAliasId(it) => _ = db.trait_alias_signature(it),
|
||||
ModuleDefId::TypeAliasId(it) => _ = db.type_alias_signature(it),
|
||||
ModuleDefId::EnumVariantId(_)
|
||||
| ModuleDefId::ModuleId(_)
|
||||
| ModuleDefId::MacroId(_)
|
||||
|
|
|
|||
|
|
@ -1095,7 +1095,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let krate = *db.all_crates().last().expect("no crate graph present");
|
||||
let def_map = db.crate_def_map(krate);
|
||||
|
||||
assert_eq!(def_map.data.exported_derives.len(), 1);
|
||||
|
|
@ -1445,7 +1445,7 @@ struct TokenStream;
|
|||
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
|
||||
"#,
|
||||
);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let krate = *db.all_crates().last().expect("no crate graph present");
|
||||
let def_map = db.crate_def_map(krate);
|
||||
|
||||
let root_module = &def_map[DefMap::ROOT].scope;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
use bitflags::bitflags;
|
||||
|
||||
use crate::{
|
||||
MacroId, ModuleDefId,
|
||||
item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs},
|
||||
visibility::Visibility,
|
||||
MacroId, ModuleDefId,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
|
|
@ -146,11 +146,7 @@ impl PerNs {
|
|||
}
|
||||
|
||||
pub fn or_else(self, f: impl FnOnce() -> PerNs) -> PerNs {
|
||||
if self.is_full() {
|
||||
self
|
||||
} else {
|
||||
self.or(f())
|
||||
}
|
||||
if self.is_full() { self } else { self.or(f()) }
|
||||
}
|
||||
|
||||
pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> {
|
||||
|
|
|
|||
|
|
@ -1,306 +0,0 @@
|
|||
//! Display and pretty printing routines.
|
||||
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
mem,
|
||||
};
|
||||
|
||||
use hir_expand::mod_path::PathKind;
|
||||
use itertools::Itertools;
|
||||
use span::Edition;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
lang_item::LangItemTarget,
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
type_ref::{
|
||||
Mutability, TraitBoundModifier, TypeBound, TypeRef, TypeRefId, TypesMap, UseArgRef,
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) fn print_path(
|
||||
db: &dyn DefDatabase,
|
||||
path: &Path,
|
||||
map: &TypesMap,
|
||||
buf: &mut dyn Write,
|
||||
edition: Edition,
|
||||
) -> fmt::Result {
|
||||
if let Path::LangItem(it, s) = path {
|
||||
write!(buf, "builtin#lang(")?;
|
||||
match *it {
|
||||
LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
|
||||
LangItemTarget::EnumId(it) => {
|
||||
write!(buf, "{}", db.enum_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::Function(it) => {
|
||||
write!(buf, "{}", db.function_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::Static(it) => {
|
||||
write!(buf, "{}", db.static_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::Struct(it) => {
|
||||
write!(buf, "{}", db.struct_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::Union(it) => {
|
||||
write!(buf, "{}", db.union_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::TypeAlias(it) => {
|
||||
write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::Trait(it) => {
|
||||
write!(buf, "{}", db.trait_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
LangItemTarget::EnumVariant(it) => {
|
||||
write!(buf, "{}", db.enum_variant_data(it).name.display(db.upcast(), edition))?
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(s) = s {
|
||||
write!(buf, "::{}", s.display(db.upcast(), edition))?;
|
||||
}
|
||||
return write!(buf, ")");
|
||||
}
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
write!(buf, "<")?;
|
||||
print_type_ref(db, anchor, map, buf, edition)?;
|
||||
write!(buf, ">::")?;
|
||||
}
|
||||
None => match path.kind() {
|
||||
PathKind::Plain => {}
|
||||
&PathKind::SELF => write!(buf, "self")?,
|
||||
PathKind::Super(n) => {
|
||||
for i in 0..*n {
|
||||
if i == 0 {
|
||||
buf.write_str("super")?;
|
||||
} else {
|
||||
buf.write_str("::super")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
PathKind::Crate => write!(buf, "crate")?,
|
||||
PathKind::Abs => {}
|
||||
PathKind::DollarCrate(_) => write!(buf, "$crate")?,
|
||||
},
|
||||
}
|
||||
|
||||
for (i, segment) in path.segments().iter().enumerate() {
|
||||
if i != 0 || !matches!(path.kind(), PathKind::Plain) {
|
||||
write!(buf, "::")?;
|
||||
}
|
||||
|
||||
write!(buf, "{}", segment.name.display(db.upcast(), edition))?;
|
||||
if let Some(generics) = segment.args_and_bindings {
|
||||
write!(buf, "::<")?;
|
||||
print_generic_args(db, generics, map, buf, edition)?;
|
||||
|
||||
write!(buf, ">")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_args(
|
||||
db: &dyn DefDatabase,
|
||||
generics: &GenericArgs,
|
||||
map: &TypesMap,
|
||||
buf: &mut dyn Write,
|
||||
edition: Edition,
|
||||
) -> fmt::Result {
|
||||
let mut first = true;
|
||||
let args = if generics.has_self_type {
|
||||
let (self_ty, args) = generics.args.split_first().unwrap();
|
||||
write!(buf, "Self=")?;
|
||||
print_generic_arg(db, self_ty, map, buf, edition)?;
|
||||
first = false;
|
||||
args
|
||||
} else {
|
||||
&generics.args
|
||||
};
|
||||
for arg in args {
|
||||
if !first {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
print_generic_arg(db, arg, map, buf, edition)?;
|
||||
}
|
||||
for binding in generics.bindings.iter() {
|
||||
if !first {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(buf, "{}", binding.name.display(db.upcast(), edition))?;
|
||||
if !binding.bounds.is_empty() {
|
||||
write!(buf, ": ")?;
|
||||
print_type_bounds(db, &binding.bounds, map, buf, edition)?;
|
||||
}
|
||||
if let Some(ty) = binding.type_ref {
|
||||
write!(buf, " = ")?;
|
||||
print_type_ref(db, ty, map, buf, edition)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_arg(
|
||||
db: &dyn DefDatabase,
|
||||
arg: &GenericArg,
|
||||
map: &TypesMap,
|
||||
buf: &mut dyn Write,
|
||||
edition: Edition,
|
||||
) -> fmt::Result {
|
||||
match arg {
|
||||
GenericArg::Type(ty) => print_type_ref(db, *ty, map, buf, edition),
|
||||
GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast(), edition)),
|
||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_ref(
|
||||
db: &dyn DefDatabase,
|
||||
type_ref: TypeRefId,
|
||||
map: &TypesMap,
|
||||
buf: &mut dyn Write,
|
||||
edition: Edition,
|
||||
) -> fmt::Result {
|
||||
// FIXME: deduplicate with `HirDisplay` impl
|
||||
match &map[type_ref] {
|
||||
TypeRef::Never => write!(buf, "!")?,
|
||||
TypeRef::Placeholder => write!(buf, "_")?,
|
||||
TypeRef::Tuple(fields) => {
|
||||
write!(buf, "(")?;
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
print_type_ref(db, *field, map, buf, edition)?;
|
||||
}
|
||||
write!(buf, ")")?;
|
||||
}
|
||||
TypeRef::Path(path) => print_path(db, path, map, buf, edition)?,
|
||||
TypeRef::RawPtr(pointee, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "*const",
|
||||
Mutability::Mut => "*mut",
|
||||
};
|
||||
write!(buf, "{mtbl} ")?;
|
||||
print_type_ref(db, *pointee, map, buf, edition)?;
|
||||
}
|
||||
TypeRef::Reference(ref_) => {
|
||||
let mtbl = match ref_.mutability {
|
||||
Mutability::Shared => "",
|
||||
Mutability::Mut => "mut ",
|
||||
};
|
||||
write!(buf, "&")?;
|
||||
if let Some(lt) = &ref_.lifetime {
|
||||
write!(buf, "{} ", lt.name.display(db.upcast(), edition))?;
|
||||
}
|
||||
write!(buf, "{mtbl}")?;
|
||||
print_type_ref(db, ref_.ty, map, buf, edition)?;
|
||||
}
|
||||
TypeRef::Array(array) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(db, array.ty, map, buf, edition)?;
|
||||
write!(buf, "; {}]", array.len.display(db.upcast(), edition))?;
|
||||
}
|
||||
TypeRef::Slice(elem) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(db, *elem, map, buf, edition)?;
|
||||
write!(buf, "]")?;
|
||||
}
|
||||
TypeRef::Fn(fn_) => {
|
||||
let ((_, return_type), args) =
|
||||
fn_.params().split_last().expect("TypeRef::Fn is missing return type");
|
||||
if fn_.is_unsafe() {
|
||||
write!(buf, "unsafe ")?;
|
||||
}
|
||||
if let Some(abi) = fn_.abi() {
|
||||
buf.write_str("extern ")?;
|
||||
buf.write_str(abi.as_str())?;
|
||||
buf.write_char(' ')?;
|
||||
}
|
||||
write!(buf, "fn(")?;
|
||||
for (i, (_, typeref)) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
print_type_ref(db, *typeref, map, buf, edition)?;
|
||||
}
|
||||
if fn_.is_varargs() {
|
||||
if !args.is_empty() {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
write!(buf, "...")?;
|
||||
}
|
||||
write!(buf, ") -> ")?;
|
||||
print_type_ref(db, *return_type, map, buf, edition)?;
|
||||
}
|
||||
TypeRef::Macro(_ast_id) => {
|
||||
write!(buf, "<macro>")?;
|
||||
}
|
||||
TypeRef::Error => write!(buf, "{{unknown}}")?,
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
write!(buf, "impl ")?;
|
||||
print_type_bounds(db, bounds, map, buf, edition)?;
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
write!(buf, "dyn ")?;
|
||||
print_type_bounds(db, bounds, map, buf, edition)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_bounds(
|
||||
db: &dyn DefDatabase,
|
||||
bounds: &[TypeBound],
|
||||
map: &TypesMap,
|
||||
buf: &mut dyn Write,
|
||||
edition: Edition,
|
||||
) -> fmt::Result {
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(buf, " + ")?;
|
||||
}
|
||||
|
||||
match bound {
|
||||
TypeBound::Path(path, modifier) => {
|
||||
match modifier {
|
||||
TraitBoundModifier::None => (),
|
||||
TraitBoundModifier::Maybe => write!(buf, "?")?,
|
||||
}
|
||||
print_path(db, &map[*path], map, buf, edition)?;
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
write!(
|
||||
buf,
|
||||
"for<{}> ",
|
||||
lifetimes.iter().map(|it| it.display(db.upcast(), edition)).format(", ")
|
||||
)?;
|
||||
print_path(db, &map[*path], map, buf, edition)?;
|
||||
}
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?,
|
||||
TypeBound::Use(args) => {
|
||||
write!(buf, "use<")?;
|
||||
let mut first = true;
|
||||
for arg in args {
|
||||
if !mem::take(&mut first) {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
match arg {
|
||||
UseArgRef::Name(it) => write!(buf, "{}", it.display(db.upcast(), edition))?,
|
||||
UseArgRef::Lifetime(it) => {
|
||||
write!(buf, "{}", it.name.display(db.upcast(), edition))?
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(buf, ">")?
|
||||
}
|
||||
TypeBound::Error => write!(buf, "{{unknown}}")?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,37 +1,43 @@
|
|||
//! Name resolution façade.
|
||||
use std::{fmt, iter, mem};
|
||||
use std::{fmt, mem};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{name::Name, MacroDefId};
|
||||
use intern::{sym, Symbol};
|
||||
use base_db::Crate;
|
||||
use hir_expand::{
|
||||
MacroDefId,
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::Name,
|
||||
};
|
||||
use intern::{Symbol, sym};
|
||||
use itertools::Itertools as _;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::SyntaxContextId;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use span::SyntaxContext;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
builtin_type::BuiltinType,
|
||||
data::ExternCrateDeclData,
|
||||
db::DefDatabase,
|
||||
expr_store::{
|
||||
scope::{ExprScopes, ScopeId},
|
||||
HygieneId,
|
||||
},
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
hir::{BindingId, ExprId, LabelId},
|
||||
item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
|
||||
lang_item::LangItemTarget,
|
||||
nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo},
|
||||
path::{ModPath, Path, PathKind},
|
||||
per_ns::PerNs,
|
||||
type_ref::{LifetimeRef, TypesMap},
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
|
||||
ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule,
|
||||
ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id,
|
||||
MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId,
|
||||
TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
|
||||
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId,
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
expr_store::{
|
||||
HygieneId,
|
||||
path::Path,
|
||||
scope::{ExprScopes, ScopeId},
|
||||
},
|
||||
hir::{
|
||||
BindingId, ExprId, LabelId,
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
},
|
||||
item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope},
|
||||
item_tree::ImportAlias,
|
||||
lang_item::LangItemTarget,
|
||||
nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo},
|
||||
per_ns::PerNs,
|
||||
type_ref::LifetimeRef,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -47,6 +53,7 @@ pub struct Resolver {
|
|||
#[derive(Clone)]
|
||||
struct ModuleItemMap {
|
||||
def_map: Arc<DefMap>,
|
||||
local_def_map: Arc<LocalDefMap>,
|
||||
module_id: LocalModuleId,
|
||||
}
|
||||
|
||||
|
|
@ -76,16 +83,13 @@ impl fmt::Debug for ExprScope {
|
|||
enum Scope {
|
||||
/// All the items and imported names of a module
|
||||
BlockScope(ModuleItemMap),
|
||||
/// Brings the generic parameters of an item into scope
|
||||
/// Brings the generic parameters of an item into scope as well as the `Self` type alias /
|
||||
/// generic for ADTs and impls.
|
||||
GenericParams { def: GenericDefId, params: Arc<GenericParams> },
|
||||
/// Brings `Self` in `impl` block into scope
|
||||
ImplDefScope(ImplId),
|
||||
/// Brings `Self` in enum, struct and union definitions into scope
|
||||
AdtScope(AdtId),
|
||||
/// Local bindings
|
||||
ExprScope(ExprScope),
|
||||
/// Macro definition inside bodies that affects all paths after it in the same block.
|
||||
MacroDefScope(Box<MacroDefId>),
|
||||
MacroDefScope(MacroDefId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
@ -101,9 +105,8 @@ pub enum TypeNs {
|
|||
BuiltinType(BuiltinType),
|
||||
TraitId(TraitId),
|
||||
TraitAliasId(TraitAliasId),
|
||||
// Module belong to type ns, but the resolver is used when all module paths
|
||||
// are fully resolved.
|
||||
// ModuleId(ModuleId)
|
||||
|
||||
ModuleId(ModuleId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -180,7 +183,7 @@ impl Resolver {
|
|||
{
|
||||
let path = match path {
|
||||
Path::BarePath(mod_path) => mod_path,
|
||||
Path::Normal(it) => it.mod_path(),
|
||||
Path::Normal(it) => &it.mod_path,
|
||||
Path::LangItem(l, seg) => {
|
||||
let type_ns = match *l {
|
||||
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
||||
|
|
@ -207,12 +210,33 @@ impl Resolver {
|
|||
return self.module_scope.resolve_path_in_type_ns(db, path);
|
||||
}
|
||||
|
||||
let remaining_idx = || if path.segments().len() == 1 { None } else { Some(1) };
|
||||
let remaining_idx = || {
|
||||
if path.segments().len() == 1 { None } else { Some(1) }
|
||||
};
|
||||
|
||||
for scope in self.scopes() {
|
||||
match scope {
|
||||
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
|
||||
Scope::GenericParams { params, def } => {
|
||||
if let &GenericDefId::ImplId(impl_) = def {
|
||||
if *first_name == sym::Self_ {
|
||||
return Some((
|
||||
TypeNs::SelfType(impl_),
|
||||
remaining_idx(),
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
} else if let &GenericDefId::AdtId(adt) = def {
|
||||
if *first_name == sym::Self_ {
|
||||
return Some((
|
||||
TypeNs::AdtSelfType(adt),
|
||||
remaining_idx(),
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
||||
return Some((
|
||||
TypeNs::GenericParam(id),
|
||||
|
|
@ -222,28 +246,26 @@ impl Resolver {
|
|||
));
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some((
|
||||
TypeNs::SelfType(impl_),
|
||||
remaining_idx(),
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
&Scope::AdtScope(adt) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some((
|
||||
TypeNs::AdtSelfType(adt),
|
||||
remaining_idx(),
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Scope::BlockScope(m) => {
|
||||
if let Some(res) = m.resolve_path_in_type_ns(db, path) {
|
||||
let res = match res.0 {
|
||||
TypeNs::ModuleId(_) if res.1.is_none() => {
|
||||
if let Some(ModuleDefId::BuiltinType(builtin)) = BUILTIN_SCOPE
|
||||
.get(first_name)
|
||||
.and_then(|builtin| builtin.take_types())
|
||||
{
|
||||
(
|
||||
TypeNs::BuiltinType(builtin),
|
||||
remaining_idx(),
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
)
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
_ => res,
|
||||
};
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
|
|
@ -269,11 +291,18 @@ impl Resolver {
|
|||
db: &dyn DefDatabase,
|
||||
visibility: &RawVisibility,
|
||||
) -> Option<Visibility> {
|
||||
let within_impl = self.scopes().any(|scope| matches!(scope, Scope::ImplDefScope(_)));
|
||||
match visibility {
|
||||
RawVisibility::Module(_, _) => {
|
||||
let (item_map, module) = self.item_scope();
|
||||
item_map.resolve_visibility(db, module, visibility, within_impl)
|
||||
let (item_map, item_local_map, module) = self.item_scope_();
|
||||
item_map.resolve_visibility(
|
||||
item_local_map,
|
||||
db,
|
||||
module,
|
||||
visibility,
|
||||
self.scopes().any(|scope| {
|
||||
matches!(scope, Scope::GenericParams { def: GenericDefId::ImplId(_), .. })
|
||||
}),
|
||||
)
|
||||
}
|
||||
RawVisibility::Public => Some(Visibility::Public),
|
||||
}
|
||||
|
|
@ -296,7 +325,7 @@ impl Resolver {
|
|||
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
|
||||
let path = match path {
|
||||
Path::BarePath(mod_path) => mod_path,
|
||||
Path::Normal(it) => it.mod_path(),
|
||||
Path::Normal(it) => &it.mod_path,
|
||||
Path::LangItem(l, None) => {
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(
|
||||
|
|
@ -314,7 +343,7 @@ impl Resolver {
|
|||
None,
|
||||
),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
))
|
||||
));
|
||||
}
|
||||
Path::LangItem(l, Some(_)) => {
|
||||
let type_ns = match *l {
|
||||
|
|
@ -336,7 +365,7 @@ impl Resolver {
|
|||
}
|
||||
};
|
||||
let n_segments = path.segments().len();
|
||||
let tmp = Name::new_symbol_root(sym::self_.clone());
|
||||
let tmp = Name::new_symbol_root(sym::self_);
|
||||
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
|
||||
let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
|
||||
if skip_to_mod {
|
||||
|
|
@ -367,6 +396,14 @@ impl Resolver {
|
|||
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
||||
}
|
||||
Scope::GenericParams { params, def } => {
|
||||
if let &GenericDefId::ImplId(impl_) = def {
|
||||
if *first_name == sym::Self_ {
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(id) = params.find_const_by_name(first_name, *def) {
|
||||
let val = ValueNs::GenericParam(id);
|
||||
return Some((
|
||||
|
|
@ -375,16 +412,6 @@ impl Resolver {
|
|||
));
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// bare `Self` doesn't work in the value namespace in a struct/enum definition
|
||||
Scope::AdtScope(_) => continue,
|
||||
Scope::BlockScope(m) => {
|
||||
if let Some(def) = m.resolve_path_in_value_ns(db, path) {
|
||||
return Some(def);
|
||||
|
|
@ -397,6 +424,22 @@ impl Resolver {
|
|||
match scope {
|
||||
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
|
||||
Scope::GenericParams { params, def } => {
|
||||
if let &GenericDefId::ImplId(impl_) = def {
|
||||
if *first_name == sym::Self_ {
|
||||
return Some((
|
||||
ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
} else if let &GenericDefId::AdtId(adt) = def {
|
||||
if *first_name == sym::Self_ {
|
||||
let ty = TypeNs::AdtSelfType(adt);
|
||||
return Some((
|
||||
ResolveValueResult::Partial(ty, 1, None),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
||||
let ty = TypeNs::GenericParam(id);
|
||||
return Some((
|
||||
|
|
@ -405,23 +448,6 @@ impl Resolver {
|
|||
));
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some((
|
||||
ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
let ty = TypeNs::AdtSelfType(*adt);
|
||||
return Some((
|
||||
ResolveValueResult::Partial(ty, 1, None),
|
||||
ResolvePathResultPrefixInfo::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Scope::BlockScope(m) => {
|
||||
if let Some(def) = m.resolve_path_in_value_ns(db, path) {
|
||||
return Some(def);
|
||||
|
|
@ -468,9 +494,16 @@ impl Resolver {
|
|||
path: &ModPath,
|
||||
expected_macro_kind: Option<MacroSubNs>,
|
||||
) -> Option<(MacroId, Option<ImportOrGlob>)> {
|
||||
let (item_map, module) = self.item_scope();
|
||||
let (item_map, item_local_map, module) = self.item_scope_();
|
||||
item_map
|
||||
.resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
|
||||
.resolve_path(
|
||||
item_local_map,
|
||||
db,
|
||||
module,
|
||||
path,
|
||||
BuiltinShadowMode::Other,
|
||||
expected_macro_kind,
|
||||
)
|
||||
.0
|
||||
.take_macros_import()
|
||||
}
|
||||
|
|
@ -485,16 +518,19 @@ impl Resolver {
|
|||
}
|
||||
|
||||
pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option<LifetimeNs> {
|
||||
if lifetime.name == sym::tick_static.clone() {
|
||||
return Some(LifetimeNs::Static);
|
||||
}
|
||||
|
||||
self.scopes().find_map(|scope| match scope {
|
||||
Scope::GenericParams { def, params } => {
|
||||
params.find_lifetime_by_name(&lifetime.name, *def).map(LifetimeNs::LifetimeParam)
|
||||
match lifetime {
|
||||
LifetimeRef::Static => Some(LifetimeNs::Static),
|
||||
LifetimeRef::Named(name) => self.scopes().find_map(|scope| match scope {
|
||||
Scope::GenericParams { def, params } => {
|
||||
params.find_lifetime_by_name(name, *def).map(LifetimeNs::LifetimeParam)
|
||||
}
|
||||
_ => None,
|
||||
}),
|
||||
LifetimeRef::Placeholder | LifetimeRef::Error => None,
|
||||
LifetimeRef::Param(lifetime_param_id) => {
|
||||
Some(LifetimeNs::LifetimeParam(*lifetime_param_id))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a set of names available in the current scope.
|
||||
|
|
@ -544,7 +580,7 @@ impl Resolver {
|
|||
for scope in self.scopes() {
|
||||
scope.process_names(&mut res, db);
|
||||
}
|
||||
let ModuleItemMap { ref def_map, module_id } = self.module_scope;
|
||||
let ModuleItemMap { ref def_map, module_id, ref local_def_map } = self.module_scope;
|
||||
// FIXME: should we provide `self` here?
|
||||
// f(
|
||||
// Name::self_param(),
|
||||
|
|
@ -566,7 +602,7 @@ impl Resolver {
|
|||
res.add(name, ScopeDef::ModuleDef(def.into()));
|
||||
},
|
||||
);
|
||||
def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
|
||||
local_def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
|
||||
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
|
||||
});
|
||||
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
|
||||
|
|
@ -581,6 +617,7 @@ impl Resolver {
|
|||
res.map
|
||||
}
|
||||
|
||||
/// Note: Not to be used directly within hir-def/hir-ty
|
||||
pub fn extern_crate_decls_in_scope<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn DefDatabase,
|
||||
|
|
@ -588,12 +625,22 @@ impl Resolver {
|
|||
self.module_scope.def_map[self.module_scope.module_id]
|
||||
.scope
|
||||
.extern_crate_decls()
|
||||
.map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone())
|
||||
.filter_map(|id| {
|
||||
let loc = id.lookup(db);
|
||||
let tree = loc.item_tree_id().item_tree(db);
|
||||
match &tree[loc.id.value].alias {
|
||||
Some(alias) => match alias {
|
||||
ImportAlias::Underscore => None,
|
||||
ImportAlias::Alias(name) => Some(name.clone()),
|
||||
},
|
||||
None => Some(tree[loc.id.value].name.clone()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn extern_crates_in_scope(&self) -> impl Iterator<Item = (Name, ModuleId)> + '_ {
|
||||
self.module_scope
|
||||
.def_map
|
||||
.local_def_map
|
||||
.extern_prelude()
|
||||
.map(|(name, module_id)| (name.clone(), module_id.0.into()))
|
||||
}
|
||||
|
|
@ -606,13 +653,12 @@ impl Resolver {
|
|||
for scope in self.scopes() {
|
||||
match scope {
|
||||
Scope::BlockScope(m) => traits.extend(m.def_map[m.module_id].scope.traits()),
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
let impl_data = db.impl_data(impl_);
|
||||
&Scope::GenericParams { def: GenericDefId::ImplId(impl_), .. } => {
|
||||
let impl_data = db.impl_signature(impl_);
|
||||
if let Some(target_trait) = impl_data.target_trait {
|
||||
if let Some(TypeNs::TraitId(trait_)) = self.resolve_path_in_type_ns_fully(
|
||||
db,
|
||||
&impl_data.types_map[target_trait.path],
|
||||
) {
|
||||
if let Some(TypeNs::TraitId(trait_)) = self
|
||||
.resolve_path_in_type_ns_fully(db, &impl_data.store[target_trait.path])
|
||||
{
|
||||
traits.insert(trait_);
|
||||
}
|
||||
}
|
||||
|
|
@ -641,29 +687,21 @@ impl Resolver {
|
|||
}
|
||||
|
||||
pub fn module(&self) -> ModuleId {
|
||||
let (def_map, local_id) = self.item_scope();
|
||||
let (def_map, _, local_id) = self.item_scope_();
|
||||
def_map.module_id(local_id)
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> CrateId {
|
||||
pub fn item_scope(&self) -> &ItemScope {
|
||||
let (def_map, _, local_id) = self.item_scope_();
|
||||
&def_map[local_id].scope
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> Crate {
|
||||
self.module_scope.def_map.krate()
|
||||
}
|
||||
|
||||
pub fn def_map(&self) -> &DefMap {
|
||||
self.item_scope().0
|
||||
}
|
||||
|
||||
pub fn where_predicates_in_scope(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&crate::generics::WherePredicate, (&GenericDefId, &TypesMap))> {
|
||||
self.scopes()
|
||||
.filter_map(|scope| match scope {
|
||||
Scope::GenericParams { params, def } => Some((params, def)),
|
||||
_ => None,
|
||||
})
|
||||
.flat_map(|(params, def)| {
|
||||
params.where_predicates().zip(iter::repeat((def, ¶ms.types_map)))
|
||||
})
|
||||
self.item_scope_().0
|
||||
}
|
||||
|
||||
pub fn generic_def(&self) -> Option<GenericDefId> {
|
||||
|
|
@ -694,19 +732,9 @@ impl Resolver {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn type_owner(&self) -> Option<TypeOwnerId> {
|
||||
self.scopes().find_map(|scope| match scope {
|
||||
Scope::BlockScope(_) | Scope::MacroDefScope(_) => None,
|
||||
&Scope::GenericParams { def, .. } => Some(def.into()),
|
||||
&Scope::ImplDefScope(id) => Some(id.into()),
|
||||
&Scope::AdtScope(adt) => Some(adt.into()),
|
||||
Scope::ExprScope(it) => Some(it.owner.into()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn impl_def(&self) -> Option<ImplId> {
|
||||
self.scopes().find_map(|scope| match scope {
|
||||
Scope::ImplDefScope(def) => Some(*def),
|
||||
&Scope::GenericParams { def: GenericDefId::ImplId(def), .. } => Some(def),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
|
@ -748,7 +776,6 @@ impl Resolver {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(_) | Scope::ImplDefScope(_) => continue,
|
||||
Scope::BlockScope(m) => {
|
||||
if m.resolve_path_in_value_ns(db, current_name_as_path).is_some() {
|
||||
// It does not resolve to our renamed variable.
|
||||
|
|
@ -801,7 +828,6 @@ impl Resolver {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(_) | Scope::ImplDefScope(_) => continue,
|
||||
Scope::BlockScope(m) => {
|
||||
if m.resolve_path_in_value_ns(db, name_as_path).is_some() {
|
||||
return None;
|
||||
|
|
@ -829,7 +855,7 @@ impl Resolver {
|
|||
scope_id: ScopeId,
|
||||
) {
|
||||
if let Some(macro_id) = expr_scopes.macro_def(scope_id) {
|
||||
resolver.scopes.push(Scope::MacroDefScope(macro_id.clone()));
|
||||
resolver.scopes.push(Scope::MacroDefScope(**macro_id));
|
||||
}
|
||||
resolver.scopes.push(Scope::ExprScope(ExprScope {
|
||||
owner,
|
||||
|
|
@ -838,9 +864,12 @@ impl Resolver {
|
|||
}));
|
||||
if let Some(block) = expr_scopes.block(scope_id) {
|
||||
let def_map = db.block_def_map(block);
|
||||
resolver
|
||||
.scopes
|
||||
.push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
|
||||
let local_def_map = block.lookup(db).module.only_local_def_map(db);
|
||||
resolver.scopes.push(Scope::BlockScope(ModuleItemMap {
|
||||
def_map,
|
||||
local_def_map,
|
||||
module_id: DefMap::ROOT,
|
||||
}));
|
||||
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
||||
// already traverses all parents, so this is O(n²). I think we could only store the
|
||||
// innermost module scope instead?
|
||||
|
|
@ -881,7 +910,7 @@ impl Resolver {
|
|||
fn handle_macro_def_scope(
|
||||
db: &dyn DefDatabase,
|
||||
hygiene_id: &mut HygieneId,
|
||||
hygiene_info: &mut Option<(SyntaxContextId, MacroDefId)>,
|
||||
hygiene_info: &mut Option<(SyntaxContext, MacroDefId)>,
|
||||
macro_id: &MacroDefId,
|
||||
) {
|
||||
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
|
||||
|
|
@ -889,11 +918,10 @@ fn handle_macro_def_scope(
|
|||
// A macro is allowed to refer to variables from before its declaration.
|
||||
// Therefore, if we got to the rib of its declaration, give up its hygiene
|
||||
// and use its parent expansion.
|
||||
let parent_ctx = db.lookup_intern_syntax_context(*parent_ctx);
|
||||
*hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
|
||||
*hygiene_info = parent_ctx.outer_expn.map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion);
|
||||
(parent_ctx.parent, expansion.def)
|
||||
*hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(db));
|
||||
*hygiene_info = parent_ctx.outer_expn(db).map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion.into());
|
||||
(parent_ctx.parent(db), expansion.def)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -903,12 +931,12 @@ fn handle_macro_def_scope(
|
|||
fn hygiene_info(
|
||||
db: &dyn DefDatabase,
|
||||
hygiene_id: HygieneId,
|
||||
) -> Option<(SyntaxContextId, MacroDefId)> {
|
||||
) -> Option<(SyntaxContext, MacroDefId)> {
|
||||
if !hygiene_id.is_root() {
|
||||
let ctx = hygiene_id.lookup(db);
|
||||
ctx.outer_expn.map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion);
|
||||
(ctx.parent, expansion.def)
|
||||
let ctx = hygiene_id.lookup();
|
||||
ctx.outer_expn(db).map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion.into());
|
||||
(ctx.parent(db), expansion.def)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -928,9 +956,10 @@ impl Resolver {
|
|||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> PerNs {
|
||||
let (item_map, module) = self.item_scope();
|
||||
let (item_map, item_local_map, module) = self.item_scope_();
|
||||
// This method resolves `path` just like import paths, so no expected macro subns is given.
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None);
|
||||
let (module_res, segment_index) =
|
||||
item_map.resolve_path(item_local_map, db, module, path, shadow, None);
|
||||
if segment_index.is_some() {
|
||||
return PerNs::none();
|
||||
}
|
||||
|
|
@ -938,13 +967,17 @@ impl Resolver {
|
|||
}
|
||||
|
||||
/// The innermost block scope that contains items or the module scope that contains this resolver.
|
||||
fn item_scope(&self) -> (&DefMap, LocalModuleId) {
|
||||
fn item_scope_(&self) -> (&DefMap, &LocalDefMap, LocalModuleId) {
|
||||
self.scopes()
|
||||
.find_map(|scope| match scope {
|
||||
Scope::BlockScope(m) => Some((&*m.def_map, m.module_id)),
|
||||
Scope::BlockScope(m) => Some((&*m.def_map, &*m.local_def_map, m.module_id)),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or((&self.module_scope.def_map, self.module_scope.module_id))
|
||||
.unwrap_or((
|
||||
&self.module_scope.def_map,
|
||||
&self.module_scope.local_def_map,
|
||||
self.module_scope.module_id,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -972,8 +1005,13 @@ impl Scope {
|
|||
})
|
||||
});
|
||||
}
|
||||
Scope::GenericParams { params, def: parent } => {
|
||||
let parent = *parent;
|
||||
&Scope::GenericParams { ref params, def: parent } => {
|
||||
if let GenericDefId::ImplId(impl_) = parent {
|
||||
acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::ImplSelfType(impl_));
|
||||
} else if let GenericDefId::AdtId(adt) = parent {
|
||||
acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::AdtSelfType(adt));
|
||||
}
|
||||
|
||||
for (local_id, param) in params.iter_type_or_consts() {
|
||||
if let Some(name) = ¶m.name() {
|
||||
let id = TypeOrConstParamId { parent, local_id };
|
||||
|
|
@ -996,12 +1034,6 @@ impl Scope {
|
|||
acc.add(¶m.name, ScopeDef::GenericParam(id.into()))
|
||||
}
|
||||
}
|
||||
Scope::ImplDefScope(i) => {
|
||||
acc.add(&Name::new_symbol_root(sym::Self_.clone()), ScopeDef::ImplSelfType(*i));
|
||||
}
|
||||
Scope::AdtScope(i) => {
|
||||
acc.add(&Name::new_symbol_root(sym::Self_.clone()), ScopeDef::AdtSelfType(*i));
|
||||
}
|
||||
Scope::ExprScope(scope) => {
|
||||
if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) {
|
||||
acc.add(&name, ScopeDef::Label(label))
|
||||
|
|
@ -1045,13 +1077,14 @@ fn resolver_for_scope_(
|
|||
for scope in scope_chain.into_iter().rev() {
|
||||
if let Some(block) = scopes.block(scope) {
|
||||
let def_map = db.block_def_map(block);
|
||||
r = r.push_block_scope(def_map);
|
||||
let local_def_map = block.lookup(db).module.only_local_def_map(db);
|
||||
r = r.push_block_scope(def_map, local_def_map);
|
||||
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
||||
// already traverses all parents, so this is O(n²). I think we could only store the
|
||||
// innermost module scope instead?
|
||||
}
|
||||
if let Some(macro_id) = scopes.macro_def(scope) {
|
||||
r = r.push_scope(Scope::MacroDefScope(macro_id.clone()));
|
||||
r = r.push_scope(Scope::MacroDefScope(**macro_id));
|
||||
}
|
||||
|
||||
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
|
||||
|
|
@ -1070,13 +1103,12 @@ impl Resolver {
|
|||
self.push_scope(Scope::GenericParams { def, params })
|
||||
}
|
||||
|
||||
fn push_impl_def_scope(self, impl_def: ImplId) -> Resolver {
|
||||
self.push_scope(Scope::ImplDefScope(impl_def))
|
||||
}
|
||||
|
||||
fn push_block_scope(self, def_map: Arc<DefMap>) -> Resolver {
|
||||
debug_assert!(def_map.block_id().is_some());
|
||||
self.push_scope(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }))
|
||||
fn push_block_scope(self, def_map: Arc<DefMap>, local_def_map: Arc<LocalDefMap>) -> Resolver {
|
||||
self.push_scope(Scope::BlockScope(ModuleItemMap {
|
||||
def_map,
|
||||
local_def_map,
|
||||
module_id: DefMap::ROOT,
|
||||
}))
|
||||
}
|
||||
|
||||
fn push_expr_scope(
|
||||
|
|
@ -1095,8 +1127,13 @@ impl ModuleItemMap {
|
|||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
|
||||
let (module_def, unresolved_idx, prefix_info) =
|
||||
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
|
||||
let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally(
|
||||
&self.local_def_map,
|
||||
db,
|
||||
self.module_id,
|
||||
path,
|
||||
BuiltinShadowMode::Other,
|
||||
);
|
||||
match unresolved_idx {
|
||||
None => {
|
||||
let (value, import) = to_value_ns(module_def)?;
|
||||
|
|
@ -1129,8 +1166,13 @@ impl ModuleItemMap {
|
|||
path: &ModPath,
|
||||
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
|
||||
{
|
||||
let (module_def, idx, prefix_info) =
|
||||
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
|
||||
let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally(
|
||||
&self.local_def_map,
|
||||
db,
|
||||
self.module_id,
|
||||
path,
|
||||
BuiltinShadowMode::Other,
|
||||
);
|
||||
let (res, import) = to_type_ns(module_def)?;
|
||||
Some((res, idx, import, prefix_info))
|
||||
}
|
||||
|
|
@ -1168,11 +1210,12 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
|
|||
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
||||
ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
|
||||
|
||||
ModuleDefId::ModuleId(it) => TypeNs::ModuleId(it),
|
||||
|
||||
ModuleDefId::FunctionId(_)
|
||||
| ModuleDefId::ConstId(_)
|
||||
| ModuleDefId::MacroId(_)
|
||||
| ModuleDefId::StaticId(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
| ModuleDefId::StaticId(_) => return None,
|
||||
};
|
||||
Some((res, def.import))
|
||||
}
|
||||
|
|
@ -1225,11 +1268,14 @@ pub trait HasResolver: Copy {
|
|||
|
||||
impl HasResolver for ModuleId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
let mut def_map = self.def_map(db);
|
||||
let (mut def_map, local_def_map) = self.local_def_map(db);
|
||||
let mut module_id = self.local_id;
|
||||
|
||||
if !self.is_block_module() {
|
||||
return Resolver { scopes: vec![], module_scope: ModuleItemMap { def_map, module_id } };
|
||||
return Resolver {
|
||||
scopes: vec![],
|
||||
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
||||
};
|
||||
}
|
||||
|
||||
let mut modules: SmallVec<[_; 1]> = smallvec![];
|
||||
|
|
@ -1243,10 +1289,14 @@ impl HasResolver for ModuleId {
|
|||
}
|
||||
let mut resolver = Resolver {
|
||||
scopes: Vec::with_capacity(modules.len()),
|
||||
module_scope: ModuleItemMap { def_map, module_id },
|
||||
module_scope: ModuleItemMap {
|
||||
def_map,
|
||||
local_def_map: local_def_map.clone(),
|
||||
module_id,
|
||||
},
|
||||
};
|
||||
for def_map in modules.into_iter().rev() {
|
||||
resolver = resolver.push_block_scope(def_map);
|
||||
resolver = resolver.push_block_scope(def_map, local_def_map.clone());
|
||||
}
|
||||
resolver
|
||||
}
|
||||
|
|
@ -1254,9 +1304,10 @@ impl HasResolver for ModuleId {
|
|||
|
||||
impl HasResolver for CrateRootModuleId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
let (def_map, local_def_map) = self.local_def_map(db);
|
||||
Resolver {
|
||||
scopes: vec![],
|
||||
module_scope: ModuleItemMap { def_map: self.def_map(db), module_id: DefMap::ROOT },
|
||||
module_scope: ModuleItemMap { def_map, local_def_map, module_id: DefMap::ROOT },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1276,10 +1327,7 @@ impl HasResolver for TraitAliasId {
|
|||
impl<T: Into<AdtId> + Copy> HasResolver for T {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
let def = self.into();
|
||||
def.module(db)
|
||||
.resolver(db)
|
||||
.push_generic_params_scope(db, def.into())
|
||||
.push_scope(Scope::AdtScope(def))
|
||||
def.module(db).resolver(db).push_generic_params_scope(db, def.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1309,11 +1357,7 @@ impl HasResolver for TypeAliasId {
|
|||
|
||||
impl HasResolver for ImplId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
self.lookup(db)
|
||||
.container
|
||||
.resolver(db)
|
||||
.push_generic_params_scope(db, self.into())
|
||||
.push_impl_def_scope(self)
|
||||
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1336,23 +1380,6 @@ impl HasResolver for UseId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasResolver for TypeOwnerId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
match self {
|
||||
TypeOwnerId::FunctionId(it) => it.resolver(db),
|
||||
TypeOwnerId::StaticId(it) => it.resolver(db),
|
||||
TypeOwnerId::ConstId(it) => it.resolver(db),
|
||||
TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.resolver(db),
|
||||
TypeOwnerId::AdtId(it) => it.resolver(db),
|
||||
TypeOwnerId::TraitId(it) => it.resolver(db),
|
||||
TypeOwnerId::TraitAliasId(it) => it.resolver(db),
|
||||
TypeOwnerId::TypeAliasId(it) => it.resolver(db),
|
||||
TypeOwnerId::ImplId(it) => it.resolver(db),
|
||||
TypeOwnerId::EnumVariantId(it) => it.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for DefWithBodyId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
match self {
|
||||
|
|
@ -1360,7 +1387,6 @@ impl HasResolver for DefWithBodyId {
|
|||
DefWithBodyId::FunctionId(f) => f.resolver(db),
|
||||
DefWithBodyId::StaticId(s) => s.resolver(db),
|
||||
DefWithBodyId::VariantId(v) => v.resolver(db),
|
||||
DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1438,7 +1464,7 @@ impl HasResolver for MacroRulesId {
|
|||
fn lookup_resolver<'db>(
|
||||
db: &(dyn DefDatabase + 'db),
|
||||
lookup: impl Lookup<
|
||||
Database<'db> = dyn DefDatabase + 'db,
|
||||
Database = dyn DefDatabase,
|
||||
Data = impl ItemTreeLoc<Container = impl HasResolver>,
|
||||
>,
|
||||
) -> Resolver {
|
||||
|
|
|
|||
975
src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
Normal file
975
src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
Normal file
|
|
@ -0,0 +1,975 @@
|
|||
//! Item signature IR definitions
|
||||
|
||||
use std::ops::Not as _;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{InFile, Intern, Lookup, name::Name};
|
||||
use intern::{Symbol, sym};
|
||||
use la_arena::{Arena, Idx};
|
||||
use rustc_abi::{IntegerType, ReprOptions};
|
||||
use syntax::{
|
||||
AstNode, SyntaxNodePtr,
|
||||
ast::{self, HasGenericParams, IsString},
|
||||
};
|
||||
use thin_vec::ThinVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
ConstId, EnumId, EnumVariantId, EnumVariantLoc, FunctionId, HasModule, ImplId, ItemContainerId,
|
||||
ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId, VariantId,
|
||||
db::DefDatabase,
|
||||
expr_store::{
|
||||
ExpressionStore, ExpressionStoreSourceMap,
|
||||
lower::{
|
||||
ExprCollector, lower_function, lower_generic_params, lower_trait, lower_trait_alias,
|
||||
lower_type_alias,
|
||||
},
|
||||
},
|
||||
hir::{ExprId, PatId, generics::GenericParams},
|
||||
item_tree::{
|
||||
AttrOwner, Field, FieldParent, FieldsShape, FileItemTreeId, ItemTree, ItemTreeId, ModItem,
|
||||
RawVisibility, RawVisibilityId,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
src::HasSource,
|
||||
type_ref::{TraitRef, TypeBound, TypeRefId},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct StructSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub flags: StructFlags,
|
||||
pub shape: FieldsShape,
|
||||
pub repr: Option<ReprOptions>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct StructFlags: u8 {
|
||||
/// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
|
||||
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
|
||||
/// Indicates whether the struct has a `#[fundamental]` attribute.
|
||||
const FUNDAMENTAL = 1 << 2;
|
||||
/// Indicates whether the struct is `PhantomData`.
|
||||
const IS_PHANTOM_DATA = 1 << 3;
|
||||
/// Indicates whether this struct is `Box`.
|
||||
const IS_BOX = 1 << 4;
|
||||
/// Indicates whether this struct is `ManuallyDrop`.
|
||||
const IS_MANUALLY_DROP = 1 << 5;
|
||||
/// Indicates whether this struct is `UnsafeCell`.
|
||||
const IS_UNSAFE_CELL = 1 << 6;
|
||||
/// Indicates whether this struct is `UnsafePinned`.
|
||||
const IS_UNSAFE_PINNED = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
impl StructSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
|
||||
let mut flags = StructFlags::empty();
|
||||
if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
|
||||
}
|
||||
if attrs.by_key(sym::fundamental).exists() {
|
||||
flags |= StructFlags::FUNDAMENTAL;
|
||||
}
|
||||
if let Some(lang) = attrs.lang_item() {
|
||||
match lang {
|
||||
LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
|
||||
LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
|
||||
LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
|
||||
LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
|
||||
LangItem::UnsafePinned => flags |= StructFlags::IS_UNSAFE_PINNED,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
let repr = attrs.repr();
|
||||
|
||||
let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db);
|
||||
let (store, generic_params, source_map) = lower_generic_params(
|
||||
db,
|
||||
loc.container,
|
||||
id.into(),
|
||||
file_id,
|
||||
value.generic_param_list(),
|
||||
value.where_clause(),
|
||||
);
|
||||
(
|
||||
Arc::new(StructSignature {
|
||||
generic_params,
|
||||
store,
|
||||
flags,
|
||||
shape: item_tree[loc.id.value].shape,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
repr,
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct UnionSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub flags: StructFlags,
|
||||
pub repr: Option<ReprOptions>,
|
||||
}
|
||||
|
||||
impl UnionSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = StructFlags::empty();
|
||||
if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
|
||||
}
|
||||
if attrs.by_key(sym::fundamental).exists() {
|
||||
flags |= StructFlags::FUNDAMENTAL;
|
||||
}
|
||||
|
||||
let repr = attrs.repr();
|
||||
|
||||
let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db);
|
||||
let (store, generic_params, source_map) = lower_generic_params(
|
||||
db,
|
||||
loc.container,
|
||||
id.into(),
|
||||
file_id,
|
||||
value.generic_param_list(),
|
||||
value.where_clause(),
|
||||
);
|
||||
(
|
||||
Arc::new(UnionSignature {
|
||||
generic_params,
|
||||
store,
|
||||
flags,
|
||||
repr,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct EnumFlags: u8 {
|
||||
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct EnumSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub flags: EnumFlags,
|
||||
pub repr: Option<ReprOptions>,
|
||||
}
|
||||
|
||||
impl EnumSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = EnumFlags::empty();
|
||||
if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
|
||||
}
|
||||
|
||||
let repr = attrs.repr();
|
||||
|
||||
let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db);
|
||||
let (store, generic_params, source_map) = lower_generic_params(
|
||||
db,
|
||||
loc.container,
|
||||
id.into(),
|
||||
file_id,
|
||||
value.generic_param_list(),
|
||||
value.where_clause(),
|
||||
);
|
||||
|
||||
(
|
||||
Arc::new(EnumSignature {
|
||||
generic_params,
|
||||
store,
|
||||
flags,
|
||||
repr,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn variant_body_type(&self) -> IntegerType {
|
||||
match self.repr {
|
||||
Some(ReprOptions { int: Some(builtin), .. }) => builtin,
|
||||
_ => IntegerType::Pointer(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct ConstFlags: u8 {
|
||||
const HAS_BODY = 1 << 1;
|
||||
const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ConstSignature {
|
||||
pub name: Option<Name>,
|
||||
// generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub type_ref: TypeRefId,
|
||||
pub flags: ConstFlags,
|
||||
}
|
||||
|
||||
impl ConstSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: ConstId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let module = loc.container.module(db);
|
||||
let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = ConstFlags::empty();
|
||||
if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
|
||||
flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
|
||||
}
|
||||
let source = loc.source(db);
|
||||
if source.value.body().is_some() {
|
||||
flags.insert(ConstFlags::HAS_BODY);
|
||||
}
|
||||
|
||||
let (store, source_map, type_ref) =
|
||||
crate::expr_store::lower::lower_type_ref(db, module, source.map(|it| it.ty()));
|
||||
|
||||
(
|
||||
Arc::new(ConstSignature {
|
||||
store: Arc::new(store),
|
||||
type_ref,
|
||||
flags,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_body(&self) -> bool {
|
||||
self.flags.contains(ConstFlags::HAS_BODY)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct StaticFlags: u8 {
|
||||
const HAS_BODY = 1 << 1;
|
||||
const MUTABLE = 1 << 3;
|
||||
const UNSAFE = 1 << 4;
|
||||
const EXPLICIT_SAFE = 1 << 5;
|
||||
const EXTERN = 1 << 6;
|
||||
const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct StaticSignature {
|
||||
pub name: Name,
|
||||
|
||||
// generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub type_ref: TypeRefId,
|
||||
pub flags: StaticFlags,
|
||||
}
|
||||
impl StaticSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: StaticId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let module = loc.container.module(db);
|
||||
let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = StaticFlags::empty();
|
||||
if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
|
||||
flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
|
||||
}
|
||||
|
||||
if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
|
||||
flags.insert(StaticFlags::EXTERN);
|
||||
}
|
||||
|
||||
let source = loc.source(db);
|
||||
if source.value.body().is_some() {
|
||||
flags.insert(StaticFlags::HAS_BODY);
|
||||
}
|
||||
if source.value.mut_token().is_some() {
|
||||
flags.insert(StaticFlags::MUTABLE);
|
||||
}
|
||||
if source.value.unsafe_token().is_some() {
|
||||
flags.insert(StaticFlags::UNSAFE);
|
||||
}
|
||||
if source.value.safe_token().is_some() {
|
||||
flags.insert(StaticFlags::EXPLICIT_SAFE);
|
||||
}
|
||||
|
||||
let (store, source_map, type_ref) =
|
||||
crate::expr_store::lower::lower_type_ref(db, module, source.map(|it| it.ty()));
|
||||
|
||||
(
|
||||
Arc::new(StaticSignature {
|
||||
store: Arc::new(store),
|
||||
type_ref,
|
||||
flags,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct ImplFlags: u8 {
|
||||
const NEGATIVE = 1 << 1;
|
||||
const UNSAFE = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ImplSignature {
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub self_ty: TypeRefId,
|
||||
pub target_trait: Option<TraitRef>,
|
||||
pub flags: ImplFlags,
|
||||
}
|
||||
|
||||
impl ImplSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: ImplId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
|
||||
let mut flags = ImplFlags::empty();
|
||||
let src = loc.source(db);
|
||||
if src.value.unsafe_token().is_some() {
|
||||
flags.insert(ImplFlags::UNSAFE);
|
||||
}
|
||||
if src.value.excl_token().is_some() {
|
||||
flags.insert(ImplFlags::NEGATIVE);
|
||||
}
|
||||
|
||||
let (store, source_map, self_ty, target_trait, generic_params) =
|
||||
crate::expr_store::lower::lower_impl(db, loc.container, src, id);
|
||||
|
||||
(
|
||||
Arc::new(ImplSignature {
|
||||
store: Arc::new(store),
|
||||
generic_params,
|
||||
self_ty,
|
||||
target_trait,
|
||||
flags,
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct TraitFlags: u8 {
|
||||
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
|
||||
const FUNDAMENTAL = 1 << 2;
|
||||
const UNSAFE = 1 << 3;
|
||||
const AUTO = 1 << 4;
|
||||
const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5;
|
||||
const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6;
|
||||
const RUSTC_PAREN_SUGAR = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TraitSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub flags: TraitFlags,
|
||||
}
|
||||
|
||||
impl TraitSignature {
|
||||
pub fn query(db: &dyn DefDatabase, id: TraitId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let mut flags = TraitFlags::empty();
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
let source = loc.source(db);
|
||||
if source.value.auto_token().is_some() {
|
||||
flags.insert(TraitFlags::AUTO);
|
||||
}
|
||||
if source.value.unsafe_token().is_some() {
|
||||
flags.insert(TraitFlags::UNSAFE);
|
||||
}
|
||||
if attrs.by_key(sym::fundamental).exists() {
|
||||
flags |= TraitFlags::FUNDAMENTAL;
|
||||
}
|
||||
if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
|
||||
}
|
||||
if attrs.by_key(sym::rustc_paren_sugar).exists() {
|
||||
flags |= TraitFlags::RUSTC_PAREN_SUGAR;
|
||||
}
|
||||
let mut skip_array_during_method_dispatch =
|
||||
attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists();
|
||||
let mut skip_boxed_slice_during_method_dispatch = false;
|
||||
for tt in attrs.by_key(sym::rustc_skip_during_method_dispatch).tt_values() {
|
||||
for tt in tt.iter() {
|
||||
if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt {
|
||||
skip_array_during_method_dispatch |= ident.sym == sym::array;
|
||||
skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if skip_array_during_method_dispatch {
|
||||
flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH;
|
||||
}
|
||||
if skip_boxed_slice_during_method_dispatch {
|
||||
flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
|
||||
}
|
||||
|
||||
let (store, source_map, generic_params) = lower_trait(db, loc.container, source, id);
|
||||
|
||||
(
|
||||
Arc::new(TraitSignature {
|
||||
store: Arc::new(store),
|
||||
generic_params,
|
||||
flags,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TraitAliasSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
}
|
||||
|
||||
impl TraitAliasSignature {
|
||||
pub fn query(
|
||||
db: &dyn DefDatabase,
|
||||
id: TraitAliasId,
|
||||
) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let source = loc.source(db);
|
||||
let (store, source_map, generic_params) = lower_trait_alias(db, loc.container, source, id);
|
||||
|
||||
(
|
||||
Arc::new(TraitAliasSignature {
|
||||
generic_params,
|
||||
store: Arc::new(store),
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct FnFlags: u16 {
|
||||
const HAS_BODY = 1 << 1;
|
||||
const DEFAULT = 1 << 2;
|
||||
const CONST = 1 << 3;
|
||||
const ASYNC = 1 << 4;
|
||||
const UNSAFE = 1 << 5;
|
||||
const HAS_VARARGS = 1 << 6;
|
||||
const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
|
||||
const HAS_SELF_PARAM = 1 << 8;
|
||||
/// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
|
||||
/// but keeping it for all functions will consume a lot of memory when there are
|
||||
/// only very few functions with it. So we only encode its existence here, and lookup
|
||||
/// it if needed.
|
||||
const HAS_TARGET_FEATURE = 1 << 9;
|
||||
const DEPRECATED_SAFE_2024 = 1 << 10;
|
||||
const EXPLICIT_SAFE = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct FunctionSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub params: Box<[TypeRefId]>,
|
||||
pub ret_type: Option<TypeRefId>,
|
||||
pub abi: Option<Symbol>,
|
||||
pub flags: FnFlags,
|
||||
// FIXME: we should put this behind a fn flags + query to avoid bloating the struct
|
||||
pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>,
|
||||
}
|
||||
|
||||
impl FunctionSignature {
|
||||
pub fn query(
|
||||
db: &dyn DefDatabase,
|
||||
id: FunctionId,
|
||||
) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let module = loc.container.module(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let mut flags = FnFlags::empty();
|
||||
let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into());
|
||||
if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
|
||||
flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
|
||||
}
|
||||
|
||||
if attrs.by_key(sym::target_feature).exists() {
|
||||
flags.insert(FnFlags::HAS_TARGET_FEATURE);
|
||||
}
|
||||
let legacy_const_generics_indices = attrs.rustc_legacy_const_generics();
|
||||
|
||||
let source = loc.source(db);
|
||||
|
||||
if source.value.unsafe_token().is_some() {
|
||||
if attrs.by_key(sym::rustc_deprecated_safe_2024).exists() {
|
||||
flags.insert(FnFlags::DEPRECATED_SAFE_2024);
|
||||
} else {
|
||||
flags.insert(FnFlags::UNSAFE);
|
||||
}
|
||||
}
|
||||
if source.value.async_token().is_some() {
|
||||
flags.insert(FnFlags::ASYNC);
|
||||
}
|
||||
if source.value.const_token().is_some() {
|
||||
flags.insert(FnFlags::CONST);
|
||||
}
|
||||
if source.value.default_token().is_some() {
|
||||
flags.insert(FnFlags::DEFAULT);
|
||||
}
|
||||
if source.value.safe_token().is_some() {
|
||||
flags.insert(FnFlags::EXPLICIT_SAFE);
|
||||
}
|
||||
if source.value.body().is_some() {
|
||||
flags.insert(FnFlags::HAS_BODY);
|
||||
}
|
||||
|
||||
let abi = source.value.abi().map(|abi| {
|
||||
abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes()))
|
||||
});
|
||||
let (store, source_map, generic_params, params, ret_type, self_param, variadic) =
|
||||
lower_function(db, module, source, id);
|
||||
if self_param {
|
||||
flags.insert(FnFlags::HAS_SELF_PARAM);
|
||||
}
|
||||
if variadic {
|
||||
flags.insert(FnFlags::HAS_VARARGS);
|
||||
}
|
||||
(
|
||||
Arc::new(FunctionSignature {
|
||||
generic_params,
|
||||
store: Arc::new(store),
|
||||
params,
|
||||
ret_type,
|
||||
abi,
|
||||
flags,
|
||||
legacy_const_generics_indices,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_body(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_BODY)
|
||||
}
|
||||
|
||||
/// True if the first param is `self`. This is relevant to decide whether this
|
||||
/// can be called as a method.
|
||||
pub fn has_self_param(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_SELF_PARAM)
|
||||
}
|
||||
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.flags.contains(FnFlags::DEFAULT)
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
self.flags.contains(FnFlags::CONST)
|
||||
}
|
||||
|
||||
pub fn is_async(&self) -> bool {
|
||||
self.flags.contains(FnFlags::ASYNC)
|
||||
}
|
||||
|
||||
pub fn is_unsafe(&self) -> bool {
|
||||
self.flags.contains(FnFlags::UNSAFE)
|
||||
}
|
||||
|
||||
pub fn is_deprecated_safe_2024(&self) -> bool {
|
||||
self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
|
||||
}
|
||||
|
||||
pub fn is_safe(&self) -> bool {
|
||||
self.flags.contains(FnFlags::EXPLICIT_SAFE)
|
||||
}
|
||||
|
||||
pub fn is_varargs(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_VARARGS)
|
||||
}
|
||||
|
||||
pub fn has_target_feature(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub struct TypeAliasFlags: u8 {
|
||||
const RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 1;
|
||||
const IS_EXTERN = 1 << 6;
|
||||
const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TypeAliasSignature {
|
||||
pub name: Name,
|
||||
pub generic_params: Arc<GenericParams>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub bounds: Box<[TypeBound]>,
|
||||
pub ty: Option<TypeRefId>,
|
||||
pub flags: TypeAliasFlags,
|
||||
}
|
||||
|
||||
impl TypeAliasSignature {
|
||||
pub fn query(
|
||||
db: &dyn DefDatabase,
|
||||
id: TypeAliasId,
|
||||
) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let mut flags = TypeAliasFlags::empty();
|
||||
let attrs = item_tree.attrs(
|
||||
db,
|
||||
loc.container.module(db).krate(),
|
||||
ModItem::from(loc.id.value).into(),
|
||||
);
|
||||
if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL);
|
||||
}
|
||||
if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
|
||||
flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
|
||||
}
|
||||
if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
|
||||
flags.insert(TypeAliasFlags::IS_EXTERN);
|
||||
}
|
||||
let source = loc.source(db);
|
||||
let (store, source_map, generic_params, bounds, ty) =
|
||||
lower_type_alias(db, loc.container.module(db), source, id);
|
||||
|
||||
(
|
||||
Arc::new(TypeAliasSignature {
|
||||
store: Arc::new(store),
|
||||
generic_params,
|
||||
flags,
|
||||
bounds,
|
||||
name: item_tree[loc.id.value].name.clone(),
|
||||
ty,
|
||||
}),
|
||||
Arc::new(source_map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct FunctionBody {
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub parameters: Box<[PatId]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SimpleBody {
|
||||
pub store: Arc<ExpressionStore>,
|
||||
}
|
||||
pub type StaticBody = SimpleBody;
|
||||
pub type ConstBody = SimpleBody;
|
||||
pub type EnumVariantBody = SimpleBody;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct VariantFieldsBody {
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub fields: Box<[Option<ExprId>]>,
|
||||
}
|
||||
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FieldData {
|
||||
pub name: Name,
|
||||
pub type_ref: TypeRefId,
|
||||
pub visibility: RawVisibility,
|
||||
pub is_unsafe: bool,
|
||||
}
|
||||
|
||||
pub type LocalFieldId = Idx<FieldData>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct VariantFields {
|
||||
fields: Arena<FieldData>,
|
||||
pub store: Arc<ExpressionStore>,
|
||||
pub shape: FieldsShape,
|
||||
}
|
||||
impl VariantFields {
|
||||
#[inline]
|
||||
pub(crate) fn query(
|
||||
db: &dyn DefDatabase,
|
||||
id: VariantId,
|
||||
) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
|
||||
let (shape, (fields, store, source_map)) = match id {
|
||||
VariantId::EnumVariantId(id) => {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let parent = loc.parent.lookup(db);
|
||||
let variant = &item_tree[loc.id.value];
|
||||
(
|
||||
variant.shape,
|
||||
lower_fields(
|
||||
db,
|
||||
parent.container,
|
||||
&item_tree,
|
||||
FieldParent::EnumVariant(loc.id.value),
|
||||
loc.source(db).map(|src| {
|
||||
variant.fields.iter().zip(
|
||||
src.field_list()
|
||||
.map(|it| {
|
||||
match it {
|
||||
ast::FieldList::RecordFieldList(record_field_list) => {
|
||||
Either::Left(record_field_list.fields().map(|it| {
|
||||
(SyntaxNodePtr::new(it.syntax()), it.ty())
|
||||
}))
|
||||
}
|
||||
ast::FieldList::TupleFieldList(field_list) => {
|
||||
Either::Right(field_list.fields().map(|it| {
|
||||
(SyntaxNodePtr::new(it.syntax()), it.ty())
|
||||
}))
|
||||
}
|
||||
}
|
||||
.into_iter()
|
||||
})
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
}),
|
||||
Some(item_tree[parent.id.value].visibility),
|
||||
),
|
||||
)
|
||||
}
|
||||
VariantId::StructId(id) => {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let strukt = &item_tree[loc.id.value];
|
||||
(
|
||||
strukt.shape,
|
||||
lower_fields(
|
||||
db,
|
||||
loc.container,
|
||||
&item_tree,
|
||||
FieldParent::Struct(loc.id.value),
|
||||
loc.source(db).map(|src| {
|
||||
strukt.fields.iter().zip(
|
||||
src.field_list()
|
||||
.map(|it| {
|
||||
match it {
|
||||
ast::FieldList::RecordFieldList(record_field_list) => {
|
||||
Either::Left(record_field_list.fields().map(|it| {
|
||||
(SyntaxNodePtr::new(it.syntax()), it.ty())
|
||||
}))
|
||||
}
|
||||
ast::FieldList::TupleFieldList(field_list) => {
|
||||
Either::Right(field_list.fields().map(|it| {
|
||||
(SyntaxNodePtr::new(it.syntax()), it.ty())
|
||||
}))
|
||||
}
|
||||
}
|
||||
.into_iter()
|
||||
})
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
}),
|
||||
None,
|
||||
),
|
||||
)
|
||||
}
|
||||
VariantId::UnionId(id) => {
|
||||
let loc = id.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let union = &item_tree[loc.id.value];
|
||||
(
|
||||
FieldsShape::Record,
|
||||
lower_fields(
|
||||
db,
|
||||
loc.container,
|
||||
&item_tree,
|
||||
FieldParent::Union(loc.id.value),
|
||||
loc.source(db).map(|src| {
|
||||
union.fields.iter().zip(
|
||||
src.record_field_list()
|
||||
.map(|it| {
|
||||
it.fields()
|
||||
.map(|it| (SyntaxNodePtr::new(it.syntax()), it.ty()))
|
||||
})
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
}),
|
||||
None,
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
(Arc::new(VariantFields { fields, store: Arc::new(store), shape }), Arc::new(source_map))
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.fields.len()
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> &Arena<FieldData> {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
|
||||
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_fields<'a>(
|
||||
db: &dyn DefDatabase,
|
||||
module: ModuleId,
|
||||
item_tree: &ItemTree,
|
||||
parent: FieldParent,
|
||||
fields: InFile<impl Iterator<Item = (&'a Field, (SyntaxNodePtr, Option<ast::Type>))>>,
|
||||
override_visibility: Option<RawVisibilityId>,
|
||||
) -> (Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap) {
|
||||
let mut arena = Arena::new();
|
||||
let cfg_options = module.krate.cfg_options(db);
|
||||
let mut col = ExprCollector::new(db, module, fields.file_id);
|
||||
for (idx, (field, (ptr, ty))) in fields.value.enumerate() {
|
||||
let attr_owner = AttrOwner::make_field_indexed(parent, idx);
|
||||
let attrs = item_tree.attrs(db, module.krate, attr_owner);
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
arena.alloc(FieldData {
|
||||
name: field.name.clone(),
|
||||
type_ref: col
|
||||
.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator),
|
||||
visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
|
||||
is_unsafe: field.is_unsafe,
|
||||
});
|
||||
} else {
|
||||
col.source_map.diagnostics.push(
|
||||
crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
|
||||
node: InFile::new(fields.file_id, ptr),
|
||||
cfg: attrs.cfg().unwrap(),
|
||||
opts: cfg_options.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
let store = col.store.finish();
|
||||
(arena, store, col.source_map)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct InactiveEnumVariantCode {
|
||||
pub cfg: CfgExpr,
|
||||
pub opts: CfgOptions,
|
||||
pub ast_id: span::FileAstId<ast::Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumVariants {
|
||||
pub variants: Box<[(EnumVariantId, Name)]>,
|
||||
}
|
||||
|
||||
impl EnumVariants {
|
||||
pub(crate) fn enum_variants_query(
|
||||
db: &dyn DefDatabase,
|
||||
e: EnumId,
|
||||
) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>) {
|
||||
let loc = e.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
|
||||
let mut diagnostics = ThinVec::new();
|
||||
let cfg_options = loc.container.krate.cfg_options(db);
|
||||
let mut index = 0;
|
||||
let variants = FileItemTreeId::range_iter(item_tree[loc.id.value].variants.clone())
|
||||
.filter_map(|variant| {
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, variant.into());
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
let enum_variant = EnumVariantLoc {
|
||||
id: ItemTreeId::new(loc.id.tree_id(), variant),
|
||||
parent: e,
|
||||
index,
|
||||
}
|
||||
.intern(db);
|
||||
index += 1;
|
||||
Some((enum_variant, item_tree[variant].name.clone()))
|
||||
} else {
|
||||
diagnostics.push(InactiveEnumVariantCode {
|
||||
ast_id: item_tree[variant].ast_id,
|
||||
cfg: attrs.cfg().unwrap(),
|
||||
opts: cfg_options.clone(),
|
||||
});
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
(
|
||||
Arc::new(EnumVariants { variants }),
|
||||
diagnostics.is_empty().not().then(|| Arc::new(diagnostics)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
|
||||
self.variants.iter().find_map(|(v, n)| if n == name { Some(*v) } else { None })
|
||||
}
|
||||
|
||||
// [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
|
||||
pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
|
||||
self.variants.iter().all(|&(v, _)| {
|
||||
// The condition check order is slightly modified from rustc
|
||||
// to improve performance by early returning with relatively fast checks
|
||||
let variant = &db.variant_fields(v.into());
|
||||
if !variant.fields().is_empty() {
|
||||
return false;
|
||||
}
|
||||
// The outer if condition is whether this variant has const ctor or not
|
||||
if !matches!(variant.shape, FieldsShape::Unit) {
|
||||
let body = db.body(v.into());
|
||||
// A variant with explicit discriminant
|
||||
if body.exprs[body.body_expr] != crate::hir::Expr::Missing {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
use either::Either;
|
||||
use hir_expand::InFile;
|
||||
use la_arena::ArenaMap;
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
use syntax::{AstNode, AstPtr, ast};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, FieldParent, ItemTreeNode},
|
||||
GenericDefId, ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
|
||||
UseId, VariantId,
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, FieldParent, ItemTreeNode},
|
||||
};
|
||||
|
||||
pub trait HasSource {
|
||||
|
|
@ -131,7 +131,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
|||
item_tree = lookup.id.item_tree(db);
|
||||
(
|
||||
lookup.source(db).map(|it| it.kind()),
|
||||
FieldParent::Variant(lookup.id.value),
|
||||
FieldParent::EnumVariant(lookup.id.value),
|
||||
lookup.parent.lookup(db).container,
|
||||
)
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
|||
let mut map = ArenaMap::new();
|
||||
match &src.value {
|
||||
ast::StructKind::Tuple(fl) => {
|
||||
let cfg_options = &db.crate_graph()[container.krate].cfg_options;
|
||||
let cfg_options = container.krate.cfg_options(db);
|
||||
let mut idx = 0;
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
let attrs = item_tree.attrs(
|
||||
|
|
@ -177,7 +177,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
|||
}
|
||||
}
|
||||
ast::StructKind::Record(fl) => {
|
||||
let cfg_options = &db.crate_graph()[container.krate].cfg_options;
|
||||
let cfg_options = container.krate.cfg_options(db);
|
||||
let mut idx = 0;
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
let attrs = item_tree.attrs(
|
||||
|
|
|
|||
|
|
@ -3,58 +3,53 @@
|
|||
use std::{fmt, panic, sync::Mutex};
|
||||
|
||||
use base_db::{
|
||||
ra_salsa::{self, Durability},
|
||||
AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
|
||||
Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb,
|
||||
SourceDatabase, SourceRoot, SourceRootId, SourceRootInput,
|
||||
};
|
||||
use hir_expand::{db::ExpandDatabase, files::FilePosition, InFile};
|
||||
use span::{EditionedFileId, FileId};
|
||||
use syntax::{algo, ast, AstNode};
|
||||
use hir_expand::{InFile, files::FilePosition};
|
||||
use salsa::{AsDynDatabase, Durability};
|
||||
use span::FileId;
|
||||
use syntax::{AstNode, algo, ast};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
||||
db::DefDatabase,
|
||||
nameres::{DefMap, ModuleSource},
|
||||
src::HasSource,
|
||||
LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
||||
};
|
||||
|
||||
#[ra_salsa::database(
|
||||
base_db::SourceRootDatabaseStorage,
|
||||
base_db::SourceDatabaseStorage,
|
||||
hir_expand::db::ExpandDatabaseStorage,
|
||||
crate::db::InternDatabaseStorage,
|
||||
crate::db::DefDatabaseStorage
|
||||
)]
|
||||
#[salsa::db]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct TestDB {
|
||||
storage: ra_salsa::Storage<TestDB>,
|
||||
events: Mutex<Option<Vec<ra_salsa::Event>>>,
|
||||
storage: salsa::Storage<Self>,
|
||||
files: Arc<base_db::Files>,
|
||||
crates_map: Arc<CratesMap>,
|
||||
events: Arc<Mutex<Option<Vec<salsa::Event>>>>,
|
||||
}
|
||||
|
||||
impl Default for TestDB {
|
||||
fn default() -> Self {
|
||||
let mut this = Self { storage: Default::default(), events: Default::default() };
|
||||
this.setup_syntax_context_root();
|
||||
let mut this = Self {
|
||||
storage: Default::default(),
|
||||
events: Default::default(),
|
||||
files: Default::default(),
|
||||
crates_map: Default::default(),
|
||||
};
|
||||
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([])));
|
||||
CrateGraphBuilder::default().set_in_db(&mut this);
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl Upcast<dyn ExpandDatabase> for TestDB {
|
||||
fn upcast(&self) -> &(dyn ExpandDatabase + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Upcast<dyn DefDatabase> for TestDB {
|
||||
fn upcast(&self) -> &(dyn DefDatabase + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ra_salsa::Database for TestDB {
|
||||
fn salsa_event(&self, event: ra_salsa::Event) {
|
||||
#[salsa::db]
|
||||
impl salsa::Database for TestDB {
|
||||
fn salsa_event(&self, event: &dyn std::ops::Fn() -> salsa::Event) {
|
||||
let mut events = self.events.lock().unwrap();
|
||||
if let Some(events) = &mut *events {
|
||||
let event = event();
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -68,34 +63,79 @@ impl fmt::Debug for TestDB {
|
|||
|
||||
impl panic::RefUnwindSafe for TestDB {}
|
||||
|
||||
impl FileLoader for TestDB {
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
FileLoaderDelegate(self).resolve_path(path)
|
||||
#[salsa::db]
|
||||
impl SourceDatabase for TestDB {
|
||||
fn file_text(&self, file_id: base_db::FileId) -> FileText {
|
||||
self.files.file_text(file_id)
|
||||
}
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> {
|
||||
FileLoaderDelegate(self).relevant_crates(file_id)
|
||||
|
||||
fn set_file_text(&mut self, file_id: base_db::FileId, text: &str) {
|
||||
let files = Arc::clone(&self.files);
|
||||
files.set_file_text(self, file_id, text);
|
||||
}
|
||||
|
||||
fn set_file_text_with_durability(
|
||||
&mut self,
|
||||
file_id: base_db::FileId,
|
||||
text: &str,
|
||||
durability: Durability,
|
||||
) {
|
||||
let files = Arc::clone(&self.files);
|
||||
files.set_file_text_with_durability(self, file_id, text, durability);
|
||||
}
|
||||
|
||||
/// Source root of the file.
|
||||
fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
|
||||
self.files.source_root(source_root_id)
|
||||
}
|
||||
|
||||
fn set_source_root_with_durability(
|
||||
&mut self,
|
||||
source_root_id: SourceRootId,
|
||||
source_root: Arc<SourceRoot>,
|
||||
durability: Durability,
|
||||
) {
|
||||
let files = Arc::clone(&self.files);
|
||||
files.set_source_root_with_durability(self, source_root_id, source_root, durability);
|
||||
}
|
||||
|
||||
fn file_source_root(&self, id: base_db::FileId) -> FileSourceRootInput {
|
||||
self.files.file_source_root(id)
|
||||
}
|
||||
|
||||
fn set_file_source_root_with_durability(
|
||||
&mut self,
|
||||
id: base_db::FileId,
|
||||
source_root_id: SourceRootId,
|
||||
durability: Durability,
|
||||
) {
|
||||
let files = Arc::clone(&self.files);
|
||||
files.set_file_source_root_with_durability(self, id, source_root_id, durability);
|
||||
}
|
||||
|
||||
fn crates_map(&self) -> Arc<CratesMap> {
|
||||
self.crates_map.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestDB {
|
||||
pub(crate) fn fetch_test_crate(&self) -> CrateId {
|
||||
let crate_graph = self.crate_graph();
|
||||
let it = crate_graph
|
||||
pub(crate) fn fetch_test_crate(&self) -> Crate {
|
||||
let all_crates = self.all_crates();
|
||||
all_crates
|
||||
.iter()
|
||||
.find(|&idx| {
|
||||
crate_graph[idx].display_name.as_ref().map(|it| it.canonical_name().as_str())
|
||||
.copied()
|
||||
.find(|&krate| {
|
||||
krate.extra_data(self).display_name.as_ref().map(|it| it.canonical_name().as_str())
|
||||
== Some("ra_test_fixture")
|
||||
})
|
||||
.or_else(|| crate_graph.iter().next())
|
||||
.unwrap();
|
||||
it
|
||||
.unwrap_or(*all_crates.last().unwrap())
|
||||
}
|
||||
|
||||
pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
|
||||
for &krate in self.relevant_crates(file_id).iter() {
|
||||
let crate_def_map = self.crate_def_map(krate);
|
||||
for (local_id, data) in crate_def_map.modules() {
|
||||
if data.origin.file_id().map(EditionedFileId::file_id) == Some(file_id) {
|
||||
if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) {
|
||||
return crate_def_map.module_id(local_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +144,7 @@ impl TestDB {
|
|||
}
|
||||
|
||||
pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId {
|
||||
let file_module = self.module_for_file(position.file_id.file_id());
|
||||
let file_module = self.module_for_file(position.file_id.file_id(self));
|
||||
let mut def_map = file_module.def_map(self);
|
||||
let module = self.mod_at_position(&def_map, position);
|
||||
|
||||
|
|
@ -203,12 +243,12 @@ impl TestDB {
|
|||
|
||||
// Find the innermost block expression that has a `DefMap`.
|
||||
let def_with_body = fn_def?.into();
|
||||
let (_, source_map) = self.body_with_source_map(def_with_body);
|
||||
let source_map = self.body_with_source_map(def_with_body).1;
|
||||
let scopes = self.expr_scopes(def_with_body);
|
||||
let root = self.parse(position.file_id);
|
||||
|
||||
let scope_iter = algo::ancestors_at_offset(&root.syntax_node(), position.offset)
|
||||
.filter_map(|node| {
|
||||
let root_syntax_node = self.parse(position.file_id).syntax_node();
|
||||
let scope_iter =
|
||||
algo::ancestors_at_offset(&root_syntax_node, position.offset).filter_map(|node| {
|
||||
let block = ast::BlockExpr::cast(node)?;
|
||||
let expr = ast::Expr::from(block);
|
||||
let expr_id = source_map
|
||||
|
|
@ -231,7 +271,7 @@ impl TestDB {
|
|||
None
|
||||
}
|
||||
|
||||
pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<ra_salsa::Event> {
|
||||
pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
|
||||
*self.events.lock().unwrap() = Some(Vec::new());
|
||||
f();
|
||||
self.events.lock().unwrap().take().unwrap()
|
||||
|
|
@ -244,8 +284,11 @@ impl TestDB {
|
|||
.filter_map(|e| match e.kind {
|
||||
// This is pretty horrible, but `Debug` is the only way to inspect
|
||||
// QueryDescriptor at the moment.
|
||||
ra_salsa::EventKind::WillExecute { database_key } => {
|
||||
Some(format!("{:?}", database_key.debug(self)))
|
||||
salsa::EventKind::WillExecute { database_key } => {
|
||||
let ingredient = self
|
||||
.as_dyn_database()
|
||||
.ingredient_debug_name(database_key.ingredient_index());
|
||||
Some(ingredient.to_string())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,80 +2,19 @@
|
|||
|
||||
use std::iter;
|
||||
|
||||
use intern::Interned;
|
||||
use hir_expand::Lookup;
|
||||
use la_arena::ArenaMap;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
ConstId, FunctionId, HasModule, ItemContainerId, ItemLoc, ItemTreeLoc, LocalFieldId,
|
||||
LocalModuleId, ModuleId, TraitId, TypeAliasId, VariantId,
|
||||
db::DefDatabase,
|
||||
nameres::DefMap,
|
||||
path::{ModPath, PathKind},
|
||||
resolver::HasResolver,
|
||||
ConstId, FunctionId, HasModule, LocalFieldId, LocalModuleId, ModuleId, VariantId,
|
||||
resolver::{HasResolver, Resolver},
|
||||
};
|
||||
|
||||
/// Visibility of an item, not yet resolved.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum RawVisibility {
|
||||
/// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
|
||||
/// equivalent to `pub(self)`.
|
||||
Module(Interned<ModPath>, VisibilityExplicitness),
|
||||
/// `pub`.
|
||||
Public,
|
||||
}
|
||||
|
||||
impl RawVisibility {
|
||||
pub(crate) fn private() -> RawVisibility {
|
||||
RawVisibility::Module(
|
||||
Interned::new(ModPath::from_kind(PathKind::SELF)),
|
||||
VisibilityExplicitness::Implicit,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast(
|
||||
db: &dyn DefDatabase,
|
||||
node: Option<ast::Visibility>,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContextId,
|
||||
) -> RawVisibility {
|
||||
let node = match node {
|
||||
None => return RawVisibility::private(),
|
||||
Some(node) => node,
|
||||
};
|
||||
Self::from_ast_with_span_map(db, node, span_for_range)
|
||||
}
|
||||
|
||||
fn from_ast_with_span_map(
|
||||
db: &dyn DefDatabase,
|
||||
node: ast::Visibility,
|
||||
span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContextId,
|
||||
) -> RawVisibility {
|
||||
let path = match node.kind() {
|
||||
ast::VisibilityKind::In(path) => {
|
||||
let path = ModPath::from_src(db.upcast(), path, span_for_range);
|
||||
match path {
|
||||
None => return RawVisibility::private(),
|
||||
Some(path) => path,
|
||||
}
|
||||
}
|
||||
ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate),
|
||||
ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
|
||||
ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::SELF),
|
||||
ast::VisibilityKind::Pub => return RawVisibility::Public,
|
||||
};
|
||||
RawVisibility::Module(Interned::new(path), VisibilityExplicitness::Explicit)
|
||||
}
|
||||
|
||||
pub fn resolve(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
resolver: &crate::resolver::Resolver,
|
||||
) -> Visibility {
|
||||
// we fall back to public visibility (i.e. fail open) if the path can't be resolved
|
||||
resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
|
||||
}
|
||||
}
|
||||
pub use crate::item_tree::{RawVisibility, VisibilityExplicitness};
|
||||
|
||||
/// Visibility of an item, with the path resolved.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -87,6 +26,15 @@ pub enum Visibility {
|
|||
}
|
||||
|
||||
impl Visibility {
|
||||
pub fn resolve(
|
||||
db: &dyn DefDatabase,
|
||||
resolver: &crate::resolver::Resolver,
|
||||
raw_vis: &RawVisibility,
|
||||
) -> Self {
|
||||
// we fall back to public visibility (i.e. fail open) if the path can't be resolved
|
||||
resolver.resolve_visibility(db, raw_vis).unwrap_or(Visibility::Public)
|
||||
}
|
||||
|
||||
pub(crate) fn is_visible_from_other_crate(self) -> bool {
|
||||
matches!(self, Visibility::Public)
|
||||
}
|
||||
|
|
@ -254,30 +202,20 @@ impl Visibility {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether the item was imported through an explicit `pub(crate) use` or just a `use` without
|
||||
/// visibility.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum VisibilityExplicitness {
|
||||
Explicit,
|
||||
Implicit,
|
||||
}
|
||||
|
||||
impl VisibilityExplicitness {
|
||||
pub fn is_explicit(&self) -> bool {
|
||||
matches!(self, Self::Explicit)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve visibility of all specific fields of a struct or union variant.
|
||||
pub(crate) fn field_visibilities_query(
|
||||
db: &dyn DefDatabase,
|
||||
variant_id: VariantId,
|
||||
) -> Arc<ArenaMap<LocalFieldId, Visibility>> {
|
||||
let var_data = variant_id.variant_data(db);
|
||||
let variant_fields = db.variant_fields(variant_id);
|
||||
let fields = variant_fields.fields();
|
||||
if fields.is_empty() {
|
||||
return Arc::default();
|
||||
}
|
||||
let resolver = variant_id.module(db).resolver(db);
|
||||
let mut res = ArenaMap::default();
|
||||
for (field_id, field_data) in var_data.fields().iter() {
|
||||
res.insert(field_id, field_data.visibility.resolve(db, &resolver));
|
||||
for (field_id, field_data) in fields.iter() {
|
||||
res.insert(field_id, Visibility::resolve(db, &resolver, &field_data.visibility));
|
||||
}
|
||||
Arc::new(res)
|
||||
}
|
||||
|
|
@ -285,11 +223,43 @@ pub(crate) fn field_visibilities_query(
|
|||
/// Resolve visibility of a function.
|
||||
pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility {
|
||||
let resolver = def.resolver(db);
|
||||
db.function_data(def).visibility.resolve(db, &resolver)
|
||||
let loc = def.lookup(db);
|
||||
let tree = loc.item_tree_id().item_tree(db);
|
||||
if let ItemContainerId::TraitId(trait_id) = loc.container {
|
||||
trait_vis(db, &resolver, trait_id)
|
||||
} else {
|
||||
Visibility::resolve(db, &resolver, &tree[tree[loc.id.value].visibility])
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve visibility of a const.
|
||||
pub(crate) fn const_visibility_query(db: &dyn DefDatabase, def: ConstId) -> Visibility {
|
||||
let resolver = def.resolver(db);
|
||||
db.const_data(def).visibility.resolve(db, &resolver)
|
||||
let loc = def.lookup(db);
|
||||
let tree = loc.item_tree_id().item_tree(db);
|
||||
if let ItemContainerId::TraitId(trait_id) = loc.container {
|
||||
trait_vis(db, &resolver, trait_id)
|
||||
} else {
|
||||
Visibility::resolve(db, &resolver, &tree[tree[loc.id.value].visibility])
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve visibility of a type alias.
|
||||
pub(crate) fn type_alias_visibility_query(db: &dyn DefDatabase, def: TypeAliasId) -> Visibility {
|
||||
let resolver = def.resolver(db);
|
||||
let loc = def.lookup(db);
|
||||
let tree = loc.item_tree_id().item_tree(db);
|
||||
if let ItemContainerId::TraitId(trait_id) = loc.container {
|
||||
trait_vis(db, &resolver, trait_id)
|
||||
} else {
|
||||
Visibility::resolve(db, &resolver, &tree[tree[loc.id.value].visibility])
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn trait_vis(db: &dyn DefDatabase, resolver: &Resolver, trait_id: TraitId) -> Visibility {
|
||||
let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let tr_def = &item_tree[tree_id.value];
|
||||
Visibility::resolve(db, resolver, &item_tree[tr_def.visibility])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ rust-version.workspace = true
|
|||
[lib]
|
||||
|
||||
[dependencies]
|
||||
cov-mark = "2.0.0-pre.1"
|
||||
cov-mark = "2.0.0"
|
||||
tracing.workspace = true
|
||||
either.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
la-arena.workspace = true
|
||||
itertools.workspace = true
|
||||
hashbrown.workspace = true
|
||||
smallvec.workspace = true
|
||||
triomphe.workspace = true
|
||||
query-group.workspace = true
|
||||
salsa.workspace = true
|
||||
|
||||
# local deps
|
||||
stdx.workspace = true
|
||||
|
|
@ -35,7 +35,7 @@ parser.workspace = true
|
|||
syntax-bridge.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
expect-test = "1.5.1"
|
||||
|
||||
[features]
|
||||
in-rust-tree = ["syntax/in-rust-tree"]
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
use std::{borrow::Cow, fmt, ops};
|
||||
|
||||
use base_db::CrateId;
|
||||
use base_db::Crate;
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use intern::{sym, Interned, Symbol};
|
||||
use intern::{Interned, Symbol, sym};
|
||||
|
||||
use mbe::{DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::{Span, SyntaxContextId};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use span::{Span, SyntaxContext};
|
||||
use syntax::unescape;
|
||||
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode};
|
||||
use syntax_bridge::{desugar_doc_comment_text, syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use syntax::{AstNode, AstToken, SyntaxNode, ast, match_ast};
|
||||
use syntax_bridge::{DocCommentDesugarMode, desugar_doc_comment_text, syntax_node_to_token_tree};
|
||||
use triomphe::ThinArc;
|
||||
|
||||
use crate::name::Name;
|
||||
use crate::{
|
||||
InFile,
|
||||
db::ExpandDatabase,
|
||||
mod_path::ModPath,
|
||||
span_map::SpanMapRef,
|
||||
tt::{self, token_to_literal, TopSubtree},
|
||||
InFile,
|
||||
tt::{self, TopSubtree, token_to_literal},
|
||||
};
|
||||
|
||||
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
||||
|
|
@ -66,10 +66,7 @@ impl RawAttrs {
|
|||
kind,
|
||||
suffix: None,
|
||||
}))),
|
||||
path: Interned::new(ModPath::from(Name::new_symbol(
|
||||
sym::doc.clone(),
|
||||
span.ctx,
|
||||
))),
|
||||
path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))),
|
||||
ctxt: span.ctx,
|
||||
}
|
||||
}),
|
||||
|
|
@ -119,50 +116,48 @@ impl RawAttrs {
|
|||
|
||||
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
|
||||
// FIXME: This should return a different type, signaling it was filtered?
|
||||
pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs {
|
||||
let has_cfg_attrs = self
|
||||
.iter()
|
||||
.any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone()));
|
||||
pub fn filter(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
|
||||
let has_cfg_attrs =
|
||||
self.iter().any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr));
|
||||
if !has_cfg_attrs {
|
||||
return self;
|
||||
}
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let new_attrs =
|
||||
self.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone());
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
let cfg_options = krate.cfg_options(db);
|
||||
let new_attrs = self
|
||||
.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr = attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
|
||||
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
|
||||
Some(it) => it,
|
||||
None => return smallvec![attr.clone()],
|
||||
};
|
||||
let index = attr.id;
|
||||
let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
|
||||
|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
|
||||
);
|
||||
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
|
||||
Some(it) => it,
|
||||
None => return smallvec![attr.clone()],
|
||||
};
|
||||
let index = attr.id;
|
||||
let attrs = parts
|
||||
.enumerate()
|
||||
.take(1 << AttrId::CFG_ATTR_BITS)
|
||||
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let entries = if new_attrs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -211,7 +206,7 @@ pub struct Attr {
|
|||
pub id: AttrId,
|
||||
pub path: Interned<ModPath>,
|
||||
pub input: Option<Box<AttrInput>>,
|
||||
pub ctxt: SyntaxContextId,
|
||||
pub ctxt: SyntaxContext,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -306,13 +301,12 @@ impl Attr {
|
|||
Some(Box::new(AttrInput::TokenTree(tt::TopSubtree::from_subtree(tree))))
|
||||
}
|
||||
(Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))), _) => {
|
||||
let input = match input.flat_tokens().get(1) {
|
||||
match input.flat_tokens().get(1) {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
|
||||
Some(Box::new(AttrInput::Literal(lit.clone())))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
input
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
|
@ -403,7 +397,7 @@ impl Attr {
|
|||
}
|
||||
|
||||
pub fn cfg(&self) -> Option<CfgExpr> {
|
||||
if *self.path.as_ident()? == sym::cfg.clone() {
|
||||
if *self.path.as_ident()? == sym::cfg {
|
||||
self.token_tree_value().map(CfgExpr::parse)
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ mod derive_macro;
|
|||
mod fn_macro;
|
||||
|
||||
pub use self::{
|
||||
attr_macro::{find_builtin_attr, pseudo_derive_attr_expansion, BuiltinAttrExpander},
|
||||
derive_macro::{find_builtin_derive, BuiltinDeriveExpander},
|
||||
attr_macro::{BuiltinAttrExpander, find_builtin_attr, pseudo_derive_attr_expansion},
|
||||
derive_macro::{BuiltinDeriveExpander, find_builtin_derive},
|
||||
fn_macro::{
|
||||
find_builtin_macro, include_input_to_file_id, BuiltinFnLikeExpander, EagerExpander,
|
||||
BuiltinFnLikeExpander, EagerExpander, find_builtin_macro, include_input_to_file_id,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
//! Builtin attributes.
|
||||
use intern::sym;
|
||||
use span::{MacroCallId, Span};
|
||||
use span::Span;
|
||||
|
||||
use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind};
|
||||
use crate::{ExpandResult, MacroCallId, MacroCallKind, db::ExpandDatabase, name, tt};
|
||||
|
||||
use super::quote;
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ fn derive_expand(
|
|||
return ExpandResult::ok(tt::TopSubtree::empty(tt::DelimSpan {
|
||||
open: span,
|
||||
close: span,
|
||||
}))
|
||||
}));
|
||||
}
|
||||
};
|
||||
pseudo_derive_attr_expansion(tt, derives, span)
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
//! Builtin derives.
|
||||
|
||||
use intern::sym;
|
||||
use itertools::{izip, Itertools};
|
||||
use itertools::{Itertools, izip};
|
||||
use parser::SyntaxKind;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{Edition, MacroCallId, Span, SyntaxContextId};
|
||||
use span::{Edition, Span, SyntaxContext};
|
||||
use stdx::never;
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
ExpandError, ExpandResult, MacroCallId,
|
||||
builtin::quote::{dollar_crate, quote},
|
||||
db::ExpandDatabase,
|
||||
hygiene::span_with_def_site_ctxt,
|
||||
name::{self, AsName, Name},
|
||||
span_map::ExpansionSpanMap,
|
||||
tt, ExpandError, ExpandResult,
|
||||
tt,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, FieldList, HasAttrs,
|
||||
HasGenericArgs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds,
|
||||
self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem,
|
||||
HasName, HasTypeBounds, edit_in_place::GenericParamsOwnerEdit, make,
|
||||
},
|
||||
ted,
|
||||
};
|
||||
|
|
@ -58,7 +59,7 @@ impl BuiltinDeriveExpander {
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
|
||||
let span = span_with_def_site_ctxt(db, span, id.into(), Edition::CURRENT);
|
||||
self.expander()(db, span, tt)
|
||||
}
|
||||
}
|
||||
|
|
@ -117,7 +118,7 @@ impl VariantShape {
|
|||
quote! {span => #it : #mapped , }
|
||||
});
|
||||
quote! {span =>
|
||||
#path { ##fields }
|
||||
#path { # #fields }
|
||||
}
|
||||
}
|
||||
&VariantShape::Tuple(n) => {
|
||||
|
|
@ -128,7 +129,7 @@ impl VariantShape {
|
|||
}
|
||||
});
|
||||
quote! {span =>
|
||||
#path ( ##fields )
|
||||
#path ( # #fields )
|
||||
}
|
||||
}
|
||||
VariantShape::Unit => path,
|
||||
|
|
@ -237,7 +238,7 @@ fn parse_adt(
|
|||
|
||||
fn parse_adt_from_syntax(
|
||||
adt: &ast::Adt,
|
||||
tm: &span::SpanMap<SyntaxContextId>,
|
||||
tm: &span::SpanMap<SyntaxContext>,
|
||||
call_site: Span,
|
||||
) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let (name, generic_param_list, where_clause, shape) = match &adt {
|
||||
|
|
@ -389,7 +390,7 @@ fn to_adt_syntax(
|
|||
db: &dyn ExpandDatabase,
|
||||
tt: &tt::TopSubtree,
|
||||
call_site: Span,
|
||||
) -> Result<(ast::Adt, span::SpanMap<SyntaxContextId>), ExpandError> {
|
||||
) -> Result<(ast::Adt, span::SpanMap<SyntaxContext>), ExpandError> {
|
||||
let (parsed, tm) = crate::db::token_tree_to_syntax_node(
|
||||
db,
|
||||
tt,
|
||||
|
|
@ -464,7 +465,7 @@ fn expand_simple_derive(
|
|||
return ExpandResult::new(
|
||||
tt::TopSubtree::empty(tt::DelimSpan { open: invoc_span, close: invoc_span }),
|
||||
e,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
ExpandResult::ok(expand_simple_derive_with_parsed(
|
||||
|
|
@ -523,7 +524,7 @@ fn expand_simple_derive_with_parsed(
|
|||
|
||||
let name = info.name;
|
||||
quote! {invoc_span =>
|
||||
impl < ##params #extra_impl_params > #trait_path for #name < ##args > where ##where_block { #trait_body }
|
||||
impl < # #params #extra_impl_params > #trait_path for #name < # #args > where # #where_block { #trait_body }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -572,7 +573,7 @@ fn clone_expand(
|
|||
quote! {span =>
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
##arms
|
||||
# #arms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -650,7 +651,7 @@ fn debug_expand(
|
|||
}
|
||||
});
|
||||
quote! {span =>
|
||||
f.debug_struct(#name) ##for_fields .finish()
|
||||
f.debug_struct(#name) # #for_fields .finish()
|
||||
}
|
||||
}
|
||||
VariantShape::Tuple(n) => {
|
||||
|
|
@ -660,7 +661,7 @@ fn debug_expand(
|
|||
}
|
||||
});
|
||||
quote! {span =>
|
||||
f.debug_tuple(#name) ##for_fields .finish()
|
||||
f.debug_tuple(#name) # #for_fields .finish()
|
||||
}
|
||||
}
|
||||
VariantShape::Unit => quote! {span =>
|
||||
|
|
@ -703,7 +704,7 @@ fn debug_expand(
|
|||
quote! {span =>
|
||||
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
|
||||
match self {
|
||||
##arms
|
||||
# #arms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -736,7 +737,7 @@ fn hash_expand(
|
|||
let it =
|
||||
names.iter().map(|it| quote! {span => #it . hash(ra_expand_state); });
|
||||
quote! {span => {
|
||||
##it
|
||||
# #it
|
||||
} }
|
||||
};
|
||||
let fat_arrow = fat_arrow(span);
|
||||
|
|
@ -754,7 +755,7 @@ fn hash_expand(
|
|||
fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
|
||||
#check_discriminant
|
||||
match self {
|
||||
##arms
|
||||
# #arms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -803,7 +804,7 @@ fn partial_eq_expand(
|
|||
let t2 = tt::Ident::new(&format!("{}_other", first.sym), first.span);
|
||||
quote!(span =>#t1 .eq( #t2 ))
|
||||
};
|
||||
quote!(span =>#first ##rest)
|
||||
quote!(span =>#first # #rest)
|
||||
}
|
||||
};
|
||||
quote! {span => ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
|
|
@ -814,7 +815,7 @@ fn partial_eq_expand(
|
|||
quote! {span =>
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
##arms
|
||||
# #arms
|
||||
_unused #fat_arrow false
|
||||
}
|
||||
}
|
||||
|
|
@ -891,7 +892,7 @@ fn ord_expand(
|
|||
let fat_arrow = fat_arrow(span);
|
||||
let mut body = quote! {span =>
|
||||
match (self, other) {
|
||||
##arms
|
||||
# #arms
|
||||
_unused #fat_arrow #krate::cmp::Ordering::Equal
|
||||
}
|
||||
};
|
||||
|
|
@ -961,14 +962,14 @@ fn partial_ord_expand(
|
|||
right,
|
||||
quote! {span =>
|
||||
match (self, other) {
|
||||
##arms
|
||||
# #arms
|
||||
_unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
|
||||
}
|
||||
},
|
||||
span,
|
||||
);
|
||||
quote! {span =>
|
||||
fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
|
||||
fn partial_cmp(&self, other: &Self) -> #krate::option::Option<#krate::cmp::Ordering> {
|
||||
#body
|
||||
}
|
||||
}
|
||||
|
|
@ -1072,7 +1073,7 @@ fn coerce_pointee_expand(
|
|||
"exactly one generic type parameter must be marked \
|
||||
as `#[pointee]` to derive `CoercePointee` traits",
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
(Some(_), Some(_)) => {
|
||||
return ExpandResult::new(
|
||||
|
|
@ -1082,7 +1083,7 @@ fn coerce_pointee_expand(
|
|||
"only one type parameter can be marked as `#[pointee]` \
|
||||
when deriving `CoercePointee` traits",
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1120,7 +1121,9 @@ fn coerce_pointee_expand(
|
|||
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
|
||||
ExpandError::other(
|
||||
span,
|
||||
format!("`derive(CoercePointee)` requires `{pointee_param_name}` to be marked `?Sized`"),
|
||||
format!(
|
||||
"`derive(CoercePointee)` requires `{pointee_param_name}` to be marked `?Sized`"
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -1311,15 +1314,15 @@ fn coerce_pointee_expand(
|
|||
}
|
||||
})
|
||||
});
|
||||
let self_for_traits = make::path_from_segments(
|
||||
|
||||
make::path_from_segments(
|
||||
[make::generic_ty_path_segment(
|
||||
make::name_ref(&struct_name.text()),
|
||||
self_params_for_traits,
|
||||
)],
|
||||
false,
|
||||
)
|
||||
.clone_for_update();
|
||||
self_for_traits
|
||||
.clone_for_update()
|
||||
};
|
||||
|
||||
let mut span_map = span::SpanMap::empty();
|
||||
|
|
@ -1335,7 +1338,7 @@ fn coerce_pointee_expand(
|
|||
let info = match parse_adt_from_syntax(&adt, &span_map, span) {
|
||||
Ok(it) => it,
|
||||
Err(err) => {
|
||||
return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err)
|
||||
return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,24 +3,27 @@
|
|||
use base_db::AnchoredPath;
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use intern::{sym, Symbol};
|
||||
use mbe::{expect_fragment, DelimiterKind};
|
||||
use span::{Edition, EditionedFileId, Span};
|
||||
use intern::{
|
||||
Symbol,
|
||||
sym::{self},
|
||||
};
|
||||
use mbe::{DelimiterKind, expect_fragment};
|
||||
use span::{Edition, FileId, Span};
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
format_smolstr,
|
||||
unescape::{unescape_byte, unescape_char, unescape_unicode, Mode},
|
||||
unescape::{Mode, unescape_byte, unescape_char, unescape_unicode},
|
||||
};
|
||||
use syntax_bridge::syntax_node_to_token_tree;
|
||||
|
||||
use crate::{
|
||||
builtin::quote::{dollar_crate, quote, WithDelimiter},
|
||||
EditionedFileId, ExpandError, ExpandResult, Lookup as _, MacroCallId,
|
||||
builtin::quote::{WithDelimiter, dollar_crate, quote},
|
||||
db::ExpandDatabase,
|
||||
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt},
|
||||
name,
|
||||
span_map::SpanMap,
|
||||
tt::{self, DelimSpan, TtElement, TtIter},
|
||||
ExpandError, ExpandResult, HirFileIdExt, Lookup as _, MacroCallId,
|
||||
};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
|
|
@ -69,7 +72,7 @@ impl BuiltinFnLikeExpander {
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
|
||||
let span = span_with_def_site_ctxt(db, span, id.into(), Edition::CURRENT);
|
||||
self.expander()(db, id, tt, span)
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +89,7 @@ impl EagerExpander {
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
|
||||
let span = span_with_def_site_ctxt(db, span, id.into(), Edition::CURRENT);
|
||||
self.expander()(db, id, tt, span)
|
||||
}
|
||||
|
||||
|
|
@ -174,10 +177,10 @@ fn line_expand(
|
|||
ExpandResult::ok(tt::TopSubtree::invisible_from_leaves(
|
||||
span,
|
||||
[tt::Leaf::Literal(tt::Literal {
|
||||
symbol: sym::INTEGER_0.clone(),
|
||||
symbol: sym::INTEGER_0,
|
||||
span,
|
||||
kind: tt::LitKind::Integer,
|
||||
suffix: Some(sym::u32.clone()),
|
||||
suffix: Some(sym::u32),
|
||||
})],
|
||||
))
|
||||
}
|
||||
|
|
@ -221,14 +224,14 @@ fn assert_expand(
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id.into(), Edition::CURRENT);
|
||||
|
||||
let mut iter = tt.iter();
|
||||
|
||||
let cond = expect_fragment(
|
||||
&mut iter,
|
||||
parser::PrefixEntryPoint::Expr,
|
||||
db.crate_graph()[id.lookup(db).krate].edition,
|
||||
id.lookup(db).krate.data(db).edition,
|
||||
tt.top_subtree().delimiter.delim_span(),
|
||||
);
|
||||
_ = iter.expect_char(',');
|
||||
|
|
@ -237,9 +240,9 @@ fn assert_expand(
|
|||
let dollar_crate = dollar_crate(span);
|
||||
let panic_args = rest.iter();
|
||||
let mac = if use_panic_2021(db, span) {
|
||||
quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) }
|
||||
quote! {call_site_span => #dollar_crate::panic::panic_2021!(# #panic_args) }
|
||||
} else {
|
||||
quote! {call_site_span => #dollar_crate::panic!(##panic_args) }
|
||||
quote! {call_site_span => #dollar_crate::panic!(# #panic_args) }
|
||||
};
|
||||
let value = cond.value;
|
||||
let expanded = quote! {call_site_span =>{
|
||||
|
|
@ -330,7 +333,7 @@ fn cfg_expand(
|
|||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let expr = CfgExpr::parse(tt);
|
||||
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
|
||||
let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false);
|
||||
let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) };
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
|
@ -342,13 +345,9 @@ fn panic_expand(
|
|||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let dollar_crate = dollar_crate(span);
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id.into(), Edition::CURRENT);
|
||||
|
||||
let mac = if use_panic_2021(db, call_site_span) {
|
||||
sym::panic_2021.clone()
|
||||
} else {
|
||||
sym::panic_2015.clone()
|
||||
};
|
||||
let mac = if use_panic_2021(db, call_site_span) { sym::panic_2021 } else { sym::panic_2015 };
|
||||
|
||||
// Pass the original arguments
|
||||
let subtree = WithDelimiter {
|
||||
|
|
@ -373,12 +372,12 @@ fn unreachable_expand(
|
|||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let dollar_crate = dollar_crate(span);
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id.into(), Edition::CURRENT);
|
||||
|
||||
let mac = if use_panic_2021(db, call_site_span) {
|
||||
sym::unreachable_2021.clone()
|
||||
sym::unreachable_2021
|
||||
} else {
|
||||
sym::unreachable_2015.clone()
|
||||
sym::unreachable_2015
|
||||
};
|
||||
|
||||
// Pass the original arguments
|
||||
|
|
@ -401,14 +400,14 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
|
|||
// stack that does not have #[allow_internal_unstable(edition_panic)].
|
||||
// (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.)
|
||||
loop {
|
||||
let Some(expn) = db.lookup_intern_syntax_context(span.ctx).outer_expn else {
|
||||
let Some(expn) = span.ctx.outer_expn(db) else {
|
||||
break false;
|
||||
};
|
||||
let expn = db.lookup_intern_macro_call(expn);
|
||||
let expn = db.lookup_intern_macro_call(expn.into());
|
||||
// FIXME: Record allow_internal_unstable in the macro def (not been done yet because it
|
||||
// would consume quite a bit extra memory for all call locs...)
|
||||
// if let Some(features) = expn.def.allow_internal_unstable {
|
||||
// if features.iter().any(|&f| f == sym::edition_panic.clone()) {
|
||||
// if features.iter().any(|&f| f == sym::edition_panic) {
|
||||
// span = expn.call_site;
|
||||
// continue;
|
||||
// }
|
||||
|
|
@ -424,12 +423,15 @@ fn compile_error_expand(
|
|||
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_str(text).as_str())),
|
||||
[
|
||||
_,
|
||||
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_str(text).as_str())),
|
||||
_ => ExpandError::other(span, "`compile_error!` argument must be a string"),
|
||||
};
|
||||
|
||||
|
|
@ -656,17 +658,17 @@ fn relative_file(
|
|||
allow_recursion: bool,
|
||||
err_span: Span,
|
||||
) -> Result<EditionedFileId, ExpandError> {
|
||||
let lookup = call_id.lookup(db);
|
||||
let call_site = lookup.kind.file_id().original_file_respecting_includes(db).file_id();
|
||||
let lookup = db.lookup_intern_macro_call(call_id);
|
||||
let call_site = lookup.kind.file_id().original_file_respecting_includes(db).file_id(db);
|
||||
let path = AnchoredPath { anchor: call_site, path: path_str };
|
||||
let res = db
|
||||
let res: FileId = db
|
||||
.resolve_path(path)
|
||||
.ok_or_else(|| ExpandError::other(err_span, format!("failed to load file `{path_str}`")))?;
|
||||
// Prevent include itself
|
||||
if res == call_site && !allow_recursion {
|
||||
Err(ExpandError::other(err_span, format!("recursive inclusion of `{path_str}`")))
|
||||
} else {
|
||||
Ok(EditionedFileId::new(res, db.crate_graph()[lookup.krate].edition))
|
||||
Ok(EditionedFileId::new(db, res, lookup.krate.data(db).edition))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -725,19 +727,19 @@ fn include_expand(
|
|||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let file_id = match include_input_to_file_id(db, arg_id, tt) {
|
||||
Ok(it) => it,
|
||||
let editioned_file_id = match include_input_to_file_id(db, arg_id, tt) {
|
||||
Ok(editioned_file_id) => editioned_file_id,
|
||||
Err(e) => {
|
||||
return ExpandResult::new(
|
||||
tt::TopSubtree::empty(DelimSpan { open: span, close: span }),
|
||||
e,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
let span_map = db.real_span_map(file_id);
|
||||
let span_map = db.real_span_map(editioned_file_id);
|
||||
// FIXME: Parse errors
|
||||
ExpandResult::ok(syntax_node_to_token_tree(
|
||||
&db.parse(file_id).syntax_node(),
|
||||
&db.parse(editioned_file_id).syntax_node(),
|
||||
SpanMap::RealSpanMap(span_map),
|
||||
span,
|
||||
syntax_bridge::DocCommentDesugarMode::ProcMacro,
|
||||
|
|
@ -776,15 +778,15 @@ fn include_str_expand(
|
|||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
call_site: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let (path, span) = match parse_string(tt) {
|
||||
let (path, input_span) = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::new(
|
||||
tt::TopSubtree::empty(DelimSpan { open: span, close: span }),
|
||||
tt::TopSubtree::empty(DelimSpan { open: call_site, close: call_site }),
|
||||
e,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -792,22 +794,22 @@ fn include_str_expand(
|
|||
// it's unusual to `include_str!` a Rust file), but we can return an empty string.
|
||||
// Ideally, we'd be able to offer a precise expansion if the user asks for macro
|
||||
// expansion.
|
||||
let file_id = match relative_file(db, arg_id, path.as_str(), true, span) {
|
||||
let file_id = match relative_file(db, arg_id, path.as_str(), true, input_span) {
|
||||
Ok(file_id) => file_id,
|
||||
Err(_) => {
|
||||
return ExpandResult::ok(quote!(span =>""));
|
||||
return ExpandResult::ok(quote!(call_site =>""));
|
||||
}
|
||||
};
|
||||
|
||||
let text = db.file_text(file_id.file_id());
|
||||
let text = &*text;
|
||||
let text = db.file_text(file_id.file_id(db));
|
||||
let text = &*text.text(db);
|
||||
|
||||
ExpandResult::ok(quote!(span =>#text))
|
||||
ExpandResult::ok(quote!(call_site =>#text))
|
||||
}
|
||||
|
||||
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option<String> {
|
||||
let krate = db.lookup_intern_macro_call(arg_id).krate;
|
||||
db.crate_graph()[krate].env.get(key.as_str())
|
||||
krate.env(db).get(key.as_str())
|
||||
}
|
||||
|
||||
fn env_expand(
|
||||
|
|
@ -822,7 +824,7 @@ fn env_expand(
|
|||
return ExpandResult::new(
|
||||
tt::TopSubtree::empty(DelimSpan { open: span, close: span }),
|
||||
e,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -860,7 +862,7 @@ fn option_env_expand(
|
|||
return ExpandResult::new(
|
||||
tt::TopSubtree::empty(DelimSpan { open: call_site, close: call_site }),
|
||||
e,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
let dollar_crate = dollar_crate(call_site);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! A simplified version of quote-crate like quasi quote macro
|
||||
#![allow(clippy::crate_in_macro_def)]
|
||||
|
||||
use intern::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
use span::Span;
|
||||
use syntax::ToSmolStr;
|
||||
use tt::IdentIsRaw;
|
||||
|
|
@ -9,7 +9,7 @@ use tt::IdentIsRaw;
|
|||
use crate::{name::Name, tt::TopSubtreeBuilder};
|
||||
|
||||
pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> {
|
||||
tt::Ident { sym: sym::dollar_crate.clone(), span, is_raw: tt::IdentIsRaw::No }
|
||||
tt::Ident { sym: sym::dollar_crate, span, is_raw: tt::IdentIsRaw::No }
|
||||
}
|
||||
|
||||
// A helper macro quote macro
|
||||
|
|
@ -61,7 +61,7 @@ macro_rules! quote_impl__ {
|
|||
$crate::builtin::quote::__quote!($span $builder $($tail)*);
|
||||
};
|
||||
|
||||
($span:ident $builder:ident ## $first:ident $($tail:tt)* ) => {{
|
||||
($span:ident $builder:ident # # $first:ident $($tail:tt)* ) => {{
|
||||
::std::iter::IntoIterator::into_iter($first).for_each(|it| $crate::builtin::quote::ToTokenTree::to_tokens(it, $span, $builder));
|
||||
$crate::builtin::quote::__quote!($span $builder $($tail)*);
|
||||
}};
|
||||
|
|
@ -203,7 +203,7 @@ 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: bool => self { crate::tt::Ident{sym: if self { sym::true_.clone() } else { sym::false_.clone() }, span, is_raw: tt::IdentIsRaw::No } };
|
||||
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 };
|
||||
|
|
@ -226,7 +226,7 @@ mod tests {
|
|||
use ::tt::IdentIsRaw;
|
||||
use expect_test::expect;
|
||||
use intern::Symbol;
|
||||
use span::{Edition, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use span::{Edition, ROOT_ERASED_FILE_AST_ID, SpanAnchor, SyntaxContext};
|
||||
use syntax::{TextRange, TextSize};
|
||||
|
||||
use super::quote;
|
||||
|
|
@ -240,7 +240,7 @@ mod tests {
|
|||
),
|
||||
ast_id: ROOT_ERASED_FILE_AST_ID,
|
||||
},
|
||||
ctx: SyntaxContextId::root(Edition::CURRENT),
|
||||
ctx: SyntaxContext::root(Edition::CURRENT),
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
@ -277,8 +277,8 @@ mod tests {
|
|||
assert_eq!(quoted.to_string(), "hello");
|
||||
let t = format!("{quoted:#?}");
|
||||
expect![[r#"
|
||||
SUBTREE $$ 937550:0@0..0#2 937550:0@0..0#2
|
||||
IDENT hello 937550:0@0..0#2"#]]
|
||||
SUBTREE $$ 937550:0@0..0#ROOT2024 937550:0@0..0#ROOT2024
|
||||
IDENT hello 937550:0@0..0#ROOT2024"#]]
|
||||
.assert_eq(&t);
|
||||
}
|
||||
|
||||
|
|
@ -324,6 +324,9 @@ mod tests {
|
|||
}
|
||||
};
|
||||
|
||||
assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");
|
||||
assert_eq!(
|
||||
quoted.to_string(),
|
||||
"impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
|
||||
use std::iter::Peekable;
|
||||
|
||||
use base_db::CrateId;
|
||||
use base_db::Crate;
|
||||
use cfg::{CfgAtom, CfgExpr};
|
||||
use intern::{sym, Symbol};
|
||||
use intern::{Symbol, sym};
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList},
|
||||
AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T,
|
||||
ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList},
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
|
||||
use crate::{MacroCallLoc, MacroDefKind, db::ExpandDatabase, proc_macro::ProcMacroKind};
|
||||
|
||||
fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
|
||||
fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option<bool> {
|
||||
if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
|
||||
return None;
|
||||
}
|
||||
let cfg = parse_from_attr_token_tree(&attr.meta()?.token_tree()?)?;
|
||||
let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false);
|
||||
let enabled = krate.cfg_options(db).check(&cfg) != Some(false);
|
||||
Some(enabled)
|
||||
}
|
||||
|
||||
fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
|
||||
fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option<bool> {
|
||||
if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -32,17 +32,17 @@ fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Optio
|
|||
pub fn check_cfg_attr_value(
|
||||
db: &dyn ExpandDatabase,
|
||||
attr: &TokenTree,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
) -> Option<bool> {
|
||||
let cfg_expr = parse_from_attr_token_tree(attr)?;
|
||||
let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false);
|
||||
let enabled = krate.cfg_options(db).check(&cfg_expr) != Some(false);
|
||||
Some(enabled)
|
||||
}
|
||||
|
||||
fn process_has_attrs_with_possible_comma<I: HasAttrs>(
|
||||
db: &dyn ExpandDatabase,
|
||||
items: impl Iterator<Item = I>,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
remove: &mut FxHashSet<SyntaxElement>,
|
||||
) -> Option<()> {
|
||||
for item in items {
|
||||
|
|
@ -144,7 +144,7 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>
|
|||
fn process_enum(
|
||||
db: &dyn ExpandDatabase,
|
||||
variants: VariantList,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
remove: &mut FxHashSet<SyntaxElement>,
|
||||
) -> Option<()> {
|
||||
'variant: for variant in variants.variants() {
|
||||
|
|
@ -344,8 +344,8 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cfg::DnfExpr;
|
||||
use expect_test::{expect, Expect};
|
||||
use syntax::{ast::Attr, AstNode, SourceFile};
|
||||
use expect_test::{Expect, expect};
|
||||
use syntax::{AstNode, SourceFile, ast::Attr};
|
||||
|
||||
use crate::cfg_process::parse_from_attr_token_tree;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,27 @@
|
|||
//! Defines a unit of change that can applied to the database to get the next
|
||||
//! state. Changes are transactional.
|
||||
use base_db::{
|
||||
ra_salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot,
|
||||
SourceRootDatabase,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
use base_db::{CrateGraphBuilder, FileChange, SourceRoot};
|
||||
use salsa::Durability;
|
||||
use span::FileId;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::ExpandDatabase, proc_macro::ProcMacros};
|
||||
use crate::{db::ExpandDatabase, proc_macro::ProcMacrosBuilder};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ChangeWithProcMacros {
|
||||
pub source_change: FileChange,
|
||||
pub proc_macros: Option<ProcMacros>,
|
||||
pub proc_macros: Option<ProcMacrosBuilder>,
|
||||
}
|
||||
|
||||
impl ChangeWithProcMacros {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn apply(self, db: &mut (impl ExpandDatabase + SourceRootDatabase)) {
|
||||
self.source_change.apply(db);
|
||||
pub fn apply(self, db: &mut impl ExpandDatabase) {
|
||||
let crates_id_map = self.source_change.apply(db);
|
||||
if let Some(proc_macros) = self.proc_macros {
|
||||
let proc_macros = proc_macros.build(
|
||||
crates_id_map
|
||||
.as_ref()
|
||||
.expect("cannot set proc macros without setting the crate graph too"),
|
||||
);
|
||||
db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,16 +30,11 @@ impl ChangeWithProcMacros {
|
|||
self.source_change.change_file(file_id, new_text)
|
||||
}
|
||||
|
||||
pub fn set_crate_graph(
|
||||
&mut self,
|
||||
graph: CrateGraph,
|
||||
ws_data: FxHashMap<CrateId, Arc<CrateWorkspaceData>>,
|
||||
) {
|
||||
pub fn set_crate_graph(&mut self, graph: CrateGraphBuilder) {
|
||||
self.source_change.set_crate_graph(graph);
|
||||
self.source_change.set_ws_data(ws_data);
|
||||
}
|
||||
|
||||
pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
|
||||
pub fn set_proc_macros(&mut self, proc_macros: ProcMacrosBuilder) {
|
||||
self.proc_macros = Some(proc_macros);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
//! Defines database & queries for macro expansion.
|
||||
|
||||
use base_db::{ra_salsa, CrateId, SourceDatabase};
|
||||
use base_db::{Crate, RootQueryDb};
|
||||
use either::Either;
|
||||
use mbe::MatchedArmIndex;
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{AstIdMap, Edition, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
|
||||
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
|
||||
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use span::{AstIdMap, Edition, Span, SyntaxContext};
|
||||
use syntax::{AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
|
||||
use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attrs::{collect_attrs, AttrId},
|
||||
AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
|
||||
EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId,
|
||||
MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||
attrs::{AttrId, collect_attrs},
|
||||
builtin::pseudo_derive_attr_expansion,
|
||||
cfg_process,
|
||||
declarative::DeclarativeMacroExpander,
|
||||
fixup::{self, SyntaxFixupUndoInfo},
|
||||
hygiene::{
|
||||
span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
|
||||
SyntaxContextExt as _,
|
||||
SyntaxContextExt as _, span_with_call_site_ctxt, span_with_def_site_ctxt,
|
||||
span_with_mixed_site_ctxt,
|
||||
},
|
||||
proc_macro::ProcMacros,
|
||||
span_map::{RealSpanMap, SpanMap, SpanMapRef},
|
||||
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
|
||||
CustomProcMacroExpander, EagerCallInfo, EagerExpander, ExpandError, ExpandResult, ExpandTo,
|
||||
ExpansionSpanMap, HirFileId, HirFileIdRepr, Lookup, MacroCallId, MacroCallKind, MacroCallLoc,
|
||||
MacroDefId, MacroDefKind, MacroFileId,
|
||||
proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros},
|
||||
span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
|
||||
tt,
|
||||
};
|
||||
/// This is just to ensure the types of smart_macro_arg and macro_arg are the same
|
||||
type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span);
|
||||
|
|
@ -52,32 +52,37 @@ pub enum TokenExpander {
|
|||
ProcMacro(CustomProcMacroExpander),
|
||||
}
|
||||
|
||||
#[ra_salsa::query_group(ExpandDatabaseStorage)]
|
||||
pub trait ExpandDatabase: SourceDatabase {
|
||||
/// The proc macros.
|
||||
#[ra_salsa::input]
|
||||
#[query_group::query_group]
|
||||
pub trait ExpandDatabase: RootQueryDb {
|
||||
/// The proc macros. Do not use this! Use `proc_macros_for_crate()` instead.
|
||||
#[salsa::input]
|
||||
fn proc_macros(&self) -> Arc<ProcMacros>;
|
||||
|
||||
/// Incrementality query to prevent queries from directly depending on `ExpandDatabase::proc_macros`.
|
||||
#[salsa::invoke(crate::proc_macro::proc_macros_for_crate)]
|
||||
fn proc_macros_for_crate(&self, krate: Crate) -> Option<Arc<CrateProcMacros>>;
|
||||
|
||||
#[salsa::invoke(ast_id_map)]
|
||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||
|
||||
/// Main public API -- parses a hir file, not caring whether it's a real
|
||||
/// file or a macro expansion.
|
||||
#[ra_salsa::transparent]
|
||||
#[salsa::transparent]
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
|
||||
|
||||
/// Implementation for the macro case.
|
||||
#[ra_salsa::lru]
|
||||
#[salsa::lru(512)]
|
||||
fn parse_macro_expansion(
|
||||
&self,
|
||||
macro_file: MacroFileId,
|
||||
macro_file: MacroCallId,
|
||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(SpanMap::new)]
|
||||
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(SpanMap::new)]
|
||||
fn span_map(&self, file_id: HirFileId) -> SpanMap;
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(crate::span_map::expansion_span_map)]
|
||||
fn expansion_span_map(&self, file_id: MacroFileId) -> Arc<ExpansionSpanMap>;
|
||||
#[ra_salsa::invoke(crate::span_map::real_span_map)]
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(crate::span_map::expansion_span_map)]
|
||||
fn expansion_span_map(&self, file_id: MacroCallId) -> Arc<ExpansionSpanMap>;
|
||||
#[salsa::invoke(crate::span_map::real_span_map)]
|
||||
fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>;
|
||||
|
||||
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
||||
|
|
@ -85,66 +90,74 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
///
|
||||
/// We encode macro definitions into ids of macro calls, this what allows us
|
||||
/// to be incremental.
|
||||
#[ra_salsa::interned]
|
||||
#[salsa::transparent]
|
||||
fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId;
|
||||
#[ra_salsa::interned]
|
||||
fn intern_syntax_context(&self, ctx: SyntaxContextData) -> SyntaxContextId;
|
||||
|
||||
#[ra_salsa::transparent]
|
||||
fn setup_syntax_context_root(&self) -> ();
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(crate::hygiene::dump_syntax_contexts)]
|
||||
fn dump_syntax_contexts(&self) -> String;
|
||||
#[salsa::transparent]
|
||||
fn lookup_intern_macro_call(&self, macro_call: MacroCallId) -> MacroCallLoc;
|
||||
|
||||
/// Lowers syntactic macro call to a token tree representation. That's a firewall
|
||||
/// query, only typing in the macro call itself changes the returned
|
||||
/// subtree.
|
||||
#[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"]
|
||||
#[salsa::invoke(macro_arg)]
|
||||
fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
|
||||
#[ra_salsa::transparent]
|
||||
|
||||
#[salsa::transparent]
|
||||
fn macro_arg_considering_derives(
|
||||
&self,
|
||||
id: MacroCallId,
|
||||
kind: &MacroCallKind,
|
||||
) -> MacroArgResult;
|
||||
|
||||
/// Fetches the expander for this macro.
|
||||
#[ra_salsa::transparent]
|
||||
#[ra_salsa::invoke(TokenExpander::macro_expander)]
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(TokenExpander::macro_expander)]
|
||||
fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
|
||||
|
||||
/// Fetches (and compiles) the expander of this decl macro.
|
||||
#[ra_salsa::invoke(DeclarativeMacroExpander::expander)]
|
||||
#[salsa::invoke(DeclarativeMacroExpander::expander)]
|
||||
fn decl_macro_expander(
|
||||
&self,
|
||||
def_crate: CrateId,
|
||||
def_crate: Crate,
|
||||
id: AstId<ast::Macro>,
|
||||
) -> Arc<DeclarativeMacroExpander>;
|
||||
|
||||
/// Special case of the previous query for procedural macros. We can't LRU
|
||||
/// proc macros, since they are not deterministic in general, and
|
||||
/// non-determinism breaks salsa in a very, very, very bad way.
|
||||
/// @edwin0cheng heroically debugged this once! See #4315 for details
|
||||
#[salsa::invoke(expand_proc_macro)]
|
||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::TopSubtree>>;
|
||||
/// Retrieves the span to be used for a proc-macro expansions spans.
|
||||
/// This is a firewall query as it requires parsing the file, which we don't want proc-macros to
|
||||
/// directly depend on as that would cause to frequent invalidations, mainly because of the
|
||||
/// parse queries being LRU cached. If they weren't the invalidations would only happen if the
|
||||
/// user wrote in the file that defines the proc-macro.
|
||||
#[salsa::invoke_interned(proc_macro_span)]
|
||||
fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> Span;
|
||||
|
||||
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
|
||||
#[salsa::invoke(parse_macro_expansion_error)]
|
||||
fn parse_macro_expansion_error(
|
||||
&self,
|
||||
macro_call: MacroCallId,
|
||||
) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
|
||||
#[ra_salsa::transparent]
|
||||
fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContextId;
|
||||
|
||||
#[salsa::transparent]
|
||||
fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext;
|
||||
}
|
||||
|
||||
fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContextId {
|
||||
match file.repr() {
|
||||
HirFileIdRepr::FileId(_) => SyntaxContextId::root(edition),
|
||||
HirFileIdRepr::MacroFile(m) => {
|
||||
db.macro_arg_considering_derives(m.macro_call_id, &m.macro_call_id.lookup(db).kind)
|
||||
.2
|
||||
.ctx
|
||||
#[salsa::interned(no_lifetime, id = span::SyntaxContext)]
|
||||
pub struct SyntaxContextWrapper {
|
||||
pub data: SyntaxContext,
|
||||
}
|
||||
|
||||
fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContext {
|
||||
match file {
|
||||
HirFileId::FileId(_) => SyntaxContext::root(edition),
|
||||
HirFileId::MacroFile(m) => {
|
||||
let kind = db.lookup_intern_macro_call(m).kind;
|
||||
db.macro_arg_considering_derives(m, &kind).2.ctx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -272,9 +285,9 @@ pub fn expand_speculative(
|
|||
loc.krate,
|
||||
&tt,
|
||||
attr_arg.as_ref(),
|
||||
span_with_def_site_ctxt(db, span, actual_macro_call, loc.def.edition),
|
||||
span_with_call_site_ctxt(db, span, actual_macro_call, loc.def.edition),
|
||||
span_with_mixed_site_ctxt(db, span, actual_macro_call, loc.def.edition),
|
||||
span_with_def_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition),
|
||||
span_with_call_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition),
|
||||
span_with_mixed_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition),
|
||||
)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
|
||||
|
|
@ -318,14 +331,16 @@ pub fn expand_speculative(
|
|||
Some((node.syntax_node(), token))
|
||||
}
|
||||
|
||||
fn ast_id_map(db: &dyn ExpandDatabase, file_id: span::HirFileId) -> triomphe::Arc<AstIdMap> {
|
||||
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> triomphe::Arc<AstIdMap> {
|
||||
triomphe::Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
|
||||
}
|
||||
|
||||
/// Main public API -- parses a hir file, not caring whether it's a real
|
||||
/// file or a macro expansion.
|
||||
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
|
||||
match file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => db.parse(file_id).syntax_node(),
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
match file_id {
|
||||
HirFileId::FileId(file_id) => db.parse(file_id).syntax_node(),
|
||||
HirFileId::MacroFile(macro_file) => {
|
||||
db.parse_macro_expansion(macro_file).value.0.syntax_node()
|
||||
}
|
||||
}
|
||||
|
|
@ -335,14 +350,13 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
|
|||
// instead of having it be untyped
|
||||
fn parse_macro_expansion(
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_file: MacroFileId,
|
||||
macro_file: MacroCallId,
|
||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
|
||||
let _p = tracing::info_span!("parse_macro_expansion").entered();
|
||||
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(macro_file);
|
||||
let def_edition = loc.def.edition;
|
||||
let expand_to = loc.expand_to();
|
||||
let mbe::ValueResult { value: (tt, matched_arm), err } =
|
||||
macro_expand(db, macro_file.macro_call_id, loc);
|
||||
let mbe::ValueResult { value: (tt, matched_arm), err } = macro_expand(db, macro_file, loc);
|
||||
|
||||
let (parse, mut rev_token_map) = token_tree_to_syntax_node(
|
||||
db,
|
||||
|
|
@ -363,23 +377,19 @@ fn parse_macro_expansion_error(
|
|||
macro_call_id: MacroCallId,
|
||||
) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> {
|
||||
let e: ExpandResult<Arc<[SyntaxError]>> =
|
||||
db.parse_macro_expansion(MacroFileId { macro_call_id }).map(|it| Arc::from(it.0.errors()));
|
||||
if e.value.is_empty() && e.err.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(e))
|
||||
}
|
||||
db.parse_macro_expansion(macro_call_id).map(|it| Arc::from(it.0.errors()));
|
||||
if e.value.is_empty() && e.err.is_none() { None } else { Some(Arc::new(e)) }
|
||||
}
|
||||
|
||||
pub(crate) fn parse_with_map(
|
||||
db: &dyn ExpandDatabase,
|
||||
file_id: HirFileId,
|
||||
) -> (Parse<SyntaxNode>, SpanMap) {
|
||||
match file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
match file_id {
|
||||
HirFileId::FileId(file_id) => {
|
||||
(db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
|
||||
}
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
HirFileId::MacroFile(macro_file) => {
|
||||
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
||||
(parse, SpanMap::ExpansionSpanMap(map))
|
||||
}
|
||||
|
|
@ -597,7 +607,7 @@ fn macro_expand(
|
|||
|
||||
let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(..) => {
|
||||
return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None)
|
||||
return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None);
|
||||
}
|
||||
_ => {
|
||||
let (macro_arg, undo_info, span) =
|
||||
|
|
@ -699,9 +709,9 @@ fn expand_proc_macro(
|
|||
loc.krate,
|
||||
¯o_arg,
|
||||
attr_arg,
|
||||
span_with_def_site_ctxt(db, span, id, loc.def.edition),
|
||||
span_with_call_site_ctxt(db, span, id, loc.def.edition),
|
||||
span_with_mixed_site_ctxt(db, span, id, loc.def.edition),
|
||||
span_with_def_site_ctxt(db, span, id.into(), loc.def.edition),
|
||||
span_with_call_site_ctxt(db, span, id.into(), loc.def.edition),
|
||||
span_with_mixed_site_ctxt(db, span, id.into(), loc.def.edition),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
@ -728,12 +738,7 @@ pub(crate) fn token_tree_to_syntax_node(
|
|||
ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
|
||||
ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
|
||||
};
|
||||
syntax_bridge::token_tree_to_syntax_node(
|
||||
tt,
|
||||
entry_point,
|
||||
&mut |ctx| ctx.lookup(db).edition,
|
||||
edition,
|
||||
)
|
||||
syntax_bridge::token_tree_to_syntax_node(tt, entry_point, &mut |ctx| ctx.edition(db), edition)
|
||||
}
|
||||
|
||||
fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
|
||||
|
|
@ -755,8 +760,10 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn setup_syntax_context_root(db: &dyn ExpandDatabase) {
|
||||
for edition in Edition::iter() {
|
||||
db.intern_syntax_context(SyntaxContextData::root(edition));
|
||||
}
|
||||
fn intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallLoc) -> MacroCallId {
|
||||
MacroCallId::new(db, macro_call)
|
||||
}
|
||||
|
||||
fn lookup_intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> MacroCallLoc {
|
||||
macro_call.loc(db)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
//! Compiled declarative macro expanders (`macro_rules!` and `macro`)
|
||||
|
||||
use base_db::CrateId;
|
||||
use base_db::Crate;
|
||||
use intern::sym;
|
||||
use span::{Edition, HirFileIdRepr, MacroCallId, Span, SyntaxContextId};
|
||||
use span::{Edition, Span, SyntaxContext};
|
||||
use stdx::TupleExt;
|
||||
use syntax::{ast, AstNode};
|
||||
use syntax::{AstNode, ast};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId,
|
||||
attrs::RawAttrs,
|
||||
db::ExpandDatabase,
|
||||
hygiene::{apply_mark, Transparency},
|
||||
tt, AstId, ExpandError, ExpandErrorKind, ExpandResult, Lookup,
|
||||
hygiene::{Transparency, apply_mark},
|
||||
tt,
|
||||
};
|
||||
|
||||
/// Old-style `macro_rules` or the new macros 2.0
|
||||
|
|
@ -41,7 +42,10 @@ impl DeclarativeMacroExpander {
|
|||
.mac
|
||||
.expand(
|
||||
&tt,
|
||||
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency, self.edition),
|
||||
|s| {
|
||||
s.ctx =
|
||||
apply_mark(db, s.ctx, call_id.into(), self.transparency, self.edition)
|
||||
},
|
||||
span,
|
||||
loc.def.edition,
|
||||
)
|
||||
|
|
@ -70,7 +74,7 @@ impl DeclarativeMacroExpander {
|
|||
|
||||
pub(crate) fn expander(
|
||||
db: &dyn ExpandDatabase,
|
||||
def_crate: CrateId,
|
||||
def_crate: Crate,
|
||||
id: AstId<ast::Macro>,
|
||||
) -> Arc<DeclarativeMacroExpander> {
|
||||
let (root, map) = crate::db::parse_with_map(db, id.file_id);
|
||||
|
|
@ -84,7 +88,7 @@ impl DeclarativeMacroExpander {
|
|||
.find(|it| {
|
||||
it.path
|
||||
.as_ident()
|
||||
.map(|it| *it == sym::rustc_macro_transparency.clone())
|
||||
.map(|it| *it == sym::rustc_macro_transparency)
|
||||
.unwrap_or(false)
|
||||
})?
|
||||
.token_tree_value()?
|
||||
|
|
@ -100,14 +104,14 @@ impl DeclarativeMacroExpander {
|
|||
_ => None,
|
||||
}
|
||||
};
|
||||
let ctx_edition = |ctx: SyntaxContextId| {
|
||||
let crate_graph = db.crate_graph();
|
||||
let ctx_edition = |ctx: SyntaxContext| {
|
||||
if ctx.is_root() {
|
||||
crate_graph[def_crate].edition
|
||||
def_crate.data(db).edition
|
||||
} else {
|
||||
let data = db.lookup_intern_syntax_context(ctx);
|
||||
// UNWRAP-SAFETY: Only the root context has no outer expansion
|
||||
crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition
|
||||
let krate =
|
||||
db.lookup_intern_macro_call(ctx.outer_expn(db).unwrap().into()).def.krate;
|
||||
krate.data(db).edition
|
||||
}
|
||||
};
|
||||
let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
|
||||
|
|
@ -160,9 +164,9 @@ impl DeclarativeMacroExpander {
|
|||
transparency(¯o_def).unwrap_or(Transparency::Opaque),
|
||||
),
|
||||
};
|
||||
let edition = ctx_edition(match id.file_id.repr() {
|
||||
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id.lookup(db).ctxt,
|
||||
HirFileIdRepr::FileId(file) => SyntaxContextId::root(file.edition()),
|
||||
let edition = ctx_edition(match id.file_id {
|
||||
HirFileId::MacroFile(macro_file) => macro_file.lookup(db).ctxt,
|
||||
HirFileId::FileId(file) => SyntaxContext::root(file.edition(db)),
|
||||
});
|
||||
Arc::new(DeclarativeMacroExpander { mac, transparency, edition })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,28 +18,34 @@
|
|||
//!
|
||||
//!
|
||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||
use base_db::CrateId;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
|
||||
use base_db::Crate;
|
||||
use span::SyntaxContext;
|
||||
use syntax::{AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, ted};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile,
|
||||
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||
ast::{self, AstNode},
|
||||
db::ExpandDatabase,
|
||||
mod_path::ModPath,
|
||||
AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern,
|
||||
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||
};
|
||||
|
||||
pub type EagerCallBackFn<'a> = &'a mut dyn FnMut(
|
||||
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
|
||||
MacroCallId,
|
||||
);
|
||||
|
||||
pub fn expand_eager_macro_input(
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
krate: Crate,
|
||||
macro_call: &ast::MacroCall,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
def: MacroDefId,
|
||||
call_site: SyntaxContextId,
|
||||
call_site: SyntaxContext,
|
||||
resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
|
||||
eager_callback: EagerCallBackFn<'_>,
|
||||
) -> ExpandResult<Option<MacroCallId>> {
|
||||
let expand_to = ExpandTo::from_call_site(macro_call);
|
||||
|
||||
|
|
@ -47,17 +53,17 @@ pub fn expand_eager_macro_input(
|
|||
// When `lazy_expand` is called, its *parent* file must already exist.
|
||||
// Here we store an eager macro id for the argument expanded subtree
|
||||
// for that purpose.
|
||||
let arg_id = MacroCallLoc {
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None },
|
||||
ctxt: call_site,
|
||||
}
|
||||
.intern(db);
|
||||
};
|
||||
let arg_id = db.intern_macro_call(loc);
|
||||
#[allow(deprecated)] // builtin eager macros are never derives
|
||||
let (_, _, span) = db.macro_arg(arg_id);
|
||||
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
|
||||
db.parse_macro_expansion(arg_id.as_macro_file());
|
||||
db.parse_macro_expansion(arg_id);
|
||||
|
||||
let mut arg_map = ExpansionSpanMap::empty();
|
||||
|
||||
|
|
@ -67,10 +73,11 @@ pub fn expand_eager_macro_input(
|
|||
&arg_exp_map,
|
||||
&mut arg_map,
|
||||
TextSize::new(0),
|
||||
InFile::new(arg_id.as_file(), arg_exp.syntax_node()),
|
||||
InFile::new(arg_id.into(), arg_exp.syntax_node()),
|
||||
krate,
|
||||
call_site,
|
||||
resolver,
|
||||
eager_callback,
|
||||
)
|
||||
};
|
||||
let err = parse_err.or(err);
|
||||
|
|
@ -107,7 +114,7 @@ pub fn expand_eager_macro_input(
|
|||
ctxt: call_site,
|
||||
};
|
||||
|
||||
ExpandResult { value: Some(loc.intern(db)), err }
|
||||
ExpandResult { value: Some(db.intern_macro_call(loc)), err }
|
||||
}
|
||||
|
||||
fn lazy_expand(
|
||||
|
|
@ -115,8 +122,9 @@ fn lazy_expand(
|
|||
def: &MacroDefId,
|
||||
macro_call: &ast::MacroCall,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
krate: CrateId,
|
||||
call_site: SyntaxContextId,
|
||||
krate: Crate,
|
||||
call_site: SyntaxContext,
|
||||
eager_callback: EagerCallBackFn<'_>,
|
||||
) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
|
||||
let expand_to = ExpandTo::from_call_site(macro_call);
|
||||
let id = def.make_call(
|
||||
|
|
@ -125,10 +133,9 @@ fn lazy_expand(
|
|||
MacroCallKind::FnLike { ast_id, expand_to, eager: None },
|
||||
call_site,
|
||||
);
|
||||
let macro_file = id.as_macro_file();
|
||||
eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id);
|
||||
|
||||
db.parse_macro_expansion(macro_file)
|
||||
.map(|parse| (InFile::new(macro_file.into(), parse.0), parse.1))
|
||||
db.parse_macro_expansion(id).map(|parse| (InFile::new(id.into(), parse.0), parse.1))
|
||||
}
|
||||
|
||||
fn eager_macro_recur(
|
||||
|
|
@ -137,9 +144,10 @@ fn eager_macro_recur(
|
|||
expanded_map: &mut ExpansionSpanMap,
|
||||
mut offset: TextSize,
|
||||
curr: InFile<SyntaxNode>,
|
||||
krate: CrateId,
|
||||
call_site: SyntaxContextId,
|
||||
krate: Crate,
|
||||
call_site: SyntaxContext,
|
||||
macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
|
||||
eager_callback: EagerCallBackFn<'_>,
|
||||
) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
|
||||
let original = curr.value.clone_for_update();
|
||||
|
||||
|
|
@ -176,7 +184,7 @@ fn eager_macro_recur(
|
|||
Some(path) => match macro_resolver(&path) {
|
||||
Some(def) => def,
|
||||
None => {
|
||||
let edition = db.crate_graph()[krate].edition;
|
||||
let edition = krate.data(db).edition;
|
||||
error = Some(ExpandError::other(
|
||||
span_map.span_at(call.syntax().text_range().start()),
|
||||
format!("unresolved macro {}", path.display(db, edition)),
|
||||
|
|
@ -205,11 +213,16 @@ fn eager_macro_recur(
|
|||
def,
|
||||
call_site,
|
||||
macro_resolver,
|
||||
eager_callback,
|
||||
);
|
||||
match value {
|
||||
Some(call_id) => {
|
||||
eager_callback(
|
||||
curr.with_value(ast_id).map(|ast_id| (AstPtr::new(&call), ast_id)),
|
||||
call_id,
|
||||
);
|
||||
let ExpandResult { value: (parse, map), err: err2 } =
|
||||
db.parse_macro_expansion(call_id.as_macro_file());
|
||||
db.parse_macro_expansion(call_id);
|
||||
|
||||
map.iter().for_each(|(o, span)| expanded_map.push(o + offset, span));
|
||||
|
||||
|
|
@ -230,8 +243,15 @@ fn eager_macro_recur(
|
|||
| MacroDefKind::BuiltInAttr(..)
|
||||
| MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(..) => {
|
||||
let ExpandResult { value: (parse, tm), err } =
|
||||
lazy_expand(db, &def, &call, curr.with_value(ast_id), krate, call_site);
|
||||
let ExpandResult { value: (parse, tm), err } = lazy_expand(
|
||||
db,
|
||||
&def,
|
||||
&call,
|
||||
curr.with_value(ast_id),
|
||||
krate,
|
||||
call_site,
|
||||
eager_callback,
|
||||
);
|
||||
|
||||
// replace macro inside
|
||||
let ExpandResult { value, err: error } = eager_macro_recur(
|
||||
|
|
@ -244,6 +264,7 @@ fn eager_macro_recur(
|
|||
krate,
|
||||
call_site,
|
||||
macro_resolver,
|
||||
eager_callback,
|
||||
);
|
||||
let err = err.or(error);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,13 @@
|
|||
use std::borrow::Borrow;
|
||||
|
||||
use either::Either;
|
||||
use span::{
|
||||
AstIdNode, EditionedFileId, ErasedFileAstId, FileAstId, HirFileId, HirFileIdRepr, MacroFileId,
|
||||
SyntaxContextId,
|
||||
};
|
||||
use span::{AstIdNode, ErasedFileAstId, FileAstId, FileId, SyntaxContext};
|
||||
use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize};
|
||||
|
||||
use crate::{
|
||||
EditionedFileId, HirFileId, MacroCallId, MacroKind,
|
||||
db::{self, ExpandDatabase},
|
||||
map_node_range_up, map_node_range_up_rooted, span_for_offset, MacroFileIdExt, MacroKind,
|
||||
map_node_range_up, map_node_range_up_rooted, span_for_offset,
|
||||
};
|
||||
|
||||
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
|
||||
|
|
@ -26,7 +24,7 @@ pub struct InFileWrapper<FileKind, T> {
|
|||
pub value: T,
|
||||
}
|
||||
pub type InFile<T> = InFileWrapper<HirFileId, T>;
|
||||
pub type InMacroFile<T> = InFileWrapper<MacroFileId, T>;
|
||||
pub type InMacroFile<T> = InFileWrapper<MacroCallId, T>;
|
||||
pub type InRealFile<T> = InFileWrapper<EditionedFileId, T>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
|
|
@ -35,12 +33,13 @@ pub struct FilePositionWrapper<FileKind> {
|
|||
pub offset: TextSize,
|
||||
}
|
||||
pub type HirFilePosition = FilePositionWrapper<HirFileId>;
|
||||
pub type MacroFilePosition = FilePositionWrapper<MacroFileId>;
|
||||
pub type MacroFilePosition = FilePositionWrapper<MacroCallId>;
|
||||
pub type FilePosition = FilePositionWrapper<EditionedFileId>;
|
||||
|
||||
impl From<FilePositionWrapper<EditionedFileId>> for FilePositionWrapper<span::FileId> {
|
||||
fn from(value: FilePositionWrapper<EditionedFileId>) -> Self {
|
||||
FilePositionWrapper { file_id: value.file_id.into(), offset: value.offset }
|
||||
impl FilePosition {
|
||||
#[inline]
|
||||
pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FilePositionWrapper<FileId> {
|
||||
FilePositionWrapper { file_id: self.file_id.file_id(db), offset: self.offset }
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
|
|
@ -49,12 +48,13 @@ pub struct FileRangeWrapper<FileKind> {
|
|||
pub range: TextRange,
|
||||
}
|
||||
pub type HirFileRange = FileRangeWrapper<HirFileId>;
|
||||
pub type MacroFileRange = FileRangeWrapper<MacroFileId>;
|
||||
pub type MacroFileRange = FileRangeWrapper<MacroCallId>;
|
||||
pub type FileRange = FileRangeWrapper<EditionedFileId>;
|
||||
|
||||
impl From<FileRangeWrapper<EditionedFileId>> for FileRangeWrapper<span::FileId> {
|
||||
fn from(value: FileRangeWrapper<EditionedFileId>) -> Self {
|
||||
FileRangeWrapper { file_id: value.file_id.into(), range: value.range }
|
||||
impl FileRange {
|
||||
#[inline]
|
||||
pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FileRangeWrapper<FileId> {
|
||||
FileRangeWrapper { file_id: self.file_id.file_id(db), range: self.range }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +76,9 @@ impl<N: AstIdNode> AstId<N> {
|
|||
pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> AstPtr<N> {
|
||||
db.ast_id_map(self.file_id).get(self.value)
|
||||
}
|
||||
pub fn erase(&self) -> ErasedAstId {
|
||||
crate::InFile::new(self.file_id, self.value.erase())
|
||||
}
|
||||
}
|
||||
|
||||
pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
|
||||
|
|
@ -162,7 +165,7 @@ impl FileIdToSyntax for EditionedFileId {
|
|||
db.parse(self).syntax_node()
|
||||
}
|
||||
}
|
||||
impl FileIdToSyntax for MacroFileId {
|
||||
impl FileIdToSyntax for MacroCallId {
|
||||
fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
|
||||
db.parse_macro_expansion(self).value.0.syntax_node()
|
||||
}
|
||||
|
|
@ -215,7 +218,7 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
|
|||
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
||||
Some(parent) => Some(node.with_value(parent)),
|
||||
None => db
|
||||
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
|
||||
.lookup_intern_macro_call(node.file_id.macro_file()?)
|
||||
.to_node_item(db)
|
||||
.syntax()
|
||||
.cloned()
|
||||
|
|
@ -232,7 +235,7 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
|
|||
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
||||
Some(parent) => Some(node.with_value(parent)),
|
||||
None => db
|
||||
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
|
||||
.lookup_intern_macro_call(node.file_id.macro_file()?)
|
||||
.to_node_item(db)
|
||||
.syntax()
|
||||
.cloned()
|
||||
|
|
@ -272,11 +275,11 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
|
|||
) -> Option<InRealFile<SyntaxNode>> {
|
||||
// This kind of upmapping can only be achieved in attribute expanded files,
|
||||
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
|
||||
let file_id = match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
return Some(InRealFile { file_id, value: self.value.borrow().clone() })
|
||||
let file_id = match self.file_id {
|
||||
HirFileId::FileId(file_id) => {
|
||||
return Some(InRealFile { file_id, value: self.value.borrow().clone() });
|
||||
}
|
||||
HirFileIdRepr::MacroFile(m)
|
||||
HirFileId::MacroFile(m)
|
||||
if matches!(m.kind(db), MacroKind::Attr | MacroKind::AttrBuiltIn) =>
|
||||
{
|
||||
m
|
||||
|
|
@ -284,7 +287,7 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
|
|||
_ => return None,
|
||||
};
|
||||
|
||||
let FileRange { file_id, range } = map_node_range_up_rooted(
|
||||
let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted(
|
||||
db,
|
||||
&db.expansion_span_map(file_id),
|
||||
self.value.borrow().text_range(),
|
||||
|
|
@ -292,13 +295,13 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
|
|||
|
||||
let kind = self.kind();
|
||||
let value = db
|
||||
.parse(file_id)
|
||||
.parse(editioned_file_id)
|
||||
.syntax_node()
|
||||
.covering_element(range)
|
||||
.ancestors()
|
||||
.take_while(|it| it.text_range() == range)
|
||||
.find(|it| it.kind() == kind)?;
|
||||
Some(InRealFile::new(file_id, value))
|
||||
Some(InRealFile::new(editioned_file_id, value))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +310,7 @@ impl InFile<&SyntaxNode> {
|
|||
pub fn original_file_range_opt(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<(FileRange, SyntaxContextId)> {
|
||||
) -> Option<(FileRange, SyntaxContext)> {
|
||||
self.borrow().map(SyntaxNode::text_range).original_node_file_range_opt(db)
|
||||
}
|
||||
}
|
||||
|
|
@ -324,9 +327,9 @@ impl InMacroFile<SyntaxToken> {
|
|||
impl InFile<SyntaxToken> {
|
||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
||||
HirFileId::MacroFile(mac_file) => {
|
||||
let (range, ctxt) = span_for_offset(
|
||||
db,
|
||||
&db.expansion_span_map(mac_file),
|
||||
|
|
@ -340,7 +343,7 @@ impl InFile<SyntaxToken> {
|
|||
}
|
||||
|
||||
// Fall back to whole macro call.
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(mac_file);
|
||||
loc.kind.original_call_range(db)
|
||||
}
|
||||
}
|
||||
|
|
@ -348,11 +351,11 @@ impl InFile<SyntaxToken> {
|
|||
|
||||
/// Attempts to map the syntax node back up its macro calls.
|
||||
pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => {
|
||||
Some(FileRange { file_id, range: self.value.text_range() })
|
||||
}
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
HirFileId::MacroFile(mac_file) => {
|
||||
let (range, ctxt) = span_for_offset(
|
||||
db,
|
||||
&db.expansion_span_map(mac_file),
|
||||
|
|
@ -361,18 +364,14 @@ impl InFile<SyntaxToken> {
|
|||
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if ctxt.is_root() {
|
||||
Some(range)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if ctxt.is_root() { Some(range) } else { None }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InMacroFile<TextSize> {
|
||||
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContextId) {
|
||||
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContext) {
|
||||
span_for_offset(db, &db.expansion_span_map(self.file_id), self.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -381,17 +380,17 @@ impl InFile<TextRange> {
|
|||
pub fn original_node_file_range(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> (FileRange, SyntaxContextId) {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
(FileRange { file_id, range: self.value }, SyntaxContextId::root(file_id.edition()))
|
||||
) -> (FileRange, SyntaxContext) {
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => {
|
||||
(FileRange { file_id, range: self.value }, SyntaxContext::root(file_id.edition(db)))
|
||||
}
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
HirFileId::MacroFile(mac_file) => {
|
||||
match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
(loc.kind.original_call_range(db), SyntaxContextId::root(loc.def.edition))
|
||||
let loc = db.lookup_intern_macro_call(mac_file);
|
||||
(loc.kind.original_call_range(db), SyntaxContext::root(loc.def.edition))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -399,13 +398,13 @@ impl InFile<TextRange> {
|
|||
}
|
||||
|
||||
pub fn original_node_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileId::MacroFile(mac_file) => {
|
||||
match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(mac_file);
|
||||
loc.kind.original_call_range(db)
|
||||
}
|
||||
}
|
||||
|
|
@ -417,13 +416,13 @@ impl InFile<TextRange> {
|
|||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileId::MacroFile(mac_file) => {
|
||||
match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
let loc = db.lookup_intern_macro_call(mac_file);
|
||||
loc.kind.original_call_range_with_body(db)
|
||||
}
|
||||
}
|
||||
|
|
@ -434,13 +433,13 @@ impl InFile<TextRange> {
|
|||
pub fn original_node_file_range_opt(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<(FileRange, SyntaxContextId)> {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => Some((
|
||||
) -> Option<(FileRange, SyntaxContext)> {
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => Some((
|
||||
FileRange { file_id, range: self.value },
|
||||
SyntaxContextId::root(file_id.edition()),
|
||||
SyntaxContext::root(file_id.edition(db)),
|
||||
)),
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
HirFileId::MacroFile(mac_file) => {
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -451,34 +450,34 @@ impl<N: AstNode> InFile<N> {
|
|||
pub fn original_ast_node_rooted(self, db: &dyn db::ExpandDatabase) -> Option<InRealFile<N>> {
|
||||
// This kind of upmapping can only be achieved in attribute expanded files,
|
||||
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
|
||||
let file_id = match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
return Some(InRealFile { file_id, value: self.value })
|
||||
let file_id = match self.file_id {
|
||||
HirFileId::FileId(file_id) => {
|
||||
return Some(InRealFile { file_id, value: self.value });
|
||||
}
|
||||
HirFileIdRepr::MacroFile(m) => m,
|
||||
HirFileId::MacroFile(m) => m,
|
||||
};
|
||||
if !matches!(file_id.kind(db), MacroKind::Attr | MacroKind::AttrBuiltIn) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let FileRange { file_id, range } = map_node_range_up_rooted(
|
||||
let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted(
|
||||
db,
|
||||
&db.expansion_span_map(file_id),
|
||||
self.value.syntax().text_range(),
|
||||
)?;
|
||||
|
||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
|
||||
let anc = db.parse(file_id).syntax_node().covering_element(range);
|
||||
let anc = db.parse(editioned_file_id).syntax_node().covering_element(range);
|
||||
let value = anc.ancestors().find_map(N::cast)?;
|
||||
Some(InRealFile::new(file_id, value))
|
||||
Some(InRealFile::new(editioned_file_id, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InFile<T> {
|
||||
pub fn into_real_file(self) -> Result<InRealFile<T>, InFile<T>> {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => Ok(InRealFile { file_id, value: self.value }),
|
||||
HirFileIdRepr::MacroFile(_) => Err(self),
|
||||
match self.file_id {
|
||||
HirFileId::FileId(file_id) => Ok(InRealFile { file_id, value: self.value }),
|
||||
HirFileId::MacroFile(_) => Err(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
use intern::sym;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{
|
||||
ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, FIXUP_ERASED_FILE_AST_ID_MARKER,
|
||||
ROOT_ERASED_FILE_AST_ID,
|
||||
ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor,
|
||||
SyntaxContext,
|
||||
};
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||
ast::{self, AstNode, HasLoopBody},
|
||||
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||
match_ast,
|
||||
};
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
use triomphe::Arc;
|
||||
|
|
@ -81,7 +82,7 @@ pub(crate) fn fixup_syntax(
|
|||
original.push(original_tree);
|
||||
let span = span_map.span_for_range(node_range);
|
||||
let replacement = Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: Span {
|
||||
range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
|
||||
anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
|
||||
|
|
@ -101,7 +102,7 @@ pub(crate) fn fixup_syntax(
|
|||
// incomplete field access: some_expr.|
|
||||
append.insert(node.clone().into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
|
|
@ -140,7 +141,7 @@ pub(crate) fn fixup_syntax(
|
|||
};
|
||||
append.insert(if_token.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
|
|
@ -170,7 +171,7 @@ pub(crate) fn fixup_syntax(
|
|||
};
|
||||
append.insert(while_token.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
|
|
@ -216,7 +217,7 @@ pub(crate) fn fixup_syntax(
|
|||
};
|
||||
append.insert(match_token.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
|
|
@ -245,9 +246,9 @@ pub(crate) fn fixup_syntax(
|
|||
};
|
||||
|
||||
let [pat, in_token, iter] = [
|
||||
sym::underscore.clone(),
|
||||
sym::in_.clone(),
|
||||
sym::__ra_fixup.clone(),
|
||||
sym::underscore,
|
||||
sym::in_,
|
||||
sym::__ra_fixup,
|
||||
].map(|sym|
|
||||
Leaf::Ident(Ident {
|
||||
sym,
|
||||
|
|
@ -283,7 +284,7 @@ pub(crate) fn fixup_syntax(
|
|||
if it.name_ref().is_some() && it.expr().is_none() {
|
||||
append.insert(colon.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
})
|
||||
|
|
@ -296,7 +297,7 @@ pub(crate) fn fixup_syntax(
|
|||
if it.segment().is_none() {
|
||||
append.insert(colon.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
})
|
||||
|
|
@ -308,7 +309,7 @@ pub(crate) fn fixup_syntax(
|
|||
if it.body().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
sym: sym::__ra_fixup,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
})
|
||||
|
|
@ -353,7 +354,7 @@ pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInf
|
|||
let span = |file_id| Span {
|
||||
range: TextRange::empty(TextSize::new(0)),
|
||||
anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
ctx: SyntaxContextId::root(span::Edition::Edition2015),
|
||||
ctx: SyntaxContext::root(span::Edition::Edition2015),
|
||||
};
|
||||
delimiter.open = span(delimiter.open.anchor.file_id);
|
||||
delimiter.close = span(delimiter.close.anchor.file_id);
|
||||
|
|
@ -465,7 +466,7 @@ fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
use expect_test::{Expect, expect};
|
||||
use span::{Edition, EditionedFileId, FileId};
|
||||
use syntax::TextRange;
|
||||
use syntax_bridge::DocCommentDesugarMode;
|
||||
|
|
|
|||
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