mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Merge remote-tracking branch 'upstream/master' into sync-from-rust
This commit is contained in:
commit
c3dbe7c717
598 changed files with 57701 additions and 17616 deletions
2
.github/actions/github-release/README.md
vendored
2
.github/actions/github-release/README.md
vendored
|
@ -10,7 +10,7 @@ perform github releases but they all tend to have their set of drawbacks.
|
|||
Additionally nothing handles deleting releases which we need for our rolling
|
||||
`dev` release.
|
||||
|
||||
To handle all this this action rolls-its-own implementation using the
|
||||
To handle all this, this action rolls its own implementation using the
|
||||
actions/toolkit repository and packages published there. These run in a Docker
|
||||
container and take various inputs to orchestrate the release from the build.
|
||||
|
||||
|
|
2
.github/workflows/autopublish.yaml
vendored
2
.github/workflows/autopublish.yaml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
git config --global user.email "runner@gha.local"
|
||||
git config --global user.name "Github Action"
|
||||
git config --global user.name "GitHub Action"
|
||||
rm Cargo.lock
|
||||
# Fix names for crates that were published before switch to kebab-case.
|
||||
cargo workspaces rename --from base-db base_db
|
||||
|
|
53
.github/workflows/ci.yaml
vendored
53
.github/workflows/ci.yaml
vendored
|
@ -18,12 +18,35 @@ env:
|
|||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
typescript: ${{ steps.filter.outputs.typescript }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dorny/paths-filter@4067d885736b84de7c414f582ac45897079b0a78
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
typescript:
|
||||
- 'editors/code/**'
|
||||
proc_macros:
|
||||
- 'crates/proc-macro-api/**'
|
||||
- 'crates/proc-macro-srv/**'
|
||||
- 'crates/proc-macro-srv-cli/**'
|
||||
- 'crates/proc-macro-test/**'
|
||||
|
||||
rust:
|
||||
needs: changes
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: Rust
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CC: deny_c
|
||||
RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable'}}"
|
||||
USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || ''}}"
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -35,30 +58,31 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 20
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup update --no-self-update ${{ env.RUST_CHANNEL }}
|
||||
rustup component add rustfmt rust-src
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
|
||||
with:
|
||||
key: ${{ env.RUST_CHANNEL }}
|
||||
|
||||
- name: Bump opt-level
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||
|
||||
- name: Compile (tests)
|
||||
run: cargo test --no-run --locked
|
||||
run: cargo test --no-run --locked ${{ env.USE_SYSROOT_ABI }}
|
||||
|
||||
# It's faster to `test` before `build` ¯\_(ツ)_/¯
|
||||
- name: Compile (rust-analyzer)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cargo build --quiet
|
||||
run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }}
|
||||
|
||||
- name: Test
|
||||
run: cargo test -- --nocapture --quiet
|
||||
run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
|
||||
|
||||
- name: Run analysis-stats on rust-analyzer
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
@ -90,7 +114,7 @@ jobs:
|
|||
rustup target add ${{ env.targets }} ${{ env.targets_ide }}
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
|
||||
|
||||
- name: Check
|
||||
run: |
|
||||
|
@ -102,6 +126,7 @@ jobs:
|
|||
done
|
||||
|
||||
typescript:
|
||||
needs: changes
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: TypeScript
|
||||
strategy:
|
||||
|
@ -114,18 +139,21 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- name: Install Nodejs
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- name: Install xvfb
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
|
||||
run: sudo apt-get install -y xvfb
|
||||
|
||||
- run: npm ci
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
# - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
|
||||
# if: runner.os == 'Linux'
|
||||
|
@ -133,16 +161,17 @@ jobs:
|
|||
|
||||
- run: npm run lint
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- name: Run VS Code tests (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
|
||||
env:
|
||||
VSCODE_CLI: 1
|
||||
run: xvfb-run npm test
|
||||
working-directory: ./editors/code
|
||||
|
||||
- name: Run VS Code tests (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
if: matrix.os == 'windows-latest' && needs.changes.outputs.typescript == 'true'
|
||||
env:
|
||||
VSCODE_CLI: 1
|
||||
run: npm test
|
||||
|
@ -150,9 +179,11 @@ jobs:
|
|||
|
||||
- run: npm run pretest
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- run: npm run package --scripts-prepend-node-path
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
end-success:
|
||||
name: bors build finished
|
||||
|
@ -165,7 +196,7 @@ jobs:
|
|||
|
||||
end-failure:
|
||||
name: bors build finished
|
||||
if: github.event.pusher.name == 'bors' && (failure() || cancelled())
|
||||
if: github.event.pusher.name == 'bors' && !success()
|
||||
runs-on: ubuntu-latest
|
||||
needs: [rust, rust-cross, typescript]
|
||||
steps:
|
||||
|
|
6
.github/workflows/publish-libs.yaml
vendored
6
.github/workflows/publish-libs.yaml
vendored
|
@ -3,9 +3,9 @@ on:
|
|||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- 'lib/**'
|
||||
- "lib/**"
|
||||
|
||||
jobs:
|
||||
publish-libs:
|
||||
|
@ -29,7 +29,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
git config --global user.email "runner@gha.local"
|
||||
git config --global user.name "Github Action"
|
||||
git config --global user.name "GitHub Action"
|
||||
# Remove r-a crates from the workspaces so we don't auto-publish them as well
|
||||
sed -i 's/ "crates\/\*"//' ./Cargo.toml
|
||||
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty
|
||||
|
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
|
@ -270,7 +270,7 @@ jobs:
|
|||
- name: Publish Extension (Code Marketplace, nightly)
|
||||
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
||||
working-directory: ./editors/code
|
||||
run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
|
||||
run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release
|
||||
|
||||
- name: Publish Extension (OpenVSX, nightly)
|
||||
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
||||
|
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -72,7 +72,7 @@
|
|||
},
|
||||
{
|
||||
// Used for testing the extension with a local build of the LSP server (in `target/release`)
|
||||
// with all other extendions loaded.
|
||||
// with all other extensions loaded.
|
||||
"name": "Run With Extensions",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
|
|
426
Cargo.lock
generated
426
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
18
Cargo.toml
18
Cargo.toml
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
members = ["xtask/", "lib/*", "crates/*"]
|
||||
exclude = ["crates/proc-macro-test/imp"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.66"
|
||||
|
@ -74,5 +75,20 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" }
|
|||
tt = { path = "./crates/tt", version = "0.0.0" }
|
||||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
line-index = { version = "0.1.0-pre.1", path = "./lib/line-index" }
|
||||
|
||||
# non-local crates
|
||||
smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] }
|
||||
smallvec = { version = "1.10.0", features = [
|
||||
"const_new",
|
||||
"union",
|
||||
"const_generics",
|
||||
] }
|
||||
smol_str = "0.2.0"
|
||||
nohash-hasher = "0.2.0"
|
||||
text-size = "1.1.0"
|
||||
# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved
|
||||
serde = { version = "=1.0.156", features = ["derive"] }
|
||||
serde_json = "1.0.94"
|
||||
triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
|
||||
|
||||
rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" }
|
||||
|
|
|
@ -3808,7 +3808,7 @@ impl<'a> Parser<'a> {
|
|||
if self.eat_keyword(keywords::Else) || !cond.returns() {
|
||||
let sp = self.sess.source_map().next_point(lo);
|
||||
let mut err = self.diagnostic()
|
||||
.struct_span_err(sp, "missing condition for `if` statemement");
|
||||
.struct_span_err(sp, "missing condition for `if` statement");
|
||||
err.span_label(sp, "expected if condition here");
|
||||
return Err(err)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@ doctest = false
|
|||
salsa = "0.17.0-pre.2"
|
||||
rustc-hash = "1.1.0"
|
||||
|
||||
triomphe.workspace = true
|
||||
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
|
||||
# local deps
|
||||
cfg.workspace = true
|
||||
profile.workspace = true
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
//! Defines a unit of change that can applied to the database to get the next
|
||||
//! state. Changes are transactional.
|
||||
|
||||
use std::{fmt, sync::Arc};
|
||||
use std::fmt;
|
||||
|
||||
use salsa::Durability;
|
||||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
|
||||
use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};
|
||||
use crate::{CrateGraph, ProcMacros, SourceDatabaseExt, SourceRoot, SourceRootId};
|
||||
|
||||
/// Encapsulate a bunch of raw `.set` calls on the database.
|
||||
#[derive(Default)]
|
||||
pub struct Change {
|
||||
pub roots: Option<Vec<SourceRoot>>,
|
||||
pub files_changed: Vec<(FileId, Option<Arc<String>>)>,
|
||||
pub files_changed: Vec<(FileId, Option<Arc<str>>)>,
|
||||
pub crate_graph: Option<CrateGraph>,
|
||||
pub proc_macros: Option<ProcMacros>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Change {
|
||||
|
@ -33,7 +35,7 @@ impl fmt::Debug for Change {
|
|||
}
|
||||
|
||||
impl Change {
|
||||
pub fn new() -> Change {
|
||||
pub fn new() -> Self {
|
||||
Change::default()
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,7 @@ impl Change {
|
|||
self.roots = Some(roots);
|
||||
}
|
||||
|
||||
pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
|
||||
pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<str>>) {
|
||||
self.files_changed.push((file_id, new_text))
|
||||
}
|
||||
|
||||
|
@ -49,6 +51,10 @@ impl Change {
|
|||
self.crate_graph = Some(graph);
|
||||
}
|
||||
|
||||
pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
|
||||
self.proc_macros = Some(proc_macros);
|
||||
}
|
||||
|
||||
pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
|
||||
let _p = profile::span("RootDatabase::apply_change");
|
||||
if let Some(roots) = self.roots {
|
||||
|
@ -67,11 +73,14 @@ impl Change {
|
|||
let source_root = db.source_root(source_root_id);
|
||||
let durability = durability(&source_root);
|
||||
// XXX: can't actually remove the file, just reset the text
|
||||
let text = text.unwrap_or_default();
|
||||
let text = text.unwrap_or_else(|| Arc::from(""));
|
||||
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)
|
||||
db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
|
||||
}
|
||||
if let Some(proc_macros) = self.proc_macros {
|
||||
db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
//! A set of high-level utility fixture methods to use in tests.
|
||||
use std::{mem, str::FromStr, sync::Arc};
|
||||
use std::{mem, str::FromStr, sync};
|
||||
|
||||
use cfg::CfgOptions;
|
||||
use rustc_hash::FxHashMap;
|
||||
use test_utils::{
|
||||
extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
|
||||
extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
|
||||
ESCAPED_CURSOR_MARKER,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
use tt::token_id::{Leaf, Subtree, TokenTree};
|
||||
use vfs::{file_set::FileSet, VfsPath};
|
||||
|
||||
use crate::{
|
||||
input::{CrateName, CrateOrigin, LangCrateOrigin},
|
||||
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
|
||||
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt,
|
||||
SourceRoot, SourceRootId,
|
||||
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
|
||||
SourceDatabaseExt, SourceRoot, SourceRootId,
|
||||
};
|
||||
|
||||
pub const WORKSPACE: SourceRootId = SourceRootId(0);
|
||||
|
||||
pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
||||
#[track_caller]
|
||||
fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
|
||||
let fixture = ChangeFixture::parse(ra_fixture);
|
||||
let mut db = Self::default();
|
||||
|
@ -27,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
|||
(db, fixture.files[0])
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) {
|
||||
let fixture = ChangeFixture::parse(ra_fixture);
|
||||
let mut db = Self::default();
|
||||
|
@ -35,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
|||
(db, fixture.files)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_files(ra_fixture: &str) -> Self {
|
||||
let fixture = ChangeFixture::parse(ra_fixture);
|
||||
let mut db = Self::default();
|
||||
|
@ -43,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
|||
db
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_files_extra_proc_macros(
|
||||
ra_fixture: &str,
|
||||
proc_macros: Vec<(String, ProcMacro)>,
|
||||
|
@ -54,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
|||
db
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
|
||||
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
|
||||
let offset = range_or_offset.expect_offset();
|
||||
(db, FilePosition { file_id, offset })
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_range(ra_fixture: &str) -> (Self, FileRange) {
|
||||
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
|
||||
let range = range_or_offset.expect_range();
|
||||
(db, FileRange { file_id, range })
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
|
||||
let fixture = ChangeFixture::parse(ra_fixture);
|
||||
let mut db = Self::default();
|
||||
|
@ -100,9 +109,16 @@ impl ChangeFixture {
|
|||
|
||||
pub fn parse_with_proc_macros(
|
||||
ra_fixture: &str,
|
||||
mut proc_macros: Vec<(String, ProcMacro)>,
|
||||
mut proc_macro_defs: Vec<(String, ProcMacro)>,
|
||||
) -> ChangeFixture {
|
||||
let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
|
||||
let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
|
||||
FixtureWithProjectMeta::parse(ra_fixture);
|
||||
let toolchain = toolchain
|
||||
.map(|it| {
|
||||
ReleaseChannel::from_str(&it)
|
||||
.unwrap_or_else(|| panic!("unknown release channel found: {it}"))
|
||||
})
|
||||
.unwrap_or(ReleaseChannel::Stable);
|
||||
let mut change = Change::new();
|
||||
|
||||
let mut files = Vec::new();
|
||||
|
@ -157,16 +173,16 @@ impl ChangeFixture {
|
|||
meta.edition,
|
||||
Some(crate_name.clone().into()),
|
||||
version,
|
||||
meta.cfg.clone(),
|
||||
meta.cfg,
|
||||
Default::default(),
|
||||
meta.env,
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
origin,
|
||||
meta.target_data_layout
|
||||
.as_deref()
|
||||
.map(Arc::from)
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
Some(toolchain),
|
||||
);
|
||||
let prev = crates.insert(crate_name.clone(), crate_id);
|
||||
assert!(prev.is_none());
|
||||
|
@ -182,7 +198,7 @@ impl ChangeFixture {
|
|||
default_target_data_layout = meta.target_data_layout;
|
||||
}
|
||||
|
||||
change.change_file(file_id, Some(Arc::new(text)));
|
||||
change.change_file(file_id, Some(Arc::from(text)));
|
||||
let path = VfsPath::new_virtual_path(meta.path);
|
||||
file_set.insert(file_id, path);
|
||||
files.push(file_id);
|
||||
|
@ -197,15 +213,15 @@ impl ChangeFixture {
|
|||
Edition::CURRENT,
|
||||
Some(CrateName::new("test").unwrap().into()),
|
||||
None,
|
||||
default_cfg.clone(),
|
||||
default_cfg,
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
default_target_data_layout
|
||||
.map(|x| x.into())
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
Some(toolchain),
|
||||
);
|
||||
} else {
|
||||
for (from, to, prelude) in crate_deps {
|
||||
|
@ -232,7 +248,7 @@ impl ChangeFixture {
|
|||
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
|
||||
roots.push(SourceRoot::new_library(fs));
|
||||
|
||||
change.change_file(core_file, Some(Arc::new(mini_core.source_code())));
|
||||
change.change_file(core_file, Some(Arc::from(mini_core.source_code())));
|
||||
|
||||
let all_crates = crate_graph.crates_in_topological_order();
|
||||
|
||||
|
@ -241,13 +257,13 @@ impl ChangeFixture {
|
|||
Edition::Edition2021,
|
||||
Some(CrateDisplayName::from_canonical_name("core".to_string())),
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::Lang(LangCrateOrigin::Core),
|
||||
target_layout.clone(),
|
||||
Some(toolchain),
|
||||
);
|
||||
|
||||
for krate in all_crates {
|
||||
|
@ -257,12 +273,13 @@ impl ChangeFixture {
|
|||
}
|
||||
}
|
||||
|
||||
let mut proc_macros = ProcMacros::default();
|
||||
if !proc_macro_names.is_empty() {
|
||||
let proc_lib_file = file_id;
|
||||
file_id.0 += 1;
|
||||
|
||||
proc_macros.extend(default_test_proc_macros());
|
||||
let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros);
|
||||
proc_macro_defs.extend(default_test_proc_macros());
|
||||
let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
|
||||
let mut fs = FileSet::default();
|
||||
fs.insert(
|
||||
proc_lib_file,
|
||||
|
@ -270,7 +287,7 @@ impl ChangeFixture {
|
|||
);
|
||||
roots.push(SourceRoot::new_library(fs));
|
||||
|
||||
change.change_file(proc_lib_file, Some(Arc::new(source)));
|
||||
change.change_file(proc_lib_file, Some(Arc::from(source)));
|
||||
|
||||
let all_crates = crate_graph.crates_in_topological_order();
|
||||
|
||||
|
@ -279,14 +296,15 @@ impl ChangeFixture {
|
|||
Edition::Edition2021,
|
||||
Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())),
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(proc_macro),
|
||||
true,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
target_layout,
|
||||
Some(toolchain),
|
||||
);
|
||||
proc_macros.insert(proc_macros_crate, Ok(proc_macro));
|
||||
|
||||
for krate in all_crates {
|
||||
crate_graph
|
||||
|
@ -305,6 +323,7 @@ impl ChangeFixture {
|
|||
roots.push(root);
|
||||
change.set_roots(roots);
|
||||
change.set_crate_graph(crate_graph);
|
||||
change.set_proc_macros(proc_macros);
|
||||
|
||||
ChangeFixture { file_position, files, change }
|
||||
}
|
||||
|
@ -323,7 +342,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
ProcMacro {
|
||||
name: "identity".into(),
|
||||
kind: crate::ProcMacroKind::Attr,
|
||||
expander: Arc::new(IdentityProcMacroExpander),
|
||||
expander: sync::Arc::new(IdentityProcMacroExpander),
|
||||
},
|
||||
),
|
||||
(
|
||||
|
@ -337,7 +356,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream {
|
|||
ProcMacro {
|
||||
name: "DeriveIdentity".into(),
|
||||
kind: crate::ProcMacroKind::CustomDerive,
|
||||
expander: Arc::new(IdentityProcMacroExpander),
|
||||
expander: sync::Arc::new(IdentityProcMacroExpander),
|
||||
},
|
||||
),
|
||||
(
|
||||
|
@ -351,7 +370,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
|
|||
ProcMacro {
|
||||
name: "input_replace".into(),
|
||||
kind: crate::ProcMacroKind::Attr,
|
||||
expander: Arc::new(AttributeInputReplaceProcMacroExpander),
|
||||
expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
|
||||
},
|
||||
),
|
||||
(
|
||||
|
@ -365,7 +384,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
|
|||
ProcMacro {
|
||||
name: "mirror".into(),
|
||||
kind: crate::ProcMacroKind::FuncLike,
|
||||
expander: Arc::new(MirrorProcMacroExpander),
|
||||
expander: sync::Arc::new(MirrorProcMacroExpander),
|
||||
},
|
||||
),
|
||||
(
|
||||
|
@ -379,7 +398,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
|
|||
ProcMacro {
|
||||
name: "shorten".into(),
|
||||
kind: crate::ProcMacroKind::FuncLike,
|
||||
expander: Arc::new(ShortenProcMacroExpander),
|
||||
expander: sync::Arc::new(ShortenProcMacroExpander),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
@ -428,7 +447,7 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
|
|||
let (version, origin) = match b.split_once(':') {
|
||||
Some(("CratesIo", data)) => match data.split_once(',') {
|
||||
Some((version, url)) => {
|
||||
(version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None })
|
||||
(version, CrateOrigin::Local { repo: Some(url.to_owned()), name: None })
|
||||
}
|
||||
_ => panic!("Bad crates.io parameter: {data}"),
|
||||
},
|
||||
|
@ -436,10 +455,9 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
|
|||
};
|
||||
(a.to_owned(), origin, Some(version.to_string()))
|
||||
} else {
|
||||
let crate_origin = match &*crate_str {
|
||||
"std" => CrateOrigin::Lang(LangCrateOrigin::Std),
|
||||
"core" => CrateOrigin::Lang(LangCrateOrigin::Core),
|
||||
_ => CrateOrigin::CratesIo { repo: None, name: None },
|
||||
let crate_origin = match LangCrateOrigin::from(&*crate_str) {
|
||||
LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None },
|
||||
origin => CrateOrigin::Lang(origin),
|
||||
};
|
||||
(crate_str, crate_origin, None)
|
||||
}
|
||||
|
|
|
@ -6,14 +6,20 @@
|
|||
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
|
||||
//! actual IO is done and lowered to input.
|
||||
|
||||
use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
|
||||
use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync};
|
||||
|
||||
use cfg::CfgOptions;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stdx::hash::{NoHashHashMap, NoHashHashSet};
|
||||
use la_arena::{Arena, Idx};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syntax::SmolStr;
|
||||
use triomphe::Arc;
|
||||
use tt::token_id::Subtree;
|
||||
use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath};
|
||||
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
|
||||
|
||||
// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
|
||||
// then the crate for the proc-macro hasn't been build yet as the build data is missing.
|
||||
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
|
||||
pub type ProcMacros = FxHashMap<CrateId, ProcMacroLoadResult>;
|
||||
|
||||
/// Files are grouped into source roots. A source root is a directory on the
|
||||
/// file systems which is watched for changes. Typically it corresponds to a
|
||||
|
@ -79,17 +85,22 @@ impl SourceRoot {
|
|||
///
|
||||
/// `CrateGraph` is `!Serialize` by design, see
|
||||
/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
|
||||
#[derive(Debug, Clone, Default /* Serialize, Deserialize */)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CrateGraph {
|
||||
arena: NoHashHashMap<CrateId, CrateData>,
|
||||
arena: Arena<CrateData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CrateId(pub u32);
|
||||
impl fmt::Debug for CrateGraph {
|
||||
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)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl stdx::hash::NoHashHashable for CrateId {}
|
||||
pub type CrateId = Idx<CrateData>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct CrateName(SmolStr);
|
||||
|
||||
impl CrateName {
|
||||
|
@ -130,8 +141,12 @@ impl ops::Deref for CrateName {
|
|||
/// Origin of the crates. It is used in emitting monikers.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum CrateOrigin {
|
||||
/// Crates that are from crates.io official registry,
|
||||
CratesIo { repo: Option<String>, name: Option<String> },
|
||||
/// Crates that are from the rustc workspace
|
||||
Rustc { name: String },
|
||||
/// Crates that are workspace members,
|
||||
Local { repo: Option<String>, name: Option<String> },
|
||||
/// Crates that are non member libraries.
|
||||
Library { repo: Option<String>, name: String },
|
||||
/// Crates that are provided by the language, like std, core, proc-macro, ...
|
||||
Lang(LangCrateOrigin),
|
||||
}
|
||||
|
@ -173,7 +188,7 @@ impl fmt::Display for LangCrateOrigin {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct CrateDisplayName {
|
||||
// The name we use to display various paths (with `_`).
|
||||
crate_name: CrateName,
|
||||
|
@ -249,10 +264,36 @@ pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
|
|||
pub struct ProcMacro {
|
||||
pub name: SmolStr,
|
||||
pub kind: ProcMacroKind,
|
||||
pub expander: Arc<dyn ProcMacroExpander>,
|
||||
pub expander: sync::Arc<dyn ProcMacroExpander>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum ReleaseChannel {
|
||||
Stable,
|
||||
Beta,
|
||||
Nightly,
|
||||
}
|
||||
|
||||
impl ReleaseChannel {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
ReleaseChannel::Stable => "stable",
|
||||
ReleaseChannel::Beta => "beta",
|
||||
ReleaseChannel::Nightly => "nightly",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(str: &str) -> Option<Self> {
|
||||
Some(match str {
|
||||
"" => ReleaseChannel::Stable,
|
||||
"nightly" => ReleaseChannel::Nightly,
|
||||
_ if str.starts_with("beta") => ReleaseChannel::Beta,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CrateData {
|
||||
pub root_file_id: FileId,
|
||||
pub edition: Edition,
|
||||
|
@ -265,13 +306,15 @@ pub struct CrateData {
|
|||
/// `Dependency` matters), this name should only be used for UI.
|
||||
pub display_name: Option<CrateDisplayName>,
|
||||
pub cfg_options: CfgOptions,
|
||||
pub potential_cfg_options: CfgOptions,
|
||||
pub target_layout: TargetLayoutLoadResult,
|
||||
/// The cfg options that could be used by the crate
|
||||
pub potential_cfg_options: Option<CfgOptions>,
|
||||
pub env: Env,
|
||||
pub dependencies: Vec<Dependency>,
|
||||
pub proc_macro: ProcMacroLoadResult,
|
||||
pub origin: CrateOrigin,
|
||||
pub is_proc_macro: bool,
|
||||
// FIXME: These things should not be per crate! These are more per workspace crate graph level things
|
||||
pub target_layout: TargetLayoutLoadResult,
|
||||
pub channel: Option<ReleaseChannel>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -290,7 +333,7 @@ pub struct Env {
|
|||
entries: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Dependency {
|
||||
pub crate_id: CrateId,
|
||||
pub name: CrateName,
|
||||
|
@ -320,12 +363,12 @@ impl CrateGraph {
|
|||
display_name: Option<CrateDisplayName>,
|
||||
version: Option<String>,
|
||||
cfg_options: CfgOptions,
|
||||
potential_cfg_options: CfgOptions,
|
||||
potential_cfg_options: Option<CfgOptions>,
|
||||
env: Env,
|
||||
proc_macro: ProcMacroLoadResult,
|
||||
is_proc_macro: bool,
|
||||
origin: CrateOrigin,
|
||||
target_layout: Result<Arc<str>, Arc<str>>,
|
||||
channel: Option<ReleaseChannel>,
|
||||
) -> CrateId {
|
||||
let data = CrateData {
|
||||
root_file_id,
|
||||
|
@ -335,16 +378,44 @@ impl CrateGraph {
|
|||
cfg_options,
|
||||
potential_cfg_options,
|
||||
env,
|
||||
proc_macro,
|
||||
dependencies: Vec::new(),
|
||||
origin,
|
||||
target_layout,
|
||||
is_proc_macro,
|
||||
channel,
|
||||
};
|
||||
let crate_id = CrateId(self.arena.len() as u32);
|
||||
let prev = self.arena.insert(crate_id, data);
|
||||
assert!(prev.is_none());
|
||||
crate_id
|
||||
self.arena.alloc(data)
|
||||
}
|
||||
|
||||
/// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced
|
||||
/// with the second input.
|
||||
pub fn remove_and_replace(
|
||||
&mut self,
|
||||
id: CrateId,
|
||||
replace_with: CrateId,
|
||||
) -> Result<(), CyclicDependenciesError> {
|
||||
for (x, data) in self.arena.iter() {
|
||||
if x == id {
|
||||
continue;
|
||||
}
|
||||
for edge in &data.dependencies {
|
||||
if edge.crate_id == id {
|
||||
self.check_cycle_after_dependency(edge.crate_id, replace_with)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if everything was ok, start to replace
|
||||
for (x, data) in self.arena.iter_mut() {
|
||||
if x == id {
|
||||
continue;
|
||||
}
|
||||
for edge in &mut data.dependencies {
|
||||
if edge.crate_id == id {
|
||||
edge.crate_id = replace_with;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_dep(
|
||||
|
@ -354,17 +425,26 @@ impl CrateGraph {
|
|||
) -> Result<(), CyclicDependenciesError> {
|
||||
let _p = profile::span("add_dep");
|
||||
|
||||
// Check if adding a dep from `from` to `to` creates a cycle. To figure
|
||||
// that out, look for a path in the *opposite* direction, from `to` to
|
||||
// `from`.
|
||||
if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) {
|
||||
self.check_cycle_after_dependency(from, dep.crate_id)?;
|
||||
|
||||
self.arena[from].add_dep(dep);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if adding a dep from `from` to `to` creates a cycle. To figure
|
||||
/// that out, look for a path in the *opposite* direction, from `to` to
|
||||
/// `from`.
|
||||
fn check_cycle_after_dependency(
|
||||
&self,
|
||||
from: CrateId,
|
||||
to: CrateId,
|
||||
) -> Result<(), CyclicDependenciesError> {
|
||||
if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
|
||||
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
|
||||
let err = CyclicDependenciesError { path };
|
||||
assert!(err.from().0 == from && err.to().0 == dep.crate_id);
|
||||
assert!(err.from().0 == from && err.to().0 == to);
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
self.arena.get_mut(&from).unwrap().add_dep(dep);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -373,14 +453,14 @@ impl CrateGraph {
|
|||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
|
||||
self.arena.keys().copied()
|
||||
self.arena.iter().map(|(idx, _)| idx)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let mut worklist = vec![of];
|
||||
let mut deps = NoHashHashSet::default();
|
||||
let mut deps = FxHashSet::default();
|
||||
|
||||
while let Some(krate) = worklist.pop() {
|
||||
if !deps.insert(krate) {
|
||||
|
@ -397,11 +477,11 @@ impl CrateGraph {
|
|||
/// 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 = NoHashHashSet::default();
|
||||
let mut rev_deps = FxHashSet::default();
|
||||
rev_deps.insert(of);
|
||||
|
||||
let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default();
|
||||
self.arena.iter().for_each(|(&krate, data)| {
|
||||
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))
|
||||
|
@ -424,9 +504,9 @@ impl CrateGraph {
|
|||
/// come before the crate itself).
|
||||
pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
|
||||
let mut res = Vec::new();
|
||||
let mut visited = NoHashHashSet::default();
|
||||
let mut visited = FxHashSet::default();
|
||||
|
||||
for krate in self.arena.keys().copied() {
|
||||
for krate in self.iter() {
|
||||
go(self, &mut visited, &mut res, krate);
|
||||
}
|
||||
|
||||
|
@ -434,7 +514,7 @@ impl CrateGraph {
|
|||
|
||||
fn go(
|
||||
graph: &CrateGraph,
|
||||
visited: &mut NoHashHashSet<CrateId>,
|
||||
visited: &mut FxHashSet<CrateId>,
|
||||
res: &mut Vec<CrateId>,
|
||||
source: CrateId,
|
||||
) {
|
||||
|
@ -450,31 +530,56 @@ impl CrateGraph {
|
|||
|
||||
// FIXME: this only finds one crate with the given root; we could have multiple
|
||||
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
|
||||
let (&crate_id, _) =
|
||||
let (crate_id, _) =
|
||||
self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?;
|
||||
Some(crate_id)
|
||||
}
|
||||
|
||||
pub fn sort_deps(&mut self) {
|
||||
self.arena
|
||||
.iter_mut()
|
||||
.for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
|
||||
}
|
||||
|
||||
/// Extends this crate graph by adding a complete disjoint second crate
|
||||
/// graph.
|
||||
/// graph and adjust the ids in the [`ProcMacroPaths`] accordingly.
|
||||
///
|
||||
/// The ids of the crates in the `other` graph are shifted by the return
|
||||
/// amount.
|
||||
pub fn extend(&mut self, other: CrateGraph) -> u32 {
|
||||
let start = self.arena.len() as u32;
|
||||
self.arena.extend(other.arena.into_iter().map(|(id, mut data)| {
|
||||
let new_id = id.shift(start);
|
||||
for dep in &mut data.dependencies {
|
||||
dep.crate_id = dep.crate_id.shift(start);
|
||||
/// This will deduplicate the crates of the graph where possible.
|
||||
/// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
|
||||
/// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
|
||||
pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
|
||||
let topo = other.crates_in_topological_order();
|
||||
let mut id_map: FxHashMap<CrateId, CrateId> = 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);
|
||||
|
||||
let res = self.arena.iter().find_map(
|
||||
|(id, data)| {
|
||||
if data == crate_data {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
);
|
||||
if let Some(res) = res {
|
||||
id_map.insert(topo, res);
|
||||
} else {
|
||||
let id = self.arena.alloc(crate_data.clone());
|
||||
id_map.insert(topo, id);
|
||||
}
|
||||
(new_id, data)
|
||||
}));
|
||||
start
|
||||
}
|
||||
|
||||
*proc_macros =
|
||||
mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect();
|
||||
}
|
||||
|
||||
fn find_path(
|
||||
&self,
|
||||
visited: &mut NoHashHashSet<CrateId>,
|
||||
visited: &mut FxHashSet<CrateId>,
|
||||
from: CrateId,
|
||||
to: CrateId,
|
||||
) -> Option<Vec<CrateId>> {
|
||||
|
@ -500,14 +605,14 @@ impl CrateGraph {
|
|||
// Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
|
||||
// As hacky as it gets.
|
||||
pub fn patch_cfg_if(&mut self) -> bool {
|
||||
let cfg_if = self.hacky_find_crate("cfg_if");
|
||||
let std = self.hacky_find_crate("std");
|
||||
// we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works
|
||||
let cfg_if =
|
||||
self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone());
|
||||
let std = self.hacky_find_crate("std").next();
|
||||
match (cfg_if, std) {
|
||||
(Some(cfg_if), Some(std)) => {
|
||||
self.arena.get_mut(&cfg_if).unwrap().dependencies.clear();
|
||||
self.arena
|
||||
.get_mut(&std)
|
||||
.unwrap()
|
||||
self.arena[cfg_if].dependencies.clear();
|
||||
self.arena[std]
|
||||
.dependencies
|
||||
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
|
||||
true
|
||||
|
@ -516,21 +621,15 @@ impl CrateGraph {
|
|||
}
|
||||
}
|
||||
|
||||
fn hacky_find_crate(&self, display_name: &str) -> Option<CrateId> {
|
||||
self.iter().find(|it| self[*it].display_name.as_deref() == Some(display_name))
|
||||
fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
|
||||
self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<CrateId> for CrateGraph {
|
||||
type Output = CrateData;
|
||||
fn index(&self, crate_id: CrateId) -> &CrateData {
|
||||
&self.arena[&crate_id]
|
||||
}
|
||||
}
|
||||
|
||||
impl CrateId {
|
||||
fn shift(self, amount: u32) -> CrateId {
|
||||
CrateId(self.0 + amount)
|
||||
&self.arena[crate_id]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,7 +731,7 @@ impl fmt::Display for CyclicDependenciesError {
|
|||
mod tests {
|
||||
use crate::CrateOrigin;
|
||||
|
||||
use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
|
||||
use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
|
||||
|
||||
#[test]
|
||||
fn detect_cyclic_dependency_indirect() {
|
||||
|
@ -642,39 +741,39 @@ mod tests {
|
|||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId(3u32),
|
||||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
|
||||
|
@ -695,26 +794,26 @@ mod tests {
|
|||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
|
||||
|
@ -732,39 +831,39 @@ mod tests {
|
|||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId(3u32),
|
||||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
|
||||
|
@ -782,26 +881,26 @@ mod tests {
|
|||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
Edition2018,
|
||||
None,
|
||||
None,
|
||||
CfgOptions::default(),
|
||||
CfgOptions::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Env::default(),
|
||||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
Err("".into()),
|
||||
None,
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(
|
||||
|
|
|
@ -6,18 +6,19 @@ mod input;
|
|||
mod change;
|
||||
pub mod fixture;
|
||||
|
||||
use std::{panic, sync::Arc};
|
||||
use std::panic;
|
||||
|
||||
use stdx::hash::NoHashHashSet;
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
|
||||
use triomphe::Arc;
|
||||
|
||||
pub use crate::{
|
||||
change::Change,
|
||||
input::{
|
||||
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
|
||||
Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
|
||||
ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId,
|
||||
TargetLayoutLoadResult,
|
||||
ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros,
|
||||
ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
|
||||
},
|
||||
};
|
||||
pub use salsa::{self, Cancelled};
|
||||
|
@ -53,13 +54,13 @@ pub struct FileRange {
|
|||
pub range: TextRange,
|
||||
}
|
||||
|
||||
pub const DEFAULT_LRU_CAP: usize = 128;
|
||||
pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
|
||||
|
||||
pub trait FileLoader {
|
||||
/// Text of the file.
|
||||
fn file_text(&self, file_id: FileId) -> Arc<String>;
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>>;
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
|
||||
}
|
||||
|
||||
/// Database which stores all significant input facts: source code and project
|
||||
|
@ -73,6 +74,10 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug {
|
|||
/// The crate graph.
|
||||
#[salsa::input]
|
||||
fn crate_graph(&self) -> Arc<CrateGraph>;
|
||||
|
||||
/// The crate graph.
|
||||
#[salsa::input]
|
||||
fn proc_macros(&self) -> Arc<ProcMacros>;
|
||||
}
|
||||
|
||||
fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
||||
|
@ -86,7 +91,7 @@ fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFil
|
|||
#[salsa::query_group(SourceDatabaseExtStorage)]
|
||||
pub trait SourceDatabaseExt: SourceDatabase {
|
||||
#[salsa::input]
|
||||
fn file_text(&self, file_id: FileId) -> Arc<String>;
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
/// Path to a file, relative to the root of its source root.
|
||||
/// Source root of the file.
|
||||
#[salsa::input]
|
||||
|
@ -95,10 +100,10 @@ pub trait SourceDatabaseExt: SourceDatabase {
|
|||
#[salsa::input]
|
||||
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
|
||||
|
||||
fn source_root_crates(&self, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>>;
|
||||
fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
|
||||
}
|
||||
|
||||
fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>> {
|
||||
fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> {
|
||||
let graph = db.crate_graph();
|
||||
let res = graph
|
||||
.iter()
|
||||
|
@ -114,7 +119,7 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHas
|
|||
pub struct FileLoaderDelegate<T>(pub T);
|
||||
|
||||
impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
|
||||
fn file_text(&self, file_id: FileId) -> Arc<String> {
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str> {
|
||||
SourceDatabaseExt::file_text(self.0, file_id)
|
||||
}
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
|
@ -124,7 +129,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
|
|||
source_root.resolve_path(path)
|
||||
}
|
||||
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
|
||||
let _p = profile::span("relevant_crates");
|
||||
let source_root = self.0.file_source_root(file_id);
|
||||
self.0.source_root_crates(source_root)
|
||||
|
|
|
@ -86,7 +86,7 @@ impl CfgOptions {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Default, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CfgDiff {
|
||||
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
|
||||
enable: Vec<CfgAtom>,
|
||||
|
|
|
@ -16,9 +16,8 @@ crossbeam-channel = "0.5.5"
|
|||
tracing = "0.1.37"
|
||||
cargo_metadata = "0.15.0"
|
||||
rustc-hash = "1.1.0"
|
||||
serde = { version = "1.0.137", features = ["derive"] }
|
||||
serde_json = "1.0.86"
|
||||
jod-thread = "0.1.2"
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
command-group = "2.0.1"
|
||||
|
||||
# local deps
|
||||
|
|
|
@ -77,7 +77,7 @@ impl fmt::Display for FlycheckConfig {
|
|||
pub struct FlycheckHandle {
|
||||
// XXX: drop order is significant
|
||||
sender: Sender<StateChange>,
|
||||
_thread: jod_thread::JoinHandle,
|
||||
_thread: stdx::thread::JoinHandle,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ impl FlycheckHandle {
|
|||
) -> FlycheckHandle {
|
||||
let actor = FlycheckActor::new(id, sender, config, workspace_root);
|
||||
let (sender, receiver) = unbounded::<StateChange>();
|
||||
let thread = jod_thread::Builder::new()
|
||||
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
||||
.name("Flycheck".to_owned())
|
||||
.spawn(move || actor.run(receiver))
|
||||
.expect("failed to spawn thread");
|
||||
|
@ -395,7 +395,7 @@ struct CargoHandle {
|
|||
/// The handle to the actual cargo process. As we cannot cancel directly from with
|
||||
/// a read syscall dropping and therefore terminating the process is our best option.
|
||||
child: JodGroupChild,
|
||||
thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
|
||||
thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
|
||||
receiver: Receiver<CargoMessage>,
|
||||
}
|
||||
|
||||
|
@ -409,7 +409,7 @@ impl CargoHandle {
|
|||
|
||||
let (sender, receiver) = unbounded();
|
||||
let actor = CargoActor::new(sender, stdout, stderr);
|
||||
let thread = jod_thread::Builder::new()
|
||||
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
||||
.name("CargoHandle".to_owned())
|
||||
.spawn(move || actor.run())
|
||||
.expect("failed to spawn thread");
|
||||
|
@ -485,7 +485,7 @@ impl CargoActor {
|
|||
|
||||
error.push_str(line);
|
||||
error.push('\n');
|
||||
return false;
|
||||
false
|
||||
};
|
||||
let output = streaming_output(
|
||||
self.stdout,
|
||||
|
|
|
@ -14,7 +14,7 @@ doctest = false
|
|||
[dependencies]
|
||||
anymap = "1.0.0-beta.2"
|
||||
arrayvec = "0.7.2"
|
||||
bitflags = "1.3.2"
|
||||
bitflags = "2.1.0"
|
||||
cov-mark = "2.0.0-pre.1"
|
||||
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
||||
dashmap = { version = "=5.4.0", features = ["raw-api"] }
|
||||
|
@ -29,6 +29,7 @@ once_cell = "1.17.0"
|
|||
rustc-hash = "1.1.0"
|
||||
smallvec.workspace = true
|
||||
tracing = "0.1.35"
|
||||
triomphe.workspace = true
|
||||
|
||||
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
|
||||
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
use std::{hash::Hash, ops, sync::Arc};
|
||||
pub mod builtin;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{hash::Hash, ops};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
|
@ -16,14 +21,16 @@ use syntax::{
|
|||
ast::{self, HasAttrs, IsString},
|
||||
AstPtr, AstToken, SmolStr, TextRange, TextSize,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
|
||||
lang_item::LangItem,
|
||||
nameres::{ModuleOrigin, ModuleSource},
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
|
||||
VariantId,
|
||||
AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
|
||||
LocalFieldId, Lookup, MacroId, VariantId,
|
||||
};
|
||||
|
||||
/// Holds documentation
|
||||
|
@ -88,6 +95,7 @@ impl Attrs {
|
|||
db: &dyn DefDatabase,
|
||||
e: EnumId,
|
||||
) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
|
||||
let _p = profile::span("variants_attrs_query");
|
||||
// FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids
|
||||
let mut res = ArenaMap::default();
|
||||
|
||||
|
@ -114,6 +122,7 @@ impl Attrs {
|
|||
db: &dyn DefDatabase,
|
||||
v: VariantId,
|
||||
) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
|
||||
let _p = profile::span("fields_attrs_query");
|
||||
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
|
||||
let mut res = ArenaMap::default();
|
||||
|
||||
|
@ -175,13 +184,13 @@ impl Attrs {
|
|||
|
||||
Arc::new(res)
|
||||
}
|
||||
|
||||
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
|
||||
AttrQuery { attrs: self, key }
|
||||
}
|
||||
}
|
||||
|
||||
impl Attrs {
|
||||
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
|
||||
AttrQuery { attrs: self, key }
|
||||
}
|
||||
|
||||
pub fn cfg(&self) -> Option<CfgExpr> {
|
||||
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
|
||||
let first = cfgs.next()?;
|
||||
|
@ -193,6 +202,7 @@ impl Attrs {
|
|||
None => Some(first),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
|
||||
match self.cfg() {
|
||||
None => true,
|
||||
|
@ -204,6 +214,10 @@ impl Attrs {
|
|||
self.by_key("lang").string_value()
|
||||
}
|
||||
|
||||
pub fn lang_item(&self) -> Option<LangItem> {
|
||||
self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
|
||||
}
|
||||
|
||||
pub fn docs(&self) -> Option<Documentation> {
|
||||
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
|
||||
let indent = doc_indent(self);
|
||||
|
@ -238,6 +252,14 @@ impl Attrs {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
|
||||
self.by_key("doc").tt_values().map(DocExpr::parse)
|
||||
}
|
||||
|
||||
pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
|
||||
self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
|
||||
}
|
||||
|
||||
pub fn is_proc_macro(&self) -> bool {
|
||||
self.by_key("proc_macro").exists()
|
||||
}
|
||||
|
@ -249,10 +271,120 @@ impl Attrs {
|
|||
pub fn is_proc_macro_derive(&self) -> bool {
|
||||
self.by_key("proc_macro_derive").exists()
|
||||
}
|
||||
|
||||
pub fn is_unstable(&self) -> bool {
|
||||
self.by_key("unstable").exists()
|
||||
}
|
||||
}
|
||||
|
||||
use std::slice::Iter as SliceIter;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub enum DocAtom {
|
||||
/// eg. `#[doc(hidden)]`
|
||||
Flag(SmolStr),
|
||||
/// eg. `#[doc(alias = "x")]`
|
||||
///
|
||||
/// Note that a key can have multiple values that are all considered "active" at the same time.
|
||||
/// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
|
||||
KeyValue { key: SmolStr, value: SmolStr },
|
||||
}
|
||||
|
||||
// Adapted from `CfgExpr` parsing code
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
|
||||
pub enum DocExpr {
|
||||
Invalid,
|
||||
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
|
||||
Atom(DocAtom),
|
||||
/// eg. `#[doc(alias("x", "y"))]`
|
||||
Alias(Vec<SmolStr>),
|
||||
}
|
||||
|
||||
impl From<DocAtom> for DocExpr {
|
||||
fn from(atom: DocAtom) -> Self {
|
||||
DocExpr::Atom(atom)
|
||||
}
|
||||
}
|
||||
|
||||
impl DocExpr {
|
||||
fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr {
|
||||
next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
|
||||
}
|
||||
|
||||
pub fn aliases(&self) -> &[SmolStr] {
|
||||
match self {
|
||||
DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
|
||||
std::slice::from_ref(value)
|
||||
}
|
||||
DocExpr::Alias(aliases) => aliases,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
|
||||
let name = match it.next() {
|
||||
None => return None,
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
|
||||
Some(_) => return Some(DocExpr::Invalid),
|
||||
};
|
||||
|
||||
// Peek
|
||||
let ret = match it.as_slice().first() {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
|
||||
match it.as_slice().get(1) {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||
it.next();
|
||||
it.next();
|
||||
// FIXME: escape? raw string?
|
||||
let value =
|
||||
SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
|
||||
DocAtom::KeyValue { key: name, value }.into()
|
||||
}
|
||||
_ => return Some(DocExpr::Invalid),
|
||||
}
|
||||
}
|
||||
Some(tt::TokenTree::Subtree(subtree)) => {
|
||||
it.next();
|
||||
let subs = parse_comma_sep(subtree);
|
||||
match name.as_str() {
|
||||
"alias" => DocExpr::Alias(subs),
|
||||
_ => DocExpr::Invalid,
|
||||
}
|
||||
}
|
||||
_ => DocAtom::Flag(name).into(),
|
||||
};
|
||||
|
||||
// Eat comma separator
|
||||
if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
|
||||
if punct.char == ',' {
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
|
||||
subtree
|
||||
.token_trees
|
||||
.iter()
|
||||
.filter_map(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||
// FIXME: escape? raw string?
|
||||
Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl AttrsWithOwner {
|
||||
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
|
||||
pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
|
||||
Self { attrs: db.attrs(owner), owner }
|
||||
}
|
||||
|
||||
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
|
||||
let _p = profile::span("attrs_query");
|
||||
// FIXME: this should use `Trace` to avoid duplication in `source_map` below
|
||||
let raw_attrs = match def {
|
||||
AttrDefId::ModuleId(module) => {
|
||||
|
@ -286,31 +418,29 @@ impl AttrsWithOwner {
|
|||
}
|
||||
}
|
||||
AttrDefId::FieldId(it) => {
|
||||
return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
|
||||
return db.fields_attrs(it.parent)[it.local_id].clone();
|
||||
}
|
||||
AttrDefId::EnumVariantId(it) => {
|
||||
return Self {
|
||||
attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
|
||||
owner: def,
|
||||
};
|
||||
return db.variants_attrs(it.parent)[it.local_id].clone();
|
||||
}
|
||||
// FIXME: DRY this up
|
||||
AttrDefId::AdtId(it) => match it {
|
||||
AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
|
||||
AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
|
||||
AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
|
||||
},
|
||||
AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
|
||||
AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
|
||||
AttrDefId::MacroId(it) => match it {
|
||||
MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
|
||||
MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
|
||||
MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
|
||||
},
|
||||
AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
|
||||
AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
|
||||
AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
|
||||
AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
|
||||
AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
|
||||
AttrDefId::GenericParamId(it) => match it {
|
||||
GenericParamId::ConstParamId(it) => {
|
||||
let src = it.parent().child_source(db);
|
||||
|
@ -331,11 +461,11 @@ impl AttrsWithOwner {
|
|||
RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id]))
|
||||
}
|
||||
},
|
||||
AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
|
||||
};
|
||||
|
||||
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
|
||||
Self { attrs: Attrs(attrs), owner: def }
|
||||
Attrs(attrs)
|
||||
}
|
||||
|
||||
pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
|
||||
|
@ -371,7 +501,7 @@ impl AttrsWithOwner {
|
|||
AttrDefId::FieldId(id) => {
|
||||
let map = db.fields_attrs_source_map(id.parent);
|
||||
let file_id = id.parent.file_id(db);
|
||||
let root = db.parse_or_expand(file_id).unwrap();
|
||||
let root = db.parse_or_expand(file_id);
|
||||
let owner = match &map[id.local_id] {
|
||||
Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
|
||||
Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
|
||||
|
@ -379,28 +509,28 @@ impl AttrsWithOwner {
|
|||
InFile::new(file_id, owner)
|
||||
}
|
||||
AttrDefId::AdtId(adt) => match adt {
|
||||
AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AdtId::StructId(id) => any_has_attrs(db, id),
|
||||
AdtId::UnionId(id) => any_has_attrs(db, id),
|
||||
AdtId::EnumId(id) => any_has_attrs(db, id),
|
||||
},
|
||||
AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::FunctionId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::EnumVariantId(id) => {
|
||||
let map = db.variants_attrs_source_map(id.parent);
|
||||
let file_id = id.parent.lookup(db).id.file_id();
|
||||
let root = db.parse_or_expand(file_id).unwrap();
|
||||
let root = db.parse_or_expand(file_id);
|
||||
InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)))
|
||||
}
|
||||
AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::StaticId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::ConstId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::TraitId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::MacroId(id) => match id {
|
||||
MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
MacroId::Macro2Id(id) => any_has_attrs(db, id),
|
||||
MacroId::MacroRulesId(id) => any_has_attrs(db, id),
|
||||
MacroId::ProcMacroId(id) => any_has_attrs(db, id),
|
||||
},
|
||||
AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::ImplId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::GenericParamId(id) => match id {
|
||||
GenericParamId::ConstParamId(id) => id
|
||||
.parent()
|
||||
|
@ -415,7 +545,7 @@ impl AttrsWithOwner {
|
|||
.child_source(db)
|
||||
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
|
||||
},
|
||||
AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
|
||||
AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
|
||||
};
|
||||
|
||||
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
|
||||
|
@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
|
|||
.nth(2);
|
||||
|
||||
match name {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text),
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
|
||||
fn any_has_attrs(
|
||||
db: &dyn DefDatabase,
|
||||
id: impl Lookup<Data = impl HasSource<Value = impl ast::HasAttrs>>,
|
||||
) -> InFile<ast::AnyHasAttrs> {
|
||||
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
|
||||
}
|
||||
|
||||
fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
|
||||
let tree = id.item_tree(db);
|
||||
let mod_item = N::id_to_mod_item(id.value);
|
||||
tree.raw_attrs(mod_item.into()).clone()
|
||||
}
|
||||
|
||||
fn attrs_from_item_tree_loc<N: ItemTreeNode>(
|
||||
db: &dyn DefDatabase,
|
||||
lookup: impl Lookup<Data = ItemLoc<N>>,
|
||||
) -> RawAttrs {
|
||||
let id = lookup.lookup(db).id;
|
||||
attrs_from_item_tree(db, id)
|
||||
}
|
||||
|
||||
fn attrs_from_item_tree_assoc<N: ItemTreeNode>(
|
||||
db: &dyn DefDatabase,
|
||||
lookup: impl Lookup<Data = AssocItemLoc<N>>,
|
||||
) -> RawAttrs {
|
||||
let id = lookup.lookup(db).id;
|
||||
attrs_from_item_tree(db, id)
|
||||
}
|
||||
|
||||
pub(crate) fn variants_attrs_source_map(
|
||||
db: &dyn DefDatabase,
|
||||
def: EnumId,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//!
|
||||
//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
|
||||
//!
|
||||
//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7.
|
||||
//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6.
|
||||
//!
|
||||
//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
|
||||
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
|
||||
|
@ -108,7 +108,7 @@ macro_rules! experimental {
|
|||
};
|
||||
}
|
||||
|
||||
/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
|
||||
/// Attributes that have a special meaning to rustc or rustdoc.
|
||||
#[rustfmt::skip]
|
||||
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
// ==========================================================================
|
||||
|
@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
|
||||
ungated!(
|
||||
should_panic, Normal,
|
||||
template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing,
|
||||
template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
|
||||
),
|
||||
// FIXME(Centril): This can be used on stable but shouldn't.
|
||||
ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
|
||||
|
@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
|
||||
// Lints:
|
||||
ungated!(
|
||||
warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
|
||||
warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
DuplicatesOk, @only_local: true,
|
||||
),
|
||||
ungated!(
|
||||
allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
|
||||
allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
DuplicatesOk, @only_local: true,
|
||||
),
|
||||
gated!(
|
||||
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
|
||||
lint_reasons, experimental!(expect)
|
||||
),
|
||||
ungated!(
|
||||
forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
|
||||
forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
DuplicatesOk, @only_local: true,
|
||||
),
|
||||
ungated!(
|
||||
deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
|
||||
deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
DuplicatesOk, @only_local: true,
|
||||
),
|
||||
ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
|
||||
gated!(
|
||||
|
@ -181,16 +185,17 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
// ABI, linking, symbols, and FFI
|
||||
ungated!(
|
||||
link, Normal,
|
||||
template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
|
||||
template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#),
|
||||
DuplicatesOk,
|
||||
),
|
||||
ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
|
||||
ungated!(no_link, Normal, template!(Word), WarnFollowing),
|
||||
ungated!(repr, Normal, template!(List: "C"), DuplicatesOk),
|
||||
ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true),
|
||||
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
|
||||
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
|
||||
ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
|
||||
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true),
|
||||
ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding),
|
||||
|
||||
// Limits:
|
||||
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
|
||||
|
@ -201,6 +206,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
),
|
||||
|
||||
// Entry point:
|
||||
gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
|
||||
ungated!(start, Normal, template!(Word), WarnFollowing),
|
||||
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
|
||||
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
|
||||
|
@ -222,11 +228,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
|
||||
ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
|
||||
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing),
|
||||
ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk),
|
||||
ungated!(
|
||||
target_feature, Normal, template!(List: r#"enable = "name""#),
|
||||
DuplicatesOk, @only_local: true,
|
||||
),
|
||||
ungated!(track_caller, Normal, template!(Word), WarnFollowing),
|
||||
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
|
||||
gated!(
|
||||
no_sanitize, Normal,
|
||||
template!(List: "address, memory, thread"), DuplicatesOk,
|
||||
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
|
||||
experimental!(no_sanitize)
|
||||
),
|
||||
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
|
||||
|
@ -235,25 +245,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
|
||||
),
|
||||
|
||||
// Debugging
|
||||
ungated!(
|
||||
debugger_visualizer, Normal,
|
||||
template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Unstable attributes:
|
||||
// ==========================================================================
|
||||
|
||||
// RFC #3191: #[debugger_visualizer] support
|
||||
gated!(
|
||||
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
|
||||
DuplicatesOk, experimental!(debugger_visualizer)
|
||||
),
|
||||
|
||||
// Linking:
|
||||
gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
|
||||
gated!(
|
||||
link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib,
|
||||
experimental!(link_ordinal)
|
||||
naked, Normal, template!(Word), WarnFollowing, @only_local: true,
|
||||
naked_functions, experimental!(naked)
|
||||
),
|
||||
|
||||
// Plugins:
|
||||
// XXX Modified for use in rust-analyzer
|
||||
// BuiltinAttribute {
|
||||
// name: sym::plugin,
|
||||
// only_local: false,
|
||||
|
@ -270,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
// cfg_fn!(plugin)
|
||||
// ),
|
||||
// },
|
||||
BuiltinAttribute {
|
||||
name: "plugin",
|
||||
template: template!(List: "name"),
|
||||
},
|
||||
|
||||
// Testing:
|
||||
gated!(
|
||||
|
@ -282,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
),
|
||||
// RFC #1268
|
||||
gated!(
|
||||
marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker)
|
||||
marker, Normal, template!(Word), WarnFollowing, @only_local: true,
|
||||
marker_trait_attr, experimental!(marker)
|
||||
),
|
||||
gated!(
|
||||
thread_local, Normal, template!(Word), WarnFollowing,
|
||||
|
@ -294,21 +299,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute,
|
||||
experimental!(optimize),
|
||||
),
|
||||
// RFC 2867
|
||||
gated!(
|
||||
instruction_set, Normal, template!(List: "set"), ErrorPreceding,
|
||||
isa_attribute, experimental!(instruction_set)
|
||||
),
|
||||
|
||||
gated!(
|
||||
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
|
||||
),
|
||||
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
|
||||
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
|
||||
gated!(
|
||||
register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
|
||||
experimental!(register_attr),
|
||||
),
|
||||
gated!(
|
||||
register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
|
||||
experimental!(register_tool),
|
||||
|
@ -321,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
// RFC 2632
|
||||
gated!(
|
||||
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
|
||||
"`const` is a temporary placeholder for marking a trait that is suitable for `const` \
|
||||
"`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
|
||||
`impls` and all default bodies as `const`, which may be removed or renamed in the \
|
||||
future."
|
||||
),
|
||||
|
@ -331,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
experimental!(deprecated_safe),
|
||||
),
|
||||
|
||||
// `#[collapse_debuginfo]`
|
||||
gated!(
|
||||
collapse_debuginfo, Normal, template!(Word), WarnFollowing,
|
||||
experimental!(collapse_debuginfo)
|
||||
),
|
||||
|
||||
// RFC 2397
|
||||
gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)),
|
||||
|
||||
// `#[cfi_encoding = ""]`
|
||||
gated!(
|
||||
cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
|
||||
experimental!(cfi_encoding)
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes: Stability, deprecation, and unsafe:
|
||||
// ==========================================================================
|
||||
|
||||
ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk),
|
||||
ungated!(
|
||||
feature, CrateLevel,
|
||||
template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true,
|
||||
),
|
||||
// DuplicatesOk since it has its own validation
|
||||
ungated!(
|
||||
stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk,
|
||||
stable, Normal,
|
||||
template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true,
|
||||
),
|
||||
ungated!(
|
||||
unstable, Normal,
|
||||
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
|
||||
),
|
||||
ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
|
||||
ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
|
||||
ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
|
||||
ungated!(
|
||||
rustc_const_stable, Normal,
|
||||
template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true,
|
||||
),
|
||||
ungated!(
|
||||
rustc_default_body_unstable, Normal,
|
||||
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk
|
||||
),
|
||||
gated!(
|
||||
allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
|
||||
"allow_internal_unstable side-steps feature gating and stability checks",
|
||||
|
@ -360,6 +381,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
|
||||
"allow_internal_unsafe side-steps the unsafe_code lint",
|
||||
),
|
||||
ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk),
|
||||
rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
|
||||
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
||||
through unstable paths"),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes: Type system related:
|
||||
|
@ -377,10 +402,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
|
||||
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
gated!(
|
||||
alloc_error_handler, Normal, template!(Word), WarnFollowing,
|
||||
experimental!(alloc_error_handler)
|
||||
),
|
||||
rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
gated!(
|
||||
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
|
||||
experimental!(default_lib_allocator),
|
||||
|
@ -461,6 +485,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
// Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
|
||||
// to assist in changes to diagnostic APIs.
|
||||
rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
|
||||
// Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
|
||||
// types (as well as any others in future).
|
||||
rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
|
||||
// Used by the `rustc::bad_opt_access` lint on fields
|
||||
// types (as well as any others in future).
|
||||
rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes, Const related:
|
||||
|
@ -504,18 +534,25 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
"language items are subject to change",
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_pass_by_value, Normal,
|
||||
template!(Word), ErrorFollowing,
|
||||
rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
|
||||
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true,
|
||||
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true,
|
||||
"#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
|
||||
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
|
||||
"#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
|
||||
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
|
||||
|
@ -527,24 +564,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
and it is only intended to be used in `alloc`."
|
||||
),
|
||||
|
||||
// modified for r-a
|
||||
// BuiltinAttribute {
|
||||
// name: sym::rustc_diagnostic_item,
|
||||
// // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
|
||||
// only_local: false,
|
||||
// type_: Normal,
|
||||
// template: template!(NameValueStr: "name"),
|
||||
// duplicates: ErrorFollowing,
|
||||
// gate: Gated(
|
||||
// Stability::Unstable,
|
||||
// sym::rustc_attrs,
|
||||
// "diagnostic items compiler internal support for linting",
|
||||
// cfg_fn!(rustc_attrs),
|
||||
// ),
|
||||
// },
|
||||
BuiltinAttribute {
|
||||
// name: sym::rustc_diagnostic_item,
|
||||
name: "rustc_diagnostic_item",
|
||||
// FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
|
||||
// only_local: false,
|
||||
// type_: Normal,
|
||||
template: template!(NameValueStr: "name"),
|
||||
// duplicates: ErrorFollowing,
|
||||
// gate: Gated(
|
||||
// Stability::Unstable,
|
||||
// sym::rustc_attrs,
|
||||
// "diagnostic items compiler internal support for linting",
|
||||
// cfg_fn!(rustc_attrs),
|
||||
// ),
|
||||
},
|
||||
gated!(
|
||||
// Used in resolve:
|
||||
|
@ -568,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
for reserving for `for<T> From<!> for T` impl"
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_test_marker, Normal, template!(Word), WarnFollowing,
|
||||
rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing,
|
||||
"the `#[rustc_test_marker]` attribute is used internally to track tests",
|
||||
),
|
||||
rustc_attr!(
|
||||
|
@ -594,11 +627,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
definition of a trait, it's currently in experimental form and should be changed before \
|
||||
being exposed outside of the std"
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
|
||||
r#"`rustc_doc_primitive` is a rustc internal attribute"#,
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes, Testing:
|
||||
// ==========================================================================
|
||||
|
||||
rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
|
||||
|
@ -639,6 +677,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk),
|
||||
gated!(
|
||||
custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#),
|
||||
ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite",
|
||||
),
|
||||
rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing),
|
||||
rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),
|
40
crates/hir-def/src/attr/tests.rs
Normal file
40
crates/hir-def/src/attr/tests.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
//! This module contains tests for doc-expression parsing.
|
||||
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
|
||||
|
||||
use mbe::syntax_node_to_token_tree;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
use crate::attr::{DocAtom, DocExpr};
|
||||
|
||||
fn assert_parse_result(input: &str, expected: DocExpr) {
|
||||
let (tt, _) = {
|
||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||
syntax_node_to_token_tree(tt.syntax())
|
||||
};
|
||||
let cfg = DocExpr::parse(&tt);
|
||||
assert_eq!(cfg, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_doc_expr_parser() {
|
||||
assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
|
||||
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias = "foo")]"#,
|
||||
DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
|
||||
);
|
||||
|
||||
assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias("foo", "bar", "baz"))]"#,
|
||||
DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
|
||||
);
|
||||
|
||||
assert_parse_result(
|
||||
r#"
|
||||
#[doc(alias("Bar", "Qux"))]
|
||||
struct Foo;"#,
|
||||
DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
|
||||
);
|
||||
}
|
|
@ -6,267 +6,30 @@ mod tests;
|
|||
pub mod scope;
|
||||
mod pretty;
|
||||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
use std::ops::Index;
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use drop_bomb::DropBomb;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
|
||||
};
|
||||
use hir_expand::{name::Name, HirFileId, InFile};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use limit::Limit;
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
expr::{
|
||||
expander::Expander,
|
||||
hir::{
|
||||
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
macro_id_to_def_id,
|
||||
nameres::DefMap,
|
||||
path::{ModPath, Path},
|
||||
src::{HasChildSource, HasSource},
|
||||
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
|
||||
UnresolvedMacro,
|
||||
BlockId, DefWithBodyId, HasModule, Lookup,
|
||||
};
|
||||
|
||||
pub use lower::LowerCtx;
|
||||
|
||||
/// A subset of Expander that only deals with cfg attributes. We only need it to
|
||||
/// avoid cyclic queries in crate def map during enum processing.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CfgExpander {
|
||||
cfg_options: CfgOptions,
|
||||
hygiene: Hygiene,
|
||||
krate: CrateId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Expander {
|
||||
cfg_expander: CfgExpander,
|
||||
def_map: Arc<DefMap>,
|
||||
current_file_id: HirFileId,
|
||||
module: LocalModuleId,
|
||||
/// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
|
||||
recursion_depth: usize,
|
||||
}
|
||||
|
||||
impl CfgExpander {
|
||||
pub(crate) fn new(
|
||||
db: &dyn DefDatabase,
|
||||
current_file_id: HirFileId,
|
||||
krate: CrateId,
|
||||
) -> CfgExpander {
|
||||
let hygiene = Hygiene::new(db.upcast(), current_file_id);
|
||||
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
|
||||
CfgExpander { cfg_options, hygiene, krate }
|
||||
}
|
||||
|
||||
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.hygiene))
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
|
||||
let attrs = self.parse_attrs(db, owner);
|
||||
attrs.is_cfg_enabled(&self.cfg_options)
|
||||
}
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
|
||||
let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
|
||||
let def_map = module.def_map(db);
|
||||
Expander {
|
||||
cfg_expander,
|
||||
def_map,
|
||||
current_file_id,
|
||||
module: module.local_id,
|
||||
recursion_depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_expand<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
macro_call: ast::MacroCall,
|
||||
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
|
||||
let mut unresolved_macro_err = None;
|
||||
|
||||
let result = self.within_limit(db, |this| {
|
||||
let macro_call = InFile::new(this.current_file_id, ¯o_call);
|
||||
|
||||
let resolver =
|
||||
|path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
|
||||
|
||||
let mut err = None;
|
||||
let call_id = match macro_call.as_call_id_with_errors(
|
||||
db,
|
||||
this.def_map.krate(),
|
||||
resolver,
|
||||
&mut |e| {
|
||||
err.get_or_insert(e);
|
||||
},
|
||||
) {
|
||||
Ok(call_id) => call_id,
|
||||
Err(resolve_err) => {
|
||||
unresolved_macro_err = Some(resolve_err);
|
||||
return ExpandResult { value: None, err: None };
|
||||
}
|
||||
};
|
||||
ExpandResult { value: call_id.ok(), err }
|
||||
});
|
||||
|
||||
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, T)>> {
|
||||
self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
|
||||
}
|
||||
|
||||
fn enter_expand_inner(
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
mut err: Option<ExpandError>,
|
||||
) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> {
|
||||
if err.is_none() {
|
||||
err = db.macro_expand_error(call_id);
|
||||
}
|
||||
|
||||
let file_id = call_id.as_file();
|
||||
|
||||
let raw_node = match db.parse_or_expand(file_id) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
// Only `None` if the macro expansion produced no usable AST.
|
||||
if err.is_none() {
|
||||
tracing::warn!("no error despite `parse_or_expand` failing");
|
||||
}
|
||||
|
||||
return ExpandResult::only_err(err.unwrap_or_else(|| {
|
||||
ExpandError::Other("failed to parse macro invocation".into())
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
ExpandResult { value: Some((file_id, raw_node)), err }
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
|
||||
self.current_file_id = mark.file_id;
|
||||
if self.recursion_depth == usize::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(crate) fn to_source<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 {
|
||||
self.cfg_expander.parse_attrs(db, owner)
|
||||
}
|
||||
|
||||
pub(crate) fn cfg_options(&self) -> &CfgOptions {
|
||||
&self.cfg_expander.cfg_options
|
||||
}
|
||||
|
||||
pub fn current_file_id(&self) -> HirFileId {
|
||||
self.current_file_id
|
||||
}
|
||||
|
||||
fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
|
||||
let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
|
||||
Path::from_src(path, &ctx)
|
||||
}
|
||||
|
||||
fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
|
||||
self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros()
|
||||
}
|
||||
|
||||
fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
|
||||
let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
|
||||
|
||||
#[cfg(not(test))]
|
||||
return Limit::new(limit);
|
||||
|
||||
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
|
||||
#[cfg(test)]
|
||||
return Limit::new(std::cmp::min(32, limit));
|
||||
}
|
||||
|
||||
fn within_limit<F, T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
op: F,
|
||||
) -> ExpandResult<Option<(Mark, T)>>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
|
||||
{
|
||||
if self.recursion_depth == usize::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::only_err(ExpandError::RecursionOverflowPosioned);
|
||||
} else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
|
||||
self.recursion_depth = usize::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
return ExpandResult::only_err(ExpandError::Other(
|
||||
"reached recursion limit during macro expansion".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let ExpandResult { value, err } = op(self);
|
||||
let Some(call_id) = value else {
|
||||
return ExpandResult { value: None, err };
|
||||
};
|
||||
|
||||
Self::enter_expand_inner(db, call_id, err).map(|value| {
|
||||
value.and_then(|(new_file_id, node)| {
|
||||
let node = T::cast(node)?;
|
||||
|
||||
self.recursion_depth += 1;
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id);
|
||||
let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id);
|
||||
let mark =
|
||||
Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
|
||||
Some((mark, node))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mark {
|
||||
file_id: HirFileId,
|
||||
bomb: DropBomb,
|
||||
}
|
||||
|
||||
/// The body of an item (function, const etc.).
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Body {
|
||||
|
@ -343,6 +106,8 @@ pub enum BodyDiagnostic {
|
|||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
|
||||
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
|
||||
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
|
||||
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
}
|
||||
|
||||
impl Body {
|
||||
|
@ -353,45 +118,54 @@ impl Body {
|
|||
let _p = profile::span("body_with_source_map_query");
|
||||
let mut params = None;
|
||||
|
||||
let (file_id, module, body) = match def {
|
||||
DefWithBodyId::FunctionId(f) => {
|
||||
let f = f.lookup(db);
|
||||
let src = f.source(db);
|
||||
params = src.value.param_list().map(|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();
|
||||
let (file_id, module, body, is_async_fn) = {
|
||||
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(|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,
|
||||
func.params.clone().map(move |param| {
|
||||
item_tree
|
||||
.attrs(db, krate, param.into())
|
||||
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
||||
}),
|
||||
)
|
||||
});
|
||||
(
|
||||
param_list,
|
||||
func.params.clone().map(move |param| {
|
||||
item_tree
|
||||
.attrs(db, krate, param.into())
|
||||
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
||||
}),
|
||||
src.file_id,
|
||||
f.module(db),
|
||||
src.value.body().map(ast::Expr::from),
|
||||
data.has_async_kw(),
|
||||
)
|
||||
});
|
||||
(src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
|
||||
}
|
||||
DefWithBodyId::ConstId(c) => {
|
||||
let c = c.lookup(db);
|
||||
let src = c.source(db);
|
||||
(src.file_id, c.module(db), src.value.body())
|
||||
}
|
||||
DefWithBodyId::StaticId(s) => {
|
||||
let s = s.lookup(db);
|
||||
let src = s.source(db);
|
||||
(src.file_id, s.module(db), src.value.body())
|
||||
}
|
||||
DefWithBodyId::VariantId(v) => {
|
||||
let e = v.parent.lookup(db);
|
||||
let src = v.parent.child_source(db);
|
||||
let variant = &src.value[v.local_id];
|
||||
(src.file_id, e.container, variant.expr())
|
||||
}
|
||||
DefWithBodyId::ConstId(c) => {
|
||||
let c = c.lookup(db);
|
||||
let src = c.source(db);
|
||||
(src.file_id, c.module(db), src.value.body(), false)
|
||||
}
|
||||
DefWithBodyId::StaticId(s) => {
|
||||
let s = s.lookup(db);
|
||||
let src = s.source(db);
|
||||
(src.file_id, s.module(db), src.value.body(), false)
|
||||
}
|
||||
DefWithBodyId::VariantId(v) => {
|
||||
let e = v.parent.lookup(db);
|
||||
let src = v.parent.child_source(db);
|
||||
let variant = &src.value[v.local_id];
|
||||
(src.file_id, e.container, variant.expr(), false)
|
||||
}
|
||||
}
|
||||
};
|
||||
let expander = Expander::new(db, file_id, module);
|
||||
let (mut body, source_map) = Body::new(db, expander, params, body);
|
||||
let (mut body, source_map) =
|
||||
Body::new(db, def, expander, params, body, module.krate, is_async_fn);
|
||||
body.shrink_to_fit();
|
||||
|
||||
(Arc::new(body), Arc::new(source_map))
|
||||
|
@ -406,22 +180,32 @@ impl Body {
|
|||
&'a self,
|
||||
db: &'a dyn DefDatabase,
|
||||
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
|
||||
self.block_scopes
|
||||
.iter()
|
||||
.map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
|
||||
self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
|
||||
pretty::print_body_hir(db, self, owner)
|
||||
}
|
||||
|
||||
pub fn pretty_print_expr(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
owner: DefWithBodyId,
|
||||
expr: ExprId,
|
||||
) -> String {
|
||||
pretty::print_expr_hir(db, self, owner, expr)
|
||||
}
|
||||
|
||||
fn new(
|
||||
db: &dyn DefDatabase,
|
||||
owner: DefWithBodyId,
|
||||
expander: Expander,
|
||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||
body: Option<ast::Expr>,
|
||||
krate: CrateId,
|
||||
is_async_fn: bool,
|
||||
) -> (Body, BodySourceMap) {
|
||||
lower::lower(db, expander, params, body)
|
||||
lower::lower(db, owner, expander, params, body, krate, is_async_fn)
|
||||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
|
@ -437,15 +221,14 @@ impl Body {
|
|||
|
||||
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
|
||||
self.walk_pats(pat_id, &mut |pat| {
|
||||
if let Pat::Bind { id, .. } = pat {
|
||||
if let Pat::Bind { id, .. } = &self[pat] {
|
||||
f(*id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
|
||||
pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
|
||||
let pat = &self[pat_id];
|
||||
f(pat);
|
||||
match pat {
|
||||
Pat::Range { .. }
|
||||
| Pat::Lit(..)
|
||||
|
@ -455,23 +238,28 @@ impl Body {
|
|||
| Pat::Missing => {}
|
||||
&Pat::Bind { subpat, .. } => {
|
||||
if let Some(subpat) = subpat {
|
||||
self.walk_pats(subpat, f);
|
||||
f(subpat);
|
||||
}
|
||||
}
|
||||
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
|
||||
args.iter().copied().for_each(|p| self.walk_pats(p, f));
|
||||
args.iter().copied().for_each(|p| f(p));
|
||||
}
|
||||
Pat::Ref { pat, .. } => self.walk_pats(*pat, f),
|
||||
Pat::Ref { pat, .. } => f(*pat),
|
||||
Pat::Slice { prefix, slice, suffix } => {
|
||||
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
|
||||
total_iter.copied().for_each(|p| self.walk_pats(p, f));
|
||||
total_iter.copied().for_each(|p| f(p));
|
||||
}
|
||||
Pat::Record { args, .. } => {
|
||||
args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f));
|
||||
args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
|
||||
}
|
||||
Pat::Box { inner } => self.walk_pats(*inner, f),
|
||||
Pat::Box { inner } => f(*inner),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
|
||||
f(pat_id);
|
||||
self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Body {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,10 +2,14 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::db::ExpandDatabase;
|
||||
use syntax::ast::HasName;
|
||||
|
||||
use crate::{
|
||||
expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
|
||||
hir::{
|
||||
Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
|
||||
Movability, Statement,
|
||||
},
|
||||
pretty::{print_generic_args, print_path, print_type_ref},
|
||||
type_ref::TypeRef,
|
||||
};
|
||||
|
@ -13,47 +17,70 @@ use crate::{
|
|||
use super::*;
|
||||
|
||||
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
|
||||
let needs_semi;
|
||||
let header = match owner {
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
needs_semi = false;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
format!(
|
||||
"fn {}",
|
||||
item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
|
||||
)
|
||||
}
|
||||
DefWithBodyId::StaticId(it) => {
|
||||
needs_semi = true;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
format!(
|
||||
"static {} = ",
|
||||
item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
|
||||
)
|
||||
}
|
||||
DefWithBodyId::ConstId(it) => {
|
||||
needs_semi = true;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
|
||||
Some(name) => name.to_string(),
|
||||
Some(name) => name.display(db.upcast()).to_string(),
|
||||
None => "_".to_string(),
|
||||
};
|
||||
format!("const {name} = ")
|
||||
}
|
||||
DefWithBodyId::VariantId(it) => {
|
||||
needs_semi = false;
|
||||
let src = it.parent.child_source(db);
|
||||
let variant = &src.value[it.local_id];
|
||||
let name = match &variant.name() {
|
||||
match &variant.name() {
|
||||
Some(name) => name.to_string(),
|
||||
None => "_".to_string(),
|
||||
};
|
||||
format!("{name}")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
|
||||
let mut p =
|
||||
Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
|
||||
if let DefWithBodyId::FunctionId(it) = owner {
|
||||
p.buf.push('(');
|
||||
body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| {
|
||||
p.print_pat(param);
|
||||
p.buf.push(':');
|
||||
p.print_type_ref(ty);
|
||||
});
|
||||
p.buf.push(')');
|
||||
p.buf.push(' ');
|
||||
}
|
||||
p.print_expr(body.body_expr);
|
||||
if needs_semi {
|
||||
if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
|
||||
p.buf.push(';');
|
||||
}
|
||||
p.buf
|
||||
}
|
||||
|
||||
pub(super) fn print_expr_hir(
|
||||
db: &dyn DefDatabase,
|
||||
body: &Body,
|
||||
_owner: DefWithBodyId,
|
||||
expr: ExprId,
|
||||
) -> String {
|
||||
let mut p =
|
||||
Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
|
||||
p.print_expr(expr);
|
||||
p.buf
|
||||
}
|
||||
|
||||
macro_rules! w {
|
||||
($dst:expr, $($arg:tt)*) => {
|
||||
{ let _ = write!($dst, $($arg)*); }
|
||||
|
@ -70,6 +97,7 @@ macro_rules! wln {
|
|||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
body: &'a Body,
|
||||
buf: String,
|
||||
indent_level: usize,
|
||||
|
@ -144,29 +172,19 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
Expr::Loop { body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
w!(self, "{}: ", self.body[*lbl].name.display(self.db));
|
||||
}
|
||||
w!(self, "loop ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::While { condition, body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
w!(self, "{}: ", self.body[*lbl].name.display(self.db));
|
||||
}
|
||||
w!(self, "while ");
|
||||
self.print_expr(*condition);
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::For { iterable, pat, body, label } => {
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
}
|
||||
w!(self, "for ");
|
||||
self.print_pat(*pat);
|
||||
w!(self, " in ");
|
||||
self.print_expr(*iterable);
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
self.print_expr(*callee);
|
||||
w!(self, "(");
|
||||
|
@ -182,10 +200,10 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
Expr::MethodCall { receiver, method_name, args, generic_args } => {
|
||||
self.print_expr(*receiver);
|
||||
w!(self, ".{}", method_name);
|
||||
w!(self, ".{}", method_name.display(self.db));
|
||||
if let Some(args) = generic_args {
|
||||
w!(self, "::<");
|
||||
print_generic_args(args, self).unwrap();
|
||||
print_generic_args(self.db, args, self).unwrap();
|
||||
w!(self, ">");
|
||||
}
|
||||
w!(self, "(");
|
||||
|
@ -219,14 +237,14 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
Expr::Continue { label } => {
|
||||
w!(self, "continue");
|
||||
if let Some(label) = label {
|
||||
w!(self, " {}", label);
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.body[*lbl].name.display(self.db));
|
||||
}
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
w!(self, "break");
|
||||
if let Some(label) = label {
|
||||
w!(self, " {}", label);
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.body[*lbl].name.display(self.db));
|
||||
}
|
||||
if let Some(expr) = expr {
|
||||
self.whitespace();
|
||||
|
@ -265,7 +283,7 @@ impl<'a> Printer<'a> {
|
|||
w!(self, "{{");
|
||||
self.indented(|p| {
|
||||
for field in &**fields {
|
||||
w!(p, "{}: ", field.name);
|
||||
w!(p, "{}: ", field.name.display(self.db));
|
||||
p.print_expr(field.expr);
|
||||
wln!(p, ",");
|
||||
}
|
||||
|
@ -282,16 +300,12 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
Expr::Field { expr, name } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ".{}", name);
|
||||
w!(self, ".{}", name.display(self.db));
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, ".await");
|
||||
}
|
||||
Expr::Try { expr } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, "?");
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, " as ");
|
||||
|
@ -359,7 +373,7 @@ impl<'a> Printer<'a> {
|
|||
self.print_expr(*index);
|
||||
w!(self, "]");
|
||||
}
|
||||
Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
|
||||
Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
|
||||
match closure_kind {
|
||||
ClosureKind::Generator(Movability::Static) => {
|
||||
w!(self, "static ");
|
||||
|
@ -369,6 +383,12 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
_ => (),
|
||||
}
|
||||
match capture_by {
|
||||
CaptureBy::Value => {
|
||||
w!(self, "move ");
|
||||
}
|
||||
CaptureBy::Ref => (),
|
||||
}
|
||||
w!(self, "|");
|
||||
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
|
||||
if i != 0 {
|
||||
|
@ -418,20 +438,17 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
Expr::Literal(lit) => self.print_literal(lit),
|
||||
Expr::Block { id: _, statements, tail, label } => {
|
||||
let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
|
||||
let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
|
||||
self.print_block(label.as_deref(), statements, tail);
|
||||
}
|
||||
Expr::Unsafe { id: _, statements, tail } => {
|
||||
self.print_block(Some("unsafe "), statements, tail);
|
||||
}
|
||||
Expr::TryBlock { id: _, statements, tail } => {
|
||||
self.print_block(Some("try "), statements, tail);
|
||||
}
|
||||
Expr::Async { id: _, statements, tail } => {
|
||||
self.print_block(Some("async "), statements, tail);
|
||||
}
|
||||
Expr::Const { id: _, statements, tail } => {
|
||||
self.print_block(Some("const "), statements, tail);
|
||||
Expr::Const(id) => {
|
||||
w!(self, "const {{ /* {id:?} */ }}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,7 +456,7 @@ impl<'a> Printer<'a> {
|
|||
fn print_block(
|
||||
&mut self,
|
||||
label: Option<&str>,
|
||||
statements: &Box<[Statement]>,
|
||||
statements: &[Statement],
|
||||
tail: &Option<la_arena::Idx<Expr>>,
|
||||
) {
|
||||
self.whitespace();
|
||||
|
@ -449,7 +466,7 @@ impl<'a> Printer<'a> {
|
|||
w!(self, "{{");
|
||||
if !statements.is_empty() || tail.is_some() {
|
||||
self.indented(|p| {
|
||||
for stmt in &**statements {
|
||||
for stmt in statements {
|
||||
p.print_stmt(stmt);
|
||||
}
|
||||
if let Some(tail) = tail {
|
||||
|
@ -497,7 +514,7 @@ impl<'a> Printer<'a> {
|
|||
w!(self, " {{");
|
||||
self.indented(|p| {
|
||||
for arg in args.iter() {
|
||||
w!(p, "{}: ", arg.name);
|
||||
w!(p, "{}: ", arg.name.display(self.db));
|
||||
p.print_pat(arg.pat);
|
||||
wln!(p, ",");
|
||||
}
|
||||
|
@ -508,9 +525,13 @@ impl<'a> Printer<'a> {
|
|||
w!(self, "}}");
|
||||
}
|
||||
Pat::Range { start, end } => {
|
||||
self.print_expr(*start);
|
||||
w!(self, "...");
|
||||
self.print_expr(*end);
|
||||
if let Some(start) = start {
|
||||
self.print_literal_or_const(start);
|
||||
}
|
||||
w!(self, "..=");
|
||||
if let Some(end) = end {
|
||||
self.print_literal_or_const(end);
|
||||
}
|
||||
}
|
||||
Pat::Slice { prefix, slice, suffix } => {
|
||||
w!(self, "[");
|
||||
|
@ -601,10 +622,18 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
|
||||
match literal_or_const {
|
||||
LiteralOrConst::Literal(l) => self.print_literal(l),
|
||||
LiteralOrConst::Const(c) => self.print_path(c),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_literal(&mut self, literal: &Literal) {
|
||||
match literal {
|
||||
Literal::String(it) => w!(self, "{:?}", it),
|
||||
Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
|
||||
Literal::CString(it) => w!(self, "\"{}\\0\"", it),
|
||||
Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
|
||||
Literal::Bool(it) => w!(self, "{}", it),
|
||||
Literal::Int(i, suffix) => {
|
||||
|
@ -629,11 +658,11 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
|
||||
fn print_type_ref(&mut self, ty: &TypeRef) {
|
||||
print_type_ref(ty, self).unwrap();
|
||||
print_type_ref(self.db, ty, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path) {
|
||||
print_path(path, self).unwrap();
|
||||
print_path(self.db, path, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_binding(&mut self, id: BindingId) {
|
||||
|
@ -644,6 +673,6 @@ impl<'a> Printer<'a> {
|
|||
BindingAnnotation::Ref => "ref ",
|
||||
BindingAnnotation::RefMut => "ref mut ",
|
||||
};
|
||||
w!(self, "{}{}", mode, name);
|
||||
w!(self, "{}{}", mode, name.display(self.db));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
//! Name resolution for expressions.
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use la_arena::{Arena, Idx};
|
||||
use la_arena::{Arena, Idx, IdxRange, RawIdx};
|
||||
use rustc_hash::FxHashMap;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
body::Body,
|
||||
db::DefDatabase,
|
||||
expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
|
||||
hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
|
||||
BlockId, DefWithBodyId,
|
||||
};
|
||||
|
||||
|
@ -17,6 +16,7 @@ pub type ScopeId = Idx<ScopeData>;
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ExprScopes {
|
||||
scopes: Arena<ScopeData>,
|
||||
scope_entries: Arena<ScopeEntry>,
|
||||
scope_by_expr: FxHashMap<ExprId, ScopeId>,
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ pub struct ScopeData {
|
|||
parent: Option<ScopeId>,
|
||||
block: Option<BlockId>,
|
||||
label: Option<(LabelId, Name)>,
|
||||
entries: Vec<ScopeEntry>,
|
||||
entries: IdxRange<ScopeEntry>,
|
||||
}
|
||||
|
||||
impl ExprScopes {
|
||||
|
@ -53,7 +53,7 @@ impl ExprScopes {
|
|||
}
|
||||
|
||||
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
|
||||
&self.scopes[scope].entries
|
||||
&self.scope_entries[self.scopes[scope].entries.clone()]
|
||||
}
|
||||
|
||||
/// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
|
||||
|
@ -85,10 +85,17 @@ impl ExprScopes {
|
|||
}
|
||||
}
|
||||
|
||||
fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
|
||||
IdxRange::new(Idx::from_raw(RawIdx::from(idx as u32))..Idx::from_raw(RawIdx::from(idx as u32)))
|
||||
}
|
||||
|
||||
impl ExprScopes {
|
||||
fn new(body: &Body) -> ExprScopes {
|
||||
let mut scopes =
|
||||
ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
|
||||
let mut scopes = ExprScopes {
|
||||
scopes: Arena::default(),
|
||||
scope_entries: Arena::default(),
|
||||
scope_by_expr: FxHashMap::default(),
|
||||
};
|
||||
let mut root = scopes.root_scope();
|
||||
scopes.add_params_bindings(body, root, &body.params);
|
||||
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
|
||||
|
@ -96,7 +103,12 @@ impl ExprScopes {
|
|||
}
|
||||
|
||||
fn root_scope(&mut self) -> ScopeId {
|
||||
self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] })
|
||||
self.scopes.alloc(ScopeData {
|
||||
parent: None,
|
||||
block: None,
|
||||
label: None,
|
||||
entries: empty_entries(self.scope_entries.len()),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
|
||||
|
@ -104,32 +116,38 @@ impl ExprScopes {
|
|||
parent: Some(parent),
|
||||
block: None,
|
||||
label: None,
|
||||
entries: vec![],
|
||||
entries: empty_entries(self.scope_entries.len()),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId {
|
||||
self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] })
|
||||
self.scopes.alloc(ScopeData {
|
||||
parent: Some(parent),
|
||||
block: None,
|
||||
label,
|
||||
entries: empty_entries(self.scope_entries.len()),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_block_scope(
|
||||
&mut self,
|
||||
parent: ScopeId,
|
||||
block: BlockId,
|
||||
block: Option<BlockId>,
|
||||
label: Option<(LabelId, Name)>,
|
||||
) -> ScopeId {
|
||||
self.scopes.alloc(ScopeData {
|
||||
parent: Some(parent),
|
||||
block: Some(block),
|
||||
block,
|
||||
label,
|
||||
entries: vec![],
|
||||
entries: empty_entries(self.scope_entries.len()),
|
||||
})
|
||||
}
|
||||
|
||||
fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
|
||||
let Binding { name, .. } = &body.bindings[binding];
|
||||
let entry = ScopeEntry { name: name.clone(), binding };
|
||||
self.scopes[scope].entries.push(entry);
|
||||
let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
|
||||
self.scopes[scope].entries =
|
||||
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
|
||||
}
|
||||
|
||||
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
|
||||
|
@ -150,9 +168,9 @@ impl ExprScopes {
|
|||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
let ExprScopes { scopes, scope_by_expr } = self;
|
||||
let ExprScopes { scopes, scope_entries, scope_by_expr } = self;
|
||||
scopes.shrink_to_fit();
|
||||
scopes.values_mut().for_each(|it| it.entries.shrink_to_fit());
|
||||
scope_entries.shrink_to_fit();
|
||||
scope_by_expr.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
@ -200,22 +218,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
|
|||
scopes.set_scope(expr, scope);
|
||||
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
|
||||
}
|
||||
Expr::Unsafe { id, statements, tail }
|
||||
| Expr::Async { id, statements, tail }
|
||||
| Expr::Const { id, statements, tail }
|
||||
| Expr::TryBlock { id, statements, tail } => {
|
||||
Expr::Const(_) => {
|
||||
// FIXME: This is broken.
|
||||
}
|
||||
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, body, scopes, &mut scope);
|
||||
}
|
||||
Expr::For { iterable, pat, body: body_expr, label } => {
|
||||
compute_expr_scopes(*iterable, body, scopes, scope);
|
||||
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
|
||||
scopes.add_pat_bindings(body, scope, *pat);
|
||||
compute_expr_scopes(*body_expr, body, scopes, &mut scope);
|
||||
}
|
||||
Expr::While { condition, body: body_expr, label } => {
|
||||
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
|
||||
compute_expr_scopes(*condition, body, scopes, &mut scope);
|
||||
|
|
|
@ -148,8 +148,8 @@ fn f() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
|
||||
BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
|
||||
BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
|
||||
BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
|
||||
crate scope
|
||||
"#]],
|
||||
);
|
||||
|
|
|
@ -106,8 +106,14 @@ impl AsName for BuiltinType {
|
|||
|
||||
impl fmt::Display for BuiltinType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let type_name = self.as_name();
|
||||
type_name.fmt(f)
|
||||
match self {
|
||||
BuiltinType::Char => f.write_str("char"),
|
||||
BuiltinType::Bool => f.write_str("bool"),
|
||||
BuiltinType::Str => f.write_str("str"),
|
||||
BuiltinType::Int(it) => it.fmt(f),
|
||||
BuiltinType::Uint(it) => it.fmt(f),
|
||||
BuiltinType::Float(it) => it.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ use syntax::ast::HasDocComments;
|
|||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
dyn_map::{keys, DynMap},
|
||||
item_scope::ItemScope,
|
||||
keys,
|
||||
nameres::DefMap,
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
|
||||
ModuleDefId, ModuleId, TraitId, VariantId,
|
||||
|
@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId {
|
|||
for (_, def_map) in body.blocks(db) {
|
||||
// All block expressions are merged into the same map, because they logically all add
|
||||
// inner items to the containing `DefWithBodyId`.
|
||||
def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
|
||||
def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
//! Contains basic data about various HIR declarations.
|
||||
|
||||
use std::sync::Arc;
|
||||
pub mod adt;
|
||||
|
||||
use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
|
||||
use hir_expand::{
|
||||
name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
|
||||
};
|
||||
use intern::Interned;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::ast;
|
||||
use syntax::{ast, Parse};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
body::{Expander, Mark},
|
||||
db::DefDatabase,
|
||||
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
|
||||
expander::{Expander, Mark},
|
||||
item_tree::{
|
||||
self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId,
|
||||
},
|
||||
macro_call_as_call_id, macro_id_to_def_id,
|
||||
nameres::{
|
||||
attr_resolution::ResolvedAttr,
|
||||
diagnostics::DefDiagnostic,
|
||||
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
|
||||
DefMap,
|
||||
DefMap, MacroSubNs,
|
||||
},
|
||||
type_ref::{TraitRef, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
|
@ -28,9 +34,8 @@ use crate::{
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionData {
|
||||
pub name: Name,
|
||||
pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
|
||||
pub params: Vec<Interned<TypeRef>>,
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub async_ret_type: Option<Interned<TypeRef>>,
|
||||
pub attrs: Attrs,
|
||||
pub visibility: RawVisibility,
|
||||
pub abi: Option<Interned<str>>,
|
||||
|
@ -43,16 +48,16 @@ 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 crate_graph = db.crate_graph();
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
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 {
|
||||
db.trait_data(trait_id).visibility.clone()
|
||||
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 enabled_params = func
|
||||
.params
|
||||
.clone()
|
||||
|
@ -99,12 +104,11 @@ impl FunctionData {
|
|||
params: enabled_params
|
||||
.clone()
|
||||
.filter_map(|id| match &item_tree[id] {
|
||||
Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
|
||||
Param::Normal(ty) => Some(ty.clone()),
|
||||
Param::Varargs => None,
|
||||
})
|
||||
.collect(),
|
||||
ret_type: func.ret_type.clone(),
|
||||
async_ret_type: func.async_ret_type.clone(),
|
||||
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
|
||||
visibility,
|
||||
abi: func.abi.clone(),
|
||||
|
@ -188,7 +192,7 @@ impl TypeAliasData {
|
|||
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 {
|
||||
db.trait_data(trait_id).visibility.clone()
|
||||
trait_vis(db, trait_id)
|
||||
} else {
|
||||
item_tree[typ.visibility].clone()
|
||||
};
|
||||
|
@ -471,7 +475,7 @@ impl ConstData {
|
|||
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 {
|
||||
db.trait_data(trait_id).visibility.clone()
|
||||
trait_vis(db, trait_id)
|
||||
} else {
|
||||
item_tree[konst.visibility].clone()
|
||||
};
|
||||
|
@ -519,7 +523,7 @@ struct AssocItemCollector<'a> {
|
|||
db: &'a dyn DefDatabase,
|
||||
module_id: ModuleId,
|
||||
def_map: Arc<DefMap>,
|
||||
inactive_diagnostics: Vec<DefDiagnostic>,
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
container: ItemContainerId,
|
||||
expander: Expander,
|
||||
|
||||
|
@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> {
|
|||
expander: Expander::new(db, file_id, module_id),
|
||||
items: Vec::new(),
|
||||
attr_calls: Vec::new(),
|
||||
inactive_diagnostics: Vec::new(),
|
||||
diagnostics: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> {
|
|||
(
|
||||
self.items,
|
||||
if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
|
||||
self.inactive_diagnostics,
|
||||
self.diagnostics,
|
||||
)
|
||||
}
|
||||
|
||||
// FIXME: proc-macro diagnostics
|
||||
fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
|
||||
let container = self.container;
|
||||
self.items.reserve(assoc_items.len());
|
||||
|
@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> {
|
|||
'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.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id.local_id,
|
||||
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
|
||||
attrs.cfg().unwrap(),
|
||||
|
@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> {
|
|||
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 };
|
||||
|
||||
if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
|
||||
match self.def_map.resolve_attr_macro(
|
||||
self.db,
|
||||
self.module_id.local_id,
|
||||
ast_id_with_path,
|
||||
attr,
|
||||
) {
|
||||
self.attr_calls.push((ast_id, call_id));
|
||||
// If proc attribute macro expansion is disabled, skip expanding it here
|
||||
if !self.db.enable_proc_attr_macros() {
|
||||
continue 'attrs;
|
||||
}
|
||||
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 exp.is_dummy() {
|
||||
Ok(ResolvedAttr::Macro(call_id)) => {
|
||||
self.attr_calls.push((ast_id, call_id));
|
||||
// If proc attribute macro expansion is disabled, skip expanding it here
|
||||
if !self.db.expand_proc_attr_macros() {
|
||||
continue 'attrs;
|
||||
}
|
||||
}
|
||||
match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
|
||||
ExpandResult { value: Some((mark, _)), .. } => {
|
||||
self.collect_macro_items(mark);
|
||||
continue 'items;
|
||||
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 exp.is_dummy() {
|
||||
continue 'attrs;
|
||||
}
|
||||
}
|
||||
ExpandResult { .. } => {}
|
||||
|
||||
let res =
|
||||
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||
self.collect_macro_items(res, &|| loc.kind.clone());
|
||||
continue 'items;
|
||||
}
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
self.module_id.local_id,
|
||||
MacroCallKind::Attr {
|
||||
ast_id,
|
||||
attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
|
||||
invoc_attr_index: attr.id,
|
||||
},
|
||||
attr.path().clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match item {
|
||||
AssocItem::Function(id) => {
|
||||
let item = &item_tree[id];
|
||||
self.collect_item(item_tree, tree_id, container, item);
|
||||
}
|
||||
}
|
||||
|
||||
let def =
|
||||
FunctionLoc { 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];
|
||||
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 name = match item.name.clone() {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
};
|
||||
let def =
|
||||
ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
|
||||
self.items.push((name, def.into()));
|
||||
}
|
||||
AssocItem::TypeAlias(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::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::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::MacroCall(call) => {
|
||||
if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) {
|
||||
let call = &item_tree[call];
|
||||
let def =
|
||||
TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
|
||||
self.items.push((item.name.clone(), def.into()));
|
||||
}
|
||||
AssocItem::MacroCall(call) => {
|
||||
let file_id = self.expander.current_file_id();
|
||||
let MacroCall { ast_id, expand_to, ref path } = item_tree[call];
|
||||
let module = self.expander.module.local_id;
|
||||
|
||||
let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
|
||||
let call = ast_id_map.get(call.ast_id).to_node(&root);
|
||||
let _cx =
|
||||
stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
|
||||
let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
|
||||
|
||||
if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {
|
||||
self.collect_macro_items(mark);
|
||||
}
|
||||
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| macro_id_to_def_id(self.db, it))
|
||||
};
|
||||
match macro_call_as_call_id(
|
||||
self.db.upcast(),
|
||||
&AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
|
||||
expand_to,
|
||||
self.expander.module.krate(),
|
||||
resolver,
|
||||
) {
|
||||
Ok(Some(call_id)) => {
|
||||
let res =
|
||||
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||
self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
|
||||
ast_id: InFile::new(file_id, ast_id),
|
||||
expand_to: hir_expand::ExpandTo::Items,
|
||||
});
|
||||
}
|
||||
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,
|
||||
},
|
||||
Clone::clone(path),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_macro_items(&mut self, mark: Mark) {
|
||||
fn collect_macro_items(
|
||||
&mut self,
|
||||
ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
|
||||
error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
|
||||
) {
|
||||
let Some((mark, parse)) = value else { return };
|
||||
|
||||
if let Some(err) = err {
|
||||
let diag = match err {
|
||||
// why is this reported here?
|
||||
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
|
||||
DefDiagnostic::unresolved_proc_macro(
|
||||
self.module_id.local_id,
|
||||
error_call_kind(),
|
||||
krate,
|
||||
)
|
||||
}
|
||||
_ => DefDiagnostic::macro_error(
|
||||
self.module_id.local_id,
|
||||
error_call_kind(),
|
||||
err.to_string(),
|
||||
),
|
||||
};
|
||||
self.diagnostics.push(diag);
|
||||
}
|
||||
if let errors @ [_, ..] = parse.errors() {
|
||||
self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
|
||||
self.module_id.local_id,
|
||||
error_call_kind(),
|
||||
errors.into(),
|
||||
));
|
||||
}
|
||||
|
||||
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]> =
|
||||
|
@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> {
|
|||
self.expander.exit(self.db, mark);
|
||||
}
|
||||
}
|
||||
|
||||
fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility {
|
||||
let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let tr_def = &item_tree[tree_id.value];
|
||||
item_tree[tr_def.visibility].clone()
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Defines hir-level representation of structs, enums and unions
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use bitflags::bitflags;
|
||||
use cfg::CfgOptions;
|
||||
use either::Either;
|
||||
|
||||
|
@ -12,15 +11,17 @@ use hir_expand::{
|
|||
};
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use rustc_abi::{Integer, IntegerType};
|
||||
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
|
||||
use syntax::ast::{self, HasName, HasVisibility};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
body::{CfgExpander, LowerCtx},
|
||||
builtin_type::{BuiltinInt, BuiltinUint},
|
||||
db::DefDatabase,
|
||||
expander::CfgExpander,
|
||||
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
|
||||
layout::{Align, ReprFlags, ReprOptions},
|
||||
lang_item::LangItem,
|
||||
lower::LowerCtx,
|
||||
nameres::diagnostics::DefDiagnostic,
|
||||
src::HasChildSource,
|
||||
src::HasSource,
|
||||
|
@ -39,8 +40,27 @@ pub struct StructData {
|
|||
pub variant_data: Arc<VariantData>,
|
||||
pub repr: Option<ReprOptions>,
|
||||
pub visibility: RawVisibility,
|
||||
pub rustc_has_incoherent_inherent_impls: bool,
|
||||
pub fundamental: bool,
|
||||
pub flags: StructFlags,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, 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)]
|
||||
|
@ -174,10 +194,25 @@ impl StructData {
|
|||
let item_tree = loc.id.item_tree(db);
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
|
||||
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
let rustc_has_incoherent_inherent_impls =
|
||||
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
|
||||
let fundamental = attrs.by_key("fundamental").exists();
|
||||
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
}
|
||||
if attrs.by_key("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 (variant_data, diagnostics) = lower_fields(
|
||||
|
@ -196,8 +231,7 @@ impl StructData {
|
|||
variant_data: Arc::new(variant_data),
|
||||
repr,
|
||||
visibility: item_tree[strukt.visibility].clone(),
|
||||
rustc_has_incoherent_inherent_impls,
|
||||
fundamental,
|
||||
flags,
|
||||
}),
|
||||
diagnostics.into(),
|
||||
)
|
||||
|
@ -218,9 +252,13 @@ impl StructData {
|
|||
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
|
||||
|
||||
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
|
||||
let rustc_has_incoherent_inherent_impls =
|
||||
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
|
||||
let fundamental = attrs.by_key("fundamental").exists();
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
}
|
||||
if attrs.by_key("fundamental").exists() {
|
||||
flags |= StructFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
|
||||
let union = &item_tree[loc.id.value];
|
||||
let (variant_data, diagnostics) = lower_fields(
|
||||
|
@ -239,8 +277,7 @@ impl StructData {
|
|||
variant_data: Arc::new(variant_data),
|
||||
repr,
|
||||
visibility: item_tree[union.visibility].clone(),
|
||||
rustc_has_incoherent_inherent_impls,
|
||||
fundamental,
|
||||
flags,
|
||||
}),
|
||||
diagnostics.into(),
|
||||
)
|
||||
|
@ -436,7 +473,7 @@ fn lower_struct(
|
|||
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
||||
ast: &InFile<ast::StructKind>,
|
||||
) -> StructKind {
|
||||
let ctx = LowerCtx::new(db, ast.file_id);
|
||||
let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
|
||||
|
||||
match &ast.value {
|
||||
ast::StructKind::Tuple(fl) => {
|
|
@ -1,32 +1,32 @@
|
|||
//! Defines database & queries for name resolution.
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
|
||||
use either::Either;
|
||||
use hir_expand::{db::ExpandDatabase, HirFileId};
|
||||
use intern::Interned;
|
||||
use la_arena::ArenaMap;
|
||||
use syntax::{ast, AstPtr};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
adt::{EnumData, StructData},
|
||||
attr::{Attrs, AttrsWithOwner},
|
||||
body::{scope::ExprScopes, Body, BodySourceMap},
|
||||
data::{
|
||||
adt::{EnumData, StructData},
|
||||
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
|
||||
TraitAliasData, TraitData, TypeAliasData,
|
||||
},
|
||||
generics::GenericParams,
|
||||
hir::ExprId,
|
||||
import_map::ImportMap,
|
||||
item_tree::{AttrOwner, ItemTree},
|
||||
lang_item::{LangItem, LangItemTarget, LangItems},
|
||||
nameres::{diagnostics::DefDiagnostic, DefMap},
|
||||
visibility::{self, Visibility},
|
||||
AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
|
||||
ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
|
||||
LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
|
||||
StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
|
||||
TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
|
||||
AnonymousConstId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId,
|
||||
EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc,
|
||||
LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc,
|
||||
ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId,
|
||||
TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
|
||||
};
|
||||
|
||||
#[salsa::query_group(InternDatabaseStorage)]
|
||||
|
@ -61,12 +61,14 @@ pub trait InternDatabase: SourceDatabase {
|
|||
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
|
||||
#[salsa::interned]
|
||||
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
|
||||
#[salsa::interned]
|
||||
fn intern_anonymous_const(&self, id: (DefWithBodyId, ExprId)) -> AnonymousConstId;
|
||||
}
|
||||
|
||||
#[salsa::query_group(DefDatabaseStorage)]
|
||||
pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
|
||||
#[salsa::input]
|
||||
fn enable_proc_attr_macros(&self) -> bool;
|
||||
fn expand_proc_attr_macros(&self) -> bool;
|
||||
|
||||
#[salsa::invoke(ItemTree::file_item_tree_query)]
|
||||
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
|
||||
|
@ -94,7 +96,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
/// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would
|
||||
/// return a `DefMap` containing `inner`.
|
||||
#[salsa::invoke(DefMap::block_def_map_query)]
|
||||
fn block_def_map(&self, block: BlockId) -> Option<Arc<DefMap>>;
|
||||
fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
|
||||
|
||||
// region:data
|
||||
|
||||
#[salsa::invoke(StructData::struct_data_query)]
|
||||
fn struct_data(&self, id: StructId) -> Arc<StructData>;
|
||||
|
@ -151,6 +155,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
#[salsa::invoke(ProcMacroData::proc_macro_data_query)]
|
||||
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
|
||||
|
||||
// endregion:data
|
||||
|
||||
#[salsa::invoke(Body::body_with_source_map_query)]
|
||||
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
|
||||
|
||||
|
@ -163,6 +169,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
#[salsa::invoke(GenericParams::generic_params_query)]
|
||||
fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
|
||||
|
||||
// region:attrs
|
||||
|
||||
#[salsa::invoke(Attrs::variants_attrs_query)]
|
||||
fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
|
||||
|
||||
|
@ -182,10 +190,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
|
||||
|
||||
#[salsa::invoke(AttrsWithOwner::attrs_query)]
|
||||
fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
|
||||
fn attrs(&self, def: AttrDefId) -> Attrs;
|
||||
|
||||
#[salsa::invoke(LangItems::crate_lang_items_query)]
|
||||
fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
|
||||
fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
|
||||
|
||||
// endregion:attrs
|
||||
|
||||
#[salsa::invoke(LangItems::lang_item_query)]
|
||||
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
|
||||
|
@ -193,6 +204,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
#[salsa::invoke(ImportMap::import_map_query)]
|
||||
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
|
||||
|
||||
// region:visibilities
|
||||
|
||||
#[salsa::invoke(visibility::field_visibilities_query)]
|
||||
fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
|
||||
|
||||
|
@ -203,9 +216,17 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
#[salsa::invoke(visibility::const_visibility_query)]
|
||||
fn const_visibility(&self, def: ConstId) -> Visibility;
|
||||
|
||||
// endregion:visibilities
|
||||
|
||||
#[salsa::invoke(LangItems::crate_lang_items_query)]
|
||||
fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
|
||||
|
||||
#[salsa::transparent]
|
||||
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
|
||||
|
||||
#[salsa::transparent]
|
||||
fn recursion_limit(&self, crate_id: CrateId) -> u32;
|
||||
|
||||
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
||||
}
|
||||
|
||||
|
@ -228,6 +249,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
|
|||
}
|
||||
}
|
||||
|
||||
fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 {
|
||||
db.crate_limits(crate_id).recursion_limit
|
||||
}
|
||||
|
||||
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
|
||||
let file = db.crate_graph()[crate_id].root_file_id;
|
||||
let item_tree = db.file_item_tree(file.into());
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
//!
|
||||
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
|
||||
//! a coincidence.
|
||||
pub mod keys;
|
||||
|
||||
use std::{
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
|
|
211
crates/hir-def/src/expander.rs
Normal file
211
crates/hir-def/src/expander.rs
Normal file
|
@ -0,0 +1,211 @@
|
|||
//! Macro expansion utilities.
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgOptions;
|
||||
use drop_bomb::DropBomb;
|
||||
use hir_expand::{
|
||||
attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId,
|
||||
InFile, MacroCallId, UnresolvedMacro,
|
||||
};
|
||||
use limit::Limit;
|
||||
use syntax::{ast, Parse, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
|
||||
MacroId, ModuleId,
|
||||
};
|
||||
|
||||
/// A subset of Expander that only deals with cfg attributes. We only need it to
|
||||
/// avoid cyclic queries in crate def map during enum processing.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CfgExpander {
|
||||
cfg_options: CfgOptions,
|
||||
hygiene: Hygiene,
|
||||
krate: CrateId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Expander {
|
||||
cfg_expander: CfgExpander,
|
||||
pub(crate) 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: Limit,
|
||||
}
|
||||
|
||||
impl CfgExpander {
|
||||
pub(crate) fn new(
|
||||
db: &dyn DefDatabase,
|
||||
current_file_id: HirFileId,
|
||||
krate: CrateId,
|
||||
) -> CfgExpander {
|
||||
let hygiene = Hygiene::new(db.upcast(), current_file_id);
|
||||
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
|
||||
CfgExpander { cfg_options, hygiene, krate }
|
||||
}
|
||||
|
||||
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.hygiene))
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
|
||||
let attrs = self.parse_attrs(db, owner);
|
||||
attrs.is_cfg_enabled(&self.cfg_options)
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
&self.hygiene
|
||||
}
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
|
||||
let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
|
||||
let recursion_limit = db.recursion_limit(module.krate);
|
||||
#[cfg(not(test))]
|
||||
let recursion_limit = Limit::new(recursion_limit as usize);
|
||||
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
|
||||
#[cfg(test)]
|
||||
let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize));
|
||||
Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit }
|
||||
}
|
||||
|
||||
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 = InFile::new(this.current_file_id, ¯o_call);
|
||||
match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| {
|
||||
resolver(path).map(|it| macro_id_to_def_id(db, 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)))
|
||||
}
|
||||
|
||||
fn enter_expand_inner(
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
error: Option<ExpandError>,
|
||||
) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
|
||||
let file_id = call_id.as_file();
|
||||
let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id);
|
||||
|
||||
ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) }
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
|
||||
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) -> LowerCtx<'a> {
|
||||
LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
|
||||
}
|
||||
|
||||
pub(crate) fn to_source<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 {
|
||||
self.cfg_expander.parse_attrs(db, owner)
|
||||
}
|
||||
|
||||
pub(crate) fn cfg_options(&self) -> &CfgOptions {
|
||||
&self.cfg_expander.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) -> Option<Path> {
|
||||
let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
|
||||
Path::from_src(path, &ctx)
|
||||
}
|
||||
|
||||
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::only_err(ExpandError::RecursionOverflowPoisoned);
|
||||
} else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
|
||||
self.recursion_depth = u32::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
return ExpandResult::only_err(ExpandError::Other(
|
||||
"reached recursion limit during macro expansion".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let ExpandResult { value, err } = op(self);
|
||||
let Some(call_id) = value else {
|
||||
return ExpandResult { value: None, err };
|
||||
};
|
||||
|
||||
Self::enter_expand_inner(db, call_id, err).map(|value| {
|
||||
value.and_then(|InFile { file_id, value }| {
|
||||
let parse = value.cast::<T>()?;
|
||||
|
||||
self.recursion_depth += 1;
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
|
||||
let old_file_id = std::mem::replace(&mut self.current_file_id, file_id);
|
||||
let mark =
|
||||
Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
|
||||
Some((mark, parse))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mark {
|
||||
file_id: HirFileId,
|
||||
bomb: DropBomb,
|
||||
}
|
|
@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15;
|
|||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PrefixKind {
|
||||
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
|
||||
/// This is the same as plain, just that paths will start with `self` iprepended f the path
|
||||
/// This is the same as plain, just that paths will start with `self` prepended if the path
|
||||
/// starts with an identifier that is not a crate.
|
||||
BySelf,
|
||||
/// Causes paths to ignore imports in the local module.
|
||||
|
@ -81,7 +81,7 @@ fn find_path_inner(
|
|||
}
|
||||
|
||||
let def_map = from.def_map(db);
|
||||
let crate_root = def_map.crate_root(db);
|
||||
let crate_root = def_map.crate_root();
|
||||
// - if the item is a module, jump straight to module search
|
||||
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
|
||||
let mut visited_modules = FxHashSet::default();
|
||||
|
@ -183,7 +183,7 @@ fn find_path_for_module(
|
|||
|
||||
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
|
||||
let root_def_map = crate_root.def_map(db);
|
||||
for (name, &def_id) in root_def_map.extern_prelude() {
|
||||
for (name, def_id) in root_def_map.extern_prelude() {
|
||||
if module_id == def_id {
|
||||
let name = scope_name.unwrap_or_else(|| name.clone());
|
||||
|
||||
|
@ -454,7 +454,7 @@ fn find_local_import_locations(
|
|||
worklist.push(ancestor);
|
||||
}
|
||||
|
||||
let def_map = def_map.crate_root(db).def_map(db);
|
||||
let def_map = def_map.crate_root().def_map(db);
|
||||
|
||||
let mut seen: FxHashSet<_> = FxHashSet::default();
|
||||
|
||||
|
@ -543,6 +543,7 @@ mod tests {
|
|||
module.local_id,
|
||||
&mod_path,
|
||||
crate::item_scope::BuiltinShadowMode::Module,
|
||||
None,
|
||||
)
|
||||
.0
|
||||
.take_types()
|
||||
|
|
|
@ -12,16 +12,17 @@ use hir_expand::{
|
|||
use intern::Interned;
|
||||
use la_arena::{Arena, ArenaMap, Idx};
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::ops::DerefMut;
|
||||
use stdx::impl_from;
|
||||
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
body::{Expander, LowerCtx},
|
||||
child_by_source::ChildBySource,
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
keys,
|
||||
dyn_map::{keys, DynMap},
|
||||
expander::Expander,
|
||||
lower::LowerCtx,
|
||||
nameres::{DefMap, MacroSubNs},
|
||||
src::{HasChildSource, HasSource},
|
||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
|
||||
|
@ -153,7 +154,6 @@ impl GenericParams {
|
|||
def: GenericDefId,
|
||||
) -> Interned<GenericParams> {
|
||||
let _p = profile::span("generic_params_query");
|
||||
|
||||
macro_rules! id_to_generics {
|
||||
($id:ident) => {{
|
||||
let id = $id.lookup(db).id;
|
||||
|
@ -176,8 +176,10 @@ impl GenericParams {
|
|||
|
||||
// Don't create an `Expander` nor call `loc.source(db)` if not needed since this
|
||||
// causes a reparse after the `ItemTree` has been created.
|
||||
let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
|
||||
for (_, param) in &func_data.params {
|
||||
let mut expander = Lazy::new(|| {
|
||||
(module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
|
||||
});
|
||||
for param in &func_data.params {
|
||||
generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
|
||||
}
|
||||
|
||||
|
@ -329,7 +331,7 @@ impl GenericParams {
|
|||
pub(crate) fn fill_implicit_impl_trait_args(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
expander: &mut impl DerefMut<Target = Expander>,
|
||||
exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
|
||||
type_ref: &TypeRef,
|
||||
) {
|
||||
type_ref.walk(&mut |type_ref| {
|
||||
|
@ -349,14 +351,28 @@ impl GenericParams {
|
|||
}
|
||||
if let TypeRef::Macro(mc) = type_ref {
|
||||
let macro_call = mc.to_node(db.upcast());
|
||||
match expander.enter_expand::<ast::Type>(db, macro_call) {
|
||||
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
|
||||
let ctx = LowerCtx::new(db, expander.current_file_id());
|
||||
let type_ref = TypeRef::from_ast(&ctx, expanded);
|
||||
self.fill_implicit_impl_trait_args(db, expander, &type_ref);
|
||||
expander.exit(db, mark);
|
||||
}
|
||||
_ => {}
|
||||
let (def_map, expander) = &mut **exp;
|
||||
|
||||
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 ctx = expander.ctx(db);
|
||||
let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
|
||||
self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
|
||||
exp.1.exit(db, mark);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -12,26 +12,29 @@
|
|||
//!
|
||||
//! See also a neighboring `body` module.
|
||||
|
||||
pub mod type_ref;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
BlockId,
|
||||
AnonymousConstId, BlockId,
|
||||
};
|
||||
|
||||
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
|
||||
|
||||
pub type ExprId = Idx<Expr>;
|
||||
|
||||
pub type BindingId = Idx<Binding>;
|
||||
|
||||
pub type ExprId = Idx<Expr>;
|
||||
|
||||
/// FIXME: this is a hacky function which should be removed
|
||||
pub(crate) fn dummy_expr_id() -> ExprId {
|
||||
ExprId::from_raw(RawIdx::from(u32::MAX))
|
||||
|
@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper {
|
|||
pub enum Literal {
|
||||
String(Box<str>),
|
||||
ByteString(Box<[u8]>),
|
||||
CString(Box<str>),
|
||||
Char(char),
|
||||
Bool(bool),
|
||||
Int(i128, Option<BuiltinInt>),
|
||||
|
@ -92,6 +96,66 @@ pub enum Literal {
|
|||
Float(FloatTypeWrapper, Option<BuiltinFloat>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
/// Used in range patterns.
|
||||
pub enum LiteralOrConst {
|
||||
Literal(Literal),
|
||||
Const(Path),
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn negate(self) -> Option<Self> {
|
||||
if let Literal::Int(i, k) = self {
|
||||
Some(Literal::Int(-i, k))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ast::LiteralKind> for Literal {
|
||||
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
|
||||
use ast::LiteralKind;
|
||||
match ast_lit_kind {
|
||||
// FIXME: these should have actual values filled in, but unsure on perf impact
|
||||
LiteralKind::IntNumber(lit) => {
|
||||
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
|
||||
Literal::Float(
|
||||
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
|
||||
builtin,
|
||||
)
|
||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
|
||||
Literal::Uint(lit.value().unwrap_or(0), builtin)
|
||||
} else {
|
||||
let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
|
||||
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
|
||||
}
|
||||
}
|
||||
LiteralKind::FloatNumber(lit) => {
|
||||
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
|
||||
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
|
||||
}
|
||||
LiteralKind::ByteString(bs) => {
|
||||
let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::ByteString(text)
|
||||
}
|
||||
LiteralKind::String(s) => {
|
||||
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::String(text)
|
||||
}
|
||||
LiteralKind::CString(s) => {
|
||||
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::CString(text)
|
||||
}
|
||||
LiteralKind::Byte(b) => {
|
||||
Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
|
||||
}
|
||||
LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
|
||||
LiteralKind::Bool(val) => Literal::Bool(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Expr {
|
||||
/// This is produced if the syntax tree does not have a required expression piece.
|
||||
|
@ -107,28 +171,19 @@ pub enum Expr {
|
|||
expr: ExprId,
|
||||
},
|
||||
Block {
|
||||
id: BlockId,
|
||||
id: Option<BlockId>,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
TryBlock {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Async {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Const {
|
||||
id: BlockId,
|
||||
id: Option<BlockId>,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Const(AnonymousConstId),
|
||||
Unsafe {
|
||||
id: BlockId,
|
||||
id: Option<BlockId>,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
|
@ -141,12 +196,6 @@ pub enum Expr {
|
|||
body: ExprId,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
For {
|
||||
iterable: ExprId,
|
||||
pat: PatId,
|
||||
body: ExprId,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
Call {
|
||||
callee: ExprId,
|
||||
args: Box<[ExprId]>,
|
||||
|
@ -163,11 +212,11 @@ pub enum Expr {
|
|||
arms: Box<[MatchArm]>,
|
||||
},
|
||||
Continue {
|
||||
label: Option<Name>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
Break {
|
||||
expr: Option<ExprId>,
|
||||
label: Option<Name>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
Return {
|
||||
expr: Option<ExprId>,
|
||||
|
@ -192,9 +241,6 @@ pub enum Expr {
|
|||
Await {
|
||||
expr: ExprId,
|
||||
},
|
||||
Try {
|
||||
expr: ExprId,
|
||||
},
|
||||
Cast {
|
||||
expr: ExprId,
|
||||
type_ref: Interned<TypeRef>,
|
||||
|
@ -231,6 +277,7 @@ pub enum Expr {
|
|||
ret_type: Option<Interned<TypeRef>>,
|
||||
body: ExprId,
|
||||
closure_kind: ClosureKind,
|
||||
capture_by: CaptureBy,
|
||||
},
|
||||
Tuple {
|
||||
exprs: Box<[ExprId]>,
|
||||
|
@ -248,6 +295,14 @@ pub enum ClosureKind {
|
|||
Async,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CaptureBy {
|
||||
/// `move |x| y + x`.
|
||||
Value,
|
||||
/// `move` keyword was not specified.
|
||||
Ref,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Movability {
|
||||
Static,
|
||||
|
@ -302,11 +357,10 @@ impl Expr {
|
|||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Const(_) => (),
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::TryBlock { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. }
|
||||
| Expr::Const { statements, tail, .. } => {
|
||||
| Expr::Async { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, else_branch, .. } => {
|
||||
|
@ -329,10 +383,6 @@ impl Expr {
|
|||
f(*condition);
|
||||
f(*body);
|
||||
}
|
||||
Expr::For { iterable, body, .. } => {
|
||||
f(*iterable);
|
||||
f(*body);
|
||||
}
|
||||
Expr::Call { callee, args, .. } => {
|
||||
f(*callee);
|
||||
args.iter().copied().for_each(f);
|
||||
|
@ -383,7 +433,6 @@ impl Expr {
|
|||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Try { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
|
@ -437,11 +486,38 @@ impl BindingAnnotation {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BindingProblems {
|
||||
/// https://doc.rust-lang.org/stable/error_codes/E0416.html
|
||||
BoundMoreThanOnce,
|
||||
/// https://doc.rust-lang.org/stable/error_codes/E0409.html
|
||||
BoundInconsistently,
|
||||
/// https://doc.rust-lang.org/stable/error_codes/E0408.html
|
||||
NotBoundAcrossAll,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Binding {
|
||||
pub name: Name,
|
||||
pub mode: BindingAnnotation,
|
||||
pub definitions: SmallVec<[PatId; 1]>,
|
||||
/// Id of the closure/generator that owns this binding. If it is owned by the
|
||||
/// top level expression, this field would be `None`.
|
||||
pub owner: Option<ExprId>,
|
||||
pub problems: Option<BindingProblems>,
|
||||
}
|
||||
|
||||
impl Binding {
|
||||
pub fn is_upvar(&self, relative_to: ExprId) -> bool {
|
||||
match self.owner {
|
||||
Some(x) => {
|
||||
// We assign expression ids in a way that outer closures will receive
|
||||
// a lower id
|
||||
x.into_raw() < relative_to.into_raw()
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -458,7 +534,7 @@ pub enum Pat {
|
|||
Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
|
||||
Or(Box<[PatId]>),
|
||||
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
|
||||
Range { start: ExprId, end: ExprId },
|
||||
Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
|
||||
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
|
||||
Path(Box<Path>),
|
||||
Lit(ExprId),
|
|
@ -1,9 +1,11 @@
|
|||
//! 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;
|
||||
|
||||
use hir_expand::{
|
||||
db::ExpandDatabase,
|
||||
name::{AsName, Name},
|
||||
AstId,
|
||||
};
|
||||
|
@ -11,9 +13,9 @@ use intern::Interned;
|
|||
use syntax::ast::{self, HasName};
|
||||
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
expr::Literal,
|
||||
hir::Literal,
|
||||
lower::LowerCtx,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
|
@ -383,15 +385,6 @@ pub enum ConstRefOrPath {
|
|||
Path(Name),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ConstRefOrPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ConstRefOrPath::Scalar(s) => s.fmt(f),
|
||||
ConstRefOrPath::Path(n) => n.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstRefOrPath {
|
||||
pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
|
||||
match expr {
|
||||
|
@ -400,6 +393,19 @@ impl ConstRefOrPath {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRefOrPath);
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.1 {
|
||||
ConstRefOrPath::Scalar(s) => s.fmt(f),
|
||||
ConstRefOrPath::Path(n) => n.display(self.0).fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
Display(db, self)
|
||||
}
|
||||
|
||||
// FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
|
||||
// parse stage.
|
||||
fn from_expr(expr: ast::Expr) -> Self {
|
|
@ -1,6 +1,6 @@
|
|||
//! A map of all publicly exported items in a crate.
|
||||
|
||||
use std::{fmt, hash::BuildHasherDefault, sync::Arc};
|
||||
use std::{fmt, hash::BuildHasherDefault};
|
||||
|
||||
use base_db::CrateId;
|
||||
use fst::{self, Streamer};
|
||||
|
@ -8,10 +8,11 @@ use hir_expand::name::Name;
|
|||
use indexmap::{map::Entry, IndexMap};
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashSet, FxHasher};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
|
||||
ModuleId, TraitId,
|
||||
db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
|
||||
ModuleDefId, ModuleId, TraitId,
|
||||
};
|
||||
|
||||
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
|
@ -32,13 +33,23 @@ pub struct ImportPath {
|
|||
pub segments: Vec<Name>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ImportPath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.segments.iter().format("::"), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportPath {
|
||||
pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a {
|
||||
struct Display<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
path: &'a ImportPath,
|
||||
}
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(
|
||||
&self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"),
|
||||
f,
|
||||
)
|
||||
}
|
||||
}
|
||||
Display { db, path: self }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.segments.len()
|
||||
}
|
||||
|
@ -75,7 +86,7 @@ impl ImportMap {
|
|||
let mut importables = import_map
|
||||
.map
|
||||
.iter()
|
||||
.map(|(item, info)| (item, fst_path(&info.path)))
|
||||
.map(|(item, info)| (item, fst_path(db, &info.path)))
|
||||
.collect::<Vec<_>>();
|
||||
importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
|
||||
|
||||
|
@ -112,6 +123,25 @@ impl ImportMap {
|
|||
self.map.get(&item)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
|
||||
let mut importable_paths: Vec<_> = self
|
||||
.map
|
||||
.iter()
|
||||
.map(|(item, info)| {
|
||||
let ns = match item {
|
||||
ItemInNs::Types(_) => "t",
|
||||
ItemInNs::Values(_) => "v",
|
||||
ItemInNs::Macros(_) => "m",
|
||||
};
|
||||
format!("- {} ({ns})", info.path.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
||||
importable_paths.sort();
|
||||
importable_paths.join("\n")
|
||||
}
|
||||
|
||||
fn collect_trait_assoc_items(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
|
@ -153,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
|
|||
|
||||
// We look only into modules that are public(ly reexported), starting with the crate root.
|
||||
let empty = ImportPath { segments: vec![] };
|
||||
let root = def_map.module_id(def_map.root());
|
||||
let root = def_map.module_id(DefMap::ROOT);
|
||||
let mut worklist = vec![(root, empty)];
|
||||
while let Some((module, mod_path)) = worklist.pop() {
|
||||
let ext_def_map;
|
||||
|
@ -167,7 +197,11 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> 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 {
|
||||
|
@ -233,13 +267,10 @@ impl fmt::Debug for ImportMap {
|
|||
let mut importable_paths: Vec<_> = self
|
||||
.map
|
||||
.iter()
|
||||
.map(|(item, info)| {
|
||||
let ns = match item {
|
||||
ItemInNs::Types(_) => "t",
|
||||
ItemInNs::Values(_) => "v",
|
||||
ItemInNs::Macros(_) => "m",
|
||||
};
|
||||
format!("- {} ({ns})", info.path)
|
||||
.map(|(item, _)| match item {
|
||||
ItemInNs::Types(it) => format!("- {it:?} (t)",),
|
||||
ItemInNs::Values(it) => format!("- {it:?} (v)",),
|
||||
ItemInNs::Macros(it) => format!("- {it:?} (m)",),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -248,9 +279,9 @@ impl fmt::Debug for ImportMap {
|
|||
}
|
||||
}
|
||||
|
||||
fn fst_path(path: &ImportPath) -> String {
|
||||
fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String {
|
||||
let _p = profile::span("fst_path");
|
||||
let mut s = path.to_string();
|
||||
let mut s = path.display(db).to_string();
|
||||
s.make_ascii_lowercase();
|
||||
s
|
||||
}
|
||||
|
@ -343,7 +374,12 @@ impl Query {
|
|||
self
|
||||
}
|
||||
|
||||
fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
|
||||
fn import_matches(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
import: &ImportInfo,
|
||||
enforce_lowercase: bool,
|
||||
) -> bool {
|
||||
let _p = profile::span("import_map::Query::import_matches");
|
||||
if import.is_trait_assoc_item {
|
||||
if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
|
||||
|
@ -354,9 +390,9 @@ impl Query {
|
|||
}
|
||||
|
||||
let mut input = if import.is_trait_assoc_item || self.name_only {
|
||||
import.path.segments.last().unwrap().to_string()
|
||||
import.path.segments.last().unwrap().display(db.upcast()).to_string()
|
||||
} else {
|
||||
import.path.to_string()
|
||||
import.path.display(db).to_string()
|
||||
};
|
||||
if enforce_lowercase || !self.case_sensitive {
|
||||
input.make_ascii_lowercase();
|
||||
|
@ -421,25 +457,27 @@ pub fn search_dependencies(
|
|||
let importables = &import_map.importables[indexed_value.value as usize..];
|
||||
|
||||
let common_importable_data = &import_map.map[&importables[0]];
|
||||
if !query.import_matches(common_importable_data, true) {
|
||||
if !query.import_matches(db, common_importable_data, true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Path shared by the importable items in this group.
|
||||
let common_importables_path_fst = fst_path(&common_importable_data.path);
|
||||
let common_importables_path_fst = fst_path(db, &common_importable_data.path);
|
||||
// Add the items from this `ModPath` group. Those are all subsequent items in
|
||||
// `importables` whose paths match `path`.
|
||||
let iter = importables
|
||||
.iter()
|
||||
.copied()
|
||||
.take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
|
||||
.take_while(|item| {
|
||||
common_importables_path_fst == fst_path(db, &import_map.map[item].path)
|
||||
})
|
||||
.filter(|&item| match item_import_kind(item) {
|
||||
Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
|
||||
None => true,
|
||||
})
|
||||
.filter(|item| {
|
||||
!query.case_sensitive // we've already checked the common importables path case-insensitively
|
||||
|| query.import_matches(&import_map.map[item], false)
|
||||
|| query.import_matches(db, &import_map.map[item], false)
|
||||
});
|
||||
res.extend(iter);
|
||||
|
||||
|
@ -472,7 +510,7 @@ mod tests {
|
|||
use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
|
||||
use expect_test::{expect, Expect};
|
||||
|
||||
use crate::{test_db::TestDB, ItemContainerId, Lookup};
|
||||
use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -496,7 +534,7 @@ mod tests {
|
|||
let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
|
||||
Some(assoc_item_path) => (assoc_item_path, "a"),
|
||||
None => (
|
||||
dependency_imports.path_of(dependency)?.to_string(),
|
||||
dependency_imports.path_of(dependency)?.display(&db).to_string(),
|
||||
match dependency {
|
||||
ItemInNs::Types(ModuleDefId::FunctionId(_))
|
||||
| ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
|
||||
|
@ -547,7 +585,11 @@ mod tests {
|
|||
None
|
||||
}
|
||||
})?;
|
||||
return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?));
|
||||
return Some(format!(
|
||||
"{}::{}",
|
||||
dependency_imports.path_of(trait_)?.display(db),
|
||||
assoc_item_name.display(db.upcast())
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -587,7 +629,7 @@ mod tests {
|
|||
|
||||
let map = db.import_map(krate);
|
||||
|
||||
Some(format!("{name}:\n{map:?}\n"))
|
||||
Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
|
||||
})
|
||||
.sorted()
|
||||
.collect::<String>();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use std::collections::hash_map::Entry;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId};
|
||||
use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use profile::Count;
|
||||
|
@ -358,12 +358,16 @@ impl ItemScope {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dump(&self, buf: &mut String) {
|
||||
pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) {
|
||||
let mut entries: Vec<_> = self.resolutions().collect();
|
||||
entries.sort_by_key(|(name, _)| name.clone());
|
||||
|
||||
for (name, def) in entries {
|
||||
format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
|
||||
format_to!(
|
||||
buf,
|
||||
"{}:",
|
||||
name.map_or("_".to_string(), |name| name.display(db).to_string())
|
||||
);
|
||||
|
||||
if def.types.is_some() {
|
||||
buf.push_str(" t");
|
||||
|
|
|
@ -40,7 +40,6 @@ use std::{
|
|||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::Index,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use ast::{AstNode, HasName, StructKind};
|
||||
|
@ -60,6 +59,7 @@ use rustc_hash::FxHashMap;
|
|||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
use syntax::{ast, match_ast, SyntaxKind};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
|
@ -101,16 +101,14 @@ pub struct ItemTree {
|
|||
top_level: SmallVec<[ModItem; 1]>,
|
||||
attrs: FxHashMap<AttrOwner, RawAttrs>,
|
||||
|
||||
// FIXME: Remove this indirection, an item tree is almost always non-empty?
|
||||
data: Option<Box<ItemTreeData>>,
|
||||
}
|
||||
|
||||
impl ItemTree {
|
||||
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
|
||||
let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
|
||||
let syntax = match db.parse_or_expand(file_id) {
|
||||
Some(node) => node,
|
||||
None => return Default::default(),
|
||||
};
|
||||
let syntax = db.parse_or_expand(file_id);
|
||||
if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax)
|
||||
{
|
||||
// FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
|
||||
|
@ -169,8 +167,8 @@ impl ItemTree {
|
|||
Attrs::filter(db, krate, self.raw_attrs(of).clone())
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self) -> String {
|
||||
pretty::print_item_tree(self)
|
||||
pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
|
||||
pretty::print_item_tree(db.upcast(), self)
|
||||
}
|
||||
|
||||
fn data(&self) -> &ItemTreeData {
|
||||
|
@ -600,19 +598,18 @@ pub struct Function {
|
|||
pub abi: Option<Interned<str>>,
|
||||
pub params: IdxRange<Param>,
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub async_ret_type: Option<Interned<TypeRef>>,
|
||||
pub ast_id: FileAstId<ast::Fn>,
|
||||
pub(crate) flags: FnFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Param {
|
||||
Normal(Option<Name>, Interned<TypeRef>),
|
||||
Normal(Interned<TypeRef>),
|
||||
Varargs,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
pub(crate) struct FnFlags: u8 {
|
||||
const HAS_SELF_PARAM = 1 << 0;
|
||||
const HAS_BODY = 1 << 1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! AST -> `ItemTree` lowering code.
|
||||
|
||||
use std::{collections::hash_map::Entry, sync::Arc};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
|
||||
use syntax::ast::{self, HasModuleItem, HasTypeBounds};
|
||||
|
@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> {
|
|||
db: &'a dyn DefDatabase,
|
||||
tree: ItemTree,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
body_ctx: crate::body::LowerCtx<'a>,
|
||||
body_ctx: crate::lower::LowerCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Ctx<'a> {
|
||||
|
@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
|
|||
db,
|
||||
tree: ItemTree::default(),
|
||||
source_ast_id_map: db.ast_id_map(file),
|
||||
body_ctx: crate::body::LowerCtx::new(db, file),
|
||||
body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
};
|
||||
let ty = Interned::new(self_type);
|
||||
let idx = self.data().params.alloc(Param::Normal(None, ty));
|
||||
let idx = self.data().params.alloc(Param::Normal(ty));
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
|
||||
|
@ -306,19 +306,7 @@ impl<'a> Ctx<'a> {
|
|||
None => {
|
||||
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
|
||||
let ty = Interned::new(type_ref);
|
||||
let mut pat = param.pat();
|
||||
// FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
|
||||
// pattern names at all
|
||||
let name = 'name: loop {
|
||||
match pat {
|
||||
Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
|
||||
Some(ast::Pat::IdentPat(ident)) => {
|
||||
break 'name ident.name().map(|it| it.as_name())
|
||||
}
|
||||
_ => break 'name None,
|
||||
}
|
||||
};
|
||||
self.data().params.alloc(Param::Normal(name, ty))
|
||||
self.data().params.alloc(Param::Normal(ty))
|
||||
}
|
||||
};
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), ¶m, self.hygiene()));
|
||||
|
@ -336,13 +324,12 @@ impl<'a> Ctx<'a> {
|
|||
None => TypeRef::unit(),
|
||||
};
|
||||
|
||||
let (ret_type, async_ret_type) = if func.async_token().is_some() {
|
||||
let async_ret_type = ret_type.clone();
|
||||
let ret_type = if func.async_token().is_some() {
|
||||
let future_impl = desugar_future_path(ret_type);
|
||||
let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
|
||||
(TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
|
||||
TypeRef::ImplTrait(vec![ty_bound])
|
||||
} else {
|
||||
(ret_type, None)
|
||||
ret_type
|
||||
};
|
||||
|
||||
let abi = func.abi().map(lower_abi);
|
||||
|
@ -376,7 +363,6 @@ impl<'a> Ctx<'a> {
|
|||
abi,
|
||||
params,
|
||||
ret_type: Interned::new(ret_type),
|
||||
async_ret_type: async_ret_type.map(Interned::new),
|
||||
ast_id,
|
||||
flags,
|
||||
};
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::db::ExpandDatabase;
|
||||
|
||||
use crate::{
|
||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||
|
@ -10,8 +12,8 @@ use crate::{
|
|||
|
||||
use super::*;
|
||||
|
||||
pub(super) fn print_item_tree(tree: &ItemTree) -> String {
|
||||
let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true };
|
||||
pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
|
||||
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
|
||||
|
||||
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
|
||||
p.print_attrs(attrs, true);
|
||||
|
@ -43,6 +45,7 @@ macro_rules! wln {
|
|||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
tree: &'a ItemTree,
|
||||
buf: String,
|
||||
indent_level: usize,
|
||||
|
@ -88,7 +91,7 @@ impl<'a> Printer<'a> {
|
|||
self,
|
||||
"#{}[{}{}]",
|
||||
inner,
|
||||
attr.path,
|
||||
attr.path.display(self.db),
|
||||
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
|
@ -102,7 +105,7 @@ impl<'a> Printer<'a> {
|
|||
|
||||
fn print_visibility(&mut self, vis: RawVisibilityId) {
|
||||
match &self.tree[vis] {
|
||||
RawVisibility::Module(path) => w!(self, "pub({}) ", path),
|
||||
RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
|
||||
RawVisibility::Public => w!(self, "pub "),
|
||||
};
|
||||
}
|
||||
|
@ -117,7 +120,7 @@ impl<'a> Printer<'a> {
|
|||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||
this.print_attrs_of(field);
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name);
|
||||
w!(this, "{}: ", name.display(self.db));
|
||||
this.print_type_ref(type_ref);
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
@ -131,7 +134,7 @@ impl<'a> Printer<'a> {
|
|||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||
this.print_attrs_of(field);
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name);
|
||||
w!(this, "{}: ", name.display(self.db));
|
||||
this.print_type_ref(type_ref);
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
@ -164,20 +167,20 @@ impl<'a> Printer<'a> {
|
|||
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
||||
match &use_tree.kind {
|
||||
UseTreeKind::Single { path, alias } => {
|
||||
w!(self, "{}", path);
|
||||
w!(self, "{}", path.display(self.db));
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias);
|
||||
}
|
||||
}
|
||||
UseTreeKind::Glob { path } => {
|
||||
if let Some(path) = path {
|
||||
w!(self, "{}::", path);
|
||||
w!(self, "{}::", path.display(self.db));
|
||||
}
|
||||
w!(self, "*");
|
||||
}
|
||||
UseTreeKind::Prefixed { prefix, list } => {
|
||||
if let Some(prefix) = prefix {
|
||||
w!(self, "{}::", prefix);
|
||||
w!(self, "{}::", prefix.display(self.db));
|
||||
}
|
||||
w!(self, "{{");
|
||||
for (i, tree) in list.iter().enumerate() {
|
||||
|
@ -205,7 +208,7 @@ impl<'a> Printer<'a> {
|
|||
ModItem::ExternCrate(it) => {
|
||||
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "extern crate {}", name);
|
||||
w!(self, "extern crate {}", name.display(self.db));
|
||||
if let Some(alias) = alias {
|
||||
w!(self, " as {}", alias);
|
||||
}
|
||||
|
@ -233,7 +236,6 @@ impl<'a> Printer<'a> {
|
|||
abi,
|
||||
params,
|
||||
ret_type,
|
||||
async_ret_type: _,
|
||||
ast_id: _,
|
||||
flags,
|
||||
} = &self.tree[it];
|
||||
|
@ -253,26 +255,20 @@ impl<'a> Printer<'a> {
|
|||
if let Some(abi) = abi {
|
||||
w!(self, "extern \"{}\" ", abi);
|
||||
}
|
||||
w!(self, "fn {}", name);
|
||||
w!(self, "fn {}", name.display(self.db));
|
||||
self.print_generic_params(explicit_generic_params);
|
||||
w!(self, "(");
|
||||
if !params.is_empty() {
|
||||
self.indented(|this| {
|
||||
for (i, param) in params.clone().enumerate() {
|
||||
for param in params.clone() {
|
||||
this.print_attrs_of(param);
|
||||
match &this.tree[param] {
|
||||
Param::Normal(name, ty) => {
|
||||
match name {
|
||||
Some(name) => w!(this, "{}: ", name),
|
||||
None => w!(this, "_: "),
|
||||
Param::Normal(ty) => {
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
w!(this, "self: ");
|
||||
}
|
||||
this.print_type_ref(ty);
|
||||
w!(this, ",");
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
|
||||
wln!(this, " // self");
|
||||
} else {
|
||||
wln!(this);
|
||||
}
|
||||
wln!(this, ",");
|
||||
}
|
||||
Param::Varargs => {
|
||||
wln!(this, "...");
|
||||
|
@ -293,7 +289,7 @@ impl<'a> Printer<'a> {
|
|||
ModItem::Struct(it) => {
|
||||
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "struct {}", name);
|
||||
w!(self, "struct {}", name.display(self.db));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_fields_and_where_clause(fields, generic_params);
|
||||
if matches!(fields, Fields::Record(_)) {
|
||||
|
@ -305,7 +301,7 @@ impl<'a> Printer<'a> {
|
|||
ModItem::Union(it) => {
|
||||
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "union {}", name);
|
||||
w!(self, "union {}", name.display(self.db));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_fields_and_where_clause(fields, generic_params);
|
||||
if matches!(fields, Fields::Record(_)) {
|
||||
|
@ -317,14 +313,14 @@ impl<'a> Printer<'a> {
|
|||
ModItem::Enum(it) => {
|
||||
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "enum {}", name);
|
||||
w!(self, "enum {}", name.display(self.db));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
self.indented(|this| {
|
||||
for variant in variants.clone() {
|
||||
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
|
||||
this.print_attrs_of(variant);
|
||||
w!(this, "{}", name);
|
||||
w!(this, "{}", name.display(self.db));
|
||||
this.print_fields(fields);
|
||||
wln!(this, ",");
|
||||
}
|
||||
|
@ -336,7 +332,7 @@ impl<'a> Printer<'a> {
|
|||
self.print_visibility(*visibility);
|
||||
w!(self, "const ");
|
||||
match name {
|
||||
Some(name) => w!(self, "{}", name),
|
||||
Some(name) => w!(self, "{}", name.display(self.db)),
|
||||
None => w!(self, "_"),
|
||||
}
|
||||
w!(self, ": ");
|
||||
|
@ -350,7 +346,7 @@ impl<'a> Printer<'a> {
|
|||
if *mutable {
|
||||
w!(self, "mut ");
|
||||
}
|
||||
w!(self, "{}: ", name);
|
||||
w!(self, "{}: ", name.display(self.db));
|
||||
self.print_type_ref(type_ref);
|
||||
w!(self, " = _;");
|
||||
wln!(self);
|
||||
|
@ -372,7 +368,7 @@ impl<'a> Printer<'a> {
|
|||
if *is_auto {
|
||||
w!(self, "auto ");
|
||||
}
|
||||
w!(self, "trait {}", name);
|
||||
w!(self, "trait {}", name.display(self.db));
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
self.indented(|this| {
|
||||
|
@ -385,7 +381,7 @@ impl<'a> Printer<'a> {
|
|||
ModItem::TraitAlias(it) => {
|
||||
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "trait {}", name);
|
||||
w!(self, "trait {}", name.display(self.db));
|
||||
self.print_generic_params(generic_params);
|
||||
w!(self, " = ");
|
||||
self.print_where_clause(generic_params);
|
||||
|
@ -418,7 +414,7 @@ impl<'a> Printer<'a> {
|
|||
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
|
||||
&self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "type {}", name);
|
||||
w!(self, "type {}", name.display(self.db));
|
||||
self.print_generic_params(generic_params);
|
||||
if !bounds.is_empty() {
|
||||
w!(self, ": ");
|
||||
|
@ -435,7 +431,7 @@ impl<'a> Printer<'a> {
|
|||
ModItem::Mod(it) => {
|
||||
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "mod {}", name);
|
||||
w!(self, "mod {}", name.display(self.db));
|
||||
match kind {
|
||||
ModKind::Inline { items } => {
|
||||
w!(self, " {{");
|
||||
|
@ -453,16 +449,16 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
ModItem::MacroCall(it) => {
|
||||
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
|
||||
wln!(self, "{}!(...);", path);
|
||||
wln!(self, "{}!(...);", path.display(self.db));
|
||||
}
|
||||
ModItem::MacroRules(it) => {
|
||||
let MacroRules { name, ast_id: _ } = &self.tree[it];
|
||||
wln!(self, "macro_rules! {} {{ ... }}", name);
|
||||
wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
|
||||
}
|
||||
ModItem::MacroDef(it) => {
|
||||
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
|
||||
self.print_visibility(*visibility);
|
||||
wln!(self, "macro {} {{ ... }}", name);
|
||||
wln!(self, "macro {} {{ ... }}", name.display(self.db));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -470,15 +466,15 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
|
||||
fn print_type_ref(&mut self, type_ref: &TypeRef) {
|
||||
print_type_ref(type_ref, self).unwrap();
|
||||
print_type_ref(self.db, type_ref, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
|
||||
print_type_bounds(bounds, self).unwrap();
|
||||
print_type_bounds(self.db, bounds, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_path(&mut self, path: &Path) {
|
||||
print_path(path, self).unwrap();
|
||||
print_path(self.db, path, self).unwrap();
|
||||
}
|
||||
|
||||
fn print_generic_params(&mut self, params: &GenericParams) {
|
||||
|
@ -493,7 +489,7 @@ impl<'a> Printer<'a> {
|
|||
w!(self, ", ");
|
||||
}
|
||||
first = false;
|
||||
w!(self, "{}", lt.name);
|
||||
w!(self, "{}", lt.name.display(self.db));
|
||||
}
|
||||
for (idx, x) in params.type_or_consts.iter() {
|
||||
if !first {
|
||||
|
@ -502,11 +498,11 @@ impl<'a> Printer<'a> {
|
|||
first = false;
|
||||
match x {
|
||||
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
|
||||
Some(name) => w!(self, "{}", name),
|
||||
Some(name) => w!(self, "{}", name.display(self.db)),
|
||||
None => w!(self, "_anon_{}", idx.into_raw()),
|
||||
},
|
||||
TypeOrConstParamData::ConstParamData(konst) => {
|
||||
w!(self, "const {}: ", konst.name);
|
||||
w!(self, "const {}: ", konst.name.display(self.db));
|
||||
self.print_type_ref(&konst.ty);
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +534,12 @@ impl<'a> Printer<'a> {
|
|||
let (target, bound) = match pred {
|
||||
WherePredicate::TypeBound { target, bound } => (target, bound),
|
||||
WherePredicate::Lifetime { target, bound } => {
|
||||
wln!(this, "{}: {},", target.name, bound.name);
|
||||
wln!(
|
||||
this,
|
||||
"{}: {},",
|
||||
target.name.display(self.db),
|
||||
bound.name.display(self.db)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
WherePredicate::ForLifetime { lifetimes, target, bound } => {
|
||||
|
@ -547,7 +548,7 @@ impl<'a> Printer<'a> {
|
|||
if i != 0 {
|
||||
w!(this, ", ");
|
||||
}
|
||||
w!(this, "{}", lt);
|
||||
w!(this, "{}", lt.display(self.db));
|
||||
}
|
||||
w!(this, "> ");
|
||||
(target, bound)
|
||||
|
@ -558,7 +559,7 @@ impl<'a> Printer<'a> {
|
|||
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) => {
|
||||
match ¶ms.type_or_consts[*id].name() {
|
||||
Some(name) => w!(this, "{}", name),
|
||||
Some(name) => w!(this, "{}", name.display(self.db)),
|
||||
None => w!(this, "_anon_{}", id.into_raw()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
|
|||
fn check(ra_fixture: &str, expect: Expect) {
|
||||
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||
let item_tree = db.file_item_tree(file_id.into());
|
||||
let pretty = item_tree.pretty_print();
|
||||
let pretty = item_tree.pretty_print(&db);
|
||||
expect.assert_eq(&pretty);
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
|
|||
fn method(&self);
|
||||
}
|
||||
"#,
|
||||
expect![[r##"
|
||||
expect![[r#"
|
||||
pub static mut ST: () = _;
|
||||
|
||||
pub(self) const _: Anon = _;
|
||||
|
@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
|
|||
#[inner_attr_in_fn]
|
||||
pub(self) fn f(
|
||||
#[attr]
|
||||
arg: u8,
|
||||
_: (),
|
||||
u8,
|
||||
(),
|
||||
) -> () { ... }
|
||||
|
||||
pub(self) trait Tr<Self>
|
||||
|
@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
|
|||
pub(self) type Assoc: AssocBound = Default;
|
||||
|
||||
pub(self) fn method(
|
||||
_: &Self, // self
|
||||
self: &Self,
|
||||
) -> ();
|
||||
}
|
||||
"##]],
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
|||
T: 'b
|
||||
{
|
||||
pub(self) fn f<G>(
|
||||
arg: impl Copy,
|
||||
impl Copy,
|
||||
) -> impl Copy
|
||||
where
|
||||
G: 'a { ... }
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
//!
|
||||
//! This attribute to tell the compiler about semi built-in std library
|
||||
//! features, such as Fn family of traits.
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::SmolStr;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
|
||||
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
|
||||
FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -200,7 +199,7 @@ pub enum GenericRequirement {
|
|||
|
||||
macro_rules! language_item_table {
|
||||
(
|
||||
$( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )*
|
||||
$( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )*
|
||||
) => {
|
||||
|
||||
/// A representation of all the valid language items in Rust.
|
||||
|
@ -220,11 +219,6 @@ macro_rules! language_item_table {
|
|||
}
|
||||
}
|
||||
|
||||
/// Opposite of [`LangItem::name`]
|
||||
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
|
||||
Self::from_str(name.as_str()?)
|
||||
}
|
||||
|
||||
/// Opposite of [`LangItem::name`]
|
||||
pub fn from_str(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
|
@ -236,84 +230,100 @@ macro_rules! language_item_table {
|
|||
}
|
||||
}
|
||||
|
||||
impl LangItem {
|
||||
/// Opposite of [`LangItem::name`]
|
||||
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
|
||||
Self::from_str(name.as_str()?)
|
||||
}
|
||||
|
||||
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
||||
let t = db.lang_item(start_crate, *self)?;
|
||||
Some(Path::LangItem(t))
|
||||
}
|
||||
}
|
||||
|
||||
language_item_table! {
|
||||
// Variant name, Name, Getter method name, Target Generic requirements;
|
||||
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
|
||||
StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
|
||||
StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
|
||||
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
|
||||
StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
|
||||
Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Clone, clone, clone_trait, Target::Trait, GenericRequirement::None;
|
||||
Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
|
||||
StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
|
||||
Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
|
||||
Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
|
||||
/// The associated item of the [`DiscriminantKind`] trait.
|
||||
Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
|
||||
Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
|
||||
Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
|
||||
DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
|
||||
PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
|
||||
Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
|
||||
DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
Drop, drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||
Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||
FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
|
||||
CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
|
||||
// language items relating to transmutability
|
||||
TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
|
||||
TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
|
||||
TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
|
||||
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
|
||||
|
||||
Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
|
||||
VaList, va_list, va_list, Target::Struct, GenericRequirement::None;
|
||||
UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
|
||||
VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
|
||||
Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
|
||||
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
|
||||
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None;
|
||||
Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None;
|
||||
Pin, pin, pin_type, Target::Struct, GenericRequirement::None;
|
||||
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None;
|
||||
Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
|
||||
Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
|
||||
|
||||
PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
|
||||
|
||||
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
|
||||
// various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
|
||||
|
@ -322,92 +332,103 @@ language_item_table! {
|
|||
// in the sense that a crate is not required to have it defined to use it, but a final product
|
||||
// is required to define it somewhere. Additionally, there are restrictions on crates that use
|
||||
// a weak lang item, but do not have it defined.
|
||||
Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
|
||||
PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None;
|
||||
ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
|
||||
PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None;
|
||||
PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None;
|
||||
PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
|
||||
PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
|
||||
Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
|
||||
PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
|
||||
ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
|
||||
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
|
||||
PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
|
||||
PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
|
||||
PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
|
||||
/// libstd panic entry point. Necessary for const eval to be able to catch it
|
||||
BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
|
||||
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
|
||||
BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
||||
// Lang items needed for `format_args!()`.
|
||||
FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
|
||||
FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
|
||||
FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None;
|
||||
FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None;
|
||||
FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None;
|
||||
FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
|
||||
BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
||||
|
||||
EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
|
||||
EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
|
||||
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
|
||||
OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
|
||||
EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
|
||||
EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
|
||||
|
||||
PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
|
||||
OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
|
||||
|
||||
ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
|
||||
PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
|
||||
|
||||
MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
|
||||
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
|
||||
|
||||
MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
|
||||
|
||||
/// Align offset for stride != 1; must not panic.
|
||||
AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
|
||||
AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
Termination, termination, termination, Target::Trait, GenericRequirement::None;
|
||||
Termination, sym::termination, termination, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Try, Try, try_trait, Target::Trait, GenericRequirement::None;
|
||||
Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
|
||||
// Language items from AST lowering
|
||||
TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
|
||||
TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
|
||||
PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
Poll, Poll, poll, Target::Enum, GenericRequirement::None;
|
||||
PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
|
||||
PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
|
||||
ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
|
||||
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
|
||||
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
// FIXME(swatinem): the following lang items are used for async lowering and
|
||||
// should become obsolete eventually.
|
||||
ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
|
||||
GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None;
|
||||
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
|
||||
GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
Context, Context, context, Target::Struct, GenericRequirement::None;
|
||||
FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
Context, sym::Context, context, Target::Struct, GenericRequirement::None;
|
||||
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
|
||||
FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
Option, sym::Option, option_type, Target::Enum, GenericRequirement::None;
|
||||
OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None;
|
||||
OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None;
|
||||
OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None;
|
||||
ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
|
||||
ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
|
||||
ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None;
|
||||
ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
|
||||
ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
|
||||
ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None;
|
||||
IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
|
||||
|
||||
IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
|
||||
PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
|
||||
PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
|
||||
|
||||
RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
Range, Range, range_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
|
||||
|
||||
String, String, string, Target::Struct, GenericRequirement::None;
|
||||
String, sym::String, string, Target::Struct, GenericRequirement::None;
|
||||
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
//! Definitions needed for computing data layout of types.
|
||||
|
||||
use std::cmp;
|
||||
|
||||
use la_arena::{Idx, RawIdx};
|
||||
pub use rustc_abi::{
|
||||
Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
|
||||
LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
|
||||
TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
|
||||
};
|
||||
|
||||
use crate::LocalEnumVariantId;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
|
||||
|
||||
impl rustc_index::vec::Idx for RustcEnumVariantIdx {
|
||||
fn new(idx: usize) -> Self {
|
||||
RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
|
||||
}
|
||||
|
||||
fn index(self) -> usize {
|
||||
u32::from(self.0.into_raw()) as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
|
||||
pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
|
||||
pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
|
||||
|
||||
pub trait IntegerExt {
|
||||
fn repr_discr(
|
||||
dl: &TargetDataLayout,
|
||||
repr: &ReprOptions,
|
||||
min: i128,
|
||||
max: i128,
|
||||
) -> Result<(Integer, bool), LayoutError>;
|
||||
}
|
||||
|
||||
impl IntegerExt for Integer {
|
||||
/// Finds the appropriate Integer type and signedness for the given
|
||||
/// signed discriminant range and `#[repr]` attribute.
|
||||
/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
|
||||
/// that shouldn't affect anything, other than maybe debuginfo.
|
||||
fn repr_discr(
|
||||
dl: &TargetDataLayout,
|
||||
repr: &ReprOptions,
|
||||
min: i128,
|
||||
max: i128,
|
||||
) -> Result<(Integer, bool), LayoutError> {
|
||||
// Theoretically, negative values could be larger in unsigned representation
|
||||
// than the unsigned representation of the signed minimum. However, if there
|
||||
// are any negative values, the only valid unsigned representation is u128
|
||||
// which can fit all i128 values, so the result remains unaffected.
|
||||
let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
|
||||
let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
|
||||
|
||||
if let Some(ity) = repr.int {
|
||||
let discr = Integer::from_attr(dl, ity);
|
||||
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
|
||||
if discr < fit {
|
||||
return Err(LayoutError::UserError(
|
||||
"Integer::repr_discr: `#[repr]` hint too small for \
|
||||
discriminant range of enum "
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
return Ok((discr, ity.is_signed()));
|
||||
}
|
||||
|
||||
let at_least = if repr.c() {
|
||||
// This is usually I32, however it can be different on some platforms,
|
||||
// notably hexagon and arm-none/thumb-none
|
||||
dl.c_enum_min_size
|
||||
} else {
|
||||
// repr(Rust) enums try to be as small as possible
|
||||
Integer::I8
|
||||
};
|
||||
|
||||
// If there are no negative values, we can use the unsigned fit.
|
||||
Ok(if min >= 0 {
|
||||
(cmp::max(unsigned_fit, at_least), false)
|
||||
} else {
|
||||
(cmp::max(signed_fit, at_least), true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum LayoutError {
|
||||
UserError(String),
|
||||
SizeOverflow,
|
||||
TargetLayoutNotAvailable,
|
||||
HasPlaceholder,
|
||||
NotImplemented,
|
||||
Unknown,
|
||||
}
|
|
@ -18,24 +18,23 @@ pub mod db;
|
|||
|
||||
pub mod attr;
|
||||
pub mod path;
|
||||
pub mod type_ref;
|
||||
pub mod builtin_type;
|
||||
pub mod builtin_attr;
|
||||
pub mod per_ns;
|
||||
pub mod item_scope;
|
||||
|
||||
pub mod lower;
|
||||
pub mod expander;
|
||||
|
||||
pub mod dyn_map;
|
||||
pub mod keys;
|
||||
|
||||
pub mod item_tree;
|
||||
|
||||
pub mod adt;
|
||||
pub mod data;
|
||||
pub mod generics;
|
||||
pub mod lang_item;
|
||||
pub mod layout;
|
||||
|
||||
pub mod expr;
|
||||
pub mod hir;
|
||||
pub use self::hir::type_ref;
|
||||
pub mod body;
|
||||
pub mod resolver;
|
||||
|
||||
|
@ -49,29 +48,34 @@ pub mod visibility;
|
|||
pub mod find_path;
|
||||
pub mod import_map;
|
||||
|
||||
pub use rustc_abi as layout;
|
||||
use triomphe::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_db;
|
||||
#[cfg(test)]
|
||||
mod macro_expansion_tests;
|
||||
mod pretty;
|
||||
|
||||
use std::{
|
||||
hash::{Hash, Hasher},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
|
||||
use base_db::{
|
||||
impl_intern_key,
|
||||
salsa::{self, InternId},
|
||||
CrateId, ProcMacroKind,
|
||||
};
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
attrs::{Attr, AttrId, AttrInput},
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
|
||||
db::ExpandDatabase,
|
||||
eager::expand_eager_macro,
|
||||
hygiene::Hygiene,
|
||||
proc_macro::ProcMacroExpander,
|
||||
AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
|
||||
MacroDefKind, UnresolvedMacro,
|
||||
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
|
||||
MacroDefId, MacroDefKind, UnresolvedMacro,
|
||||
};
|
||||
use item_tree::ExternBlock;
|
||||
use la_arena::Idx;
|
||||
|
@ -82,8 +86,8 @@ use syntax::ast;
|
|||
use ::tt::token_id as tt;
|
||||
|
||||
use crate::{
|
||||
adt::VariantData,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::VariantData,
|
||||
item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
|
||||
Static, Struct, Trait, TraitAlias, TypeAlias, Union,
|
||||
|
@ -104,13 +108,7 @@ pub struct ModuleId {
|
|||
impl ModuleId {
|
||||
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
|
||||
match self.block {
|
||||
Some(block) => {
|
||||
db.block_def_map(block).unwrap_or_else(|| {
|
||||
// NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s,
|
||||
// so the `DefMap` here must exist.
|
||||
unreachable!("no `block_def_map` for `ModuleId` {:?}", self);
|
||||
})
|
||||
}
|
||||
Some(block) => db.block_def_map(block),
|
||||
None => db.crate_def_map(self.krate),
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +234,7 @@ pub struct EnumVariantId {
|
|||
pub local_id: LocalEnumVariantId,
|
||||
}
|
||||
|
||||
pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
|
||||
pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FieldId {
|
||||
|
@ -244,7 +242,7 @@ pub struct FieldId {
|
|||
pub local_id: LocalFieldId,
|
||||
}
|
||||
|
||||
pub type LocalFieldId = Idx<adt::FieldData>;
|
||||
pub type LocalFieldId = Idx<data::adt::FieldData>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
|
@ -478,6 +476,46 @@ impl_from!(
|
|||
for ModuleDefId
|
||||
);
|
||||
|
||||
// FIXME: make this a DefWithBodyId
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct AnonymousConstId(InternId);
|
||||
impl_intern_key!(AnonymousConstId);
|
||||
|
||||
/// A constant, which might appears as a const item, an annonymous const block in expressions
|
||||
/// or patterns, or as a constant in types with const generics.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum GeneralConstId {
|
||||
ConstId(ConstId),
|
||||
AnonymousConstId(AnonymousConstId),
|
||||
}
|
||||
|
||||
impl_from!(ConstId, AnonymousConstId for GeneralConstId);
|
||||
|
||||
impl GeneralConstId {
|
||||
pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option<GenericDefId> {
|
||||
match self {
|
||||
GeneralConstId::ConstId(x) => Some(x.into()),
|
||||
GeneralConstId::AnonymousConstId(x) => {
|
||||
let (parent, _) = db.lookup_intern_anonymous_const(x);
|
||||
parent.as_generic_def_id()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(self, db: &dyn db::DefDatabase) -> String {
|
||||
match self {
|
||||
GeneralConstId::ConstId(const_id) => db
|
||||
.const_data(const_id)
|
||||
.name
|
||||
.as_ref()
|
||||
.and_then(|x| x.as_str())
|
||||
.unwrap_or("_")
|
||||
.to_owned(),
|
||||
GeneralConstId::AnonymousConstId(id) => format!("{{anonymous const {id:?}}}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The defs which have a body.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DefWithBodyId {
|
||||
|
@ -799,52 +837,43 @@ impl AttrDefId {
|
|||
pub trait AsMacroCall {
|
||||
fn as_call_id(
|
||||
&self,
|
||||
db: &dyn db::DefDatabase,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
) -> Option<MacroCallId> {
|
||||
self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok()
|
||||
self.as_call_id_with_errors(db, krate, resolver).ok()?.value
|
||||
}
|
||||
|
||||
fn as_call_id_with_errors(
|
||||
&self,
|
||||
db: &dyn db::DefDatabase,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
error_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
|
||||
}
|
||||
|
||||
impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
fn as_call_id_with_errors(
|
||||
&self,
|
||||
db: &dyn db::DefDatabase,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
mut error_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> 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 h = Hygiene::new(db.upcast(), self.file_id);
|
||||
let path =
|
||||
self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
|
||||
let h = Hygiene::new(db, self.file_id);
|
||||
let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
|
||||
|
||||
let path = match error_sink
|
||||
.option(path, || ExpandError::Other("malformed macro invocation".into()))
|
||||
{
|
||||
Ok(path) => path,
|
||||
Err(error) => {
|
||||
return Ok(Err(error));
|
||||
}
|
||||
let Some(path) = path else {
|
||||
return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
|
||||
};
|
||||
|
||||
macro_call_as_call_id(
|
||||
macro_call_as_call_id_(
|
||||
db,
|
||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||
expands_to,
|
||||
krate,
|
||||
resolver,
|
||||
error_sink,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -863,26 +892,37 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
|
|||
}
|
||||
|
||||
fn macro_call_as_call_id(
|
||||
db: &dyn db::DefDatabase,
|
||||
db: &dyn ExpandDatabase,
|
||||
call: &AstIdWithPath<ast::MacroCall>,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
error_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<Option<MacroCallId>, UnresolvedMacro> {
|
||||
macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
|
||||
}
|
||||
|
||||
fn macro_call_as_call_id_(
|
||||
db: &dyn ExpandDatabase,
|
||||
call: &AstIdWithPath<ast::MacroCall>,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
let def =
|
||||
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
||||
|
||||
let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
|
||||
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
|
||||
|
||||
expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
|
||||
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
|
||||
expand_eager_macro(db, krate, macro_call, def, &resolver)?
|
||||
} else {
|
||||
Ok(def.as_lazy_macro(
|
||||
db.upcast(),
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
|
||||
))
|
||||
ExpandResult {
|
||||
value: Some(def.as_lazy_macro(
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
|
||||
)),
|
||||
err: None,
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
@ -986,7 +1026,6 @@ fn attr_macro_as_call_id(
|
|||
macro_attr: &Attr,
|
||||
krate: CrateId,
|
||||
def: MacroDefId,
|
||||
is_derive: bool,
|
||||
) -> MacroCallId {
|
||||
let arg = match macro_attr.input.as_deref() {
|
||||
Some(AttrInput::TokenTree(tt, map)) => (
|
||||
|
@ -1007,7 +1046,6 @@ fn attr_macro_as_call_id(
|
|||
ast_id: item_attr.ast_id,
|
||||
attr_args: Arc::new(arg),
|
||||
invoc_attr_index: macro_attr.id,
|
||||
is_derive,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
45
crates/hir-def/src/lower.rs
Normal file
45
crates/hir-def/src/lower.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
//! Context for lowering paths.
|
||||
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::DefDatabase, path::Path};
|
||||
|
||||
pub struct LowerCtx<'a> {
|
||||
pub db: &'a dyn DefDatabase,
|
||||
hygiene: Hygiene,
|
||||
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
|
||||
}
|
||||
|
||||
impl<'a> LowerCtx<'a> {
|
||||
pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
|
||||
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
|
||||
}
|
||||
|
||||
pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
|
||||
LowerCtx {
|
||||
db,
|
||||
hygiene: Hygiene::new(db.upcast(), file_id),
|
||||
ast_id_map: Some((file_id, OnceCell::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
|
||||
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
&self.hygiene
|
||||
}
|
||||
|
||||
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
|
||||
Path::from_src(ast, self)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
|
||||
let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
|
||||
let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
|
||||
Some(InFile::new(file_id, ast_id_map.ast_id(item)))
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ struct Foo;
|
|||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
|
||||
impl < > core::marker::Copy for Foo< > {}"#]],
|
||||
impl < > core::marker::Copy for Foo< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ macro Copy {}
|
|||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
|
||||
impl < > crate ::marker::Copy for Foo< > {}"#]],
|
||||
impl < > crate ::marker::Copy for Foo< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ struct Foo<A, B>;
|
|||
#[derive(Copy)]
|
||||
struct Foo<A, B>;
|
||||
|
||||
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
|
||||
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
|
|||
#[derive(Copy)]
|
||||
struct Foo<A, B, 'a, 'b>;
|
||||
|
||||
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
|
||||
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -84,13 +84,33 @@ fn test_clone_expand() {
|
|||
r#"
|
||||
//- minicore: derive, clone
|
||||
#[derive(Clone)]
|
||||
struct Foo<A, B>;
|
||||
enum Command<A, B> {
|
||||
Move { x: A, y: B },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
#[derive(Clone)]
|
||||
struct Foo<A, B>;
|
||||
enum Command<A, B> {
|
||||
Move { x: A, y: B },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
|
||||
impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
|
||||
impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where {
|
||||
fn clone(&self ) -> Self {
|
||||
match self {
|
||||
Command::Move {
|
||||
x: x, y: y,
|
||||
}
|
||||
=>Command::Move {
|
||||
x: x.clone(), y: y.clone(),
|
||||
}
|
||||
, Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump,
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -106,6 +126,270 @@ struct Foo<const X: usize, T>(u32);
|
|||
#[derive(Clone)]
|
||||
struct Foo<const X: usize, T>(u32);
|
||||
|
||||
impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
|
||||
impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {
|
||||
fn clone(&self ) -> Self {
|
||||
match self {
|
||||
Foo(f0, )=>Foo(f0.clone(), ),
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_expand() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, default
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
field1: i32,
|
||||
field2: (),
|
||||
}
|
||||
#[derive(Default)]
|
||||
enum Bar {
|
||||
Foo(u8),
|
||||
#[default]
|
||||
Bar,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
field1: i32,
|
||||
field2: (),
|
||||
}
|
||||
#[derive(Default)]
|
||||
enum Bar {
|
||||
Foo(u8),
|
||||
#[default]
|
||||
Bar,
|
||||
}
|
||||
|
||||
impl < > core::default::Default for Foo< > where {
|
||||
fn default() -> Self {
|
||||
Foo {
|
||||
field1: core::default::Default::default(), field2: core::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl < > core::default::Default for Bar< > where {
|
||||
fn default() -> Self {
|
||||
Bar::Bar
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq_expand() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, eq
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
|
||||
impl < > core::cmp::PartialEq for Command< > where {
|
||||
fn eq(&self , other: &Self ) -> bool {
|
||||
match (self , other) {
|
||||
(Command::Move {
|
||||
x: x_self, y: y_self,
|
||||
}
|
||||
, Command::Move {
|
||||
x: x_other, y: y_other,
|
||||
}
|
||||
)=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false
|
||||
}
|
||||
}
|
||||
}
|
||||
impl < > core::cmp::Eq for Command< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_ord_expand() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, ord
|
||||
#[derive(PartialOrd, Ord)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
#[derive(PartialOrd, Ord)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
|
||||
impl < > core::cmp::PartialOrd for Command< > where {
|
||||
fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> {
|
||||
match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
|
||||
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
|
||||
match (self , other) {
|
||||
(Command::Move {
|
||||
x: x_self, y: y_self,
|
||||
}
|
||||
, Command::Move {
|
||||
x: x_other, y: y_other,
|
||||
}
|
||||
)=>match x_self.partial_cmp(&x_other) {
|
||||
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
|
||||
match y_self.partial_cmp(&y_other) {
|
||||
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
|
||||
core::option::Option::Some(core::cmp::Ordering::Equal)
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
|
||||
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
|
||||
core::option::Option::Some(core::cmp::Ordering::Equal)
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
, (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
|
||||
}
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl < > core::cmp::Ord for Command< > where {
|
||||
fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
|
||||
match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
|
||||
core::cmp::Ordering::Equal=> {
|
||||
match (self , other) {
|
||||
(Command::Move {
|
||||
x: x_self, y: y_self,
|
||||
}
|
||||
, Command::Move {
|
||||
x: x_other, y: y_other,
|
||||
}
|
||||
)=>match x_self.cmp(&x_other) {
|
||||
core::cmp::Ordering::Equal=> {
|
||||
match y_self.cmp(&y_other) {
|
||||
core::cmp::Ordering::Equal=> {
|
||||
core::cmp::Ordering::Equal
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
|
||||
core::cmp::Ordering::Equal=> {
|
||||
core::cmp::Ordering::Equal
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
, (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
c=>return c,
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_expand() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, hash
|
||||
use core::hash::Hash;
|
||||
|
||||
#[derive(Hash)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
use core::hash::Hash;
|
||||
|
||||
#[derive(Hash)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
|
||||
impl < > core::hash::Hash for Command< > where {
|
||||
fn hash<H: core::hash::Hasher>(&self , state: &mut H) {
|
||||
core::mem::discriminant(self ).hash(state);
|
||||
match self {
|
||||
Command::Move {
|
||||
x: x, y: y,
|
||||
}
|
||||
=> {
|
||||
x.hash(state);
|
||||
y.hash(state);
|
||||
}
|
||||
, Command::Do(f0, )=> {
|
||||
f0.hash(state);
|
||||
}
|
||||
, Command::Jump=> {}
|
||||
,
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_expand() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, fmt
|
||||
use core::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
use core::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
|
||||
impl < > core::fmt::Debug for Command< > where {
|
||||
fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
Command::Move {
|
||||
x: x, y: y,
|
||||
}
|
||||
=>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"),
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ macro_rules! column {() => {}}
|
|||
|
||||
fn main() { column!(); }
|
||||
"#,
|
||||
expect![[r##"
|
||||
expect![[r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! column {() => {}}
|
||||
|
||||
fn main() { 0; }
|
||||
"##]],
|
||||
fn main() { 0 as u32; }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,12 @@ macro_rules! line {() => {}}
|
|||
|
||||
fn main() { line!() }
|
||||
"#,
|
||||
expect![[r##"
|
||||
expect![[r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! line {() => {}}
|
||||
|
||||
fn main() { 0 }
|
||||
"##]],
|
||||
fn main() { 0 as u32 }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
|
|||
#[rustc_builtin_macro]
|
||||
macro_rules! option_env {() => {}}
|
||||
|
||||
fn main() { $crate::option::Option::None:: < &str>; }
|
||||
fn main() { ::core::option::Option::None:: < &str>; }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ fn main() {
|
|||
format_args!("{} {:?}", arg1(a, b, c), arg2);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
|
@ -201,9 +201,9 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]);
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]);
|
||||
}
|
||||
"#]],
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,7 @@ fn main() {
|
|||
format_args!("{} {:?}", a::<A,B>(), b);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
|
@ -229,9 +229,76 @@ macro_rules! format_args {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]);
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
|
||||
}
|
||||
"#]],
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_args_expand_with_raw_strings() {
|
||||
check(
|
||||
r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
|
||||
}
|
||||
|
||||
fn main() {
|
||||
format_args!(
|
||||
r#"{},mismatch,"{}","{}""#,
|
||||
location_csv_pat(db, &analysis, vfs, &sm, pat_id),
|
||||
mismatch.expected.display(db),
|
||||
mismatch.actual.display(db)
|
||||
);
|
||||
}
|
||||
"##,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_args_expand_eager() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat {}
|
||||
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
|
||||
}
|
||||
|
||||
fn main() {
|
||||
format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b);
|
||||
}
|
||||
"#,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat {}
|
||||
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -250,7 +317,7 @@ fn main() {
|
|||
format_args!/*+errors*/("{} {:?}", a.);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
|
@ -259,10 +326,10 @@ macro_rules! format_args {
|
|||
|
||||
fn main() {
|
||||
let _ =
|
||||
/* parse error: expected field name or number */
|
||||
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]);
|
||||
/* error: no rule matches input tokens *//* parse error: expected field name or number */
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]);
|
||||
}
|
||||
"#]],
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
mod tt_conversion;
|
||||
mod matching;
|
||||
mod meta_syntax;
|
||||
mod metavar_expr;
|
||||
mod regression;
|
||||
|
||||
use expect_test::expect;
|
||||
|
@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22
|
|||
);
|
||||
}
|
||||
#[test]
|
||||
fn float_field_acces_macro_input() {
|
||||
fn float_field_access_macro_input() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
|
@ -922,7 +923,7 @@ macro_rules! m {
|
|||
|
||||
fn bar() -> &'a Baz<u8> {}
|
||||
|
||||
fn bar() -> extern "Rust"fn() -> Ret {}
|
||||
fn bar() -> extern "Rust" fn() -> Ret {}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1293,19 +1294,53 @@ ok!();
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_vertical_bar_with_pat() {
|
||||
fn test_vertical_bar_with_pat_param() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m { (|$pat:pat| ) => { ok!(); } }
|
||||
macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
|
||||
m! { |x| }
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m { (|$pat:pat| ) => { ok!(); } }
|
||||
macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
|
||||
ok!();
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_std_matches() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
|
||||
match $expression {
|
||||
$pattern $(if $guard)? => true,
|
||||
_ => false
|
||||
}
|
||||
};
|
||||
}
|
||||
fn main() {
|
||||
matches!(0, 0 | 1 if true);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
|
||||
match $expression {
|
||||
$pattern $(if $guard)? => true,
|
||||
_ => false
|
||||
}
|
||||
};
|
||||
}
|
||||
fn main() {
|
||||
match 0 {
|
||||
0|1 if true =>true , _=>false
|
||||
};
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dollar_crate_lhs_is_not_meta() {
|
||||
check(
|
||||
|
@ -1580,92 +1615,6 @@ struct Foo;
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dollar_dollar() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! register_struct { ($Struct:ident) => {
|
||||
macro_rules! register_methods { ($$($method:ident),*) => {
|
||||
macro_rules! implement_methods { ($$$$($$val:expr),*) => {
|
||||
struct $Struct;
|
||||
impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
register_struct!(Foo);
|
||||
register_methods!(alpha, beta);
|
||||
implement_methods!(1, 2, 3);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! register_struct { ($Struct:ident) => {
|
||||
macro_rules! register_methods { ($$($method:ident),*) => {
|
||||
macro_rules! implement_methods { ($$$$($$val:expr),*) => {
|
||||
struct $Struct;
|
||||
impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
macro_rules !register_methods {
|
||||
($($method: ident), *) = > {
|
||||
macro_rules!implement_methods {
|
||||
($$($val: expr), *) = > {
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
$(fn $method()-> &'static[u32] {
|
||||
&[$$($$val), *]
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules !implement_methods {
|
||||
($($val: expr), *) = > {
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn alpha()-> &'static[u32] {
|
||||
&[$($val), *]
|
||||
}
|
||||
fn beta()-> &'static[u32] {
|
||||
&[$($val), *]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn alpha() -> &'static[u32] {
|
||||
&[1, 2, 3]
|
||||
}
|
||||
fn beta() -> &'static[u32] {
|
||||
&[1, 2, 3]
|
||||
}
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metavar_exprs() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
||||
}
|
||||
const _: i32 = m!(a b c);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
||||
}
|
||||
const _: i32 = -0--1--2;
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_punct_without_space() {
|
||||
// Puncts are "glued" greedily.
|
||||
|
|
|
@ -33,7 +33,7 @@ m!(&k");
|
|||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m { ($i:literal) => {}; }
|
||||
/* error: Failed to lower macro args to token tree */"#]],
|
||||
/* error: invalid token tree */"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ fn main() {
|
|||
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }
|
||||
|
||||
fn main() {
|
||||
let a = 2let b = 5drop(b-a)println!("{}", a+b)
|
||||
let a = 2 let b = 5 drop(b-a)println!("{}", a+b)
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
|
@ -106,7 +106,6 @@ stringify!(;
|
|||
|
||||
#[test]
|
||||
fn range_patterns() {
|
||||
// FIXME: rustc thinks there are three patterns here, not one.
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
|
@ -118,7 +117,7 @@ m!(.. .. ..);
|
|||
macro_rules! m {
|
||||
($($p:pat)*) => (stringify!($($p |)*);)
|
||||
}
|
||||
stringify!(.. .. .. |);
|
||||
stringify!(.. | .. | .. |);
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
311
crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
Normal file
311
crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
Normal file
|
@ -0,0 +1,311 @@
|
|||
//! Tests for RFC 3086 metavariable expressions.
|
||||
|
||||
use expect_test::expect;
|
||||
|
||||
use crate::macro_expansion_tests::check;
|
||||
|
||||
#[test]
|
||||
fn test_dollar_dollar() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! register_struct { ($Struct:ident) => {
|
||||
macro_rules! register_methods { ($$($method:ident),*) => {
|
||||
macro_rules! implement_methods { ($$$$($$val:expr),*) => {
|
||||
struct $Struct;
|
||||
impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
register_struct!(Foo);
|
||||
register_methods!(alpha, beta);
|
||||
implement_methods!(1, 2, 3);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! register_struct { ($Struct:ident) => {
|
||||
macro_rules! register_methods { ($$($method:ident),*) => {
|
||||
macro_rules! implement_methods { ($$$$($$val:expr),*) => {
|
||||
struct $Struct;
|
||||
impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
macro_rules !register_methods {
|
||||
($($method: ident), *) = > {
|
||||
macro_rules!implement_methods {
|
||||
($$($val: expr), *) = > {
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
$(fn $method()-> &'static[u32] {
|
||||
&[$$($$val), *]
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules !implement_methods {
|
||||
($($val: expr), *) = > {
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn alpha()-> &'static[u32] {
|
||||
&[$($val), *]
|
||||
}
|
||||
fn beta()-> &'static[u32] {
|
||||
&[$($val), *]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn alpha() -> &'static[u32] {
|
||||
&[1, 2, 3]
|
||||
}
|
||||
fn beta() -> &'static[u32] {
|
||||
&[1, 2, 3]
|
||||
}
|
||||
}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metavar_exprs() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
||||
}
|
||||
const _: i32 = m!(a b c);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
||||
}
|
||||
const _: i32 = -0--1--2;
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_basic() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
($($t:ident),*) => {
|
||||
${count(t)}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
m!();
|
||||
m!(a);
|
||||
m!(a, a);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
($($t:ident),*) => {
|
||||
${count(t)}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
0;
|
||||
1;
|
||||
2;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_with_depth() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($( $( $($t:ident)* ),* );*) => {
|
||||
$(
|
||||
{
|
||||
let depth_none = ${count(t)};
|
||||
let depth_zero = ${count(t, 0)};
|
||||
let depth_one = ${count(t, 1)};
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
foo!(
|
||||
a a a, a, a a;
|
||||
a a a
|
||||
)
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! foo {
|
||||
($( $( $($t:ident)* ),* );*) => {
|
||||
$(
|
||||
{
|
||||
let depth_none = ${count(t)};
|
||||
let depth_zero = ${count(t, 0)};
|
||||
let depth_one = ${count(t, 1)};
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
{
|
||||
let depth_none = 6;
|
||||
let depth_zero = 3;
|
||||
let depth_one = 6;
|
||||
} {
|
||||
let depth_none = 3;
|
||||
let depth_zero = 1;
|
||||
let depth_one = 3;
|
||||
}
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_depth_out_of_bounds() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($($t:ident)*) => { ${count(t, 1)} };
|
||||
($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
|
||||
}
|
||||
macro_rules! bar {
|
||||
($($t:ident)*) => { ${count(t, 1024)} };
|
||||
($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
foo!(a b);
|
||||
foo!(1 2; 3);
|
||||
bar!(a b);
|
||||
bar!(1 2; 3);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! foo {
|
||||
($($t:ident)*) => { ${count(t, 1)} };
|
||||
($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
|
||||
}
|
||||
macro_rules! bar {
|
||||
($($t:ident)*) => { ${count(t, 1024)} };
|
||||
($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
/* error: ${count} out of bounds */;
|
||||
/* error: ${count} out of bounds */;
|
||||
/* error: ${count} out of bounds */;
|
||||
/* error: ${count} out of bounds */;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn misplaced_count() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($($t:ident)*) => { $(${count(t)})* };
|
||||
($l:literal) => { ${count(l)} }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
foo!(a b c);
|
||||
foo!(1);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! foo {
|
||||
($($t:ident)*) => { $(${count(t)})* };
|
||||
($l:literal) => { ${count(l)} }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
/* error: ${count} misplaced */;
|
||||
/* error: ${count} misplaced */;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_count() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! too_many_args {
|
||||
($($t:ident)*) => { ${count(t, 1, leftover)} }
|
||||
}
|
||||
macro_rules! depth_suffixed {
|
||||
($($t:ident)*) => { ${count(t, 0usize)} }
|
||||
}
|
||||
macro_rules! depth_too_large {
|
||||
($($t:ident)*) => { ${count(t, 18446744073709551616)} }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
too_many_args!();
|
||||
depth_suffixed!();
|
||||
depth_too_large!();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! too_many_args {
|
||||
($($t:ident)*) => { ${count(t, 1, leftover)} }
|
||||
}
|
||||
macro_rules! depth_suffixed {
|
||||
($($t:ident)*) => { ${count(t, 0usize)} }
|
||||
}
|
||||
macro_rules! depth_too_large {
|
||||
($($t:ident)*) => { ${count(t, 18446744073709551616)} }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
/* error: invalid macro definition: invalid metavariable expression */;
|
||||
/* error: invalid macro definition: invalid metavariable expression */;
|
||||
/* error: invalid macro definition: invalid metavariable expression */;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_interaction_with_empty_binding() {
|
||||
// FIXME: Should this error? rustc currently accepts it.
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
($($t:ident),*) => {
|
||||
${count(t, 100)}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
m!();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
($($t:ident),*) => {
|
||||
${count(t, 100)}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
0;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
|
@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst {
|
|||
|
||||
#[derive(Clone)] struct CharEscapeDebugContinue;
|
||||
impl Fn<(char, )> for CharEscapeDebugContinue {
|
||||
#[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
|
||||
#[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
|
||||
c.escape_debug_ext(false )
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FnMut<(char, )> for CharEscapeDebugContinue {
|
||||
#[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
|
||||
#[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
|
||||
Fn::call(&*self , (c, ))
|
||||
}
|
||||
}
|
||||
impl FnOnce<(char, )> for CharEscapeDebugContinue {
|
||||
type Output = char::EscapeDebug;
|
||||
#[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
|
||||
#[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
|
||||
Fn::call(&self , (c, ))
|
||||
}
|
||||
}
|
||||
#[derive(Clone)] struct CharEscapeUnicode;
|
||||
impl Fn<(char, )> for CharEscapeUnicode {
|
||||
#[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
|
||||
#[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
|
||||
c.escape_unicode()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FnMut<(char, )> for CharEscapeUnicode {
|
||||
#[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
|
||||
#[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
|
||||
Fn::call(&*self , (c, ))
|
||||
}
|
||||
}
|
||||
impl FnOnce<(char, )> for CharEscapeUnicode {
|
||||
type Output = char::EscapeUnicode;
|
||||
#[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
|
||||
#[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
|
||||
Fn::call(&self , (c, ))
|
||||
}
|
||||
}
|
||||
#[derive(Clone)] struct CharEscapeDefault;
|
||||
impl Fn<(char, )> for CharEscapeDefault {
|
||||
#[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
|
||||
#[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
|
||||
c.escape_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FnMut<(char, )> for CharEscapeDefault {
|
||||
#[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
|
||||
#[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
|
||||
Fn::call(&*self , (c, ))
|
||||
}
|
||||
}
|
||||
impl FnOnce<(char, )> for CharEscapeDefault {
|
||||
type Output = char::EscapeDefault;
|
||||
#[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
|
||||
#[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
|
||||
Fn::call(&self , (c, ))
|
||||
}
|
||||
}
|
||||
|
@ -833,7 +833,7 @@ macro_rules! rgb_color {
|
|||
/* parse error: expected SEMICOLON */
|
||||
/* parse error: expected expression, item or let statement */
|
||||
pub fn new() {
|
||||
let _ = 0as u32<<(8+8);
|
||||
let _ = 0 as u32<<(8+8);
|
||||
}
|
||||
// MACRO_ITEMS@0..31
|
||||
// FN@0..31
|
||||
|
|
|
@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
|
|||
macro_rules! m2 { ($x:ident) => {} }
|
||||
|
||||
/* error: invalid macro definition: expected subtree */
|
||||
/* error: Failed to lower macro args to token tree */
|
||||
/* error: invalid token tree */
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ mod builtin_fn_macro;
|
|||
mod builtin_derive_macro;
|
||||
mod proc_macros;
|
||||
|
||||
use std::{iter, ops::Range, sync::Arc};
|
||||
use std::{iter, ops::Range, sync};
|
||||
|
||||
use ::mbe::TokenMap;
|
||||
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
|
||||
|
@ -33,8 +33,13 @@ use syntax::{
|
|||
use tt::token_id::{Subtree, TokenId};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver,
|
||||
src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId,
|
||||
db::DefDatabase,
|
||||
macro_id_to_def_id,
|
||||
nameres::{DefMap, MacroSubNs, ModuleSource},
|
||||
resolver::HasResolver,
|
||||
src::HasSource,
|
||||
test_db::TestDB,
|
||||
AdtId, AsMacroCall, Lookup, ModuleDefId,
|
||||
};
|
||||
|
||||
#[track_caller]
|
||||
|
@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
ProcMacro {
|
||||
name: "identity_when_valid".into(),
|
||||
kind: base_db::ProcMacroKind::Attr,
|
||||
expander: Arc::new(IdentityWhenValidProcMacroExpander),
|
||||
expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
|
||||
},
|
||||
)];
|
||||
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let local_id = def_map.root();
|
||||
let local_id = DefMap::ROOT;
|
||||
let module = def_map.module_id(local_id);
|
||||
let resolver = module.resolver(&db);
|
||||
let source = def_map[local_id].definition_source(&db);
|
||||
|
@ -125,21 +130,17 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
|
||||
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
|
||||
let macro_call = InFile::new(source.file_id, ¯o_call);
|
||||
let mut error = None;
|
||||
let macro_call_id = macro_call
|
||||
.as_call_id_with_errors(
|
||||
&db,
|
||||
krate,
|
||||
|path| {
|
||||
resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
|
||||
},
|
||||
&mut |err| error = Some(err),
|
||||
)
|
||||
.unwrap()
|
||||
let res = macro_call
|
||||
.as_call_id_with_errors(&db, krate, |path| {
|
||||
resolver
|
||||
.resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
|
||||
.map(|it| macro_id_to_def_id(&db, it))
|
||||
})
|
||||
.unwrap();
|
||||
let macro_call_id = res.value.unwrap();
|
||||
let macro_file = MacroFile { macro_call_id };
|
||||
let mut expansion_result = db.parse_macro_expansion(macro_file);
|
||||
expansion_result.err = expansion_result.err.or(error);
|
||||
expansion_result.err = expansion_result.err.or(res.err);
|
||||
expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id)));
|
||||
}
|
||||
|
||||
|
@ -157,34 +158,33 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
if let Some(err) = exp.err {
|
||||
format_to!(expn_text, "/* error: {} */", err);
|
||||
}
|
||||
if let Some((parse, token_map)) = exp.value {
|
||||
if expect_errors {
|
||||
assert!(!parse.errors().is_empty(), "no parse errors in expansion");
|
||||
for e in parse.errors() {
|
||||
format_to!(expn_text, "/* parse error: {} */\n", e);
|
||||
}
|
||||
} else {
|
||||
assert!(
|
||||
parse.errors().is_empty(),
|
||||
"parse errors in expansion: \n{:#?}",
|
||||
parse.errors()
|
||||
);
|
||||
let (parse, token_map) = exp.value;
|
||||
if expect_errors {
|
||||
assert!(!parse.errors().is_empty(), "no parse errors in expansion");
|
||||
for e in parse.errors() {
|
||||
format_to!(expn_text, "/* parse error: {} */\n", e);
|
||||
}
|
||||
let pp = pretty_print_macro_expansion(
|
||||
parse.syntax_node(),
|
||||
show_token_ids.then_some(&*token_map),
|
||||
} else {
|
||||
assert!(
|
||||
parse.errors().is_empty(),
|
||||
"parse errors in expansion: \n{:#?}",
|
||||
parse.errors()
|
||||
);
|
||||
let indent = IndentLevel::from_node(call.syntax());
|
||||
let pp = reindent(indent, pp);
|
||||
format_to!(expn_text, "{}", pp);
|
||||
}
|
||||
let pp = pretty_print_macro_expansion(
|
||||
parse.syntax_node(),
|
||||
show_token_ids.then_some(&*token_map),
|
||||
);
|
||||
let indent = IndentLevel::from_node(call.syntax());
|
||||
let pp = reindent(indent, pp);
|
||||
format_to!(expn_text, "{}", pp);
|
||||
|
||||
if tree {
|
||||
let tree = format!("{:#?}", parse.syntax_node())
|
||||
.split_inclusive('\n')
|
||||
.map(|line| format!("// {line}"))
|
||||
.collect::<String>();
|
||||
format_to!(expn_text, "\n{}", tree)
|
||||
}
|
||||
if tree {
|
||||
let tree = format!("{:#?}", parse.syntax_node())
|
||||
.split_inclusive('\n')
|
||||
.map(|line| format!("// {line}"))
|
||||
.collect::<String>();
|
||||
format_to!(expn_text, "\n{}", tree)
|
||||
}
|
||||
let range = call.syntax().text_range();
|
||||
let range: Range<usize> = range.into();
|
||||
|
@ -287,6 +287,7 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str
|
|||
let curr_kind = token.kind();
|
||||
let space = match (prev_kind, curr_kind) {
|
||||
_ if prev_kind.is_trivia() || curr_kind.is_trivia() => "",
|
||||
_ if prev_kind.is_literal() && !curr_kind.is_punct() => " ",
|
||||
(T!['{'], T!['}']) => "",
|
||||
(T![=], _) | (_, T![=]) => " ",
|
||||
(_, T!['{']) => " ",
|
|
@ -57,9 +57,9 @@ mod path_resolution;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{cmp::Ord, ops::Deref, sync::Arc};
|
||||
use std::{cmp::Ord, ops::Deref};
|
||||
|
||||
use base_db::{CrateId, Edition, FileId};
|
||||
use base_db::{CrateId, Edition, FileId, ProcMacroKind};
|
||||
use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
|
@ -67,6 +67,7 @@ use profile::Count;
|
|||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
@ -76,7 +77,8 @@ use crate::{
|
|||
path::ModPath,
|
||||
per_ns::PerNs,
|
||||
visibility::Visibility,
|
||||
AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
|
||||
AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId,
|
||||
ProcMacroId,
|
||||
};
|
||||
|
||||
/// Contains the results of (early) name resolution.
|
||||
|
@ -92,7 +94,6 @@ use crate::{
|
|||
pub struct DefMap {
|
||||
_c: Count<Self>,
|
||||
block: Option<BlockInfo>,
|
||||
root: LocalModuleId,
|
||||
modules: Arena<ModuleData>,
|
||||
krate: CrateId,
|
||||
/// The prelude module for this crate. This either comes from an import
|
||||
|
@ -102,7 +103,22 @@ pub struct DefMap {
|
|||
/// but that attribute is nightly and when used in a block, it affects resolution globally
|
||||
/// so we aren't handling this correctly anyways).
|
||||
prelude: Option<ModuleId>,
|
||||
/// The extern prelude is only populated for non-block DefMaps
|
||||
/// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
|
||||
/// this contains all kinds of macro, not just `macro_rules!` macro.
|
||||
macro_use_prelude: FxHashMap<Name, MacroId>,
|
||||
|
||||
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
|
||||
/// attributes.
|
||||
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
|
||||
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
|
||||
data: Arc<DefMapCrateData>,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
extern_prelude: FxHashMap<Name, ModuleId>,
|
||||
|
||||
/// Side table for resolving derive helpers.
|
||||
|
@ -110,9 +126,6 @@ pub struct DefMap {
|
|||
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
|
||||
/// The error that occurred when failing to load the proc-macro dll.
|
||||
proc_macro_loading_error: Option<Box<str>>,
|
||||
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
|
||||
/// attributes.
|
||||
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
|
||||
|
||||
/// Custom attributes registered with `#![register_attr]`.
|
||||
registered_attrs: Vec<SmolStr>,
|
||||
|
@ -122,10 +135,36 @@ pub struct DefMap {
|
|||
unstable_features: FxHashSet<SmolStr>,
|
||||
/// #[rustc_coherence_is_core]
|
||||
rustc_coherence_is_core: bool,
|
||||
no_core: bool,
|
||||
no_std: bool,
|
||||
|
||||
edition: Edition,
|
||||
recursion_limit: Option<u32>,
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
}
|
||||
|
||||
impl DefMapCrateData {
|
||||
fn shrink_to_fit(&mut self) {
|
||||
let Self {
|
||||
extern_prelude,
|
||||
exported_derives,
|
||||
fn_proc_macro_mapping,
|
||||
registered_attrs,
|
||||
registered_tools,
|
||||
unstable_features,
|
||||
proc_macro_loading_error: _,
|
||||
rustc_coherence_is_core: _,
|
||||
no_core: _,
|
||||
no_std: _,
|
||||
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();
|
||||
registered_tools.shrink_to_fit();
|
||||
unstable_features.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
/// For `DefMap`s computed for a block expression, this stores its location in the parent map.
|
||||
|
@ -134,7 +173,23 @@ struct BlockInfo {
|
|||
/// The `BlockId` this `DefMap` was created from.
|
||||
block: BlockId,
|
||||
/// The containing module.
|
||||
parent: ModuleId,
|
||||
parent: BlockRelativeModuleId,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
struct BlockRelativeModuleId {
|
||||
block: Option<BlockId>,
|
||||
local_id: LocalModuleId,
|
||||
}
|
||||
|
||||
impl BlockRelativeModuleId {
|
||||
fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
|
||||
self.into_module(krate).def_map(db)
|
||||
}
|
||||
|
||||
fn into_module(self, krate: CrateId) -> ModuleId {
|
||||
ModuleId { krate, block: self.block, local_id: self.local_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<LocalModuleId> for DefMap {
|
||||
|
@ -224,6 +279,8 @@ pub struct ModuleData {
|
|||
}
|
||||
|
||||
impl DefMap {
|
||||
pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
|
||||
|
||||
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
|
||||
let _p = profile::span("crate_def_map_query").detail(|| {
|
||||
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
|
||||
|
@ -243,17 +300,10 @@ impl DefMap {
|
|||
Arc::new(def_map)
|
||||
}
|
||||
|
||||
pub(crate) fn block_def_map_query(
|
||||
db: &dyn DefDatabase,
|
||||
block_id: BlockId,
|
||||
) -> Option<Arc<DefMap>> {
|
||||
pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
|
||||
let block: BlockLoc = db.lookup_intern_block(block_id);
|
||||
|
||||
let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
if item_tree.top_level_items().is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let parent_map = block.module.def_map(db);
|
||||
let krate = block.module.krate;
|
||||
|
@ -265,36 +315,48 @@ impl DefMap {
|
|||
let module_data =
|
||||
ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
|
||||
|
||||
let mut def_map = DefMap::empty(krate, parent_map.edition, module_data);
|
||||
def_map.block = Some(BlockInfo { block: block_id, parent: block.module });
|
||||
let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
|
||||
def_map.data = parent_map.data.clone();
|
||||
def_map.block = Some(BlockInfo {
|
||||
block: block_id,
|
||||
parent: BlockRelativeModuleId {
|
||||
block: block.module.block,
|
||||
local_id: block.module.local_id,
|
||||
},
|
||||
});
|
||||
|
||||
let def_map = collector::collect_defs(db, def_map, tree_id);
|
||||
Some(Arc::new(def_map))
|
||||
Arc::new(def_map)
|
||||
}
|
||||
|
||||
fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
|
||||
let mut modules: Arena<ModuleData> = Arena::default();
|
||||
let root = modules.alloc(module_data);
|
||||
assert_eq!(root, Self::ROOT);
|
||||
|
||||
DefMap {
|
||||
_c: Count::new(),
|
||||
block: None,
|
||||
krate,
|
||||
edition,
|
||||
recursion_limit: None,
|
||||
extern_prelude: FxHashMap::default(),
|
||||
exported_derives: FxHashMap::default(),
|
||||
fn_proc_macro_mapping: FxHashMap::default(),
|
||||
proc_macro_loading_error: None,
|
||||
derive_helpers_in_scope: FxHashMap::default(),
|
||||
prelude: None,
|
||||
root,
|
||||
modules,
|
||||
registered_attrs: Vec::new(),
|
||||
registered_tools: Vec::new(),
|
||||
unstable_features: FxHashSet::default(),
|
||||
krate,
|
||||
prelude: None,
|
||||
macro_use_prelude: FxHashMap::default(),
|
||||
derive_helpers_in_scope: FxHashMap::default(),
|
||||
diagnostics: Vec::new(),
|
||||
rustc_coherence_is_core: false,
|
||||
data: Arc::new(DefMapCrateData {
|
||||
extern_prelude: FxHashMap::default(),
|
||||
exported_derives: FxHashMap::default(),
|
||||
fn_proc_macro_mapping: FxHashMap::default(),
|
||||
proc_macro_loading_error: None,
|
||||
registered_attrs: Vec::new(),
|
||||
registered_tools: Vec::new(),
|
||||
unstable_features: FxHashSet::default(),
|
||||
rustc_coherence_is_core: false,
|
||||
no_core: false,
|
||||
no_std: false,
|
||||
edition,
|
||||
recursion_limit: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,31 +379,31 @@ impl DefMap {
|
|||
}
|
||||
|
||||
pub fn registered_tools(&self) -> &[SmolStr] {
|
||||
&self.registered_tools
|
||||
&self.data.registered_tools
|
||||
}
|
||||
|
||||
pub fn registered_attrs(&self) -> &[SmolStr] {
|
||||
&self.registered_attrs
|
||||
&self.data.registered_attrs
|
||||
}
|
||||
|
||||
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
|
||||
self.unstable_features.contains(feature)
|
||||
self.data.unstable_features.contains(feature)
|
||||
}
|
||||
|
||||
pub fn is_rustc_coherence_is_core(&self) -> bool {
|
||||
self.rustc_coherence_is_core
|
||||
self.data.rustc_coherence_is_core
|
||||
}
|
||||
|
||||
pub fn root(&self) -> LocalModuleId {
|
||||
self.root
|
||||
pub fn is_no_std(&self) -> bool {
|
||||
self.data.no_std || self.data.no_core
|
||||
}
|
||||
|
||||
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
|
||||
self.fn_proc_macro_mapping.get(&id).copied()
|
||||
self.data.fn_proc_macro_mapping.get(&id).copied()
|
||||
}
|
||||
|
||||
pub fn proc_macro_loading_error(&self) -> Option<&str> {
|
||||
self.proc_macro_loading_error.as_deref()
|
||||
self.data.proc_macro_loading_error.as_deref()
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> CrateId {
|
||||
|
@ -356,8 +418,12 @@ impl DefMap {
|
|||
self.prelude
|
||||
}
|
||||
|
||||
pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ {
|
||||
self.extern_prelude.iter()
|
||||
pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
|
||||
self.data.extern_prelude.iter().map(|(name, def)| (name, *def))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
|
||||
self.macro_use_prelude.iter().map(|(name, def)| (name, *def))
|
||||
}
|
||||
|
||||
pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
|
||||
|
@ -365,11 +431,8 @@ impl DefMap {
|
|||
ModuleId { krate: self.krate, local_id, block }
|
||||
}
|
||||
|
||||
pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||
self.with_ancestor_maps(db, self.root, &mut |def_map, _module| {
|
||||
if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None }
|
||||
})
|
||||
.expect("DefMap chain without root")
|
||||
pub(crate) fn crate_root(&self) -> ModuleId {
|
||||
ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT }
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path(
|
||||
|
@ -378,9 +441,16 @@ impl DefMap {
|
|||
original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
expected_macro_subns: Option<MacroSubNs>,
|
||||
) -> (PerNs, Option<usize>) {
|
||||
let res =
|
||||
self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
|
||||
let res = self.resolve_path_fp_with_macro(
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
original_module,
|
||||
path,
|
||||
shadow,
|
||||
expected_macro_subns,
|
||||
);
|
||||
(res.resolved_def, res.segment_index)
|
||||
}
|
||||
|
||||
|
@ -397,6 +467,7 @@ impl DefMap {
|
|||
original_module,
|
||||
path,
|
||||
shadow,
|
||||
None, // Currently this function isn't used for macro resolution.
|
||||
);
|
||||
(res.resolved_def, res.segment_index)
|
||||
}
|
||||
|
@ -416,7 +487,7 @@ impl DefMap {
|
|||
}
|
||||
let mut block = self.block;
|
||||
while let Some(block_info) = block {
|
||||
let parent = block_info.parent.def_map(db);
|
||||
let parent = block_info.parent.def_map(db, self.krate);
|
||||
if let Some(it) = f(&parent, block_info.parent.local_id) {
|
||||
return Some(it);
|
||||
}
|
||||
|
@ -429,7 +500,8 @@ impl DefMap {
|
|||
/// If this `DefMap` is for a block expression, returns the module containing the block (which
|
||||
/// might again be a block, or a module inside a block).
|
||||
pub fn parent(&self) -> Option<ModuleId> {
|
||||
Some(self.block?.parent)
|
||||
let BlockRelativeModuleId { block, local_id } = self.block?.parent;
|
||||
Some(ModuleId { krate: self.krate, block, local_id })
|
||||
}
|
||||
|
||||
/// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing
|
||||
|
@ -437,7 +509,13 @@ impl DefMap {
|
|||
pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
|
||||
match self[local_mod].parent {
|
||||
Some(parent) => Some(self.module_id(parent)),
|
||||
None => self.block.map(|block| block.parent),
|
||||
None => {
|
||||
self.block.map(
|
||||
|BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| {
|
||||
ModuleId { krate: self.krate, block, local_id }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,25 +526,31 @@ impl DefMap {
|
|||
let mut arc;
|
||||
let mut current_map = self;
|
||||
while let Some(block) = current_map.block {
|
||||
go(&mut buf, current_map, "block scope", current_map.root);
|
||||
go(&mut buf, db, current_map, "block scope", Self::ROOT);
|
||||
buf.push('\n');
|
||||
arc = block.parent.def_map(db);
|
||||
arc = block.parent.def_map(db, self.krate);
|
||||
current_map = &arc;
|
||||
}
|
||||
go(&mut buf, current_map, "crate", current_map.root);
|
||||
go(&mut buf, db, current_map, "crate", Self::ROOT);
|
||||
return buf;
|
||||
|
||||
fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
|
||||
fn go(
|
||||
buf: &mut String,
|
||||
db: &dyn DefDatabase,
|
||||
map: &DefMap,
|
||||
path: &str,
|
||||
module: LocalModuleId,
|
||||
) {
|
||||
format_to!(buf, "{}\n", path);
|
||||
|
||||
map.modules[module].scope.dump(buf);
|
||||
map.modules[module].scope.dump(db.upcast(), 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}");
|
||||
let path = format!("{path}::{}", name.display(db.upcast()));
|
||||
buf.push('\n');
|
||||
go(buf, map, &path, *child);
|
||||
go(buf, db, map, &path, *child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -477,7 +561,7 @@ impl DefMap {
|
|||
let mut current_map = self;
|
||||
while let Some(block) = current_map.block {
|
||||
format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
|
||||
arc = block.parent.def_map(db);
|
||||
arc = block.parent.def_map(db, self.krate);
|
||||
current_map = &arc;
|
||||
}
|
||||
|
||||
|
@ -489,34 +573,20 @@ impl DefMap {
|
|||
// Exhaustive match to require handling new fields.
|
||||
let Self {
|
||||
_c: _,
|
||||
exported_derives,
|
||||
extern_prelude,
|
||||
macro_use_prelude,
|
||||
diagnostics,
|
||||
modules,
|
||||
registered_attrs,
|
||||
registered_tools,
|
||||
fn_proc_macro_mapping,
|
||||
derive_helpers_in_scope,
|
||||
unstable_features,
|
||||
proc_macro_loading_error: _,
|
||||
block: _,
|
||||
edition: _,
|
||||
recursion_limit: _,
|
||||
krate: _,
|
||||
prelude: _,
|
||||
root: _,
|
||||
rustc_coherence_is_core: _,
|
||||
data: _,
|
||||
} = self;
|
||||
|
||||
extern_prelude.shrink_to_fit();
|
||||
exported_derives.shrink_to_fit();
|
||||
macro_use_prelude.shrink_to_fit();
|
||||
diagnostics.shrink_to_fit();
|
||||
modules.shrink_to_fit();
|
||||
registered_attrs.shrink_to_fit();
|
||||
registered_tools.shrink_to_fit();
|
||||
fn_proc_macro_mapping.shrink_to_fit();
|
||||
derive_helpers_in_scope.shrink_to_fit();
|
||||
unstable_features.shrink_to_fit();
|
||||
for (_, module) in modules.iter_mut() {
|
||||
module.children.shrink_to_fit();
|
||||
module.scope.shrink_to_fit();
|
||||
|
@ -529,7 +599,7 @@ impl DefMap {
|
|||
}
|
||||
|
||||
pub fn recursion_limit(&self) -> Option<u32> {
|
||||
self.recursion_limit
|
||||
self.data.recursion_limit
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,3 +634,48 @@ pub enum ModuleSource {
|
|||
Module(ast::Module),
|
||||
BlockExpr(ast::BlockExpr),
|
||||
}
|
||||
|
||||
/// See `sub_namespace_match()`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MacroSubNs {
|
||||
/// Function-like macros, suffixed with `!`.
|
||||
Bang,
|
||||
/// Macros inside attributes, i.e. attribute macros and derive macros.
|
||||
Attr,
|
||||
}
|
||||
|
||||
impl MacroSubNs {
|
||||
fn from_id(db: &dyn DefDatabase, macro_id: MacroId) -> Self {
|
||||
let expander = match macro_id {
|
||||
MacroId::Macro2Id(it) => it.lookup(db).expander,
|
||||
MacroId::MacroRulesId(it) => it.lookup(db).expander,
|
||||
MacroId::ProcMacroId(it) => {
|
||||
return match it.lookup(db).kind {
|
||||
ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
|
||||
ProcMacroKind::FuncLike => Self::Bang,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently.
|
||||
match expander {
|
||||
MacroExpander::Declarative
|
||||
| MacroExpander::BuiltIn(_)
|
||||
| MacroExpander::BuiltInEager(_) => Self::Bang,
|
||||
MacroExpander::BuiltInAttr(_) | MacroExpander::BuiltInDerive(_) => Self::Attr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Quoted from [rustc]:
|
||||
/// Macro namespace is separated into two sub-namespaces, one for bang macros and
|
||||
/// one for attribute-like macros (attributes, derives).
|
||||
/// We ignore resolutions from one sub-namespace when searching names in scope for another.
|
||||
///
|
||||
/// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75
|
||||
fn sub_namespace_match(candidate: Option<MacroSubNs>, expected: Option<MacroSubNs>) -> bool {
|
||||
match (candidate, expected) {
|
||||
(Some(candidate), Some(expected)) => candidate == expected,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
|
|||
use syntax::{ast, SmolStr};
|
||||
|
||||
use crate::{
|
||||
attr_macro_as_call_id, builtin_attr,
|
||||
attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
|
||||
attr_macro_as_call_id,
|
||||
db::DefDatabase,
|
||||
item_scope::BuiltinShadowMode,
|
||||
macro_id_to_def_id,
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
AstIdWithPath, LocalModuleId, UnresolvedMacro,
|
||||
};
|
||||
|
||||
use super::DefMap;
|
||||
use super::{DefMap, MacroSubNs};
|
||||
|
||||
pub enum ResolvedAttr {
|
||||
/// Attribute resolved to an attribute macro.
|
||||
|
@ -42,9 +43,12 @@ impl DefMap {
|
|||
original_module,
|
||||
&ast_id.path,
|
||||
BuiltinShadowMode::Module,
|
||||
Some(MacroSubNs::Attr),
|
||||
);
|
||||
let def = match resolved_res.resolved_def.take_macros() {
|
||||
Some(def) => {
|
||||
// `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
|
||||
// macro, or even function-like macro when the path is qualified.
|
||||
if def.is_attribute(db) {
|
||||
def
|
||||
} else {
|
||||
|
@ -60,7 +64,6 @@ impl DefMap {
|
|||
attr,
|
||||
self.krate,
|
||||
macro_id_to_def_id(db, def),
|
||||
false,
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -75,20 +78,16 @@ impl DefMap {
|
|||
let name = name.to_smol_str();
|
||||
let pred = |n: &_| *n == name;
|
||||
|
||||
let registered = self.registered_tools.iter().map(SmolStr::as_str);
|
||||
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
|
||||
let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
|
||||
let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
|
||||
// FIXME: tool modules can be shadowed by actual modules
|
||||
if is_tool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if segments.len() == 1 {
|
||||
let registered = self.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = builtin_attr::INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.map(|it| it.name)
|
||||
.chain(registered)
|
||||
.any(pred);
|
||||
let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
|
||||
return is_inert;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,10 @@ use base_db::CrateId;
|
|||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{attrs::AttrId, MacroCallKind};
|
||||
use la_arena::Idx;
|
||||
use syntax::ast::{self, AnyHasAttrs};
|
||||
use syntax::{
|
||||
ast::{self, AnyHasAttrs},
|
||||
SyntaxError,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
item_tree::{self, ItemTreeId},
|
||||
|
@ -29,11 +32,15 @@ pub enum DefDiagnosticKind {
|
|||
|
||||
MacroError { ast: MacroCallKind, message: String },
|
||||
|
||||
MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
|
||||
|
||||
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
|
||||
|
||||
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
|
||||
|
||||
MalformedDerive { ast: AstId<ast::Adt>, id: usize },
|
||||
|
||||
MacroDefError { ast: AstId<ast::Macro>, message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -81,7 +88,8 @@ impl DefDiagnostic {
|
|||
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_proc_macro(
|
||||
// FIXME: Whats the difference between this and unresolved_macro_call
|
||||
pub(crate) fn unresolved_proc_macro(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
krate: CrateId,
|
||||
|
@ -89,7 +97,7 @@ impl DefDiagnostic {
|
|||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
|
||||
}
|
||||
|
||||
pub(super) fn macro_error(
|
||||
pub(crate) fn macro_error(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
message: String,
|
||||
|
@ -97,7 +105,22 @@ impl DefDiagnostic {
|
|||
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_macro_call(
|
||||
pub(crate) fn macro_expansion_parse_error(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
errors: &[SyntaxError],
|
||||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::MacroExpansionParseError {
|
||||
ast,
|
||||
errors: errors.to_vec().into_boxed_slice(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Whats the difference between this and unresolved_proc_macro
|
||||
pub(crate) fn unresolved_macro_call(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
path: ModPath,
|
||||
|
|
|
@ -74,12 +74,20 @@ impl ModDir {
|
|||
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
|
||||
}
|
||||
None if file_id.is_include_macro(db.upcast()) => {
|
||||
candidate_files.push(format!("{name}.rs"));
|
||||
candidate_files.push(format!("{name}/mod.rs"));
|
||||
candidate_files.push(format!("{}.rs", name.display(db.upcast())));
|
||||
candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
|
||||
}
|
||||
None => {
|
||||
candidate_files.push(format!("{}{name}.rs", self.dir_path.0));
|
||||
candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0));
|
||||
candidate_files.push(format!(
|
||||
"{}{}.rs",
|
||||
self.dir_path.0,
|
||||
name.display(db.upcast())
|
||||
));
|
||||
candidate_files.push(format!(
|
||||
"{}{}/mod.rs",
|
||||
self.dir_path.0,
|
||||
name.display(db.upcast())
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -91,7 +99,7 @@ impl ModDir {
|
|||
let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
|
||||
(DirPath::empty(), false)
|
||||
} else {
|
||||
(DirPath::new(format!("{name}/")), true)
|
||||
(DirPath::new(format!("{}/", name.display(db.upcast()))), true)
|
||||
};
|
||||
if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
|
||||
return Ok((file_id, is_mod_rs, mod_dir));
|
||||
|
|
|
@ -16,11 +16,11 @@ use hir_expand::name::Name;
|
|||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_scope::BUILTIN_SCOPE,
|
||||
nameres::{BuiltinShadowMode, DefMap},
|
||||
nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
|
||||
path::{ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
||||
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -58,18 +58,22 @@ impl ResolvePathResult {
|
|||
}
|
||||
}
|
||||
|
||||
impl DefMap {
|
||||
pub(super) fn resolve_name_in_extern_prelude(
|
||||
&self,
|
||||
impl PerNs {
|
||||
pub(super) fn filter_macro(
|
||||
mut self,
|
||||
db: &dyn DefDatabase,
|
||||
name: &Name,
|
||||
) -> Option<ModuleId> {
|
||||
match self.block {
|
||||
Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
|
||||
None => self.extern_prelude.get(name).copied(),
|
||||
}
|
||||
}
|
||||
expected: Option<MacroSubNs>,
|
||||
) -> Self {
|
||||
self.macros = self.macros.filter(|&(id, _)| {
|
||||
let this = MacroSubNs::from_id(db, id);
|
||||
sub_namespace_match(Some(this), expected)
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl DefMap {
|
||||
pub(crate) fn resolve_visibility(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
|
@ -83,7 +87,7 @@ impl DefMap {
|
|||
let mut vis = match visibility {
|
||||
RawVisibility::Module(path) => {
|
||||
let (result, remaining) =
|
||||
self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
|
||||
self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
|
||||
if remaining.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
@ -106,7 +110,7 @@ impl DefMap {
|
|||
// ...unless we're resolving visibility for an associated item in an impl.
|
||||
if self.block_id() != m.block && !within_impl {
|
||||
cov_mark::hit!(adjust_vis_in_block_def_map);
|
||||
vis = Visibility::Module(self.module_id(self.root()));
|
||||
vis = Visibility::Module(self.module_id(Self::ROOT));
|
||||
tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +128,9 @@ impl DefMap {
|
|||
mut original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
// Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're
|
||||
// resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths.
|
||||
expected_macro_subns: Option<MacroSubNs>,
|
||||
) -> ResolvePathResult {
|
||||
let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||
|
||||
|
@ -136,6 +143,7 @@ impl DefMap {
|
|||
original_module,
|
||||
path,
|
||||
shadow,
|
||||
expected_macro_subns,
|
||||
);
|
||||
|
||||
// Merge `new` into `result`.
|
||||
|
@ -154,7 +162,7 @@ impl DefMap {
|
|||
match ¤t_map.block {
|
||||
Some(block) => {
|
||||
original_module = block.parent.local_id;
|
||||
arc = block.parent.def_map(db);
|
||||
arc = block.parent.def_map(db, current_map.krate);
|
||||
current_map = &*arc;
|
||||
}
|
||||
None => return result,
|
||||
|
@ -169,11 +177,15 @@ impl DefMap {
|
|||
original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
expected_macro_subns: Option<MacroSubNs>,
|
||||
) -> ResolvePathResult {
|
||||
let graph = db.crate_graph();
|
||||
let _cx = stdx::panic_context::enter(format!(
|
||||
"DefMap {:?} crate_name={:?} block={:?} path={path}",
|
||||
self.krate, graph[self.krate].display_name, self.block
|
||||
"DefMap {:?} crate_name={:?} block={:?} path={}",
|
||||
self.krate,
|
||||
graph[self.krate].display_name,
|
||||
self.block,
|
||||
path.display(db.upcast())
|
||||
));
|
||||
|
||||
let mut segments = path.segments().iter().enumerate();
|
||||
|
@ -181,21 +193,21 @@ impl DefMap {
|
|||
PathKind::DollarCrate(krate) => {
|
||||
if krate == self.krate {
|
||||
cov_mark::hit!(macro_dollar_crate_self);
|
||||
PerNs::types(self.crate_root(db).into(), Visibility::Public)
|
||||
PerNs::types(self.crate_root().into(), Visibility::Public)
|
||||
} else {
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let module = def_map.module_id(def_map.root);
|
||||
let module = def_map.module_id(Self::ROOT);
|
||||
cov_mark::hit!(macro_dollar_crate_other);
|
||||
PerNs::types(module.into(), Visibility::Public)
|
||||
}
|
||||
}
|
||||
PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
|
||||
PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
|
||||
// plain import or absolute path in 2015: crate-relative with
|
||||
// fallback to extern prelude (with the simplification in
|
||||
// rust-lang/rust#57745)
|
||||
// FIXME there must be a nicer way to write this condition
|
||||
PathKind::Plain | PathKind::Abs
|
||||
if self.edition == Edition::Edition2015
|
||||
if self.data.edition == Edition::Edition2015
|
||||
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
|
||||
{
|
||||
let (_, segment) = match segments.next() {
|
||||
|
@ -220,7 +232,13 @@ impl DefMap {
|
|||
if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
|
||||
|
||||
tracing::debug!("resolving {:?} in module", segment);
|
||||
self.resolve_name_in_module(db, original_module, segment, prefer_module)
|
||||
self.resolve_name_in_module(
|
||||
db,
|
||||
original_module,
|
||||
segment,
|
||||
prefer_module,
|
||||
expected_macro_subns,
|
||||
)
|
||||
}
|
||||
PathKind::Super(lvl) => {
|
||||
let mut module = original_module;
|
||||
|
@ -236,16 +254,20 @@ impl DefMap {
|
|||
);
|
||||
tracing::debug!(
|
||||
"`super` path: {} -> {} in parent map",
|
||||
path,
|
||||
new_path
|
||||
);
|
||||
return block.parent.def_map(db).resolve_path_fp_with_macro(
|
||||
db,
|
||||
mode,
|
||||
block.parent.local_id,
|
||||
&new_path,
|
||||
shadow,
|
||||
path.display(db.upcast()),
|
||||
new_path.display(db.upcast())
|
||||
);
|
||||
return block
|
||||
.parent
|
||||
.def_map(db, self.krate)
|
||||
.resolve_path_fp_with_macro(
|
||||
db,
|
||||
mode,
|
||||
block.parent.local_id,
|
||||
&new_path,
|
||||
shadow,
|
||||
expected_macro_subns,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("super path in root module");
|
||||
|
@ -271,7 +293,7 @@ impl DefMap {
|
|||
Some((_, segment)) => segment,
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
if let Some(&def) = self.extern_prelude.get(segment) {
|
||||
if let Some(&def) = self.data.extern_prelude.get(segment) {
|
||||
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||
PerNs::types(def.into(), Visibility::Public)
|
||||
} else {
|
||||
|
@ -303,7 +325,12 @@ impl DefMap {
|
|||
);
|
||||
tracing::debug!("resolving {:?} in other crate", path);
|
||||
let defp_map = module.def_map(db);
|
||||
let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
|
||||
// Macro sub-namespaces only matter when resolving single-segment paths
|
||||
// because `macro_use` and other preludes should be taken into account. At
|
||||
// this point, we know we're resolving a multi-segment path so macro kind
|
||||
// expectation is discarded.
|
||||
let (def, s) =
|
||||
defp_map.resolve_path(db, module.local_id, &path, shadow, None);
|
||||
return ResolvePathResult::with(
|
||||
def,
|
||||
ReachedFixedPoint::Yes,
|
||||
|
@ -331,11 +358,11 @@ impl DefMap {
|
|||
Some(local_id) => {
|
||||
let variant = EnumVariantId { parent: e, local_id };
|
||||
match &*enum_data.variants[local_id].variant_data {
|
||||
crate::adt::VariantData::Record(_) => {
|
||||
crate::data::adt::VariantData::Record(_) => {
|
||||
PerNs::types(variant.into(), Visibility::Public)
|
||||
}
|
||||
crate::adt::VariantData::Tuple(_)
|
||||
| crate::adt::VariantData::Unit => {
|
||||
crate::data::adt::VariantData::Tuple(_)
|
||||
| crate::data::adt::VariantData::Unit => {
|
||||
PerNs::both(variant.into(), variant.into(), Visibility::Public)
|
||||
}
|
||||
}
|
||||
|
@ -381,19 +408,24 @@ impl DefMap {
|
|||
module: LocalModuleId,
|
||||
name: &Name,
|
||||
shadow: BuiltinShadowMode,
|
||||
expected_macro_subns: Option<MacroSubNs>,
|
||||
) -> PerNs {
|
||||
// Resolve in:
|
||||
// - legacy scope of macro
|
||||
// - current module / scope
|
||||
// - extern prelude
|
||||
// - extern prelude / macro_use prelude
|
||||
// - std prelude
|
||||
let from_legacy_macro = self[module]
|
||||
.scope
|
||||
.get_legacy_macro(name)
|
||||
// FIXME: shadowing
|
||||
.and_then(|it| it.last())
|
||||
.map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public));
|
||||
let from_scope = self[module].scope.get(name);
|
||||
.copied()
|
||||
.filter(|&id| {
|
||||
sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
|
||||
})
|
||||
.map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
|
||||
let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
|
||||
let from_builtin = match self.block {
|
||||
Some(_) => {
|
||||
// Only resolve to builtins in the root `DefMap`.
|
||||
|
@ -410,13 +442,27 @@ impl DefMap {
|
|||
};
|
||||
|
||||
let extern_prelude = || {
|
||||
self.extern_prelude
|
||||
if self.block.is_some() {
|
||||
// Don't resolve extern prelude in block `DefMap`s.
|
||||
return PerNs::none();
|
||||
}
|
||||
self.data
|
||||
.extern_prelude
|
||||
.get(name)
|
||||
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
|
||||
};
|
||||
let macro_use_prelude = || {
|
||||
self.macro_use_prelude
|
||||
.get(name)
|
||||
.map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
|
||||
};
|
||||
let prelude = || self.resolve_in_prelude(db, name);
|
||||
|
||||
from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
|
||||
from_legacy_macro
|
||||
.or(from_scope_or_builtin)
|
||||
.or_else(extern_prelude)
|
||||
.or_else(macro_use_prelude)
|
||||
.or_else(prelude)
|
||||
}
|
||||
|
||||
fn resolve_name_in_crate_root_or_extern_prelude(
|
||||
|
@ -426,13 +472,20 @@ impl DefMap {
|
|||
) -> PerNs {
|
||||
let from_crate_root = match self.block {
|
||||
Some(_) => {
|
||||
let def_map = self.crate_root(db).def_map(db);
|
||||
def_map[def_map.root].scope.get(name)
|
||||
let def_map = self.crate_root().def_map(db);
|
||||
def_map[Self::ROOT].scope.get(name)
|
||||
}
|
||||
None => self[self.root].scope.get(name),
|
||||
None => self[Self::ROOT].scope.get(name),
|
||||
};
|
||||
let from_extern_prelude = || {
|
||||
self.resolve_name_in_extern_prelude(db, name)
|
||||
if self.block.is_some() {
|
||||
// Don't resolve extern prelude in block `DefMap`s.
|
||||
return PerNs::none();
|
||||
}
|
||||
self.data
|
||||
.extern_prelude
|
||||
.get(name)
|
||||
.copied()
|
||||
.map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ impl Attrs {
|
|||
}
|
||||
|
||||
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
|
||||
// the same strucuture.
|
||||
// the same structure.
|
||||
#[rustfmt::skip]
|
||||
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
|
||||
match tt {
|
||||
|
|
|
@ -4,10 +4,9 @@ mod macros;
|
|||
mod mod_resolution;
|
||||
mod primitives;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||
use expect_test::{expect, Expect};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::DefDatabase, test_db::TestDB};
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use base_db::SourceDatabaseExt;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{AdtId, ModuleDefId};
|
||||
use crate::{db::DefDatabase, AdtId, ModuleDefId};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -15,7 +14,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, Arc::new(ra_fixture_change.to_string()));
|
||||
db.set_file_text(pos.file_id, Arc::from(ra_fixture_change));
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -96,7 +95,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
|||
});
|
||||
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
|
||||
}
|
||||
db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
|
||||
db.set_file_text(pos.file_id, Arc::from("m!(Y);"));
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_function_should_not_invalidate_expansions() {
|
||||
fn typing_inside_a_function_should_not_invalidate_item_expansions() {
|
||||
let (mut db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
|
@ -140,7 +139,7 @@ m!(Z);
|
|||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).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(")).count();
|
||||
assert_eq!(n_reparsed_macros, 3);
|
||||
}
|
||||
|
||||
|
@ -150,7 +149,7 @@ fn quux() { 92 }
|
|||
m!(Y);
|
||||
m!(Z);
|
||||
"#;
|
||||
db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -161,7 +160,7 @@ m!(Z);
|
|||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 1);
|
||||
let n_reparsed_macros =
|
||||
events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
|
||||
events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
|
||||
assert_eq!(n_reparsed_macros, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,6 +259,72 @@ mod priv_mod {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_use_filter() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:empty,multiple,all
|
||||
#[macro_use()]
|
||||
extern crate empty;
|
||||
|
||||
foo_not_imported!();
|
||||
|
||||
#[macro_use(bar1)]
|
||||
#[macro_use()]
|
||||
#[macro_use(bar2, bar3)]
|
||||
extern crate multiple;
|
||||
|
||||
bar1!();
|
||||
bar2!();
|
||||
bar3!();
|
||||
bar_not_imported!();
|
||||
|
||||
#[macro_use(baz1)]
|
||||
#[macro_use]
|
||||
#[macro_use(baz2)]
|
||||
extern crate all;
|
||||
|
||||
baz1!();
|
||||
baz2!();
|
||||
baz3!();
|
||||
|
||||
//- /empty.rs crate:empty
|
||||
#[macro_export]
|
||||
macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
|
||||
|
||||
//- /multiple.rs crate:multiple
|
||||
#[macro_export]
|
||||
macro_rules! bar1 { () => { struct OkBar1; } }
|
||||
#[macro_export]
|
||||
macro_rules! bar2 { () => { struct OkBar2; } }
|
||||
#[macro_export]
|
||||
macro_rules! bar3 { () => { struct OkBar3; } }
|
||||
#[macro_export]
|
||||
macro_rules! bar_not_imported { () => { struct NotOkBar; } }
|
||||
|
||||
//- /all.rs crate:all
|
||||
#[macro_export]
|
||||
macro_rules! baz1 { () => { struct OkBaz1; } }
|
||||
#[macro_export]
|
||||
macro_rules! baz2 { () => { struct OkBaz2; } }
|
||||
#[macro_export]
|
||||
macro_rules! baz3 { () => { struct OkBaz3; } }
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
OkBar1: t v
|
||||
OkBar2: t v
|
||||
OkBar3: t v
|
||||
OkBaz1: t v
|
||||
OkBaz2: t v
|
||||
OkBaz3: t v
|
||||
all: t
|
||||
empty: t
|
||||
multiple: t
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prelude_is_macro_use() {
|
||||
cov_mark::check!(prelude_is_macro_use);
|
||||
|
@ -664,6 +730,29 @@ pub struct bar;
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_dollar_crate_is_correct_in_derive_meta() {
|
||||
let map = compute_crate_def_map(
|
||||
r#"
|
||||
//- minicore: derive, clone
|
||||
//- /main.rs crate:main deps:lib
|
||||
lib::foo!();
|
||||
|
||||
//- /lib.rs crate:lib
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
#[derive($crate::Clone)]
|
||||
struct S;
|
||||
}
|
||||
}
|
||||
|
||||
pub use core::clone::Clone;
|
||||
"#,
|
||||
);
|
||||
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_derive() {
|
||||
let map = compute_crate_def_map(
|
||||
|
@ -683,7 +772,7 @@ pub macro Copy {}
|
|||
pub macro Clone {}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(map.modules[map.root].scope.impls().len(), 2);
|
||||
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -726,7 +815,7 @@ pub macro derive($item:item) {}
|
|||
pub macro Clone {}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(map.modules[map.root].scope.impls().len(), 1);
|
||||
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -991,7 +1080,7 @@ macro_rules! mbe {
|
|||
|
||||
#[test]
|
||||
fn collects_derive_helpers() {
|
||||
let def_map = compute_crate_def_map(
|
||||
let db = TestDB::with_files(
|
||||
r#"
|
||||
#![crate_type="proc-macro"]
|
||||
struct TokenStream;
|
||||
|
@ -1002,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
|
||||
assert_eq!(def_map.exported_derives.len(), 1);
|
||||
match def_map.exported_derives.values().next() {
|
||||
assert_eq!(def_map.data.exported_derives.len(), 1);
|
||||
match def_map.data.exported_derives.values().next() {
|
||||
Some(helpers) => match &**helpers {
|
||||
[attr] => assert_eq!(attr.to_string(), "helper_attr"),
|
||||
[attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
|
@ -1169,7 +1260,7 @@ struct A;
|
|||
|
||||
#[test]
|
||||
fn macro_use_imports_all_macro_types() {
|
||||
let def_map = compute_crate_def_map(
|
||||
let db = TestDB::with_files(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:lib
|
||||
#[macro_use]
|
||||
|
@ -1192,18 +1283,153 @@ struct TokenStream;
|
|||
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
|
||||
"#,
|
||||
);
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
|
||||
let root = &def_map[def_map.root()].scope;
|
||||
let actual = root
|
||||
.legacy_macros()
|
||||
.sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0))
|
||||
.map(|(name, _)| format!("{name}\n"))
|
||||
.collect::<String>();
|
||||
let root_module = &def_map[DefMap::ROOT].scope;
|
||||
assert!(
|
||||
root_module.legacy_macros().count() == 0,
|
||||
"`#[macro_use]` shouldn't bring macros into textual macro scope",
|
||||
);
|
||||
|
||||
let actual = def_map
|
||||
.macro_use_prelude
|
||||
.iter()
|
||||
.map(|(name, _)| name.display(&db).to_string())
|
||||
.sorted()
|
||||
.join("\n");
|
||||
|
||||
expect![[r#"
|
||||
legacy
|
||||
macro20
|
||||
proc_attr
|
||||
"#]]
|
||||
proc_attr"#]]
|
||||
.assert_eq(&actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
|
||||
check(
|
||||
r#"
|
||||
//- /lib.rs edition:2021 crate:lib deps:dep,core
|
||||
#[macro_use]
|
||||
extern crate dep;
|
||||
|
||||
macro foo() { struct Ok; }
|
||||
macro bar() { fn ok() {} }
|
||||
|
||||
foo!();
|
||||
bar!();
|
||||
|
||||
//- /dep.rs crate:dep
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => { struct NotOk; }
|
||||
}
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod prelude {
|
||||
pub mod rust_2021 {
|
||||
#[macro_export]
|
||||
macro_rules! bar {
|
||||
() => { fn not_ok() {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Ok: t v
|
||||
bar: m
|
||||
dep: t
|
||||
foo: m
|
||||
ok: v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_use_prelude_is_eagerly_expanded() {
|
||||
// See FIXME in `ModCollector::collect_macro_call()`.
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:lib
|
||||
#[macro_use]
|
||||
extern crate lib;
|
||||
mk_foo!();
|
||||
mod a {
|
||||
foo!();
|
||||
}
|
||||
//- /lib.rs crate:lib
|
||||
#[macro_export]
|
||||
macro_rules! mk_foo {
|
||||
() => {
|
||||
macro_rules! foo {
|
||||
() => { struct Ok; }
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
a: t
|
||||
lib: t
|
||||
|
||||
crate::a
|
||||
Ok: t v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_sub_namespace() {
|
||||
let map = compute_crate_def_map(
|
||||
r#"
|
||||
//- minicore: derive, clone
|
||||
macro_rules! Clone { () => {} }
|
||||
macro_rules! derive { () => {} }
|
||||
|
||||
#[derive(Clone)]
|
||||
struct S;
|
||||
"#,
|
||||
);
|
||||
assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_sub_namespace2() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs edition:2021 crate:main deps:proc,core
|
||||
use proc::{foo, bar};
|
||||
|
||||
foo!();
|
||||
bar!();
|
||||
|
||||
//- /proc.rs crate:proc
|
||||
#![crate_type="proc-macro"]
|
||||
#[proc_macro_derive(foo)]
|
||||
pub fn foo() {}
|
||||
#[proc_macro_attribute]
|
||||
pub fn bar() {}
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod prelude {
|
||||
pub mod rust_2021 {
|
||||
pub macro foo() {
|
||||
struct Ok;
|
||||
}
|
||||
pub macro bar() {
|
||||
fn ok() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Ok: t v
|
||||
bar: m
|
||||
foo: m
|
||||
ok: v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -887,7 +887,7 @@ mod module;
|
|||
//- /module.rs
|
||||
#![cfg(NEVER)]
|
||||
|
||||
struct AlsoShoulntAppear;
|
||||
struct AlsoShouldNotAppear;
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
|
|
|
@ -7,15 +7,14 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
type_ref::{ConstRefOrPath, LifetimeRef},
|
||||
lang_item::LangItemTarget,
|
||||
lower::LowerCtx,
|
||||
type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef},
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::type_ref::{TypeBound, TypeRef};
|
||||
|
||||
pub use hir_expand::mod_path::{path, ModPath, PathKind};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -36,13 +35,19 @@ impl Display for ImportAlias {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path {
|
||||
/// Type based path like `<T>::foo`.
|
||||
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
|
||||
type_anchor: Option<Interned<TypeRef>>,
|
||||
mod_path: Interned<ModPath>,
|
||||
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
|
||||
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
|
||||
pub enum Path {
|
||||
/// A normal path
|
||||
Normal {
|
||||
/// Type based path like `<T>::foo`.
|
||||
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
|
||||
type_anchor: Option<Interned<TypeRef>>,
|
||||
mod_path: Interned<ModPath>,
|
||||
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
|
||||
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
|
||||
},
|
||||
/// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
|
||||
/// links via a normal path since they might be private and not accessible in the usage place.
|
||||
LangItem(LangItemTarget),
|
||||
}
|
||||
|
||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||
|
@ -102,51 +107,77 @@ impl Path {
|
|||
) -> Path {
|
||||
let generic_args = generic_args.into();
|
||||
assert_eq!(path.len(), generic_args.len());
|
||||
Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
|
||||
Path::Normal {
|
||||
type_anchor: None,
|
||||
mod_path: Interned::new(path),
|
||||
generic_args: Some(generic_args),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a known mod path to `Path`.
|
||||
pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
|
||||
Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &PathKind {
|
||||
&self.mod_path.kind
|
||||
match self {
|
||||
Path::Normal { mod_path, .. } => &mod_path.kind,
|
||||
Path::LangItem(_) => &PathKind::Abs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
||||
self.type_anchor.as_deref()
|
||||
match self {
|
||||
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
|
||||
Path::LangItem(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments(&self) -> PathSegments<'_> {
|
||||
let s = PathSegments {
|
||||
segments: self.mod_path.segments(),
|
||||
generic_args: self.generic_args.as_deref(),
|
||||
let Path::Normal { mod_path, generic_args, .. } = self else {
|
||||
return PathSegments {
|
||||
segments: &[],
|
||||
generic_args: None,
|
||||
};
|
||||
};
|
||||
let s =
|
||||
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
|
||||
if let Some(generic_args) = s.generic_args {
|
||||
assert_eq!(s.segments.len(), generic_args.len());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn mod_path(&self) -> &ModPath {
|
||||
&self.mod_path
|
||||
pub fn mod_path(&self) -> Option<&ModPath> {
|
||||
match self {
|
||||
Path::Normal { mod_path, .. } => Some(&mod_path),
|
||||
Path::LangItem(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn qualifier(&self) -> Option<Path> {
|
||||
if self.mod_path.is_ident() {
|
||||
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
|
||||
return None;
|
||||
};
|
||||
if mod_path.is_ident() {
|
||||
return None;
|
||||
}
|
||||
let res = Path {
|
||||
type_anchor: self.type_anchor.clone(),
|
||||
let res = Path::Normal {
|
||||
type_anchor: type_anchor.clone(),
|
||||
mod_path: Interned::new(ModPath::from_segments(
|
||||
self.mod_path.kind,
|
||||
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
|
||||
mod_path.kind,
|
||||
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
|
||||
)),
|
||||
generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
|
||||
generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn is_self_type(&self) -> bool {
|
||||
self.type_anchor.is_none()
|
||||
&& self.generic_args.as_deref().is_none()
|
||||
&& self.mod_path.is_Self()
|
||||
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
|
||||
return false;
|
||||
};
|
||||
type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +253,7 @@ impl GenericArgs {
|
|||
|
||||
impl From<Name> for Path {
|
||||
fn from(name: Name) -> Path {
|
||||
Path {
|
||||
Path::Normal {
|
||||
type_anchor: None,
|
||||
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
|
||||
generic_args: None,
|
||||
|
|
|
@ -2,17 +2,15 @@
|
|||
|
||||
use std::iter;
|
||||
|
||||
use crate::type_ref::ConstRefOrPath;
|
||||
use crate::{lower::LowerCtx, type_ref::ConstRefOrPath};
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::name::{name, AsName};
|
||||
use intern::Interned;
|
||||
use syntax::ast::{self, AstNode, HasTypeBounds};
|
||||
|
||||
use super::AssociatedTypeBinding;
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||
path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
|
@ -75,8 +73,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
|||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let Path { mod_path, generic_args: path_generic_args, .. } =
|
||||
Path::from_src(trait_ref.path()?, ctx)?;
|
||||
let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
|
||||
Path::from_src(trait_ref.path()?, ctx)? else
|
||||
{
|
||||
return None;
|
||||
};
|
||||
let num_segments = mod_path.segments().len();
|
||||
kind = mod_path.kind;
|
||||
|
||||
|
@ -157,7 +158,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
|||
}
|
||||
|
||||
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
|
||||
return Some(Path {
|
||||
return Some(Path::Normal {
|
||||
type_anchor,
|
||||
mod_path,
|
||||
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
|
||||
|
@ -188,6 +189,10 @@ pub(super) fn lower_generic_args(
|
|||
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() {
|
||||
let name = name_ref.as_name();
|
||||
let args = assoc_type_arg
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::mod_path::PathKind;
|
||||
use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
|
||||
use intern::Interned;
|
||||
use itertools::Itertools;
|
||||
|
||||
|
@ -11,11 +11,14 @@ use crate::{
|
|||
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||
pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||
if let Path::LangItem(x) = path {
|
||||
return write!(buf, "$lang_item::{x:?}");
|
||||
}
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
write!(buf, "<")?;
|
||||
print_type_ref(anchor, buf)?;
|
||||
print_type_ref(db, anchor, buf)?;
|
||||
write!(buf, ">::")?;
|
||||
}
|
||||
None => match path.kind() {
|
||||
|
@ -41,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
|||
write!(buf, "::")?;
|
||||
}
|
||||
|
||||
write!(buf, "{}", segment.name)?;
|
||||
write!(buf, "{}", segment.name.display(db))?;
|
||||
if let Some(generics) = segment.args_and_bindings {
|
||||
write!(buf, "::<")?;
|
||||
print_generic_args(generics, buf)?;
|
||||
print_generic_args(db, generics, buf)?;
|
||||
|
||||
write!(buf, ">")?;
|
||||
}
|
||||
|
@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
|
||||
pub(crate) fn print_generic_args(
|
||||
db: &dyn ExpandDatabase,
|
||||
generics: &GenericArgs,
|
||||
buf: &mut dyn Write,
|
||||
) -> 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(self_ty, buf)?;
|
||||
print_generic_arg(db, self_ty, buf)?;
|
||||
first = false;
|
||||
args
|
||||
} else {
|
||||
|
@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
|
|||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
print_generic_arg(arg, buf)?;
|
||||
print_generic_arg(db, arg, buf)?;
|
||||
}
|
||||
for binding in generics.bindings.iter() {
|
||||
if !first {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(buf, "{}", binding.name)?;
|
||||
write!(buf, "{}", binding.name.display(db))?;
|
||||
if !binding.bounds.is_empty() {
|
||||
write!(buf, ": ")?;
|
||||
print_type_bounds(&binding.bounds, buf)?;
|
||||
print_type_bounds(db, &binding.bounds, buf)?;
|
||||
}
|
||||
if let Some(ty) = &binding.type_ref {
|
||||
write!(buf, " = ")?;
|
||||
print_type_ref(ty, buf)?;
|
||||
print_type_ref(db, ty, buf)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
|
||||
pub(crate) fn print_generic_arg(
|
||||
db: &dyn ExpandDatabase,
|
||||
arg: &GenericArg,
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
match arg {
|
||||
GenericArg::Type(ty) => print_type_ref(ty, buf),
|
||||
GenericArg::Const(c) => write!(buf, "{c}"),
|
||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
|
||||
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
|
||||
GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
|
||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
|
||||
pub(crate) fn print_type_ref(
|
||||
db: &dyn ExpandDatabase,
|
||||
type_ref: &TypeRef,
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
// FIXME: deduplicate with `HirDisplay` impl
|
||||
match type_ref {
|
||||
TypeRef::Never => write!(buf, "!")?,
|
||||
|
@ -108,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
|
|||
if i != 0 {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
print_type_ref(field, buf)?;
|
||||
print_type_ref(db, field, buf)?;
|
||||
}
|
||||
write!(buf, ")")?;
|
||||
}
|
||||
TypeRef::Path(path) => print_path(path, buf)?,
|
||||
TypeRef::Path(path) => print_path(db, path, buf)?,
|
||||
TypeRef::RawPtr(pointee, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
Mutability::Shared => "*const",
|
||||
Mutability::Mut => "*mut",
|
||||
};
|
||||
write!(buf, "{mtbl} ")?;
|
||||
print_type_ref(pointee, buf)?;
|
||||
print_type_ref(db, pointee, buf)?;
|
||||
}
|
||||
TypeRef::Reference(pointee, lt, mtbl) => {
|
||||
let mtbl = match mtbl {
|
||||
|
@ -128,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
|
|||
};
|
||||
write!(buf, "&")?;
|
||||
if let Some(lt) = lt {
|
||||
write!(buf, "{} ", lt.name)?;
|
||||
write!(buf, "{} ", lt.name.display(db))?;
|
||||
}
|
||||
write!(buf, "{mtbl}")?;
|
||||
print_type_ref(pointee, buf)?;
|
||||
print_type_ref(db, pointee, buf)?;
|
||||
}
|
||||
TypeRef::Array(elem, len) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(elem, buf)?;
|
||||
write!(buf, "; {len}]")?;
|
||||
print_type_ref(db, elem, buf)?;
|
||||
write!(buf, "; {}]", len.display(db))?;
|
||||
}
|
||||
TypeRef::Slice(elem) => {
|
||||
write!(buf, "[")?;
|
||||
print_type_ref(elem, buf)?;
|
||||
print_type_ref(db, elem, buf)?;
|
||||
write!(buf, "]")?;
|
||||
}
|
||||
TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
|
||||
|
@ -154,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
|
|||
if i != 0 {
|
||||
write!(buf, ", ")?;
|
||||
}
|
||||
print_type_ref(typeref, buf)?;
|
||||
print_type_ref(db, typeref, buf)?;
|
||||
}
|
||||
if *varargs {
|
||||
if !args.is_empty() {
|
||||
|
@ -163,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
|
|||
write!(buf, "...")?;
|
||||
}
|
||||
write!(buf, ") -> ")?;
|
||||
print_type_ref(return_type, buf)?;
|
||||
print_type_ref(db, return_type, buf)?;
|
||||
}
|
||||
TypeRef::Macro(_ast_id) => {
|
||||
write!(buf, "<macro>")?;
|
||||
|
@ -171,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
|
|||
TypeRef::Error => write!(buf, "{{unknown}}")?,
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
write!(buf, "impl ")?;
|
||||
print_type_bounds(bounds, buf)?;
|
||||
print_type_bounds(db, bounds, buf)?;
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
write!(buf, "dyn ")?;
|
||||
print_type_bounds(bounds, buf)?;
|
||||
print_type_bounds(db, bounds, buf)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
|
|||
}
|
||||
|
||||
pub(crate) fn print_type_bounds(
|
||||
db: &dyn ExpandDatabase,
|
||||
bounds: &[Interned<TypeBound>],
|
||||
buf: &mut dyn Write,
|
||||
) -> fmt::Result {
|
||||
|
@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds(
|
|||
TraitBoundModifier::None => (),
|
||||
TraitBoundModifier::Maybe => write!(buf, "?")?,
|
||||
}
|
||||
print_path(path, buf)?;
|
||||
print_path(db, path, buf)?;
|
||||
}
|
||||
TypeBound::ForLifetime(lifetimes, path) => {
|
||||
write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
|
||||
print_path(path, buf)?;
|
||||
write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
|
||||
print_path(db, path, buf)?;
|
||||
}
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
|
||||
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
|
||||
TypeBound::Error => write!(buf, "{{unknown}}")?,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Name resolution façade.
|
||||
use std::{fmt, hash::BuildHasherDefault, sync::Arc};
|
||||
use std::{fmt, hash::BuildHasherDefault};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::name::{name, Name};
|
||||
|
@ -7,16 +7,18 @@ use indexmap::IndexMap;
|
|||
use intern::Interned;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
body::scope::{ExprScopes, ScopeId},
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
expr::{BindingId, ExprId, LabelId},
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
hir::{BindingId, ExprId, LabelId},
|
||||
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
|
||||
nameres::DefMap,
|
||||
path::{ModPath, PathKind},
|
||||
lang_item::LangItemTarget,
|
||||
nameres::{DefMap, MacroSubNs},
|
||||
path::{ModPath, Path, PathKind},
|
||||
per_ns::PerNs,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
|
||||
|
@ -78,7 +80,7 @@ enum Scope {
|
|||
ExprScope(ExprScope),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum TypeNs {
|
||||
SelfType(ImplId),
|
||||
GenericParam(TypeParamId),
|
||||
|
@ -153,7 +155,8 @@ impl Resolver {
|
|||
path: &ModPath,
|
||||
) -> Option<PerNs> {
|
||||
let (item_map, module) = self.item_scope();
|
||||
let (module_res, idx) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
|
||||
let (module_res, idx) =
|
||||
item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
|
||||
match module_res.take_types()? {
|
||||
ModuleDefId::TraitId(it) => {
|
||||
let idx = idx?;
|
||||
|
@ -176,8 +179,27 @@ impl Resolver {
|
|||
pub fn resolve_path_in_type_ns(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> Option<(TypeNs, Option<usize>)> {
|
||||
let path = match path {
|
||||
Path::Normal { mod_path, .. } => mod_path,
|
||||
Path::LangItem(l) => {
|
||||
return Some((
|
||||
match *l {
|
||||
LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
|
||||
LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
|
||||
LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
|
||||
LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
|
||||
LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
|
||||
LangItemTarget::Trait(x) => TypeNs::TraitId(x),
|
||||
LangItemTarget::Function(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::Static(_) => return None,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
let first_name = path.segments().first()?;
|
||||
let skip_to_mod = path.kind != PathKind::Plain;
|
||||
if skip_to_mod {
|
||||
|
@ -217,7 +239,7 @@ impl Resolver {
|
|||
pub fn resolve_path_in_type_ns_fully(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> Option<TypeNs> {
|
||||
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
|
||||
if unresolved.is_some() {
|
||||
|
@ -245,8 +267,24 @@ impl Resolver {
|
|||
pub fn resolve_path_in_value_ns(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> Option<ResolveValueResult> {
|
||||
let path = match path {
|
||||
Path::Normal { mod_path, .. } => mod_path,
|
||||
Path::LangItem(l) => {
|
||||
return Some(ResolveValueResult::ValueNs(match *l {
|
||||
LangItemTarget::Function(x) => ValueNs::FunctionId(x),
|
||||
LangItemTarget::Static(x) => ValueNs::StaticId(x),
|
||||
LangItemTarget::Struct(x) => ValueNs::StructId(x),
|
||||
LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
|
||||
LangItemTarget::Union(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::TypeAlias(_)
|
||||
| LangItemTarget::Trait(_)
|
||||
| LangItemTarget::EnumId(_) => return None,
|
||||
}))
|
||||
}
|
||||
};
|
||||
let n_segments = path.segments().len();
|
||||
let tmp = name![self];
|
||||
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
|
||||
|
@ -340,7 +378,7 @@ impl Resolver {
|
|||
pub fn resolve_path_in_value_ns_fully(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> Option<ValueNs> {
|
||||
match self.resolve_path_in_value_ns(db, path)? {
|
||||
ResolveValueResult::ValueNs(it) => Some(it),
|
||||
|
@ -348,9 +386,17 @@ impl Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
|
||||
pub fn resolve_path_as_macro(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
expected_macro_kind: Option<MacroSubNs>,
|
||||
) -> Option<MacroId> {
|
||||
let (item_map, module) = self.item_scope();
|
||||
item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
|
||||
item_map
|
||||
.resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
|
||||
.0
|
||||
.take_macros()
|
||||
}
|
||||
|
||||
/// Returns a set of names available in the current scope.
|
||||
|
@ -415,7 +461,10 @@ impl Resolver {
|
|||
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
|
||||
})
|
||||
});
|
||||
def_map.extern_prelude().for_each(|(name, &def)| {
|
||||
def_map.macro_use_prelude().for_each(|(name, def)| {
|
||||
res.add(name, ScopeDef::ModuleDef(def.into()));
|
||||
});
|
||||
def_map.extern_prelude().for_each(|(name, def)| {
|
||||
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
|
||||
});
|
||||
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
|
||||
|
@ -441,7 +490,7 @@ impl Resolver {
|
|||
&Scope::ImplDefScope(impl_) => {
|
||||
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
|
||||
if let Some(TypeNs::TraitId(trait_)) =
|
||||
self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path())
|
||||
self.resolve_path_in_type_ns_fully(db, &target_trait.path)
|
||||
{
|
||||
traits.insert(trait_);
|
||||
}
|
||||
|
@ -536,15 +585,13 @@ impl Resolver {
|
|||
scope_id,
|
||||
}));
|
||||
if let Some(block) = expr_scopes.block(scope_id) {
|
||||
if let Some(def_map) = db.block_def_map(block) {
|
||||
let root = def_map.root();
|
||||
resolver
|
||||
.scopes
|
||||
.push(Scope::BlockScope(ModuleItemMap { def_map, module_id: 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?
|
||||
}
|
||||
let def_map = db.block_def_map(block);
|
||||
resolver
|
||||
.scopes
|
||||
.push(Scope::BlockScope(ModuleItemMap { 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?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,7 +639,8 @@ impl Resolver {
|
|||
shadow: BuiltinShadowMode,
|
||||
) -> PerNs {
|
||||
let (item_map, module) = self.item_scope();
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
|
||||
// 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);
|
||||
if segment_index.is_some() {
|
||||
return PerNs::none();
|
||||
}
|
||||
|
@ -705,13 +753,11 @@ fn resolver_for_scope_(
|
|||
|
||||
for scope in scope_chain.into_iter().rev() {
|
||||
if let Some(block) = scopes.block(scope) {
|
||||
if let Some(def_map) = db.block_def_map(block) {
|
||||
let root = def_map.root();
|
||||
r = r.push_block_scope(def_map, 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?
|
||||
}
|
||||
let def_map = db.block_def_map(block);
|
||||
r = r.push_block_scope(def_map, 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?
|
||||
}
|
||||
|
||||
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
|
||||
|
@ -1000,6 +1046,12 @@ impl HasResolver for GenericDefId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasResolver for EnumVariantId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
self.parent.resolver(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for VariantId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
match self {
|
||||
|
|
|
@ -20,7 +20,7 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
|
|||
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
||||
let tree = self.id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
||||
let root = db.parse_or_expand(self.id.file_id()).unwrap();
|
||||
let root = db.parse_or_expand(self.id.file_id());
|
||||
let node = &tree[self.id.value];
|
||||
|
||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
||||
|
@ -33,7 +33,7 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
|
|||
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
||||
let tree = self.id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
||||
let root = db.parse_or_expand(self.id.file_id()).unwrap();
|
||||
let root = db.parse_or_expand(self.id.file_id());
|
||||
let node = &tree[self.id.value];
|
||||
|
||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
||||
|
@ -46,7 +46,7 @@ impl HasSource for Macro2Loc {
|
|||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
||||
let tree = self.id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
||||
let root = db.parse_or_expand(self.id.file_id()).unwrap();
|
||||
let root = db.parse_or_expand(self.id.file_id());
|
||||
let node = &tree[self.id.value];
|
||||
|
||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
||||
|
@ -59,7 +59,7 @@ impl HasSource for MacroRulesLoc {
|
|||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
||||
let tree = self.id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
||||
let root = db.parse_or_expand(self.id.file_id()).unwrap();
|
||||
let root = db.parse_or_expand(self.id.file_id());
|
||||
let node = &tree[self.id.value];
|
||||
|
||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
||||
|
@ -72,7 +72,7 @@ impl HasSource for ProcMacroLoc {
|
|||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
||||
let tree = self.id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
||||
let root = db.parse_or_expand(self.id.file_id()).unwrap();
|
||||
let root = db.parse_or_expand(self.id.file_id());
|
||||
let node = &tree[self.id.value];
|
||||
|
||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
//! Database used for testing `hir_def`.
|
||||
|
||||
use std::{
|
||||
fmt, panic,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{fmt, panic, sync::Mutex};
|
||||
|
||||
use base_db::{
|
||||
salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
|
||||
SourceDatabase, Upcast,
|
||||
salsa::{self, Durability},
|
||||
AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
|
||||
Upcast,
|
||||
};
|
||||
use hir_expand::{db::ExpandDatabase, InFile};
|
||||
use stdx::hash::NoHashHashSet;
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{algo, ast, AstNode};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
@ -35,7 +34,7 @@ pub(crate) struct TestDB {
|
|||
impl Default for TestDB {
|
||||
fn default() -> Self {
|
||||
let mut this = Self { storage: Default::default(), events: Default::default() };
|
||||
this.set_enable_proc_attr_macros(true);
|
||||
this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
|
||||
this
|
||||
}
|
||||
}
|
||||
|
@ -70,13 +69,13 @@ impl fmt::Debug for TestDB {
|
|||
impl panic::RefUnwindSafe for TestDB {}
|
||||
|
||||
impl FileLoader for TestDB {
|
||||
fn file_text(&self, file_id: FileId) -> Arc<String> {
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str> {
|
||||
FileLoaderDelegate(self).file_text(file_id)
|
||||
}
|
||||
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
|
||||
FileLoaderDelegate(self).resolve_path(path)
|
||||
}
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
|
||||
FileLoaderDelegate(self).relevant_crates(file_id)
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +110,7 @@ impl TestDB {
|
|||
}
|
||||
_ => {
|
||||
// FIXME: handle `mod` inside block expression
|
||||
return def_map.module_id(def_map.root());
|
||||
return def_map.module_id(DefMap::ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +119,7 @@ impl TestDB {
|
|||
/// Finds the smallest/innermost module in `def_map` containing `position`.
|
||||
fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
|
||||
let mut size = None;
|
||||
let mut res = def_map.root();
|
||||
let mut res = DefMap::ROOT;
|
||||
for (module, data) in def_map.modules() {
|
||||
let src = data.definition_source(self);
|
||||
if src.file_id != position.file_id.into() {
|
||||
|
@ -209,13 +208,11 @@ impl TestDB {
|
|||
});
|
||||
|
||||
for scope in scope_iter {
|
||||
let containing_blocks =
|
||||
let mut containing_blocks =
|
||||
scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
|
||||
|
||||
for block in containing_blocks {
|
||||
if let Some(def_map) = self.block_def_map(block) {
|
||||
return Some(def_map);
|
||||
}
|
||||
if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) {
|
||||
return Some(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
use std::iter;
|
||||
|
||||
use hir_expand::{hygiene::Hygiene, InFile};
|
||||
use la_arena::ArenaMap;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
|
|
@ -22,6 +22,7 @@ hashbrown = { version = "0.12.1", features = [
|
|||
"inline-more",
|
||||
], default-features = false }
|
||||
smallvec.workspace = true
|
||||
triomphe.workspace = true
|
||||
|
||||
# local deps
|
||||
stdx.workspace = true
|
||||
|
|
|
@ -115,6 +115,7 @@ impl AstIdMap {
|
|||
}
|
||||
}
|
||||
}
|
||||
res.arena.shrink_to_fit();
|
||||
res
|
||||
}
|
||||
|
||||
|
@ -123,6 +124,10 @@ impl AstIdMap {
|
|||
FileAstId { raw, _ty: PhantomData }
|
||||
}
|
||||
|
||||
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
||||
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
|
||||
}
|
||||
|
||||
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
|
||||
let ptr = SyntaxNodePtr::new(item);
|
||||
let hash = hash_ptr(&ptr);
|
||||
|
@ -136,10 +141,6 @@ impl AstIdMap {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
||||
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
|
||||
}
|
||||
|
||||
fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
|
||||
self.arena.alloc(SyntaxNodePtr::new(item))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
use std::{fmt, ops, sync::Arc};
|
||||
use std::{fmt, ops};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgExpr;
|
||||
|
@ -8,12 +8,12 @@ use intern::Interned;
|
|||
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::ExpandDatabase,
|
||||
hygiene::Hygiene,
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::AsName,
|
||||
mod_path::ModPath,
|
||||
tt::{self, Subtree},
|
||||
InFile,
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
|||
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RawAttrs {
|
||||
// FIXME: Make this a ThinArc
|
||||
entries: Option<Arc<[Attr]>>,
|
||||
}
|
||||
|
||||
|
@ -50,7 +51,9 @@ impl RawAttrs {
|
|||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||
}),
|
||||
})
|
||||
.collect::<Arc<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
// FIXME: use `Arc::from_iter` when it becomes available
|
||||
let entries: Arc<[Attr]> = Arc::from(entries);
|
||||
|
||||
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
|
||||
}
|
||||
|
@ -68,7 +71,7 @@ impl RawAttrs {
|
|||
(Some(a), Some(b)) => {
|
||||
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
|
||||
Self {
|
||||
entries: Some(
|
||||
entries: Some(Arc::from(
|
||||
a.iter()
|
||||
.cloned()
|
||||
.chain(b.iter().map(|it| {
|
||||
|
@ -78,8 +81,9 @@ impl RawAttrs {
|
|||
<< AttrId::AST_INDEX_BITS;
|
||||
it
|
||||
}))
|
||||
.collect(),
|
||||
),
|
||||
// FIXME: use `Arc::from_iter` when it becomes available
|
||||
.collect::<Vec<_>>(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,48 +100,51 @@ impl RawAttrs {
|
|||
}
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let new_attrs = self
|
||||
.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
let new_attrs = Arc::from(
|
||||
self.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
|
||||
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
|
||||
Some(it) => it,
|
||||
None => return smallvec![attr.clone()],
|
||||
};
|
||||
let index = attr.id;
|
||||
let attrs =
|
||||
parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| {
|
||||
let tree = Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
token_trees: attr.to_vec(),
|
||||
};
|
||||
// FIXME hygiene
|
||||
let hygiene = Hygiene::new_unhygienic();
|
||||
Attr::from_tt(db, &tree, &hygiene, 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)| {
|
||||
let tree = Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
token_trees: attr.to_vec(),
|
||||
};
|
||||
// FIXME hygiene
|
||||
let hygiene = Hygiene::new_unhygienic();
|
||||
Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
|
||||
},
|
||||
);
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
// FIXME: use `Arc::from_iter` when it becomes available
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
RawAttrs { entries: Some(new_attrs) }
|
||||
}
|
||||
|
@ -266,7 +273,11 @@ impl Attr {
|
|||
}
|
||||
|
||||
/// Parses this attribute as a token tree consisting of comma separated paths.
|
||||
pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
|
||||
pub fn parse_path_comma_token_tree<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn ExpandDatabase,
|
||||
hygiene: &'a Hygiene,
|
||||
) -> Option<impl Iterator<Item = ModPath> + 'a> {
|
||||
let args = self.token_tree_value()?;
|
||||
|
||||
if args.delimiter.kind != DelimiterKind::Parenthesis {
|
||||
|
@ -275,19 +286,37 @@ impl Attr {
|
|||
let paths = args
|
||||
.token_trees
|
||||
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
|
||||
.filter_map(|tts| {
|
||||
.filter_map(move |tts| {
|
||||
if tts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let segments = tts.iter().filter_map(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
|
||||
_ => None,
|
||||
});
|
||||
Some(ModPath::from_segments(PathKind::Plain, segments))
|
||||
// FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
|
||||
let subtree = tt::Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
token_trees: tts.into_iter().cloned().collect(),
|
||||
};
|
||||
let (parse, _) =
|
||||
mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem);
|
||||
let meta = ast::Meta::cast(parse.syntax_node())?;
|
||||
// Only simple paths are allowed.
|
||||
if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let path = meta.path()?;
|
||||
ModPath::from_src(db, path, hygiene)
|
||||
});
|
||||
|
||||
Some(paths)
|
||||
}
|
||||
|
||||
pub fn cfg(&self) -> Option<CfgExpr> {
|
||||
if *self.path.as_ident()? == crate::name![cfg] {
|
||||
self.token_tree_value().map(CfgExpr::parse)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_attrs(
|
||||
|
|
|
@ -96,7 +96,7 @@ fn derive_attr_expand(
|
|||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let derives = match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0,
|
||||
MacroCallKind::Attr { attr_args, .. } if loc.def.is_attribute_derive() => &attr_args.0,
|
||||
_ => return ExpandResult::ok(tt::Subtree::empty()),
|
||||
};
|
||||
pseudo_derive_attr_expansion(tt, derives)
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
//! Builtin derives.
|
||||
|
||||
use ::tt::Ident;
|
||||
use base_db::{CrateOrigin, LangCrateOrigin};
|
||||
use itertools::izip;
|
||||
use mbe::TokenMap;
|
||||
use std::collections::HashSet;
|
||||
use stdx::never;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::tt::{self, TokenId};
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
|
||||
ast::{
|
||||
self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName,
|
||||
HasTypeBounds, PathType,
|
||||
},
|
||||
match_ast,
|
||||
};
|
||||
|
||||
|
@ -58,10 +66,129 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
|
|||
BuiltinDeriveExpander::find_by_name(ident)
|
||||
}
|
||||
|
||||
enum VariantShape {
|
||||
Struct(Vec<tt::Ident>),
|
||||
Tuple(usize),
|
||||
Unit,
|
||||
}
|
||||
|
||||
fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
|
||||
(0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified()))
|
||||
}
|
||||
|
||||
impl VariantShape {
|
||||
fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
|
||||
self.as_pattern_map(path, |x| quote!(#x))
|
||||
}
|
||||
|
||||
fn field_names(&self) -> Vec<tt::Ident> {
|
||||
match self {
|
||||
VariantShape::Struct(s) => s.clone(),
|
||||
VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(),
|
||||
VariantShape::Unit => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn as_pattern_map(
|
||||
&self,
|
||||
path: tt::Subtree,
|
||||
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
|
||||
) -> tt::Subtree {
|
||||
match self {
|
||||
VariantShape::Struct(fields) => {
|
||||
let fields = fields.iter().map(|x| {
|
||||
let mapped = field_map(x);
|
||||
quote! { #x : #mapped , }
|
||||
});
|
||||
quote! {
|
||||
#path { ##fields }
|
||||
}
|
||||
}
|
||||
&VariantShape::Tuple(n) => {
|
||||
let fields = tuple_field_iterator(n).map(|x| {
|
||||
let mapped = field_map(&x);
|
||||
quote! {
|
||||
#mapped ,
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
#path ( ##fields )
|
||||
}
|
||||
}
|
||||
VariantShape::Unit => path,
|
||||
}
|
||||
}
|
||||
|
||||
fn from(value: Option<FieldList>, token_map: &TokenMap) -> Result<Self, ExpandError> {
|
||||
let r = match value {
|
||||
None => VariantShape::Unit,
|
||||
Some(FieldList::RecordFieldList(x)) => VariantShape::Struct(
|
||||
x.fields()
|
||||
.map(|x| x.name())
|
||||
.map(|x| name_to_token(token_map, x))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()),
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
enum AdtShape {
|
||||
Struct(VariantShape),
|
||||
Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option<usize> },
|
||||
Union,
|
||||
}
|
||||
|
||||
impl AdtShape {
|
||||
fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
|
||||
self.as_pattern_map(name, |x| quote!(#x))
|
||||
}
|
||||
|
||||
fn field_names(&self) -> Vec<Vec<tt::Ident>> {
|
||||
match self {
|
||||
AdtShape::Struct(s) => {
|
||||
vec![s.field_names()]
|
||||
}
|
||||
AdtShape::Enum { variants, .. } => {
|
||||
variants.iter().map(|(_, fields)| fields.field_names()).collect()
|
||||
}
|
||||
AdtShape::Union => {
|
||||
never!("using fields of union in derive is always wrong");
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_pattern_map(
|
||||
&self,
|
||||
name: &tt::Ident,
|
||||
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
|
||||
) -> Vec<tt::Subtree> {
|
||||
match self {
|
||||
AdtShape::Struct(s) => {
|
||||
vec![s.as_pattern_map(quote! { #name }, field_map)]
|
||||
}
|
||||
AdtShape::Enum { variants, .. } => variants
|
||||
.iter()
|
||||
.map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map))
|
||||
.collect(),
|
||||
AdtShape::Union => {
|
||||
never!("pattern matching on union is always wrong");
|
||||
vec![quote! { un }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BasicAdtInfo {
|
||||
name: tt::Ident,
|
||||
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
|
||||
param_types: Vec<Option<tt::Subtree>>,
|
||||
shape: AdtShape,
|
||||
/// first field is the name, and
|
||||
/// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
|
||||
/// third fields is where bounds, if any
|
||||
param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
|
||||
associated_types: Vec<tt::Subtree>,
|
||||
}
|
||||
|
||||
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
|
@ -75,29 +202,52 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
|||
ExpandError::Other("no item found".into())
|
||||
})?;
|
||||
let node = item.syntax();
|
||||
let (name, params) = match_ast! {
|
||||
let (name, params, shape) = match_ast! {
|
||||
match node {
|
||||
ast::Struct(it) => (it.name(), it.generic_param_list()),
|
||||
ast::Enum(it) => (it.name(), it.generic_param_list()),
|
||||
ast::Union(it) => (it.name(), it.generic_param_list()),
|
||||
ast::Struct(it) => (it.name(), it.generic_param_list(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)),
|
||||
ast::Enum(it) => {
|
||||
let default_variant = it.variant_list().into_iter().flat_map(|x| x.variants()).position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
|
||||
(
|
||||
it.name(),
|
||||
it.generic_param_list(),
|
||||
AdtShape::Enum {
|
||||
default_variant,
|
||||
variants: it.variant_list()
|
||||
.into_iter()
|
||||
.flat_map(|x| x.variants())
|
||||
.map(|x| Ok((name_to_token(&token_map,x.name())?, VariantShape::from(x.field_list(), &token_map)?))).collect::<Result<_, ExpandError>>()?
|
||||
}
|
||||
)
|
||||
},
|
||||
ast::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
|
||||
_ => {
|
||||
debug!("unexpected node is {:?}", node);
|
||||
return Err(ExpandError::Other("expected struct, enum or union".into()))
|
||||
},
|
||||
}
|
||||
};
|
||||
let name = name.ok_or_else(|| {
|
||||
debug!("parsed item has no name");
|
||||
ExpandError::Other("missing name".into())
|
||||
})?;
|
||||
let name_token_id =
|
||||
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
|
||||
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
|
||||
let mut param_type_set: HashSet<String> = HashSet::new();
|
||||
let param_types = params
|
||||
.into_iter()
|
||||
.flat_map(|param_list| param_list.type_or_const_params())
|
||||
.map(|param| {
|
||||
if let ast::TypeOrConstParam::Const(param) = param {
|
||||
let name = {
|
||||
let this = param.name();
|
||||
match this {
|
||||
Some(x) => {
|
||||
param_type_set.insert(x.to_string());
|
||||
mbe::syntax_node_to_token_tree(x.syntax()).0
|
||||
}
|
||||
None => tt::Subtree::empty(),
|
||||
}
|
||||
};
|
||||
let bounds = match ¶m {
|
||||
ast::TypeOrConstParam::Type(x) => {
|
||||
x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
|
||||
}
|
||||
ast::TypeOrConstParam::Const(_) => None,
|
||||
};
|
||||
let ty = if let ast::TypeOrConstParam::Const(param) = param {
|
||||
let ty = param
|
||||
.ty()
|
||||
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
|
||||
|
@ -105,27 +255,107 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
|||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
(name, ty, bounds)
|
||||
})
|
||||
.collect();
|
||||
Ok(BasicAdtInfo { name: name_token, param_types })
|
||||
let is_associated_type = |p: &PathType| {
|
||||
if let Some(p) = p.path() {
|
||||
if let Some(parent) = p.qualifier() {
|
||||
if let Some(x) = parent.segment() {
|
||||
if let Some(x) = x.path_type() {
|
||||
if let Some(x) = x.path() {
|
||||
if let Some(pname) = x.as_single_name_ref() {
|
||||
if param_type_set.contains(&pname.to_string()) {
|
||||
// <T as Trait>::Assoc
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pname) = parent.as_single_name_ref() {
|
||||
if param_type_set.contains(&pname.to_string()) {
|
||||
// T::Assoc
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
let associated_types = node
|
||||
.descendants()
|
||||
.filter_map(PathType::cast)
|
||||
.filter(is_associated_type)
|
||||
.map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
|
||||
.collect::<Vec<_>>();
|
||||
let name_token = name_to_token(&token_map, name)?;
|
||||
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
|
||||
}
|
||||
|
||||
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
|
||||
let name = name.ok_or_else(|| {
|
||||
debug!("parsed item has no name");
|
||||
ExpandError::Other("missing name".into())
|
||||
})?;
|
||||
let name_token_id =
|
||||
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
|
||||
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
|
||||
Ok(name_token)
|
||||
}
|
||||
|
||||
/// Given that we are deriving a trait `DerivedTrait` for a type like:
|
||||
///
|
||||
/// ```ignore (only-for-syntax-highlight)
|
||||
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
|
||||
/// a: A,
|
||||
/// b: B::Item,
|
||||
/// b1: <B as DeclaredTrait>::Item,
|
||||
/// c1: <C as WhereTrait>::Item,
|
||||
/// c2: Option<<C as WhereTrait>::Item>,
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// create an impl like:
|
||||
///
|
||||
/// ```ignore (only-for-syntax-highlight)
|
||||
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
|
||||
/// C: WhereTrait,
|
||||
/// A: DerivedTrait + B1 + ... + BN,
|
||||
/// B: DerivedTrait + B1 + ... + BN,
|
||||
/// C: DerivedTrait + B1 + ... + BN,
|
||||
/// B::Item: DerivedTrait + B1 + ... + BN,
|
||||
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
|
||||
/// ...
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
|
||||
/// therefore does not get bound by the derived trait.
|
||||
fn expand_simple_derive(
|
||||
tt: &tt::Subtree,
|
||||
trait_path: tt::Subtree,
|
||||
trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let info = match parse_adt(tt) {
|
||||
Ok(info) => info,
|
||||
Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
|
||||
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
|
||||
};
|
||||
let trait_body = trait_body(&info);
|
||||
let mut where_block = vec![];
|
||||
let (params, args): (Vec<_>, Vec<_>) = info
|
||||
.param_types
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, param_ty)| {
|
||||
let ident = tt::Leaf::Ident(tt::Ident {
|
||||
span: tt::TokenId::unspecified(),
|
||||
text: format!("T{idx}").into(),
|
||||
});
|
||||
.map(|(ident, param_ty, bound)| {
|
||||
let ident_ = ident.clone();
|
||||
if let Some(b) = bound {
|
||||
let ident = ident.clone();
|
||||
where_block.push(quote! { #ident : #b , });
|
||||
}
|
||||
if let Some(ty) = param_ty {
|
||||
(quote! { const #ident : #ty , }, quote! { #ident_ , })
|
||||
} else {
|
||||
|
@ -134,9 +364,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
|
|||
}
|
||||
})
|
||||
.unzip();
|
||||
|
||||
where_block.extend(info.associated_types.iter().map(|x| {
|
||||
let x = x.clone();
|
||||
let bound = trait_path.clone();
|
||||
quote! { #x : #bound , }
|
||||
}));
|
||||
|
||||
let name = info.name;
|
||||
let expanded = quote! {
|
||||
impl < ##params > #trait_path for #name < ##args > {}
|
||||
impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body }
|
||||
};
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
@ -163,7 +400,7 @@ fn copy_expand(
|
|||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::marker::Copy })
|
||||
expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {})
|
||||
}
|
||||
|
||||
fn clone_expand(
|
||||
|
@ -172,7 +409,63 @@ fn clone_expand(
|
|||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::clone::Clone })
|
||||
expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
let star = tt::Punct {
|
||||
char: '*',
|
||||
spacing: ::tt::Spacing::Alone,
|
||||
span: tt::TokenId::unspecified(),
|
||||
};
|
||||
return quote! {
|
||||
fn clone(&self) -> Self {
|
||||
#star self
|
||||
}
|
||||
};
|
||||
}
|
||||
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
|
||||
let star = tt::Punct {
|
||||
char: '*',
|
||||
spacing: ::tt::Spacing::Alone,
|
||||
span: tt::TokenId::unspecified(),
|
||||
};
|
||||
return quote! {
|
||||
fn clone(&self) -> Self {
|
||||
match #star self {}
|
||||
}
|
||||
};
|
||||
}
|
||||
let name = &adt.name;
|
||||
let patterns = adt.shape.as_pattern(name);
|
||||
let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() });
|
||||
let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
#pat #fat_arrow #expr,
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
##arms
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// This function exists since `quote! { => }` doesn't work.
|
||||
fn fat_arrow() -> ::tt::Subtree<TokenId> {
|
||||
let eq =
|
||||
tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
|
||||
quote! { #eq> }
|
||||
}
|
||||
|
||||
/// This function exists since `quote! { && }` doesn't work.
|
||||
fn and_and() -> ::tt::Subtree<TokenId> {
|
||||
let and =
|
||||
tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
|
||||
quote! { #and& }
|
||||
}
|
||||
|
||||
fn default_expand(
|
||||
|
@ -180,8 +473,38 @@ fn default_expand(
|
|||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::default::Default })
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| {
|
||||
let body = match &adt.shape {
|
||||
AdtShape::Struct(fields) => {
|
||||
let name = &adt.name;
|
||||
fields
|
||||
.as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default()))
|
||||
}
|
||||
AdtShape::Enum { default_variant, variants } => {
|
||||
if let Some(d) = default_variant {
|
||||
let (name, fields) = &variants[*d];
|
||||
let adt_name = &adt.name;
|
||||
fields.as_pattern_map(
|
||||
quote!(#adt_name :: #name),
|
||||
|_| quote!(#krate::default::Default::default()),
|
||||
)
|
||||
} else {
|
||||
// FIXME: Return expand error here
|
||||
quote!()
|
||||
}
|
||||
}
|
||||
AdtShape::Union => {
|
||||
// FIXME: Return expand error here
|
||||
quote!()
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
fn default() -> Self {
|
||||
#body
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn debug_expand(
|
||||
|
@ -189,8 +512,79 @@ fn debug_expand(
|
|||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::fmt::Debug })
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| {
|
||||
let for_variant = |name: String, v: &VariantShape| match v {
|
||||
VariantShape::Struct(fields) => {
|
||||
let for_fields = fields.iter().map(|x| {
|
||||
let x_string = x.to_string();
|
||||
quote! {
|
||||
.field(#x_string, & #x)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
f.debug_struct(#name) ##for_fields .finish()
|
||||
}
|
||||
}
|
||||
VariantShape::Tuple(n) => {
|
||||
let for_fields = tuple_field_iterator(*n).map(|x| {
|
||||
quote! {
|
||||
.field( & #x)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
f.debug_tuple(#name) ##for_fields .finish()
|
||||
}
|
||||
}
|
||||
VariantShape::Unit => quote! {
|
||||
f.write_str(#name)
|
||||
},
|
||||
};
|
||||
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
|
||||
let star = tt::Punct {
|
||||
char: '*',
|
||||
spacing: ::tt::Spacing::Alone,
|
||||
span: tt::TokenId::unspecified(),
|
||||
};
|
||||
return quote! {
|
||||
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
|
||||
match #star self {}
|
||||
}
|
||||
};
|
||||
}
|
||||
let arms = match &adt.shape {
|
||||
AdtShape::Struct(fields) => {
|
||||
let fat_arrow = fat_arrow();
|
||||
let name = &adt.name;
|
||||
let pat = fields.as_pattern(quote!(#name));
|
||||
let expr = for_variant(name.to_string(), fields);
|
||||
vec![quote! { #pat #fat_arrow #expr }]
|
||||
}
|
||||
AdtShape::Enum { variants, .. } => variants
|
||||
.iter()
|
||||
.map(|(name, v)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
let adt_name = &adt.name;
|
||||
let pat = v.as_pattern(quote!(#adt_name :: #name));
|
||||
let expr = for_variant(name.to_string(), v);
|
||||
quote! {
|
||||
#pat #fat_arrow #expr ,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
AdtShape::Union => {
|
||||
// FIXME: Return expand error here
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
|
||||
match self {
|
||||
##arms
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn hash_expand(
|
||||
|
@ -198,8 +592,47 @@ fn hash_expand(
|
|||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::hash::Hash })
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote! {};
|
||||
}
|
||||
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
|
||||
let star = tt::Punct {
|
||||
char: '*',
|
||||
spacing: ::tt::Spacing::Alone,
|
||||
span: tt::TokenId::unspecified(),
|
||||
};
|
||||
return quote! {
|
||||
fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
|
||||
match #star self {}
|
||||
}
|
||||
};
|
||||
}
|
||||
let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
|
||||
|(pat, names)| {
|
||||
let expr = {
|
||||
let it = names.iter().map(|x| quote! { #x . hash(state); });
|
||||
quote! { {
|
||||
##it
|
||||
} }
|
||||
};
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
#pat #fat_arrow #expr ,
|
||||
}
|
||||
},
|
||||
);
|
||||
quote! {
|
||||
fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
|
||||
#krate::mem::discriminant(self).hash(state);
|
||||
match self {
|
||||
##arms
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn eq_expand(
|
||||
|
@ -208,7 +641,7 @@ fn eq_expand(
|
|||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::Eq })
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {})
|
||||
}
|
||||
|
||||
fn partial_eq_expand(
|
||||
|
@ -217,7 +650,65 @@ fn partial_eq_expand(
|
|||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote! {};
|
||||
}
|
||||
let name = &adt.name;
|
||||
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
|(pat1, pat2, names)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
let body = match &*names {
|
||||
[] => {
|
||||
quote!(true)
|
||||
}
|
||||
[first, rest @ ..] => {
|
||||
let rest = rest.iter().map(|x| {
|
||||
let t1 = Ident::new(format!("{}_self", x.text), x.span);
|
||||
let t2 = Ident::new(format!("{}_other", x.text), x.span);
|
||||
let and_and = and_and();
|
||||
quote!(#and_and #t1 .eq( #t2 ))
|
||||
});
|
||||
let first = {
|
||||
let t1 = Ident::new(format!("{}_self", first.text), first.span);
|
||||
let t2 = Ident::new(format!("{}_other", first.text), first.span);
|
||||
quote!(#t1 .eq( #t2 ))
|
||||
};
|
||||
quote!(#first ##rest)
|
||||
}
|
||||
};
|
||||
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
},
|
||||
);
|
||||
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn self_and_other_patterns(
|
||||
adt: &BasicAdtInfo,
|
||||
name: &tt::Ident,
|
||||
) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
|
||||
let self_patterns = adt.shape.as_pattern_map(name, |x| {
|
||||
let t = Ident::new(format!("{}_self", x.text), x.span);
|
||||
quote!(#t)
|
||||
});
|
||||
let other_patterns = adt.shape.as_pattern_map(name, |x| {
|
||||
let t = Ident::new(format!("{}_other", x.text), x.span);
|
||||
quote!(#t)
|
||||
});
|
||||
(self_patterns, other_patterns)
|
||||
}
|
||||
|
||||
fn ord_expand(
|
||||
|
@ -225,8 +716,63 @@ fn ord_expand(
|
|||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::Ord })
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| {
|
||||
fn compare(
|
||||
krate: &tt::TokenTree,
|
||||
left: tt::Subtree,
|
||||
right: tt::Subtree,
|
||||
rest: tt::Subtree,
|
||||
) -> tt::Subtree {
|
||||
let fat_arrow1 = fat_arrow();
|
||||
let fat_arrow2 = fat_arrow();
|
||||
quote! {
|
||||
match #left.cmp(&#right) {
|
||||
#krate::cmp::Ordering::Equal #fat_arrow1 {
|
||||
#rest
|
||||
}
|
||||
c #fat_arrow2 return c,
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote!();
|
||||
}
|
||||
let left = quote!(#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(#krate::intrinsics::discriminant_value(other));
|
||||
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
|(pat1, pat2, fields)| {
|
||||
let mut body = quote!(#krate::cmp::Ordering::Equal);
|
||||
for f in fields.into_iter().rev() {
|
||||
let t1 = Ident::new(format!("{}_self", f.text), f.span);
|
||||
let t2 = Ident::new(format!("{}_other", f.text), f.span);
|
||||
body = compare(krate, quote!(#t1), quote!(#t2), body);
|
||||
}
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
},
|
||||
);
|
||||
let fat_arrow = fat_arrow();
|
||||
let body = compare(
|
||||
krate,
|
||||
left,
|
||||
right,
|
||||
quote! {
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow #krate::cmp::Ordering::Equal
|
||||
}
|
||||
},
|
||||
);
|
||||
quote! {
|
||||
fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
|
||||
#body
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn partial_ord_expand(
|
||||
|
@ -234,6 +780,61 @@ fn partial_ord_expand(
|
|||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| {
|
||||
fn compare(
|
||||
krate: &tt::TokenTree,
|
||||
left: tt::Subtree,
|
||||
right: tt::Subtree,
|
||||
rest: tt::Subtree,
|
||||
) -> tt::Subtree {
|
||||
let fat_arrow1 = fat_arrow();
|
||||
let fat_arrow2 = fat_arrow();
|
||||
quote! {
|
||||
match #left.partial_cmp(&#right) {
|
||||
#krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 {
|
||||
#rest
|
||||
}
|
||||
c #fat_arrow2 return c,
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote!();
|
||||
}
|
||||
let left = quote!(#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(#krate::intrinsics::discriminant_value(other));
|
||||
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
|(pat1, pat2, fields)| {
|
||||
let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
|
||||
for f in fields.into_iter().rev() {
|
||||
let t1 = Ident::new(format!("{}_self", f.text), f.span);
|
||||
let t2 = Ident::new(format!("{}_other", f.text), f.span);
|
||||
body = compare(krate, quote!(#t1), quote!(#t2), body);
|
||||
}
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
|
||||
},
|
||||
);
|
||||
let fat_arrow = fat_arrow();
|
||||
let body = compare(
|
||||
krate,
|
||||
left,
|
||||
right,
|
||||
quote! {
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
|
||||
}
|
||||
},
|
||||
);
|
||||
quote! {
|
||||
fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
|
||||
#body
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
//! Builtin macro
|
||||
|
||||
use std::mem;
|
||||
|
||||
use ::tt::Ident;
|
||||
use base_db::{AnchoredPath, Edition, FileId};
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
|
||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{
|
||||
ast::{self, AstToken},
|
||||
SmolStr,
|
||||
|
@ -67,7 +71,7 @@ macro_rules! register_builtin {
|
|||
pub struct ExpandedEager {
|
||||
pub(crate) subtree: tt::Subtree,
|
||||
/// The included file ID of the include macro.
|
||||
pub(crate) included_file: Option<FileId>,
|
||||
pub(crate) included_file: Option<(FileId, TokenMap)>,
|
||||
}
|
||||
|
||||
impl ExpandedEager {
|
||||
|
@ -90,11 +94,6 @@ register_builtin! {
|
|||
(module_path, ModulePath) => module_path_expand,
|
||||
(assert, Assert) => assert_expand,
|
||||
(stringify, Stringify) => stringify_expand,
|
||||
(format_args, FormatArgs) => format_args_expand,
|
||||
(const_format_args, ConstFormatArgs) => format_args_expand,
|
||||
// format_args_nl only differs in that it adds a newline in the end,
|
||||
// so we use the same stub expansion for now
|
||||
(format_args_nl, FormatArgsNl) => format_args_expand,
|
||||
(llvm_asm, LlvmAsm) => asm_expand,
|
||||
(asm, Asm) => asm_expand,
|
||||
(global_asm, GlobalAsm) => global_asm_expand,
|
||||
|
@ -106,6 +105,9 @@ register_builtin! {
|
|||
(trace_macros, TraceMacros) => trace_macros_expand,
|
||||
|
||||
EAGER:
|
||||
(format_args, FormatArgs) => format_args_expand,
|
||||
(const_format_args, ConstFormatArgs) => format_args_expand,
|
||||
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
|
||||
(compile_error, CompileError) => compile_error_expand,
|
||||
(concat, Concat) => concat_expand,
|
||||
(concat_idents, ConcatIdents) => concat_idents_expand,
|
||||
|
@ -135,9 +137,8 @@ fn line_expand(
|
|||
_tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// dummy implementation for type-checking purposes
|
||||
let line_num = 0;
|
||||
let expanded = quote! {
|
||||
#line_num
|
||||
0 as u32
|
||||
};
|
||||
|
||||
ExpandResult::ok(expanded)
|
||||
|
@ -179,9 +180,8 @@ fn column_expand(
|
|||
_tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// dummy implementation for type-checking purposes
|
||||
let col_num = 0;
|
||||
let expanded = quote! {
|
||||
#col_num
|
||||
0 as u32
|
||||
};
|
||||
|
||||
ExpandResult::ok(expanded)
|
||||
|
@ -234,45 +234,173 @@ fn file_expand(
|
|||
}
|
||||
|
||||
fn format_args_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<ExpandedEager> {
|
||||
format_args_expand_general(db, id, tt, "")
|
||||
.map(|x| ExpandedEager { subtree: x, included_file: None })
|
||||
}
|
||||
|
||||
fn format_args_nl_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<ExpandedEager> {
|
||||
format_args_expand_general(db, id, tt, "\\n")
|
||||
.map(|x| ExpandedEager { subtree: x, included_file: None })
|
||||
}
|
||||
|
||||
fn format_args_expand_general(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
end_string: &str,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// We expand `format_args!("", a1, a2)` to
|
||||
// ```
|
||||
// $crate::fmt::Arguments::new_v1(&[], &[
|
||||
// $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt),
|
||||
// $crate::fmt::Argument::new(&arg2,$crate::fmt::Display::fmt),
|
||||
// ])
|
||||
// ```,
|
||||
// which is still not really correct, but close enough for now
|
||||
let mut args = parse_exprs_with_sep(tt, ',');
|
||||
let args = parse_exprs_with_sep(tt, ',');
|
||||
|
||||
let expand_error =
|
||||
ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
|
||||
|
||||
if args.is_empty() {
|
||||
return ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
mbe::ExpandError::NoMatchingRule.into(),
|
||||
);
|
||||
return expand_error;
|
||||
}
|
||||
for arg in &mut args {
|
||||
let mut key_args = FxHashMap::default();
|
||||
let mut args = args.into_iter().filter_map(|mut arg| {
|
||||
// Remove `key =`.
|
||||
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
|
||||
{
|
||||
// but not with `==`
|
||||
if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' )
|
||||
if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
|
||||
{
|
||||
arg.token_trees.drain(..2);
|
||||
let key = arg.token_trees.drain(..2).next().unwrap();
|
||||
key_args.insert(key.to_string(), arg);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _format_string = args.remove(0);
|
||||
let arg_tts = args.into_iter().flat_map(|arg| {
|
||||
quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
|
||||
}.token_trees);
|
||||
let expanded = quote! {
|
||||
#DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
||||
Some(arg)
|
||||
}).collect::<Vec<_>>().into_iter();
|
||||
// ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
|
||||
let format_subtree = args.next().unwrap();
|
||||
let format_string = (|| {
|
||||
let token_tree = format_subtree.token_trees.get(0)?;
|
||||
match token_tree {
|
||||
tt::TokenTree::Leaf(l) => match l {
|
||||
tt::Leaf::Literal(l) => {
|
||||
if let Some(mut text) = l.text.strip_prefix('r') {
|
||||
let mut raw_sharps = String::new();
|
||||
while let Some(t) = text.strip_prefix('#') {
|
||||
text = t;
|
||||
raw_sharps.push('#');
|
||||
}
|
||||
text =
|
||||
text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
|
||||
Some((text, l.span, Some(raw_sharps)))
|
||||
} else {
|
||||
let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
|
||||
let span = l.span;
|
||||
Some((text, span, None))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
tt::TokenTree::Subtree(_) => None,
|
||||
}
|
||||
})();
|
||||
let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
|
||||
return expand_error;
|
||||
};
|
||||
ExpandResult::ok(expanded)
|
||||
let mut format_iter = format_string.chars().peekable();
|
||||
let mut parts = vec![];
|
||||
let mut last_part = String::new();
|
||||
let mut arg_tts = vec![];
|
||||
let mut err = None;
|
||||
while let Some(c) = format_iter.next() {
|
||||
// Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
|
||||
match c {
|
||||
'{' => {
|
||||
if format_iter.peek() == Some(&'{') {
|
||||
format_iter.next();
|
||||
last_part.push('{');
|
||||
continue;
|
||||
}
|
||||
let mut argument = String::new();
|
||||
while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
|
||||
argument.push(match format_iter.next() {
|
||||
Some(c) => c,
|
||||
None => return expand_error,
|
||||
});
|
||||
}
|
||||
let format_spec = match format_iter.next().unwrap() {
|
||||
'}' => "".to_owned(),
|
||||
':' => {
|
||||
let mut s = String::new();
|
||||
while let Some(c) = format_iter.next() {
|
||||
if c == '}' {
|
||||
break;
|
||||
}
|
||||
s.push(c);
|
||||
}
|
||||
s
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
parts.push(mem::take(&mut last_part));
|
||||
let arg_tree = if argument.is_empty() {
|
||||
match args.next() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
err = Some(mbe::ExpandError::NoMatchingRule.into());
|
||||
tt::Subtree::empty()
|
||||
}
|
||||
}
|
||||
} else if let Some(tree) = key_args.get(&argument) {
|
||||
tree.clone()
|
||||
} else {
|
||||
// FIXME: we should pick the related substring of the `_format_string_span` as the span. You
|
||||
// can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
|
||||
let ident = Ident::new(argument, tt::TokenId::unspecified());
|
||||
quote!(#ident)
|
||||
};
|
||||
let formatter = match &*format_spec {
|
||||
"?" => quote!(::core::fmt::Debug::fmt),
|
||||
"" => quote!(::core::fmt::Display::fmt),
|
||||
_ => {
|
||||
// FIXME: implement the rest and return expand error here
|
||||
quote!(::core::fmt::Display::fmt)
|
||||
}
|
||||
};
|
||||
arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), });
|
||||
}
|
||||
'}' => {
|
||||
if format_iter.peek() == Some(&'}') {
|
||||
format_iter.next();
|
||||
last_part.push('}');
|
||||
} else {
|
||||
return expand_error;
|
||||
}
|
||||
}
|
||||
_ => last_part.push(c),
|
||||
}
|
||||
}
|
||||
last_part += end_string;
|
||||
if !last_part.is_empty() {
|
||||
parts.push(last_part);
|
||||
}
|
||||
let part_tts = parts.into_iter().map(|x| {
|
||||
let text = if let Some(raw) = &raw_sharps {
|
||||
format!("r{raw}\"{}\"{raw}", x).into()
|
||||
} else {
|
||||
format!("\"{}\"", x).into()
|
||||
};
|
||||
let l = tt::Literal { span: tt::TokenId::unspecified(), text };
|
||||
quote!(#l ,)
|
||||
});
|
||||
let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
|
||||
let expanded = quote! {
|
||||
::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
|
||||
};
|
||||
ExpandResult { value: expanded, err }
|
||||
}
|
||||
|
||||
fn asm_expand(
|
||||
|
@ -566,16 +694,16 @@ fn include_expand(
|
|||
let path = parse_string(tt)?;
|
||||
let file_id = relative_file(db, arg_id, &path, false)?;
|
||||
|
||||
let subtree =
|
||||
parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0;
|
||||
Ok((subtree, file_id))
|
||||
let (subtree, map) =
|
||||
parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
|
||||
Ok((subtree, map, file_id))
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok((subtree, file_id)) => {
|
||||
ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) })
|
||||
Ok((subtree, map, file_id)) => {
|
||||
ExpandResult::ok(ExpandedEager { subtree, included_file: Some((file_id, map)) })
|
||||
}
|
||||
Err(e) => ExpandResult::with_err(
|
||||
Err(e) => ExpandResult::new(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
),
|
||||
|
@ -588,7 +716,7 @@ fn include_bytes_expand(
|
|||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<ExpandedEager> {
|
||||
if let Err(e) = parse_string(tt) {
|
||||
return ExpandResult::with_err(
|
||||
return ExpandResult::new(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
);
|
||||
|
@ -613,7 +741,7 @@ fn include_str_expand(
|
|||
let path = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::with_err(
|
||||
return ExpandResult::new(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
)
|
||||
|
@ -650,7 +778,7 @@ fn env_expand(
|
|||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::with_err(
|
||||
return ExpandResult::new(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
)
|
||||
|
@ -686,16 +814,16 @@ fn option_env_expand(
|
|||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
return ExpandResult::with_err(
|
||||
return ExpandResult::new(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
|
||||
let expanded = match get_env_inner(db, arg_id, &key) {
|
||||
None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
|
||||
Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) },
|
||||
None => quote! { ::core::option::Option::None::<&str> },
|
||||
Some(s) => quote! { ::core::option::Option::Some(#s) },
|
||||
};
|
||||
|
||||
ExpandResult::ok(ExpandedEager::new(expanded))
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
//! Defines database & queries for macro expansion.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::{salsa, SourceDatabase};
|
||||
use base_db::{salsa, Edition, SourceDatabase};
|
||||
use either::Either;
|
||||
use limit::Limit;
|
||||
use mbe::syntax_node_to_token_tree;
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs, HasDocComments},
|
||||
AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
|
||||
AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup,
|
||||
hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
|
||||
ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
|
||||
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
|
||||
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
|
||||
builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
|
||||
BuiltinDeriveExpander, BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId,
|
||||
HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
|
||||
ProcMacroExpander,
|
||||
};
|
||||
|
||||
/// Total limit on the number of tokens produced by any macro invocation.
|
||||
|
@ -33,6 +33,8 @@ pub enum TokenExpander {
|
|||
DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap },
|
||||
/// Stuff like `line!` and `file!`.
|
||||
Builtin(BuiltinFnLikeExpander),
|
||||
/// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
|
||||
BuiltinEager(EagerExpander),
|
||||
/// `global_allocator` and such.
|
||||
BuiltinAttr(BuiltinAttrExpander),
|
||||
/// `derive(Copy)` and such.
|
||||
|
@ -51,6 +53,9 @@ impl TokenExpander {
|
|||
match self {
|
||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
|
||||
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
|
||||
TokenExpander::BuiltinEager(it) => {
|
||||
it.expand(db, id, tt).map_err(Into::into).map(|res| res.subtree)
|
||||
}
|
||||
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
|
||||
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
|
||||
TokenExpander::ProcMacro(_) => {
|
||||
|
@ -66,6 +71,7 @@ impl TokenExpander {
|
|||
match self {
|
||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id),
|
||||
TokenExpander::Builtin(..)
|
||||
| TokenExpander::BuiltinEager(..)
|
||||
| TokenExpander::BuiltinAttr(..)
|
||||
| TokenExpander::BuiltinDerive(..)
|
||||
| TokenExpander::ProcMacro(..) => id,
|
||||
|
@ -76,6 +82,7 @@ impl TokenExpander {
|
|||
match self {
|
||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id),
|
||||
TokenExpander::Builtin(..)
|
||||
| TokenExpander::BuiltinEager(..)
|
||||
| TokenExpander::BuiltinAttr(..)
|
||||
| TokenExpander::BuiltinDerive(..)
|
||||
| TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
|
||||
|
@ -90,12 +97,15 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
/// Main public API -- parses a hir file, not caring whether it's a real
|
||||
/// file or a macro expansion.
|
||||
#[salsa::transparent]
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
|
||||
#[salsa::transparent]
|
||||
fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult<Parse<SyntaxNode>>;
|
||||
/// Implementation for the macro case.
|
||||
// This query is LRU cached
|
||||
fn parse_macro_expansion(
|
||||
&self,
|
||||
macro_file: MacroFile,
|
||||
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
|
||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
|
||||
|
||||
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
||||
/// reason why we use salsa at all.
|
||||
|
@ -119,15 +129,19 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
/// just fetches procedural ones.
|
||||
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
|
||||
|
||||
/// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
|
||||
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
|
||||
/// Expand macro call to a token tree.
|
||||
// This query is LRU cached
|
||||
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
|
||||
/// 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!
|
||||
/// non-determinism breaks salsa in a very, very, very bad way.
|
||||
/// @edwin0cheng heroically debugged this once!
|
||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
|
||||
/// Firewall query that returns the error from the `macro_expand` query.
|
||||
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
|
||||
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
|
||||
fn parse_macro_expansion_error(
|
||||
&self,
|
||||
macro_call: MacroCallId,
|
||||
) -> ExpandResult<Box<[SyntaxError]>>;
|
||||
|
||||
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
|
||||
}
|
||||
|
@ -159,8 +173,8 @@ pub fn expand_speculative(
|
|||
);
|
||||
|
||||
let (attr_arg, token_id) = match loc.kind {
|
||||
MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => {
|
||||
let attr = if is_derive {
|
||||
MacroCallKind::Attr { invoc_attr_index, .. } => {
|
||||
let attr = if loc.def.is_attribute_derive() {
|
||||
// for pseudo-derive expansion we actually pass the attribute itself only
|
||||
ast::Attr::cast(speculative_args.clone())
|
||||
} else {
|
||||
|
@ -236,17 +250,26 @@ pub fn expand_speculative(
|
|||
}
|
||||
|
||||
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
|
||||
let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default();
|
||||
Arc::new(map)
|
||||
Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
|
||||
}
|
||||
|
||||
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
|
||||
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
|
||||
match file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
|
||||
HirFileIdRepr::FileId(file_id) => db.parse(file_id).tree().syntax().clone(),
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
// FIXME: Note how we convert from `Parse` to `SyntaxNode` here,
|
||||
// forgetting about parse errors.
|
||||
db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
|
||||
db.parse_macro_expansion(macro_file).value.0.syntax_node()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_or_expand_with_err(
|
||||
db: &dyn ExpandDatabase,
|
||||
file_id: HirFileId,
|
||||
) -> ExpandResult<Parse<SyntaxNode>> {
|
||||
match file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()),
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
db.parse_macro_expansion(macro_file).map(|(it, _)| it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,35 +277,34 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<Syntax
|
|||
fn parse_macro_expansion(
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_file: MacroFile,
|
||||
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
|
||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
|
||||
let _p = profile::span("parse_macro_expansion");
|
||||
let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id);
|
||||
let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
|
||||
|
||||
if let Some(err) = &err {
|
||||
// Note:
|
||||
// The final goal we would like to make all parse_macro success,
|
||||
// such that the following log will not call anyway.
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let node = loc.kind.to_node(db);
|
||||
if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
// Note:
|
||||
// The final goal we would like to make all parse_macro success,
|
||||
// such that the following log will not call anyway.
|
||||
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let node = loc.to_node(db);
|
||||
|
||||
// collect parent information for warning log
|
||||
let parents =
|
||||
std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db))
|
||||
.map(|n| format!("{:#}", n.value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
// collect parent information for warning log
|
||||
let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
|
||||
it.file_id.call_node(db)
|
||||
})
|
||||
.map(|n| format!("{:#}", n.value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
tracing::debug!(
|
||||
"fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
|
||||
err,
|
||||
node.value,
|
||||
parents
|
||||
);
|
||||
tracing::debug!(
|
||||
"fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
|
||||
err,
|
||||
node.value,
|
||||
parents
|
||||
);
|
||||
}
|
||||
}
|
||||
let tt = match value {
|
||||
Some(tt) => tt,
|
||||
None => return ExpandResult { value: None, err },
|
||||
};
|
||||
|
||||
let expand_to = macro_expand_to(db, macro_file.macro_call_id);
|
||||
|
||||
|
@ -291,7 +313,7 @@ fn parse_macro_expansion(
|
|||
|
||||
let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to);
|
||||
|
||||
ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err }
|
||||
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
|
||||
}
|
||||
|
||||
fn macro_arg(
|
||||
|
@ -339,7 +361,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
|
|||
.map(|it| it.syntax().clone())
|
||||
.collect()
|
||||
}
|
||||
MacroCallKind::Attr { is_derive: true, .. } => return None,
|
||||
MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
|
||||
MacroCallKind::Attr { invoc_attr_index, .. } => {
|
||||
cov_mark::hit!(attribute_macro_attr_censoring);
|
||||
ast::Item::cast(node.clone())?
|
||||
|
@ -385,13 +407,14 @@ fn macro_def(
|
|||
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
|
||||
match id.kind {
|
||||
MacroDefKind::Declarative(ast_id) => {
|
||||
let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021;
|
||||
let (mac, def_site_token_map) = match ast_id.to_node(db) {
|
||||
ast::Macro::MacroRules(macro_rules) => {
|
||||
let arg = macro_rules
|
||||
.token_tree()
|
||||
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
|
||||
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
|
||||
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?;
|
||||
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?;
|
||||
(mac, def_site_token_map)
|
||||
}
|
||||
ast::Macro::MacroDef(macro_def) => {
|
||||
|
@ -399,7 +422,7 @@ fn macro_def(
|
|||
.body()
|
||||
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
|
||||
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
|
||||
let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?;
|
||||
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?;
|
||||
(mac, def_site_token_map)
|
||||
}
|
||||
};
|
||||
|
@ -412,82 +435,96 @@ fn macro_def(
|
|||
MacroDefKind::BuiltInDerive(expander, _) => {
|
||||
Ok(Arc::new(TokenExpander::BuiltinDerive(expander)))
|
||||
}
|
||||
MacroDefKind::BuiltInEager(..) => {
|
||||
// FIXME: Return a random error here just to make the types align.
|
||||
// This obviously should do something real instead.
|
||||
Err(mbe::ParseError::UnexpectedToken("unexpected eager macro".into()))
|
||||
MacroDefKind::BuiltInEager(expander, ..) => {
|
||||
Ok(Arc::new(TokenExpander::BuiltinEager(expander)))
|
||||
}
|
||||
MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))),
|
||||
}
|
||||
}
|
||||
|
||||
fn macro_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
) -> ExpandResult<Option<Arc<tt::Subtree>>> {
|
||||
fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
|
||||
let _p = profile::span("macro_expand");
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
if let Some(eager) = &loc.eager {
|
||||
return ExpandResult {
|
||||
value: Some(eager.arg_or_expansion.clone()),
|
||||
// FIXME: There could be errors here!
|
||||
err: None,
|
||||
};
|
||||
return ExpandResult { value: eager.arg_or_expansion.clone(), err: eager.error.clone() };
|
||||
}
|
||||
|
||||
let macro_arg = match db.macro_arg(id) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
return ExpandResult::only_err(ExpandError::Other(
|
||||
"Failed to lower macro args to token tree".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let expander = match db.macro_def(loc.def) {
|
||||
Ok(it) => it,
|
||||
// FIXME: This is weird -- we effectively report macro *definition*
|
||||
// errors lazily, when we try to expand the macro. Instead, they should
|
||||
// be reported at the definition site (when we construct a def map).
|
||||
// be reported at the definition site when we construct a def map.
|
||||
// (Note we do report them also at the definition site in the late diagnostic pass)
|
||||
Err(err) => {
|
||||
return ExpandResult::only_err(ExpandError::Other(
|
||||
format!("invalid macro definition: {err}").into(),
|
||||
))
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: vec![],
|
||||
}),
|
||||
err: Some(ExpandError::Other(format!("invalid macro definition: {err}").into())),
|
||||
}
|
||||
}
|
||||
};
|
||||
let Some(macro_arg) = db.macro_arg(id) else {
|
||||
return ExpandResult {
|
||||
value: Arc::new(
|
||||
tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: Vec::new(),
|
||||
},
|
||||
),
|
||||
err: Some(ExpandError::Other(
|
||||
"invalid token tree"
|
||||
.into(),
|
||||
)),
|
||||
};
|
||||
};
|
||||
let ExpandResult { value: mut tt, err } = expander.expand(db, id, ¯o_arg.0);
|
||||
// Set a hard limit for the expanded tt
|
||||
let count = tt.count();
|
||||
if TOKEN_LIMIT.check(count).is_err() {
|
||||
return ExpandResult::only_err(ExpandError::Other(
|
||||
format!(
|
||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||
count,
|
||||
TOKEN_LIMIT.inner(),
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: vec![],
|
||||
}),
|
||||
err: Some(ExpandError::Other(
|
||||
format!(
|
||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||
count,
|
||||
TOKEN_LIMIT.inner(),
|
||||
)
|
||||
.into(),
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fixup::reverse_fixups(&mut tt, ¯o_arg.1, ¯o_arg.2);
|
||||
|
||||
ExpandResult { value: Some(Arc::new(tt)), err }
|
||||
ExpandResult { value: Arc::new(tt), err }
|
||||
}
|
||||
|
||||
fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
|
||||
db.macro_expand(macro_call).err
|
||||
fn parse_macro_expansion_error(
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> ExpandResult<Box<[SyntaxError]>> {
|
||||
db.parse_macro_expansion(MacroFile { macro_call_id })
|
||||
.map(|it| it.0.errors().to_vec().into_boxed_slice())
|
||||
}
|
||||
|
||||
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||
let macro_arg = match db.macro_arg(id) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
return ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other("No arguments for proc-macro".into()),
|
||||
)
|
||||
}
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let Some(macro_arg) = db.macro_arg(id) else {
|
||||
return ExpandResult {
|
||||
value: tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: Vec::new(),
|
||||
},
|
||||
err: Some(ExpandError::Other(
|
||||
"invalid token tree"
|
||||
.into(),
|
||||
)),
|
||||
};
|
||||
};
|
||||
|
||||
let expander = match loc.def.kind {
|
||||
|
@ -512,8 +549,7 @@ fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<HygieneFram
|
|||
}
|
||||
|
||||
fn macro_expand_to(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandTo {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||
loc.kind.expand_to()
|
||||
db.lookup_intern_macro_call(id).expand_to()
|
||||
}
|
||||
|
||||
fn token_tree_to_syntax_node(
|
||||
|
|
|
@ -18,10 +18,9 @@
|
|||
//!
|
||||
//!
|
||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use syntax::{ted, SyntaxNode};
|
||||
use syntax::{ted, Parse, SyntaxNode};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
ast::{self, AstNode},
|
||||
|
@ -32,77 +31,16 @@ use crate::{
|
|||
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorEmitted {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
pub trait ErrorSink {
|
||||
fn emit(&mut self, err: ExpandError);
|
||||
|
||||
fn option<T>(
|
||||
&mut self,
|
||||
opt: Option<T>,
|
||||
error: impl FnOnce() -> ExpandError,
|
||||
) -> Result<T, ErrorEmitted> {
|
||||
match opt {
|
||||
Some(it) => Ok(it),
|
||||
None => {
|
||||
self.emit(error());
|
||||
Err(ErrorEmitted { _private: () })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn option_with<T>(
|
||||
&mut self,
|
||||
opt: impl FnOnce() -> Option<T>,
|
||||
error: impl FnOnce() -> ExpandError,
|
||||
) -> Result<T, ErrorEmitted> {
|
||||
self.option(opt(), error)
|
||||
}
|
||||
|
||||
fn result<T>(&mut self, res: Result<T, ExpandError>) -> Result<T, ErrorEmitted> {
|
||||
match res {
|
||||
Ok(it) => Ok(it),
|
||||
Err(e) => {
|
||||
self.emit(e);
|
||||
Err(ErrorEmitted { _private: () })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
|
||||
match (res.value, res.err) {
|
||||
(None, Some(err)) => {
|
||||
self.emit(err);
|
||||
Err(ErrorEmitted { _private: () })
|
||||
}
|
||||
(Some(value), opt_err) => {
|
||||
if let Some(err) = opt_err {
|
||||
self.emit(err);
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
(None, None) => unreachable!("`ExpandResult` without value or error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorSink for &'_ mut dyn FnMut(ExpandError) {
|
||||
fn emit(&mut self, err: ExpandError) {
|
||||
self(err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_eager_macro(
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
def: MacroDefId,
|
||||
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
diagnostic_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
let MacroDefKind::BuiltInEager(eager, _) = def.kind else {
|
||||
panic!("called `expand_eager_macro` on non-eager macro def {def:?}")
|
||||
};
|
||||
let hygiene = Hygiene::new(db, macro_call.file_id);
|
||||
let parsed_args = macro_call
|
||||
.value
|
||||
|
@ -115,60 +53,54 @@ pub fn expand_eager_macro(
|
|||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
|
||||
// Note:
|
||||
// When `lazy_expand` is called, its *parent* file must be already exists.
|
||||
// Here we store an eager macro id for the argument expanded subtree here
|
||||
// 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 = db.intern_macro_call(MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(EagerCallInfo {
|
||||
eager: Some(Box::new(EagerCallInfo {
|
||||
arg_or_expansion: Arc::new(parsed_args.clone()),
|
||||
included_file: None,
|
||||
}),
|
||||
error: None,
|
||||
})),
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
|
||||
});
|
||||
|
||||
let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
|
||||
let result = match eager_macro_recur(
|
||||
let ExpandResult { value, mut err } = eager_macro_recur(
|
||||
db,
|
||||
&hygiene,
|
||||
InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
|
||||
krate,
|
||||
resolver,
|
||||
diagnostic_sink,
|
||||
) {
|
||||
Ok(Ok(it)) => it,
|
||||
Ok(Err(err)) => return Ok(Err(err)),
|
||||
Err(err) => return Err(err),
|
||||
)?;
|
||||
let Some(value ) = value else {
|
||||
return Ok(ExpandResult { value: None, err })
|
||||
};
|
||||
let subtree = {
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(&value).0;
|
||||
subtree.delimiter = crate::tt::Delimiter::unspecified();
|
||||
subtree
|
||||
};
|
||||
let subtree = to_subtree(&result);
|
||||
|
||||
if let MacroDefKind::BuiltInEager(eager, _) = def.kind {
|
||||
let res = eager.expand(db, arg_id, &subtree);
|
||||
if let Some(err) = res.err {
|
||||
diagnostic_sink(err);
|
||||
}
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(EagerCallInfo {
|
||||
arg_or_expansion: Arc::new(res.value.subtree),
|
||||
included_file: res.value.included_file,
|
||||
}),
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
|
||||
};
|
||||
|
||||
Ok(Ok(db.intern_macro_call(loc)))
|
||||
} else {
|
||||
panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
|
||||
let res = eager.expand(db, arg_id, &subtree);
|
||||
if err.is_none() {
|
||||
err = res.err;
|
||||
}
|
||||
}
|
||||
|
||||
fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(node).0;
|
||||
subtree.delimiter = crate::tt::Delimiter::unspecified();
|
||||
subtree
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(Box::new(EagerCallInfo {
|
||||
arg_or_expansion: Arc::new(res.value.subtree),
|
||||
included_file: res.value.included_file,
|
||||
error: err.clone(),
|
||||
})),
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
|
||||
};
|
||||
|
||||
Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
|
||||
}
|
||||
|
||||
fn lazy_expand(
|
||||
|
@ -176,7 +108,7 @@ fn lazy_expand(
|
|||
def: &MacroDefId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
krate: CrateId,
|
||||
) -> ExpandResult<Option<InFile<SyntaxNode>>> {
|
||||
) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
|
||||
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
||||
|
||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
|
@ -186,10 +118,8 @@ fn lazy_expand(
|
|||
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
|
||||
);
|
||||
|
||||
let err = db.macro_expand_error(id);
|
||||
let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
|
||||
|
||||
ExpandResult { value, err }
|
||||
let file_id = id.as_file();
|
||||
db.parse_or_expand_with_err(file_id).map(|parse| InFile::new(file_id, parse))
|
||||
}
|
||||
|
||||
fn eager_macro_recur(
|
||||
|
@ -198,23 +128,25 @@ fn eager_macro_recur(
|
|||
curr: InFile<SyntaxNode>,
|
||||
krate: CrateId,
|
||||
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
mut diagnostic_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
|
||||
let original = curr.value.clone_for_update();
|
||||
|
||||
let children = original.descendants().filter_map(ast::MacroCall::cast);
|
||||
let mut replacements = Vec::new();
|
||||
|
||||
// Note: We only report a single error inside of eager expansions
|
||||
let mut error = None;
|
||||
|
||||
// Collect replacement
|
||||
for child in children {
|
||||
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
|
||||
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
|
||||
None => {
|
||||
diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
|
||||
error = Some(ExpandError::Other("malformed macro invocation".into()));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let insert = match def.kind {
|
||||
let ExpandResult { value, err } = match def.kind {
|
||||
MacroDefKind::BuiltInEager(..) => {
|
||||
let id = match expand_eager_macro(
|
||||
db,
|
||||
|
@ -222,45 +154,49 @@ fn eager_macro_recur(
|
|||
curr.with_value(child.clone()),
|
||||
def,
|
||||
macro_resolver,
|
||||
diagnostic_sink,
|
||||
) {
|
||||
Ok(Ok(it)) => it,
|
||||
Ok(Err(err)) => return Ok(Err(err)),
|
||||
Ok(it) => it,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
db.parse_or_expand(id.as_file())
|
||||
.expect("successful macro expansion should be parseable")
|
||||
.clone_for_update()
|
||||
id.map(|call| {
|
||||
call.map(|call| db.parse_or_expand(call.as_file()).clone_for_update())
|
||||
})
|
||||
}
|
||||
MacroDefKind::Declarative(_)
|
||||
| MacroDefKind::BuiltIn(..)
|
||||
| MacroDefKind::BuiltInAttr(..)
|
||||
| MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(..) => {
|
||||
let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
|
||||
let val = match diagnostic_sink.expand_result_option(res) {
|
||||
Ok(it) => it,
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
let ExpandResult { value, err } =
|
||||
lazy_expand(db, &def, curr.with_value(child.clone()), krate);
|
||||
|
||||
// replace macro inside
|
||||
let hygiene = Hygiene::new(db, val.file_id);
|
||||
match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) {
|
||||
Ok(Ok(it)) => it,
|
||||
Ok(Err(err)) => return Ok(Err(err)),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
let hygiene = Hygiene::new(db, value.file_id);
|
||||
let ExpandResult { value, err: error } = eager_macro_recur(
|
||||
db,
|
||||
&hygiene,
|
||||
// FIXME: We discard parse errors here
|
||||
value.map(|it| it.syntax_node()),
|
||||
krate,
|
||||
macro_resolver,
|
||||
)?;
|
||||
let err = if err.is_none() { error } else { err };
|
||||
ExpandResult { value, err }
|
||||
}
|
||||
};
|
||||
|
||||
if err.is_some() {
|
||||
error = err;
|
||||
}
|
||||
// check if the whole original syntax is replaced
|
||||
if child.syntax() == &original {
|
||||
return Ok(Ok(insert));
|
||||
return Ok(ExpandResult { value, err: error });
|
||||
}
|
||||
|
||||
replacements.push((child, insert));
|
||||
if let Some(insert) = value {
|
||||
replacements.push((child, insert));
|
||||
}
|
||||
}
|
||||
|
||||
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
|
||||
Ok(Ok(original))
|
||||
Ok(ExpandResult { value: Some(original), err: error })
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use tt::token_id::Subtree;
|
|||
/// The result of calculating fixes for a syntax node -- a bunch of changes
|
||||
/// (appending to and replacing nodes), the information that is needed to
|
||||
/// reverse those changes afterwards, and a token map.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct SyntaxFixups {
|
||||
pub(crate) append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||
pub(crate) replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||
|
@ -24,7 +24,7 @@ pub(crate) struct SyntaxFixups {
|
|||
}
|
||||
|
||||
/// This is the information needed to reverse the fixups.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct SyntaxFixupUndoInfo {
|
||||
original: Vec<Subtree>,
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
//!
|
||||
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
|
||||
//! this moment, this is horribly incomplete and handles only `$crate`.
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use db::TokenExpander;
|
||||
use either::Either;
|
||||
|
@ -12,6 +10,7 @@ use syntax::{
|
|||
ast::{self, HasDocComments},
|
||||
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::{self, ExpandDatabase},
|
||||
|
@ -200,8 +199,14 @@ fn make_hygiene_info(
|
|||
});
|
||||
|
||||
let macro_def = db.macro_def(loc.def).ok()?;
|
||||
let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
|
||||
let (_, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
|
||||
Arc::new((
|
||||
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
))
|
||||
});
|
||||
|
||||
Some(HygieneInfo {
|
||||
file: macro_file,
|
||||
|
|
|
@ -20,11 +20,13 @@ pub mod mod_path;
|
|||
pub mod attrs;
|
||||
mod fixup;
|
||||
|
||||
use mbe::TokenMap;
|
||||
pub use mbe::{Origin, ValueResult};
|
||||
|
||||
use ::tt::token_id as tt;
|
||||
use triomphe::Arc;
|
||||
|
||||
use std::{fmt, hash::Hash, iter, sync::Arc};
|
||||
use std::{fmt, hash::Hash, iter};
|
||||
|
||||
use base_db::{
|
||||
impl_intern_key,
|
||||
|
@ -51,11 +53,11 @@ use crate::{
|
|||
|
||||
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum ExpandError {
|
||||
UnresolvedProcMacro(CrateId),
|
||||
Mbe(mbe::ExpandError),
|
||||
RecursionOverflowPosioned,
|
||||
RecursionOverflowPoisoned,
|
||||
Other(Box<str>),
|
||||
}
|
||||
|
||||
|
@ -70,7 +72,7 @@ impl fmt::Display for ExpandError {
|
|||
match self {
|
||||
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
|
||||
ExpandError::Mbe(it) => it.fmt(f),
|
||||
ExpandError::RecursionOverflowPosioned => {
|
||||
ExpandError::RecursionOverflowPoisoned => {
|
||||
f.write_str("overflow expanding the original macro")
|
||||
}
|
||||
ExpandError::Other(it) => f.write_str(it),
|
||||
|
@ -113,7 +115,7 @@ impl_intern_key!(MacroCallId);
|
|||
pub struct MacroCallLoc {
|
||||
pub def: MacroDefId,
|
||||
pub(crate) krate: CrateId,
|
||||
eager: Option<EagerCallInfo>,
|
||||
eager: Option<Box<EagerCallInfo>>,
|
||||
pub kind: MacroCallKind,
|
||||
}
|
||||
|
||||
|
@ -139,7 +141,8 @@ pub enum MacroDefKind {
|
|||
struct EagerCallInfo {
|
||||
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
|
||||
arg_or_expansion: Arc<tt::Subtree>,
|
||||
included_file: Option<FileId>,
|
||||
included_file: Option<(FileId, TokenMap)>,
|
||||
error: Option<ExpandError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -166,8 +169,6 @@ pub enum MacroCallKind {
|
|||
/// Outer attributes are counted first, then inner attributes. This does not support
|
||||
/// out-of-line modules, which may have attributes spread across 2 files!
|
||||
invoc_attr_index: AttrId,
|
||||
/// Whether this attribute is the `#[derive]` attribute.
|
||||
is_derive: bool,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -205,8 +206,8 @@ impl HirFileId {
|
|||
HirFileIdRepr::FileId(id) => break id,
|
||||
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
|
||||
file_id = match loc.eager {
|
||||
Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
|
||||
file_id = match loc.eager.as_deref() {
|
||||
Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
|
||||
_ => loc.kind.file_id(),
|
||||
};
|
||||
}
|
||||
|
@ -230,18 +231,17 @@ impl HirFileId {
|
|||
pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
|
||||
let macro_file = self.macro_file()?;
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
Some(loc.kind.to_node(db))
|
||||
Some(loc.to_node(db))
|
||||
}
|
||||
|
||||
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
|
||||
pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> {
|
||||
let mut call =
|
||||
db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db);
|
||||
let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
|
||||
loop {
|
||||
match call.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
|
||||
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
||||
call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
|
||||
call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -255,8 +255,14 @@ impl HirFileId {
|
|||
let arg_tt = loc.kind.arg(db)?;
|
||||
|
||||
let macro_def = db.macro_def(loc.def).ok()?;
|
||||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
|
||||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
|
||||
Arc::new((
|
||||
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
))
|
||||
});
|
||||
|
||||
let def = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
|
@ -298,7 +304,7 @@ impl HirFileId {
|
|||
let macro_file = self.macro_file()?;
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let attr = match loc.def.kind {
|
||||
MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
|
||||
MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
|
||||
_ => return None,
|
||||
};
|
||||
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
|
||||
|
@ -319,7 +325,7 @@ impl HirFileId {
|
|||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. }))
|
||||
matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. }))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
@ -342,7 +348,7 @@ impl HirFileId {
|
|||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. })
|
||||
loc.def.is_attribute_derive()
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
|
@ -413,22 +419,15 @@ impl MacroDefId {
|
|||
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_attribute_derive(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: attribute indices do not account for nested `cfg_attr`
|
||||
|
||||
impl MacroCallKind {
|
||||
/// Returns the file containing the macro invocation.
|
||||
fn file_id(&self) -> HirFileId {
|
||||
match *self {
|
||||
MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
|
||||
| MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
|
||||
| MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroCallLoc {
|
||||
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
|
||||
match self {
|
||||
match self.kind {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
ast_id.with_value(ast_id.to_node(db).syntax().clone())
|
||||
}
|
||||
|
@ -444,23 +443,49 @@ impl MacroCallKind {
|
|||
.unwrap_or_else(|| it.syntax().clone())
|
||||
})
|
||||
}
|
||||
MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => {
|
||||
// FIXME: handle `cfg_attr`
|
||||
ast_id.with_value(ast_id.to_node(db)).map(|it| {
|
||||
it.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(|it| match it {
|
||||
Either::Left(attr) => Some(attr.syntax().clone()),
|
||||
Either::Right(_) => None,
|
||||
})
|
||||
.unwrap_or_else(|| it.syntax().clone())
|
||||
})
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
||||
if self.def.is_attribute_derive() {
|
||||
// FIXME: handle `cfg_attr`
|
||||
ast_id.with_value(ast_id.to_node(db)).map(|it| {
|
||||
it.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(|it| match it {
|
||||
Either::Left(attr) => Some(attr.syntax().clone()),
|
||||
Either::Right(_) => None,
|
||||
})
|
||||
.unwrap_or_else(|| it.syntax().clone())
|
||||
})
|
||||
} else {
|
||||
ast_id.with_value(ast_id.to_node(db).syntax().clone())
|
||||
}
|
||||
}
|
||||
MacroCallKind::Attr { ast_id, .. } => {
|
||||
ast_id.with_value(ast_id.to_node(db).syntax().clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_to(&self) -> ExpandTo {
|
||||
match self.kind {
|
||||
MacroCallKind::FnLike { expand_to, .. } => expand_to,
|
||||
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
||||
MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements,
|
||||
MacroCallKind::Attr { .. } => {
|
||||
// is this always correct?
|
||||
ExpandTo::Items
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: attribute indices do not account for nested `cfg_attr`
|
||||
|
||||
impl MacroCallKind {
|
||||
/// Returns the file containing the macro invocation.
|
||||
fn file_id(&self) -> HirFileId {
|
||||
match *self {
|
||||
MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
|
||||
| MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
|
||||
| MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the original file range that best describes the location of this macro call.
|
||||
///
|
||||
|
@ -538,15 +563,6 @@ impl MacroCallKind {
|
|||
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_to(&self) -> ExpandTo {
|
||||
match self {
|
||||
MacroCallKind::FnLike { expand_to, .. } => *expand_to,
|
||||
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
||||
MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements,
|
||||
MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroCallId {
|
||||
|
@ -610,7 +626,7 @@ impl ExpansionInfo {
|
|||
|
||||
let token_range = token.value.text_range();
|
||||
match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
|
||||
MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
|
||||
// FIXME: handle `cfg_attr`
|
||||
let attr = item
|
||||
.doc_comments_and_attrs()
|
||||
|
@ -626,7 +642,8 @@ impl ExpansionInfo {
|
|||
token.value.text_range().checked_sub(attr_input_start)?;
|
||||
// shift by the item's tree's max id
|
||||
let token_id = attr_args.1.token_by_range(relative_range)?;
|
||||
let token_id = if *is_derive {
|
||||
|
||||
let token_id = if loc.def.is_attribute_derive() {
|
||||
// we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
|
||||
token_id
|
||||
} else {
|
||||
|
@ -677,20 +694,31 @@ impl ExpansionInfo {
|
|||
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
|
||||
let loc = db.lookup_intern_macro_call(call_id);
|
||||
|
||||
if let Some((file, map)) = loc.eager.and_then(|e| e.included_file) {
|
||||
// Special case: map tokens from `include!` expansions to the included file
|
||||
let range = map.first_range_by_token(token_id, token.value.kind())?;
|
||||
let source = db.parse(file);
|
||||
|
||||
let token = source.syntax_node().covering_element(range).into_token()?;
|
||||
|
||||
return Some((InFile::new(file.into(), token), Origin::Call));
|
||||
}
|
||||
|
||||
// Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
|
||||
let (token_map, tt) = match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, is_derive: true, .. } => {
|
||||
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
||||
}
|
||||
MacroCallKind::Attr { attr_args, .. } => {
|
||||
// try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
|
||||
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
|
||||
match self.macro_arg_shift.unshift(token_id) {
|
||||
Some(unshifted) => {
|
||||
token_id = unshifted;
|
||||
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
||||
if loc.def.is_attribute_derive() {
|
||||
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
||||
} else {
|
||||
// try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input
|
||||
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
|
||||
match self.macro_arg_shift.unshift(token_id) {
|
||||
Some(unshifted) => {
|
||||
token_id = unshifted;
|
||||
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
|
||||
}
|
||||
None => (&self.macro_arg.1, self.arg.clone()),
|
||||
}
|
||||
None => (&self.macro_arg.1, self.arg.clone()),
|
||||
}
|
||||
}
|
||||
_ => match origin {
|
||||
|
@ -718,7 +746,7 @@ pub type AstId<N> = InFile<FileAstId<N>>;
|
|||
|
||||
impl<N: AstNode> AstId<N> {
|
||||
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
|
||||
let root = db.parse_or_expand(self.file_id).unwrap();
|
||||
let root = db.parse_or_expand(self.file_id);
|
||||
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
|
||||
}
|
||||
}
|
||||
|
@ -754,7 +782,7 @@ impl<T> InFile<T> {
|
|||
}
|
||||
|
||||
pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
|
||||
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
||||
db.parse_or_expand(self.file_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -950,6 +978,7 @@ fn ascend_node_border_tokens(
|
|||
let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
|
||||
let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
|
||||
|
||||
// FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore
|
||||
let first = first_token(node)?;
|
||||
let last = last_token(node)?;
|
||||
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
|
||||
|
@ -977,6 +1006,7 @@ impl<N: AstNode> InFile<N> {
|
|||
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
|
||||
}
|
||||
|
||||
// FIXME: this should return `Option<InFileNotHirFile<N>>`
|
||||
pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
fmt::{self, Display as _},
|
||||
iter,
|
||||
};
|
||||
|
||||
|
@ -24,6 +24,12 @@ pub struct ModPath {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct UnescapedModPath<'a>(&'a ModPath);
|
||||
|
||||
impl<'a> UnescapedModPath<'a> {
|
||||
pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
UnescapedDisplay { db, path: self }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
|
@ -110,52 +116,30 @@ impl ModPath {
|
|||
UnescapedModPath(self)
|
||||
}
|
||||
|
||||
fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
|
||||
let mut first_segment = true;
|
||||
let mut add_segment = |s| -> fmt::Result {
|
||||
if !first_segment {
|
||||
f.write_str("::")?;
|
||||
}
|
||||
first_segment = false;
|
||||
f.write_str(s)?;
|
||||
Ok(())
|
||||
};
|
||||
match self.kind {
|
||||
PathKind::Plain => {}
|
||||
PathKind::Super(0) => add_segment("self")?,
|
||||
PathKind::Super(n) => {
|
||||
for _ in 0..n {
|
||||
add_segment("super")?;
|
||||
}
|
||||
}
|
||||
PathKind::Crate => add_segment("crate")?,
|
||||
PathKind::Abs => add_segment("")?,
|
||||
PathKind::DollarCrate(_) => add_segment("$crate")?,
|
||||
}
|
||||
for segment in &self.segments {
|
||||
if !first_segment {
|
||||
f.write_str("::")?;
|
||||
}
|
||||
first_segment = false;
|
||||
if escaped {
|
||||
segment.fmt(f)?
|
||||
} else {
|
||||
segment.unescaped().fmt(f)?
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
Display { db, path: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModPath {
|
||||
struct Display<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
path: &'a ModPath,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Display<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self._fmt(f, true)
|
||||
display_fmt_path(self.db, self.path, f, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for UnescapedModPath<'a> {
|
||||
struct UnescapedDisplay<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
path: &'a UnescapedModPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for UnescapedDisplay<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0._fmt(f, false)
|
||||
display_fmt_path(self.db, self.path.0, f, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +148,46 @@ impl From<Name> for ModPath {
|
|||
ModPath::from_segments(PathKind::Plain, iter::once(name))
|
||||
}
|
||||
}
|
||||
fn display_fmt_path(
|
||||
db: &dyn ExpandDatabase,
|
||||
path: &ModPath,
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
escaped: bool,
|
||||
) -> fmt::Result {
|
||||
let mut first_segment = true;
|
||||
let mut add_segment = |s| -> fmt::Result {
|
||||
if !first_segment {
|
||||
f.write_str("::")?;
|
||||
}
|
||||
first_segment = false;
|
||||
f.write_str(s)?;
|
||||
Ok(())
|
||||
};
|
||||
match path.kind {
|
||||
PathKind::Plain => {}
|
||||
PathKind::Super(0) => add_segment("self")?,
|
||||
PathKind::Super(n) => {
|
||||
for _ in 0..n {
|
||||
add_segment("super")?;
|
||||
}
|
||||
}
|
||||
PathKind::Crate => add_segment("crate")?,
|
||||
PathKind::Abs => add_segment("")?,
|
||||
PathKind::DollarCrate(_) => add_segment("$crate")?,
|
||||
}
|
||||
for segment in &path.segments {
|
||||
if !first_segment {
|
||||
f.write_str("::")?;
|
||||
}
|
||||
first_segment = false;
|
||||
if escaped {
|
||||
segment.display(db).fmt(f)?;
|
||||
} else {
|
||||
segment.unescaped().display(db).fmt(f)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_path(
|
||||
db: &dyn ExpandDatabase,
|
||||
|
|
|
@ -24,27 +24,6 @@ enum Repr {
|
|||
TupleField(usize),
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 {
|
||||
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for UnescapedName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 .0 {
|
||||
Repr::Text(text) => {
|
||||
let text = text.strip_prefix("r#").unwrap_or(text);
|
||||
fmt::Display::fmt(&text, f)
|
||||
}
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> UnescapedName<'a> {
|
||||
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
|
||||
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
|
||||
|
@ -60,6 +39,11 @@ impl<'a> UnescapedName<'a> {
|
|||
Repr::TupleField(it) => SmolStr::new(it.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
_ = db;
|
||||
UnescapedDisplay { name: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
|
@ -78,7 +62,7 @@ impl Name {
|
|||
Self::new_text(lt.text().into())
|
||||
}
|
||||
|
||||
/// Shortcut to create inline plain text name
|
||||
/// Shortcut to create inline plain text name. Panics if `text.len() > 22`
|
||||
const fn new_inline(text: &str) -> Name {
|
||||
Name::new_text(SmolStr::new_inline(text))
|
||||
}
|
||||
|
@ -112,6 +96,17 @@ impl Name {
|
|||
Name::new_inline("[missing name]")
|
||||
}
|
||||
|
||||
/// Generates a new name which is only equal to itself, by incrementing a counter. Due
|
||||
/// its implementation, it should not be used in things that salsa considers, like
|
||||
/// type names or field names, and it should be only used in names of local variables
|
||||
/// and labels and similar things.
|
||||
pub fn generate_new_name() -> Name {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static CNT: AtomicUsize = AtomicUsize::new(0);
|
||||
let c = CNT.fetch_add(1, Ordering::Relaxed);
|
||||
Name::new_text(format!("<ra@gennew>{c}").into())
|
||||
}
|
||||
|
||||
/// Returns the tuple index this name represents if it is a tuple field.
|
||||
pub fn as_tuple_index(&self) -> Option<usize> {
|
||||
match self.0 {
|
||||
|
@ -156,6 +151,40 @@ impl Name {
|
|||
Repr::TupleField(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
_ = db;
|
||||
Display { name: self }
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a> {
|
||||
name: &'a Name,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Display<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name.0 {
|
||||
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UnescapedDisplay<'a> {
|
||||
name: &'a UnescapedName<'a>,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for UnescapedDisplay<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name.0 .0 {
|
||||
Repr::Text(text) => {
|
||||
let text = text.strip_prefix("r#").unwrap_or(text);
|
||||
fmt::Display::fmt(&text, f)
|
||||
}
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsName {
|
||||
|
@ -337,18 +366,24 @@ pub mod known {
|
|||
crate_type,
|
||||
derive,
|
||||
global_allocator,
|
||||
no_core,
|
||||
no_std,
|
||||
test,
|
||||
test_case,
|
||||
recursion_limit,
|
||||
feature,
|
||||
// known methods of lang items
|
||||
call_once,
|
||||
call_mut,
|
||||
call,
|
||||
eq,
|
||||
ne,
|
||||
ge,
|
||||
gt,
|
||||
le,
|
||||
lt,
|
||||
// known fields of lang items
|
||||
pieces,
|
||||
// lang items
|
||||
add_assign,
|
||||
add,
|
||||
|
@ -363,6 +398,7 @@ pub mod known {
|
|||
deref,
|
||||
div_assign,
|
||||
div,
|
||||
drop,
|
||||
fn_mut,
|
||||
fn_once,
|
||||
future_trait,
|
||||
|
|
|
@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
|
|||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct ProcMacroExpander {
|
||||
proc_macro_id: Option<ProcMacroId>,
|
||||
proc_macro_id: ProcMacroId,
|
||||
}
|
||||
|
||||
const DUMMY_ID: u32 = !0;
|
||||
|
||||
impl ProcMacroExpander {
|
||||
pub fn new(proc_macro_id: ProcMacroId) -> Self {
|
||||
Self { proc_macro_id: Some(proc_macro_id) }
|
||||
assert_ne!(proc_macro_id.0, DUMMY_ID);
|
||||
Self { proc_macro_id }
|
||||
}
|
||||
|
||||
pub fn dummy() -> Self {
|
||||
Self { proc_macro_id: None }
|
||||
Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
|
||||
}
|
||||
|
||||
pub fn is_dummy(&self) -> bool {
|
||||
self.proc_macro_id.is_none()
|
||||
self.proc_macro_id.0 == DUMMY_ID
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
|
@ -32,33 +35,37 @@ impl ProcMacroExpander {
|
|||
attr_arg: Option<&tt::Subtree>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
match self.proc_macro_id {
|
||||
Some(id) => {
|
||||
let krate_graph = db.crate_graph();
|
||||
let proc_macros = match &krate_graph[def_crate].proc_macro {
|
||||
Ok(proc_macros) => proc_macros,
|
||||
Err(_) => {
|
||||
ProcMacroId(DUMMY_ID) => {
|
||||
ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate))
|
||||
}
|
||||
ProcMacroId(id) => {
|
||||
let proc_macros = db.proc_macros();
|
||||
let proc_macros = match proc_macros.get(&def_crate) {
|
||||
Some(Ok(proc_macros)) => proc_macros,
|
||||
Some(Err(_)) | None => {
|
||||
never!("Non-dummy expander even though there are no proc macros");
|
||||
return ExpandResult::with_err(
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other("Internal error".into()),
|
||||
);
|
||||
}
|
||||
};
|
||||
let proc_macro = match proc_macros.get(id.0 as usize) {
|
||||
let proc_macro = match proc_macros.get(id as usize) {
|
||||
Some(proc_macro) => proc_macro,
|
||||
None => {
|
||||
never!(
|
||||
"Proc macro index out of bounds: the length is {} but the index is {}",
|
||||
proc_macros.len(),
|
||||
id.0
|
||||
id
|
||||
);
|
||||
return ExpandResult::with_err(
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other("Internal error".into()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let krate_graph = db.crate_graph();
|
||||
// Proc macros have access to the environment variables of the invoking crate.
|
||||
let env = &krate_graph[calling_crate].env;
|
||||
match proc_macro.expander.expand(tt, attr_arg, env) {
|
||||
|
@ -74,17 +81,12 @@ impl ProcMacroExpander {
|
|||
}
|
||||
}
|
||||
ProcMacroExpansionError::System(text)
|
||||
| ProcMacroExpansionError::Panic(text) => ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other(text.into()),
|
||||
),
|
||||
| ProcMacroExpansionError::Panic(text) => {
|
||||
ExpandResult::new(tt::Subtree::empty(), ExpandError::Other(text.into()))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
None => ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::UnresolvedProcMacro(def_crate),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,12 @@ impl ToTokenTree for crate::tt::TokenTree {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for &crate::tt::TokenTree {
|
||||
fn to_token(self) -> crate::tt::TokenTree {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for crate::tt::Subtree {
|
||||
fn to_token(self) -> crate::tt::TokenTree {
|
||||
self.into()
|
||||
|
|
|
@ -15,7 +15,7 @@ doctest = false
|
|||
cov-mark = "2.0.0-pre.1"
|
||||
itertools = "0.10.5"
|
||||
arrayvec = "0.7.2"
|
||||
bitflags = "1.3.2"
|
||||
bitflags = "2.1.0"
|
||||
smallvec.workspace = true
|
||||
ena = "0.14.0"
|
||||
either = "1.7.0"
|
||||
|
@ -28,6 +28,8 @@ chalk-recursive = { version = "0.89.0", default-features = false }
|
|||
chalk-derive = "0.89.0"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
once_cell = "1.17.0"
|
||||
triomphe.workspace = true
|
||||
nohash-hasher.workspace = true
|
||||
typed-arena = "2.0.1"
|
||||
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
|
||||
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
//! reference to a type with the field `bar`. This is an approximation of the
|
||||
//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use chalk_ir::cast::Cast;
|
||||
use hir_def::lang_item::LangItem;
|
||||
use hir_expand::name::name;
|
||||
use limit::Limit;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
|
||||
|
@ -23,6 +22,21 @@ pub(crate) enum AutoderefKind {
|
|||
Overloaded,
|
||||
}
|
||||
|
||||
pub fn autoderef(
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
ty: Canonical<Ty>,
|
||||
) -> impl Iterator<Item = Canonical<Ty>> + '_ {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let mut autoderef = Autoderef::new(&mut table, ty);
|
||||
let mut v = Vec::new();
|
||||
while let Some((ty, _steps)) = autoderef.next() {
|
||||
v.push(autoderef.table.canonicalize(ty).value);
|
||||
}
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Autoderef<'a, 'db> {
|
||||
pub(crate) table: &'a mut InferenceTable<'db>,
|
||||
|
@ -76,49 +90,43 @@ pub(crate) fn autoderef_step(
|
|||
table: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
) -> Option<(AutoderefKind, Ty)> {
|
||||
if let Some(derefed) = builtin_deref(&ty) {
|
||||
if let Some(derefed) = builtin_deref(table, &ty, false) {
|
||||
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
|
||||
} else {
|
||||
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: replace uses of this with Autoderef above
|
||||
pub fn autoderef(
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
ty: Canonical<Ty>,
|
||||
) -> impl Iterator<Item = Canonical<Ty>> + '_ {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let mut autoderef = Autoderef::new(&mut table, ty);
|
||||
let mut v = Vec::new();
|
||||
while let Some((ty, _steps)) = autoderef.next() {
|
||||
v.push(autoderef.table.canonicalize(ty).value);
|
||||
}
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
|
||||
let _p = profile::span("deref");
|
||||
autoderef_step(table, ty).map(|(_, ty)| ty)
|
||||
}
|
||||
|
||||
fn builtin_deref(ty: &Ty) -> Option<&Ty> {
|
||||
pub(crate) fn builtin_deref<'ty>(
|
||||
table: &mut InferenceTable<'_>,
|
||||
ty: &'ty Ty,
|
||||
explicit: bool,
|
||||
) -> Option<&'ty Ty> {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty),
|
||||
TyKind::Ref(.., ty) => Some(ty),
|
||||
// FIXME: Maybe accept this but diagnose if its not explicit?
|
||||
TyKind::Raw(.., ty) if explicit => Some(ty),
|
||||
&TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
|
||||
if crate::lang_items::is_box(table.db, adt) {
|
||||
substs.at(Interner, 0).ty(Interner)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
|
||||
pub(crate) fn deref_by_trait(
|
||||
table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
) -> Option<Ty> {
|
||||
let _p = profile::span("deref_by_trait");
|
||||
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
|
||||
// don't try to deref unknown variables
|
||||
return None;
|
||||
}
|
||||
|
||||
let db = table.db;
|
||||
let deref_trait =
|
||||
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
|
||||
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
|
||||
|
|
|
@ -18,7 +18,6 @@ use crate::{
|
|||
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
|
||||
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
|
||||
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
|
||||
ValueTyDefId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -195,6 +194,19 @@ impl TyBuilder<()> {
|
|||
params.placeholder_subst(db)
|
||||
}
|
||||
|
||||
pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
|
||||
let params = generics(db.upcast(), def.into());
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
params.iter_id().map(|id| match id {
|
||||
either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
either::Either::Right(id) => {
|
||||
unknown_const_as_generic(db.const_param_ty(id)).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn subst_for_def(
|
||||
db: &dyn HirDatabase,
|
||||
def: impl Into<GenericDefId>,
|
||||
|
@ -233,6 +245,25 @@ impl TyBuilder<()> {
|
|||
TyBuilder::new((), params, parent_subst)
|
||||
}
|
||||
|
||||
pub fn subst_for_closure(
|
||||
db: &dyn HirDatabase,
|
||||
parent: DefWithBodyId,
|
||||
sig_ty: Ty,
|
||||
) -> Substitution {
|
||||
let sig_ty = sig_ty.cast(Interner);
|
||||
let self_subst = iter::once(&sig_ty);
|
||||
let Some(parent) = parent.as_generic_def_id() else {
|
||||
return Substitution::from_iter(Interner, self_subst);
|
||||
};
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
self_subst
|
||||
.chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build(self) -> Substitution {
|
||||
let ((), subst) = self.build_internal();
|
||||
subst
|
||||
|
@ -362,21 +393,4 @@ impl TyBuilder<Binders<Ty>> {
|
|||
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
|
||||
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
|
||||
}
|
||||
|
||||
pub fn value_ty(
|
||||
db: &dyn HirDatabase,
|
||||
def: ValueTyDefId,
|
||||
parent_subst: Option<Substitution>,
|
||||
) -> TyBuilder<Binders<Ty>> {
|
||||
let poly_value_ty = db.value_ty(def);
|
||||
let id = match def.to_generic_def_id() {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
// static items
|
||||
assert!(parent_subst.is_none());
|
||||
return TyBuilder::new_empty(poly_value_ty);
|
||||
}
|
||||
};
|
||||
TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! The implementation of `RustIrDatabase` for Chalk, which provides information
|
||||
//! about the code that Chalk needs.
|
||||
use std::sync::Arc;
|
||||
use core::ops;
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use cov_mark::hit;
|
||||
use tracing::debug;
|
||||
|
||||
use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
|
||||
|
@ -10,9 +10,9 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
|
|||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
expr::Movability,
|
||||
hir::Movability,
|
||||
lang_item::{lang_attr, LangItem, LangItemTarget},
|
||||
AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
|
||||
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
|
||||
};
|
||||
use hir_expand::name::name;
|
||||
|
||||
|
@ -25,7 +25,7 @@ use crate::{
|
|||
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||
to_assoc_type_id, to_chalk_trait_id,
|
||||
traits::ChalkContext,
|
||||
utils::generics,
|
||||
utils::{generics, ClosureSubst},
|
||||
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
|
||||
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
|
||||
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
|
||||
|
@ -108,17 +108,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
|
||||
};
|
||||
|
||||
fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
|
||||
let block = module.containing_block()?;
|
||||
hit!(block_local_impls);
|
||||
db.trait_impls_in_block(block)
|
||||
}
|
||||
|
||||
// Note: Since we're using impls_for_trait, only impls where the trait
|
||||
// can be resolved should ever reach Chalk. impl_datum relies on that
|
||||
// and will panic if the trait can't be resolved.
|
||||
let in_deps = self.db.trait_impls_in_deps(self.krate);
|
||||
let in_self = self.db.trait_impls_in_crate(self.krate);
|
||||
let trait_module = trait_.module(self.db.upcast());
|
||||
let type_module = match self_ty_fp {
|
||||
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
|
||||
|
@ -128,33 +117,62 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
|
||||
_ => None,
|
||||
};
|
||||
let impl_maps = [
|
||||
Some(in_deps),
|
||||
Some(in_self),
|
||||
local_impls(self.db, trait_module),
|
||||
type_module.and_then(|m| local_impls(self.db, m)),
|
||||
];
|
||||
|
||||
let mut def_blocks =
|
||||
[trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
|
||||
|
||||
// Note: Since we're using impls_for_trait, only impls where the trait
|
||||
// can be resolved should ever reach Chalk. impl_datum relies on that
|
||||
// and will panic if the trait can't be resolved.
|
||||
let in_deps = self.db.trait_impls_in_deps(self.krate);
|
||||
let in_self = self.db.trait_impls_in_crate(self.krate);
|
||||
|
||||
let block_impls = iter::successors(self.block, |&block_id| {
|
||||
cov_mark::hit!(block_local_impls);
|
||||
self.db.block_def_map(block_id).parent().and_then(|module| module.containing_block())
|
||||
})
|
||||
.inspect(|&block_id| {
|
||||
// make sure we don't search the same block twice
|
||||
def_blocks.iter_mut().for_each(|block| {
|
||||
if *block == Some(block_id) {
|
||||
*block = None;
|
||||
}
|
||||
});
|
||||
})
|
||||
.map(|block_id| self.db.trait_impls_in_block(block_id));
|
||||
|
||||
let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
|
||||
|
||||
let result: Vec<_> = if fps.is_empty() {
|
||||
debug!("Unrestricted search for {:?} impls...", trait_);
|
||||
impl_maps
|
||||
.iter()
|
||||
.filter_map(|o| o.as_ref())
|
||||
.flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
|
||||
.collect()
|
||||
} else {
|
||||
impl_maps
|
||||
.iter()
|
||||
.filter_map(|o| o.as_ref())
|
||||
.flat_map(|impls| {
|
||||
fps.iter().flat_map(move |fp| {
|
||||
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let mut result = vec![];
|
||||
match fps {
|
||||
[] => {
|
||||
debug!("Unrestricted search for {:?} impls...", trait_);
|
||||
let mut f = |impls: &TraitImpls| {
|
||||
result.extend(impls.for_trait(trait_).map(id_to_chalk));
|
||||
};
|
||||
f(&in_self);
|
||||
in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
|
||||
block_impls.for_each(|it| f(&it));
|
||||
def_blocks
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.for_each(|it| f(&self.db.trait_impls_in_block(it)));
|
||||
}
|
||||
fps => {
|
||||
let mut f =
|
||||
|impls: &TraitImpls| {
|
||||
result.extend(fps.iter().flat_map(|fp| {
|
||||
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
|
||||
}));
|
||||
};
|
||||
f(&in_self);
|
||||
in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
|
||||
block_impls.for_each(|it| f(&it));
|
||||
def_blocks
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.for_each(|it| f(&self.db.trait_impls_in_block(it)));
|
||||
}
|
||||
}
|
||||
|
||||
debug!("impls_for_trait returned {} impls", result.len());
|
||||
result
|
||||
|
@ -193,7 +211,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
&self,
|
||||
environment: &chalk_ir::Environment<Interner>,
|
||||
) -> chalk_ir::ProgramClauses<Interner> {
|
||||
self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
|
||||
self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone())
|
||||
}
|
||||
|
||||
fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
|
||||
|
@ -321,7 +339,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
_closure_id: chalk_ir::ClosureId<Interner>,
|
||||
substs: &chalk_ir::Substitution<Interner>,
|
||||
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
|
||||
let sig_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
|
||||
let sig_ty = ClosureSubst(substs).sig_ty();
|
||||
let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
|
||||
let io = rust_ir::FnDefInputsAndOutputDatum {
|
||||
argument_types: sig.params().to_vec(),
|
||||
|
@ -347,13 +365,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
|
||||
fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
|
||||
let id = from_chalk_trait_id(trait_id);
|
||||
self.db.trait_data(id).name.to_string()
|
||||
self.db.trait_data(id).name.display(self.db.upcast()).to_string()
|
||||
}
|
||||
fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
|
||||
match adt_id {
|
||||
hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(),
|
||||
hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(),
|
||||
hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(),
|
||||
hir_def::AdtId::StructId(id) => {
|
||||
self.db.struct_data(id).name.display(self.db.upcast()).to_string()
|
||||
}
|
||||
hir_def::AdtId::EnumId(id) => {
|
||||
self.db.enum_data(id).name.display(self.db.upcast()).to_string()
|
||||
}
|
||||
hir_def::AdtId::UnionId(id) => {
|
||||
self.db.union_data(id).name.display(self.db.upcast()).to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn adt_size_align(&self, _id: chalk_ir::AdtId<Interner>) -> Arc<rust_ir::AdtSizeAlign> {
|
||||
|
@ -362,7 +386,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
}
|
||||
fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
|
||||
let id = self.db.associated_ty_data(assoc_ty_id).name;
|
||||
self.db.type_alias_data(id).name.to_string()
|
||||
self.db.type_alias_data(id).name.display(self.db.upcast()).to_string()
|
||||
}
|
||||
fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
|
||||
format!("Opaque_{}", opaque_ty_id.0)
|
||||
|
@ -373,7 +397,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
fn generator_datum(
|
||||
&self,
|
||||
id: chalk_ir::GeneratorId<Interner>,
|
||||
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
|
||||
) -> Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
|
||||
let (parent, expr) = self.db.lookup_intern_generator(id.into());
|
||||
|
||||
// We fill substitution with unknown type, because we only need to know whether the generic
|
||||
|
@ -398,8 +422,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
let input_output = crate::make_type_and_const_binders(it, input_output);
|
||||
|
||||
let movability = match self.db.body(parent)[expr] {
|
||||
hir_def::expr::Expr::Closure {
|
||||
closure_kind: hir_def::expr::ClosureKind::Generator(movability),
|
||||
hir_def::hir::Expr::Closure {
|
||||
closure_kind: hir_def::hir::ClosureKind::Generator(movability),
|
||||
..
|
||||
} => movability,
|
||||
_ => unreachable!("non generator expression interned as generator"),
|
||||
|
@ -414,7 +438,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
fn generator_witness_datum(
|
||||
&self,
|
||||
id: chalk_ir::GeneratorId<Interner>,
|
||||
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
|
||||
) -> Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
|
||||
// FIXME: calculate inner types
|
||||
let inner_types =
|
||||
rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) };
|
||||
|
@ -435,7 +459,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
|
||||
impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase {
|
||||
fn fn_def_variance(
|
||||
&self,
|
||||
fn_def_id: chalk_ir::FnDefId<Interner>,
|
||||
|
@ -451,9 +475,10 @@ impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
|
|||
pub(crate) fn program_clauses_for_chalk_env_query(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
block: Option<BlockId>,
|
||||
environment: chalk_ir::Environment<Interner>,
|
||||
) -> chalk_ir::ProgramClauses<Interner> {
|
||||
chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment)
|
||||
chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment)
|
||||
}
|
||||
|
||||
pub(crate) fn associated_ty_data_query(
|
||||
|
@ -786,17 +811,17 @@ pub(crate) fn adt_variance_query(
|
|||
)
|
||||
}
|
||||
|
||||
/// Returns instantiated predicates.
|
||||
pub(super) fn convert_where_clauses(
|
||||
db: &dyn HirDatabase,
|
||||
def: GenericDefId,
|
||||
substs: &Substitution,
|
||||
) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
|
||||
let generic_predicates = db.generic_predicates(def);
|
||||
let mut result = Vec::with_capacity(generic_predicates.len());
|
||||
for pred in generic_predicates.iter() {
|
||||
result.push(pred.clone().substitute(Interner, substs));
|
||||
}
|
||||
result
|
||||
db.generic_predicates(def)
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|pred| pred.substitute(Interner, substs))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn generic_predicate_to_inline_bound(
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
//! Various extensions traits for Chalk types.
|
||||
|
||||
use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
|
||||
use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
|
||||
use hir_def::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
|
||||
generics::TypeOrConstParamData,
|
||||
lang_item::LangItem,
|
||||
type_ref::Rawness,
|
||||
FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
||||
DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
|
||||
CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
|
||||
db::HirDatabase,
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
|
||||
to_chalk_trait_id,
|
||||
utils::{generics, ClosureSubst},
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
|
||||
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
|
||||
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
|
||||
};
|
||||
|
||||
pub trait TyExt {
|
||||
fn is_unit(&self) -> bool;
|
||||
fn is_integral(&self) -> bool;
|
||||
fn is_scalar(&self) -> bool;
|
||||
fn is_floating_point(&self) -> bool;
|
||||
fn is_never(&self) -> bool;
|
||||
fn is_unknown(&self) -> bool;
|
||||
|
@ -28,8 +32,10 @@ pub trait TyExt {
|
|||
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
|
||||
fn as_builtin(&self) -> Option<BuiltinType>;
|
||||
fn as_tuple(&self) -> Option<&Substitution>;
|
||||
fn as_closure(&self) -> Option<ClosureId>;
|
||||
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
|
||||
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
|
||||
fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>;
|
||||
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
|
||||
fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
|
||||
|
||||
|
@ -44,6 +50,7 @@ pub trait TyExt {
|
|||
|
||||
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
|
||||
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
|
||||
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool;
|
||||
|
||||
/// FIXME: Get rid of this, it's not a good abstraction
|
||||
fn equals_ctor(&self, other: &Ty) -> bool;
|
||||
|
@ -62,6 +69,10 @@ impl TyExt for Ty {
|
|||
)
|
||||
}
|
||||
|
||||
fn is_scalar(&self) -> bool {
|
||||
matches!(self.kind(Interner), TyKind::Scalar(_))
|
||||
}
|
||||
|
||||
fn is_floating_point(&self) -> bool {
|
||||
matches!(
|
||||
self.kind(Interner),
|
||||
|
@ -128,12 +139,20 @@ impl TyExt for Ty {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_closure(&self) -> Option<ClosureId> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Closure(id, _) => Some(*id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
|
||||
match self.callable_def(db) {
|
||||
Some(CallableDefId::FunctionId(func)) => Some(func),
|
||||
Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
|
||||
|
@ -141,6 +160,13 @@ impl TyExt for Ty {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Raw(mutability, ty) => Some((ty, *mutability)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
|
||||
|
@ -176,10 +202,7 @@ impl TyExt for Ty {
|
|||
let sig = db.callable_item_signature(callable_def);
|
||||
Some(sig.substitute(Interner, parameters))
|
||||
}
|
||||
TyKind::Closure(.., substs) => {
|
||||
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
|
||||
sig_param.callable_sig(db)
|
||||
}
|
||||
TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +341,20 @@ impl TyExt for Ty {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
|
||||
let crate_id = owner.module(db.upcast()).krate();
|
||||
let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
|
||||
return false;
|
||||
};
|
||||
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
|
||||
let env = db.trait_environment_for_body(owner);
|
||||
let goal = Canonical {
|
||||
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
|
||||
binders: CanonicalVarKinds::empty(Interner),
|
||||
};
|
||||
db.trait_solve(crate_id, None, goal).is_some()
|
||||
}
|
||||
|
||||
fn equals_ctor(&self, other: &Ty) -> bool {
|
||||
match (self.kind(Interner), other.kind(Interner)) {
|
||||
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
|
||||
|
|
|
@ -3,19 +3,20 @@
|
|||
use base_db::CrateId;
|
||||
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
|
||||
use hir_def::{
|
||||
expr::Expr,
|
||||
path::ModPath,
|
||||
hir::Expr,
|
||||
path::Path,
|
||||
resolver::{Resolver, ValueNs},
|
||||
type_ref::ConstRef,
|
||||
ConstId, EnumVariantId,
|
||||
EnumVariantId, GeneralConstId, StaticId,
|
||||
};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
||||
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
|
||||
Interner, MemoryMap, Ty, TyBuilder,
|
||||
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
|
||||
mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
|
||||
ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder,
|
||||
};
|
||||
|
||||
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
||||
|
@ -57,7 +58,7 @@ pub enum ConstEvalError {
|
|||
impl From<MirLowerError> for ConstEvalError {
|
||||
fn from(value: MirLowerError) -> Self {
|
||||
match value {
|
||||
MirLowerError::ConstEvalError(e) => *e,
|
||||
MirLowerError::ConstEvalError(_, e) => *e,
|
||||
_ => ConstEvalError::MirLowerError(value),
|
||||
}
|
||||
}
|
||||
|
@ -72,10 +73,11 @@ impl From<MirEvalError> for ConstEvalError {
|
|||
pub(crate) fn path_to_const(
|
||||
db: &dyn HirDatabase,
|
||||
resolver: &Resolver,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
mode: ParamLoweringMode,
|
||||
args_lazy: impl FnOnce() -> Generics,
|
||||
debruijn: DebruijnIndex,
|
||||
expected_ty: Ty,
|
||||
) -> Option<Const> {
|
||||
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
|
||||
Some(ValueNs::GenericParam(p)) => {
|
||||
|
@ -89,7 +91,7 @@ pub(crate) fn path_to_const(
|
|||
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
|
||||
None => {
|
||||
never!(
|
||||
"Generic list doesn't contain this param: {:?}, {}, {:?}",
|
||||
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
|
||||
args,
|
||||
path,
|
||||
p
|
||||
|
@ -100,6 +102,10 @@ pub(crate) fn path_to_const(
|
|||
};
|
||||
Some(ConstData { ty, value }.intern(Interner))
|
||||
}
|
||||
Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
|
||||
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
|
||||
expected_ty,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -124,14 +130,15 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
|
|||
|
||||
/// Interns a constant scalar with the given type
|
||||
pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
|
||||
let layout = db.layout_of_ty(ty.clone(), krate);
|
||||
let bytes = match value {
|
||||
ConstRef::Int(i) => {
|
||||
// FIXME: We should handle failure of layout better.
|
||||
let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
|
||||
let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
|
||||
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
|
||||
}
|
||||
ConstRef::UInt(i) => {
|
||||
let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
|
||||
let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
|
||||
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
|
||||
}
|
||||
ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
|
||||
|
@ -153,13 +160,17 @@ pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) ->
|
|||
)
|
||||
}
|
||||
|
||||
pub fn try_const_usize(c: &Const) -> Option<u128> {
|
||||
pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
|
||||
match &c.data(Interner).value {
|
||||
chalk_ir::ConstValue::BoundVar(_) => None,
|
||||
chalk_ir::ConstValue::InferenceVar(_) => None,
|
||||
chalk_ir::ConstValue::Placeholder(_) => None,
|
||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||
ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
|
||||
ConstScalar::UnevaluatedConst(c, subst) => {
|
||||
let ec = db.const_eval(*c, subst.clone()).ok()?;
|
||||
try_const_usize(db, &ec)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
|
@ -168,7 +179,16 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
|
|||
pub(crate) fn const_eval_recover(
|
||||
_: &dyn HirDatabase,
|
||||
_: &[String],
|
||||
_: &ConstId,
|
||||
_: &GeneralConstId,
|
||||
_: &Substitution,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||
}
|
||||
|
||||
pub(crate) fn const_eval_static_recover(
|
||||
_: &dyn HirDatabase,
|
||||
_: &[String],
|
||||
_: &StaticId,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||
}
|
||||
|
@ -183,11 +203,39 @@ pub(crate) fn const_eval_discriminant_recover(
|
|||
|
||||
pub(crate) fn const_eval_query(
|
||||
db: &dyn HirDatabase,
|
||||
const_id: ConstId,
|
||||
def: GeneralConstId,
|
||||
subst: Substitution,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
let def = const_id.into();
|
||||
let body = db.mir_body(def)?;
|
||||
let c = interpret_mir(db, &body, false)?;
|
||||
let body = match def {
|
||||
GeneralConstId::ConstId(c) => {
|
||||
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
|
||||
}
|
||||
GeneralConstId::AnonymousConstId(c) => {
|
||||
let (def, root) = db.lookup_intern_anonymous_const(c);
|
||||
let body = db.body(def);
|
||||
let infer = db.infer(def);
|
||||
Arc::new(monomorphize_mir_body_bad(
|
||||
db,
|
||||
lower_to_mir(db, def, &body, &infer, root)?,
|
||||
subst,
|
||||
db.trait_environment_for_body(def),
|
||||
)?)
|
||||
}
|
||||
};
|
||||
let c = interpret_mir(db, &body, false).0?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub(crate) fn const_eval_static_query(
|
||||
db: &dyn HirDatabase,
|
||||
def: StaticId,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
let body = db.monomorphized_mir_body(
|
||||
def.into(),
|
||||
Substitution::empty(Interner),
|
||||
db.trait_environment_for_body(def.into()),
|
||||
)?;
|
||||
let c = interpret_mir(db, &body, false).0?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
|
@ -209,9 +257,13 @@ pub(crate) fn const_eval_discriminant_variant(
|
|||
};
|
||||
return Ok(value);
|
||||
}
|
||||
let mir_body = db.mir_body(def)?;
|
||||
let c = interpret_mir(db, &mir_body, false)?;
|
||||
let c = try_const_usize(&c).unwrap() as i128;
|
||||
let mir_body = db.monomorphized_mir_body(
|
||||
def,
|
||||
Substitution::empty(Interner),
|
||||
db.trait_environment_for_body(def),
|
||||
)?;
|
||||
let c = interpret_mir(db, &mir_body, false).0?;
|
||||
let c = try_const_usize(db, &c).unwrap() as i128;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
|
@ -226,15 +278,16 @@ pub(crate) fn eval_to_const(
|
|||
debruijn: DebruijnIndex,
|
||||
) -> Const {
|
||||
let db = ctx.db;
|
||||
let infer = ctx.clone().resolve_all();
|
||||
if let Expr::Path(p) = &ctx.body.exprs[expr] {
|
||||
let resolver = &ctx.resolver;
|
||||
if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
|
||||
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
let infer = ctx.clone().resolve_all();
|
||||
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
||||
if let Ok(result) = interpret_mir(db, &mir_body, true) {
|
||||
if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
377
crates/hir-ty/src/consteval/tests/intrinsics.rs
Normal file
377
crates/hir-ty/src/consteval/tests/intrinsics.rs
Normal file
|
@ -0,0 +1,377 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn size_of() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of<T>() -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = size_of::<i32>();
|
||||
"#,
|
||||
4,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transmute() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn transmute<T, U>(e: T) -> U;
|
||||
}
|
||||
|
||||
const GOAL: i32 = transmute((1i16, 1i16));
|
||||
"#,
|
||||
0x00010001,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_eval_select() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
|
||||
where
|
||||
G: FnOnce<ARG, Output = RET>,
|
||||
F: FnOnce<ARG, Output = RET>;
|
||||
}
|
||||
|
||||
const fn in_const(x: i32, y: i32) -> i32 {
|
||||
x + y
|
||||
}
|
||||
|
||||
fn in_rt(x: i32, y: i32) -> i32 {
|
||||
x + y
|
||||
}
|
||||
|
||||
const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt);
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapping_add() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn wrapping_add<T>(a: T, b: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: u8 = wrapping_add(10, 250);
|
||||
"#,
|
||||
4,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_add() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn saturating_add<T>(a: T, b: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: u8 = saturating_add(10, 250);
|
||||
"#,
|
||||
255,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn saturating_add<T>(a: T, b: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i8 = saturating_add(5, 8);
|
||||
"#,
|
||||
13,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allocator() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "Rust" {
|
||||
#[rustc_allocator]
|
||||
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
|
||||
#[rustc_deallocator]
|
||||
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
|
||||
#[rustc_reallocator]
|
||||
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
|
||||
#[rustc_allocator_zeroed]
|
||||
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
|
||||
}
|
||||
|
||||
const GOAL: u8 = unsafe {
|
||||
let ptr = __rust_alloc(4, 1);
|
||||
let ptr2 = ((ptr as usize) + 1) as *mut u8;
|
||||
*ptr = 23;
|
||||
*ptr2 = 32;
|
||||
let ptr = __rust_realloc(ptr, 4, 1, 8);
|
||||
let ptr2 = ((ptr as usize) + 1) as *mut u8;
|
||||
*ptr + *ptr2
|
||||
};
|
||||
"#,
|
||||
55,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflowing_add() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
|
||||
}
|
||||
|
||||
const GOAL: u8 = add_with_overflow(1, 2).0;
|
||||
"#,
|
||||
3,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
|
||||
}
|
||||
|
||||
const GOAL: u8 = add_with_overflow(1, 2).1 as u8;
|
||||
"#,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn needs_drop() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: copy, sized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn needs_drop<T: ?Sized>() -> bool;
|
||||
}
|
||||
struct X;
|
||||
const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
|
||||
"#,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn likely() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn likely(b: bool) -> bool;
|
||||
pub fn unlikely(b: bool) -> bool;
|
||||
}
|
||||
|
||||
const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false);
|
||||
"#,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_point() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn sqrtf32(x: f32) -> f32;
|
||||
pub fn powf32(a: f32, x: f32) -> f32;
|
||||
pub fn fmaf32(a: f32, b: f32, c: f32) -> f32;
|
||||
}
|
||||
|
||||
const GOAL: f32 = sqrtf32(1.2) + powf32(3.4, 5.6) + fmaf32(-7.8, 1.3, 2.4);
|
||||
"#,
|
||||
i128::from_le_bytes(pad16(
|
||||
&f32::to_le_bytes(1.2f32.sqrt() + 3.4f32.powf(5.6) + (-7.8f32).mul_add(1.3, 2.4)),
|
||||
true,
|
||||
)),
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn powif64(a: f64, x: i32) -> f64;
|
||||
pub fn sinf64(x: f64) -> f64;
|
||||
pub fn minnumf64(x: f64, y: f64) -> f64;
|
||||
}
|
||||
|
||||
const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3);
|
||||
"#,
|
||||
i128::from_le_bytes(pad16(
|
||||
&f64::to_le_bytes(1.2f64.powi(5) + 3.4f64.sin() + (-7.8f64).min(1.3)),
|
||||
true,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
extern "rust-intrinsic" {
|
||||
pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T;
|
||||
pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
|
||||
pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
|
||||
pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T);
|
||||
pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
|
||||
}
|
||||
|
||||
fn should_not_reach() {
|
||||
_ // fails the test if executed
|
||||
}
|
||||
|
||||
const GOAL: i32 = {
|
||||
let mut x = 5;
|
||||
atomic_store_release(&mut x, 10);
|
||||
let mut y = atomic_xchg_acquire(&mut x, 100);
|
||||
atomic_xadd_acqrel(&mut y, 20);
|
||||
if (30, true) != atomic_cxchg_release_seqcst(&mut y, 30, 40) {
|
||||
should_not_reach();
|
||||
}
|
||||
if (40, false) != atomic_cxchg_release_seqcst(&mut y, 30, 50) {
|
||||
should_not_reach();
|
||||
}
|
||||
if (40, true) != atomic_cxchgweak_acquire_acquire(&mut y, 40, 30) {
|
||||
should_not_reach();
|
||||
}
|
||||
let mut z = atomic_xsub_seqcst(&mut x, -200);
|
||||
atomic_xor_seqcst(&mut x, 1024);
|
||||
atomic_load_seqcst(&x) + z * 3 + atomic_load_seqcst(&y) * 2
|
||||
};
|
||||
"#,
|
||||
660 + 1024,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offset() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
extern "rust-intrinsic" {
|
||||
pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
|
||||
}
|
||||
|
||||
const GOAL: u8 = unsafe {
|
||||
let ar: &[(u8, u8, u8)] = &[
|
||||
(10, 11, 12),
|
||||
(20, 21, 22),
|
||||
(30, 31, 32),
|
||||
(40, 41, 42),
|
||||
(50, 51, 52),
|
||||
];
|
||||
let ar: *const [(u8, u8, u8)] = ar;
|
||||
let ar = ar as *const (u8, u8, u8);
|
||||
let element = *offset(ar, 2);
|
||||
element.1
|
||||
};
|
||||
"#,
|
||||
31,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arith_offset() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
extern "rust-intrinsic" {
|
||||
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
|
||||
}
|
||||
|
||||
const GOAL: u8 = unsafe {
|
||||
let ar: &[(u8, u8, u8)] = &[
|
||||
(10, 11, 12),
|
||||
(20, 21, 22),
|
||||
(30, 31, 32),
|
||||
(40, 41, 42),
|
||||
(50, 51, 52),
|
||||
];
|
||||
let ar: *const [(u8, u8, u8)] = ar;
|
||||
let ar = ar as *const (u8, u8, u8);
|
||||
let element = *arith_offset(arith_offset(ar, 102), -100);
|
||||
element.1
|
||||
};
|
||||
"#,
|
||||
31,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_nonoverlapping() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
|
||||
}
|
||||
|
||||
const GOAL: u8 = unsafe {
|
||||
let mut x = 2;
|
||||
let y = 5;
|
||||
copy_nonoverlapping(&y, &mut x, 1);
|
||||
x
|
||||
};
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
extern "rust-intrinsic" {
|
||||
pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
|
||||
}
|
||||
|
||||
const GOAL: i32 = unsafe {
|
||||
let mut x = [1i32, 2, 3, 4, 5];
|
||||
let y = (&mut x as *mut _) as *mut i32;
|
||||
let z = (y as usize + 4) as *const i32;
|
||||
copy(z, y, 4);
|
||||
x[0] + x[1] + x[2] + x[3] + x[4]
|
||||
};
|
||||
"#,
|
||||
19,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctpop() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn ctpop<T: Copy>(x: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i64 = ctpop(-29);
|
||||
"#,
|
||||
61,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cttz() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn cttz<T: Copy>(x: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i64 = cttz(-24);
|
||||
"#,
|
||||
3,
|
||||
);
|
||||
}
|
|
@ -1,27 +1,27 @@
|
|||
//! The home of `HirDatabase`, which is the Salsa database containing all the
|
||||
//! type inference-related queries.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync;
|
||||
|
||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
expr::ExprId,
|
||||
layout::{Layout, LayoutError, TargetDataLayout},
|
||||
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
|
||||
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
|
||||
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
|
||||
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
|
||||
LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
|
||||
};
|
||||
use la_arena::ArenaMap;
|
||||
use smallvec::SmallVec;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
chalk_db,
|
||||
consteval::ConstEvalError,
|
||||
layout::{Layout, LayoutError},
|
||||
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
|
||||
mir::{BorrowckResult, MirBody, MirLowerError},
|
||||
Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
|
||||
PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
|
||||
ValueTyDefId,
|
||||
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
|
||||
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
|
||||
TyDefId, ValueTyDefId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
|
@ -38,8 +38,28 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
#[salsa::cycle(crate::mir::mir_body_recover)]
|
||||
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::mir_body_for_closure_query)]
|
||||
fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
|
||||
#[salsa::cycle(crate::mir::monomorphized_mir_body_recover)]
|
||||
fn monomorphized_mir_body(
|
||||
&self,
|
||||
def: DefWithBodyId,
|
||||
subst: Substitution,
|
||||
env: Arc<crate::TraitEnvironment>,
|
||||
) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
|
||||
fn monomorphized_mir_body_for_closure(
|
||||
&self,
|
||||
def: ClosureId,
|
||||
subst: Substitution,
|
||||
env: Arc<crate::TraitEnvironment>,
|
||||
) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::borrowck_query)]
|
||||
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
|
||||
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::lower::ty_query)]
|
||||
#[salsa::cycle(crate::lower::ty_recover)]
|
||||
|
@ -57,7 +77,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
|
||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
||||
fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
|
||||
fn const_eval(&self, def: GeneralConstId, subst: Substitution)
|
||||
-> Result<Const, ConstEvalError>;
|
||||
|
||||
#[salsa::invoke(crate::consteval::const_eval_static_query)]
|
||||
#[salsa::cycle(crate::consteval::const_eval_static_recover)]
|
||||
fn const_eval_static(&self, def: StaticId) -> Result<Const, ConstEvalError>;
|
||||
|
||||
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
||||
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
||||
|
@ -71,7 +96,16 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
|
||||
#[salsa::invoke(crate::layout::layout_of_adt_query)]
|
||||
#[salsa::cycle(crate::layout::layout_of_adt_recover)]
|
||||
fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
|
||||
fn layout_of_adt(
|
||||
&self,
|
||||
def: AdtId,
|
||||
subst: Substitution,
|
||||
krate: CrateId,
|
||||
) -> Result<Arc<Layout>, LayoutError>;
|
||||
|
||||
#[salsa::invoke(crate::layout::layout_of_ty_query)]
|
||||
#[salsa::cycle(crate::layout::layout_of_ty_recover)]
|
||||
fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result<Arc<Layout>, LayoutError>;
|
||||
|
||||
#[salsa::invoke(crate::layout::target_data_layout_query)]
|
||||
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
|
||||
|
@ -97,6 +131,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
#[salsa::invoke(crate::lower::generic_predicates_query)]
|
||||
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
|
||||
|
||||
#[salsa::invoke(crate::lower::trait_environment_for_body_query)]
|
||||
#[salsa::transparent]
|
||||
fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>;
|
||||
|
||||
#[salsa::invoke(crate::lower::trait_environment_query)]
|
||||
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
|
||||
|
||||
|
@ -108,7 +146,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
|
||||
|
||||
#[salsa::invoke(InherentImpls::inherent_impls_in_block_query)]
|
||||
fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
|
||||
fn inherent_impls_in_block(&self, block: BlockId) -> Arc<InherentImpls>;
|
||||
|
||||
/// Collects all crates in the dependency graph that have impls for the
|
||||
/// given fingerprint. This is only used for primitive types and types
|
||||
|
@ -125,10 +163,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
|
||||
|
||||
#[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
|
||||
fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
|
||||
fn trait_impls_in_block(&self, block: BlockId) -> Arc<TraitImpls>;
|
||||
|
||||
#[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
|
||||
fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
|
||||
fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>;
|
||||
|
||||
// Interned IDs for Chalk integration
|
||||
#[salsa::interned]
|
||||
|
@ -148,24 +186,34 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId;
|
||||
|
||||
#[salsa::invoke(chalk_db::associated_ty_data_query)]
|
||||
fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
|
||||
fn associated_ty_data(
|
||||
&self,
|
||||
id: chalk_db::AssocTypeId,
|
||||
) -> sync::Arc<chalk_db::AssociatedTyDatum>;
|
||||
|
||||
#[salsa::invoke(chalk_db::trait_datum_query)]
|
||||
fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId)
|
||||
-> Arc<chalk_db::TraitDatum>;
|
||||
fn trait_datum(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
trait_id: chalk_db::TraitId,
|
||||
) -> sync::Arc<chalk_db::TraitDatum>;
|
||||
|
||||
#[salsa::invoke(chalk_db::struct_datum_query)]
|
||||
fn struct_datum(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
struct_id: chalk_db::AdtId,
|
||||
) -> Arc<chalk_db::StructDatum>;
|
||||
) -> sync::Arc<chalk_db::StructDatum>;
|
||||
|
||||
#[salsa::invoke(chalk_db::impl_datum_query)]
|
||||
fn impl_datum(&self, krate: CrateId, impl_id: chalk_db::ImplId) -> Arc<chalk_db::ImplDatum>;
|
||||
fn impl_datum(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
impl_id: chalk_db::ImplId,
|
||||
) -> sync::Arc<chalk_db::ImplDatum>;
|
||||
|
||||
#[salsa::invoke(chalk_db::fn_def_datum_query)]
|
||||
fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>;
|
||||
fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> sync::Arc<chalk_db::FnDefDatum>;
|
||||
|
||||
#[salsa::invoke(chalk_db::fn_def_variance_query)]
|
||||
fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
|
||||
|
@ -178,7 +226,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
&self,
|
||||
krate: CrateId,
|
||||
id: chalk_db::AssociatedTyValueId,
|
||||
) -> Arc<chalk_db::AssociatedTyValue>;
|
||||
) -> sync::Arc<chalk_db::AssociatedTyValue>;
|
||||
|
||||
#[salsa::invoke(crate::traits::normalize_projection_query)]
|
||||
#[salsa::transparent]
|
||||
|
@ -193,6 +241,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn trait_solve(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
block: Option<BlockId>,
|
||||
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
|
||||
) -> Option<crate::Solution>;
|
||||
|
||||
|
@ -200,6 +249,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn trait_solve_query(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
block: Option<BlockId>,
|
||||
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
|
||||
) -> Option<crate::Solution>;
|
||||
|
||||
|
@ -207,19 +257,26 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn program_clauses_for_chalk_env(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
block: Option<BlockId>,
|
||||
env: chalk_ir::Environment<Interner>,
|
||||
) -> chalk_ir::ProgramClauses<Interner>;
|
||||
}
|
||||
|
||||
fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
|
||||
let _p = profile::span("infer:wait").detail(|| match def {
|
||||
DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
|
||||
DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
|
||||
DefWithBodyId::ConstId(it) => {
|
||||
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
|
||||
DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
|
||||
DefWithBodyId::StaticId(it) => {
|
||||
db.static_data(it).name.clone().display(db.upcast()).to_string()
|
||||
}
|
||||
DefWithBodyId::ConstId(it) => db
|
||||
.const_data(it)
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(Name::missing)
|
||||
.display(db.upcast())
|
||||
.to_string(),
|
||||
DefWithBodyId::VariantId(it) => {
|
||||
db.enum_data(it.parent).variants[it.local_id].name.to_string()
|
||||
db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
|
||||
}
|
||||
});
|
||||
db.infer_query(def)
|
||||
|
@ -228,10 +285,11 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
|
|||
fn trait_solve_wait(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
block: Option<BlockId>,
|
||||
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
|
||||
) -> Option<crate::Solution> {
|
||||
let _p = profile::span("trait_solve::wait");
|
||||
db.trait_solve_query(krate, goal)
|
||||
db.trait_solve_query(krate, block, goal)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -16,8 +16,8 @@ use std::fmt;
|
|||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
expr::{Pat, PatId},
|
||||
data::adt::VariantData,
|
||||
hir::{Pat, PatId},
|
||||
src::HasSource,
|
||||
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
|
||||
StructId,
|
||||
|
@ -223,7 +223,7 @@ impl<'a> DeclValidator<'a> {
|
|||
}
|
||||
|
||||
// Check the function name.
|
||||
let function_name = data.name.to_string();
|
||||
let function_name = data.name.display(self.db.upcast()).to_string();
|
||||
let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement {
|
||||
current_name: data.name.clone(),
|
||||
suggested_text: new_name,
|
||||
|
@ -244,7 +244,9 @@ impl<'a> DeclValidator<'a> {
|
|||
id,
|
||||
Replacement {
|
||||
current_name: bind_name.clone(),
|
||||
suggested_text: to_lower_snake_case(&bind_name.to_string())?,
|
||||
suggested_text: to_lower_snake_case(
|
||||
&bind_name.display(self.db.upcast()).to_string(),
|
||||
)?,
|
||||
expected_case: CaseType::LowerSnakeCase,
|
||||
},
|
||||
))
|
||||
|
@ -287,7 +289,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::Function,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: fn_name_replacement.expected_case,
|
||||
ident_text: fn_name_replacement.current_name.to_string(),
|
||||
ident_text: fn_name_replacement.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: fn_name_replacement.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -343,7 +345,10 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type,
|
||||
ident: AstPtr::new(&name_ast),
|
||||
expected_case: replacement.expected_case,
|
||||
ident_text: replacement.current_name.to_string(),
|
||||
ident_text: replacement
|
||||
.current_name
|
||||
.display(self.db.upcast())
|
||||
.to_string(),
|
||||
suggested_text: replacement.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -362,7 +367,7 @@ impl<'a> DeclValidator<'a> {
|
|||
let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
|
||||
|
||||
// Check the structure name.
|
||||
let struct_name = data.name.to_string();
|
||||
let struct_name = data.name.display(self.db.upcast()).to_string();
|
||||
let struct_name_replacement = if !non_camel_case_allowed {
|
||||
to_camel_case(&struct_name).map(|new_name| Replacement {
|
||||
current_name: data.name.clone(),
|
||||
|
@ -379,7 +384,7 @@ impl<'a> DeclValidator<'a> {
|
|||
if !non_snake_case_allowed {
|
||||
if let VariantData::Record(fields) = data.variant_data.as_ref() {
|
||||
for (_, field) in fields.iter() {
|
||||
let field_name = field.name.to_string();
|
||||
let field_name = field.name.display(self.db.upcast()).to_string();
|
||||
if let Some(new_name) = to_lower_snake_case(&field_name) {
|
||||
let replacement = Replacement {
|
||||
current_name: field.name.clone(),
|
||||
|
@ -434,7 +439,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::Structure,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: replacement.expected_case,
|
||||
ident_text: replacement.current_name.to_string(),
|
||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: replacement.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -479,7 +484,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::Field,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: field_to_rename.expected_case,
|
||||
ident_text: field_to_rename.current_name.to_string(),
|
||||
ident_text: field_to_rename.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: field_to_rename.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -496,7 +501,7 @@ impl<'a> DeclValidator<'a> {
|
|||
}
|
||||
|
||||
// Check the enum name.
|
||||
let enum_name = data.name.to_string();
|
||||
let enum_name = data.name.display(self.db.upcast()).to_string();
|
||||
let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement {
|
||||
current_name: data.name.clone(),
|
||||
suggested_text: new_name,
|
||||
|
@ -510,7 +515,9 @@ impl<'a> DeclValidator<'a> {
|
|||
.filter_map(|(_, variant)| {
|
||||
Some(Replacement {
|
||||
current_name: variant.name.clone(),
|
||||
suggested_text: to_camel_case(&variant.name.to_string())?,
|
||||
suggested_text: to_camel_case(
|
||||
&variant.name.display(self.db.upcast()).to_string(),
|
||||
)?,
|
||||
expected_case: CaseType::UpperCamelCase,
|
||||
})
|
||||
})
|
||||
|
@ -558,7 +565,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::Enum,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: replacement.expected_case,
|
||||
ident_text: replacement.current_name.to_string(),
|
||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: replacement.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -603,7 +610,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::Variant,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: variant_to_rename.expected_case,
|
||||
ident_text: variant_to_rename.current_name.to_string(),
|
||||
ident_text: variant_to_rename.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: variant_to_rename.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -623,7 +630,7 @@ impl<'a> DeclValidator<'a> {
|
|||
None => return,
|
||||
};
|
||||
|
||||
let const_name = name.to_string();
|
||||
let const_name = name.display(self.db.upcast()).to_string();
|
||||
let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) {
|
||||
Replacement {
|
||||
current_name: name.clone(),
|
||||
|
@ -648,7 +655,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::Constant,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: replacement.expected_case,
|
||||
ident_text: replacement.current_name.to_string(),
|
||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: replacement.suggested_text,
|
||||
};
|
||||
|
||||
|
@ -668,7 +675,7 @@ impl<'a> DeclValidator<'a> {
|
|||
|
||||
let name = &data.name;
|
||||
|
||||
let static_name = name.to_string();
|
||||
let static_name = name.display(self.db.upcast()).to_string();
|
||||
let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) {
|
||||
Replacement {
|
||||
current_name: name.clone(),
|
||||
|
@ -693,7 +700,7 @@ impl<'a> DeclValidator<'a> {
|
|||
ident_type: IdentType::StaticVariable,
|
||||
ident: AstPtr::new(&ast_ptr),
|
||||
expected_case: replacement.expected_case,
|
||||
ident_text: replacement.current_name.to_string(),
|
||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
||||
suggested_text: replacement.suggested_text,
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
//! fields, etc.
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use either::Either;
|
||||
use hir_def::lang_item::LangItem;
|
||||
|
@ -12,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
|
|||
use hir_expand::name;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use triomphe::Arc;
|
||||
use typed_arena::Arena;
|
||||
|
||||
use crate::{
|
||||
|
@ -27,7 +27,7 @@ use crate::{
|
|||
|
||||
pub(crate) use hir_def::{
|
||||
body::Body,
|
||||
expr::{Expr, ExprId, MatchArm, Pat, PatId},
|
||||
hir::{Expr, ExprId, MatchArm, Pat, PatId},
|
||||
LocalFieldId, VariantId,
|
||||
};
|
||||
|
||||
|
@ -207,7 +207,7 @@ impl ExprValidator {
|
|||
|
||||
let report = compute_match_usefulness(&cx, &m_arms, scrut_ty);
|
||||
|
||||
// FIXME Report unreacheble arms
|
||||
// FIXME Report unreachable arms
|
||||
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
|
||||
|
||||
let witnesses = report.non_exhaustiveness_witnesses;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue