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:
bors 2025-04-30 20:42:22 +00:00
commit b45dd71d18
896 changed files with 33442 additions and 39725 deletions

View file

@ -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)) => {

View file

@ -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 };

View file

@ -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(

View file

@ -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)
}
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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),

View file

@ -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,

View file

@ -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

View file

@ -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:

View file

@ -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.
-->

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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 )*))}}

View file

@ -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

View file

@ -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 }
}

View file

@ -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,)
]
);
}
}

View file

@ -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))
}

View file

@ -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

View file

@ -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]>,
}

View file

@ -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};

View file

@ -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()

View file

@ -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"]

View file

@ -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};

View file

@ -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),
},
}
}

View file

@ -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()
}

View file

@ -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(),
}
}

View file

@ -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;
}
_ => {}
}

View file

@ -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,

View file

@ -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(&macro_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,
}

View file

@ -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();
}
}

View file

@ -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))

View file

@ -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(&macro_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

View file

@ -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)

View file

@ -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(&lt.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_));
}
}
}

View file

@ -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 })
}

View file

@ -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);
}

View file

@ -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,
}
}
}

View file

@ -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(|(&param, 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}}"),
}
}
}
}

View file

@ -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();

View file

@ -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;

View file

@ -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 { .. }));
}
_ => {}
}
}

View file

@ -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
"#]],
);

View file

@ -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]>>
{...}
"#]],
);
}

View file

@ -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,

View file

@ -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 &param 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(&lt));
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(&lt))
})
.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,
&macro_types_map,
&macro_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,
}
}

View file

@ -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)]

View file

@ -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() {

View 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 }
})
}
}

View file

@ -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(&lt));
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(&lt)))
.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

View file

@ -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]

View file

@ -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),

View file

@ -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

View file

@ -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(), &param, 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(), &param, 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)
}

View file

@ -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, &params.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, &params.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), &params.types_map);
}
});
true
}
fn print_ast_id(&mut self, ast_id: ErasedFileAstId) {
wln!(self, "// AstId: {:?}", ast_id.into_raw());
}

View file

@ -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(

View file

@ -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);

View file

@ -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,
}
}
}

View file

@ -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))
}
}

View file

@ -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) {

View file

@ -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;
"#]],
);
}

View file

@ -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
}
);
}
"#]],
);
}

View file

@ -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)

View file

@ -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, &macro_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>()
}
}

View file

@ -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#&#0: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#&#0: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#"#]],
);
}

View file

@ -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) })
}
}

View 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;
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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)]

View file

@ -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,
));

View file

@ -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)

View file

@ -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

View file

@ -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;

View file

@ -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(_)

View file

@ -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;

View file

@ -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>)> {

View file

@ -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(())
}

View file

@ -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, &params.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) = &param.name() {
let id = TypeOrConstParamId { parent, local_id };
@ -996,12 +1034,6 @@ impl Scope {
acc.add(&param.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 {

View 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
})
}
}

View file

@ -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(

View file

@ -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,
})

View file

@ -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])
}

View file

@ -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"]

View file

@ -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

View file

@ -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,
},
};

View file

@ -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)

View file

@ -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);
}
};

View file

@ -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);

View file

@ -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 () ,}}}"
);
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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,
&macro_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)
}

View file

@ -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(&macro_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 })
}

View file

@ -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);

View file

@ -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),
}
}
}

View file

@ -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